diff --git a/.gitignore b/.gitignore index d6d744a..e7eba2b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,13 +8,20 @@ # Ignore all logfiles, tempfiles, public assets, /log/*.log /tmp -public/system/* -public/assets/* + +# Ignore public subdirectories public/apidocs/* +public/assets/* +public/fonts/* +public/images/* +public/javascripts/* +public/stylesheets/* +public/system/* +public/videos/* # Ignore branded content app/views/branded/* -app/assets/**/* +app/assets config/locales/static_pages/*.yml # Ignore gemfile.lock @@ -28,6 +35,7 @@ # Ignore the test DB db/test.sqlite3 +db/test.sqlite3-journal # Ignore the SimpleCov output coverage @@ -41,6 +49,7 @@ config/initializers/recaptcha.rb config/initializers/devise.rb config/initializers/wicked_pdf.rb +config/initializers/fingerprint.rb # Ignore enviroments settings #config/environments/development.rb @@ -64,4 +73,10 @@ # ignore auto-generated gettext files when running gettext:find config/locale/*/app.edit.po -config/locale/*/app.po.time_stamp \ No newline at end of file +config/locale/*/app.po.time_stamp + +# Front-end related +lib/assets/node_modules +lib/assets/npm-debug.log +lib/assets/.eslintcache +!.keep diff --git a/.travis.yml b/.travis.yml index 453b68d..00a8a7d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,10 @@ language: ruby rvm: - 2.2.2 - +before_install: + - curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - && sudo apt-get install -y nodejs before_script: + - cd lib/assets && npm install && npm run bundle -- -p && cd - - cp config/database_example.yml config/database.yml - cp config/secrets_example.yml config/secrets.yml - cp config/branding_example.yml config/branding.yml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1617b1..85f55f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,14 +3,48 @@ These guidelines are an attempt to ensure that we are able to provide the community with a reliable system, stable APIs, a clear roadmap, and a predictable release schedule. -* If you would like to contribute to the project. Please follow these steps to submit a contribution: +* If you would like to contribute to the project, please follow these steps to submit a contribution: * Comment on the Github issue (or create one if one does not exist) and let us know that you're working on it. - * Fork the project (if you have not already) or rebase your fork so that it is up to date with the current repository's '_**development**_' branch + * Fork the project (if you have not already) or rebase your fork so that it is up to date with the current repository's **`development`** branch * Create a new branch in your fork. This will ensure that you are able to work at your own pace and continue to pull in any updates made to this project. - * Make your changes in the new branch - * When you have finished your work, make sure that your version of the '_**development**_' branch is still up to date with this project. Then merge your new branch into your '_**development**_' branch. - * Then create a new Pull Request (PR) to this project's '_**contributions**_' branch in GitHub + * Make your changes in the new branch. When you have finished your work (e.g. 3 commits), squash all the commits on the branch that you are working on: + ```bash + git rebase -i HEAD~n # Where n is the number of commits you want to squash + ``` + This command's output will look similar to this: + ``` + pick 819b37a First commit in the feature branch. + pick 8634c87 More changes in the feature branch. + pick 59df9aa Third commit in feature branch. + + # Rebase 6c51182..59df9aa onto 6c51182 + ``` + Leave the first commit as `pick` and change `pick` to `squash` for all following commits (to squash them into the single first commit), like so: + ``` + pick 819b37a First commit in the feature branch. + squash 8634c87 More changes in the feature branch. + squash 59df9aa Third commit in feature branch. + + # Rebase 6c51182..59df9aa onto 6c51182 + ``` + Then, change `pick` to `squash` for the 2nd and 3rd commits (to squash them into the single first commit). + * To make sure that your version of the **`development`** branch is still up to date with this project, switch to it and synchronise: + ```bash + git checkout development + git pull origin development + ``` + * Switch back to your feature branch and rebase: + ```bash + git checkout + git rebase development + ``` + * Fix merge conflicts (if any encountered) and then push to your fork: + ```bash + git push origin + ``` + * Then create a new Pull Request (PR) to this project's **`contributions`** branch on GitHub. * 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. + * Delete your feature branch if it is not required anymore. Table of contents: * [Github Workflow](#github-workflow) @@ -21,14 +55,14 @@ ## GitHub Workflow A contribution consists of any work that is voluntarily submitted to the project. This includes bug fixes, enhancements and documentation that is intended as an improvement to the DMP Roadmap system. -Any individual with a GitHub account may propose a Contribution by submitting a Pull Request (PR) to this project's '_**contributions**_' branch. The project team will evaluate each PR as time permits and communicate with the contributor via comments on the PR. We will not accept a contribution until it adheres to the guidelines outlined in this document. If your contribution fits well with the development roadmap, the team will merge it into the project and schedule it for the next upcoming release. +Any individual with a GitHub account may propose a Contribution by submitting a Pull Request (PR) to this project's **`contributions`** branch. The project team will evaluate each PR as time permits and communicate with the contributor via comments on the PR. We will not accept a contribution until it adheres to the guidelines outlined in this document. If your contribution fits well with the development roadmap, the team will merge it into the project and schedule it for the next upcoming release. ![GitHub Workflow ](https://github.com/DMPRoadmap/roadmap/blob/master/public/github-contributor-infographic-final.png) ## Pull Requests Please use these checklists to help you prepare your Pull Request for submission. -ALL Pull Requests MUST be made to the '_**contributions**_' branch! +ALL Pull Requests MUST be made to the **`contributions`** branch! #### Checklist for changes to a database table and/or its corresponding model * Did you include the appropriate database migration? ```> rails g migration AddTwitterIdToUsers``` diff --git a/Gemfile b/Gemfile index bb1c5b9..d7851c3 100644 --- a/Gemfile +++ b/Gemfile @@ -5,32 +5,32 @@ # ------------------------------------------------ # RAILS gem 'rails', '4.2.7' -gem 'railties' +gem 'railties', '~> 4.2' # GEMS ADDED TO HELP HANDLE RAILS MIGRATION FROM 3.x to 4.2 # THESE GEMS HELP SUPPORT DEPRACATED FUNCTIONALITY AND WILL LOSE SUPPORT IN FUTURE VERSIONS # WE SHOULD CONSIDER BRINGING THE CODE UP TO DATE INSTEAD -gem 'protected_attributes' # Provides attr_accessor functions +gem 'protected_attributes', '~> 1.1.3' # Provides attr_accessor functions gem 'responders', '~> 2.0' # Allows use of respond_with and respond_to in controllers # ------------------------------------------------ # DATABASE/SERVER gem 'mysql2', '~> 0.3.18' -gem 'pg' -gem 'flag_shih_tzu' # Allows for bitfields in activereccord +gem 'pg', '~> 0.19.0' +gem 'flag_shih_tzu', '~> 0.3' # Allows for bitfields in activereccord # ------------------------------------------------ # JS <-> RUBY BRIDGE -gem 'libv8' +gem 'libv8', '~> 3.16' gem 'therubyracer', '>=0.11.4', platforms: :ruby # ------------------------------------------------ # JSON DSL - USED BY API -gem 'jbuilder' +gem 'jbuilder', '~> 2.6.0' # ------------------------------------------------ # SLUGS/PERMALINKS -gem 'friendly_id' +gem 'friendly_id', '~> 5.1.0' # ------------------------------------------------ # SUPER ADMIN SECTION @@ -39,88 +39,80 @@ # ------------------------------------------------ # USERS # devise for user authentication -gem 'devise' -gem 'devise_invitable' -gem 'omniauth' -gem 'omniauth-shibboleth' -gem 'omniauth-orcid' +gem 'devise', '~> 4.2.0' +gem 'devise_invitable', '~> 1.7.0' +gem 'omniauth', '~> 1.3.2' +gem 'omniauth-shibboleth', '~> 1.2.1' +gem 'omniauth-orcid', '~> 2.0' +gem 'ruby_dig' # for omniauth-orcid #rolify for roles -gem 'rolify' +gem 'rolify', '~> 5.1.0' # Gems for repository integration -gem 'pundit' +gem 'pundit', '~> 1.1.0' # ------------------------------------------------ # SETTINGS FOR TEMPLATES AND PLANS (FONTS, COLUMN LAYOUTS, ETC) -gem 'ledermann-rails-settings' +gem 'ledermann-rails-settings', '~> 2.4.2' # ------------------------------------------------ # VIEWS -gem 'sass-rails' -gem 'less-rails' # WE SHOULD PROBABLY USE SASS OR LESS NOT BOTH -gem 'jquery-rails' -gem 'font-awesome-rails' -gem 'twitter-bootstrap-rails', '2.2.8' -gem 'tinymce-rails' # WYSIWYG EDITOR -gem 'contact_us', '>= 1.2.0' # COULD BE EASILY REPLACED WITH OUR OWN CODE -gem 'recaptcha', '>= 4.0' -gem 'dragonfly' # LOGO UPLOAD -gem 'formtastic' +gem 'contact_us', '~> 1.2.0' # COULD BE EASILY REPLACED WITH OUR OWN CODE +gem 'recaptcha', '~> 4.1.0' +gem 'dragonfly', '~> 1.0.12' # LOGO UPLOAD +gem 'formtastic', '~> 3.1.4' # ------------------------------------------------ # EXPORTING -gem 'wkhtmltopdf-binary' -gem 'thin' -gem 'wicked_pdf' -gem 'htmltoword', '>= 0.7' -gem 'feedjira' +gem 'wkhtmltopdf-binary', '~> 0.12.3' +gem 'thin', '~> 1.7' +gem 'wicked_pdf', '~> 1.1.0' +gem 'htmltoword', '~> 0.5.1' +gem 'feedjira', '~> 2.0.0' gem 'yaml_db', :git => 'https://github.com/vyruss/yaml_db.git' # ------------------------------------------------ # INTERNATIONALIZATION -gem "i18n-js", ">= 3.0.0.rc11" #damodar added TODO: explain gem 'gettext_i18n_rails', '~> 1.8' gem "gettext_i18n_rails_js", "~> 1.2.0" gem 'gettext', '>=3.0.2', :require => false, :group => :development # ------------------------------------------------ # API -gem 'swagger-docs' +gem 'swagger-docs', '>= 0.2.9 ' # ------------------------------------------------ # CODE DOCUMENTATION -gem 'yard', '>= 0.9.11' -gem 'redcarpet' +gem 'yard', '>= 0.9.5' +gem 'redcarpet', '>= 3.3.4' - +# ------------------------------------------------ +# PAGINATION +gem 'kaminari', '>= 1.0' # ------------------------------------------------ # ENVIRONMENT SPECIFIC DEPENDENCIES group :development, :test do - gem "byebug" + gem "byebug", '~> 9.0' end group :test do - gem 'minitest-rails-capybara' - gem 'minitest-reporters' - gem 'rack-test' - gem 'webmock' - gem 'sqlite3' - gem 'simplecov', require: false + gem 'minitest-rails-capybara', '~> 2.1.2' + gem 'minitest-reporters', '~> 1.1.11' + gem 'rack-test', '~> 0.6.3' + gem 'webmock', '~> 2.1.0' + gem 'sqlite3', '~> 1.3.12' + gem 'simplecov', '~> 0.12', require: false end group :development do - gem "better_errors" - gem "binding_of_caller" - gem 'web-console', '~>2.0' - gem 'rack-mini-profiler' + gem "better_errors", '~> 2.1.1' + gem "binding_of_caller", '~> 0.7.2' + gem 'web-console', '~> 2.3.0' + gem 'rack-mini-profiler', '~> 0.10.1' #gem 'flamegraph' end -group :production do - gem 'uglifier' # JS minifier -end - # ------------------------------------------------ # GEMS THAT ARE NO LONGER IN USE # diff --git a/Gemfile.lock b/Gemfile.lock index 6e6fceb..8365c2a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -84,7 +84,6 @@ rack-test (>= 0.5.4) xpath (~> 2.0) coderay (1.1.1) - commonjs (0.2.7) concurrent-ruby (1.0.2) contact_us (1.2.0) rails (>= 4.2.0) @@ -122,8 +121,6 @@ loofah (~> 2.0) sax-machine (~> 1.0) flag_shih_tzu (0.3.15) - font-awesome-rails (4.7.0.1) - railties (>= 3.2, < 5.1) formtastic (3.1.4) actionpack (>= 3.2.13) friendly_id (5.1.0) @@ -141,14 +138,13 @@ globalid (0.3.7) activesupport (>= 4.1.0) hashdiff (0.3.0) - hashie (3.4.6) - htmltoword (0.7.0) + hashie (3.5.7) + htmltoword (0.5.1) actionpack nokogiri rubyzip (>= 1.0) - i18n (0.7.0) - i18n-js (3.0.0.rc14) - i18n (~> 0.6, >= 0.6.6) + i18n (0.9.5) + concurrent-ruby (~> 1.0) jbuilder (2.6.0) activesupport (>= 3.0.0, < 5.1) multi_json (~> 1.2) @@ -156,7 +152,7 @@ rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - json (1.8.3) + json (1.8.6) jwt (1.5.6) kaminari (1.0.1) activesupport (>= 4.1.0) @@ -172,13 +168,6 @@ kaminari-core (1.0.1) ledermann-rails-settings (2.4.2) activerecord (>= 3.1) - less (2.6.0) - commonjs (~> 0.2.7) - less-rails (2.7.1) - actionpack (>= 4.0) - less (~> 2.6.0) - sprockets (> 2, < 4) - tilt libv8 (3.16.14.15) locale (2.1.2) loofah (2.0.3) @@ -189,7 +178,7 @@ mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.3.0) - minitest (5.9.1) + minitest (5.11.3) minitest-capybara (0.8.2) capybara (~> 2.2) minitest (~> 5.0) @@ -211,26 +200,26 @@ ruby-progressbar momentjs-rails (2.17.1) railties (>= 3.1) - multi_json (1.12.1) - multi_xml (0.5.5) + multi_json (1.13.1) + multi_xml (0.6.0) multipart-post (2.0.0) mysql2 (0.3.21) - nokogiri (1.8.1) + nokogiri (1.8.2) mini_portile2 (~> 2.3.0) normalize-rails (4.1.1) - oauth2 (1.2.0) - faraday (>= 0.8, < 0.10) + oauth2 (1.4.0) + faraday (>= 0.8, < 0.13) jwt (~> 1.0) multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - omniauth (1.3.1) + omniauth (1.3.2) hashie (>= 1.2, < 4) rack (>= 1.0, < 3) - omniauth-oauth2 (1.4.0) - oauth2 (~> 1.0) + omniauth-oauth2 (1.5.0) + oauth2 (~> 1.1) omniauth (~> 1.2) - omniauth-orcid (1.2.1) + omniauth-orcid (2.0.2) omniauth-oauth2 (~> 1.3) omniauth-shibboleth (1.2.1) omniauth (>= 1.0.0) @@ -242,7 +231,7 @@ activemodel (>= 4.0.1, < 5.0) pundit (1.1.0) activesupport (>= 3.0.0) - rack (1.6.4) + rack (1.6.9) rack-mini-profiler (0.10.1) rack (>= 1.2.0) rack-test (0.6.3) @@ -260,9 +249,9 @@ sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) - rails-dom-testing (1.0.7) - activesupport (>= 4.2.0.beta, < 5.0) - nokogiri (~> 1.6.0) + rails-dom-testing (1.0.9) + activesupport (>= 4.2.0, < 5.0) + nokogiri (~> 1.6) rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.3) loofah (~> 2.0) @@ -280,7 +269,8 @@ railties (>= 4.2.0, < 5.1) rolify (5.1.0) ruby-progressbar (1.8.1) - rubyzip (1.2.1) + ruby_dig (0.0.2) + rubyzip (1.2.0) safe_yaml (1.0.4) sass (3.4.22) sass-rails (5.0.6) @@ -316,19 +306,10 @@ eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) thor (0.19.1) - thread_safe (0.3.5) + thread_safe (0.3.6) tilt (2.0.5) - tinymce-rails (4.4.3) - railties (>= 3.1.1) - twitter-bootstrap-rails (2.2.8) - actionpack (>= 3.1) - execjs - rails (>= 3.1) - railties (>= 3.1) - tzinfo (1.2.2) + tzinfo (1.2.5) thread_safe (~> 0.1) - uglifier (3.0.2) - execjs (>= 0.3.0, < 3) warden (1.2.6) rack (>= 1.0) web-console (2.3.0) @@ -351,63 +332,57 @@ DEPENDENCIES administrate! - better_errors - binding_of_caller - byebug - contact_us (>= 1.2.0) - devise - devise_invitable - dragonfly - feedjira - flag_shih_tzu - font-awesome-rails - formtastic - friendly_id + better_errors (~> 2.1.1) + binding_of_caller (~> 0.7.2) + byebug (~> 9.0) + contact_us (~> 1.2.0) + devise (~> 4.2.0) + devise_invitable (~> 1.7.0) + dragonfly (~> 1.0.12) + feedjira (~> 2.0.0) + flag_shih_tzu (~> 0.3) + formtastic (~> 3.1.4) + friendly_id (~> 5.1.0) gettext (>= 3.0.2) gettext_i18n_rails (~> 1.8) gettext_i18n_rails_js (~> 1.2.0) - htmltoword (>= 0.7) - i18n-js (>= 3.0.0.rc11) - jbuilder - jquery-rails - ledermann-rails-settings - less-rails - libv8 - minitest-rails-capybara - minitest-reporters + htmltoword (~> 0.5.1) + jbuilder (~> 2.6.0) + kaminari (>= 1.0) + ledermann-rails-settings (~> 2.4.2) + libv8 (~> 3.16) + minitest-rails-capybara (~> 2.1.2) + minitest-reporters (~> 1.1.11) mysql2 (~> 0.3.18) - omniauth - omniauth-orcid - omniauth-shibboleth - pg - protected_attributes - pundit - rack-mini-profiler - rack-test + omniauth (~> 1.3.2) + omniauth-orcid (~> 2.0) + omniauth-shibboleth (~> 1.2.1) + pg (~> 0.19.0) + protected_attributes (~> 1.1.3) + pundit (~> 1.1.0) + rack-mini-profiler (~> 0.10.1) + rack-test (~> 0.6.3) rails (= 4.2.7) - railties - recaptcha (>= 4.0) - redcarpet + railties (~> 4.2) + recaptcha (~> 4.1.0) + redcarpet (>= 3.3.4) responders (~> 2.0) - rolify - sass-rails - simplecov - sqlite3 - swagger-docs + rolify (~> 5.1.0) + ruby_dig + simplecov (~> 0.12) + sqlite3 (~> 1.3.12) + swagger-docs (>= 0.2.9) therubyracer (>= 0.11.4) - thin - tinymce-rails - twitter-bootstrap-rails (= 2.2.8) - uglifier - web-console (~> 2.0) - webmock - wicked_pdf - wkhtmltopdf-binary + thin (~> 1.7) + web-console (~> 2.3.0) + webmock (~> 2.1.0) + wicked_pdf (~> 1.1.0) + wkhtmltopdf-binary (~> 0.12.3) yaml_db! - yard (>= 0.9.11) + yard (>= 0.9.5) RUBY VERSION ruby 2.2.2p95 BUNDLED WITH - 1.14.6 + 1.13.7 diff --git a/README.md b/README.md index 0ae2435..d439328 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,9 @@ Roadmap is a Ruby on Rails application and you will need to have: * Ruby >= 2.2.2 * Rails >= 4.2 -* MySql >= 5.0 OR PostgreSql +* MySQL >= 5.0 OR PostgreSQL -#### Migrating data from a running instance of DMPOnline_v4 or DMPTool -Migration instructions will be coming soon +Further detail on how to install Ruby on Rails applications are available from the Ruby on Rails site: http://rubyonrails.org Further details on how to install MySQL and create your first user and database. Be sure to follow the instructions for your particular environment. * Install: http://dev.mysql.com/downloads/mysql/ @@ -35,7 +34,7 @@ * Ruby on Rails Tutorial Book: http://www.railstutorial.org/ #### Installation -* Create your database. Select UTF-8 Unicode (utf8) encoding. +* Create your database. Select UTF-8 Unicode encoding (`utf8mb4` if using MySQL). * Clone this repository (or Fork the repository first if you plan on contributing) > > git clone https://github.com/[your organization]/roadmap.git diff --git a/app/controllers/annotations_controller.rb b/app/controllers/annotations_controller.rb index 73a34fb..9191895 100644 --- a/app/controllers/annotations_controller.rb +++ b/app/controllers/annotations_controller.rb @@ -2,108 +2,42 @@ respond_to :html after_action :verify_authorized - #create annotations - def admin_create - # authorize the question (includes to reduce queries) - @question = Question.includes(section: { phase: :template}).find(params[:question_id]) - authorize @question - if params[:example_answer_text].present? - example_answer = init_annotation(params[:example_answer_text], @question, current_user.org, Annotation.types[:example_answer]) - end - if params[:guidance_text].present? - guidance = init_annotation(params[:guidance_text], @question, current_user.org, Annotation.types[:guidance]) - end - # if they dont exist, no requirement for them to be saved - ex_save = example_answer.present? ? example_answer.save : true - guid_save = guidance.present? ? guidance.save : true - @question.section.phase.template.dirty = true - - if ex_save && guid_save - redirect_to admin_show_phase_path(id: @question.section.phase_id, section_id: @question.section_id, question_id: @question.id, edit: 'true'), notice: _('Information was successfully created.') - else - @section = @question.section - @phase = @section.phase - @open = true - @sections = @phase.sections - @section_id = @section.id - @question_id = @example_answer.question - if !ex_save && !guid_save - flash[:notice] = failed_create_error(example_answer, _('example answer')) + '\n' + - failed_create_error(gudiance, _('guidance')) - elsif !guid_save - flash[:notice] = failed_create_error(gudiance, _('guidance')) - elsif !ex_save - flash[:notice] = failed_create_error(example_answer, _('example answer')) - end - render "phases/admin_show" - end - end - - #update a example answer of a template def admin_update - @question = Question.includes(section: { phase: :template}).find(params[:question_id]) - if params[:guidance_id].present? - guidance = Annotation.includes(question: {section: {phase: :template}}).find(params[:guidance_id]) - authorize guidance - end - if params[:example_answer_id].present? - example_answer = Annotation.includes(question: {section: {phase: :template}}).find(params[:example_answer_id]) - authorize example_answer - end - verify_authorized - # if guidance present, update - if params[:guidance_text].present? - if guidance.present? - guidance.text = params[:guidance_text] - else - guidance = init_annotation(params[:guidance_text], @question, current_user.org, Annotation.types[:guidance]) - end - end - # if example answer present, update - if params[:example_answer_text].present? - if example_answer.present? - example_answer.text = params[:example_answer_text] - else - example_answer = init_annotation(params[:example_answer_text], @question, current_user.org, Annotation.types[:example_answer]) - end - end - # only required to save if we updated/created one - ex_save = example_answer.present? ? example_answer.save : true - guid_save = guidance.present? ? guidance.save : true + question = Question.includes(section: { phase: :template}).find(params[:question_id]) + authorize question - @section = @question.section - @phase = @section.phase - @phase.template.dirty = true +# example_answer = Annotation.find_or_create_by(question: question, org: current_user.org, type: Annotation.types[:example_answer]) +# guidance = Annotation.find_or_create_by(question: question, org: current_user.org, type: Annotation.types[:guidance]) + + hash = {question_id: question.id, org_id: current_user.org.id} + process_changes(hash.merge({type: Annotation.types[:example_answer]}), params[:example_answer_text], _('example answer')) + process_changes(hash.merge({type: Annotation.types[:guidance]}), params[:guidance_text], _('guidance')) - if ex_save && guid_save - redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, question_id: @question.id, edit: 'true'), notice: _('Information was successfully updated.') - else - if !ex_save && !guid_save - flash[:notice] = failed_create_error(example_answer, _('example answer')) + '\n' + - failed_create_error(gudiance, _('guidance')) - elsif !guid_save - flash[:notice] = failed_create_error(gudiance, _('guidance')) - elsif !ex_save - flash[:notice] = failed_create_error(example_answer, _('example answer')) - end - render action: "phases/admin_show" + if !flash[:notice].blank? || !flash[:alert].blank? + template = question.section.phase.template + template.dirty = true + template.save! end + tab = params[:r] || 'all-templates' + redirect_to "#{admin_show_phase_path(question.section.phase.id)}?section_id=#{question.section.id}&r=#{tab}" end #delete an annotation def admin_destroy - @example_answer = Annotation.includes(question: { section: {phase: :template}}).find(params[:id]) - authorize @example_answer - @question = @example_answer.question - @section = @question.section - @phase = @section.phase - @phase.template.dirty = true - if @example_answer.destroy - redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, edit: 'true'), notice: _('Information was successfully deleted.') - else - redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, edit: 'true'), notice: flash[:notice] = failed_destroy_error(@example_answer, _('example answer')) + annotation = Annotation.find(params[:id]) + authorize annotation + parent_ids = Annotation.joins("INNER JOIN questions ON annotations.question_id = questions.id").joins("INNER JOIN sections ON questions.section_id = sections.id").joins("INNER JOIN phases ON sections.phase_id = phases.id").where("annotations.id": params[:id]).pluck("phases.id", "sections.id").first #annotation.question.section.phase.id + if annotation.present? + type = (annotation.type == Annotation.types[:example_answer] ? 'example answer' : 'guidance') + if annotation.destroy! + flash[:notice] = success_message(type, _('deleted')) + else + flash[:alert] = failed_destroy_error(annotation, type) + end end + tab = params[:r] || 'all-templates' + redirect_to "#{admin_show_phase_path(parent_ids[0])}?section_id=#{parent_ids[1]}&r=#{tab}" end private @@ -117,4 +51,28 @@ return annotation end + def process_changes(hash, input, type) + # If the input is available update the annotation otherwise remove it if it exists + if input.present? + annotation = Annotation.find_or_create_by(hash) + if annotation.text != input + annotation.text = input + if annotation.save! + flash[:notice] = "#{(flash[:notice].nil? ? '' : flash[:notice] + '
')}#{success_message(type, _('updated'))}" + else + flash[:alert] = "#{(flash[:alert].nil? ? '' : flash[:alert] + '
')}#{failed_update_error(annotation, type)}" + end + end + else + # If the user cleared the text and the record exists, delete it + annotation = Annotation.find_by(hash) + if annotation.present? + if annotation.destroy! + flash[:notice] = "#{(flash[:notice].nil? ? '' : flash[:notice] + '
')}#{success_message(type, _('removed'))}" + else + flash[:alert] = "#{(flash[:alert].nil? ? '' : flash[:alert] + '
')}#{failed_update_error(annotation, type)}" + end + end + end + end end diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb index 406d75f..9c9f70e 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -1,46 +1,85 @@ class AnswersController < ApplicationController - after_action :verify_authorized respond_to :html - # PUT/PATCH /answers/[:id] - def update + # POST /answers/create_or_update + def create_or_update p_params = permitted_params() + + #First it is checked plan exists and question exist for that plan + begin + p = Plan.find(p_params[:plan_id]) + if !p.question_exists?(p_params[:question_id]) + render(status: :not_found, json: + { msg: _("There is no question with id %{question_id} associated to plan id %{plan_id}"\ + "for which to create or update an answer") %{ :question_id => p_params[:question_id], :plan_id => p_params[:plan_id] }}) + return + end + rescue ActiveRecord::RecordNotFound + render(status: :not_found, json: + { msg: _('There is no plan with id %{id} for which to create or update an answer') %{ :id => p_params[:plan_id] }}) + return + end + q = Question.find(p_params[:question_id]) + Answer.transaction do - @answer = Answer.find_by({plan_id: p_params[:plan_id], question_id: p_params[:question_id]}) begin - if @answer.present? - authorize @answer - @answer.update(p_params) - if p_params[:question_option_ids].present? - @answer.touch() # Saves the record with the updated_at set to the current time. Needed if only answer.question_options is updated - end - else - @answer = Answer.new(p_params) - @answer.lock_version = 1 - authorize @answer - # NOTE: save! and destroy! must be used for transactions as they raise errors instead of returning false + @answer = Answer.find_by!({ plan_id: p_params[:plan_id], question_id: p_params[:question_id] }) + authorize @answer + @answer.update(p_params.merge({ user_id: current_user.id })) + if p_params[:question_option_ids].present? + @answer.touch() # Saves the record with the updated_at set to the current time. Needed if only answer.question_options is updated + end + if q.question_format.rda_metadata? + @answer.update_answer_hash(JSON.parse(params[:standards]), p_params[:text]) @answer.save! end + rescue ActiveRecord::RecordNotFound + @answer = Answer.new(p_params.merge({ user_id: current_user.id })) + @answer.lock_version = 1 + authorize @answer + if q.question_format.rda_metadata? + @answer.update_answer_hash(JSON.parse(params[:standards]), p_params[:text]) + end + @answer.save! rescue ActiveRecord::StaleObjectError @stale_answer = @answer @answer = Answer.find_by({plan_id: p_params[:plan_id], question_id: p_params[:question_id]}) end end - @plan = Plan.includes({ - sections: { - questions: [ - :answers, - :question_format - ] - } - }).find(p_params[:plan_id]) - @question = @answer.question - @section = @plan.get_section(@question.section_id) + if @answer.present? + @plan = Plan.includes({ + sections: { + questions: [ + :answers, + :question_format + ] + } + }).find(p_params[:plan_id]) + @question = @answer.question + @section = @plan.get_section(@question.section_id) - respond_to do |format| - format.js {} + render json: { + "question" => { + "id" => @question.id, + "answer_lock_version" => @answer.lock_version, + "locking" => @stale_answer ? + render_to_string(partial: 'answers/locking', locals: { question: @question, answer: @stale_answer, user: @answer.user }, formats: [:html]) : + nil, + "form" => render_to_string(partial: 'answers/new_edit', locals: { question: @question, answer: @answer, readonly: false, locking: false }, formats: [:html]), + "answer_status" => render_to_string(partial: 'answers/status', locals: { answer: @answer}, formats: [:html]) + }, + "section" => { + "id" => @section.id, + "progress" => render_to_string(partial: '/sections/progress', locals: { section: @section, plan: @plan }, formats: [:html]) + }, + "plan" => { + "id" => @plan.id, + "progress" => render_to_string(:partial => 'plans/progress', locals: { plan: @plan, current_phase: @section.phase }, formats: [:html]) + } + }.to_json end + end # End update private diff --git a/app/controllers/api/v0/statistics_controller.rb b/app/controllers/api/v0/statistics_controller.rb index b3b95a4..57c2e43 100644 --- a/app/controllers/api/v0/statistics_controller.rb +++ b/app/controllers/api/v0/statistics_controller.rb @@ -3,23 +3,127 @@ class StatisticsController < Api::V0::BaseController before_action :authenticate - ## - # GET - # @return a count of users who joined DMPonline between the optional specified dates - # users are scoped to the organisation of the user initiating the call + # GET /api/v0/statistics/users_joined?start_date=&end_date=&org_id= + # Returns the number of users joined for the user's org. + # If start_date is passed, only counts those with created_at is >= than start_date + # If end_date is passed, only counts those with created_at is <= than end_date are + # If org_id is passed and user has super_admin privileges that counter is performed against org_id param instead of user's org + # @return def users_joined raise Pundit::NotAuthorizedError unless Api::V0::StatisticsPolicy.new(@user, :statistics).users_joined? - users = restrict_date_range(@user.org.users) - confirmed_users = [] - users.each do |user| - unless user.confirmed_at.blank? - confirmed_users += [user] - end + + scoped = User.unscoped.where.not(confirmed_at: nil) + if @user.can_super_admin? && params[:org_id].present? + scoped = scoped.where(org_id: params[:org_id]) + else + scoped = scoped.where(org_id: @user.org_id) end - @users_count = confirmed_users.count - respond_with @users_count + + if params[:range_dates].present? + r = {} + params[:range_dates].each_pair do |k, v| + r[k] = scoped + .where('created_at >=?', v['start_date']) + .where('created_at <=?', v['end_date']).count + end + respond_to do |format| + format.json { render(json: r.to_json) } + format.csv { + send_data(CSV.generate do |csv| + csv << [_('Month'), _('No. Users joined')] + total = 0 + r.each_pair{ |k,v| csv << [k,v]; total+=v } + csv << [_('Total'), total] + end, filename: "#{_('users_joined')}.csv") } + end + else + scoped = scoped.where('created_at >= ?', Date.parse(params[:start_date])) if params[:start_date].present? + scoped = scoped.where('created_at <= ?', Date.parse(params[:end_date])) if params[:end_date].present? + @users_count = scoped.count + respond_with @users_count + end + end + # GET + # Returns the number of completed plans within the user's org for the data start_date and end_date specified + def completed_plans + raise Pundit::NotAuthorizedError unless Api::V0::StatisticsPolicy.new(@user, :statistics).completed_plans? + + roles = Role.where("#{Role.creator_condition} OR #{Role.administrator_condition}") + + users = User.unscoped + if @user.can_super_admin? && params[:org_id].present? + users = users.where(org_id: params[:org_id]) + else + users = users.where(org_id: @user.org_id) + end + + plans = Plan.where(complete: true) + if params[:range_dates].present? + r = {} + params[:range_dates].each_pair do |k, v| + range_date_plans = plans + .where('plans.updated_at >=?', v['start_date']) + .where('plans.updated_at <=?', v['end_date']) + r[k] = roles.joins(:user, :plan).merge(users).merge(range_date_plans).select(:plan_id).distinct.count + end + respond_to do |format| + format.json { render(json: r.to_json) } + format.csv { + send_data(CSV.generate do |csv| + csv << [_('Month'), _('No. Completed Plans')] + total = 0 + r.each_pair{ |k,v| csv << [k,v]; total+=v } + csv << [_('Total'), total] + end, filename: "#{_('completed_plans')}.csv") } + end + else + plans = plans.where('plans.updated_at >= ?', Date.parse(params[:start_date])) if params[:start_date].present? + plans = plans.where('plans.updated_at <= ?', Date.parse(params[:end_date])) if params[:end_date].present? + count = roles.joins(:user, :plan).merge(users).merge(plans).select(:plan_id).distinct.count + render(json: { completed_plans: count }) + end end + # /api/v0/statistics/created_plans + # Returns the number of created plans within the user's org for the data start_date and end_date specified + def created_plans + raise Pundit::NotAuthorizedError unless Api::V0::StatisticsPolicy.new(@user, :statistics).plans? + + roles = Role.where("#{Role.creator_condition} OR #{Role.administrator_condition}") + + users = User.unscoped + if @user.can_super_admin? && params[:org_id].present? + users = users.where(org_id: params[:org_id]) + else + users = users.where(org_id: @user.org_id) + end + + plans = Plan.all + if params[:range_dates].present? + r = {} + params[:range_dates].each_pair do |k, v| + range_date_plans = plans + .where('plans.created_at >=?', v['start_date']) + .where('plans.created_at <=?', v['end_date']) + r[k] = roles.joins(:user, :plan).merge(users).merge(range_date_plans).select(:plan_id).distinct.count + end + respond_to do |format| + format.json { render(json: r.to_json) } + format.csv { + send_data(CSV.generate do |csv| + csv << [_('Month'), _('No. Plans')] + total = 0 + r.each_pair{ |k,v| csv << [k,v]; total+=v } + csv << [_('Total'), total] + end, filename: "#{_('plans')}.csv") } + end + else + plans = plans.where('plans.created_at >= ?', Date.parse(params[:start_date])) if params[:start_date].present? + plans = plans.where('plans.created_at <= ?', Date.parse(params[:end_date])) if params[:end_date].present? + count = roles.joins(:user, :plan).merge(users).merge(plans).select(:plan_id).distinct.count + render(json: { created_plans: count }) + end + end ## # GET diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2d3dcb0..ecf5dcf 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -13,7 +13,7 @@ def user_not_authorized if user_signed_in? - redirect_to plans_url, notice: _('You are not authorized to perform this action.') + redirect_to plans_url, alert: _('You are not authorized to perform this action.') else redirect_to root_url, alert: _('You need to sign in or sign up before continuing.') end @@ -49,19 +49,28 @@ end def after_sign_in_path_for(resource) - session[:previous_url] || root_path + if from_external_domain? || request.referer.eql?(new_user_session_url(:protocol => 'https')) || request.referer.eql?(new_user_registration_url(:protocol => 'https')) + root_path + else + return request.referer unless request.referer.nil? + root_path + end end def after_sign_up_path_for(resource) - session[:previous_url] || root_path + if from_external_domain? or request.referer.eql?(new_user_session_url(:protocol => 'https')) + root_path + else + request.referer + end end def after_sign_in_error_path_for(resource) - session[:previous_url] || root_path + (from_external_domain? ? root_path : request.referer || root_path) end def after_sign_up_error_path_for(resource) - session[:previous_url] || root_path + (from_external_domain? ? root_path : request.referer || root_path) end def authenticate_admin! @@ -80,6 +89,22 @@ def failed_destroy_error(obj, obj_name) "#{_('Could not delete the %{o}.') % {o: obj_name}} #{errors_to_s(obj)}" end + + def success_message(obj_name, action) + "#{_('Successfully %{action} your %{object}.') % {object: obj_name, action: action}}" + end + + # Check whether the string is a valid array of JSON objects + def is_json_array_of_objects?(string) + if string.present? + begin + json = JSON.parse(string) + return (json.is_a?(Array) && json.all?{ |o| o.is_a?(Hash) }) + rescue JSON::ParserError + return false + end + end + end private # Override rails default render action to look for a branded version of a @@ -120,4 +145,13 @@ end # ------------------------------------------------------------- + def from_external_domain? + if request.referer.present? + referer = URI.parse(request.referer) + home = URI.parse(root_url) + referer.host != home.host + else + false + end + end end diff --git a/app/controllers/concerns/conditional_user_mailer.rb b/app/controllers/concerns/conditional_user_mailer.rb new file mode 100644 index 0000000..aa7622b --- /dev/null +++ b/app/controllers/concerns/conditional_user_mailer.rb @@ -0,0 +1,35 @@ +module ConditionalUserMailer + extend ActiveSupport::Concern + + # Adds following methods as class methods for the class that include this module + included do + # Executes a given block passed if the recipient user has the preference email key enabled + # @param recipients {User | Enumerable } User object or any object that includes Enumerable class + # @param key {String} - A key (dot notation) whose value is true/false and belongs to prefences.email (see config/branding.yml) + def deliver_if(recipients: [], key:) + raise(ArgumentError, 'key must be String') unless key.is_a?(String) + if block_given? + split_key = key.split('.') + if !recipients.respond_to?(:each) + recipients = Array(recipients) + end + recipients.each do |r| + if r.respond_to?(:get_preferences) + email_hash = r.get_preferences('email') + should_deliver = split_key.reduce(email_hash) do |m,o| + if m.is_a?(Hash) + m[o.to_sym] + else + break + end + end + yield r if should_deliver.is_a?(TrueClass) + end + end + true + else # Block not given + false + end + end + end +end \ No newline at end of file diff --git a/app/controllers/concerns/paginable.rb b/app/controllers/concerns/paginable.rb new file mode 100644 index 0000000..76340d3 --- /dev/null +++ b/app/controllers/concerns/paginable.rb @@ -0,0 +1,117 @@ +module Paginable + extend ActiveSupport::Concern + + included do + # Renders paginable layout with the partial view passed + # partial {String} - Represents a path to where the partial view is stored + # controller {String} - Represents the name of the controller to handles the pagination + # action {String} - Represents the method name within the controller + # path_params {Hash} - A hash of additional URL path parameters (e.g. path_paths = { id: 'foo' } for /paginable/templates/:id/history/:page) + # query_params {Hash} - A hash of query parameters used to merge with params object from the controller for which this concern is included + # scope {ActiveRecord::Relation} - Represents scope variable + # locals {Hash} - A hash objects with any additional local variables to be passed to the partial view + def paginable_renderise(partial: nil, controller: params[:controller], action: params[:action], path_params: {}, query_params: {}, scope: nil, locals: {}) + raise ArgumentError, 'scope should be an ActiveRecord::Relation object' unless scope.is_a?(ActiveRecord::Relation) + raise ArgumentError, 'path_params should be a Hash object' unless path_params.is_a?(Hash) + raise ArgumentError, 'query_params should be a Hash object' unless query_params.is_a?(Hash) + raise ArgumentError, 'locals should be a Hash object' unless locals.is_a?(Hash) + @paginable_controller = controller + @paginable_action = action + @paginable_path_params = path_params + merge_query_params(query_params) + refined_scope = refine_query(scope) + render(layout: "/layouts/paginable", partial: partial, locals: { + controller: controller, + action: action, + paginable: refined_scope.respond_to?(:total_pages), + scope: refined_scope }.merge(locals)) + end + # Returns the base url of the paginable route for a given page passed + def paginable_base_url(page = 1) + options = { controller: @paginable_controller || params[:controller], + action: @paginable_action || params[:action], page: page } + if @paginable_path_params.present? + options = @paginable_path_params.merge(options) + end + return url_for(options) + end + # Generates an HTML link to sort given a sort field. + # sort_field {String} - Represents the column name for a table + def paginable_sort_link(sort_field) + return link_to(sort_link_name(sort_field), sort_link_url(sort_field), 'data-remote': true) + end + # Determines whether or not the latest request included the search functionality + def searchable? + return params[:search].present? + end + # Generates an HTML link with search functionality (if latest request included the search functionality) + # text {String} - Represents the text for the searchable link + # page {String | Fixnum } - Represents the page to request for a search term + def paginable_search_link(text = _('link name'), page = 1) + url = paginable_base_url(page) + url+= "?search=#{params[:search]}" if searchable? + return link_to(text, url, 'data-remote': true) + end + end + private + # Attemps to merge query_params into params hash unless a key is already present at params + def merge_query_params(query_params = {}) + query_params.each_pair.reduce(params) do |m, o| + key = o[0].to_sym + if m[key].nil? + m[key] = o[1].to_s + end + m + end + end + # Returns the upcase string (e.g ASC or DESC) if sort_direction param is present in any of the forms 'asc', 'desc', 'ASC', 'DESC' + # otherwise returns nil + def sort_direction + if params[:sort_direction].present? + directions = ['asc', 'desc', 'ASC', 'DESC'] + return directions.include?(params[:sort_direction]) ? params[:sort_direction].upcase : 'ASC' + end + return nil + end + # Returns DESC when ASC is passed and vice versa, otherwise nil + def swap_sort_direction(direction) + return 'DESC' if direction == 'ASC' + return 'ASC' if direction == 'DESC' + end + # Refine a scope passed to this concern if any of the params (search, sort_field or page) are present + def refine_query(scope) + scope = scope.search(params[:search]) if params[:search].present? # Can raise NoMethodError if the scope does not define a search method + if params[:sort_field].present? + direction = sort_direction + scope = direction.present? ? scope.order("#{params[:sort_field]} #{direction}") : scope.order("#{params[:sort_field]}") # Can raise ActiveRecord::StatementInvalid (e.g. column does not exist, ambiguity on column, etc) + end + if params[:page] != 'ALL' + scope = scope.page(params[:page]) # Can raise error if page is not a number + end + return scope + end + # Returns the sort link name for a given sort_field. The link name includes html prevented of being escaped + def sort_link_name(sort_field) + className = 'fa-sort' + direction = sort_direction + if direction.present? && params[:sort_field] == sort_field + className = direction == 'ASC'? 'fa-sort-asc' : 'fa-sort-desc' + end + return raw("") + end + # Returns the sort url for a given sort_field. + def sort_link_url(sort_field) + page = params[:page] == 'ALL' ? 'ALL' : 1 # Retain ALL param page if latest request included it + url = paginable_base_url(page)+"?" + query_string = [] + query_string << "search=#{params[:search]}" if params[:search].present? + direction = sort_direction + if direction.present? && params[:sort_field] == sort_field + query_string << "sort_direction=#{swap_sort_direction(direction)}" + else + query_string << "sort_direction=ASC" + end + query_string << "sort_field=#{sort_field}" + return url+query_string.join('&') + end +end \ No newline at end of file diff --git a/app/controllers/guidance_groups_controller.rb b/app/controllers/guidance_groups_controller.rb index ce09688..9bbfae9 100644 --- a/app/controllers/guidance_groups_controller.rb +++ b/app/controllers/guidance_groups_controller.rb @@ -27,9 +27,9 @@ end if @guidance_group.save - redirect_to admin_index_guidance_path, notice: _('Guidance group was successfully created.') + redirect_to admin_index_guidance_path, notice: success_message(_('guidance group'), _('created')) else - flash[:notice] = failed_create_error(@guidance_group, _('guidance group')) + flash[:alert] = failed_create_error(@guidance_group, _('guidance group')) render 'admin_new' end end @@ -50,14 +50,13 @@ @guidance_group.published = true unless params[:save_publish].nil? if @guidance_group.update_attributes(params[:guidance_group]) - redirect_to admin_index_guidance_path(params[:guidance_group]), notice: _('Guidance group was successfully updated.') + redirect_to admin_index_guidance_path(params[:guidance_group]), notice: success_message(_('guidance group'), _('saved')) else - flash[:notice] = failed_update_error(@guidance_group, _('guidance group')) + flash[:alert] = failed_update_error(@guidance_group, _('guidance group')) render 'admin_edit' end end -# TODO: This does not have a route in config/routes.rb and is unreachable! # PUT /guidance_groups/1 def admin_update_publish @guidance_group = GuidanceGroup.find(params[:id]) @@ -65,13 +64,22 @@ @guidance_group.org.id = current_user.org.id @guidance_group.published = true - if @guidance_group.update_attributes(params[:guidance_group]) - redirect_to admin_index_guidance_path(params[:guidance_group]), notice: _('Guidance group was successfully updated.') - else - redirect_to admin_index_guidance_path(@guidance_group), notice: failed_update_error(@guidance_group, _('guidance group')) - end + @guidance_group.save + flash[:notice] = _('Your guidance group has been published and is now available to users.') + redirect_to admin_index_guidance_path end + # PUT /guidance_groups/1 + def admin_update_unpublish + @guidance_group = GuidanceGroup.find(params[:id]) + authorize @guidance_group + @guidance_group.org.id = current_user.org.id + @guidance_group.published = false + + @guidance_group.save + flash[:notice] = _('Your guidance group is no longer published and will not be available to users.') + redirect_to admin_index_guidance_path + end # DELETE /guidance_groups/1 # DELETE /guidance_groups/1.json @@ -79,9 +87,9 @@ @guidance_group = GuidanceGroup.find(params[:id]) authorize @guidance_group if @guidance_group.destroy - redirect_to admin_index_guidance_path, notice: _('Guidance group was successfully deleted.') + redirect_to admin_index_guidance_path, notice: success_message(_('guidance group'), _('deleted')) else - redirect_to admin_index_guidance_path, notice: failed_destroy_error(@guidance_group, _('guidance group')) + redirect_to admin_index_guidance_path, alert: failed_destroy_error(@guidance_group, _('guidance group')) end end diff --git a/app/controllers/guidances_controller.rb b/app/controllers/guidances_controller.rb index 9cfcfc5..357889f 100644 --- a/app/controllers/guidances_controller.rb +++ b/app/controllers/guidances_controller.rb @@ -6,84 +6,138 @@ # GET /guidances def admin_index authorize Guidance - @guidances = policy_scope(Guidance) - @guidance_groups = GuidanceGroup.where(org_id: current_user.org_id) - end - - ## - # GET /guidances/1 - def admin_show - @guidance = Guidance.eager_load(:guidance_group, :themes).find(params[:id]) - authorize @guidance + @guidances = Guidance.by_org(current_user.org).includes(:guidance_group, :themes).page(1) + @guidance_groups = GuidanceGroup.by_org(current_user.org).page(1) end def admin_new - @guidance = Guidance.new - authorize @guidance - @themes = Theme.all.order('title') - @guidance_groups = GuidanceGroup.where(org_id: current_user.org_id).order('name ASC') - end + guidance = Guidance.new + authorize guidance + themes = Theme.all.order('title') + guidance_groups = GuidanceGroup.where(org_id: current_user.org_id).order('name ASC') + render(:new_edit, locals: { guidance: guidance, themes: themes, + guidance_groups: guidance_groups, options: { url: admin_create_guidance_path, method: :post }}) + end ## # GET /guidances/1/edit def admin_edit - @guidance = Guidance.eager_load(:themes, :guidance_group).find(params[:id]) - authorize @guidance - @themes = Theme.all.order('title') - @guidance_groups = GuidanceGroup.where(org_id: current_user.org_id).order('name ASC') + guidance = Guidance.eager_load(:themes, :guidance_group).find(params[:id]) + authorize guidance + themes = Theme.all.order('title') + guidance_groups = GuidanceGroup.where(org_id: current_user.org_id).order('name ASC') + render(:new_edit, locals: { guidance: guidance, themes: themes, + guidance_groups: guidance_groups, options: { url: admin_update_guidance_path(guidance), method: :put }}) end ## # POST /guidances def admin_create - @guidance = Guidance.new(guidance_params) - authorize @guidance - @guidance.text = params["guidance-text"] + guidance = Guidance.new(guidance_params) + authorize guidance + guidance.text = params["guidance-text"] - @guidance.themes = [] + guidance.themes = [] if !guidance_params[:theme_ids].nil? - guidance_params[:theme_ids].map{|t| @guidance.themes << Theme.find(t.to_i) unless t.empty? } + guidance_params[:theme_ids].map{|t| guidance.themes << Theme.find(t.to_i) unless t.empty? } end + + if guidance.save - if @guidance.save - redirect_to admin_show_guidance_path(@guidance), notice: _('Guidance was successfully created.') + if guidance.published? + guidance_group = GuidanceGroup.find(guidance.guidance_group_id) + if !guidance_group.published? || guidance_group.published.nil? + guidance_group.published = true + guidance_group.save + end + end + flash[:notice] = success_message(_('guidance'), _('created')) + redirect_to(action: :admin_index) + else - flash[:notice] = failed_create_error(@guidance, _('guidance')) - @themes = Theme.all.order('title') - @guidance_groups = GuidanceGroup.where(org_id: current_user.org_id).order('name ASC') - render action: "admin_new" + flash[:alert] = failed_create_error(guidance, _('guidance')) + redirect_to(action: :admin_index) end end ## # PUT /guidances/1 def admin_update - @guidance = Guidance.find(params[:id]) - authorize @guidance - @guidance.text = params["guidance-text"] - if @guidance.update_attributes(guidance_params) - redirect_to admin_show_guidance_path(params[:guidance]), notice: _('Guidance was successfully updated.') + guidance = Guidance.find(params[:id]) + authorize guidance + guidance.text = params["guidance-text"] + + attrs = guidance_params + attrs[:theme_ids] = [] unless attrs[:theme_ids] + + if guidance.update_attributes(attrs) + if guidance.published? + guidance_group = GuidanceGroup.find(guidance.guidance_group_id) + if !guidance_group.published? || guidance_group.published.nil? + guidance_group.published = true + guidance_group.save + end + end + flash[:notice] = success_message(_('guidance'), _('saved')) + redirect_to(action: :admin_index) else - flash[:notice] = failed_update_error(@guidance, _('guidance')) - @themes = Theme.all.order('title') - @guidance_groups = GuidanceGroup.where(org_id: current_user.org_id).order('name ASC') - - render action: "admin_edit" + flash[:alert] = failed_update_error(guidance, _('guidance')) + redirect_to(action: :admin_edit, id: params[:id]) end end ## # DELETE /guidances/1 def admin_destroy - @guidance = Guidance.find(params[:id]) - authorize @guidance - if @guidance.destroy - redirect_to admin_index_guidance_path, notice: _('Guidance was successfully deleted.') + guidance = Guidance.find(params[:id]) + authorize guidance + guidance_group = GuidanceGroup.find(guidance.guidance_group_id) + if guidance.destroy + unless guidance_group.guidances.where(published: true).exists? + guidance_group.published = false + guidance_group.save + end + flash[:notice] = success_message(_('guidance'), _('deleted')) + redirect_to(action: :admin_index) else - redirect_to admin_index_guidance_path, notice: failed_destroy_error(@guidance, _('guidance')) + flash[:alert] = failed_destroy_error(guidance, _('guidance')) + redirect_to(action: :admin_index) end - end + end + # PUT /guidances/1 + def admin_publish + guidance = Guidance.find(params[:id]) + authorize guidance + + guidance.published = true + guidance_group = GuidanceGroup.find(guidance.guidance_group_id) + if !guidance_group.published? || guidance_group.published.nil? + guidance_group.published = true + guidance_group.save + end + guidance.save + + flash[:notice] = _('Your guidance has been published and is now available to users.') + redirect_to(action: :admin_index) + end + + # PUT /guidances/1 + def admin_unpublish + guidance = Guidance.find(params[:id]) + authorize guidance + + guidance.published = false + guidance.save + guidance_group = GuidanceGroup.find(guidance.guidance_group_id) + unless guidance_group.guidances.where(published: true).exists? + guidance_group.published = false + guidance_group.save + end + + flash[:notice] = _('Your guidance is no longer published and will not be available to users.') + redirect_to(action: :admin_index) + end private def guidance_params diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index 42b2c6d..32211b3 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -1,73 +1,98 @@ class NotesController < ApplicationController + include ConditionalUserMailer + require "pp" after_action :verify_authorized respond_to :html - require "pp" - def create @note = Note.new - user_id = params[:new_note][:user_id] - @note.user_id = user_id - question_id = params[:new_note][:question_id] - plan_id = params[:new_note][:plan_id] + @note.user_id = params[:note][:user_id] - # create answer if we dont already have one - answer=nil # if defined within transaction block, was not accessable after - # ensure user has access to plan BEFORE creating/finding an answer - raise Pundit::NotAuthorizedError unless Plan.find(plan_id).readable_by?(user_id) + # create answer if we don't have one already + @answer = nil # if defined within the transaction block, was not accessable afterward + # ensure user has access to plan BEFORE creating/finding answer + raise Pundit::NotAuthorizedError unless Plan.find(params[:note][:plan_id]).readable_by?(@note.user_id) Answer.transaction do - answer = Answer.find_by(question_id: question_id, plan_id: plan_id) - if answer.blank? - answer = Answer.new - answer.plan_id = plan_id - answer.question_id = question_id - answer.user_id = user_id - answer.save! + @answer = Answer.find_by(plan_id: params[:note][:plan_id], question_id: params[:note][:question_id]) + if @answer.blank? + @answer = Answer.new + @answer.plan_id = params[:note][:plan_id] + @answer.question_id = params[:note][:question_id] + @answer.user_id = @note.user_id + @answer.save! end end - @note.answer = answer - @note.text = params["#{question_id}new_note_text"] + @note.answer = @answer + @note.text = params[:note][:text] authorize @note - @plan = answer.plan - @answer = answer - @question = Question.find(question_id) + @plan = @answer.plan + + @question = Question.find(params[:note][:question_id]) if @note.save @status = true - @notice = _('Comment was successfully created.') + answer = @note.answer + plan = answer.plan + owner = plan.owner + deliver_if(recipients: owner, key: 'users.new_comment') do |r| + UserMailer.new_comment(current_user, plan).deliver_now() + end + @notice = success_message(_('comment'), _('created')) + render(json: { + "notes" => { + "id" => params[:note][:question_id], + "html" => render_to_string(partial: 'layout', locals: {plan: @plan, question: @question, answer: @answer }, formats: [:html]) + }, + "title" => { + "id" => params[:note][:question_id], + "html" => render_to_string(partial: 'title', locals: { answer: @answer}, formats: [:html]) + } + }.to_json, status: :created) else @status = false @notice = failed_create_error(@note, _('note')) + render json: { + "msg" => @notice + }.to_json, status: :bad_request end - notes = answer.notes.all - @num_notes = notes.count end - - def update - @note = Note.find(params[:note][:id]) + @note = Note.find(params[:id]) authorize @note - @note.text = params["#{params[:note][:id]}_note_text"] + @note.text = params[:note][:text] @answer = @note.answer @question = @answer.question @plan = @answer.plan + question_id = @note.answer.question_id.to_s + if @note.update_attributes(params[:note]) - @notice = _('Comment was successfully saved.') + @notice = success_message(_('comment'), _('saved')) + render(json: { + "notes" => { + "id" => question_id, + "html" => render_to_string(partial: 'layout', locals: {plan: @plan, question: @question, answer: @answer }, formats: [:html]) + }, + "title" => { + "id" => question_id, + "html" => render_to_string(partial: 'title', locals: { answer: @answer}, formats: [:html]) + } + }.to_json, status: :ok) else @notice = failed_update_error(@note, _('note')) + render json: { + "msg" => @notice + }.to_json, status: :bad_request end end - - def archive - @note = Note.find(params[:note][:id]) + @note = Note.find(params[:id]) authorize @note @note.archived = true @note.archived_by = params[:note][:archived_by] @@ -76,10 +101,25 @@ @question = @answer.question @plan = @answer.plan + question_id = @note.answer.question_id.to_s + if @note.update_attributes(params[:note]) - @notice = _('Comment removed.') + @notice = success_message(_('comment'), _('removed')) + render(json: { + "notes" => { + "id" => question_id, + "html" => render_to_string(partial: 'layout', locals: {plan: @plan, question: @question, answer: @answer }, formats: [:html]) + }, + "title" => { + "id" => question_id, + "html" => render_to_string(partial: 'title', locals: { answer: @answer}, formats: [:html]) + } + }.to_json, status: :ok) else @notice = failed_destroy_error(@note, _('note')) + render json: { + "msg" => @notice + }.to_json, status: :bad_request end end end diff --git a/app/controllers/org_admin/plans_controller.rb b/app/controllers/org_admin/plans_controller.rb new file mode 100644 index 0000000..b8bd89d --- /dev/null +++ b/app/controllers/org_admin/plans_controller.rb @@ -0,0 +1,65 @@ +module OrgAdmin + class PlansController < ApplicationController + # GET org_admin/plans + def index + # Test auth directly and throw Pundit error sincePundit is unaware of namespacing + raise Pundit::NotAuthorizedError unless current_user.present? && current_user.can_org_admin? + + vals = Role.access_values_for(:reviewer) + @feedback_plans = Plan.joins(:roles).where('roles.user_id = ? and roles.access IN (?)', current_user.id, vals) + @plans = current_user.org.plans + end + + # GET org_admin/plans/:id/feedback_complete + def feedback_complete + plan = Plan.find(params[:id]) + # Test auth directly and throw Pundit error sincePundit is unaware of namespacing + raise Pundit::NotAuthorizedError unless current_user.present? && current_user.can_org_admin? + raise Pundit::NotAuthorizedError unless plan.reviewable_by?(current_user.id) + + if plan.complete_feedback(current_user) + redirect_to org_admin_plans_path, notice: _('%{plan_owner} has been notified that you have finished providing feedback') % { plan_owner: plan.owner.name(false) } + else + redirect_to org_admin_plans_path, alert: _('Unable to notify user that you have finished providing feedback.') + end + end + + # GET /org_admin/download_plans + def download_plans + # Test auth directly and throw Pundit error sincePundit is unaware of namespacing + raise Pundit::NotAuthorizedError unless current_user.present? && current_user.can_org_admin? + + org = current_user.org + file_name = org.name.gsub(/ /, "_") + header_cols = [ + "#{_('Project title')}", + "#{_('Template')}", + "#{_('Organisation')}", + "#{_('Owner name')}", + "#{_('Owner email')}", + "#{_('Updated')}", + "#{_('Visibility')}" + ] + + plans = CSV.generate do |csv| + csv << header_cols + org.plans.includes(template: :org).order(updated_at: :desc).each do |plan| + owner = plan.owner + csv << [ + "#{plan.title}", + "#{plan.template.title}", + "#{plan.owner.org.present? ? plan.owner.org.name : ''}", + "#{plan.owner.name(false)}", + "#{plan.owner.email}", + "#{l(plan.latest_update.to_date, formats: :short)}", + "#{Plan.visibility_message(plan.visibility.to_sym).capitalize}" + ] + end + end + + respond_to do |format| + format.csv { send_data plans, filename: "#{file_name}.csv" } + end + end + end +end \ No newline at end of file diff --git a/app/controllers/org_admin/templates_controller.rb b/app/controllers/org_admin/templates_controller.rb new file mode 100644 index 0000000..158016e --- /dev/null +++ b/app/controllers/org_admin/templates_controller.rb @@ -0,0 +1,417 @@ +module OrgAdmin + class TemplatesController < ApplicationController + include Paginable + include TemplateFilter + after_action :verify_authorized + + # GET /org_admin/templates + # ----------------------------------------------------- + def index + authorize Template + + # Apply scoping + all_templates_hash = apply_scoping(params[:scope] || 'all', false, true) + own_hash = apply_scoping(params[:scope] || 'all', false, false) + customizable_hash = apply_scoping(params[:scope] || 'all', true, false) + + # Apply pagination + all_templates_hash[:templates] = all_templates_hash[:templates].page(1) + own_hash[:templates] = own_hash[:templates].page(1) + customizable_hash[:templates] = customizable_hash[:templates].page(1) + + # Gather up all of the publication dates for the live versions of each template. + published = get_publication_dates(all_templates_hash[:scopes][:dmptemplate_ids]) + + render 'index', locals: { + all_templates: all_templates_hash[:templates], + customizable_templates: customizable_hash[:templates], + own_templates: own_hash[:templates], + customized_templates: customizable_hash[:customizations], + published: published, + current_org: current_user.org, + orgs: Org.all, + current_tab: params[:r], + scopes: { all: all_templates_hash[:scopes], orgs: own_hash[:scopes], funders: customizable_hash[:scopes] } + } + end + + # GET /org_admin/templates/new + # ----------------------------------------------------- + def new + authorize Template + @current_tab = params[:r] || 'all-templates' + end + + # POST /org_admin/templates + # ----------------------------------------------------- + def create + authorize Template + # creates a new template with version 0 and new dmptemplate_id + @template = Template.new(params[:template]) + @template.org_id = current_user.org.id + @template.description = params['template-desc'] + @template.links = (params["template-links"].present? ? JSON.parse(params["template-links"]) : {"funder": [], "sample_plan": []}) + + if @template.save + redirect_to edit_org_admin_template_path(@template), notice: success_message(@template.template_type, _('created')) + else + @hash = @template.to_hash + flash[:alert] = failed_create_error(@template, @template.template_type) + render action: "new" + end + end + + # GET /org_admin/templates/:id/edit + # ----------------------------------------------------- + def edit + @template = Template.includes(:org, phases: [sections: [questions: [:question_options, :question_format, :annotations]]]).find(params[:id]) + authorize @template + + @current = Template.current(@template.dmptemplate_id) + @current_tab = params[:r] || 'all-templates' + + if @template == @current + if @template.published? + new_version = @template.get_new_version + if !new_version.nil? + redirect_to(action: 'edit', id: new_version.id, r: @current_tab) + return + else + flash[:alert] = _("Unable to create a new version of this #{@template.template_type}. You are currently working with a published copy.") + end + end + else + flash[:notice] = _("You are viewing a historical version of this #{@template.template_type}. You will not be able to make changes.") + end + + # once the correct template has been generated, we convert it to hash + @template_hash = @template.to_hash + + render('container', + locals: { + partial_path: 'edit', + template: @template, + current: @current, + edit: @template == @current, + template_hash: @template_hash, + current_tab: @current_tab + }) + end + + # PUT /org_admin/templates/:id (AJAXable) + # ----------------------------------------------------- + def update + @template = Template.find(params[:id]) + authorize @template # NOTE if non-authorized error is raised, it performs a redirect to root_path and no JSON output is generated + + current = Template.current(@template.dmptemplate_id) + + # Only allow the current version to be updated + if current != @template + render(status: :forbidden, json: { msg: _("You can not edit a historical version of this #{@template.template_type}.")}) + else + template_links = nil + begin + template_links = JSON.parse(params["template-links"]) if params["template-links"].present? + rescue JSON::ParserError + render(status: :bad_request, json: { msg: _("Error parsing links for a #{@template.template_type}") }) + return + end + + # TODO dirty check at template model instead of here for reusability, i.e. method dirty? passing a template object + if @template.description != params["template-desc"] || + @template.title != params[:template][:title] || + @template.links != template_links + @template.dirty = true + end + + @template.description = params["template-desc"] + @template.links = template_links if template_links.present? + + # If the visibility checkbox is not checked and the user's org is a funder set the visibility to public + # otherwise default it to organisationally_visible + if current_user.org.funder? && params[:template_visibility].nil? + @template.visibility = Template.visibilities[:publicly_visible] + else + @template.visibility = Template.visibilities[:organisationally_visible] + end + + if @template.update_attributes(params[:template]) + render(status: :ok, json: { msg: success_message(@template.template_type, _('saved'))}) + else + # Note failed_update_error may return HTML tags (e.g.
) and therefore the client should parse them accordingly + render(status: :bad_request, json: { msg: failed_update_error(@template, @template.template_type)}) + end + end + end + + # DELETE /org_admin/templates/:id + # ----------------------------------------------------- + def destroy + @template = Template.find(params[:id]) + current_tab = params[:r] || 'all-templates' + authorize @template + + if @template.plans.length <= 0 + current = Template.current(@template.dmptemplate_id) + + # Only allow the current version to be destroyed + if current == @template + if @template.destroy + flash[:notice] = success_message(@template.template_type, _('removed')) + redirect_to org_admin_templates_path(r: current_tab) + else + @hash = @template.to_hash + flash[:alert] = failed_destroy_error(@template, @template.template_type) + redirect_to org_admin_templates_path(r: current_tab) + end + else + flash[:alert] = _("You cannot delete historical versions of this #{@template.template_type}.") + redirect_to org_admin_templates_path(r: current_tab) + end + else + flash[:alert] = _("You cannot delete a #{@template.template_type} that has been used to create plans.") + redirect_to org_admin_templates_path(r: current_tab) + end + end + + # GET /org_admin/templates/:id/history + # ----------------------------------------------------- + def history + @template = Template.find(params[:id]) + authorize @template + @templates = Template.where(dmptemplate_id: @template.dmptemplate_id) + @current = Template.current(@template.dmptemplate_id) + @current_tab = params[:r] || 'all-templates' + end + + # GET /org_admin/templates/:id/customize + # ----------------------------------------------------- + def customize + @template = Template.find(params[:id]) + @current_tab = params[:r] || 'all-templates' + authorize @template + + customisation = Template.deep_copy(@template) + customisation.org = current_user.org + customisation.version = 0 + customisation.customization_of = @template.dmptemplate_id + customisation.dmptemplate_id = loop do + random = rand 2147483647 + break random unless Template.exists?(dmptemplate_id: random) + end + customisation.dirty = true + customisation.save + + customisation.phases.includes(:sections, :questions).each do |phase| + phase.modifiable = false + phase.save! + phase.sections.each do |section| + section.modifiable = false + section.save! + section.questions.each do |question| + question.modifiable = false + question.save! + end + end + end + + redirect_to edit_org_admin_template_path(customisation, r: 'funder-templates') + end + + # GET /org_admin/templates/:id/transfer_customization + # the funder template's id is passed through here + # ----------------------------------------------------- + def transfer_customization + @template = Template.includes(:org).find(params[:id]) + @current_tab = params[:r] || 'all-templates' + authorize @template + new_customization = Template.deep_copy(@template) + new_customization.org_id = current_user.org_id + new_customization.published = false + new_customization.customization_of = @template.dmptemplate_id + new_customization.dirty = true + new_customization.phases.includes(sections: :questions).each do |phase| + phase.modifiable = false + phase.save + phase.sections.each do |section| + section.modifiable = false + section.save + section.questions.each do |question| + question.modifiable = false + question.save + end + end + end + customizations = Template.includes(:org, phases:[sections: [questions: :annotations]]).where(org_id: current_user.org_id, customization_of: @template.dmptemplate_id).order(version: :desc) + # existing version to port over + max_version = customizations.first + new_customization.dmptemplate_id = max_version.dmptemplate_id + new_customization.version = max_version.version + 1 + # here we rip the customizations out of the old template + # First, we find any customzed phases or sections + max_version.phases.each do |phase| + # check if the phase was added as a customization + if phase.modifiable + # deep copy the phase and add it to the template + phase_copy = Phase.deep_copy(phase) + phase_copy.number = new_customization.phases.length + 1 + phase_copy.template_id = new_customization.id + phase_copy.save! + else + # iterate over the sections to see if any of them are customizations + phase.sections.each do |section| + if section.modifiable + # this is a custom section + section_copy = Section.deep_copy(section) + customization_phase = new_customization.phases.includes(:sections).where(number: phase.number).first + section_copy.phase_id = customization_phase.id + # custom sections get added to the end + section_copy.number = customization_phase.sections.length + 1 + # section from phase with corresponding number in the main_template + section_copy.save! + else + # not a customized section, iterate over questions + customization_phase = new_customization.phases.includes(sections: [questions: :annotations]).where(number: phase.number).first + customization_section = customization_phase.sections.where(number: section.number).first + section.questions.each do |question| + # find corresponding question in new template + customization_question = customization_section.questions.where(number: question.number).first + # apply annotations + question.annotations.where(org_id: current_user.org_id).each do |annotation| + annotation_copy = Annotation.deep_copy(annotation) + annotation_copy.question_id = customization_question.id + annotation_copy.save! + end + end + end + end + end + end + new_customization.save + redirect_to edit_org_admin_template_path(new_customization, r: 'funder-templates') + end + + # PUT /org_admin/templates/:id/copy (AJAX) + # ----------------------------------------------------- + def copy + @template = Template.find(params[:id]) + current_tab = params[:r] || 'all-templates' + authorize @template + + new_copy = Template.deep_copy(@template) + new_copy.title = "Copy of " + @template.title + new_copy.version = 0 + new_copy.published = false + new_copy.dmptemplate_id = loop do + random = rand 2147483647 + break random unless Template.exists?(dmptemplate_id: random) + end + + if new_copy.save + flash[:notice] = "#{@template.template_type.capitalize} was successfully copied." + redirect_to edit_org_admin_template_path(id: new_copy.id, edit: true, r: 'organisation-templates'), notice: _('Information was successfully created.') + else + flash[:alert] = failed_create_error(new_copy, @template.template_type) + end + + end + + # GET /org_admin/templates/:id/publish (AJAX) TODO convert to PUT verb + # ----------------------------------------------------- + def publish + template = Template.find(params[:id]) + authorize template + current = Template.current(template.dmptemplate_id) + + # Only allow the current version to be updated + if current != template + redirect_to org_admin_templates_path, alert: _("You can not publish a historical version of this #{template.template_type}.") + + else + # Unpublish the older published version if there is one + live = Template.live(template.dmptemplate_id) + if !live.nil? and self != live + live.published = false + live.save! + end + # Set the dirty flag to false + template.dirty = false + template.published = true + template.save + + flash[:notice] = _("Your #{template.template_type} has been published and is now available to users.") + redirect_to "#{org_admin_templates_path}#{template.template_type == 'customisation' ? '#funder-templates' : '#organisation-templates'}" + end + end + + # GET /org_admin/templates/:id/unpublish (AJAX) TODO convert to PUT verb + # ----------------------------------------------------- + def unpublish + template = Template.find(params[:id]) + authorize template + + if template.nil? + flash[:alert] = _("That #{template.template_type} is not currently published.") + else + template.published = false + template.save + flash[:notice] = _("Your #{template.template_type} is no longer published. Users will not be able to create new DMPs for this #{template.template_type} until you re-publish it") + end + + redirect_to "#{org_admin_templates_path}#{template.template_type == 'customisation' ? '#funder-templates' : '#organisation-templates'}" + end + + # PUT /org_admin/template_options (AJAX) + # Collect all of the templates available for the org+funder combination + # -------------------------------------------------------------------------- + def template_options() + org_id = (plan_params[:org_id] == '-1' ? '' : plan_params[:org_id]) + funder_id = (plan_params[:funder_id] == '-1' ? '' : plan_params[:funder_id]) + authorize Template.new + + templates = [] + + if org_id.present? || funder_id.present? + unless funder_id.blank? + # Load the funder's template(s) + templates = Template.valid.publicly_visible.where(published: true, org_id: funder_id).to_a + + unless org_id.blank? + # Swap out any organisational cusotmizations of a funder template + templates.each do |tmplt| + customization = Template.valid.find_by(published: true, org_id: org_id, customization_of: tmplt.dmptemplate_id) + if customization.present? && tmplt.created_at < customization.created_at + templates.delete(tmplt) + templates << customization + end + end + end + end + + # If the no funder was specified OR the funder matches the org + if funder_id.blank? || funder_id == org_id + # Retrieve the Org's templates + templates << Template.organisationally_visible.valid.where(published: true, org_id: org_id, customization_of: nil).to_a + end + + templates = templates.flatten.uniq + end + + # If no templates were available use the default template + if templates.empty? + templates << Template.where(is_default: true, published: true).first + end + templates = (templates.count > 0 ? templates.sort{|x,y| x.title <=> y.title} : []) + + render json: {"templates": templates.collect{|t| {id: t.id, title: t.title} }}.to_json + end + + + # ====================================================== + private + def plan_params + params.require(:plan).permit(:org_id, :funder_id) + end + end +end \ No newline at end of file diff --git a/app/controllers/orgs_controller.rb b/app/controllers/orgs_controller.rb index d0f5a8e..82c83a8 100644 --- a/app/controllers/orgs_controller.rb +++ b/app/controllers/orgs_controller.rb @@ -1,20 +1,16 @@ class OrgsController < ApplicationController - after_action :verify_authorized + after_action :verify_authorized, except: ['shibboleth_ds', 'shibboleth_ds_passthru'] respond_to :html ## - # GET /organisations/1 - def admin_show - @org = Org.find(params[:id]) - authorize @org - end - - ## # GET /organisations/1/edit def admin_edit - @org = Org.find(params[:id]) - authorize @org - @languages = Language.all.order("name") + org = Org.find(params[:id]) + authorize org + languages = Language.all.order("name") + org.links = {"org": []} unless org.links.present? + render 'admin_edit', locals: {org: org, languages: languages, method: 'PUT', + url: admin_update_org_path(org) } end ## @@ -23,30 +19,91 @@ attrs = org_params @org = Org.find(params[:id]) authorize @org - @org.banner_text = params["org_banner_text"] - @org.logo = org_params[:logo] if org_params[:logo] - + @org.logo = attrs[:logo] if attrs[:logo] + tab = (attrs[:feedback_enabled].present? ? 'feedback' : 'profile') + if params[:org_links].present? + @org.links = JSON.parse(params[:org_links]) + end + begin - if @org.update_attributes(org_params) - redirect_to admin_show_org_path(params[:id]), notice: _('Organisation was successfully updated.') + # 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 + shib = IdentifierScheme.find_by(name: 'shibboleth') + shib_settings = @org.org_identifiers.select{ |ids| ids.identifier_scheme == shib}.first + + if params[:shib_id].present? || params[:shib_domain].present? + shib_settings = OrgIdentifier.new(org: @org, identifier_scheme: shib) unless shib_settings.present? + shib_settings.identifier = params[:shib_id] + shib_settings.attrs = {domain: params[:shib_domain]} + shib_settings.save + else + if shib_settings.present? + # The user cleared the shib values so delete the object + shib_settings.destroy + end + end + end + end + + if @org.update_attributes(attrs) + flash[:notice] = success_message(_('organisation'), _('saved')) + redirect_to "#{admin_edit_org_path(@org)}\##{tab}" else - # For some reason our custom validator returns as a string and not a hash like normal activerecord - # errors. We followed the example provided in the Rails guides when building the validator so - # its unclear why its doing this. Placing a check here for the data type. We should reasses though - # when doing a broader eval of the look/feel of the site and we come up with a standardized way of - # displaying errors - flash[:notice] = failed_update_error(@org, _('organisation')) - render action: "admin_edit" + failure = failed_update_error(@org, _('organisation')) if failure.blank? + redirect_to "#{admin_edit_org_path(@org)}\##{tab}", alert: failure end rescue Dragonfly::Job::Fetch::NotFound => dflye - flash[:notice] = _('There seems to be a problem with your logo. Please upload it again.') - render action: "admin_edit" + redirect_to "#{admin_edit_org_path(@org)}\##{tab}", alert: _('There seems to be a problem with your logo. Please upload it again.') + end + end + + # GET /orgs/shibboleth_ds + # ---------------------------------------------------------------- + def shibboleth_ds + redirect_to root_path unless current_user.nil? + + @user = User.new + # Display the custom Shibboleth discovery service page. + @orgs = Org.joins(:identifier_schemes).where('identifier_schemes.name = ?', 'shibboleth').sort{|x,y| x.name <=> y.name } + + if @orgs.empty? + flash[:alert] = _('No organisations are currently registered.') + redirect_to user_shibboleth_omniauth_authorize_path + end + end + + # POST /orgs/shibboleth_ds + # ---------------------------------------------------------------- + def shibboleth_ds_passthru + if !params[:org_name].blank? + session['org_id'] = params[:org_name] + + scheme = IdentifierScheme.find_by(name: 'shibboleth') + shib_entity = OrgIdentifier.where(org_id: params[:org_name], identifier_scheme: scheme) + + if !shib_entity.empty? + # Force SSL + url = "#{request.base_url.gsub('http:', 'https:')}#{Rails.application.config.shibboleth_login}" + target = "#{user_shibboleth_omniauth_callback_url.gsub('http:', 'https:')}" + + #initiate shibboleth login sequence + redirect_to "#{url}?target=#{target}&entityID=#{shib_entity.first.identifier}" + else + flash[:alert] = _('Your organisation does not seem to be properly configured.') + redirect_to shibboleth_ds_path + end + + else + flash[:notice] = _('Please choose an organisation') + redirect_to shibboleth_ds_path end end private def org_params - params.require(:org).permit(:name, :abbreviation, :target_url, :is_other, :banner_text, :language_id, - :region_id, :logo, :contact_email, :remove_logo) + params.require(:org).permit(:name, :abbreviation, :logo, :contact_email, :contact_name, :remove_logo, :org_type, + :feedback_enabled, :feedback_email_subject, :feedback_email_msg) end -end +end \ No newline at end of file diff --git a/app/controllers/paginable/guidance_groups_controller.rb b/app/controllers/paginable/guidance_groups_controller.rb new file mode 100644 index 0000000..a6400d1 --- /dev/null +++ b/app/controllers/paginable/guidance_groups_controller.rb @@ -0,0 +1,11 @@ +module Paginable + class GuidanceGroupsController < ApplicationController + include Paginable + # /paginable/guidance_groups/index/:page + def index + authorize(Guidance) + paginable_renderise(partial: 'index', + scope: GuidanceGroup.by_org(current_user.org)) + end + end +end \ No newline at end of file diff --git a/app/controllers/paginable/guidances_controller.rb b/app/controllers/paginable/guidances_controller.rb new file mode 100644 index 0000000..5887df5 --- /dev/null +++ b/app/controllers/paginable/guidances_controller.rb @@ -0,0 +1,11 @@ +module Paginable + class GuidancesController < ApplicationController + include Paginable + # /paginable/guidances/index/:page + def index + authorize(Guidance) + paginable_renderise(partial: 'index', + scope: Guidance.by_org(current_user.org).includes(:guidance_group, :themes)) + end + end +end \ No newline at end of file diff --git a/app/controllers/paginable/orgs_controller.rb b/app/controllers/paginable/orgs_controller.rb new file mode 100644 index 0000000..fc78fa6 --- /dev/null +++ b/app/controllers/paginable/orgs_controller.rb @@ -0,0 +1,11 @@ +class Paginable::OrgsController < ApplicationController + include Paginable + # /paginable/guidances/index/:page + def index + authorize(Org) + paginable_renderise( + partial: 'index', + scope: Org.includes(:templates, :users), + query_params: { sort_field: 'orgs.name', sort_direction: :asc }) + end +end diff --git a/app/controllers/paginable/plans_controller.rb b/app/controllers/paginable/plans_controller.rb new file mode 100644 index 0000000..8d29990 --- /dev/null +++ b/app/controllers/paginable/plans_controller.rb @@ -0,0 +1,26 @@ +class Paginable::PlansController < ApplicationController + include Paginable + # /paginable/plans/privately_visible/:page + def privately_visible + raise Pundit::NotAuthorizedError unless Paginable::PlanPolicy.new(current_user).privately_visible? + paginable_renderise(partial: 'privately_visible', scope: Plan.active(current_user)) + end + # GET /paginable/plans/organisationally_or_publicly_visible/:page + def organisationally_or_publicly_visible + raise Pundit::NotAuthorizedError unless Paginable::PlanPolicy.new(current_user).organisationally_or_publicly_visible? + paginable_renderise(partial: 'organisationally_or_publicly_visible', + scope: Plan.organisationally_or_publicly_visible(current_user), + query_params: { sort_field: 'plans.title', sort_direction: :asc }) + end + # GET /paginable/plans/publicly_visible/:page + def publicly_visible + paginable_renderise(partial: 'publicly_visible', + scope: Plan.publicly_visible) + end + # GET /paginable/plans/org_admin/:page + def org_admin + raise Pundit::NotAuthorizedError unless current_user.present? && current_user.can_org_admin? + paginable_renderise(partial: 'org_admin', + scope: current_user.org.plans) + end +end \ No newline at end of file diff --git a/app/controllers/paginable/templates_controller.rb b/app/controllers/paginable/templates_controller.rb new file mode 100644 index 0000000..b3370c1 --- /dev/null +++ b/app/controllers/paginable/templates_controller.rb @@ -0,0 +1,92 @@ +class Paginable::TemplatesController < ApplicationController + include Paginable + include TemplateFilter + + # GET /paginable/templates/all/:page (AJAX) + # ----------------------------------------------------- + def all + raise Pundit::NotAuthorizedError unless Paginable::TemplatePolicy.new(current_user).all? + # Apply scoping + hash = apply_scoping(params[:scope] || 'all', false, true) + + # Apply pagination + hash[:templates] = hash[:templates].page(params[:page]) if params[:page] != 'ALL' + + # Gather up all of the publication dates for the live versions of each template. + published = get_publication_dates(hash[:scopes][:dmptemplate_ids]) + + paginable_renderise partial: 'all', + scope: hash[:templates], + locals: { current_org: current_user.org.id, + customizations: hash[:customizations], + published: published, + scopes: hash[:scopes]} + end + + # GET /paginable/templates/funders/:page (AJAX) + # ----------------------------------------------------- + def funders + raise Pundit::NotAuthorizedError unless Paginable::TemplatePolicy.new(current_user).funders? + # Apply scoping + hash = apply_scoping(params[:scope] || 'all', true, false) + + # Apply pagination + hash[:templates] = hash[:templates].page(params[:page]) if params[:page] != 'ALL' + + # Gather up all of the publication dates for the live versions of each template. + published = get_publication_dates(hash[:scopes][:dmptemplate_ids]) + + paginable_renderise partial: 'funders', + scope: hash[:templates], + locals: { current_org: current_user.org.id, + customizations: hash[:customizations], + published: published, + scopes: hash[:scopes] } + end + + # GET /paginable/templates/orgs/:page (AJAX) + # ----------------------------------------------------- + def orgs + raise Pundit::NotAuthorizedError unless Paginable::TemplatePolicy.new(current_user).orgs? + # Apply scoping + hash = apply_scoping(params[:scope] || 'all', false, false) + + # Apply pagination + hash[:templates] = hash[:templates].page(params[:page]) if params[:page] != 'ALL' + + # Gather up all of the publication dates for the live versions of each template. + published = get_publication_dates(hash[:scopes][:dmptemplate_ids]) + + paginable_renderise partial: 'orgs', + scope: hash[:templates], + locals: { current_org: current_user.org.id, + customizations: hash[:customizations], + published: published, + scopes: hash[:scopes]} + end + + # GET /paginable/templates/publicly_visible/:page (AJAX) + # ----------------------------------------------------- + def publicly_visible + templates = Template.live(Template.families(Org.funder.pluck(:id)).pluck(:dmptemplate_id)).publicly_visible.pluck(:id) << + Template.where(is_default: true).valid.published.pluck(:id) + + paginable_renderise( + partial: 'publicly_visible', + scope: Template.includes(:org).where(id: templates.uniq.flatten).valid.published) + end + + # GET /paginable/templates/:id/history/:page (AJAX) + # ----------------------------------------------------- + def history + @template = Template.find(params[:id]) + authorize @template + @templates = Template.where(dmptemplate_id: @template.dmptemplate_id) + @current = Template.current(@template.dmptemplate_id) + paginable_renderise( + partial: 'history', + scope: @templates, + query_params: { sort_field: :version, sort_direction: :desc }, + locals: { current: @current }) + end +end diff --git a/app/controllers/paginable/themes_controller.rb b/app/controllers/paginable/themes_controller.rb new file mode 100644 index 0000000..76b9263 --- /dev/null +++ b/app/controllers/paginable/themes_controller.rb @@ -0,0 +1,10 @@ +module Paginable + class ThemesController < ApplicationController + include Paginable + # /paginable/themes/index/:page + def index + authorize(Theme) + paginable_renderise(partial: 'index', scope: Theme.all) + end + end +end \ No newline at end of file diff --git a/app/controllers/paginable/users_controller.rb b/app/controllers/paginable/users_controller.rb new file mode 100644 index 0000000..a39eb75 --- /dev/null +++ b/app/controllers/paginable/users_controller.rb @@ -0,0 +1,13 @@ +class Paginable::UsersController < ApplicationController + include Paginable + # /paginable/users/index/:page + def index + authorize User + if current_user.can_super_admin? + scope = User.includes(:roles) + else + scope = current_user.org.users.includes(:roles) + end + paginable_renderise(partial: 'index', scope: scope) + end +end \ No newline at end of file diff --git a/app/controllers/phases_controller.rb b/app/controllers/phases_controller.rb index 1fce879..14e3210 100644 --- a/app/controllers/phases_controller.rb +++ b/app/controllers/phases_controller.rb @@ -4,82 +4,30 @@ after_action :verify_authorized - # GET /plans/:plan_id/phases/:id/edit - def edit - @plan, @phase = Plan.load_for_phase(params[:plan_id], params[:id]) - # check if plan exists first - if @plan.nil? - raise Pundit::NotAuthorizedError, "Must have access to plan" - end - if @phase.nil? - raise Pundit::NotAuthorizedError, "Phase must belong to plan" - end - # authorization done on plan so found in plan_policy - authorize @plan - @answers = @plan.answers.reduce({}){ |m, a| m[a.question_id] = a; m } - @readonly = !@plan.editable_by?(current_user.id) - - # Now we need to get all the themed guidance for the plan. - # TODO: think this through again, there may be a better way to do this. - # - # Ultimately we are heading to a map from question id to theme to guidance. - # - # get the ids of the dynamically selected guidance groups - # and keep a map of them so we can extract the names later - guidance_groups_ids = @plan.guidance_groups.map{|pgg| pgg.id} - guidance_groups = GuidanceGroup.includes({guidances: :themes}).where(published: true, id: guidance_groups_ids) - - # create a map from theme to array of guidances - # where guidance is a hash with the text and the org name - theme_guidance = {} - - guidance_groups.includes(guidances:[:themes]).each do |guidance_group| - guidance_group.guidances.each do |guidance| - if guidance.published - guidance.themes.each do |theme| - title = theme.title - if !theme_guidance.has_key?(title) - theme_guidance[title] = Array.new - end - theme_guidance[title] << { - text: guidance.text, - org: guidance_group.name + ':' - } - end - end - end - end - - questions = [] - # Appends all the questions for a given phase into questions Array. - @phase.sections.each do |section| - section.questions.each do |question| - questions.push(question) - end - end - @question_guidance = {} - # Puts in question_guidance (key/value) entries where key is the question id and value is a hash. - # Each question id hash has (key/value) entries where key is a theme and value is an Array of {text, org} objects - # Example hash - # question_guidance = { question.id => - # { theme => [ {text: "......", org: "....."} ] } - # } - questions.each do |question| - qg = {} - question.themes.each do |t| - title = t.title - qg[title] = theme_guidance[title] if theme_guidance.has_key?(title) - end - @question_guidance[question.id] = qg - end - - if !user_signed_in? then - respond_to do |format| - format.html { redirect_to edit_user_registration_path } - end - end + # GET /plans/:plan_id/phases/:id/edit + def edit + plan = Plan.find(params[:plan_id]) + authorize plan + + plan, phase = Plan.load_for_phase(params[:plan_id], params[:id]) + + readonly = !plan.editable_by?(current_user.id) + + guidance_groups_ids = plan.guidance_groups.collect(&:id) + + guidance_groups = GuidanceGroup.where(published: true, id: guidance_groups_ids) + # Since the answers have been pre-fetched through plan (see Plan.load_for_phase) + # we create a hash whose keys are question id and value is the answer associated + answers = plan.answers.reduce({}){ |m, a| m[a.question_id] = a; m } + + render('/phases/edit', locals: { + plan: plan, phase: phase, readonly: readonly, + question_guidance: plan.guidance_by_question_as_hash, + guidance_groups: guidance_groups, + answers: answers }) end + # GET /plans/PLANID/phases/PHASEID/status.json def status @plan = Plan.eager_load(params[:plan_id]) @@ -93,34 +41,14 @@ end end - - #show and edit a phase of the template def admin_show - @phase = Phase.eager_load(:sections).find_by('phases.id = ?', params[:id]) + @phase = Phase.includes(:template, :sections).order(:number).find(params[:id]) authorize @phase @current = Template.current(@phase.template.dmptemplate_id) @edit = (@phase.template.org == current_user.org) && (@phase.template == @current) - #@edit = params[:edit] == "true" ? true : false - #verify if there are any sections if not create one - @sections = @phase.sections - if !@sections.any?() || @sections.count == 0 - @section = @phase.sections.build - @section.phase = @phase - @section.title = '' - @section.number = 1 - @section.published = true - @section.modifiable = true - @section.save - @new_sec = true - end - #verify if section_id has been passed, if so then open that section - if params.has_key?(:section_id) - @open = true - @section_id = params[:section_id].to_i - end if params.has_key?(:question_id) @question_id = params[:question_id].to_i end @@ -129,6 +57,20 @@ else @original_org = @phase.template.org end + + if @phase.template != @current + flash[:notice] = _('You are viewing a historical version of this template. You will not be able to make changes.') + end + + render('/org_admin/templates/container', + locals: { + partial_path: 'admin_show', + phase: @phase, + template: @phase.template, + edit: @edit, + current_section: params.has_key?(:section_id) ? params[:section_id].to_i : nil, + current_tab: params[:r] || 'all-templates' + }) end @@ -137,6 +79,7 @@ @phase = Phase.find(params[:id]) authorize @phase @template = @phase.template + @current_tab = params[:r] || 'all-templates' end @@ -147,6 +90,13 @@ @phase.template = @template authorize @phase @phase.number = @template.phases.count + 1 + render('/org_admin/templates/container', + locals: { + partial_path: 'admin_add', + template: @template, + edit: true, + current_tab: params[:r] || 'all-templates' + }) end @@ -157,15 +107,16 @@ @phase.description = params["phase-desc"] @phase.modifiable = true + @current_tab = params[:r] || 'all-templates' if @phase.save @phase.template.dirty = true @phase.template.save! - redirect_to admin_show_phase_path(id: @phase.id, edit: 'true'), notice: _('Information was successfully created.') + redirect_to admin_show_phase_path(id: @phase.id, r: @current_tab), notice: success_message(_('phase'), _('created')) else - flash[:notice] = failed_create_error(@phase, _('phase')) + flash[:alert] = failed_create_error(@phase, _('phase')) @template = @phase.template - render "admin_add" + redirect_to edit_org_admin_template_path(id: @phase.template_id, r: @current_tab) end end @@ -174,12 +125,14 @@ def admin_update @phase = Phase.find(params[:id]) authorize @phase + @phase.description = params["phase-desc"] + @current_tab = params[:r] || 'all-templates' if @phase.update_attributes(params[:phase]) @phase.template.dirty = true @phase.template.save! - redirect_to admin_show_phase_path(@phase), notice: _('Information was successfully updated.') + redirect_to admin_show_phase_path(@phase, r: @current_tab), notice: success_message(_('phase'), _('saved')) else @sections = @phase.sections @template = @phase.template @@ -189,13 +142,13 @@ @open = !params[:section_id].nil? @section_id = (params[:section_id].nil? ? nil : params[:section_id].to_i) @question_id = (params[:question_id].nil? ? nil : params[:question_id].to_i) - flash[:notice] = failed_update_error(@phase, _('phase')) + flash[:alert] = failed_update_error(@phase, _('phase')) if @phase.template.customization_of.present? @original_org = Template.where(dmptemplate_id: @phase.template.customization_of).first.org else @original_org = @phase.template.org end - render 'admin_show' + redirect_to admin_show_phase_path(@phase, r: @current_tab) end end @@ -204,11 +157,12 @@ @phase = Phase.find(params[:phase_id]) authorize @phase @template = @phase.template + @current_tab = params[:r] || 'all-templates' if @phase.destroy @template.dirty = true @template.save! - redirect_to admin_template_template_path(@template), notice: _('Information was successfully deleted.') + redirect_to edit_org_admin_template_path(@template, r: @current_tab), notice: success_message(_('phase'), _('deleted')) else @sections = @phase.sections @@ -218,14 +172,13 @@ @open = !params[:section_id].nil? @section_id = (params[:section_id].nil? ? nil : params[:section_id].to_i) @question_id = (params[:question_id].nil? ? nil : params[:question_id].to_i) - flash[:notice] = failed_destroy_error(@phase, _('phase')) + flash[:alert] = failed_destroy_error(@phase, _('phase')) if @phase.template.customization_of.present? @original_org = Template.where(dmptemplate_id: @phase.template.customization_of).first.org else @original_org = @phase.template.org end - render 'admin_show' + redirect_to admin_show_phase_path(@phase, r: @current_tab) end end - end diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index 3c713bb..ac9ec75 100644 --- a/app/controllers/plans_controller.rb +++ b/app/controllers/plans_controller.rb @@ -1,16 +1,16 @@ class PlansController < ApplicationController + include ConditionalUserMailer require 'pp' + helper PaginableHelper helper SettingsTemplateHelper - - after_action :verify_authorized + after_action :verify_authorized, except: [:overview] def index authorize Plan - @plans = current_user.plans + @plans = Plan.active(current_user).page(1) + @organisationally_or_publicly_visible = Plan.organisationally_or_publicly_visible(current_user).page(1) end - - # GET /plans/new # ------------------------------------------------------------------------------------ def new @@ -18,12 +18,14 @@ authorize @plan # Get all of the available funders and non-funder orgs - @funders = Org.funders.joins(:templates).where(templates: {published: true}).uniq.sort{|x,y| x.name <=> y.name } - @orgs = (Org.institutions + Org.managing_orgs).flatten.uniq.sort{|x,y| x.name <=> y.name } + @funders = Org.funder.joins(:templates).where(templates: {published: true}).uniq.sort{|x,y| x.name <=> y.name } + @orgs = (Org.organisation + Org.institution + Org.managing_orgs).flatten.uniq.sort{|x,y| x.name <=> y.name } # Get the current user's org @default_org = current_user.org if @orgs.include?(current_user.org) + flash[:notice] = "#{_('This is a')} #{_('test plan')}" if params[:test] + @is_test = params[:test] ||= false respond_to :html end @@ -33,21 +35,31 @@ @plan = Plan.new authorize @plan - @plan.principal_investigator = current_user.surname.blank? ? nil : "#{current_user.firstname} #{current_user.surname}" - @plan.data_contact = current_user.email - @plan.funder_name = plan_params[:funder_name] + # We set these ids to -1 on the page to trick ariatiseForm into allowing the autocomplete to be blank if + # the no org/funder checkboxes are checked off + org_id = (plan_params[:org_id] == '-1' ? '' : plan_params[:org_id]) + funder_id = (plan_params[:funder_id] == '-1' ? '' : plan_params[:funder_id]) - # If a template hasn't been identified look for the available templates + # If the template_id is blank then we need to look up the available templates and return JSON if plan_params[:template_id].blank? - template_options(plan_params[:org_id], plan_params[:funder_id]) - - # Return the 'Select a template' section + # Something went wrong there should always be a template id respond_to do |format| - format.js {} + flash[:alert] = _('Unable to identify a suitable template for your plan.') + format.html { redirect_to new_plan_path } end - - # Otherwise create the plan else + # Otherwise create the plan + @plan.principal_investigator = current_user.surname.blank? ? nil : "#{current_user.firstname} #{current_user.surname}" + @plan.principal_investigator_email = current_user.email + + orcid = current_user.identifier_for(IdentifierScheme.find_by(name: 'orcid')) + @plan.principal_investigator_identifier = orcid.identifier unless orcid.nil? + + @plan.funder_name = plan_params[:funder_name] + + @plan.visibility = (plan_params['visibility'].blank? ? Rails.application.config.default_plan_visibility : + plan_params[:visibility]) + @plan.template = Template.find(plan_params[:template_id]) if plan_params[:title].blank? @@ -60,144 +72,123 @@ if @plan.save @plan.assign_creator(current_user) - # pre-select org's guidance - ggs = GuidanceGroup.where(org_id: plan_params[:org_id], - optional_subset: false, - published: true) + # pre-select org's guidance and the default org's guidance + ids = (Org.managing_orgs << org_id).flatten.uniq + ggs = GuidanceGroup.where(org_id: ids, optional_subset: false, published: true) + if !ggs.blank? then @plan.guidance_groups << ggs end default = Template.default - msg = "#{_('Plan was successfully created.')} " + msg = "#{success_message(_('plan'), _('created'))}
" if !default.nil? && default == @plan.template # We used the generic/default template - msg += _('This plan is based on the default template.') + msg += " #{_('This plan is based on the default template.')}" elsif !@plan.template.customization_of.nil? # We used a customized version of the the funder template - msg += "#{_('This plan is based on the')} #{plan_params[:funder_name]} #{_('template with customisations by the')} #{plan_params[:org_name]}" + msg += " #{_('This plan is based on the')} #{plan_params[:funder_name]}: '#{@plan.template.title}' #{_('template with customisations by the')} #{plan_params[:org_name]}" else # We used the specified org's or funder's template - msg += "#{_('This plan is based on the')} #{@plan.template.org.name} template." + msg += " #{_('This plan is based on the')} #{@plan.template.org.name}: '#{@plan.template.title}' template." end - flash[:notice] = msg - respond_to do |format| - format.js { render js: "window.location='#{plan_url(@plan)}?editing=true'" } + flash[:notice] = msg + format.html { redirect_to plan_path(@plan) } end else # Something went wrong so report the issue to the user - flash[:notice] = failed_create_error(@plan, 'Plan') respond_to do |format| - format.js {} + flash[:alert] = failed_create_error(@plan, 'Plan') + format.html { redirect_to new_plan_path } end end end end - - # GET /plans/show def show @plan = Plan.eager_load(params[:id]) authorize @plan + + @visibility = @plan.visibility.present? ? @plan.visibility.to_s : Rails.application.config.default_plan_visibility + @editing = (!params[:editing].nil? && @plan.administerable_by?(current_user.id)) # Get all Guidance Groups applicable for the plan and group them by org @all_guidance_groups = @plan.get_guidance_group_options @all_ggs_grouped_by_org = @all_guidance_groups.sort.group_by(&:org) + @selected_guidance_groups = @plan.guidance_groups # Important ones come first on the page - we grab the user's org's GGs and "Organisation" org type GGs @important_ggs = [] - @important_ggs << [current_user.org, @all_ggs_grouped_by_org.delete(current_user.org)] + + @important_ggs << [current_user.org, @all_ggs_grouped_by_org[current_user.org]] if @all_ggs_grouped_by_org.include?(current_user.org) @all_ggs_grouped_by_org.each do |org, ggs| if org.organisation? @important_ggs << [org,ggs] - @all_ggs_grouped_by_org.delete(org) + end + + # If this is one of the already selected guidance groups its important! + if !(ggs & @selected_guidance_groups).empty? + @important_ggs << [org,ggs] unless @important_ggs.include?([org,ggs]) end end # Sort the rest by org name for the accordion - @all_ggs_grouped_by_org = @all_ggs_grouped_by_org.sort_by {|org,gg| org.name} + @important_ggs = @important_ggs.sort_by{|org,gg| (org.nil? ? '' : org.name)} + @all_ggs_grouped_by_org = @all_ggs_grouped_by_org.sort_by {|org,gg| (org.nil? ? '' : org.name)} + @selected_guidance_groups = @selected_guidance_groups.collect{|gg| gg.id} - @selected_guidance_groups = @plan.guidance_groups.pluck(:id) @based_on = (@plan.template.customization_of.nil? ? @plan.template : Template.where(dmptemplate: @plan.template.customization_of).first) respond_to :html end - - - # we can go into this with the user able to edit or not able to edit - # the same edit form gets rendered but then different partials get used - # to render the answers depending on whether it is readonly or not - # - # we may or may not have a phase param. - # if we have none then we are editing/displaying the plan details - # if we have a phase then we are editing that phase. - # - # GET /plans/1/edit - def edit - @plan = Plan.find(params[:id]) - authorize @plan - # If there was no phase specified use the template's 1st phase - @phase = (params[:phase].nil? ? @plan.template.phases.first : Phase.find(params[:phase])) - @readonly = !@plan.editable_by?(current_user.id) - respond_to :html - end - - # PUT /plans/1 # PUT /plans/1.json def update @plan = Plan.find(params[:id]) authorize @plan + attrs = plan_params respond_to do |format| - if @plan.update_attributes(params[:plan]) - format.html { redirect_to @plan, :editing => false, notice: _('Plan was successfully updated.') } - format.json { head :no_content } - else - flash[:notice] = failed_update_error(@plan, _('plan')) + begin + # Save the guidance group selections + guidance_group_ids = params[:guidance_group_ids].blank? ? [] : params[:guidance_group_ids].map(&:to_i).uniq + @plan.guidance_groups = GuidanceGroup.where(id: guidance_group_ids) + @plan.save + + if @plan.update_attributes(attrs) + format.html { redirect_to @plan, :editing => false, notice: success_message(_('plan'), _('saved')) } + format.json {render json: {code: 1, msg: success_message(_('plan'), _('saved'))}} + else + flash[:alert] = failed_update_error(@plan, _('plan')) + format.html { render action: "edit" } + format.json {render json: {code: 0, msg: flash[:alert]}} + end + + rescue Exception + flash[:alert] = failed_update_error(@plan, _('plan')) format.html { render action: "edit" } + format.json {render json: {code: 0, msg: flash[:alert]}} end end end - - - def update_guidance_choices - @plan = Plan.find(params[:id]) - authorize @plan - guidance_group_ids = params[:guidance_group_ids].blank? ? [] : params[:guidance_group_ids].map(&:to_i) - all_guidance_groups = @plan.get_guidance_group_options - plan_groups = @plan.guidance_groups - guidance_groups = GuidanceGroup.where( id: guidance_group_ids) - all_guidance_groups.each do |group| - # case where plan group exists but not in selection - if plan_groups.include?(group) && ! guidance_groups.include?(group) - # remove from plan groups - @plan.guidance_groups.delete(group) - end - # case where plan group dosent exist and in selection - if !plan_groups.include?(group) && guidance_groups.include?(group) - # add to plan groups - @plan.guidance_groups << group - end - end - @plan.save - flash[:notice] = _('Guidance choices saved.') - redirect_to action: "show" - end - def share @plan = Plan.find(params[:id]) - authorize @plan - #@plan_data = @plan.to_hash + if @plan.present? + authorize @plan + # Get the roles where the user is not a reviewer + @plan_roles = @plan.roles.select{ |r| !r.reviewer? } + else + redirect_to(plans_path) + end end @@ -206,11 +197,11 @@ authorize @plan if @plan.destroy respond_to do |format| - format.html { redirect_to plans_url, notice: _('Plan was successfully deleted.') } + format.html { redirect_to plans_url, notice: success_message(_('plan'), _('deleted')) } end else respond_to do |format| - flash[:notice] = failed_create_error(@plan, _('plan')) + flash[:alert] = failed_create_error(@plan, _('plan')) format.html { render action: "edit" } end end @@ -240,68 +231,135 @@ end end - def show_export + def download @plan = Plan.find(params[:id]) authorize @plan - render 'show_export' + @phase_options = @plan.phases.order(:number).pluck(:title,:id) + @export_settings = @plan.settings(:export) + render 'download' end def export - @plan = Plan.find(params[:id]) + @plan = Plan.includes(:answers).find(params[:id]) authorize @plan - # If no format is specified, default to PDF - params[:format] = 'pdf' if params[:format].nil? + @show_coversheet = params[:export][:project_details].present? + @show_sections_questions = params[:export][:question_headings].present? + @show_unanswered = params[:export][:unanswered_questions].present? + @public_plan = false - @exported_plan = ExportedPlan.new.tap do |ep| - ep.plan = @plan - ep.phase_id = params[:phase_id] - ep.user = current_user - ep.format = params[:format].to_sym - plan_settings = @plan.settings(:export) + @hash = @plan.as_pdf(@show_coversheet) + @formatting = params[:export][:formatting] || @plan.settings(:export).formatting + file_name = @plan.title.gsub(/ /, "_") - Settings::Template::DEFAULT_SETTINGS.each do |key, value| - ep.settings(:export).send("#{key}=", plan_settings.send(key)) + + respond_to do |format| + format.html { render layout: false } + format.csv { send_data @plan.as_csv(@show_sections_questions), filename: "#{file_name}.csv" } + format.text { send_data render_to_string(partial: 'shared/export/plan_txt'), filename: "#{file_name}.txt" } + format.docx { render docx: "#{file_name}.docx", content: render_to_string(partial: 'shared/export/plan') } + format.pdf do + render pdf: file_name, + margin: @formatting[:margin], + footer: { + center: _('Created using the %{application_name}. Last modified %{date}') % {application_name: Rails.configuration.branding[:application][:name], date: l(@plan.updated_at.to_date, formats: :short)}, + font_size: 8, + spacing: (Integer(@formatting[:margin][:bottom]) / 2) - 4, + right: '[page] of [topage]' + } end end + end + + + def duplicate + plan = Plan.find(params[:id]) + authorize plan + @plan = Plan.deep_copy(plan) + respond_to do |format| + if @plan.save + @plan.assign_creator(current_user) + format.html { redirect_to @plan, notice: success_message(_('plan'), _('copied')) } + else + format.html { redirect_to plans_path, alert: failed_create_error(@plan, 'Plan') } + end + end + end + + # POST /plans/:id/visibility + def visibility + plan = Plan.find(params[:id]) + if plan.present? + authorize plan + if plan.visibility_allowed? + plan.visibility = plan_params[:visibility] + if plan.save + deliver_if(recipients: plan.owner_and_coowners, key: 'owners_and_coowners.visibility_changed') do |r| + UserMailer.plan_visibility(r,plan).deliver_now() + end + render status: :ok, json: { msg: success_message(_('plan\'s visibility'), _('changed')) } + else + render status: :internal_server_error, json: { msg: _('Error raised while saving the visibility for plan id %{plan_id}') %{ :plan_id => params[:id]} } + end + else + render status: :forbidden, json: { + msg: _('Unable to change the plan\'s status since it is needed at least '\ + '%{percentage} percentage responded') %{ :percentage => Rails.application.config.default_plan_percentage_answered } } + end + else + render status: :not_found, json: { msg: _('Unable to find plan id %{plan_id}') %{ :plan_id => params[:id]} } + end + end + + def set_test + plan = Plan.find(params[:id]) + authorize plan + plan.visibility = (params[:is_test] === "1" ? :is_test : :privately_visible) + if plan.save + render json: {code: 1, msg: (plan.is_test? ? _('Your project is now a test.') : _('Your project is no longer a test.') )} + else + render status: :bad_request, json: {code: 0, msg: _("Unable to change the plan's test status")} + end + end + + def request_feedback + plan = Plan.find(params[:id]) + authorize plan + alert = _('Unable to submit your request for feedback at this time.') begin - @exported_plan.save! - file_name = @exported_plan.settings(:export)[:value]['title'].gsub(/ /, "_") - - respond_to do |format| - format.html - format.csv { send_data @exported_plan.as_csv, filename: "#{file_name}.csv" } - format.text { send_data @exported_plan.as_txt, filename: "#{file_name}.txt" } - format.docx { render docx: 'export', filename: "#{file_name}.docx" } - format.pdf do - @formatting = @plan.settings(:export).formatting - render pdf: file_name, - margin: @formatting[:margin], - footer: { - center: _('This document was generated by %{application_name}') % {application_name: Rails.configuration.branding[:application][:name]}, - font_size: 8, - spacing: (@formatting[:margin][:bottom] / 2) - 4, - right: '[page] of [topage]' - } - end - end - rescue ActiveRecord::RecordInvalid => e - redirect_to show_export_plan_path(@plan), notice: _('%{format} is not a valid exporting format. Available formats to export are %{available_formats}.') % - {format: params[:format], available_formats: ExportedPlan::VALID_FORMATS.to_s} + if plan.request_feedback(current_user) + redirect_to share_plan_path(plan), notice: _('Your request for feedback has been submitted.') + else + redirect_to share_plan_path(plan), alert: alert + end + rescue Exception + redirect_to share_plan_path(plan), alert: alert end end - + def overview + begin + plan = Plan.overview(params[:id]) + authorize plan + render(:overview, locals: { plan: plan }) + rescue ActiveRecord::RecordNotFound + flash[:alert] = _('There is no plan associated with id %{id}') %{ :id => params[:id] } + redirect_to(action: :index) + end + end private - def plan_params - params.require(:plan).permit(:org_id, :org_name, :funder_id, :funder_name, :template_id, :title) + params.require(:plan).permit(:org_id, :org_name, :funder_id, :funder_name, :template_id, :title, :visibility, + :grant_number, :description, :identifier, :principal_investigator, + :principal_investigator_email, :principal_investigator_identifier, + :data_contact, :data_contact_email, :data_contact_phone, :guidance_group_ids) end + # different versions of the same template have the same dmptemplate_id # but different version numbers so for each set of templates with the # same dmptemplate_id choose the highest version number. @@ -364,50 +422,4 @@ end plan.delete(src_plan_key) end - - # Collect all of the templates available for the org+funder combination - # -------------------------------------------------------------------------- - def template_options(org_id, funder_id) - @templates = [] - - if org_id.present? || funder_id.present? - if funder_id.blank? - # Load the org's template(s) - if org_id.present? - org = Org.find(org_id) - @templates = Template.valid.where(published: true, org: org, customization_of: nil).to_a - @msg = _("We found multiple DMP templates corresponding to the research organisation.") if @templates.count > 1 - end - - else - funder = Org.find(funder_id) - # Load the funder's template(s) - @templates = Template.valid.where(published: true, org: funder).to_a - - if org_id.present? - org = Org.find(org_id) - - # Swap out any organisational cusotmizations of a funder template - @templates.each do |tmplt| - customization = Template.valid.find_by(published: true, org: org, customization_of: tmplt.dmptemplate_id) - if customization.present? && tmplt.updated_at < customization.created_at - @templates.delete(tmplt) - @templates << customization - end - end - end - - @msg = _("We found multiple DMP templates corresponding to the funder.") if @templates.count > 1 - end - end - - # If no templates were available use the generic templates - if @templates.empty? - @msg = _("Using the generic Data Management Plan") - @templates << Template.default - end - - @templates = @templates.sort{|x,y| x.title <=> y.title } if @templates.count > 1 - end - end diff --git a/app/controllers/public_pages_controller.rb b/app/controllers/public_pages_controller.rb new file mode 100644 index 0000000..9307c4a --- /dev/null +++ b/app/controllers/public_pages_controller.rb @@ -0,0 +1,86 @@ +class PublicPagesController < ApplicationController + after_action :verify_authorized, except: [:template_index, :plan_index] + + # GET template_index + # ----------------------------------------------------- + def template_index + templates = Template.live(Template.families(Org.funder.pluck(:id)).pluck(:dmptemplate_id)).publicly_visible.pluck(:id) << + Template.where(is_default: true).valid.published.pluck(:id) + @templates = Template.includes(:org).where(id: templates.uniq.flatten).valid.published.order(title: :asc).page(1) + end + + # GET template_export/:id + # ----------------------------------------------------- + def template_export + # only export live templates, id passed is dmptemplate_id + @template = Template.live(params[:id]) + # covers authorization for this action. Pundit dosent support passing objects into scoped policies + raise Pundit::NotAuthorizedError unless PublicPagePolicy.new( @template).template_export? + skip_authorization + # 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(/ /, "_") + respond_to do |format| + format.docx { render docx: 'template_export', filename: "#{file_name}.docx" } + format.pdf do + render pdf: file_name, + margin: @formatting[:margin], + footer: { + center: _('Template created using the %{application_name}. 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]' + } + end + end + rescue ActiveRecord::RecordInvalid => e # What scenario is this triggered in? it's common to our export pages + #send back to public_index page + redirect_to public_templates_path, alert: _('Unable to download the DMP Template at this time.') + end + + end + + # GET plan_export/:id + # ------------------------------------------------------------- + def plan_export + @plan = Plan.includes(:answers).find(params[:id]) + # covers authorization for this action. Pundit dosent support passing objects into scoped policies + raise Pundit::NotAuthorizedError unless PublicPagePolicy.new(@plan, current_user).plan_organisationally_exportable? || PublicPagePolicy.new(@plan).plan_export? + skip_authorization + + @show_coversheet = true + @show_sections_questions = true + @show_unanswered = true + @public_plan = true + + @hash = @plan.as_pdf(@show_coversheet) + @formatting = @plan.settings(:export).formatting + file_name = @plan.title.gsub(/ /, "_") + + respond_to do |format| + format.html + format.csv { send_data @exported_plan.as_csv(@sections, @unanswered_question, @question_headings), filename: "#{file_name}.csv" } + format.text { send_data @exported_plan.as_txt(@sections, @unanswered_question, @question_headings, @show_details), filename: "#{file_name}.txt" } + format.docx { render docx: 'export', filename: "#{file_name}.docx" } + format.pdf do + render pdf: file_name, + margin: @formatting[:margin], + footer: { + center: _('Created using the %{application_name}. Last modified %{date}') % {application_name: Rails.configuration.branding[:application][:name], date: l(@plan.updated_at.to_date, formats: :short)}, + font_size: 8, + spacing: (@formatting[:margin][:bottom] / 2) - 4, + right: '[page] of [topage]' + } + end + end + end + + # GET /plans_index + # ------------------------------------------------------------------------------------ + def plan_index + @plans = Plan.publicly_visible.order(:title => :asc).page(1) + end +end diff --git a/app/controllers/question_formats_controller.rb b/app/controllers/question_formats_controller.rb new file mode 100644 index 0000000..ced3624 --- /dev/null +++ b/app/controllers/question_formats_controller.rb @@ -0,0 +1,10 @@ +class QuestionFormatsController < ApplicationController + # do we need authorizaton on this? it will only return the URL for the rda api + # down the line we will add more methods for other external api's + def rda_api_address + render json: { + 'url' => QuestionFormat.rda_metadata.first.description + }.to_json + end + +end diff --git a/app/controllers/questions_controller.rb b/app/controllers/questions_controller.rb index f3250bb..292d970 100644 --- a/app/controllers/questions_controller.rb +++ b/app/controllers/questions_controller.rb @@ -8,6 +8,7 @@ @question = Question.new(question_params) authorize @question @question.modifiable = true + current_tab = params[:r] || 'all-templates' if @question.question_format.textfield? @question.default_value = params["question-default-value-textfield"] elsif @question.question_format.textarea? @@ -24,7 +25,7 @@ guidance = Annotation.new({question_id: @question.id, org_id: current_user.org_id, text: params[:guidance], type: Annotation.types[:guidance]}) guidance.save end - redirect_to admin_show_phase_path(id: @question.section.phase_id, section_id: @question.section_id, question_id: @question.id, edit: 'true'), notice: _('Information was successfully created.') + redirect_to admin_show_phase_path(id: @question.section.phase_id, section_id: @question.section_id, question_id: @question.id, r: current_tab), notice: success_message(_('question'), _('created')) else @edit = (@question.section.phase.template.org == current_user.org) @open = true @@ -34,16 +35,16 @@ @section_id = @question.section.id @question_id = @question.id - flash[:notice] = failed_create_error(@question, _('question')) + flash[:alert] = failed_create_error(@question, _('question')) if @phase.template.customization_of.present? @original_org = Template.where(dmptemplate_id: @phase.template.customization_of).first.org else @original_org = @phase.template.org end - render template: 'phases/admin_show' + redirect_to admin_show_phase_path(id: @question.section.phase_id, section_id: @question.section_id, r: current_tab) end rescue ActionController::ParameterMissing => e - flash[:notice] = e.message + flash[:alert] = e.message end end @@ -51,26 +52,31 @@ def admin_update @question = Question.find(params[:id]) authorize @question + guidance = @question.get_guidance_annotation(current_user.org_id) + current_tab = params[:r] || 'all-templates' if params["question-guidance-#{params[:id]}"].present? - if guidance.blank? - guidance = @question.annotations.build - guidance.type = :guidance - guidance.org_id = current_user.org_id + unless guidance.present? + guidance = Annotation.new(type: :guidance, org_id: current_user.org_id, question_id: @question.id) end guidance.text = params["question-guidance-#{params[:id]}"] guidance.save + else + # The user cleared out the guidance value so delete the record + guidance.destroy! if guidance.present? end example_answer = @question.get_example_answer(current_user.org_id) if params["question"]["annotations_attributes"].present? && params["question"]["annotations_attributes"]["0"]["id"].present? - if example_answer.blank? - example_answer = @question.annotations.build - example_answer.type = :example_answer - example_answer.org_id = current_user.org_id + unless example_answer.present? + example_answer = Annotation.new(type: :example_answer, org_id: current_user.org_id, question_id: @question.id) end example_answer.text = params["question"]["annotations_attributes"]["0"]["text"] example_answer.save - end + else + # The user cleared out the example answer value so delete the record + example_answer.destroy if example_answer.present? + end + if @question.question_format.textfield? @question.default_value = params["question-default-value-textfield"] elsif @question.question_format.textarea? @@ -79,11 +85,15 @@ @section = @question.section @phase = @section.phase template = @phase.template - if @question.update_attributes(params[:question]) + + attrs = params[:question] + attrs[:theme_ids] = [] unless attrs[:theme_ids] + + if @question.update_attributes(attrs) @phase.template.dirty = true @phase.template.save! - redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, question_id: @question.id, edit: 'true'), notice: _('Information was successfully updated.') + redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, question_id: @question.id, r: current_tab), notice: success_message(_('question'), _('saved')) else @edit = (@phase.template.org == current_user.org) @open = true @@ -91,13 +101,13 @@ @section_id = @section.id @question_id = @question.id - flash[:notice] = failed_update_error(@question, _('question')) + flash[:alert] = failed_update_error(@question, _('question')) if @phase.template.customization_of.present? @original_org = Template.where(dmptemplate_id: @phase.template.customization_of).first.org else @original_org = @phase.template.org end - render template: 'phases/admin_show' + redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, question_id: @question.id, r: current_tab) end end @@ -107,13 +117,14 @@ authorize @question @section = @question.section @phase = @section.phase + current_tab = params[:r] || 'all-templates' if @question.destroy @phase.template.dirty = true @phase.template.save! - redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, edit: 'true'), notice: _('Information was successfully deleted.') + redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, r: current_tab), notice: success_message(_('question'), _('deleted')) else - redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, edit: 'true'), notice: failed_destroy_error(@question, 'question') + redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, r: current_tab), alert: failed_destroy_error(@question, 'question') end end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index a628929..aa0380d 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -2,11 +2,17 @@ class RegistrationsController < Devise::RegistrationsController def edit - @languages = Language.all.order("name") + @user = current_user + @prefs = @user.get_preferences(:email) + @languages = Language.sorted_by_abbreviation @orgs = Org.where(parent_id: nil).order("name") @other_organisations = Org.where(parent_id: nil, is_other: true).pluck(:id) @identifier_schemes = IdentifierScheme.where(active: true).order(:name) @default_org = current_user.org + + if !@prefs + flash[:alert] = 'No default preferences found (should be in branding.yml).' + end end # GET /resource @@ -15,19 +21,18 @@ IdentifierScheme.all.each do |scheme| oauth = session["devise.#{scheme.name.downcase}_data"] unless session["devise.#{scheme.name.downcase}_data"].nil? end - + @user = User.new - + unless oauth.nil? # The OAuth provider could not be determined or there was no unique UID! - if oauth[:provider].nil? || oauth[:uid].nil? - flash[:notice] = t('identifier_schemes.new_login_failure') - + if oauth['provider'].nil? || oauth['uid'].nil? + # flash[:alert] = _('We were unable to verify your account. Please use the following form to create a new account. You will be able to link your new account afterward.') else # Connect the new user with the identifier sent back by the OAuth provider - flash[:notice] = t('identifier_schemes.new_login_success') - UserIdentifier.create(identifier_scheme: oauth[:provider].upcase, - identifier: oauth[:uid], + flash[:notice] = _('Please make a choice below. After linking your details to a %{application_name} account, you will be able to sign in directly with your institutional credentials.') % {application_name: Rails.configuration.branding[:application][:name]} + UserIdentifier.create(identifier_scheme: IdentifierScheme.find_by(name: oauth['provider'].downcase), + identifier: oauth['uid'], user: @user) end end @@ -35,48 +40,78 @@ # POST /resource def create - #logger.debug "#{sign_up_params}" - if sign_up_params[:accept_terms] != "1" then + oauth = {provider: nil, uid: nil} + IdentifierScheme.all.each do |scheme| + oauth = session["devise.#{scheme.name.downcase}_data"] unless session["devise.#{scheme.name.downcase}_data"].nil? + end + + if !sign_up_params[:accept_terms] redirect_to after_sign_up_error_path_for(resource), alert: _('You must accept the terms and conditions to register.') + elsif params[:user][:org_id].blank? && params[:user][:other_organisation].blank? + redirect_to after_sign_up_error_path_for(resource), alert: _('Please select an organisation from the list, or enter your organisation\'s name.') else - existing_user = User.find_by_email(sign_up_params[:email]) - if !existing_user.nil? # If email exists - if (existing_user.password == "" || existing_user.password.nil?) && existing_user.confirmed_at.nil? # If user has not accepted invitation yet - existing_user.destroy # Only solution for now - super + existing_user = User.where_case_insensitive('email', sign_up_params[:email]).first + if existing_user.present? + if existing_user.invitation_token.present? && !existing_user.accept_terms? + existing_user.destroy # Destroys the existing user since the accept terms are nil/false. and they have an invitation + # Note any existing role for that user will be deleted too. Added to accommodate issue at: + # https://github.com/DMPRoadmap/roadmap/issues/322 when invited user creates an account outside the invite workflow else redirect_to after_sign_up_error_path_for(resource), alert: _('That email address is already registered.') + return end - else + end + if params[:user][:org_id].blank? + other_org = Org.find_by(is_other: true) + if other_org.nil? + redirect_to(after_sign_up_error_path_for(resource), alert: _('You cannot be assigned to other organisation since that option does not exist in the system. Please contact your system administrators.')) and return + end + params[:user][:org_id] = other_org.id + end build_resource(sign_up_params) if resource.save if resource.active_for_authentication? set_flash_message :notice, :signed_up if is_navigational_format? sign_up(resource_name, resource) UserMailer.welcome_notification(current_user).deliver + unless oauth.nil? + # The OAuth provider could not be determined or there was no unique UID! + unless oauth['provider'].nil? || oauth['uid'].nil? + prov = IdentifierScheme.find_by(name: oauth['provider'].downcase) + # Until we enable ORCID signups + if prov.name == 'shibboleth' + UserIdentifier.create(identifier_scheme: prov, + identifier: oauth['uid'], + user: @user) + flash[:notice] = _('Welcome! You have signed up successfully with your institutional credentials. You will now be able to access your account with them.') + end + end + end respond_with resource, location: after_sign_up_path_for(resource) else set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format? - #expire_session_data_after_sign_in! <-- DEPRECATED BY DEVISE respond_with resource, location: after_inactive_sign_up_path_for(resource) end else clean_up_passwords resource redirect_to after_sign_up_error_path_for(resource), alert: _('Error processing registration. Please check that you have entered a valid email address and that your chosen password is at least 8 characters long.') end - end end end - def update if user_signed_in? then + @prefs = @user.get_preferences(:email) @orgs = Org.where(parent_id: nil).order("name") @default_org = current_user.org @other_organisations = Org.where(parent_id: nil, is_other: true).pluck(:id) @identifier_schemes = IdentifierScheme.where(active: true).order(:name) @languages = Language.sorted_by_abbreviation - do_update(require_password=needs_password?(current_user, params)) + if params[:skip_personal_details] == "true" + do_update_password(current_user, params) + else + do_update(require_password=needs_password?(current_user, params)) + end else render(:file => File.join(Rails.root, 'public/403.html'), :status => 403, :layout => false) end @@ -107,34 +142,23 @@ message +=_('Please enter a Last name.') + ' ' mandatory_params &&= false end - if params[:user][:org_id].blank? - message += _('Please select an organisation, or select Other.') + if params[:user][:org_id].blank? && params[:user][:other_organisation].blank? + message += _('Please select an organisation from the list, or enter your organisation\'s name.') mandatory_params &&= false end if mandatory_params # has the user entered all the details if require_password # user is changing email or password if current_user.email != params[:user][:email] # if user is changing email - if params[:user][:current_password].blank? # password needs to be present + if params[:user][:password].blank? # password needs to be present message = _('Please enter your password to change email address.') successfully_updated = false else successfully_updated = current_user.update_with_password(password_update) end - elsif params[:user][:password].present? # if user is changing password - successfully_updated = false # shared across first 3 conditions - if params[:user][:current_password].blank? - message = _('Please enter your current password') - elsif params[:user][:password_confirmation].blank? - message = _('Please enter a password confirmation') - elsif params[:user][:password] != params[:user][:password_confirmation] - message = _('Password and comfirmation must match') - else - successfully_updated = current_user.update_with_password(password_update) - end - else # potentially unreachable... but I dont like to leave off the else - successfully_updated = current_user.update_with_password(password_update) + else # This case is never reached since this method when called with require_password = true is because the email changed. The case for password changed goes to do_update_password instead + successfully_updated = current_user.update_without_password(update_params) end - else # password not required + else # password not required successfully_updated = current_user.update_without_password(update_params) end else @@ -154,18 +178,43 @@ end session[:locale] = current_user.get_locale unless current_user.get_locale.nil? set_gettext_locale #Method defined at controllers/application_controller.rb - set_flash_message :notice, _('Details successfully updated.') + set_flash_message :notice, success_message(_('profile'), _('saved')) sign_in current_user, bypass: true # Sign in the user bypassing validation in case his password changed - redirect_to edit_user_registration_path, notice: _('Details successfully updated.') + redirect_to "#{edit_user_registration_path}\#personal-details", notice: success_message(_('profile'), _('saved')) else - flash[:notice] = message.blank? ? failed_update_error(current_user, _('profile')) : message + flash[:alert] = message.blank? ? failed_update_error(current_user, _('profile')) : message render "edit" end end + def do_update_password(current_user, params) + if params[:user][:current_password].blank? + message = _('Please enter your current password') + elsif params[:user][:password_confirmation].blank? + message = _('Please enter a password confirmation') + elsif params[:user][:password] != params[:user][:password_confirmation] + message = _('Password and comfirmation must match') + else + successfully_updated = current_user.update_with_password(password_update) + end + #render the correct page + if successfully_updated + session[:locale] = current_user.get_locale unless current_user.get_locale.nil? + set_gettext_locale #Method defined at controllers/application_controller.rb + set_flash_message :notice, success_message(_('password'), _('saved')) + sign_in current_user, bypass: true # TODO this method is deprecated + redirect_to "#{edit_user_registration_path}\#password-details", notice: success_message(_('password'), _('saved')) + + else + flash[:alert] = message.blank? ? failed_update_error(current_user, _('profile')) : message + redirect_to "#{edit_user_registration_path}\#password-details" + end + end + def sign_up_params - params.require(:user).permit(:email, :password, :password_confirmation, :firstname, :surname, + params.require(:user).permit(:email, :password, :password_confirmation, + :firstname, :surname, :recovery_email, :accept_terms, :org_id, :other_organisation) end diff --git a/app/controllers/roles_controller.rb b/app/controllers/roles_controller.rb index ad2b69b..3b65891 100644 --- a/app/controllers/roles_controller.rb +++ b/app/controllers/roles_controller.rb @@ -1,4 +1,5 @@ class RolesController < ApplicationController + include ConditionalUserMailer respond_to :html after_action :verify_authorized @@ -6,8 +7,9 @@ registered = true @role = Role.new(role_params) authorize @role + access_level = params[:role][:access_level].to_i - set_access_level(access_level) + @role.set_access_level(access_level) message = '' if params[:user].present? if @role.plan.owner.present? && @role.plan.owner.email == params[:user] @@ -26,10 +28,14 @@ message += _('Plan shared with %{email}.') % {email: user.email} @role.user = user if @role.save - if registered then UserMailer.sharing_notification(@role, current_user).deliver_now end + if registered + deliver_if(recipients: user, key: 'users.added_as_coowner') do |r| + UserMailer.sharing_notification(@role, r).deliver_now + end + end flash[:notice] = message else - flash[:notice] = failed_create_error(@role, _('role')) + flash[:alert] = failed_create_error(@role, _('role')) end end end @@ -44,14 +50,14 @@ @role = Role.find(params[:id]) authorize @role access_level = params[:role][:access_level].to_i - set_access_level(access_level) + @role.set_access_level(access_level) if @role.update_attributes(role_params) - flash[:notice] = _('Sharing details successfully updated.') - UserMailer.permissions_change_notification(@role, current_user).deliver_now - redirect_to controller: 'plans', action: 'share', id: @role.plan.id + deliver_if(recipients: @role.user, key: 'users.added_as_coowner') do |r| + UserMailer.permissions_change_notification(@role, current_user).deliver_now + end + render json: {code: 1, msg: "Successfully changed the permissions for #{@role.user.email}. They have been notified via email."} else - flash[:notice] = failed_create_error(@role, _('role')) - render action: "edit" + render json: {code: 0, msg: flash[:alert]} end end @@ -62,32 +68,33 @@ plan = @role.plan @role.destroy flash[:notice] = _('Access removed') - UserMailer.project_access_removed_notification(user, plan, current_user).deliver_now + deliver_if(recipients: user, key: 'users.added_as_coowner') do |r| + UserMailer.plan_access_removed(user, plan, current_user).deliver_now + end redirect_to controller: 'plans', action: 'share', id: @role.plan.id end + + # This function makes user's role on a plan inactive - i.e. "removes" this from their plans + def deactivate + role = Role.find(params[:id]) + authorize role + role.active = false + # if creator, remove from public plans list + if role.creator? && role.plan.publicly_visible? + role.plan.visibility = Plan.visibilities[:privately_visible] + role.plan.save + end + if role.save + flash[:notice] = _('Plan removed') + else + flash[:alert] = _('Unable to remove the plan') + end + redirect_to(plans_path) + end private def role_params params.require(:role).permit(:plan_id) end - - def set_access_level(access_level) - if access_level >= 1 - @role.commenter = true - else - @role.commenter = false - end - if access_level >= 2 - @role.editor = true - else - @role.editor = false - end - if access_level >= 3 - @role.administrator = true - else - @role.administrator = false - end - end - -end +end \ No newline at end of file diff --git a/app/controllers/sections_controller.rb b/app/controllers/sections_controller.rb index b6ef3d1..e02d8a4 100644 --- a/app/controllers/sections_controller.rb +++ b/app/controllers/sections_controller.rb @@ -9,25 +9,26 @@ @section.description = params["section-desc"] @section.modifiable = true @phase = @section.phase + current_tab = params[:r] || 'all-templates' if @section.save @section.phase.template.dirty = true @section.phase.template.save! - redirect_to admin_show_phase_path(id: @section.phase_id, - :section_id => @section.id, edit: 'true'), notice: _('Information was successfully created.') + redirect_to admin_show_phase_path(id: @section.phase_id, r: current_tab, + :section_id => @section.id), notice: success_message(_('section'), _('created')) else @edit = (@phase.template.org == current_user.org) @open = true @sections = @phase.sections @section_id = @section.id @question_id = nil - flash[:notice] = failed_create_error(@section, _('section')) + flash[:alert] = failed_create_error(@section, _('section')) if @phase.template.customization_of.present? @original_org = Template.where(dmptemplate_id: @phase.template.customization_of).first.org else @original_org = @phase.template.org end - render template: 'phases/admin_show' + redirect_to admin_show_phase_path(id: @phase.id, r: current_tab) end end @@ -36,26 +37,27 @@ def admin_update @section = Section.includes(phase: :template).find(params[:id]) authorize @section - @section.description = params["section-desc-#{params[:id]}"] + @section.description = params["section-desc"] @phase = @section.phase + current_tab = params[:r] || 'all-templates' if @section.update_attributes(params[:section]) @section.phase.template.dirty = true @section.phase.template.save! - redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id , edit: 'true'), notice: _('Information was successfully updated.') + redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, r: current_tab), notice: success_message(_('section'), _('saved')) else @edit = (@phase.template.org == current_user.org) @open = true @sections = @phase.sections @section_id = @section.id @question_id = nil - flash[:notice] = failed_update_error(@section, _('section')) + flash[:alert] = failed_update_error(@section, _('section')) if @phase.template.customization_of.present? @original_org = Template.where(dmptemplate_id: @phase.template.customization_of).first.org else @original_org = @phase.template.org end - render template: 'phases/admin_show' + redirect_to admin_show_phase_path(id: @phase.id, section_id: @section.id, r: current_tab) end end @@ -65,12 +67,11 @@ @section = Section.includes(phase: :template).find(params[:section_id]) authorize @section @phase = @section.phase - + current_tab = params[:r] || 'all-templates' if @section.destroy @phase.template.dirty = true @phase.template.save! - - redirect_to admin_show_phase_path(id: @phase.id, edit: 'true' ), notice: _('Information was successfully deleted.') + redirect_to admin_show_phase_path(id: @phase.id, r: current_tab), notice: success_message(_('section'), _('deleted')) else @edit = (@phase.template.org == current_user.org) @open = true @@ -78,13 +79,13 @@ @section_id = @section.id @question_id = nil - flash[:notice] = failed_destroy_error(@section, _('section')) + flash[:alert] = failed_destroy_error(@section, _('section')) if @phase.template.customization_of.present? @original_org = Template.where(dmptemplate_id: @phase.template.customization_of).first.org else @original_org = @phase.template.org end - render template: 'phases/admin_show' + redirect_to(admin_show_phase_path(id: @phase.id, r: current_tab)) end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index a4154d9..dacd5a1 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,33 +1,31 @@ class SessionsController < Devise::SessionsController - - # POST /auth/:provider/callback - # --------------------------------------------------------------------- -=begin - def oauth_create - existing_user = User.find_by_email(params[:user][:email]) - - unless params[:omniauth].nil? - existing_user = UserIdentifier.find_by(identifier: params[:omniauth][:auth]) - end + + def new + redirect_to(root_path) end -=end - + # Capture the user's shibboleth id if they're coming in from an IDP # --------------------------------------------------------------------- def create existing_user = User.find_by(email: params[:user][:email]) if !existing_user.nil? -# TODO: Not sure why we check for shib data in params and then use session value below. We should move this to the -# new user_identifiers table - if !params[:shibboleth_data].nil? - #after authentication verify if session[:shibboleth] exists - existing_user.update_attributes(shibboleth_id: session[:shibboleth_data][:uid]) + # Until ORCID login is supported + if !session['devise.shibboleth_data'].nil? + if u = UserIdentifier.create(identifier_scheme: IdentifierScheme.find_by(name: 'shibboleth'), + identifier: session['devise.shibboleth_data']['uid'], + user: existing_user) + success = _('Your account has been successfully linked to your institutional credentials. You will now be able to sign in with them.') + end + existing_user.update_attributes(shibboleth_id: session['devise.shibboleth_data'][:uid]) end session[:locale] = existing_user.get_locale unless existing_user.get_locale.nil? set_gettext_locale #Method defined at controllers/application_controller.rb end super + if success + flash[:notice] = success + end end def destroy @@ -35,4 +33,4 @@ session[:locale] = nil set_gettext_locale #Method defined at controllers/application_controller.rb end -end \ No newline at end of file +end diff --git a/app/controllers/settings/plans_controller.rb b/app/controllers/settings/plans_controller.rb index c406401..3117367 100644 --- a/app/controllers/settings/plans_controller.rb +++ b/app/controllers/settings/plans_controller.rb @@ -36,7 +36,8 @@ flash[:alert] = _('An error has occurred while saving/resetting your export settings.') end respond_to do |format| - format.html { redirect_to(show_export_plan_path(@plan.id)) } + @phase_options = @plan.phases.order(:number).pluck(:title,:id) + format.html { redirect_to(download_plan_path(@plan.id)) } # format.json { render json: settings_json } end end diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index 6f38009..d111b3f 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -15,59 +15,4 @@ def roadmap end - - def public_plans - @plans = Plan.publicly_visible - end - - # GET /plans/[:plan_slug]/public_export - # ------------------------------------------------------------- - def public_export - redirect_to public_plans_path, notice: _('Exporting public plan is under development. Apologies for any inconvience.') - #@plan = Plan.find(params[:id]) - - # Force PDF response - #request.format = :pdf - - # if the project is designated as public - #if @plan.visibility == :publicly_visible - # if !@plan.nil? - # @exported_plan = ExportedPlan.new.tap do |ep| - # ep.plan = @plan - # ep.user = current_user ||= nil - # #ep.format = request.format.try(:symbol) - # ep.format = request.format.to_sym - # plan_settings = @plan.settings(:export) - - # Settings::Dmptemplate::DEFAULT_SETTINGS.each do |key, value| - # ep.settings(:export).send("#{key}=", plan_settings.send(key)) - # end - # end - - # @exported_plan.save! # FIXME: handle invalid request types without erroring? - # file_name = @exported_plan.project_name - - # respond_to do |format| - # format.pdf do - # @formatting = @plan.settings(:export).formatting - # render pdf: file_name, - # margin: @formatting[:margin], - # footer: { - # center: t('helpers.plan.export.pdf.generated_by'), - # font_size: 8, - # spacing: (@formatting[:margin][:bottom] / 2) - 4, - # right: '[page] of [topage]' - # } - # end - # end - - # else - # the project has no plans for some reason - # redirect_to public_plans_path, notice: _('The plan is incomplete.') - # end - #else - # Otherwise redirect to the home page with an unauthorized message - # redirect_to public_plans_path, notice: _('This account does not have access to that plan.') - #end - end end \ No newline at end of file diff --git a/app/controllers/super_admin/orgs_controller.rb b/app/controllers/super_admin/orgs_controller.rb new file mode 100644 index 0000000..72d338b --- /dev/null +++ b/app/controllers/super_admin/orgs_controller.rb @@ -0,0 +1,81 @@ +module SuperAdmin + class OrgsController < ApplicationController + after_action :verify_authorized + + def index + authorize Org + render 'index', locals: { orgs: Org.includes(:templates, :users) } + end + + def new + org = Org.new + authorize org + org.links = {"org": []} + render 'orgs/admin_edit', locals: {org: org, languages: Language.all.order("name"), + method: 'POST', url: super_admin_orgs_path } + end + + def create + authorize Org + org = Org.new(org_params) + org.logo = params[:logo] if params[:logo] + if params[:org_links].present? + org.links = JSON.parse(params[:org_links]) + end + + begin + org.funder = params[:funder].present? + org.institution = params[:institution].present? + org.organisation = params[:organisation].present? + + # Handle Shibboleth identifiers if that is enabled + if Rails.application.config.shibboleth_use_filtered_discovery_service + shib = IdentifierScheme.find_by(name: 'shibboleth') + + if params[:shib_id].present? || params[:shib_domain].present? + org.org_identifiers << OrgIdentifier.new( + identifier_scheme: shib, + identifier: params[:shib_id], + attrs: {domain: params[:shib_domain]}.to_json.to_s + ) + end + end + + if org.save + redirect_to admin_edit_org_path(org.id), notice: success_message(_('organisation'), _('created')) + else + flash[:alert] = failed_create_error(org, _('organisation')) + render 'orgs/admin_edit', locals: {org: org, languages: Language.all.order("name"), + method: 'POST', url: super_admin_orgs_path } + end + rescue Dragonfly::Job::Fetch::NotFound => dflye + redirect_to admin_edit_org_path(org), alert: _('There seems to be a problem with your logo. Please upload it again.') + render 'orgs/admin_edit', locals: {org: org, languages: Language.all.order("name"), + method: 'POST', url: super_admin_orgs_path } + end + end + + def destroy + org = Org.includes(:users, :templates, :guidance_groups).find(params[:id]) + authorize org + + # Only allow the delete if the org has no dependencies + unless org.users.length > 0 || org.templates.length > 0 + org.guidance_groups.delete_all + + if org.destroy! + redirect_to super_admin_orgs_path, notice: success_message(_('organisation'), _('removed')) + else + redirect_to super_admin_orgs_path, alert: failed_destroy_error(org, _('organisation')) + end + end + end + + private + def org_params + params.require(:org).permit(:name, :abbreviation, :logo, :contact_email, :contact_name, :remove_logo, + :feedback_enabled, :feedback_email_subject, :feedback_email_msg) + end + + end +end \ No newline at end of file diff --git a/app/controllers/super_admin/themes_controller.rb b/app/controllers/super_admin/themes_controller.rb new file mode 100644 index 0000000..75532fe --- /dev/null +++ b/app/controllers/super_admin/themes_controller.rb @@ -0,0 +1,74 @@ +module SuperAdmin + class ThemesController < ApplicationController + helper PaginableHelper + def index + authorize(Theme) + render(:index, locals: { themes: Theme.all.page(1) }) + end + + def new + authorize(Theme) + render(:new_edit, locals: { theme: Theme.new, options: { url: super_admin_themes_path, method: :POST, title: _('New Theme') }}) + end + + def create + authorize(Theme) + begin + pparams = permitted_params + Theme.create!(pparams) + flash[:notice] = _('Theme created successfully') + rescue ActionController::ParameterMissing + flash[:alert] = _('Unable to save since theme parameter is missing') + rescue ActiveRecord::RecordInvalid => e + flash[:alert] = e.message + end + redirect_to(action: :index) + end + + def edit + authorize(Theme) + begin + theme = Theme.find(params[:id]) + render(:new_edit, locals: { theme: theme, options: { url: super_admin_theme_path(theme), method: :PUT, title: _('Edit Theme') }}) + rescue ActiveRecord::RecordNotFound + flash[:alert] = _('There is no theme associated with id %{id}') % { :id => params[:id] } + redirect_to(action: :index) + end + end + + def update + authorize(Theme) + begin + pparams = permitted_params + Theme.find(params[:id]).update_attributes!(pparams) + flash[:notice] = _('Theme updated successfully') + rescue ActiveRecord::RecordNotFound + flash[:alert] = _('There is no theme associated with id %{id}') % { :id => params[:id] } + rescue ActionController::ParameterMissing + flash[:alert] = _('Unable to save since theme parameter is missing') + rescue ActiveRecord::RecordInvalid => e + flash[:alert] = e.message + end + redirect_to(action: :index) + end + + def destroy + authorize(Theme) + begin + Theme.find(params[:id]).destroy! + flash[:notice] = _('Successfully deleted your theme') + rescue ActiveRecord::RecordNotFound + flash[:alert] = _('There is no theme associated with id %{id}') % { :id => params[:id] } + rescue ActiveRecord::RecordNotDestroyed # Unlikely to happen since we don't have callback associated to destroy! but put for safety + flash[:alert] = _('The theme with id %{id} could not be destroyed') % { :id => params[:id] } + end + redirect_to(action: :index) + end + # Private instance methods + private + + def permitted_params + params.require(:theme).permit(:title, :description) + end + end +end diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb deleted file mode 100644 index 2717fef..0000000 --- a/app/controllers/templates_controller.rb +++ /dev/null @@ -1,327 +0,0 @@ -# [+Project:+] DMPRoadmap -# [+Description:+] This controller is responsible for all the actions in the admin interface under templates (e.g. phases, versions, sections, questions, suggested answer) (index; show; create; edit; delete) -# [+Copyright:+] Digital Curation Centre and University of California Curation Center - -class TemplatesController < ApplicationController - respond_to :html - after_action :verify_authorized - - # GET /org/admin/templates/:id/admin_index - # ----------------------------------------------------- - def admin_index - authorize Template - - funder_templates, org_templates = [], [] - - # Get all of the unique template family ids (dmptemplate_id) for each funder and the current org - funder_ids = Org.funders.includes(:templates).collect{|f| f.templates.where(published: true).valid.collect{|ft| ft.dmptemplate_id } }.flatten.uniq - org_ids = current_user.org.templates.where(customization_of: nil).valid.collect{|t| t.dmptemplate_id }.flatten.uniq - - org_ids.each do |id| - current = Template.current(id) - live = Template.live(id) - org_templates << {current: current, live: live} - end - funder_ids.each do |id| - funder_live = Template.live(id) - current = Template.org_customizations(id, current_user.org_id) - # if we have a current template, check to see if there is a live version - live = current.nil? ? nil : Template.live(current.dmptemplate_id) - # need a current version, default to funder live if no custs exist - current = funder_live unless current.present? - - funder_templates << {current: current, live: live, funder_live: funder_live, stale: funder_live.updated_at > current.created_at} - end - - @funder_templates = funder_templates.sort{|x,y| - x[:current].title <=> y[:current].title - } - @org_templates = org_templates.sort{|x,y| - x[:current].title <=> y[:current].title - } - end - - # GET /org/admin/templates/:id/admin_customize - # ----------------------------------------------------- - def admin_customize - @template = Template.find(params[:id]) - authorize @template - - customisation = Template.deep_copy(@template) - customisation.org = current_user.org - customisation.version = 0 - customisation.customization_of = @template.dmptemplate_id - customisation.dmptemplate_id = loop do - random = rand 2147483647 - break random unless Template.exists?(dmptemplate_id: random) - end - customisation.dirty = true - customisation.save - - customisation.phases.includes(:sections, :questions).each do |phase| - phase.modifiable = false - phase.save! - phase.sections.each do |section| - section.modifiable = false - section.save! - section.questions.each do |question| - question.modifiable = false - question.save! - end - end - end - - redirect_to admin_template_template_path(customisation) - end - - # GET /org/admin/templates/:id/admin_transfer_customization - # the funder template's id is passed through here - # ----------------------------------------------------- - def admin_transfer_customization - @template = Template.includes(:org).find(params[:id]) - authorize @template - new_customization = Template.deep_copy(@template) - new_customization.org_id = current_user.org_id - new_customization.published = false - new_customization.customization_of = @template.dmptemplate_id - new_customization.dirty = true - new_customization.phases.includes(sections: :questions).each do |phase| - phase.modifiable = false - phase.save - phase.sections.each do |section| - section.modifiable = false - section.save - section.questions.each do |question| - question.modifiable = false - question.save - end - end - end - customizations = Template.includes(:org, phases:[sections: [questions: :annotations]]).where(org_id: current_user.org_id, customization_of: @template.dmptemplate_id).order(version: :desc) - # existing version to port over - max_version = customizations.first - new_customization.dmptemplate_id = max_version.dmptemplate_id - new_customization.version = max_version.version + 1 - # here we rip the customizations out of the old template - # First, we find any customzed phases or sections - max_version.phases.each do |phase| - # check if the phase was added as a customization - if phase.modifiable - # deep copy the phase and add it to the template - phase_copy = Phase.deep_copy(phase) - phase_copy.number = new_customization.phases.length + 1 - phase_copy.template_id = new_customization.id - phase_copy.save! - else - # iterate over the sections to see if any of them are customizations - phase.sections.each do |section| - if section.modifiable - # this is a custom section - section_copy = Section.deep_copy(section) - customization_phase = new_customization.phases.includes(:sections).where(number: phase.number).first - section_copy.phase_id = customization_phase.id - # custom sections get added to the end - section_copy.number = customization_phase.sections.length + 1 - # section from phase with corresponding number in the main_template - section_copy.save! - else - # not a customized section, iterate over questions - customization_phase = new_customization.phases.includes(sections: [questions: :annotations]).where(number: phase.number).first - customization_section = customization_phase.sections.where(number: section.number).first - section.questions.each do |question| - # find corresponding question in new template - customization_question = customization_section.questions.where(number: question.number).first - # apply annotations - question.annotations.where(org_id: current_user.org_id).each do |annotation| - annotation_copy = Annotation.deep_copy(annotation) - annotation_copy.question_id = customization_question.id - annotation_copy.save! - end - end - end - end - end - end - new_customization.save - redirect_to admin_template_template_path(new_customization) - end - - # PUT /org/admin/templates/:id/admin_publish - # ----------------------------------------------------- - def admin_publish - @template = Template.find(params[:id]) - authorize @template - - current = Template.current(@template.dmptemplate_id) - - # Only allow the current version to be updated - if current != @template - redirect_to admin_template_template_path(@template), notice: _('You can not publish a historical version of this template.') - - else - # Unpublish the older published version if there is one - live = Template.live(@template.dmptemplate_id) - if !live.nil? and self != live - live.published = false - live.save! - end - # Set the dirty flag to false - @template.dirty = false - @template.published = true - @template.save - - flash[:notice] = _('Your template has been published and is now available to users.') - - redirect_to admin_index_template_path(current_user.org) - end - end - - # PUT /org/admin/templates/:id/admin_unpublish - # ----------------------------------------------------- - def admin_unpublish - template = Template.find(params[:id]) - authorize template - - # Unpublish the live version - @template = Template.live(template.dmptemplate_id) - - if @template.nil? - flash[:notice] = _('That template is not currently published.') - else - @template.published = false - @template.save - flash[:notice] = _('Your template is no longer published. Users will not be able to create new DMPs for this template until you re-publish it') - end - - redirect_to admin_index_template_path(current_user.org) - end - - # GET /org/admin/templates/:id/admin_template - # ----------------------------------------------------- - def admin_template - @template = Template.includes(:org, phases: [sections: [questions: [:question_options, :question_format, :annotations]]]).find(params[:id]) - authorize @template - - @current = Template.current(@template.dmptemplate_id) - - if @template == @current - # If the template is published - if @template.published? - # We need to create a new, editable version - new_version = Template.deep_copy(@template) - new_version.version = (@template.version + 1) - new_version.published = false - new_version.save - @template = new_version -# @current = Template.current(@template.dmptemplate_id) - end - else - flash[:notice] = _('You are viewing a historical version of this template. You will not be able to make changes.') - end - - # If the template is published - if @template.published? - # We need to create a new, editable version - new_version = Template.deep_copy(@template) - new_version.version = (@template.version + 1) - new_version.published = false - new_version.save - @template = new_version - end - - # once the correct template has been generated, we convert it to hash - @hash = @template.to_hash - end - - - # PUT /org/admin/templates/:id/admin_update - # ----------------------------------------------------- - def admin_update - @template = Template.find(params[:id]) - authorize @template - - current = Template.current(@template.dmptemplate_id) - - # Only allow the current version to be updated - if current != @template - redirect_to admin_template_template_path(@template), notice: _('You can not edit a historical version of this template.') - - else - if @template.description != params["template-desc"] || - @template.title != params[:template][:title] - @template.dirty = true - end - - @template.description = params["template-desc"] - if @template.update_attributes(params[:template]) - flash[:notice] = _('Information was successfully updated.') - - else - flash[:notice] = failed_update_error(@template, _('template')) - end - - @hash = @template.to_hash - render 'admin_template' - end - end - - - # GET /org/admin/templates/:id/admin_new - # ----------------------------------------------------- - def admin_new - authorize Template - end - - - # POST /org/admin/templates/:id/admin_create - # ----------------------------------------------------- - def admin_create - # creates a new template with version 0 and new dmptemplate_id - @template = Template.new(params[:template]) - authorize @template - @template.org_id = current_user.org.id - @template.description = params['template-desc'] - - if @template.save - redirect_to admin_template_template_path(@template), notice: _('Information was successfully created.') - else - @hash = @template.to_hash - flash[:notice] = failed_create_error(@template, _('template')) - render action: "admin_new" - end - end - - - # DELETE /org/admin/templates/:id/admin_destroy - # ----------------------------------------------------- - def admin_destroy - @template = Template.find(params[:id]) - authorize @template - - current = Template.current(@template.dmptemplate_id) - - # Only allow the current version to be destroyed - if current == @template - if @template.destroy - redirect_to admin_index_template_path - else - @hash = @template.to_hash - flash[:notice] = failed_destroy_error(@template, _('template')) - render admin_template_template_path(@template) - end - else - flash[:notice] = _('You cannot delete historical versions of this template.') - redirect_to admin_index_template_path - end - end - - # GET /org/admin/templates/:id/admin_template_history - # ----------------------------------------------------- - def admin_template_history - @template = Template.find(params[:id]) - authorize @template - @templates = Template.where(dmptemplate_id: @template.dmptemplate_id).order(:version) - @current = Template.current(@template.dmptemplate_id) - end - -end diff --git a/app/controllers/token_permission_types_controller.rb b/app/controllers/token_permission_types_controller.rb deleted file mode 100644 index 345bf1f..0000000 --- a/app/controllers/token_permission_types_controller.rb +++ /dev/null @@ -1,12 +0,0 @@ -class TokenPermissionTypesController < ApplicationController - respond_to :html - - ## - # GET - Lists all TokenPermissionTypes available to the user - # also lists their description - def index - authorize TokenPermissionType - @user = current_user - @token_types = @user.org.token_permission_types - end -end \ No newline at end of file diff --git a/app/controllers/usage_controller.rb b/app/controllers/usage_controller.rb new file mode 100644 index 0000000..08b9299 --- /dev/null +++ b/app/controllers/usage_controller.rb @@ -0,0 +1,7 @@ +class UsageController < ApplicationController + # GET /usage + def index + raise Pundit::NotAuthorizedError unless current_user.present? && (current_user.can_org_admin? || current_user.can_super_admin?) + render('index', locals: { orgs: Org.all }) + end +end \ No newline at end of file diff --git a/app/controllers/user_identifiers_controller.rb b/app/controllers/user_identifiers_controller.rb index 6b78c8e..8c451af 100644 --- a/app/controllers/user_identifiers_controller.rb +++ b/app/controllers/user_identifiers_controller.rb @@ -14,7 +14,7 @@ identifier.destroy! flash[:notice] = _('Successfully unlinked your account from %{is}.') % {is: identifier.identifier_scheme.description} else - flash[:notice] = _('Unable to unlink your account from %{is}.') % {is: identifier.identifier_scheme.description} + flash[:alert] = _('Unable to unlink your account from %{is}.') % {is: identifier.identifier_scheme.description} end redirect_to edit_user_registration_path diff --git a/app/controllers/users/invitations_controller.rb b/app/controllers/users/invitations_controller.rb new file mode 100644 index 0000000..6734545 --- /dev/null +++ b/app/controllers/users/invitations_controller.rb @@ -0,0 +1,14 @@ +class Users::InvitationsController < Devise::InvitationsController + protected + # Override require_no_authentication method defined at DeviseController (parent of Devise::InvitationsController) + # The following filter gets executed any time GET /users/invitation/accept?invitation_token=valid_token + # is requested. It replaces the default error message from devise (e.g. You are already signed in.) + # if the user is signed in already while trying to access to that URL + def require_no_authentication + super + if flash[:alert].present? + flash[:alert] = nil + flash[:notice] = _('You are already signed in as another user. Please log out to activate your invitation.') + end + end +end \ No newline at end of file diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb index d50e397..38719f9 100644 --- a/app/controllers/users/omniauth_callbacks_controller.rb +++ b/app/controllers/users/omniauth_callbacks_controller.rb @@ -1,4 +1,5 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController + ## # Dynamically build a handler for each omniauth provider # ------------------------------------------------------------- @@ -26,7 +27,6 @@ # If the uid didn't have a match in the system send them to register if user.nil? session["devise.#{scheme.name.downcase}_data"] = request.env["omniauth.auth"] - flash[:notice] = t('identifier_schemes.new_login_success') redirect_to new_user_registration_url # Otherwise sign them in @@ -36,7 +36,7 @@ set_flash_message(:notice, :success, kind: scheme.description) if is_navigational_format? sign_in_and_redirect user, event: :authentication else - flash[:notice] = t('identifier_schemes.new_login_success') + flash[:notice] = _('Successfully signed in') redirect_to new_user_registration_url end end @@ -51,17 +51,27 @@ flash[:notice] = _('Your account has been successfully linked to %{scheme}.') % { scheme: scheme.description } else - flash[:notice] = _('Unable to link your account to %{scheme}.') % { scheme: scheme.description } + flash[:alert] = _('Unable to link your account to %{scheme}.') % { scheme: scheme.description } end + + else + # If a user was found but does NOT match the current user then the identifier has + # already been attached to another account (likely the user has 2 accounts) + identifier = UserIdentifier.where(identifier: request.env["omniauth.auth"].uid).first + if identifier.user.id != current_user.id + flash[:alert] = _("The current #{scheme.description} iD has been already linked to a user with email #{identifier.user.email}") + end + + # Otherwise, the identifier was found and it matches the one already associated + # with the current user so nothing else needs to be done end - + # Redirect to the User Profile page redirect_to edit_user_registration_path end end + # ------------------------------------------------------------- - - def failure redirect_to root_path end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index b26a62e..4737072 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,4 +1,7 @@ class UsersController < ApplicationController + helper PaginableHelper + helper PermsHelper + include ConditionalUserMailer after_action :verify_authorized respond_to :html @@ -7,7 +10,11 @@ # Displays number of roles[was project_group], name, email, and last sign in def admin_index authorize User - @users = current_user.org.users.includes(:roles) + if current_user.can_super_admin? + @users = User.page(1) + else + @users = current_user.org.users.page(1) + end end ## @@ -15,10 +22,25 @@ # Permissions which the user already has are pre-selected # Selecting new permissions and saving calls the admin_update_permissions action def admin_grant_permissions - @user = User.includes(:perms).find(params[:id]) - authorize @user - user_perms = current_user.perms - @perms = user_perms & [Perm.grant_permissions, Perm.modify_templates, Perm.modify_guidance, Perm.use_api, Perm.change_org_details] + user = User.find(params[:id]) + authorize user + + # Super admin can grant any Perm, org admins can only grant Perms they + # themselves have access to + if current_user.can_super_admin? + perms = Perm.all + else + perms = current_user.perms + end + + render json: { + "user" => { + "id" => user.id, + "html" => render_to_string(partial: 'users/admin_grant_permissions', + locals: { user: user, perms: perms }, + formats: [:html]) + } + }.to_json end ## @@ -26,7 +48,7 @@ # redirects to the admin_index action # should add validation that the perms given are current perms of the current_user def admin_update_permissions - @user = User.includes(:perms).find(params[:id]) + @user = User.find(params[:id]) authorize @user perms_ids = params[:perm_ids].blank? ? [] : params[:perm_ids].map(&:to_i) perms = Perm.where( id: perms_ids) @@ -49,9 +71,73 @@ end if @user.save! - redirect_to({controller: 'users', action: 'admin_index'}, {notice: _('Information was successfully updated.')}) # helpers.success key does not exist, replaced with a generic string + deliver_if(recipients: @user, key: 'users.admin_privileges') do |r| + UserMailer.admin_privileges(r).deliver_now + end + render(json: { + code: 1, + msg: success_message(_('permissions'), _('saved')), + current_privileges: render_to_string(partial: 'users/current_privileges', locals: { user: @user }, formats: [:html]) + }) else - flash[:notice] = failed_update_error(@user, _('user')) + render(json: { code: 0, msg: failed_update_error(@user, _('user')) }) + end + end + + def update_email_preferences + prefs = params[:prefs] + authorize current_user, :update? + pref = current_user.pref + # does user not have prefs? + if pref.blank? + pref = Pref.new + pref.settings = {} + pref.user = current_user + end + pref.settings[:email] = booleanize_hash(prefs) + pref.save + + # Include active tab in redirect path + redirect_to "#{edit_user_registration_path}\#notification-preferences", notice: success_message(_('preferences'), _('saved')) + end + + # PUT /users/:id/org_swap + # ----------------------------------------------------- + def org_swap + # Allows the user to swap their org affiliation on the fly + authorize current_user + begin + org = Org.find(org_swap_params[:org_id]) + rescue ActiveRecord::RecordNotFound + redirect_to(request.referer, alert: _('Please select an organisation from the list')) and return + end + if org.present? + current_user.org = org + if current_user.save! + redirect_to request.referer, notice: _('Your organisation affiliation has been changed. You may now edit templates for %{org_name}.') % {org_name: current_user.org.name} + else + redirect_to request.referer, alert: _('Unable to change your organisation affiliation at this time.') + end + else + redirect_to request.referer, alert: _('Unknown organisation.') + end + end + + private + def org_swap_params + params.require(:user).permit(:org_id, :org_name) + end + + ## + # html forms return our boolean values as strings, this converts them to true/false + def booleanize_hash(node) + #leaf: convert to boolean and return + #hash: iterate over leaves + unless node.is_a?(Hash) + return node == "true" + end + node.each do |key, value| + node[key] = booleanize_hash(value) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 8c37586..992c6ab 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -15,19 +15,26 @@ end # --------------------------------------------------------------------------- - def javascript(*files) - content_for(:head) { javascript_include_tag(*files) } - end - - # --------------------------------------------------------------------------- def hash_to_js_json_variable(obj_name, hash) "".html_safe end # Determines whether or not the URL path passed matches with the full path (including params) of the last URL requested. # see http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-fullpath for details - def isActivePage(path) - return request.fullpath() == path + # --------------------------------------------------------------------------- + def isActivePage(path, exact_match = false) + if exact_match + return request.fullpath == path + else + return request.fullpath.include?(path) + end end + def is_integer?(string) + return string.present? && string.match(/^(\d)+$/) + end + + def fingerprinted_asset(name) + Rails.env.production? ? "#{name}-#{ASSET_FINGERPRINT}" : name + end end diff --git a/app/helpers/mailer_helper.rb b/app/helpers/mailer_helper.rb new file mode 100644 index 0000000..a7ee2df --- /dev/null +++ b/app/helpers/mailer_helper.rb @@ -0,0 +1,31 @@ +module MailerHelper + include PermsHelper + def feedback_confirmation_default_subject + _('%{application_name}: Your plan has been submitted for feedback') + end + + def feedback_confirmation_default_message + _('

Hello %{user_name}.

'\ + '

Your plan "%{plan_name}" has been submitted for feedback from an administrator at your organisation. '\ + 'If you have questions pertaining to this action, please contact us at %{organisation_email}.

') + end + + def feedback_constant_to_text(text, user, plan, org) + _("#{text}") % {application_name: Rails.configuration.branding[:application][:name], + user_name: user.name, + plan_name: plan.title, + organisation_email: org.contact_email} + end + + # Returns an unordered HTML list with the permissions associated to the user passed + def privileges_list(user) + if user.respond_to?(:perms) && user.perms.respond_to?(:each) + names = name_and_text + r= "" + end + end +end \ No newline at end of file diff --git a/app/helpers/paginable_helper.rb b/app/helpers/paginable_helper.rb new file mode 100644 index 0000000..cb653ea --- /dev/null +++ b/app/helpers/paginable_helper.rb @@ -0,0 +1,3 @@ +module PaginableHelper + include Paginable +end \ No newline at end of file diff --git a/app/helpers/perms_helper.rb b/app/helpers/perms_helper.rb new file mode 100644 index 0000000..6a007b8 --- /dev/null +++ b/app/helpers/perms_helper.rb @@ -0,0 +1,15 @@ +module PermsHelper + # Returns a hash whose keys are the names associated to Perms and values are the text to be displayed to the end user + def name_and_text + { + :add_organisations => _('Add organisations'), + :change_org_affiliation => _('Change affiliation'), + :grant_permissions => _('Manage user privileges'), + :modify_templates => _('Manage templates'), + :modify_guidance => _('Manage guidance'), + :use_api => _('API rights'), + :change_org_details => _('Manage organisation details'), + :grant_api_to_orgs => _('Grant API to organisations') + } + end +end \ No newline at end of file diff --git a/app/helpers/plans_helper.rb b/app/helpers/plans_helper.rb index cf5a0c6..1b9cb6d 100644 --- a/app/helpers/plans_helper.rb +++ b/app/helpers/plans_helper.rb @@ -1,74 +1,4 @@ module PlansHelper - - # Build variable column headings for the project list - # -------------------------------------------------------- - def plan_list_column_heading(column) - if column.kind_of?(Array) - heading = (column.first.kind_of?(String) ? column.first : _(' - ')) - - elsif column.kind_of?(String) - heading = column - - else - heading = _(' - ') - end - - klass = (['name', 'description'].include?(heading) ? :dmp_th_big : :dmp_th_small) - - content_tag(:th, t("helpers.project.columns.#{heading}"), class: klass) # parametrised YAML keys are no longer possible with gettext, TODO - end - - # Populate a variable column for the project list - # -------------------------------------------------------- - def plan_list_column_body(column, plan) - - col = (column.kind_of?(Array) ? column[0] : column) - - klass, content = case col - when 'name' - [ "dmp_td_big", link_to(plan.title, plan_path(plan), class: "dmp_table_link") ] - when 'owner' - user = plan.owner - text = if user.nil? - _(' - ') - elsif user == current_user - _('Me') - else - user.name - end - - [ "tmp_td_small", text ] - when 'shared' - shared_num = plan.users.count - 1 - text = shared_num > 0 ? (_('Yes') + " (with #{shared_num} people) ") : _('No') # Hardcoded strings are not internationalised - [ "dmp_td_small", text ] - when 'visibility' - text = if plan.visibility == 'organisationally_visible' - _('Organisational') - elsif plan.visibility == 'publicly_visible' - _('Public') - elsif plan.visibility == 'is_test' - _('Test/Practice') - elsif plan.visibility == 'privately_visible' - _('Private') - end - ["dmp_td_small", text ] - when 'last_edited' - [ "dmp_td_small", l(plan.latest_update.to_date, formats: :short) ] - when 'description' - [ "dmp_td_medium", (plan.try(col) || _(' - ')) ] - when 'non_link_name' - [ "dmp_td_big", plan.title ] - when 'template' - ["dmp_td_big", plan.template.title] - when 'organisation' - ["dmp_td_medium", plan.template.org.name] # This will trigger 2 queries for each function call, i.e. one for templates and another for orgs tables - else - [ "dmp_td_small", (plan.try(col) || _(' - ')) ] - end - content_tag(:td, content, class: klass) - end - # Shows whether the user has default, template-default or custom settings # for the given plan. # -------------------------------------------------------- @@ -87,4 +17,46 @@ content_tag(:small, t("helpers.settings.plans.#{key}")) end + # display the role of the user for a given plan + def display_role(role) + if role.creator? + access = _('Owner') + + else + case role.access_level + when 3 + access = _('Co-owner') + when 2 + access = _('Editor') + when 1 + access = _('Read only') + end + end + return access + end + + # display the visibility of the plan + def display_visibility(val) + case val + when 'organisationally_visible' + return "#{_('Organisation')}" + when 'publicly_visible' + return "#{_('Public')}" + when 'privately_visible' + return "#{_('Private')}" + else + return "#{_('Private')}" # Test Plans + end + end + + def visibility_tooltip(val) + case val + when 'organisationally_visible' + return _('Organisation: anyone at my organisation can view.') + when 'publicly_visible' + return _('Public: anyone can view.') + else + return _('Private: restricted to me and people I invite.') + end + end end diff --git a/app/helpers/settings_template_helper.rb b/app/helpers/settings_template_helper.rb index 255c95e..ea18f38 100644 --- a/app/helpers/settings_template_helper.rb +++ b/app/helpers/settings_template_helper.rb @@ -17,7 +17,7 @@ elsif admin_field == 'funder' return _('Funder') elsif admin_field == 'institution' - return _('Institution') + return _('Organisation') elsif admin_field == 'orcid' return _('Your ORCID') end diff --git a/app/helpers/template_helper.rb b/app/helpers/template_helper.rb new file mode 100644 index 0000000..42ab240 --- /dev/null +++ b/app/helpers/template_helper.rb @@ -0,0 +1,8 @@ +module TemplateHelper + def links_to_a_elements(links, separator = ', ') + a = links.map do |l| + "#{l['text']}" + end + a.join(separator) + end +end \ No newline at end of file diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index f679ba2..b54bf09 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -1,47 +1,127 @@ class UserMailer < ActionMailer::Base - default from: Rails.configuration.branding[:organisation][:email] - + include MailerHelper + helper MailerHelper + default from: Rails.configuration.branding[:organisation][:email] + def welcome_notification(user) @user = user FastGettext.with_locale FastGettext.default_locale do mail(to: @user.email, - subject: "#{_('Welcome to')} #{Rails.configuration.branding[:application][:name]}") + subject: _('Welcome to %{tool_name}') %{ :tool_name => Rails.configuration.branding[:application][:name] }) end end - def sharing_notification(role, user) + def sharing_notification(role, user) @role = role @user = user FastGettext.with_locale FastGettext.default_locale do - mail(to: @role.user.email, - subject: "#{_('A Data Management Plan in ')} #{Rails.configuration.branding[:application][:name]} #{_(' has been shared with you')}") - end - end - - def permissions_change_notification(role, current_user) - @role = role - @current_user = current_user - FastGettext.with_locale FastGettext.default_locale do mail(to: @role.user.email, - subject: "#{_('Changed permissions on a DMP in')} #{Rails.configuration.branding[:application][:name]}") + subject: _('A Data Management Plan in %{tool_name} has been shared with you') %{ :tool_name => Rails.configuration.branding[:application][:name] }) end - end - - def project_access_removed_notification(user, plan, current_user) - @user = user - @plan = plan - @current_user = current_user + end + + def permissions_change_notification(role, user) + @role = role + @user = user FastGettext.with_locale FastGettext.default_locale do - mail(to: @user.email, - subject: "#{_('Permissions removed on a DMP in')} #{Rails.configuration.branding[:application][:name]}") + mail(to: @role.user.email, + subject: _('Changed permissions on a Data Management Plan in %{tool_name}') %{ :tool_name => Rails.configuration.branding[:application][:name] }) end - end + end + + def plan_access_removed(user, plan, current_user) + @user = user + @plan = plan + @current_user = current_user + FastGettext.with_locale FastGettext.default_locale do + mail(to: @user.email, + subject: "#{_('Permissions removed on a DMP in %{tool_name}') %{ :tool_name => Rails.configuration.branding[:application][:name] }}") + end + end def api_token_granted_notification(user) @user = user FastGettext.with_locale FastGettext.default_locale do mail(to: @user.email, - subject: "#{_('API rights in')} #{Rails.configuration.branding[:application][:name]}") + subject: _('API rights in %{tool_name}') %{ :tool_name => Rails.configuration.branding[:application][:name] }) end end + + def feedback_notification(recipient, plan, requestor) + @user = requestor + + if @user.org.present? + @org = @user.org + @plan = plan + @recipient = recipient + + FastGettext.with_locale FastGettext.default_locale do + mail(to: recipient.email, + subject: _("%{application_name}: %{user_name} requested feedback on a plan") % {application_name: Rails.configuration.branding[:application][:name], user_name: @user.name(false)}) + end + end + end + + def feedback_complete(recipient, plan, requestor) + @requestor = requestor + @user = recipient + @plan = plan + + FastGettext.with_locale FastGettext.default_locale do + mail(to: recipient.email, + subject: _("%{application_name}: Expert feedback has been provided for %{plan_title}") % {application_name: Rails.configuration.branding[:application][:name], plan_title: @plan.title}) + end + end + + def feedback_confirmation(recipient, plan, requestor) + user = requestor + + if user.org.present? + org = user.org + plan = plan + + # Use the generic feedback confirmation message unless the Org has specified one + subject = (org.feedback_email_subject.present? ? org.feedback_email_subject : feedback_confirmation_default_subject) + message = (org.feedback_email_msg.present? ? org.feedback_email_msg : feedback_confirmation_default_message) + + @body = feedback_constant_to_text(message, user, plan, org) + + FastGettext.with_locale FastGettext.default_locale do + mail(to: recipient.email, + subject: feedback_constant_to_text(subject, user, plan, org)) + end + end + end + + def plan_visibility(user, plan) + @user = user + @plan = plan + FastGettext.with_locale FastGettext.default_locale do + mail(to: @user.email, + subject: _('DMP Visibility Changed: %{plan_title}') %{ :plan_title => @plan.title }) + end + end + + # @param commenter - User who wrote the comment + # @param plan - Plan for which the comment is associated to + def new_comment(commenter, plan) + if commenter.is_a?(User) && plan.is_a?(Plan) + if plan.owner.present? + @commenter = commenter + @plan = plan + FastGettext.with_locale FastGettext.default_locale do + mail(to: plan.owner.email, subject: + _('%{tool_name}: A new comment was added to %{plan_title}') %{ :tool_name => Rails.configuration.branding[:application][:name], :plan_title => plan.title }) + end + end + end + end + + def admin_privileges(user) + @user = user + FastGettext.with_locale FastGettext.default_locale do + mail(to: user.email, subject: + _('Administrator privileges granted in %{tool_name}') %{ :tool_name => Rails.configuration.branding[:application][:name] }) + end + end end diff --git a/app/models/answer.rb b/app/models/answer.rb index d46ad59..a2a625f 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -1,5 +1,18 @@ class Answer < ActiveRecord::Base - + + after_save do |answer| + if answer.plan_id.present? + plan = answer.plan + complete = plan.no_questions_matches_no_answers? + if plan.complete != complete + plan.complete = complete + plan.save! + else + plan.touch # Force updated_at changes if nothing changed since save only saves if changes were made to the record + end + end + end + ## # Associations belongs_to :question @@ -13,30 +26,44 @@ ## # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 - attr_accessible :text, :plan_id, :lock_version, :question_id, :user_id, :question_option_ids, + attr_accessible :text, :plan_id, :lock_version, :question_id, :user_id, :question_option_ids, :question, :user, :plan, :question_options, :notes, :note_ids, :id, :as => [:default, :admin] ## # Validations # validates :user, :plan, :question, presence: true -# +# # # Make sure there is only one answer per question! -# validates :question, uniqueness: {scope: [:plan], +# validates :question, uniqueness: {scope: [:plan], # message: I18n.t('helpers.answer.only_one_per_question')} -# +# # # The answer MUST have a text value if the question is NOT option based or a question_option if -# # it is option based. -# validates :text, presence: true, if: Proc.new{|a| +# # it is option based. +# validates :text, presence: true, if: Proc.new{|a| # (a.question.nil? ? false : !a.question.question_format.option_based?) # } -# validates :question_options, presence: true, if: Proc.new{|a| +# validates :question_options, presence: true, if: Proc.new{|a| # (a.question.nil? ? false : a.question.question_format.option_based?) # } -# +# # # Make sure the plan and question are associated with the same template! # validates :plan, :question, answer_for_correct_template: true + ## + # deep copy the given answer + # + # @params [Answer] question_option to be deep copied + # @return [Answer] the saved, copied answer + def self.deep_copy(answer) + answer_copy = answer.dup + answer.question_options.each do |opt| + answer_copy.question_options << opt + end + answer_copy.save! + return answer_copy + end + # This method helps to decide if an answer option (:radiobuttons, :checkbox, etc ) in form views should be checked or not # Returns true if the given option_id is present in question_options, otherwise returns false def has_question_option(option_id) @@ -52,8 +79,44 @@ else # (e.g. textarea or textfield question formats) return self.text.present? end - else - return false end + return false + end + # Returns answer notes whose archived is blank sorted by updated_at in descending order + def non_archived_notes + return notes.select{ |n| n.archived.blank? }.sort!{ |x,y| y.updated_at <=> x.updated_at } + end + + + ## + # Returns the parsed JSON hash for the current answer object + # Generates a new hash if none exists for rda_questions + # + # @return [Hash] the parsed hash of the answer. + # Should have keys 'standards', 'text' + # 'standards' is a list of : pairs + # 'text' is the text from the comments box + def answer_hash + default = {'standards' => {}, 'text' => ''} + begin + h = self.text.nil? ? default : JSON.parse(self.text) + rescue JSON::ParserError => e + h = default + end + return h + end + + ## + # Given a hash of standards and a comment value, this updates answer + # text for rda_questions + # + # @param [standards] a hash of standards + # @param [text] option comment text + # nothing returned, but the status of the text field of the answer is changed + def update_answer_hash(standards={},text="") + h = {} + h['standards'] = standards + h['text'] = text + self.text = h.to_json end end diff --git a/app/models/concerns/exportable_plan.rb b/app/models/concerns/exportable_plan.rb new file mode 100644 index 0000000..b9254b8 --- /dev/null +++ b/app/models/concerns/exportable_plan.rb @@ -0,0 +1,126 @@ +module ExportablePlan + extend ActiveSupport::Concern + + included do + + def as_pdf(coversheet = false) + prepare(coversheet) + end + + def as_csv(headings = true, unanswered = true) + hash = prepare(false) + + CSV.generate do |csv| + hdrs = (hash[:phases].length > 1 ? [_('Phase')] : []) + if headings + hdrs << [_('Section'),_('Question'),_('Answer')] + else + csv << _('Answer') + end + + csv << hdrs.flatten + hash[:phases].each do |phase| + phase[:sections].each do |section| + section[:questions].each do |question| + answer = self.answer(question[:id], false) + answer_text = answer.present? ? answer.text : (unanswered ? 'Not Answered' : '') + flds = (hash[:phases].length > 1 ? [phase[:title]] : []) + if headings + question_text = (question[:text].length > 1 ? question[:text].join(', ') : question[:text][0]) + flds << [ section[:title], sanitize_text(question_text), sanitize_text(answer_text) ] + else + flds << [ sanitize_text(answer_text) ] + end + + csv << flds.flatten + end + end + end + end + end + + private + def prepare(coversheet = false) + hash = coversheet ? prepare_coversheet : {} + template = Template.includes(phases: { sections: {questions: :question_format } }). + joins(phases: { sections: { questions: :question_format } }). + where(id: self.template_id).order('sections.number', 'questions.number').first + + hash[:title] = self.title + hash[:answers] = self.answers + + # add the relevant questions/answers + phases = [] + template.phases.each do |phase| + phs = { title: phase.title, number: phase.number, sections: [] } + phase.sections.each do |section| + sctn = { title: section.title, number: section.number, questions: [] } + section.questions.each do |question| + txt = [] + if question.question_format.option_based? + opts = QuestionOption.where(question_id: question.id) + opts.each do |opt| + txt << opt.text + end + else + txt << question.text + end + sctn[:questions] << { id: question.id, text: txt, format: question.question_format } + end + phs[:sections] << sctn + end + phases << phs + end + hash[:phases] = phases + + record_plan_export(:pdf) + + hash + end + + def prepare_coversheet + hash = {} + # name of owner and any co-owners + attribution = self.owner.present? ? [self.owner.name(false)] : [] + self.roles.administrator.not_creator.each do |role| + attribution << role.user.name(false) + end + hash[:attribution] = attribution + + # Org name of plan owner's org + hash[:affiliation] = self.owner.present? ? self.owner.org.name : '' + + # set the funder name + hash[:funder] = self.funder_name.present? ? self.funder_name : (self.template.org.present? ? self.template.org.name : '') + + # set the template name and customizer name if applicable + hash[:template] = self.template.title + customizer = "" + cust_questions = self.questions.where(modifiable: true).pluck(:id) + # if the template is customized, and has custom answered questions + if self.template.customization_of.present? && Answer.where(plan_id: self.id, question_id: cust_questions).present? + customizer = _(" Customised By: ") + self.template.org.name + end + hash[:customizer] = customizer + hash + end + + def record_plan_export(format) + exported_plan = ExportedPlan.new.tap do |ep| + ep.plan = self + ep.phase_id = self.phases.first.id + ep.format = format + plan_settings = self.settings(:export) + + Settings::Template::DEFAULT_SETTINGS.each do |key, value| + ep.settings(:export).send("#{key}=", plan_settings.send(key)) + end + end + exported_plan.save + end + + def sanitize_text(text) + if (!text.nil?) then ActionView::Base.full_sanitizer.sanitize(text.gsub(/ /i,"")) end + end + end +end diff --git a/app/models/concerns/json_link_validator.rb b/app/models/concerns/json_link_validator.rb new file mode 100644 index 0000000..8315a7d --- /dev/null +++ b/app/models/concerns/json_link_validator.rb @@ -0,0 +1,32 @@ +module JSONLinkValidator + extend ActiveSupport::Concern + + included do + # Parses a stringified JSON according to validate_links (e.g. [{ link: String, text: String}, ...]) + # param {String} the stringified JSON value + # Returns an Array of hashes after decoding/validating the stringified JSON passed, otherwise nil + def parse_links(value) + return nil unless value.is_a?(String) + begin + parsed_value = JSON.parse(value) + return valid_links?(parsed_value) ? parsed_value : nil + rescue JSON::ParserError + nil + end + end + # Validates whether or not the value passed is conforming to [{ link: String, text: String}, ...] + def valid_links?(value) + if value.is_a?(Array) + r = value.all? do |o| + o.is_a?(Hash) && + o.has_key?('link') && + o.has_key?('text') && + o['link'].is_a?(String) && + o['text'].is_a?(String) + end + return r + end + false + end + end +end \ No newline at end of file diff --git a/app/models/exported_plan.rb b/app/models/exported_plan.rb index 0bd9ef1..bb79837 100644 --- a/app/models/exported_plan.rb +++ b/app/models/exported_plan.rb @@ -11,11 +11,11 @@ VALID_FORMATS = ['csv', 'html', 'pdf', 'text', 'docx'] - validates :format, inclusion: { + validates :format, inclusion: { in: VALID_FORMATS, - message: -> (object, data) do - _('%{value} is not a valid format') % { :value => data[:value] } - end + message: -> (object, data) do + _('%{value} is not a valid format') % { :value => data[:value] } + end } validates :plan, :format, presence: {message: _("can't be blank")} @@ -99,24 +99,40 @@ # Export formats - def as_csv + def as_csv(sections, unanswered_questions, question_headings) CSV.generate do |csv| - csv << [_('Section'),_('Question'),_('Answer'),_('Selected option(s)'),_('Answered by'),_('Answered at')] - self.sections.each do |section| - questions = self.questions_for_section(section) - if questions.present? - questions.each do |question| - answer = self.plan.answer(question.id) - q_format = question.question_format - if q_format.option_based? - options_string = answer.question_options.collect {|o| o.text}.join('; ') - else - options_string = '' - end + if question_headings + csv << [_('Section'),_('Question'),_('Answer'),_('Selected option(s)'),_('Answered by'),_('Answered at')] + else + csv << [_('Section'),_('Answer'),_('Selected option(s)'),_('Answered by'),_('Answered at')] + end + sections.each do |section| + section.questions.each do |question| + answer = Answer.where(plan_id: self.plan_id, question_id: question.id).first + # skip unansewered questions + if answer.blank? && !unanswered_questions + next + end + answer_text = answer.present? ? answer.text : '' + q_format = question.question_format + if q_format.option_based? + options_string = answer.question_options.collect {|o| o.text}.join('; ') + else + options_string = '' + end + if question_headings csv << [ section.title, sanitize_text(question.text), - question.option_comment_display ? sanitize_text(answer.text) : '', + question.option_comment_display ? sanitize_text(answer_text) : '', + options_string, + user.name, + answer.updated_at + ] + else + csv << [ + section.title, + question.option_comment_display ? sanitize_text(answer_text) : '', options_string, user.name, answer.updated_at @@ -127,40 +143,44 @@ end end - def as_txt + def as_txt(sections, unanswered_questions, question_headings, details) output = "#{self.plan.title}\n\n#{self.plan.template.title}\n" output += "\n"+_('Details')+"\n\n" - - self.admin_details.each do |at| - value = self.send(at) - if value.present? - output += admin_field_t(at.to_s) + ": " + value + "\n" - else - output += admin_field_t(at.to_s) + ": " + _('-') + "\n" - end + if details + self.admin_details.each do |at| + value = self.send(at) + if value.present? + output += admin_field_t(at.to_s) + ": " + value + "\n" + else + output += admin_field_t(at.to_s) + ": " + _('-') + "\n" + end + end end - self.sections.each do |section| - questions = self.questions_for_section(section) - if questions.present? + sections.each do |section| + if question_headings output += "\n#{section.title}\n" - questions.each do |question| + end + section.questions.each do |question| + answer = self.plan.answer(question.id, false) + #skip if question un-answered + if answer.nil? && !unanswered_questions then next end + + if question_headings qtext = sanitize_text( question.text.gsub(/<li>/, ' * ') ) output += "\n* #{qtext}" - answer = self.plan.answer(question.id, false) - - if answer.nil? - output += _('Question not answered.')+ "\n" - else - q_format = question.question_format - if q_format.option_based? - output += answer.question_options.collect {|o| o.text}.join("\n") - if question.option_comment_display - output += "\n#{sanitize_text(answer.text)}\n" - end - else + end + if answer.nil? + output += _('Question not answered.')+ "\n" + else + q_format = question.question_format + if q_format.option_based? + output += answer.question_options.collect {|o| o.text}.join("\n") + if question.option_comment_display output += "\n#{sanitize_text(answer.text)}\n" end + else + output += "\n#{sanitize_text(answer.text)}\n" end end end diff --git a/app/models/guidance.rb b/app/models/guidance.rb index 8ba321b..4c5bb5c 100644 --- a/app/models/guidance.rb +++ b/app/models/guidance.rb @@ -27,7 +27,15 @@ validates :text, presence: {message: _("can't be blank")} + # Retrieves every guidance associated to an org + scope :by_org, -> (org) { + joins(:guidance_group).merge(GuidanceGroup.by_org(org)) + } + scope :search, -> (term) { + search_pattern = "%#{term}%" + joins(:guidance_group).where("guidances.text LIKE ? OR guidance_groups.name LIKE ?", search_pattern, search_pattern) + } ## # Determine if a guidance is in a group which belongs to a specified organisation # @@ -43,20 +51,6 @@ end ## - # returns all guidance that belongs to a specified organisation - # - # @param org_id [Integer] the integer id for an organisation - # @return [Array<Guidance>] list of guidance - def self.by_org(org_id) - org_guidance = [] - # TODO: re-write below querry when guidance_in_group removed from model - Org.find_by(id: org_id).guidance_groups.each do |group| - org_guidance += Guidance.where(guidance_group_id: group.id) - end - return org_guidance - end - - ## # returns all templates belgonging to a specified guidance group # # @param guidance_group [Integer] the integer id for an guidance_group @@ -98,7 +92,7 @@ end # guidance groups are viewable if they are owned by a funder - if Org.funders.include?(guidance.guidance_group.org) + if Org.funder.include?(guidance.guidance_group.org) viewable = true end end @@ -119,7 +113,7 @@ def self.all_viewable(user) managing_groups = Org.includes(guidance_groups: :guidances).managing_orgs.collect{|o| o.guidance_groups} # find all groups owned by a Funder organisation - funder_groups = Org.includes(guidance_groups: :guidances).funders.collect{|org| org.guidance_groups} + funder_groups = Org.includes(guidance_groups: :guidances).funder.collect{|org| org.guidance_groups} # find all groups owned by any of the user's organisations organisation_groups = user.org.guidance_groups @@ -129,5 +123,4 @@ # pass the list of viewable guidances to the view return all_viewable_guidances.flatten end - end diff --git a/app/models/guidance_group.rb b/app/models/guidance_group.rb index ee3e685..b898ded 100644 --- a/app/models/guidance_group.rb +++ b/app/models/guidance_group.rb @@ -7,7 +7,7 @@ has_and_belongs_to_many :plans, join_table: :plans_guidance_groups # depricated but needed for migration "single_group_for_guidance" # has_and_belongs_to_many :guidances, join_table: "guidance_in_group" - + ## # Possibly needed for active_admin @@ -22,10 +22,14 @@ # # What do they do? do they do it efficiently, and do we need them? - - - - + # Retrieves every guidance group associated to an org + scope :by_org, -> (org) { + where(org_id: org.id) + } + scope :search, -> (term) { + search_pattern = "%#{term}%" + where("name LIKE ?", search_pattern) + } ## # Converts the current guidance group to a string containing the display name. @@ -49,7 +53,7 @@ # @return [Array<GuidanceGroup>] a list of guidance groups def self.guidance_groups_excluding(excluded_orgs) excluded_org_ids = Array.new - + if excluded_orgs.is_a?(Array) excluded_orgs.each do |org| excluded_org_ids << org.id @@ -57,7 +61,7 @@ else excluded_org_ids << excluded_orgs end - + return_orgs = GuidanceGroup.where("org_id NOT IN (?)", excluded_org_ids) return return_orgs end @@ -106,7 +110,7 @@ managing_org_groups = Org.includes(guidance_groups: [guidances: :themes]).managing_orgs.collect{|org| org.guidance_groups} # find all groups owned by a Funder organisation - funder_groups = Org.includes(:guidance_groups).funders.collect{|org| org.guidance_groups} + funder_groups = Org.includes(:guidance_groups).funder.collect{|org| org.guidance_groups} organisation_groups = [user.org.guidance_groups] diff --git a/app/models/language.rb b/app/models/language.rb index 2d02783..1d86727 100644 --- a/app/models/language.rb +++ b/app/models/language.rb @@ -11,4 +11,6 @@ scope :sorted_by_abbreviation, -> { all.order(:abbreviation) } scope :default, -> { where(default_language: true).first } + # Retrieves the id for a given abbreviation of a language + scope :id_for, -> (abbreviation) { where(abbreviation: abbreviation).pluck(:id).first } end \ No newline at end of file diff --git a/app/models/org.rb b/app/models/org.rb index 9abbe2e..dee239c 100644 --- a/app/models/org.rb +++ b/app/models/org.rb @@ -2,11 +2,11 @@ include GlobalHelpers include FlagShihTzu extend Dragonfly::Model::Validations + validates_with OrgLinksValidator - ## - # Sort order: Name ASC - default_scope { order(name: :asc) } - + # Stores links as an JSON object: { org: [{"link":"www.example.com","text":"foo"}, ...] } + # The links are validated against custom validator allocated at validators/template_links_validator.rb + serialize :links, JSON ## # Associations @@ -16,28 +16,30 @@ has_many :templates has_many :users has_many :annotations - + has_and_belongs_to_many :token_permission_types, join_table: "org_token_permissions", unique: true + has_many :org_identifiers + has_many :identifier_schemes, through: :org_identifiers + ## # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 - attr_accessible :abbreviation, :banner_text, :logo, :remove_logo, - :logo_file_name, :name, :target_url, + attr_accessible :abbreviation, :logo, :remove_logo, + :logo_file_name, :name, :links, :organisation_type_id, :wayfless_entity, :parent_id, :sort_name, - :token_permission_type_ids, :language_id, :contact_email, - :language, :org_type, :region, :token_permission_types, - :guidance_group_ids, :is_other, :region_id, :logo_uid, :logo_name - + :token_permission_type_ids, :language_id, :contact_email, :contact_name, + :language, :org_type, :region, :token_permission_types, + :guidance_group_ids, :is_other, :region_id, :logo_uid, :logo_name, + :feedback_enabled, :feedback_email_subject, :feedback_email_msg ## # Validators - validates :contact_email, email: true, allow_nil: true +# validates :contact_email, email: true, allow_nil: true validates :name, presence: {message: _("can't be blank")}, uniqueness: {message: _("must be unique")} # allow validations for logo upload dragonfly_accessor :logo do after_assign :resize_image end - validates_property :height, of: :logo, in: (0..100), message: _("height must be less than 100px") validates_property :format, of: :logo, in: ['jpeg', 'png', 'gif','jpg','bmp'], message: _("must be one of the following formats: jpeg, jpg, png, gif, bmp") validates_size_of :logo, maximum: 500.kilobytes, message: _("can't be larger than 500KB") @@ -54,16 +56,20 @@ # Predefined queries for retrieving the managain organisation and funders scope :managing_orgs, -> { where(abbreviation: Rails.configuration.branding[:organisation][:abbreviation]) } - scope :funders, -> { where(org_type: 2) } - scope :institutions, -> { where(org_type: 1) } + scope :search, -> (term) { + search_pattern = "%#{term}%" + where("orgs.name LIKE ? OR orgs.contact_email LIKE ?", search_pattern, search_pattern) + } + + after_create :create_guidance_group # EVALUATE CLASS AND INSTANCE METHODS BELOW # # What do they do? do they do it efficiently, and do we need them? # Determines the locale set for the organisation - # @return String or nil + # @return String or nil def get_locale if !self.language.nil? return self.language.abbreviation @@ -82,23 +88,20 @@ # defaults to none if no org type present # # @return [String] - def type - if self.institution? - return "Institution" - elsif self.funder? - return "Funder" - elsif self.organisation? - return "Organisation" - elsif self.research_institute? - return "Research Institute" - elsif self.project? - return "Project" - elsif self.school? - return "School" - end - return "None" + def org_type_to_s + ret = [] + ret << "Institution" if self.institution? + ret << "Funder" if self.funder? + ret << "Organisation" if self.organisation? + ret << "Research Institute" if self.research_institute? + ret << "Project" if self.project? + ret << "School" if self.school? + return (ret.length > 0 ? ret.join(', ') : "None") end + def funder_only? + self.org_type == Org.org_type_values_for(:funder).min + end ## # returns the name of the organisation @@ -121,98 +124,6 @@ end ## - # finds all organisations who have a parent of the passed organisation type - # - # @param [String] the name of an organisation type - # @return [Array<Organisation>] -=begin - def self.orgs_with_parent_of_type(org_type) - parents = OrganisationType.find_by_name(org_type).organisations - children = Array.new - parents.each do |parent| - children += parent.children - end - return children - end - - ## - # returns a list of all guidance groups belonging to other organisations - # - # @return [Array<GuidanceGroup>] - def self.other_organisations - org_types = [GlobalHelpers.constant("organisation_types.funder")] - organisations_list = [] - org_types.each do |ot| - new_org_obejct = OrganisationType.find_by_name(ot) - - org_with_guidance = GuidanceGroup.joins(new_org_obejct.organisations) - - organisations_list = organisations_list + org_with_guidance - end - return organisations_list - end - - - ## - # returns a list of all guidance groups belonging to other organisations - # - # @return [Array<GuidanceGroup>] - def self.other_organisations - org_types = [GlobalHelpers.constant("organisation_types.funder")] - organisations_list = [] - org_types.each do |ot| - new_org_obejct = OrganisationType.find_by_name(ot) - - org_with_guidance = GuidanceGroup.joins(new_org_obejct.organisations) - - organisations_list = organisations_list + org_with_guidance - end - return organisations_list - end - - ## - # returns a list of all sections of a given version from this organisation and it's parents - # - # @param version_id [Integer] version number of the section - # @return [Array<Section>] list of sections - def all_sections(version_id) - if parent.nil? - secs = sections.where("version_id = ?", version_id) - if secs.nil? then - secs = Array.new - end - return secs - else - return sections.where("version_id = ? ", version_id).all + parent.all_sections(version_id) - end - end - - ## - # returns the guidance groups of this organisation and all of it's children - # - # @return [Array<GuidanceGroup>] list of guidance groups - def all_guidance_groups - ggs = guidance_groups - children.each do |c| - ggs = ggs + c.all_guidance_groups - end - return ggs - end - - ## - # returns the highest parent organisation in the tree - # - # @return [organisation] the root organisation - def root - if parent.nil? - return self - else - return parent.root - end - end -=end - - ## # returns all published templates belonging to the organisation # # @return [Array<Dmptemplate>] published dmptemplates @@ -228,16 +139,36 @@ end end end - + + def org_admins + User.joins(:perms).where("users.org_id = ? AND perms.name IN (?)", self.id, + ['grant_permissions', 'modify_templates', 'modify_guidance', 'change_org_details']) + end + + def plans + Plan.includes(:template, :phases, :roles, :users).joins(:roles, :users).where('users.org_id = ? AND roles.access IN (?)', + self.id, Role.access_values_for(:owner).concat(Role.access_values_for(:administrator))) + end + + def grant_api!(token_permission_type) + self.token_permission_types << token_permission_type unless self.token_permission_types.include? token_permission_type + end + private ## # checks size of logo and resizes if necessary # def resize_image unless logo.nil? - if logo.height != 100 - self.logo = logo.thumb('x100') # resize height and maintain aspect ratio + if logo.height != 75 + self.logo = logo.thumb('x75') # resize height and maintain aspect ratio end end - end + end + + # creates a dfefault Guidance Group on create on the Org + def create_guidance_group + GuidanceGroup.create(name: self.abbreviation? ? self.abbreviation : self.name , org_id: self.id) + end + end diff --git a/app/models/org_identifier.rb b/app/models/org_identifier.rb new file mode 100644 index 0000000..db61fd6 --- /dev/null +++ b/app/models/org_identifier.rb @@ -0,0 +1,13 @@ +class OrgIdentifier < ActiveRecord::Base + belongs_to :org + belongs_to :identifier_scheme + + # Should only be able to have one identifier per scheme! + validates_uniqueness_of :identifier_scheme, scope: :org + + validates :identifier, :org, :identifier_scheme, presence: {message: _("can't be blank")} + + def attrs=(hash) + write_attribute(:attrs, (hash.is_a?(Hash) ? hash.to_json.to_s : '{}')) + end +end \ No newline at end of file diff --git a/app/models/perm.rb b/app/models/perm.rb index 58aad3e..fcc429f 100644 --- a/app/models/perm.rb +++ b/app/models/perm.rb @@ -9,17 +9,6 @@ #attr_accessible :name, :as => [:default, :admin] validates :name, presence: {message: _("can't be blank")}, uniqueness: {message: _("must be unique")} - - ## - # Constant perms - #ADD_ORGS = Perm.where(name: 'add_organisations').first.freeze - #CHANGE_AFFILIATION = Perm.where(name: 'change_org_affiliation').first.freeze - #GRANT_PERMISSIONS = Perm.where(name: 'grant_permissions').first.freeze - #MODIFY_TEMPLATES = Perm.where(name: 'modify_templates').first.freeze - #MODIFY_GUIDANCE = Perm.where(name: 'modify_guidance').first.freeze - #USE_API = Perm.where(name: 'use_api').first.freeze - #CHANGE_ORG_DETAILS = Perm.where(name: 'change_org_details').first.freeze - #GRANT_API = Perm.where(name: 'grant_api_to_orgs').first.freeze scope :add_orgs, -> {Perm.find_by(name: 'add_organisations')} scope :change_affiliation, -> {Perm.find_by(name: 'change_org_affiliation')} diff --git a/app/models/phase.rb b/app/models/phase.rb index 5c2323f..61f9765 100644 --- a/app/models/phase.rb +++ b/app/models/phase.rb @@ -30,13 +30,19 @@ validates :title, :number, :template, presence: {message: _("can't be blank")} - + scope :titles, -> (template_id) { + Phase.where(template_id: template_id).select(:id, :title) + } # EVALUATE CLASS AND INSTANCE METHODS BELOW # # What do they do? do they do it efficiently, and do we need them? - - - + + + # Callbacks + after_save do |phase| + # Updates the template.updated_at attribute whenever a phase has been created/updated + phase.template.touch + end ## @@ -106,4 +112,21 @@ return phase_copy end + # Returns the number of answered question for the phase. + def num_answered_questions(plan) + return 0 if plan.nil? + return sections.reduce(0) do |m, s| + m + s.num_answered_questions(plan) + end + end + + # Returns the number of questions for a phase. Note, this method becomes useful + # for when sections and their questions are eager loaded so that avoids SQL queries. + def num_questions + n = 0 + self.sections.each do |s| + n+= s.questions.size() + end + return n + end end diff --git a/app/models/plan.rb b/app/models/plan.rb index a71bcff..229cb24 100644 --- a/app/models/plan.rb +++ b/app/models/plan.rb @@ -1,5 +1,6 @@ class Plan < ActiveRecord::Base - + include ConditionalUserMailer + include ExportablePlan before_validation :set_creation_defaults ## @@ -20,7 +21,6 @@ has_many :roles - # COMMENTED OUT THE DIRECT CONNECTION HERE TO Users to prevent assignment of users without an access_level specified (currently defaults to creator) # has_many :users, through: :roles @@ -32,7 +32,8 @@ :exported_plans, :project, :title, :template, :grant_number, :identifier, :principal_investigator, :principal_investigator_identifier, :description, :data_contact, :funder_name, :visibility, :exported_plans, - :roles, :users, :org, :as => [:default, :admin] + :roles, :users, :org, :data_contact_email, :data_contact_phone, :feedback_requested, + :principal_investigator_email, :as => [:default, :admin] accepts_nested_attributes_for :roles # public is a Ruby keyword so using publicly @@ -52,8 +53,32 @@ # Scope queries # Note that in ActiveRecord::Enum the mappings are exposed through a class method with the pluralized attribute name (e.g visibilities rather than visibility) - scope :publicly_visible, -> { where(:visibility => visibilities[:publicly_visible]).order(:title => :asc) } + scope :publicly_visible, -> { includes(:template).where(:visibility => visibilities[:publicly_visible]) } + # Retrieves any plan in which the user has an active role and it is not a reviewer + scope :active, -> (user) { + includes([:template, :roles]).where({ "roles.active": true, "roles.user_id": user.id }).where(Role.not_reviewer_condition) + } + + # Retrieves any plan organisationally or publicly visible for a given org id + scope :organisationally_or_publicly_visible, -> (user) { + includes(:template, {roles: :user}) + .where({ + visibility: [visibilities[:organisationally_visible], visibilities[:publicly_visible]], + "roles.access": Role.access_values_for(:creator, :administrator, :editor, :commenter).min, + "users.org_id": user.org_id}) + .where(['NOT EXISTS (SELECT 1 FROM roles WHERE plan_id = plans.id AND user_id = ?)', user.id]) + } + + scope :search, -> (term) { + search_pattern = "%#{term}%" + joins(:template).where("plans.title LIKE ? OR templates.title LIKE ?", search_pattern, search_pattern) + } + + # Retrieves plan, template, org, phases, sections and questions + scope :overview, -> (id) { + Plan.includes(:phases, :sections, :questions, template: [ :org ]).find(id) + } ## # Settings for the template has_settings :export, class_name: 'Settings::Template' do |s| @@ -80,10 +105,10 @@ # returns the template for this plan, or generates an empty template and returns that # # @return [Dmptemplate] the template associated with this plan - def dmptemplate - #self.project.try(:dmptemplate) || Dmptemplate.new - self.template - end + def dmptemplate + #self.project.try(:dmptemplate) || Dmptemplate.new + self.template + end @@ -105,24 +130,24 @@ # @param qid [Integer] the id for the question to find the answer for # @param create_if_missing [Boolean] if true, will genereate a default answer to the question # @return [Answer,nil] the most recent answer to the question, or a new question with default value, or nil - def answer(qid, create_if_missing = true) - answer = answers.where(:question_id => qid).order("created_at DESC").first - question = Question.find(qid) - if answer.nil? && create_if_missing then - answer = Answer.new - answer.plan_id = id - answer.question_id = qid - answer.text = question.default_value - default_options = Array.new - question.question_options.each do |option| - if option.is_default - default_options << option - end - end - answer.question_options = default_options - end - return answer - end + def answer(qid, create_if_missing = true) + answer = answers.where(:question_id => qid).order("created_at DESC").first + question = Question.find(qid) + if answer.nil? && create_if_missing then + answer = Answer.new + answer.plan_id = id + answer.question_id = qid + answer.text = question.default_value + default_options = Array.new + question.question_options.each do |option| + if option.is_default + default_options << option + end + end + answer.question_options = default_options + end + return answer + end # TODO: This just retrieves all of the guidance associated with the themes within the template # so why are we transferring it here to the plan? @@ -173,88 +198,161 @@ end return ggroups.uniq end + + ## + # Sets up the plan for feedback: + # emails confirmation messages to owners + # emails org admins and org contact + # adds org admins to plan with the 'reviewer' Role + def request_feedback(user) + Plan.transaction do + begin + val = Role.access_values_for(:reviewer, :commenter).min + self.feedback_requested = true + + # Share the plan with each org admin as the reviewer role + admins = user.org.org_admins + admins.each do |admin| + self.roles << Role.new(user: admin, access: val) + end - - - - ## - # returns the guidances associated with the project's organisation, for a specified question - # - # @param question [Question] the question to find guidance for - # @return array of hashes with orgname, themes and the guidance itself - def guidance_for_question(question) - guidances = [] - - # add in the guidance for the template org - unless self.template.org.nil? then - self.template.org.guidance_groups.each do |group| - group.guidances.each do |guidance| - common_themes = guidance.themes.all & question.themes.all - if common_themes.length > 0 - guidances << { orgname: self.template.org.name, theme: common_themes.join(','), guidance: guidance } + if self.save! + # Send an email confirmation to the owners and co-owners + owners = User.joins(:roles).where('roles.plan_id =? AND roles.access IN (?)', self.id, Role.access_values_for(:administrator)) + deliver_if(recipients: owners, key: 'users.feedback_requested') do |r| + UserMailer.feedback_confirmation(r, self, user).deliver_now end - end - end - end - - # add in the guidance for the user's org - unless self.owner.nil? - unless self.owner.org.nil? then - self.owner.org.guidance_groups.each do |group| - group.guidances.each do |guidance| - common_themes = guidance.themes.all & question.themes.all - if common_themes.length > 0 - guidances << { orgname: self.template.org.name, theme: common_themes.join(','), guidance: guidance } - end + # Send an email to all of the org admins as well as the Org's administrator email + if user.org.contact_email.present? && !admins.collect{ |u| u.email }.include?(user.org.contact_email) + admins << User.new(email: user.org.contact_email, firstname: user.org.contact_name) end + deliver_if(recipients: admins, key: 'admins.feedback_requested') do |r| + UserMailer.feedback_notification(r, self, user).deliver_now + end + true + else + false end + rescue Exception => e + Rails.logger.error e + false end end - - # Get guidance by theme from any guidance groups currently selected - self.guidance_groups.each do |group| - group.guidances.each do |guidance| - common_themes = guidance.themes.all & question.themes.all - if common_themes.length > 0 - guidances << { orgname: self.template.org.name, theme: common_themes.join(','), guidance: guidance } - end - end - end - - return guidances end - - - ## - # adds the given guidance to a hash indexed by a passed guidance group and theme - # - # @param guidance_array [{GuidanceGroup => {Theme => Array<Gudiance>}}] the passed hash of arrays of guidances. Indexed by GuidanceGroup and Theme. - # @param guidance_group [GuidanceGroup] the guidance_group index of the hash - # @param theme [Theme] the theme object for the GuidanceGroup - # @param guidance [Guidance] the guidance object to be appended to the correct section of the array - # @return [{GuidanceGroup => {Theme => Array<Guidance>}}] the updated object which was passed in - def add_guidance_to_array(guidance_array, guidance_group, theme, guidance) - if guidance_array[guidance_group].nil? then - guidance_array[guidance_group] = {} - end - if theme.nil? then - if guidance_array[guidance_group]["no_theme"].nil? then - guidance_array[guidance_group]["no_theme"] = [] - end - if !guidance_array[guidance_group]["no_theme"].include?(guidance) then - guidance_array[guidance_group]["no_theme"].push(guidance) - end - else - if guidance_array[guidance_group][theme].nil? then - guidance_array[guidance_group][theme] = [] - end - if !guidance_array[guidance_group][theme].include?(guidance) then - guidance_array[guidance_group][theme].push(guidance) + # Finalizes the feedback for the plan: + # emails confirmation messages to owners + # sets flag on plans.feedback_requested to false + # removes org admins from the 'reviewer' Role for the Plan + def complete_feedback(org_admin) + Plan.transaction do + begin + self.feedback_requested = false + + # Remove the org admins reviewer role from the plan + vals = Role.access_values_for(:reviewer) + self.roles.delete(Role.where(plan: self, access: vals)) + + if self.save! + # Send an email confirmation to the owners and co-owners + owners = User.joins(:roles).where('roles.plan_id =? AND roles.access IN (?)', self.id, Role.access_values_for(:administrator)) + deliver_if(recipients: owners, key: 'users.feedback_provided') do |r| + UserMailer.feedback_complete(r, self, org_admin).deliver_now + end + true + else + false + end + rescue Exception => e + Rails.logger.error e + false end end - return guidance_array + end + + # Returns all of the plan's available guidance by question as a hash for use on the write plan page + # { + # QUESTION: { + # GUIDANCE_GROUP: { + # THEME: [GUIDANCE, GUIDANCE], + # THEME: [GUIDANCE] + # } + # } + # } + def guidance_by_question_as_hash + # Get all of the selected guidance groups for the plan + guidance_groups_ids = self.guidance_groups.collect(&:id) + guidance_groups = GuidanceGroup.joins(:org).where("guidance_groups.published = ? AND guidance_groups.id IN (?) AND orgs.id != ?", + true, guidance_groups_ids, self.template.org.id) + + # Gather all of the Themes used in the plan as a hash + # { + # QUESTION: [THEME, THEME], + # QUESTION: [THEME] + # } + question_themes = {} + themes_used = [] + self.questions.joins(:themes).pluck('questions.id', 'themes.title').each do |qt| + themes_used << qt[1] unless themes_used.include?(qt[1]) + question_themes[qt[0]] = [] unless question_themes[qt[0]].present? + question_themes[qt[0]] << qt[1] unless question_themes[qt[0]].include?(qt[1]) + end + + # Gather all of the Guidance available for the themes used in the plan as a hash + # { + # THEME: { + # GUIDANCE_GROUP: [GUIDANCE, GUIDANCE], + # GUIDANCE_GROUP: [GUIDANCE] + # } + # } + theme_guidance = {} + GuidanceGroup.includes(guidances: :themes).joins(:guidances). + where('guidance_groups.published = ? AND guidances.published = ? AND themes.title IN (?) AND guidance_groups.id IN (?)', true, true, themes_used, guidance_groups.collect(&:id)). + pluck('guidance_groups.name', 'themes.title', 'guidances.text').each do |tg| + + theme_guidance[tg[1]] = {} unless theme_guidance[tg[1]].present? + theme_guidance[tg[1]][tg[0]] = [] unless theme_guidance[tg[1]][tg[0]].present? + theme_guidance[tg[1]][tg[0]] << tg[2] unless theme_guidance[tg[1]][tg[0]].include?(tg[2]) + end + + # Generate a hash for the view that contains all of a question guidance + # { + # QUESTION: { + # GUIDANCE_GROUP: { + # THEME: [GUIDANCE, GUIDANCE], + # THEME: [GUIDANCE] + # } + # } + # } + question_guidance = {} + question_themes.keys.each do |question| + ggs = {} + # Gather all of the guidance groups applicable to the themes assigned to the question + groups = [] + question_themes[question].each do |theme| + groups << theme_guidance[theme].keys if theme_guidance[theme].present? + end + + # Loop through all of the applicable guidance groups and collect their themed guidance + groups.flatten.uniq.each do |guidance_group| + guidances_by_theme = {} + + # Collect all of the guidances for each theme used by the question + question_themes[question].each do |theme| + if theme_guidance[theme].present? && theme_guidance[theme][guidance_group].present? + guidances_by_theme[theme] = [] unless guidances_by_theme[theme].present? + guidances_by_theme[theme] = theme_guidance[theme][guidance_group] + end + end + + ggs[guidance_group] = guidances_by_theme unless ggs[guidance_group] + end + + question_guidance[question] = ggs + end + + question_guidance end ## @@ -262,36 +360,76 @@ # # @param user_id [Integer] the id for a user # @return [Boolean] true if user can edit the plan - def editable_by?(user_id) + def editable_by?(user_id) user_id = user_id.id if user_id.is_a?(User) - role = roles.where(user_id: user_id).first - return role.present? && role.editor? - end + has_role(user_id, :editor) + end ## # determines if the plan is readable by the specified user - # TODO: introduce explicit readable rather than implicit - # currently role with no flags = readable # # @param user_id [Integer] the id for a user # @return [Boolean] true if the user can read the plan - def readable_by?(user_id) + def readable_by?(user_id) + user = user_id.is_a?(User) ? user_id : User.find(user_id) + owner_orgs = self.owner_and_coowners.collect(&:org) + + # Super Admins can view plans read-only, Org Admins can view their Org's plans + # otherwise the user must have the commenter role + (user.can_super_admin? || + user.can_org_admin? && owner_orgs.include?(user.org) || + has_role(user.id, :commenter)) + end + + ## + # determines if the plan is readable by the specified user + # + # @param user_id [Integer] the id for a user + # @return [Boolean] true if the user can read the plan + def commentable_by?(user_id) user_id = user_id.id if user_id.is_a?(User) - role = roles.where(user_id: user_id).first - return role.present? - end + has_role(user_id, :commenter) + end ## # determines if the plan is administerable by the specified user # # @param user_id [Integer] the id for the user # @return [Boolean] true if the user can administer the plan - def administerable_by?(user_id) + def administerable_by?(user_id) user_id = user_id.id if user_id.is_a?(User) - role = roles.where(user_id: user_id).first - return role.present? && role.administrator? - end + has_role(user_id, :administrator) + end + ## + # determines if the plan is owned by the specified user + # + # @param user_id [Integer] the id for the user + # @return [Boolean] true if the user can administer the plan + def owned_by?(user_id) + user_id = user_id.id if user_id.is_a?(User) + has_role(user_id, :creator) + end + + ## + # determines if the plan is reviewable by the specified user + # + # @param user_id [Integer] the id for the user + # @return [Boolean] true if the user can administer the plan + def reviewable_by?(user_id) + user_id = user_id.id if user_id.is_a?(User) + has_role(user_id, :reviewer) + end + + ## + # determines whether or not the specified user has any rol on the plan + # + # @param user_id [Integer] the id for the user + # @return [Boolean] true if the user has any rol + def any_role?(user) + user_id = user.id if user.is_a?(User) + !self.roles.index{ |rol| rol.user_id == user_id }.nil? + end ## # defines and returns the status of the plan @@ -433,199 +571,6 @@ return status end -# TODO: Guessing this isn't in use since it still refers to Project and Version -=begin - ## - # defines and returns the details for the plan - # details consists of a hash of: project_title, phase_title, and for each section, - # section: title, question text for each question, answer type and answer value - # - # @return [Details] - def details - details = { - "project_title" => project.title, - "phase_title" => version.phase.title, - "sections" => {} - } - sections.sort_by(&:"number").each do |s| - details["sections"][s.number] = {} - details["sections"][s.number]["title"] = s.title - details["sections"][s.number]["questions"] = {} - s.questions.order("number").each do |q| - details["sections"][s.number]["questions"][q.number] = {} - details["sections"][s.number]["questions"][q.number]["question_text"] = q.text - answer = answer(q.id, false) - if ! answer.nil? then - q_format = q.question_format - if (q_format.title == t("helpers.checkbox") || q_format.title == t("helpers.multi_select_box") || - q_format.title == t("helpers.radio_buttons") || q_format.title == t("helpers.dropdown")) then - details["sections"][s.number]["questions"][q.number]["selections"] = {} - answer.options.each do |o| - details["sections"][s.number]["questions"][q.number]["selections"][o.number] = o.text - end - end - details["sections"][s.number]["questions"][q.number]["answer_text"] = answer.text - end - end - end - return details - end -=end - -# TODO: commenting this old lock stuff out since PlanSection is gone and we wanted to get rid of it -=begin - ## - # determines wether or not a specified section of a plan is locked to a specified user and returns a status hash - # - # @param section_id [Integer] the setion to determine if locked - # @param user_id [Integer] the user to determine if locked for - # @return [Hash{String => Hash{String => Boolean, nil, String, Integer}}] - def locked(section_id, user_id) - plan_section = plan_sections.where("section_id = ? AND user_id != ? AND release_time > ?", section_id, user_id, Time.now).last - if plan_section.nil? then - status = { - "locked" => false, - "locked_by" => nil, - "timestamp" => nil, - "id" => nil - } - else - status = { - "locked" => true, - "locked_by" => plan_section.user.name, - "timestamp" => plan_section.updated_at, - "id" => plan_section.id - } - end - end - - ## - # for each section, lock the section with the given user_id - # - # @param user_id [Integer] the id for the user who can use the sections - def lock_all_sections(user_id) - sections.each do |s| - lock_section(s.id, user_id, 1800) - end - end - - ## - # for each section, unlock the section - # - # @param user_id [Integer] the id for the user to unlock the sections for - def unlock_all_sections(user_id) - plan_sections.where(:user_id => user_id).order("created_at DESC").each do |lock| - lock.delete - end - end - - ## - # for each section, unlock the section - # Not sure how this is different from unlock_all_sections - # - # @param user_id [Integer] - def delete_recent_locks(user_id) - plan_sections.where(:user_id => user_id).each do |lock| - lock.delete - end - end - - ## - # Locks the specified section to only be used by the specified user, for the number of secconds specified - # - # @param section_id [Integer] the id of the section to be locked - # @param user_id [Integer] the id of the user who can use the section - # @param release_time [Integer] the number of secconds the section will be locked for, defaults to 60 - # @return [Boolean] wether or not the section was locked - def lock_section(section_id, user_id, release_time = 60) - status = locked(section_id, user_id) - if ! status["locked"] then - plan_section = PlanSection.new - plan_section.plan_id = id - plan_section.section_id = section_id - plan_section.release_time = Time.now + release_time.seconds - plan_section.user_id = user_id - plan_section.save - elsif status["current_user"] then - plan_section = PlanSection.find(status["id"]) - plan_section.release_time = Time.now + release_time.seconds - plan_section.save - else - return false - end - end - - ## - # unlocks the specified section for the specified user - # - # @param section_id [Integer] the id for the section to be unlocked - # @param user_id [Integer] the id for the user for whom the section was previously locked - # @return [Boolean] wether or not the lock was removed - def unlock_section(section_id, user_id) - plan_sections.where(:section_id => section_id, :user_id => user_id).order("created_at DESC").each do |lock| - lock.delete - end - end -=end - -# TODO: Commenting out because this method appears below as well so this one is overwritten -=begin - ## - # returns the time of either the latest answer to any question, or the latest update to the model - # - # @return [DateTime] the time at which the plan was last changed - def latest_update - if answers.any? then - last_answered = answers.order("updated_at DESC").first.updated_at - if last_answered > updated_at then - return last_answered - else - return updated_at - end - else - return updated_at - end - end -=end - -# TODO: Guessing this isn't in use since it still refers to Project and Version -=begin - ## - # returns an array of hashes. Each hash contains the question's id, the answer_id, - # the answer_text, the answer_timestamp, and the answer_options - # - # @param section_id [Integer] the section to find answers of - # @return [Array<Hash{String => nil,String,Integer,DateTime}] - def section_answers(section_id) - section = Section.find(section_id) - section_questions = Array.new - counter = 0 - section.questions.each do |q| - section_questions[counter] = {} - section_questions[counter]["id"] = q.id - #section_questions[counter]["multiple_choice"] = q.multiple_choice - q_answer = answer(q.id, false) - if q_answer.nil? then - section_questions[counter]["answer_id"] = nil - if q.suggested_answers.find_by_organisation_id(project.organisation_id).nil? then - section_questions[counter]["answer_text"] = "" - else - section_questions[counter]["answer_text"] = q.default_value - end - section_questions[counter]["answer_timestamp"] = nil - section_questions[counter]["answer_options"] = Array.new - else - section_questions[counter]["answer_id"] = q_answer.id - section_questions[counter]["answer_text"] = q_answer.text - section_questions[counter]["answer_timestamp"] = q_answer.created_at - section_questions[counter]["answer_options"] = q_answer.options.pluck(:id) - end - counter = counter + 1 - end - return section_questions - end -=end - ## # assigns the passed user_id to the creater_role for the project @@ -637,83 +582,6 @@ add_user(user_id, true, true, true) end - - -# TODO: commenting these out because they are overriden by private methods below, so this -# is unreachable -=begin - ## - # Based on the height of the text gathered so far and the available vertical - # space of the pdf, estimate a percentage of how much space has been used. - # This is highly dependent on the layout in the pdf. A more accurate approach - # would be to render the pdf and check how much space had been used, but that - # could be very slow. - # NOTE: This is only an estimate, rounded up to the nearest 5%; it is intended - # for guidance when editing plan data, not to be 100% accurate. - # - # @param used_height [Integer] an estimate of the height used so far - # @return [Integer] the estimate of space used of an A4 portrain - def estimate_space_used(used_height) - @formatting ||= self.settings(:export).formatting - - return 0 unless @formatting[:font_size] > 0 - - margin_height = @formatting[:margin][:top].to_i + @formatting[:margin][:bottom].to_i - page_height = A4_PAGE_HEIGHT - margin_height # 297mm for A4 portrait - available_height = page_height * self.template.settings(:export).max_pages - - percentage = (used_height / available_height) * 100 - (percentage / ROUNDING).ceil * ROUNDING # round up to nearest five - end - - ## - # Take a guess at the vertical height (in mm) of the given text based on the - # font-size and left/right margins stored in the plan's settings. - # This assumes a fixed-width for each glyph, which is obviously - # incorrect for the font-face choices available; the idea is that - # they'll hopefully average out to that in the long-run. - # Allows for hinting different font sizes (offset from base via font_size_inc) - # and vertical margins (i.e. for heading text) - # - # @param text [String] the text to estimate size of - # @param font_size_inc [Integer] the size of the font of the text, defaults to 0 - # @param vertical_margin [Integer] the top margin above the text, defaults to 0 - def height_of_text(text, font_size_inc = 0, vertical_margin = 0) - @formatting ||= self.settings(:export).formatting - @margin_width ||= @formatting[:margin][:left].to_i + @formatting[:margin][:right].to_i - @base_font_size ||= @formatting[:font_size] - - return 0 unless @base_font_size > 0 - - font_height = FONT_HEIGHT_CONVERSION_FACTOR * (@base_font_size + font_size_inc) - font_width = font_height * FONT_WIDTH_HEIGHT_RATIO # Assume glyph width averages at 2/5s the height - leading = font_height / 2 - - chars_in_line = (A4_PAGE_WIDTH - @margin_width) / font_width # 210mm for A4 portrait - num_lines = (text.length / chars_in_line).ceil - - (num_lines * font_height) + vertical_margin + leading - end -=end - -# TODO: What are these used for? Should just be using self.org and self.org.funder? -=begin - ## - # sets a new funder for the project - # defaults to the first dmptemplate if the current template is nill and the funder has more than one dmptemplate - # - # @param new_funder_id [Integer] the id for a new funder - # @return [Organisation] the new funder - def funder_id=(new_funder_id) - if new_funder_id != "" then - new_funder = Org.find(new_funder_id); - if new_funder.templates.count >= 1 && self.template.nil? then - self.template = new_funder.templates.first - end - end - end -=end - ## # returns the funder id for the plan # @@ -742,86 +610,6 @@ end end -=begin - ## - # returns the name of the funder for the project - # - # @return [String] the name fo the funder for the project - def funder_name - if self.funder.nil? - return read_attribute(:funder_name) - else - return self.funder.name - end - end - - ## - # defines a new funder_name for the project. - # - # @param new_funder_name [String] the string name of the new funder - # @return [Integer, nil] the org_id of the new funder - def funder_name=(new_funder_name) - write_attribute(:funder_name, new_funder_name) - org_table = Org.arel_table - existing_org = Org.where(org_table[:name].matches(new_funder_name)) - if existing_org.nil? - existing_org = Org.where(org_table[:abbreviation].matches(new_funder_name)) - end - unless existing_org.empty? - self.funder_id=existing_org.id - end - end - - ## - # sets a new institution_id if there is no current organisation - # - # @param new_institution_id [Integer] the id for the new institution - # @return [Integer, Bool] false if an organisation exists, or the id of the set org if a new organisation is set - def institution_id=(new_institution_id) - if organisation.nil? then - self.organisation_id = new_institution_id - end - end - - ## - # returns the organisation which is root over the owning organisation - # - # @return [Integer, nil] the organisation_id or nil - def institution_id -# if organisation.nil? -# return nil -# else -# return organisation.root.id -# end - return template.org.id - end - - ## - # defines a new organisation_id for the project - # but is confusingly labled unit_id - # - # @param new_unit_id [Integer] - # @return [Integer, Boolean] the new organisation ID or false if no unit_id was passed - def unit_id=(new_unit_id) - unless new_unit_id.nil? ||new_unit_id == "" - self.organisation_id = new_unit_id - end - end - - ## - # returns the organisation_id or nil - # again seems redundant - # - # @return [nil, Integer] nil if no organisation, or the id if there is an organisation specified - def unit_id - if organisation.nil? || organisation.parent_id.nil? - return nil - else - return organisation_id - end - end -=end - ## # assigns the passed user_id as an editor for the project # gives the user rights to read and edit @@ -849,42 +637,6 @@ add_user(user_id, true, true) end -# TODO: ProjectGroup doesn't exist anymore so commenting these out -=begin - ## - # returns the projects which the user can atleast read - # - # @param user_id [Integer] the user to lookup projects for - # @return [Array<Project>] list of all projects the user can atleast read - def self.projects_for_user(user_id) - projects = Array.new - groups = ProjectGroup.where("user_id = ?", user_id) - unless groups.nil? then - groups.each do |group| - unless group.project.nil? then - projects << group.project - end - end - end - return projects - end - - ## - # whether or not the specified user_id created this project - # should be renamed to created_by? - # - # @param user_id [Integer] the user to check the priveleges of - # @return [Boolean] true if the user created the project - def created_by(user_id) - user = project_groups.find_by_user_id(user_id) - if (! user.nil?) && user.project_creator then - return true - else - return false - end - end -=end - ## # the datetime for the latest update of this plan # @@ -914,12 +666,23 @@ # # @return [User] the creater of the project def owner - self.roles.each do |role| - if role.creator? - return role.user - end - end - return nil + vals = Role.access_values_for(:creator) + User.joins(:roles).where('roles.plan_id = ? AND roles.access IN (?)', self.id, vals).first + end + + ## + # returns the shared roles of a plan, excluding the creator + def shared + role_values = Role.where(plan: self).where(Role.not_creator_condition).any? + end + + ## + # the owner and co-owners of the project + # + # @return [Users] + def owner_and_coowners + vals = Role.access_values_for(:creator).concat(Role.access_values_for(:administrator)) + User.joins(:roles).where("roles.plan_id = ? AND roles.access IN (?)", self.id, vals) end ## @@ -930,30 +693,9 @@ self.latest_update.to_date end -# TODO: These next 2 reference defunct models so commenting out -=begin - ## - # whether or not the plan is shared with anybody - # - # @return [Boolean] true if the project has been shared - def shared? - self.project_groups.count > 1 - end - - alias_method :shared, :shared? - - ## - # the organisation who owns the project - # - # @return [Dmptemplate,Organisation,String] the template, it's owner, or it's owner's abreviation - def template_owner - self.dmptemplate.try(:organisation).try(:abbreviation) - end -=end - # Returns the number of answered questions from the entire plan def num_answered_questions - return Answer.where(id: answers.map(&:id)).includes({question: :question_format}, :question_options).reduce(0) do |m, a| + return Answer.where(id: answers.map(&:id)).includes({ question: :question_format }, :question_options).reduce(0) do |m, a| if a.is_valid? m+=1 end @@ -966,14 +708,9 @@ self.sections.find { |s| s.id == section_id } end - # Returns the number of questions for a plan. Note, this method becomes useful - # for when sections and their questions are eager loaded so that avoids SQL queries. + # Returns the number of questions for a plan. def num_questions - n = 0 - sections.includes(:questions).joins(:questions).each do |s| - n+= s.questions.length - end - return n + return sections.includes(:questions).joins(:questions).reduce(0){ |m, s| m + s.questions.length } end # the following two methods are for eager loading. One gets used for the plan/show # page and the oter for the plan/edit. The difference is just that one pulls in more than @@ -991,16 +728,88 @@ ]).find(id) end + # Pre-fetched a plan phase together with its sections and questions associated. It also pre-fetches the answers and notes associated to the plan def self.load_for_phase(id, phase_id) - plan = Plan.joins(template: {phases: {sections: :questions}}).where("plans.id = #{id} AND phases.id = #{phase_id} ").includes(template: {phases: {sections: :questions}}).merge(Plan.includes(answers: :notes))[0] - phase = plan.template.phases.first + plan = Plan + .joins(template: { phases: { sections: :questions }}) + .preload(template: { phases: { sections: :questions }}) # Preserves the default order defined in the model relationships + .where("plans.id = :id AND phases.id = :phase_id", { id: id, phase_id: phase_id }) + .merge(Plan.includes(answers: :notes))[0] + phase = plan.template.phases.find {|p| p.id==phase_id.to_i } + return plan, phase end + # deep copy the given plan and all of it's associations + # + # @params [Plan] plan to be deep copied + # @return [Plan] saved copied plan + def self.deep_copy(plan) + plan_copy = plan.dup + plan_copy.title = "Copy of " + plan.title + plan_copy.save! + plan.answers.each do |answer| + answer_copy = Answer.deep_copy(answer) + answer_copy.plan_id = plan_copy.id + answer_copy.save! + end + plan.guidance_groups.each do |guidance_group| + if guidance_group.present? + plan_copy.guidance_groups << GuidanceGroup.where(id: guidance_group.id).first + end + end + return plan_copy + end + # Returns visibility message given a Symbol type visibility passed, otherwise nil + def self.visibility_message(type) + message = { + :organisationally_visible => _('organisational'), + :publicly_visible => _('public'), + :is_test => _('test'), + :privately_visible => _('private') + } + message[type] + end + + # Determines whether or not visibility changes are permitted according to the + # percentage of the plan answered in respect to a threshold defined at application.config + def visibility_allowed? + value=(self.num_answered_questions().to_f/self.num_questions()*100).round(2) + !self.is_test? && value >= Rails.application.config.default_plan_percentage_answered + end + + # Determines whether or not a question (given its id) exists for the self plan + def question_exists?(question_id) + Plan.joins(:questions).exists?(id: self.id, "questions.id": question_id) + end + + # Checks whether or not the number of questions matches the number of valid answers + def no_questions_matches_no_answers? + num_questions = question_ids.length + pre_fetched_answers = Answer + .includes({ question: :question_format }, :question_options) + .where(id: answer_ids) + num_answers = pre_fetched_answers.reduce(0) do |m, a| + if a.is_valid? + m+=1 + end + m + end + return num_questions == num_answers + end private + # Returns whether or not the user has the specified role for the plan + def has_role(user_id, role_as_sym) + if user_id.is_a?(Integer) && role_as_sym.is_a?(Symbol) + vals = Role.access_values_for(role_as_sym) + self.roles.where(user_id: user_id, access: vals, active: true).first.present? + else + false + end + end ## # adds a user to the project @@ -1073,57 +882,57 @@ ## - # Based on the height of the text gathered so far and the available vertical - # space of the pdf, estimate a percentage of how much space has been used. - # This is highly dependent on the layout in the pdf. A more accurate approach - # would be to render the pdf and check how much space had been used, but that - # could be very slow. - # NOTE: This is only an estimate, rounded up to the nearest 5%; it is intended - # for guidance when editing plan data, not to be 100% accurate. + # Based on the height of the text gathered so far and the available vertical + # space of the pdf, estimate a percentage of how much space has been used. + # This is highly dependent on the layout in the pdf. A more accurate approach + # would be to render the pdf and check how much space had been used, but that + # could be very slow. + # NOTE: This is only an estimate, rounded up to the nearest 5%; it is intended + # for guidance when editing plan data, not to be 100% accurate. # # @param used_height [Integer] an estimate of the height used so far # @return [Integer] the estimate of space used of an A4 portrain - def estimate_space_used(used_height) - @formatting ||= self.settings(:export).formatting + def estimate_space_used(used_height) + @formatting ||= self.settings(:export).formatting - return 0 unless @formatting[:font_size] > 0 + return 0 unless @formatting[:font_size] > 0 - margin_height = @formatting[:margin][:top].to_i + @formatting[:margin][:bottom].to_i - page_height = A4_PAGE_HEIGHT - margin_height # 297mm for A4 portrait - available_height = page_height * self.dmptemplate.settings(:export).max_pages + margin_height = @formatting[:margin][:top].to_i + @formatting[:margin][:bottom].to_i + page_height = A4_PAGE_HEIGHT - margin_height # 297mm for A4 portrait + available_height = page_height * self.dmptemplate.settings(:export).max_pages - percentage = (used_height / available_height) * 100 - (percentage / ROUNDING).ceil * ROUNDING # round up to nearest five - end + percentage = (used_height / available_height) * 100 + (percentage / ROUNDING).ceil * ROUNDING # round up to nearest five + end ## - # Take a guess at the vertical height (in mm) of the given text based on the - # font-size and left/right margins stored in the plan's settings. - # This assumes a fixed-width for each glyph, which is obviously - # incorrect for the font-face choices available; the idea is that - # they'll hopefully average out to that in the long-run. - # Allows for hinting different font sizes (offset from base via font_size_inc) - # and vertical margins (i.e. for heading text) + # Take a guess at the vertical height (in mm) of the given text based on the + # font-size and left/right margins stored in the plan's settings. + # This assumes a fixed-width for each glyph, which is obviously + # incorrect for the font-face choices available; the idea is that + # they'll hopefully average out to that in the long-run. + # Allows for hinting different font sizes (offset from base via font_size_inc) + # and vertical margins (i.e. for heading text) # # @param text [String] the text to estimate size of # @param font_size_inc [Integer] the size of the font of the text, defaults to 0 # @param vertical_margin [Integer] the top margin above the text, defaults to 0 - def height_of_text(text, font_size_inc = 0, vertical_margin = 0) - @formatting ||= self.settings(:export).formatting - @margin_width ||= @formatting[:margin][:left].to_i + @formatting[:margin][:right].to_i - @base_font_size ||= @formatting[:font_size] + def height_of_text(text, font_size_inc = 0, vertical_margin = 0) + @formatting ||= self.settings(:export).formatting + @margin_width ||= @formatting[:margin][:left].to_i + @formatting[:margin][:right].to_i + @base_font_size ||= @formatting[:font_size] - return 0 unless @base_font_size > 0 + return 0 unless @base_font_size > 0 - font_height = FONT_HEIGHT_CONVERSION_FACTOR * (@base_font_size + font_size_inc) - font_width = font_height * FONT_WIDTH_HEIGHT_RATIO # Assume glyph width averages at 2/5s the height - leading = font_height / 2 + font_height = FONT_HEIGHT_CONVERSION_FACTOR * (@base_font_size + font_size_inc) + font_width = font_height * FONT_WIDTH_HEIGHT_RATIO # Assume glyph width averages at 2/5s the height + leading = font_height / 2 - chars_in_line = (A4_PAGE_WIDTH - @margin_width) / font_width # 210mm for A4 portrait - num_lines = (text.length / chars_in_line).ceil + chars_in_line = (A4_PAGE_WIDTH - @margin_width) / font_width # 210mm for A4 portrait + num_lines = (text.length / chars_in_line).ceil - (num_lines * font_height) + vertical_margin + leading - end + (num_lines * font_height) + vertical_margin + leading + end # Initialize the title and dirty flags for new templates # -------------------------------------------------------- @@ -1131,8 +940,6 @@ # Only run this before_validation because rails fires this before save/create if self.id.nil? self.title = "My plan (#{self.template.title})" if self.title.nil? && !self.template.nil? - self.visibility = 3 end end - end diff --git a/app/models/pref.rb b/app/models/pref.rb new file mode 100644 index 0000000..02e1a59 --- /dev/null +++ b/app/models/pref.rb @@ -0,0 +1,19 @@ +class Pref < ActiveRecord::Base + ## + # Serialize prefs to JSON + # The settings object only stores deviations from the default + serialize :settings, JSON + + ## + # Associations + belongs_to :user + + ## + # Returns the hash generated from default preferences + # + # @return [JSON] preferences hash + def self.default_settings + return Rails.configuration.branding[:preferences] + end + +end \ No newline at end of file diff --git a/app/models/question.rb b/app/models/question.rb index 0a3e947..b92355d 100644 --- a/app/models/question.rb +++ b/app/models/question.rb @@ -115,6 +115,10 @@ return example_answer.first end + def first_example_answer + self.annotations.where(type: Annotation.types[:example_answer]).order(:created_at).first + end + ## # get guidance belonging to the current user's org for this question(need org # to distinguish customizations) diff --git a/app/models/question_format.rb b/app/models/question_format.rb index b7d6afc..b6d1e36 100644 --- a/app/models/question_format.rb +++ b/app/models/question_format.rb @@ -4,11 +4,11 @@ # Associations has_many :questions - enum formattype: [ :textarea, :textfield, :radiobuttons, :checkbox, :dropdown, :multiselectbox, :date ] + enum formattype: [ :textarea, :textfield, :radiobuttons, :checkbox, :dropdown, :multiselectbox, :date, :rda_metadata ] attr_accessible :formattype - + validates :title, presence: {message: _("can't be blank")}, uniqueness: {message: _("must be unique")} - + ## # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 @@ -16,7 +16,7 @@ # Retrieves the id for a given formattype passed scope :id_for, -> (formattype) { where(formattype: formattype).pluck(:id).first } - + ## # Define Bit Field Values so we can test a format without doing string comps # Column type diff --git a/app/models/role.rb b/app/models/role.rb index a51177d..b7cdf28 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -1,4 +1,5 @@ class Role < ActiveRecord::Base + after_initialize :set_defaults include FlagShihTzu ## @@ -14,6 +15,7 @@ 2 => :administrator, # 2 3 => :editor, # 4 4 => :commenter, # 8 + 5 => :reviewer, # 16 column: 'access' validates :user, :plan, :access, presence: {message: _("can't be blank")} @@ -21,6 +23,7 @@ ## # return the access level for the current project group + # 5 if the user is a reviewer # 3 if the user is an administrator # 2 if the user is an editor # 1 if the user can only read @@ -28,7 +31,9 @@ # # @return [Integer] def access_level - if self.administrator? + if self.reviewer? + return 5 + elsif self.administrator? return 3 elsif self.editor? return 2 @@ -37,6 +42,57 @@ end end + # Sets access_level according to bit fields defined in the column access + # TODO refactor according to the hash defined above (e.g. 1 key is :creator, 2 key is :administrator, etc) + def set_access_level(access_level) + if access_level >= 1 + self.commenter = true + else + self.commenter = false + end + if access_level >= 2 + self.editor = true + else + self.editor = false + end + if access_level >= 3 + self.administrator = true + else + self.administrator = false + end + end + + # Returns a hash of hashes where each key represents an access level (e.g. see access_level method to understand the integers) + # This method becomes useful for generating template messages (e.g. permissions change notification mailer) + def self.access_level_messages + { + 5 => { + :type => _('reviewer'), + :placeholder1 => _('read the plan and provide feedback.'), + :placeholder2 => nil + }, + 3 => { + :type => _('co-owner'), + :placeholder1 => _('write and edit the plan in a collaborative manner.'), + :placeholder2 => _('You can also grant rights to other collaborators.') + }, + 2 => { + :type => _('editor'), + :placeholder1 => _('write and edit the plan in a collaborative manner.'), + :placeholder2 => nil, + }, + 1 => { + :type => _('read-only'), + :placeholder1 => _('read the plan and leave comments.'), + :placeholder2 => nil, + } + } + end + + def set_defaults + self.active = true if self.new_record? + end + end # ----------------------------------------------------- @@ -56,4 +112,20 @@ # 12 - editor + commenter # 13 - creator + editor + commenter # 14 - administrator + editor + commenter -# 15 - creator + administrator + editor + commenter \ No newline at end of file +# 15 - creator + administrator + editor + commenter +# 16 - reviewer +# 17 - creator + reviewer +# 18 - administrator + reviewer +# 19 - creator + administrator + reviewer +# 20 - editor + reviewer +# 21 - creator + editor + reviewer +# 22 - administraor + editor + reviewer +# 23 - creator + editor + administrator + reviewer +# 24 - commenter + reviewer +# 25 - creator + commenter + reviewer +# 26 - administrator + commenter + reviewer +# 27 - creator + administrator + commenter + reviewer +# 28 - editor + commenter + reviewer +# 29 - creator + editor + commenter + reviewer +# 30 - administrator + editor + commenter + reviewer +# 31 - creator + administrator + editor + commenter + reviewer \ No newline at end of file diff --git a/app/models/section.rb b/app/models/section.rb index 18a3420..1a145d1 100644 --- a/app/models/section.rb +++ b/app/models/section.rb @@ -23,16 +23,18 @@ "#{title}" end - # Returns the number of answered questions for a given plan id + # Returns the number of answered questions for a given plan def num_answered_questions(plan) + return 0 if plan.nil? questions_hash = questions.reduce({}){ |m, q| m[q.id] = q; m } - return plan.answers.includes({question: :question_format}, :question_options).reduce(0) do |m, a| + return plan.answers.includes({ question: :question_format }, :question_options).reduce(0) do |m, a| if questions_hash[a.question_id].present? && a.is_valid? - m+=1 + m+= 1 end m end end + ## # deep copy of the given section and all it's associations # diff --git a/app/models/settings/template.rb b/app/models/settings/template.rb index a9fd6f8..d27957f 100644 --- a/app/models/settings/template.rb +++ b/app/models/settings/template.rb @@ -16,13 +16,13 @@ DEFAULT_SETTINGS = { formatting: { - margin: { # in millimeters - top: 10, - bottom: 10, - left: 10, - right: 10 + margin: { + top: 25, + bottom: 20, + left: 12, + right: 12 }, - font_face: VALID_FONT_FACES.first, + font_face: 'Arial, Helvetica, Sans-Serif', font_size: 10 # pt }, max_pages: 3, diff --git a/app/models/template.rb b/app/models/template.rb index f8a7e6a..21e5b9c 100644 --- a/app/models/template.rb +++ b/app/models/template.rb @@ -1,8 +1,14 @@ class Template < ActiveRecord::Base include GlobalHelpers + include ActiveModel::Validations + validates_with TemplateLinksValidator before_validation :set_creation_defaults - scope :valid, -> {where(migrated: false)} + + # Stores links as an JSON object: { funder: [{"link":"www.example.com","text":"foo"}, ...], sample_plan: [{"link":"www.example.com","text":"foo"}, ...]} + # The links is validated against custom validator allocated at validators/template_links_validator.rb + serialize :links, JSON + ## # Associations belongs_to :org @@ -19,7 +25,12 @@ # -relies on protected_attributes gem as syntax depricated in rails 4.2 attr_accessible :id, :org_id, :description, :published, :title, :locale, :customization_of, :is_default, :guidance_group_ids, :org, :plans, :phases, :dmptemplate_id, - :migrated, :version, :visibility, :published, :as => [:default, :admin] + :migrated, :version, :visibility, :published, :links, :as => [:default, :admin] + + # A standard template should be organisationally visible. Funder templates that are + # meant for external use will be publicly visible. This allows a funder to create 'funder' as + # well as organisational templates. The default template should also always be publicly_visible + enum visibility: [:organisationally_visible, :publicly_visible] # defines the export setting for a template object has_settings :export, class_name: 'Settings::Template' do |s| @@ -28,19 +39,80 @@ validates :org, :title, :version, presence: {message: _("can't be blank")} + scope :valid, -> { where(migrated: false) } + scope :published, -> { where(published: true) } + + # Retrieves all valid and published templates + scope :valid_published, -> (is_default: false) { + Template.where(templates: { is_default: is_default }).valid().published() + } + + scope :publicly_visible, -> { where(:visibility => Template.visibilities[:publicly_visible]) } + scope :organisationally_visible, -> { where(:visibility => Template.visibilities[:organisationally_visible]) } + + # Retrieves template with distinct dmptemplate_id that are valid (e.g. migrated false) and customization_of is nil. Note, + # if organisation ids are passed, the query will filter only those distinct dmptemplate_ids for those organisations + scope :families, -> (org_ids=nil) { + if org_ids.is_a?(Array) + valid.where(org_id: org_ids, customization_of: nil).distinct + else + valid.where(customization_of: nil).distinct + end + } + # Retrieves the maximum version for the array of dmptemplate_ids passed. If dmptemplate_ids is missing, every maximum + # version for each different dmptemplate_id will be retrieved + scope :dmptemplate_ids_with_max_version, -> (dmptemplate_ids=nil) { + if dmptemplate_ids.is_a?(Array) + select("MAX(version) AS version", :dmptemplate_id).where(dmptemplate_id: dmptemplate_ids).group(:dmptemplate_id) + else + select("MAX(version) AS version", :dmptemplate_id).group(:dmptemplate_id) + end + } + # Retrieves the maximum version for the array of customization_ofs passed. If customization_ofs is missing, every maximum + # version for each different customization_of will be retrieved + scope :customization_ofs_with_max_version, -> (customization_ofs=nil) { + if customization_ofs.is_a?(Array) + select("MAX(version) AS version", :customization_of).where(customization_of: customization_ofs).group(:customization_of) + else + select("MAX(version) AS version", :customization_of).group(:customization_of) + end + } + # Retrieves the latest template version, i.e. the one with maximum version for each dmptemplate_id + scope :latest_version, -> (dmptemplate_ids=nil) { + from(dmptemplate_ids_with_max_version(dmptemplate_ids), :current) + .joins("INNER JOIN templates ON current.version = templates.version"\ + " AND current.dmptemplate_id = templates.dmptemplate_id") + } + # Retrieves the latest customized version, i.e. the one with maximum version for each customization_of=dmptemplate_id + scope :latest_customization, -> (org_id, dmptemplate_ids=nil) { + from(customization_ofs_with_max_version(dmptemplate_ids), :current) + .joins("INNER JOIN templates ON current.version = templates.version"\ + " AND current.customization_of = templates.customization_of") + .where('templates.org_id = ?', org_id) + } + + scope :search, -> (term) { + search_pattern = "%#{term}%" + joins(:org).where("templates.title LIKE ? OR orgs.name LIKE ?", search_pattern, search_pattern) + } + # Retrieves the list of all dmptemplate_ids (template versioning families) for the specified Org def self.dmptemplate_ids Template.all.valid.distinct.pluck(:dmptemplate_id) end - # Retrieves the most recent version of the template for the specified Org and dmptemplate_id + # Retrieves the most recent version of the template for the specified dmptemplate_id def self.current(dmptemplate_id) Template.where(dmptemplate_id: dmptemplate_id).order(version: :desc).valid.first end # Retrieves the current published version of the template for the specified Org and dmptemplate_id def self.live(dmptemplate_id) - Template.where(dmptemplate_id: dmptemplate_id, published: true).valid.first + if dmptemplate_id.respond_to?(:each) + Template.where(dmptemplate_id: dmptemplate_id, published: true).valid + else + Template.where(dmptemplate_id: dmptemplate_id, published: true).valid.first + end end def self.default @@ -52,13 +124,59 @@ # specified org and dmptemplate_id # returns nil if no customizations found # - # @params [integer] dmptemplate_id of the original template + # @params dmptemplate_ids of the original template # @params [integer] org_id for the customizing organisation # @return [nil, Template] the customized template or nil - def self.org_customizations(dmptemplate_id, org_id) - Template.where(customization_of: dmptemplate_id, org_id: org_id).order(version: :desc).valid.first + def self.org_customizations(dmptemplate_ids, org_id) + template_ids = latest_customization(org_id, dmptemplate_ids).pluck(:id) + includes(:org).where(id: template_ids) end - + + # Retrieves current templates with their org associated for a set of valid orgs + # TODO pass an array of org ids instead of Org instances + def self.get_latest_template_versions(orgs) + if orgs.respond_to?(:each) + families_ids = families(orgs.map(&:id)).pluck(:dmptemplate_id) + elsif orgs.is_a?(Org) + families_ids = families([orgs.id]).pluck(:dmptemplate_id) + else + families_ids = [] + end + template_ids = latest_version(families_ids).pluck(:id) + includes(:org).where(id: template_ids) + end + + # Retrieves current templates with their org associated for a set of valid orgs + # TODO pass an array of org ids instead of Org instances + def self.get_public_published_template_versions(orgs) + if orgs.respond_to?(:each) + families_ids = families(orgs.map(&:id)).pluck(:dmptemplate_id) + elsif orgs.is_a?(Org) + families_ids = families([orgs.id]).pluck(:dmptemplate_id) + else + families_ids = [] + end + includes(:org).where(dmptemplate_id: families_ids, published: true, visibility: Template.visibilities[:publicly_visible]) + end + + ## + # create a new version of the most current copy of the template + # + # @return [Template] new version + def get_new_version + if self.id.present? + new_version = Template.deep_copy(self) + new_version.version = (self.version + 1) + new_version.published = false + new_version.visibility = self.visibility # do not change the visibility + new_version.is_default = self.is_default # retain the default template flag + new_version.save! + new_version + else + nil + end + end + ## # deep copy the given template and all of it's associations # @@ -136,6 +254,10 @@ end return !modifiable end + + def template_type + self.customization_of.present? ? _('customisation') : _('template') + end # -------------------------------------------------------- private @@ -146,9 +268,10 @@ self.published = false self.migrated = false self.dirty = false - self.visibility = 1 - self.is_default = false + self.visibility = 0 # Organisationally visible by default + self.is_default = false if self.is_default.nil? self.version = 0 if self.version.nil? + self.visibility = Template.visibilities[:organisationally_visible] if self.visibility.nil? # Generate a unique identifier for the dmptemplate_id if necessary if self.dmptemplate_id.nil? @@ -159,5 +282,4 @@ end end end - end diff --git a/app/models/theme.rb b/app/models/theme.rb index bd787f6..4f0738e 100644 --- a/app/models/theme.rb +++ b/app/models/theme.rb @@ -15,12 +15,10 @@ validates :title, presence: {message: _("can't be blank")} - # EVALUATE CLASS AND INSTANCE METHODS BELOW - # - # What do they do? do they do it efficiently, and do we need them? - - - + scope :search, -> (term) { + search_pattern = "%#{term}%" + where("title LIKE ? OR description LIKE ?", search_pattern, search_pattern) + } ## # returns the title of the theme # diff --git a/app/models/user.rb b/app/models/user.rb index f35abe3..4fca8fc 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,5 @@ class User < ActiveRecord::Base - include GlobalHelpers - + include ConditionalUserMailer ## # Devise # Include default devise modules. Others available are: @@ -10,11 +9,17 @@ :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:shibboleth, :orcid] + + ## + # User Notification Preferences + serialize :prefs, Hash + ## # Associations has_and_belongs_to_many :perms, join_table: :users_perms belongs_to :language belongs_to :org + has_one :pref has_many :answers has_many :notes has_many :exported_plans @@ -37,25 +42,30 @@ has_many :user_identifiers has_many :identifier_schemes, through: :user_identifiers - ## - # Possibly needed for active_admin - # -relies on protected_attributes gem as syntax depricated in rails 4.2 - #accepts_nested_attributes_for :roles - #attr_accessible :password_confirmation, :encrypted_password, :remember_me, - # :id, :email, :firstname, :last_login,:login_count, :orcid_id, - # :password, :shibboleth_id, :user_status_id, :surname, - # :user_type_id, :org_id, :skip_invitation, :other_organisation, - # :accept_terms, :role_ids, :dmponline3, :api_token, - # :organisation, :language, :language_id, :org, :perms, - # :confirmed_at, :org_id - validates :email, email: true, allow_nil: true, uniqueness: {message: _("must be unique")} ## # Scopes default_scope { includes(:org, :perms) } + # Retrieves all of the org_admins for the specified org + scope :org_admins, -> (org_id) { + joins(:perms).where("users.org_id = ? AND perms.name IN (?)", org_id, + ['grant_permissions', 'modify_templates', 'modify_guidance', 'change_org_details']) + } + scope :search, -> (term) { + search_pattern = "%#{term}%" + # MySQL does not support standard string concatenation and since concat_ws or concat functions do + # not exist for sqlite, we have to come up with this conditional + if ActiveRecord::Base.connection.adapter_name == "Mysql2" + where("concat_ws(' ', firstname, surname) LIKE ? OR email LIKE ?", search_pattern, search_pattern) + else + where("firstname || ' ' || surname LIKE ? OR email LIKE ?", search_pattern, search_pattern) + end + } + + after_update :when_org_changes # EVALUATE CLASS AND INSTANCE METHODS BELOW # @@ -89,6 +99,15 @@ end ## + # returns all active plans for a user + # + # @return [Plans] + def active_plans + self.plans.includes(:template).where("roles.active": true).where(Role.not_reviewer_condition) + end + + + ## # Returns the user's identifier for the specified scheme name # # @param the identifier scheme name (e.g. ORCID) @@ -97,28 +116,6 @@ user_identifiers.where(identifier_scheme: scheme).first end -# TODO: Check the logic here. Its deleting the permissions if the user does not have permission -# to change orgs and either the incoming or existing org is nil. -# We should also NOT be auto-saving here!!! - ## - # sets a new organisation id for the user - # if the user has any perms such as org_admin or admin, those are removed - # if the user had an api_token, that is removed - # - # @param new_organisation_id [Integer] the id for an organisation - # @return [String] the empty string as a causality of setting api_token - def org_id=(new_org_id) - unless self.can_change_org? || new_org_id.nil? || self.org.nil? || (new_org_id.to_s == self.org.id.to_s) - # rip all permissions from the user - self.perms.delete_all - end - # set the user's new organisation - super(new_org_id) - self.save! - # rip api permissions from the user - self.remove_token! - end - ## # sets a new organisation for the user # @@ -214,23 +211,11 @@ end ## - # checks what type the user's organisation is - # - # @return [String] the organisation type -=begin - def org_type - org_type = org.organisation_type - return org_type - end -=end - - ## # removes the api_token from the user # modifies the user model def remove_token! unless api_token.blank? - self.api_token = "" - self.save! + update_column(:api_token, "") unless new_record? end end @@ -243,9 +228,7 @@ random_token = SecureRandom.urlsafe_base64(nil, false) break random_token unless User.exists?(api_token: random_token) end - self.save! - # send an email to the user to notify them of their new api token - #UserMailer.api_token_granted_notification(self) + update_column(:api_token, api_token) unless new_record? end end @@ -264,6 +247,31 @@ end ## + # Return the user's preferences for a given base key + # + # @return [JSON] with symbols as keys + def get_preferences(key) + defaults = Pref.default_settings[key.to_sym] || Pref.default_settings[key.to_s] + + if self.pref.present? + existing = self.pref.settings[key.to_s].deep_symbolize_keys + + # Check for new preferences + defaults.keys.each do |grp| + defaults[grp].keys.each do |pref, v| + # If the group isn't present in the saved values add all of it's preferences + existing[grp] = defaults[grp] if existing[grp].nil? + # If the preference isn't present in the saved values add the default + existing[grp][pref] = defaults[grp][pref] if existing[grp][pref].nil? + end + end + existing + else + defaults + end + end + + ## # Override devise_invitable email title # -------------------------------------------------------------- def deliver_invitation(options = {}) @@ -272,25 +280,19 @@ ## # Case insensitive search over User model # @param field [string] The name of the field being queried - # @param val [string] The string to search for, case insensitive + # @param val [string] The string to search for, case insensitive. val is duck typed to check whether or not downcase method exist # @return [ActiveRecord::Relation] The result of the search def self.where_case_insensitive(field, val) - User.where("lower(#{field}) = ?", val.downcase) + User.where("lower(#{field}) = ?", val.respond_to?(:downcase) ? val.downcase : val.to_s) end -# TODO: Remove this, its never called. - # this generates a reset password link for a given user - # which can then be sent to them with the appropriate host - # prepended. -=begin - def reset_password_link - raw, enc = Devise.token_generator.generate(self.class, :reset_password_token) - self.reset_password_token = enc - self.reset_password_sent_at = Time.now.utc - save(validate: false) - - edit_user_password_path + '?reset_password_token=' + raw + private + def when_org_changes + if org_id != org_id_was + unless can_change_org? + perms.delete_all + remove_token! + end + end end -=end - end diff --git a/app/policies/answer_policy.rb b/app/policies/answer_policy.rb index 1e9502c..b72f7a5 100644 --- a/app/policies/answer_policy.rb +++ b/app/policies/answer_policy.rb @@ -8,7 +8,7 @@ @answer = answer end - def update? + def create_or_update? # TODO: Remove the owner check after the Roles have been updated # is the plan editable by the user or the user is the owner of the plan @answer.plan.editable_by?(@user.id) || @user == @answer.plan.owner diff --git a/app/policies/api/v0/statistics_policy.rb b/app/policies/api/v0/statistics_policy.rb index 155a3da..44ed714 100644 --- a/app/policies/api/v0/statistics_policy.rb +++ b/app/policies/api/v0/statistics_policy.rb @@ -18,6 +18,9 @@ true end + def completed_plans? + true + end ## # need to check if your org owns this template def using_template? diff --git a/app/policies/guidance_group_policy.rb b/app/policies/guidance_group_policy.rb index bb514c3..2a66437 100644 --- a/app/policies/guidance_group_policy.rb +++ b/app/policies/guidance_group_policy.rb @@ -23,6 +23,10 @@ user.can_modify_guidance? && (guidance_group.org_id == user.org_id) end + def admin_update_unpublish? + user.can_modify_guidance? && (guidance_group.org_id == user.org_id) + end + def admin_new? user.can_modify_guidance? end diff --git a/app/policies/guidance_policy.rb b/app/policies/guidance_policy.rb index 725326c..09cf419 100644 --- a/app/policies/guidance_policy.rb +++ b/app/policies/guidance_policy.rb @@ -19,6 +19,10 @@ user.can_modify_guidance? && guidance.in_group_belonging_to?(user.org_id) end + def index? + admin_index? + end + def admin_index? user.can_modify_guidance? end @@ -35,6 +39,14 @@ user.can_modify_guidance? && guidance.in_group_belonging_to?(user.org_id) end + def admin_publish? + user.can_modify_guidance? + end + + def admin_unpublish? + user.can_modify_guidance? + end + def update_phases? user.can_modify_guidance? end @@ -50,10 +62,4 @@ def update_questions? user.can_modify_guidance? end - - class Scope < Scope - def resolve - scope = Guidance.includes(:guidance_group, :themes).by_org(user.org_id) - end - end end \ No newline at end of file diff --git a/app/policies/note_policy.rb b/app/policies/note_policy.rb index 88a9e11..2a25489 100644 --- a/app/policies/note_policy.rb +++ b/app/policies/note_policy.rb @@ -9,15 +9,15 @@ end def create? - @note.answer.plan.readable_by?(@user.id) + @note.answer.plan.commentable_by?(@user.id) end def update? - Plan.find(@note.answer.plan_id).readable_by?(@user.id) + Plan.find(@note.answer.plan_id).commentable_by?(@user.id) end def archive? - Plan.find(@note.answer.plan_id).readable_by?(@user.id) + Plan.find(@note.answer.plan_id).commentable_by?(@user.id) end end diff --git a/app/policies/org_policy.rb b/app/policies/org_policy.rb index 311d389..46fa09c 100644 --- a/app/policies/org_policy.rb +++ b/app/policies/org_policy.rb @@ -6,19 +6,32 @@ @user = user @org = org end - + def admin_show? user.can_modify_org_details? && (user.org_id == org.id) end def admin_edit? - user.can_modify_org_details? && (user.org_id == org.id) + user.can_modify_org_details? && (user.org_id == org.id || user.can_super_admin?) end def admin_update? - user.can_modify_org_details? && (user.org_id == org.id) + user.can_modify_org_details? && (user.org_id == org.id || user.can_super_admin?) end + def index? + user.can_super_admin? + end + def new? + user.can_super_admin? + end + def create? + user.can_super_admin? + end + def destroy? + user.can_super_admin? + end + def parent? true end @@ -30,5 +43,4 @@ def templates? true end - end \ No newline at end of file diff --git a/app/policies/paginable/plan_policy.rb b/app/policies/paginable/plan_policy.rb new file mode 100644 index 0000000..01c259a --- /dev/null +++ b/app/policies/paginable/plan_policy.rb @@ -0,0 +1,14 @@ +module Paginable + class PlanPolicy < ApplicationPolicy + def initialize(user) + @user = user + end + def privately_visible? + @user.is_a?(User) + end + + def organisationally_or_publicly_visible? + @user.is_a?(User) + end + end +end \ No newline at end of file diff --git a/app/policies/paginable/template_policy.rb b/app/policies/paginable/template_policy.rb new file mode 100644 index 0000000..a966cd8 --- /dev/null +++ b/app/policies/paginable/template_policy.rb @@ -0,0 +1,18 @@ +module Paginable + class TemplatePolicy < ApplicationPolicy + def initialize(user) + @user = user + end + def all? + @user.is_a?(User) && @user.can_super_admin? + end + + def funders? + @user.is_a?(User) && @user.can_org_admin? + end + + def orgs? + @user.is_a?(User) && @user.can_org_admin? + end + end +end \ No newline at end of file diff --git a/app/policies/plan_policy.rb b/app/policies/plan_policy.rb index aa99a2b..870cd41 100644 --- a/app/policies/plan_policy.rb +++ b/app/policies/plan_policy.rb @@ -3,7 +3,8 @@ attr_reader :plan def initialize(user, plan) - raise Pundit::NotAuthorizedError, "must be logged in" unless user + raise Pundit::NotAuthorizedError, _("must be logged in") unless user + raise Pundit::NotAuthorizedError, _("are not authorized to view that plan") unless plan || plan.publicly_visible? @user = user @plan = plan end @@ -12,23 +13,19 @@ @plan.readable_by?(@user.id) end - def edit? - @plan.readable_by?(@user.id) - end - - def update_guidance_choices? - @plan.editable_by?(@user.id) - end - def share? - @plan.readable_by?(@user.id) + @plan.editable_by?(@user.id) end def export? @plan.readable_by?(@user.id) end - def show_export? + def download? + @plan.readable_by?(@user.id) + end + + def edit? @plan.readable_by?(@user.id) end @@ -39,44 +36,40 @@ def destroy? @plan.editable_by?(@user.id) end - + def status? @plan.readable_by?(@user.id) end - - def possible_templates? - @plan.id.nil? - end -# TODO: These routes are no lonmger used -=begin - def section_answers? - @plan.readable_by?(@user.id) - end - - def locked? - @plan.readable_by?(@user.id) - end - - def delete_recent_locks? + def duplicate? @plan.editable_by?(@user.id) end - def unlock_all_sections? - @plan.editable_by?(@user.id) + def visibility? + @plan.administerable_by?(@user.id) end - def lock_section? - @plan.editable_by?(@user.id) + def set_test? + @plan.administerable_by?(@user.id) end - def unlock_section? - @plan.editable_by?(@user.id) - end -=end - def answer? @plan.readable_by?(@user.id) end + def request_feedback? + @plan.administerable_by?(@user.id) + end + + def overview? + @plan.readable_by?(@user.id) + end + + def select_guidances_list? + @plan.readable_by?(@user.id) + end + + def update_guidances_list? + @plan.editable_by?(@user.id) + end end diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb deleted file mode 100644 index 9ca5bcc..0000000 --- a/app/policies/project_policy.rb +++ /dev/null @@ -1,43 +0,0 @@ -class ProjectPolicy < ApplicationPolicy - attr_reader :user - attr_reader :project - - def initialize(user, project) - raise Pundit::NotAuthorizedError, "must be logged in" unless user - @user = user - @project = project - end - - def show? - @project.readable_by(@user.id) - end - - def edit? - @project.editable_by(@user.id) - end - - def share? - @project.editable_by(@user.id) - end - - def export? - @project.readable_by(@user.id) - end - - def update? - @project.editable_by(@user.id) - end - - def destroy? - @project.editable_by(@user.id) - end - - def possible_templates? - true - end - - def possible_guidance? - true - end - -end \ No newline at end of file diff --git a/app/policies/public_page_policy.rb b/app/policies/public_page_policy.rb new file mode 100644 index 0000000..1e1ec53 --- /dev/null +++ b/app/policies/public_page_policy.rb @@ -0,0 +1,32 @@ +class PublicPagePolicy < ApplicationPolicy + + def initialize(object, object2 = nil) + @object = object + @object2 = object2 + end + + def plan_index? + true + end + + def template_index? + true + end + + def template_export? + @object.is_default || @object.org.funder? + end + + def plan_export? + @object.publicly_visible? + end + + def plan_organisationally_exportable? + plan = @object + user = @object2 + if plan.is_a?(Plan) && user.is_a?(User) + return plan.publicly_visible? || (plan.organisationally_visible? && plan.owner.present? && plan.owner.org_id == user.org_id) + end + return false; + end +end diff --git a/app/policies/role_policy.rb b/app/policies/role_policy.rb index cf2b691..8570ba1 100644 --- a/app/policies/role_policy.rb +++ b/app/policies/role_policy.rb @@ -19,4 +19,8 @@ def destroy? @role.plan.administerable_by?(@user.id) end + + def deactivate? + @role.user_id = @user.id + end end \ No newline at end of file diff --git a/app/policies/template_policy.rb b/app/policies/template_policy.rb index 754e622..b4a62c3 100644 --- a/app/policies/template_policy.rb +++ b/app/policies/template_policy.rb @@ -1,11 +1,70 @@ class TemplatePolicy < ApplicationPolicy attr_reader :user, :template - - def initialize(user, template) - raise Pundit::NotAuthorizedError, "must be logged in" unless user + + 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? || user.can_modify_templates? + end + + def new? + user.can_super_admin? || user.can_modify_templates? + end + + def create? + user.can_super_admin? || user.can_modify_templates? + end + + def edit? + user.can_super_admin? || (user.can_modify_templates? && template.org_id == user.org_id) + end + + def update? + user.can_super_admin? || (user.can_modify_templates? && template.org_id == user.org_id) + end + + 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 + + def customize? + user.can_super_admin? || user.can_modify_templates? + end + + def transfer_customization? + user.can_super_admin? || user.can_modify_templates? + end + + # Pagination + def all? + user.can_super_admin? + end + def funders? + user.can_super_admin? || user.can_modify_templates? + end + def orgs? + user.can_super_admin? || user.can_modify_templates? + end + + # AJAX Calls + def copy? + user.can_super_admin? || (user.can_modify_templates? && (template.org_id == user.org_id)) + end + def publish? + user.can_super_admin? || (user.can_modify_templates? && (template.org_id == user.org_id)) + end + def unpublish? + user.can_super_admin? || (user.can_modify_templates? && (template.org_id == user.org_id)) + end + ## # Users can modify templates if: @@ -13,55 +72,13 @@ # - The template which they are modifying belongs to their org ## - def admin_index? - user.can_modify_templates? - end - - def admin_template? - user.can_modify_templates? && (template.org_id == user.org_id) - end - - def admin_customize? - user.can_modify_templates? - end - - def admin_publish? - user.can_modify_templates? && (template.org_id == user.org_id) - end - - def admin_unpublish? - user.can_modify_templates? && (template.org_id == user.org_id) - end - - def admin_update? - user.can_modify_templates? && (template.org_id == user.org_id) - end - - def admin_new? - user.can_modify_templates? - end - - def admin_create? - user.can_modify_templates? && (template.org_id.nil? || (template.org_id == user.org_id)) - end - - def admin_destroy? - user.can_modify_templates? && (template.org_id == user.org_id) - end - - def admin_template_history? - user.can_modify_templates? && (template.org_id == user.org_id) - end - - def admin_transfer_customization? - user.can_modify_templates? - end - class Scope < Scope - def resolve - scope.where(org_id: user.org_id) - end + # Anyone with an account should be able to get templates for the sepecified research_org + funder + # This policy is applicable to the Create Plan page + def template_options? + user.present? end + end \ No newline at end of file diff --git a/app/policies/theme_policy.rb b/app/policies/theme_policy.rb new file mode 100644 index 0000000..3767696 --- /dev/null +++ b/app/policies/theme_policy.rb @@ -0,0 +1,24 @@ +class ThemePolicy < ApplicationPolicy + def initialize(user, *args) + raise Pundit::NotAuthorizedError, _("must be logged in") unless user + @user = user + end + def index? + @user.can_super_admin? + end + def new? + @user.can_super_admin? + end + def create? + @user.can_super_admin? + end + def edit? + @user.can_super_admin? + end + def update? + @user.can_super_admin? + end + def destroy? + @user.can_super_admin? + end +end diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index a97a91b..070e52f 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -11,14 +11,23 @@ @user.can_grant_permissions? end + def index? + admin_index? + end + def admin_grant_permissions? - @user.can_grant_permissions? && (@users.org_id == @user.org_id) + @user.can_grant_permissions? && ((@users.org_id == @user.org_id) || @user.can_super_admin?) end def admin_update_permissions? - @user.can_grant_permissions? && (@users.org_id == @user.org_id) + @user.can_grant_permissions? && ((@users.org_id == @user.org_id) || @user.can_super_admin?) end + # Allows the user to swap their org affiliation on the fly + def org_swap? + user.can_super_admin? + end + class Scope < Scope def resolve scope.where(org_id: user.org_id) diff --git a/app/validators/org_links_validator.rb b/app/validators/org_links_validator.rb new file mode 100644 index 0000000..82ca15d --- /dev/null +++ b/app/validators/org_links_validator.rb @@ -0,0 +1,13 @@ +class OrgLinksValidator < ActiveModel::Validator + include JSONLinkValidator + def validate(record) + links = record.links + if links.is_a?(Hash) + if !links.has_key?('org') + record.errors[:links] << _('A key "org" is expected for links hash') %{ :key => k } + end + else + record.errors[:links] << _('A hash is expected for links') + end + end +end \ No newline at end of file diff --git a/app/validators/template_links_validator.rb b/app/validators/template_links_validator.rb new file mode 100644 index 0000000..61ef486 --- /dev/null +++ b/app/validators/template_links_validator.rb @@ -0,0 +1,18 @@ +class TemplateLinksValidator < ActiveModel::Validator + include JSONLinkValidator + def validate(record) + links = record.links + expected_keys = ['funder', 'sample_plan'] + if links.is_a?(Hash) + expected_keys.each do |k| + if !links.has_key?(k) + record.errors[:links] << _('A key %{key} is expected for links hash') %{ :key => k } + else + record.errors[:links] << _('The key %{key} does not have a valid set of object links') %{ :key => k } unless valid_links?(links[k]) + end + end + else + record.errors[:links] << _('A hash is expected for links') + end + end +end \ No newline at end of file diff --git a/app/views/annotations/_add_annotation.html.erb b/app/views/annotations/_add_annotation.html.erb deleted file mode 100644 index 8add942..0000000 --- a/app/views/annotations/_add_annotation.html.erb +++ /dev/null @@ -1,32 +0,0 @@ -<!--add annotation. question is passed as an argument--> -<h3><%= _('Add Annotations') %></h3> -<%= form_tag admin_create_annotation_path , class: 'add_annotation_form' do %> - <table class="dmp_details_table phase"> - <tr> - <td class="first_template"><%= _('Example Answer')%></td> - <td> - <ul> - <li><%= text_area_tag :example_answer_text, nil, rows: 5 %> - </li> - </ul> - </td> - </tr> - <tr> - <td class="first_template"><%= _('Guidance')%></td> - <td> - <ul> - <li><%= text_area_tag :guidance_text, nil, rows: 5 %> - </li> - </ul> - </td> - </tr> - </table> - <br/> - - <!-- submit buttons --> - <div class="move_2_right"> - <%= submit_tag _('Save'), class: "btn btn-primary" %> - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - <%= link_to _('Cancel'), "#", class: "btn cancel cancel_add_annotations" %> - </div> -<%end%> diff --git a/app/views/annotations/_edit_annotation.html.erb b/app/views/annotations/_edit_annotation.html.erb deleted file mode 100644 index 3a59362..0000000 --- a/app/views/annotations/_edit_annotation.html.erb +++ /dev/null @@ -1,43 +0,0 @@ -<!--edit annotation. question is passed as an argument--> -<%= form_tag admin_update_annotation_path, method: :put do %> - <% example_answer_text = example_answer.present? ? example_answer.text : '' %> - <% guidance_text = guidance.present? ? guidance.text : '' %> - <%= hidden_field_tag :example_answer_id, example_answer.present? ? example_answer.id : nil %> - <%= hidden_field_tag :guidance_id, guidance.present? ? guidance.id : nil %> - - <table class="dmp_details_table phase"> - <tr> - <td class="first_template"><%= _('Example Answer')%></td> - <td> - <ul> - <li><%= text_area_tag :example_answer_text, example_answer_text, rows: 5 %></li> - </ul> - </td> - </tr> - <tr> - <td class="first_template"><%= _('Guidance')%></td> - <td> - <ul> - <li><%= text_area_tag :guidance_text, guidance_text, rows: 5 %></li> - </ul> - </td> - </tr> - </table> - <br/> - - <!-- submit/ delete buttons --> - <div class="move_2_right"> - <%= submit_tag _('Save'), class: 'btn btn-primary' %> - <% if example_answer.present? %> - <%= link_to _('Delete Example Answer'), admin_destroy_annotation_path(id: example_answer.id), - confirm: _("You are about to delete an example answer for '%{question_text}'. Are you sure?") % { :question_text => question.text }, method: :delete, class: "btn btn-primary"%> - <% end %> - <% if guidance.present? %> - <%= link_to _('Delete Example Answer'), admin_destroy_annotation_path(id: guidance.id), - confirm: _("You are about to delete a guidance for '%{question_text}'. Are you sure?") % { :question_text => question.text }, method: :delete, class: "btn btn-primary"%> - <% end %> - - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - <%= link_to _('Cancel'), '#', class: 'btn cancel cancel_edit_annotations' %> - </div> -<% end %> diff --git a/app/views/annotations/_new_edit.html.erb b/app/views/annotations/_new_edit.html.erb new file mode 100644 index 0000000..c17f06c --- /dev/null +++ b/app/views/annotations/_new_edit.html.erb @@ -0,0 +1,22 @@ +<!--edit/add annotations. question is passed as an argument--> +<%= form_tag options[:url], method: options[:method] do %> + <div class="row"> + <div class="form-group col-md-10"> + <%= label_tag :example_answer_text, _('Example answer'), class: "control-label" %> + <%= text_area_tag :example_answer_text, (example_answer.present? ? example_answer.text : ''), class: "question" %> + </div> + + <div class="form-group col-md-10"> + <%= label_tag :guidance_text, _('Guidance'), class: 'control-label' %> + <%= text_area_tag :guidance_text, (guidance.present? ? guidance.text : ''), class: 'question' %> + </div> + + <div class="form-group col-md-10"> + <div class="pull-right"> + <%= submit_tag _('Save'), class: 'btn btn-primary' %> + <%= hidden_field_tag :question_id, question.id, class: "question_id" %> + <%= link_to _('Cancel'), "#annotations_div_#{question.id}", class: 'btn cancel btn-default cancel_edit_annotations', role: 'button' %> + </div> + </div> + </div> +<% end %> diff --git a/app/views/annotations/_show.html.erb b/app/views/annotations/_show.html.erb new file mode 100644 index 0000000..b5085f4 --- /dev/null +++ b/app/views/annotations/_show.html.erb @@ -0,0 +1,13 @@ +<% org = template.org.abbreviation.present? ? template.org.abbreviation : template.org.name %> +<% if example_answer.present? || guidance.present? %> + <dl<%= for_plan ? ' class="dl-horizontal"' : '' %>> + <% if example_answer.present? %> + <dt><%= _('Example answer') %></dt> + <dd><%= raw example_answer.text %><dd> + <% end %> + <% if guidance.present? %> + <dt><%= _('Guidance') %></dt> + <dd><%= raw guidance.text %></dd> + <% end %> + </dl> +<% end %> diff --git a/app/views/annotations/_show_annotation.html.erb b/app/views/annotations/_show_annotation.html.erb deleted file mode 100644 index a790d70..0000000 --- a/app/views/annotations/_show_annotation.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -<!--show annotations. annotation is passed as an argument--> -<h3><%= _('Annotations') %> </h3> -<table class="dmp_details_table phase"> - <% if example_answer.present? %> - <tr> - <td class="first_template"> - <%= _('Example Answer')%> - </td> - <td><%= raw example_answer.text %></td> - </tr> - <% end %> - <% if guidance.present? %> - <tr> - <td class="first_template"> - <%= _('Guidance')%> - </td> - <td><%= raw guidance.text %></td> - </tr> - <% end %> -</table> -<br/> - -<div class="move_2_right"> - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - <%= link_to _('Edit Annotations'), '# ', class: "btn btn-primary edit_form_for_annotations"%> -</div> diff --git a/app/views/answers/_form_fields.html.erb b/app/views/answers/_form_fields.html.erb deleted file mode 100644 index e69de29..0000000 --- a/app/views/answers/_form_fields.html.erb +++ /dev/null diff --git a/app/views/answers/_locking.html.erb b/app/views/answers/_locking.html.erb index 40a3d90..e47642b 100644 --- a/app/views/answers/_locking.html.erb +++ b/app/views/answers/_locking.html.erb @@ -1,5 +1,5 @@ <div class="answer_notice"> <p><%= _('The following answer cannot be saved') %></p> - <%= render partial: '/answers/new_edit', locals: { question: question, answer: answer, readonly: true } %> + <%= render partial: '/answers/new_edit', locals: { question: question, answer: answer, readonly: true, locking: true } %> <p><%= _('since %{name} saved the answer below while you were editing. Please, combine your changes and then save the answer again.') % { name: user.name} %></p> -</div> +</div> \ No newline at end of file diff --git a/app/views/answers/_new_edit.html.erb b/app/views/answers/_new_edit.html.erb index 2434033..14e1513 100644 --- a/app/views/answers/_new_edit.html.erb +++ b/app/views/answers/_new_edit.html.erb @@ -1,101 +1,78 @@ +<%# locals: { question, answer, readonly, locking } %> <!-- This partial creates a form for each type of question. The local variables are: plan, answer, question, readonly --> +<!-- Question text --> <% q_format = question.question_format %> -<%= semantic_form_for answer, :url => {controller: :answers, action: :update }, html: {method: "put", class: "roadmap-form", 'data-autosave': question.id }, remote: true do |f| %> - <fieldset class="standard"> - <% if !readonly %> - <%= f.input :id, as: :hidden, input_html: { value: answer.id } %> - <%= f.input :plan_id, as: :hidden, input_html: { value: answer.plan_id } %> - <%= f.input :user_id, as: :hidden, input_html: { value: answer.user_id } %> - <%= f.input :question_id, as: :hidden, input_html: { value: answer.question_id } %> - <%= f.input :lock_version, as: :hidden, input_html: { value: answer.lock_version } %> - <% end %> +<% if q_format.rda_metadata? %> + <p> + <strong><%= raw question.text %></strong> + </p> + <% answer_hash = answer.answer_hash %> + <div class="rda_metadata"><button class="remove-standard" style="display:none;"></button> + <div class="selected_standards"><strong><%=_("Your Selected Standards:")%></strong></br><ul class="list bullet"></ul></div> + <div class="rda_right" style="float:right;width:50%;margin-bottom:5px;display:none;"> + OR Search:</br> + <input type="text" data-provide="typeahead" class="standards-typeahead"></input></br> + <button class="btn btn-primary select_standard_typeahead"><%=_("Add Standard")%></button> + </div> + <div class="subject"><%=_("Please select a subject")%></br> + <select name="subject" class="form-control"></select> + </div> + <div class="sub-subject"><%=_("Please select a sub-subject")%></br> + <select name="sub-subject" class="form-control"></select> + </div> + </br> + <div class="suggested-answer-div"> + <span class="suggested-answer-intro"> + <strong><%=_("Browse Standards") %></strong> + </span> - <!-- Question text --> - <p> - <strong><%= raw question.text %></strong> + <div class="browse-standards-border"> + <p class="suggested-answer"> + <strong><%=_("Please wait, Standards are loading")%></strong> </p> + </div> + <div> + <a href="#" class="custom-standard"><strong>Standard not listed? Add your own.</strong></a> + <div class="add-custom-standard" style="display:none;"> + <input type="text" class="custom-standard-name"></input> + <button class="btn btn-primary submit_custom_standard">Add Standard</button> + </div> + </div> + </div> + </div> +<% end %> - <!--Example Answer area--> - <% if !readonly && question.annotations.where(type: Annotation.types[:example_answer]).any? %> - <% annotation = question.annotations.where(type: Annotation.types[:example_answer]).order(:created_at).first %> - <% if annotation.text.present? %> - <div class="suggested-answer-div"> - <span class="suggested-answer-intro"> - <%="#{annotation.org.abbreviation} "%> <%=_('Example of answer')%> - </span> - - <div class="suggested-answer-border"> - <p class="suggested-answer"> - <%= raw annotation.text %> - </p> - </div> - </div> - <% end %> - <% end %> - - <% if question.option_based? %> - <% options = question.question_options.by_number %> - <% if q_format.checkbox? %> - <ol class="choices-group"> - <% options.each do |op| %> - <li> - <%= f.check_box(:question_option_ids, { multiple: true, checked: answer.has_question_option(op.id), disabled: readonly }, op.id, nil) %> - <%= raw op.text %> - </li> - <% end %> - </ol> - <% elsif q_format.radiobuttons? %> - <ol class="choices-group"> - <% options.each do |op| %> - <li> - <%= f.radio_button :question_option_ids, op.id, { checked: answer.has_question_option(op.id), id: "answer_option_ids_#{op.id}", disabled: readonly } %> - <%= raw op.text %> - </li> - <% end %> - </ol> - <% elsif q_format.dropdown? || q_format.multiselectbox? %> - <% - options_html = "" - options.each do |op| - options_html += answer.has_question_option(op.id) ? - "<option value=#{op.id} selected=\"selected\">#{op.text}</option>" : - "<option value=#{op.id}>#{op.text}</option>" - end - %> - <%= select_tag('answer[question_option_ids]', raw(options_html), - {multiple: q_format.multiselectbox?, include_blank: q_format.dropdown?, disabled: readonly }) %> - <% end %> - <!-- Comment text area for option_based questions --> - <% if question.option_comment_display == true %> - <%= label_tag('answer[text]', _('Comment')) %> - <% if readonly %> - <p><%= raw(answer.text) %></p> - <% else %> - <%= text_area_tag('answer[text]', answer.text, id: "answer-text-#{question.id}") %> - <%= tinymce(selector: "#answer-text-#{question.id}", setup: "$.fn.tinymce_answer_events", content_css: asset_path('application.css')) %> - <% end %> - <%end%> - <% end %> - <!-- For non-option_based questions --> - <% if q_format.textfield? %> - <% if readonly %> - <p><%= strip_tags(answer.text) %></p> - <% else %> - <%= text_field_tag('answer[text]', strip_tags(answer.text)) %> - <% end %> - <% elsif q_format.textarea? %> - <% if readonly %> - <p><%= raw(answer.text) %></p> - <% else %> - <%= text_area_tag('answer[text]', answer.text, id: "answer-text-#{question.id}") %> - <%= tinymce(selector: "#answer-text-#{question.id}", setup: "$.fn.tinymce_answer_events", content_css: asset_path('application.css')) %> - <% end %> - <% end %> - - <% if !readonly %> - <input type="submit" class="form-submit" value="<%= _('Save') %>" /> - <% end %> - </fieldset> - <% end %> \ No newline at end of file +<%= form_for answer, url: {controller: :answers, action: :create_or_update}, html: {method: :post, 'data-autosave': question.id, class: 'form-answer' } do |f| %> + <% if !readonly %> + <%= f.hidden_field :plan_id %> + <%= f.hidden_field :question_id %> + <%= f.hidden_field :lock_version %> + <% if q_format.rda_metadata? %> + <%= hidden_field_tag :standards, answer_hash['standards'].to_json %> + <% end %> + <% end %> + <fieldset <%= 'disabled' if readonly %>> + <% if question.option_based? || question.question_format.rda_metadata? %> + <%= render(partial: 'questions/new_edit_question_option_based', locals: { f: f, question: question, answer: answer }) %> + <% elsif question.question_format.textfield?%> + <%= render(partial: 'questions/new_edit_question_textfield', locals: { f: f, question: question, answer: answer }) %> + <% elsif question.question_format.textarea? %> + <%= render(partial: 'questions/new_edit_question_textarea', locals: { f: f, question: question, answer: answer, locking: locking }) %> + <% end %> + <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %> + </fieldset> + <!--Example Answer area --> + <% annotation = question.first_example_answer %> + <% if annotation.present? && annotation.text.present? %> + <div class="panel panel-default"> + <span class="label label-default"> + <%="#{annotation.org.abbreviation} "%> <%=_('example answer')%> + </span> + <div class="panel-body"> + <%= raw annotation.text %> + </div> + </div> + <% end %> +<% end %> diff --git a/app/views/answers/_status.html.erb b/app/views/answers/_status.html.erb index ddf8dcf..bddd28f 100644 --- a/app/views/answers/_status.html.erb +++ b/app/views/answers/_status.html.erb @@ -1,9 +1,10 @@ - <!-- Partial for handling the answer status (e.g. Saving, Unsaved, Not Answered, Answered) --> - <span class="saving-message" style="display:none"><%= _('Saving...')%></span> - <span class="label label-inverse answer-unsaved" style="display:none"><%= _('Unsaved changes') %></span> - <br/> - <% if answer.updated_at.blank? %> - <span class="label label-warning answer-status not-answered"><%= _('Not answered yet') %></span> - <% else %> - <span class="label label-info answer-status"><%= _('Answered')%> <abbr class="timeago" data-time="<%= answer.updated_at.iso8601 %>" title="<%= answer.updated_at.iso8601 %>"><%= answer.updated_at.iso8601 %></abbr><%= _(' by')%> <%= answer.user.name %></span> - <% end %> \ No newline at end of file +<%# locals: { answer } %> +<!-- Partial for handling the answer status (e.g. saving, error-saving, data-status) --> +<span class="label label-info status" style="display:none;" data-status="saving"><%= _('Saving...') %></span> +<span class="label label-warning status" style="display: none;" data-status="error-saving"></span> +<% if answer.is_valid? %> + <span class="label label-info status" data-status="saved-at"> + <%= _('Answered')%> <time class="timeago" datetime="<%= answer.updated_at.iso8601 %>"></time> + <%= _(' by %{user_name}') %{ :user_name => answer.user.name } if answer.user.present? %> + </span> +<% end %> \ No newline at end of file diff --git a/app/views/answers/update.js.erb b/app/views/answers/update.js.erb deleted file mode 100644 index 2290a67..0000000 --- a/app/views/answers/update.js.erb +++ /dev/null @@ -1,26 +0,0 @@ -// partial /answers/locking -<% if @stale_answer %> - $("#answer-locking-<%= @question.id%>") - .html("<%= escape_javascript(render partial: '/answers/locking', locals: { question: @question, answer: @stale_answer, user: @answer.user }) %>"); -<% else %> - $("#answer-locking-<%= @question.id%>").html(""); -<% end %> - -// partial /answer/new_edit -if(tinymce) - tinymce.remove("#answer-text-<%= @question.id %>"); -$("#answer-form-<%= @question.id%>") - .html("<%= escape_javascript(render partial: '/answers/new_edit', locals: { question: @question, answer: @answer, readonly: false }) %>"); - -// partial /answer/status -$("#answer-status-<%= @question.id %>") - .html("<%= escape_javascript(render partial: '/answers/status', locals: { answer: @answer}) %>"); -if($.fn.init_answer_status) - $.fn.init_answer_status(); - -// partial /plans/progress -$(".progress").html("<%= escape_javascript(render :partial => '/plans/progress', locals: { plan: @plan }) %>"); - -// partial /sections/progress -$("#section-progress-<%= @section.id %>") - .html("<%= escape_javascript(render partial: '/sections/progress', locals: { section: @section, plan: @plan }) %>"); \ No newline at end of file diff --git a/app/views/contact_us/contacts/_new_left.html.erb b/app/views/contact_us/contacts/_new_left.html.erb new file mode 100644 index 0000000..1d8f999 --- /dev/null +++ b/app/views/contact_us/contacts/_new_left.html.erb @@ -0,0 +1,42 @@ +<%= form_for @contact, url: contacts_path do |f| %> +<% if ContactUs.require_name %> + <div class="form-group"> + <%= f.label(:name, _('Name'), class: 'control-label') %> + <%= f.text_field(:name, + class: "form-control", + value: current_user.nil? ? '' : current_user.name(false), + readonly: current_user.present?, + "aria-required": true) %> + </div> +<% end %> + <div class="form-group"> + <%= f.label(:email, _('Email'), class: 'control-label') %> + <%= f.email_field(:email, + class: "form-control", + value: current_user.nil? ? '' : current_user.email, + readonly: current_user.present?, + "aria-required": true) %> + </div> +<% if ContactUs.require_subject %> + <div class="form-group"> + <%= f.label(:subject, _('Subject')) %> + <%= f.text_field(:subject, + class: "form-control", + "aria-required": true) %> + </div> +<% end %> + <div class="form-group"> + <%= f.label(:message, _('Message')) %> + <%= f.text_area(:message, + class: "form-control", + rows: 10, + "aria-required": true) %> + </div> +<% if !user_signed_in? then %> + <div class="form-group"><!-- FIX first https://github.com/DMPRoadmap/roadmap/issues/501 !--> + <%= label_tag(nil, _('Security check')) %> + <%= recaptcha_tags %> + </div> +<% end %> + <%= f.button(_('Submit'), class: "btn btn-default", type: "submit") %> +<% end %> \ No newline at end of file diff --git a/app/views/contact_us/contacts/_new_right.html.erb b/app/views/contact_us/contacts/_new_right.html.erb new file mode 100644 index 0000000..8d87283 --- /dev/null +++ b/app/views/contact_us/contacts/_new_right.html.erb @@ -0,0 +1,39 @@ +<!-- new_right template --> +<address> + <strong><%= Rails.configuration.branding[:organisation][:name] %></strong><br> + <% [Rails.configuration.branding[:organisation][:address_line1], + Rails.configuration.branding[:organisation][:address_line2], + Rails.configuration.branding[:organisation][:address_line3], + Rails.configuration.branding[:organisation][:address_line4], + Rails.configuration.branding[:organisation][:address_country]].each do |addr_line| %> +<% if addr_line.present? %> + <%= addr_line %><br> +<% end %> +<% end %> +</address> +<% +telephone=Rails.configuration.branding[:organisation][:telephone] +if telephone.present? %> + <strong><%= "#{_('Helpline')} #{telephone}" %></strong> +<% end %> +<% +email=Rails.configuration.branding[:organisation][:email] +if email.present? %> + <address> + <strong><%= _('Email') %></strong> + <a href="<%= "mailto:#{email}" %>"><%= email %></a> + </address> +<% end %> +<% +link=Rails.configuration.branding[:organisation][:google_maps_link] +if link.present? %> + <iframe + width="90%" + height="250" + frameborder="0" + scrolling="no" + marginheight="0" + marginwidth="0" + src="<%= link %>"> + </iframe> +<% end %> \ No newline at end of file diff --git a/app/views/contact_us/contacts/new.html.erb b/app/views/contact_us/contacts/new.html.erb index c4d2f30..f98ec59 100644 --- a/app/views/contact_us/contacts/new.html.erb +++ b/app/views/contact_us/contacts/new.html.erb @@ -1,111 +1,20 @@ -<% javascript "contacts/new_contact.js" %> - -<h1> - <%= _("Contact Us") %> -</h1> -<p> - <%= raw _('%{application_name} is provided by the %{organisation_name}. You can find out more about us on our <a href="%{organisation_url}" target="_blank">website</a>. 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]} %> -</p> -<div class="column_container"> - <!-- left column content --> - <div class="column_left"> - <div class="white_background"> - <%= form_for @contact, url: contacts_path, html: {class: "roadmap-form"} do |f| %> - <fieldset class="side-by-side"> - <% if ContactUs.require_name %> - <div> - <%= f.label :name, (_('Name') + content_tag(:abbr, "*", class: "required")).html_safe %> - <% if user_signed_in? then %> - <%= f.text_field :name, value: current_user.name(false) %> - <% else %> - <%= f.text_field :name %> - <% end %> - <% if f.object.errors[:name].present? %> - <p class="inline-error"><%= f.object.errors[:name].join(_(" and ")) %></p> - <% end %> - </div> - <% end %> - - <div> - <%= f.label :email, (_('Email') + content_tag(:abbr, "*", class: "required")).html_safe %> - <% if user_signed_in? then %> - <%= f.email_field :email, value: current_user.email %> - <% else %> - <%= f.email_field :email %> - <% end %> - <% if f.object.errors[:email].present? %> - <p class="inline-error"><%= f.object.errors[:email].join(_(" and ")) %></p> - <% end %> - </div> - - <% if ContactUs.require_subject %> - <div> - <%= f.label :subject, (_('Subject') + content_tag(:abbr, "*", class: "required")).html_safe %> - <%= f.text_field :subject %> - <% if f.object.errors[:subject].present? %> - <p class="inline-error"><%= f.object.errors[:subject].join(_(" and ")) %></p> - <% end %> - </div> - <% end %> - - <div> - <%= f.label :message, (_('Message') + content_tag(:abbr, "*", class: "required")).html_safe %> - <%= f.text_area :message, rows: 10, class: "input-large" %> - <% if f.object.errors[:message].present? %> - <p class="inline-error"><%= f.object.errors[:message].join(_(" and ")) %></p> - <% end %> - </div> - - <% if !user_signed_in? then %> - <div> - <%= t('helpers.security_check') %> - <%= recaptcha_tags %> - </div> - <% end %> - - <div> - <label></label> - <%= render partial: 'shared/accessible_submit_button', - locals: {id: 'create_contact_submit', - val: 'Submit', - disabled_initially: true, - tooltip: _('Fill in the required fields')} %> - </div> - </fieldset> - - <% end %> - </div> +<div class="row"> + <div class="col-md-12"> + <h1><%= _("Contact Us") %></h1> + <p> + <%= raw _('%{application_name} is provided by the %{organisation_name}.<br /> You can find out more about us on our <a href="%{organisation_url}" target="_blank">website</a>. 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]} %> + </p> </div> - - <!-- right column information --> - <div class="column_right"> - <div class="white_background"> - <%= raw _("<ul><li>%{organisation_name}</li> - <li>%{organisation_address_line1}</li> - <li>%{organisation_address_line2}</li> - <li>%{organisation_address_line3}</li> - <li>%{organisation_address_line4}</li> - <li>%{organisation_address_country}</li> - </ul> - <p>Helpline: %{organisation_telephone}</p> - <p>Email <a href='mailto:%{organisation_email}?Subject=%{application_name} inquiry' target='_top'>%{organisation_email}</a></p>") % - { organisation_name: Rails.configuration.branding[:organisation][:name], - organisation_address_line1: Rails.configuration.branding[:organisation][:address_line1], - organisation_address_line2: Rails.configuration.branding[:organisation][:address_line2], - organisation_address_line3: Rails.configuration.branding[:organisation][:address_line3], - organisation_address_line4: Rails.configuration.branding[:organisation][:address_line4], - organisation_address_country: Rails.configuration.branding[:organisation][:address_country], - organisation_telephone: Rails.configuration.branding[:organisation][:telephone], - organisation_email: Rails.configuration.branding[:organisation][:email], - application_name: Rails.configuration.branding[:application][:name]} %> - <!-- map --> - <div class="div_map"> - <iframe width="90%" height="250" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="<%= Rails.configuration.branding[:organisation][:google_maps_link] %>"></iframe> - - </div> - </div> - </div> - </div> +<div class="row"> + <div class="col-md-6"><!-- Left column container --> + <%= render :partial => "contact_us/contacts/new_left" %> + </div><!-- Left column container --> + <div class="col-md-6"><!-- Right column container --> + <div class="pull-right"> + <%= render :partial => "contact_us/contacts/new_right" %> + </div> + </div><!-- Right column container --> +</div> \ No newline at end of file diff --git a/app/views/contact_us/contacts/new_formtastic.html.erb b/app/views/contact_us/contacts/new_formtastic.html.erb deleted file mode 100644 index 8554019..0000000 --- a/app/views/contact_us/contacts/new_formtastic.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -<h1><%= t('.contact_us') %></h1> -<%= semantic_form_for @contact, :url => contacts_path do |f| %> - <%= f.input :name, :label => t('.name') if ContactUs.require_name %> - <%= f.input :email, :label => t('.email') %> - <%= f.input :subject, :label => t('.subject') if ContactUs.require_subject %> - <%= f.input :message, :as => :text, :label => t('.message') %> - <%= f.action :submit, :label => t('.submit'), :button_html => { :alt => t('.submit'), :id => 'contact_us_contact_submit', :title => t('.submit') } %> -<% end %> diff --git a/app/views/contact_us/contacts/new_simple_form.html.erb b/app/views/contact_us/contacts/new_simple_form.html.erb deleted file mode 100644 index b4593f0..0000000 --- a/app/views/contact_us/contacts/new_simple_form.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -<h1><%= t('.contact_us') %></h1> -<%= simple_form_for @contact, :url => contacts_path do |f| %> - <%= f.input :name, :label => t('.name') if ContactUs.require_name %> - <%= f.input :email, :label => t('.email') %> - <%= f.input :subject, :label => t('.subject') if ContactUs.require_subject %> - <%= f.input :message, :as => :text, :label => t('.message') %> - <%= f.button :submit, :label => t('.submit'), :button_html => { :alt => t('.submit'), :id => 'contact_us_contact_submit', :title => t('.submit') } %> -<% end %> diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb deleted file mode 100644 index f5fadfb..0000000 --- a/app/views/devise/confirmations/new.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -<h2><%= t('custom_devise.resend_confirmation') %></h2> - -<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %> - <%= devise_error_messages! %> - - <div><%= f.label :email %><br /> - <%= f.email_field :email, :autofocus => true %></div> - - <div><%= f.submit t('custom_devise.resend_confirmation') %></div> -<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/devise/invitations/edit.html.erb b/app/views/devise/invitations/edit.html.erb index 4a4b73e..255dcfd 100644 --- a/app/views/devise/invitations/edit.html.erb +++ b/app/views/devise/invitations/edit.html.erb @@ -1,15 +1,27 @@ -<h2><%= t "devise.invitations.edit.header" %></h2> -<div class="white_background"> - <%= form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => { :method => :put } do |f| %> - <%= devise_error_messages! %> - <%= f.hidden_field :invitation_token %> - - <p><%= f.label :password %><br /> - <%= f.password_field :password %></p> - - <p><%= f.label :password_confirmation %><br /> - <%= f.password_field :password_confirmation %></p> - - <p><%= f.submit t("devise.invitations.edit.submit_button") %></p> - <% end %> -</div> +<div class="row"> + <div class="col-md-12"> + <h1><%= _("Create an account to view the plan") %></h1> + <p><%= _("You will need to create an account in order to accept your invitation to view the data management plan (DMP).") %></p> + </div> +</div> + +<div class="row"> + <div class="col-md-12"> + <%= form_for resource, as: resource_name, url: invitation_path(resource_name), html: {method: :put, id: "invitation_create_account_form"} do |f| %> + <%= devise_error_messages! %> + <%= f.hidden_field :invitation_token %> + + <div class="form-group"> + <%= f.label(:password, _('New password'), class: 'control-label') %> + <%= f.password_field(:password, class: 'form-control', "aria-required": true, "data-validation": "password") %> + </div> + <div class="form-group"> + <%= f.label(:password_confirmation, _('Password confirmation'), class: 'control-label') %> + <%= f.password_field(:password_confirmation, class: 'form-control', "aria-required": true, "data-validation": "password") %> + </div> + + <%= f.button(_('Create account'), class: "btn btn-default", type: "submit") %> + <% end %> + </div> +</div> + diff --git a/app/views/devise/invitations/new.html.erb b/app/views/devise/invitations/new.html.erb deleted file mode 100644 index 3567356..0000000 --- a/app/views/devise/invitations/new.html.erb +++ /dev/null @@ -1,14 +0,0 @@ -<h2><%= t "devise.invitations.new.header" %></h2> -<div class="white_background"> - <%= form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %> - <%= devise_error_messages! %> - - <% resource.class.invite_key_fields.each do |field| -%> - <p><%= f.label field %><br /> - <%= f.text_field field %></p> - <% end -%> - - <p><%= f.submit t("devise.invitations.new.submit_button") %></p> - <% end %> - -</div> \ No newline at end of file diff --git a/app/views/devise/mailer/invitation_instructions.html.erb b/app/views/devise/mailer/invitation_instructions.html.erb index c7235c1..5e11588 100644 --- a/app/views/devise/mailer/invitation_instructions.html.erb +++ b/app/views/devise/mailer/invitation_instructions.html.erb @@ -1,8 +1,26 @@ +<% + tool_name = Rails.configuration.branding[:application][:name] + link = accept_invitation_url(@resource, :invitation_token => @token) + helpdesk_email = Rails.configuration.branding[:organisation][:helpdesk_email] + contact_us = (Rails.configuration.branding[:organisation][:contact_us_url] || contact_us_url) + email_subject = _('Query or feedback related to %{tool_name}') %{ :tool_name => tool_name } +%> <% FastGettext.with_locale FastGettext.default_locale do %> -<p><%= _("Hello") %> <%= @resource.email %>,</p> -<p><%= _("A colleague has invited you to contribute to their Data Management Plan at ") %> <%= link_to Rails.configuration.branding[:application][:name], root_url %>.</p> -<p><%= link_to _("Click here to accept the invitation"), accept_invitation_url(@resource, :invitation_token => @token) %> (<%= _("or copy") %> <%= accept_invitation_url(@resource, :invitation_token => @token) %> <%= _("into your browser") %>).</p> -<p><%= _("If you don't want to accept the invitation, please ignore this email.") %><br /><%= _("Your account won't be created until you access the link above and set your password.") %></p> -<p><%=_('All the best,')%><br /><%= _('The ')%><%= Rails.configuration.branding[:application][:name] %><%=_(' team')%>.</p> - -<% end %> + <p> + <%= _('Hello %{user_email}') %{ :user_email => @resource.email } %> + </p> + <p> + <%= _('A colleague has invited you to contribute to their Data Management Plan in %{tool_name}') %{ :tool_name => tool_name } %> + </p> + <p> + <%= raw(_('%{click_here} to accept the invitation, (or copy %{link} into your browser). If you don\'t want to accept the invitation, please ignore this email.') %{ :click_here => link_to(_('Click here'), link), :link => link }) %> + </p> + <p> + <%= _('All the best') %> + <br /> + <%= _('The %{tool_name} team') %{:tool_name => tool_name} %> + </p> + <p> + <%= _('Please do not reply to this email.') %> <%= raw(_('If you have any questions or need help, please contact us at %{helpdesk_email} or visit %{contact_us}') %{ :helpdesk_email => mail_to(helpdesk_email, helpdesk_email, subject: email_subject), :contact_us_url => link_to(contact_us, contact_us) }) %> + </p> +<% end %> \ No newline at end of file diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb index 55858c9..76dde6e 100644 --- a/app/views/devise/mailer/reset_password_instructions.html.erb +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -1,13 +1,24 @@ +<% + tool_name = Rails.configuration.branding[:application][:name] + helpdesk_email = Rails.configuration.branding[:organisation][:helpdesk_email] + contact_us = Rails.configuration.branding[:organisation][:contact_us_url] || contact_us_url + email_subject = _('Query or feedback related to %{tool_name}') %{ :tool_name => tool_name } +%> <% FastGettext.with_locale FastGettext.default_locale do %> - <p><%= _("Hello ") %><%= @resource.email %></p> - - <p><%= _("Someone has requested a link to change your ") %><%= Rails.configuration.branding[:application][:name] %><%= _(" password. You can do this through the link below.") %></p> - - <p><%= link_to _('Change my password'), edit_password_url(@resource, :reset_password_token => @token) %></p> - - <p><%= _("If you didn't request this, please ignore this email.") %></p> - <p> - <%= _("Many thanks,") %><br /><%= _('The ') %><%= Rails.configuration.branding[:application][:name] %><%= _(' team') %> + <%= _('Hello %{user_email}') %{ :user_email => @resource.email } %> </p> -<% end %> \ No newline at end of file + <p> + <%= _('Someone has requested a link to change your %{tool_name} password. You can do this through the link below.') %{ :tool_name => tool_name } %> + </p> + <p><%= link_to _('Change my password'), edit_password_url(@resource, :reset_password_token => @token) %></p> + <p><%= _("If you didn't request this, please ignore this email.") %></p> + <p> + <%= _('All the best') %> + <br /> + <%= _('The %{tool_name} team') %{:tool_name => tool_name} %> + </p> + <p> + <%= _('Please do not reply to this email.') %> <%= raw(_('If you have any questions or need help, please contact us at %{helpdesk_email} or visit %{contact_us}') %{ :helpdesk_email => mail_to(helpdesk_email, helpdesk_email, subject: email_subject), :contact_us => link_to(contact_us, contact_us) }) %> + </p> +<% end %> diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb index a82a78f..cabaa15 100644 --- a/app/views/devise/passwords/edit.html.erb +++ b/app/views/devise/passwords/edit.html.erb @@ -1,26 +1,32 @@ -<h1><%= t('helpers.change_password') %></h1> -<div class="dmp_details_body"> +<div class="row"> + <div class="col-md-12"> + <h1><%= _('Change your password') %></h1> + </div> +</div> - <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %> - <%= devise_error_messages! %> - <%= f.hidden_field :reset_password_token %> - <div class="dmp_details"> - <table class="dmp_details_table"> - <tr> - <td class="first"><%= t('helpers.new_password') %></td> - <td><%= f.password_field :password, :autofocus => true %></td> - </tr> - <tr> - <td class="first"><%= t('helpers.password') %></td> - <td><%= f.password_field :password_confirmation %></td> - </tr> - </table> - <div class="div_right"> - <%= f.submit t('helpers.save'), :class => 'btn btn-primary' %> - </div> - </div> - <% end %> +<div class="row"> + <div class="col-md-12"> + <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: {method: :put, id: "user_reset_password_form"}) do |f| %> + <%= devise_error_messages! %> + <%= f.hidden_field :reset_password_token %> - <%= render "devise/shared/links" %> + <div class="form-group"> + <%= f.label(:password, _('New password'), class: 'control-label') %> + <%= f.password_field(:password, class: 'form-control', "aria-required": true) %> + </div> + <div class="form-group"> + <%= f.label(:password_confirmation, _('Password confirmation'), class: 'control-label') %> + <%= f.password_field(:password_confirmation, class: 'form-control', "aria-required": true) %> + </div> + <div class="checkbox"> + <label for="passwords_toggle"> + <input type="checkbox" id="passwords_toggle" class="passwords_toggle"/><%= _('Show passwords') %> + </label> + </div> -</div> + <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %> + + <%= render "devise/shared/links" %> + <% end %> + </div> +</div> diff --git a/app/views/devise/passwords/new.html.erb b/app/views/devise/passwords/new.html.erb index f95b651..a4bfb2e 100644 --- a/app/views/devise/passwords/new.html.erb +++ b/app/views/devise/passwords/new.html.erb @@ -1,26 +1,24 @@ -<h1><%= t('helpers.forgot_password') %></h1> - <div class="dmp_details_body"> +<div class="row"> + <div class="col-md-12"> + <% unless @user.errors[:email].empty? %> + <p class="alert alert-error"><%= _('The email address you entered is not registered.') %></p> + <% end %> + + <h1><%= _('Forgot your password?') %></h1> - <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> - <%= devise_error_messages! %> - <div class="dmp_details"> - <table class="dmp_details_table"> - <tr> - <td class="first"><%= t("helpers.email") %> - <p><%= t("helpers.send_password_info")%></p> - - </td> - <td><%= f.email_field :email, :autofocus => true , :style => "width:95%;" %></td> - </tr> - </table> - - - </div> - <div class="div_right"> - <%= f.submit t("helpers.send"), :class => "btn btn-primary", :id => "send_btn" %> - </div> - <% end %> + <p class="left-indent"><%= _('Please enter your email below and we will send you instructions on how to reset your password.') %></p> + </div> +</div> -<%= render "devise/shared/links" %> - -</div> +<div class="row"> + <div class="col-md-12"> + <%= form_for resource, as: 'user', url: user_password_path, html: {method: :post, id: "user_request_reset_password_form"} do |f| %> + <div class="form-group"> + <%= f.label(:email, _('Email'), class: 'control-label') %> + <%= f.email_field(:email, class: 'form-control', "aria-required": true) %> + </div> + + <%= f.button(_('Send'), class: "btn btn-default", type: "submit") %> + <% end %> + </div> +</div> \ No newline at end of file diff --git a/app/views/devise/registrations/_external_identifier.html.erb b/app/views/devise/registrations/_external_identifier.html.erb index 226b270..b48b983 100644 --- a/app/views/devise/registrations/_external_identifier.html.erb +++ b/app/views/devise/registrations/_external_identifier.html.erb @@ -1,21 +1,91 @@ -<div> - <% if id.nil? || id.identifier == '' %> - <%= link_to "#{_("Link account with #{scheme.description} ID")}", - Rails.application.routes.url_helpers.send( - "user_#{scheme.name.downcase}_omniauth_authorize_path" - ), - title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") - %> - <% else %> - <% if scheme.user_landing_url.nil? %> - <%= _("Your account has been linked to #{scheme.description}.") %> - <% else %> - <%= link_to "#{_("Your account has been linked to #{scheme.description}.")}", "#{scheme.user_landing_url}/#{id.identifier}", target: '_blank', - title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") %> +<% if id.nil? || id.identifier == '' %> + <% if scheme.name.downcase == 'orcid' %> + <%= link_to Rails.application.routes.url_helpers.send("user_orcid_omniauth_authorize_path"), + id: "connect-orcid-button", target: '_blank', + title: _("ORCID provides a persistent digital identifier that distinguishes you from other researchers. Learn more at orcid.org"), + 'data-toggle': "tooltip" do %> + <%= image_tag 'https://orcid.org/sites/default/files/images/orcid_16x16.png', alt: _('ORCID logo') %> +   + <%= _("Create or connect your ORCID iD") %> + <% end %> + <% elsif scheme.name.downcase == 'shibboleth' %> + <i class="fa fa-user" title="<%= _('Institutional credentials') %>" aria-hidden="true"></i> +   + <%= link_to _('Link your institutional credentials'), + Rails.application.routes.url_helpers.send("user_shibboleth_omniauth_authorize_path"), + title: _("Link your institutional credentials to access your account with them."), + 'data-toggle': "tooltip" %> <% end %> - <%= link_to '<i class="fa fa-fw fa-times-circle" aria-hidden="false"></i>'.html_safe, - destroy_user_identifier_path(id), method: :delete, - title: _("Unlink your account from #{scheme.description}. You can link again at any time."), - data: {confirm: _("Are you sure you want to unlink #{scheme.description} ID?")} %> - <% end %> -</div> +<% else %> + <% if scheme.name.downcase == 'orcid' %> + <% titletext = _("ORCID provides a persistent digital identifier that distinguishes you from other researchers. Learn more at orcid.org") %> + <% unlinktext = _("Disconnect your account from ORCID. You can reconnect at any time.") %> + <% unlinkconf = _("Are you sure you want to disconnect your ORCID ID?") %> + <%= link_to "#{scheme.user_landing_url}/#{id.identifier}", + id: 'orcid-id', + target: '_blank', + style: 'text-decoration: none', + title: titletext, + 'aria-label': titletext, + 'data-toggle': "tooltip" do %> + <%= image_tag "#{scheme.logo_url}", id: 'orcid-id-logo', alt: scheme.description %> +   + <%= "#{scheme.user_landing_url}/#{id.identifier}" %> + <% end %> + <% elsif scheme.name.downcase == 'shibboleth' %> + <% titletext = _("Your account has been linked to your organisation. You can now login with that method.") %> + <% unlinktext = _("Unlink your account from your organisation. You can link again at any time.") %> + <% unlinkconf = _("Are you sure you want to unlink your institutional credentials?") %> + <% if scheme.user_landing_url.nil? %> + <i class="fa fa-user" title="<%= _('Institutional credentials') %>" aria-hidden="true"></i> +   + <%= titletext %> + <% else %> + <%= link_to "#{scheme.user_landing_url}/#{id.identifier}", + target: '_blank', + style: 'text-decoration: none', + title: titletext, + 'aria-label': titletext, + 'data-toggle': "tooltip" do %> + <i class="fa fa-user" title="<%= scheme.description %>" aria-hidden="true"></i> +   + <%= titletext %> + <% end %> + <% end %> + <% else %> + <% titletext = _("Your account has been linked to #{scheme.description}.") %> + <% if scheme.user_landing_url.nil? %> + <% if scheme.logo_url.nil? %> + <i class="fa fa-user" title="<%= scheme.description %>" aria-hidden="true"></i> + <% else %> + <%= image_tag "#{scheme.logo_url}", id: 'orcid-id-logo', alt: scheme.description %> + <% end %> +   + <%= titletext %> + <% else %> + <%= link_to "#{scheme.user_landing_url}/#{id.identifier}", + target: '_blank', + style: 'text-decoration: none', + title: titletext, + 'aria-label': titletext, + 'data-toggle': "tooltip" do %> + <% if scheme.logo_url.nil? %> + <i class="fa fa-user" title="<%= scheme.description %>" aria-hidden="true"></i> + <% else %> + <%= image_tag "#{scheme.logo_url}", id: 'orcid-id-logo', alt: scheme.description %> + <% end %> +   + <%= titletext %> + <% end %> + <% end %> + <% unlinktext = _("Unlink your account from #{scheme.description}. You can link again at any time.") %> + <% unlinkconf = _("Are you sure you want to unlink #{scheme.description} ID?") %> + <% end %> + <%= link_to '<i class="fa fa-fw fa-times-circle" aria-hidden="true"></i>'.html_safe, + destroy_user_identifier_path(id), + method: :delete, + title: unlinktext, + data: {confirm: unlinkconf}, + 'aria-label': unlinktext, + 'data-toggle': "tooltip" %> +<% end %> diff --git a/app/views/devise/registrations/_password_confirmation.html.erb b/app/views/devise/registrations/_password_confirmation.html.erb new file mode 100644 index 0000000..c7d7577 --- /dev/null +++ b/app/views/devise/registrations/_password_confirmation.html.erb @@ -0,0 +1,41 @@ +<div class="modal fade" id="password-confirmation" tabindex="-1" role="dialog" aria-labelledby="modal-title"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title">Please confirm your changes</h4> + </div> + <div id="confirmation" class="modal-body"> + <div id="email-change"> + <p><%= _('Your email address is also your login id and therefore an important part of your account information. For your safety we require you to confirm your password to make this change.') %></p> + <div class="form-group"> + <%= f.label :current_password, _('Password'), class: 'control-label', for: 'current_password' %> + <%= f.password_field :current_password, class: 'form-control', id: 'current_password' %> + <%= f.password_field :password, class: 'hide' %> + </div> + </div> + + <div id="org-change" class="hide"> + <p> + <%= _('Are you sure you want to change your organisational affiliation? Doing so will remove your administrative privileges.') %> + </p> + <div class="form-group"> + <label class="control-label"><%= _('Organisation') %></label> + </div> + <div class="checkbox"> + <label for="confirm_org_change"> + <input type="checkbox" id="confirm_org_change" /> + <%= _('Yes, I understand that I will lose my administrative privileges') %> + </label> + </div> + </div> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-default" id="pwd-cancel-confirmation" + data-dismiss="modal">Cancel</button> + <button type="button" class="btn btn-primary" id="pwd-confirmation" + data-dismiss="modal">Continue</button> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/app/views/devise/registrations/_password_details.html.erb b/app/views/devise/registrations/_password_details.html.erb new file mode 100644 index 0000000..e1afd03 --- /dev/null +++ b/app/views/devise/registrations/_password_details.html.erb @@ -0,0 +1,33 @@ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {method: :put, id: "password_details_registration_form" }) do |f| %> + + <p class="form-control-static"><%= _('If you would like to change your password please complete the following fields.') %></p> + + <%= hidden_field_tag :skip_personal_details, "true" %> + + <div class="form-group col-xs-8"> + <%= f.label(:current_password, _('Current password'), class: 'control-label') %> + <%= f.password_field(:current_password, class: "form-control", "aria-required": true ) %> + </div> + + <div class="form-group col-xs-8"> + <%= f.label(:password, _('New password'), class: 'control-label') %> + <%= f.password_field(:password, class: "form-control", "aria-required": true , id: 'user_new_password') %> + </div> + + <div class="form-group col-xs-8"> + <%= f.label(:password_confirmation, _('Password confirmation'), class: 'control-label') %> + <%= f.password_field(:password_confirmation, class: "form-control", "aria-required": true, ) %> + </div> + + <div class="form-group col-xs-8"> + <div class="checkbox"> + <label> + <input type="checkbox" id="passwords_toggle" class="passwords_toggle" /><%= _('Show passwords') %> + </label> + </div> + </div> + + <div class="form-group col-xs-8"> + <%= f.button(_('Save'), class: 'btn btn-default', type: "submit") %> + </div> +<% end %> diff --git a/app/views/devise/registrations/_personal_details.html.erb b/app/views/devise/registrations/_personal_details.html.erb new file mode 100644 index 0000000..0bc6bda --- /dev/null +++ b/app/views/devise/registrations/_personal_details.html.erb @@ -0,0 +1,88 @@ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {method: :put, id: 'personal_details_registration_form' }) do |f| %> + <p class="form-control-static"> + <%= _("Please note that your email address is used as your username. + If you change this, remember to use your new email address on sign in.") %> + </p> + + <p class="form-control-static"><%= _('You can edit any of the details below.') %></p> + <%= hidden_field_tag :unlink_flag, "false", id: 'unlink_flag' %> + + <div class="form-group col-xs-8"> + <%= f.label(:email, _('Email'), class: 'control-label') %> + <%= f.email_field(:email, class: "form-control", "aria-required": true, value: @user.email) %> + <%= hidden_field_tag :original_email, @user.email %> + </div> + + <div class="form-group col-xs-8"> + <%= f.label(:firstname, _('First name'), class: 'control-label') %> + <%= f.text_field(:firstname, class: "form-control", "aria-required": true, value: @user.firstname) %> + </div> + + <div class="form-group col-xs-8"> + <%= f.label(:surname, _('Last name'), class: 'control-label') %> + <%= f.text_field(:surname, class: "form-control", "aria-required": true, value: @user.surname) %> + </div> + + <% org_admin = (current_user.can_org_admin? && !current_user.can_super_admin?) %> + <div class="form-group col-xs-8" id="org-controls" <%= raw "data-toggle=\"tooltip\" title=\"#{_('Changing your organisation will result in the loss of your administrative privileges.')}\"" if org_admin %>> + <%= render partial: "shared/my_org", locals: {f: f, default_org: @default_org, orgs: @orgs, allow_other_orgs: true} %> + </div> + <% if org_admin %> + <input type="hidden" id="original_org" value="<%= @user.org_id %>"> + <% end %> + + <% if MANY_LANGUAGES %> + <div class="form-group col-xs-8"> + <% lang_id = current_user.language.nil? ? Language.id_for(FastGettext.default_locale) : current_user.language.id %> + <%= f.label(:language_id, _('Language'), class: 'control-label') %> + <%= select_tag("user[language_id]", + options_from_collection_for_select(@languages, "id", "name", lang_id), + class: "form-control") %> + </div> + <% end %> + + <% if current_user.can_org_admin? %> + <div class="form-group col-xs-8"> + <label><%= _('My privileges') %></label> + <p><%= (current_user.can_super_admin? ? _('Super Admin') : _('Organisational Admin')) %></p> + </div> + <% end %> + + <% @identifier_schemes.each do |scheme| %> + <div class="form-group col-xs-8"> + <% if scheme.name.downcase == 'shibboleth' %> + <label class='control-label'> + <span class="aria-only" aria-hidden="false"><%= _('Institutional credentials') %></span> + </label> + <% elsif scheme.name.downcase == 'orcid' %> + <%= label_tag(:scheme_name, 'ORCID', class: 'control-label') %> + <% else %> + <%= label_tag(:scheme_name, scheme.name.capitalize, class: 'control-label') %> + <% end %> + + <div class='identifier-scheme'> + <%= render partial: "external_identifier", + locals: { scheme: scheme, + id: current_user.identifier_for(scheme)} %> + </div> + </div> + <% end %> + + <% unless @user.api_token.blank? %> + <div class="form-group col-xs-8"> + <%= f.label(:api_token, _('API token'), class: 'control-label') %> + <%= @user.api_token %> + </div> + <div class="form-group col-xs-8"> + <%= label_tag(:api_information, _('API Information'), class: 'control-label') %> + <a href="https://github.com/DMPRoadmap/roadmap/wiki/API-Documentation"><%= _('How to use the API') %></a> + </div> + <% end %> + + <div class="form-group col-xs-8"> + <%= f.button(_('Save'), class: 'btn btn-default', type: "submit", id: "personal_details_registration_form_submit") %> + </div> + + <%= render partial: 'password_confirmation', locals: {f: f} %> + +<% end %> diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 8bb0f82..75e0d7f 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -1,117 +1,46 @@ -<div class="main"> - <h1><%= _('Edit profile') %></h1> +<div class="row"> + <div class="col-md-12"> + <h1><%= _('Edit profile') %></h1> + </div> +</div> - <p> - <%= _("Please note that your email address is used as your username. If you change this, remember to use your new email address on sign in.") %> - </p> - - <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {method: :put, class: "roadmap-form white_background"}) do |f| %> - <!-- Edit Profile --> - <fieldset class="side-by-side"> - <legend><%= _('You can edit any of the details below.') %></legend> - - <%= hidden_field_tag :unlink_flag, "false", id: "unlink_flag" %> - - <div> - <%= f.label :email, _('Email') + " *" %> - <%= f.email_field :email, as: :email, class: "input-medium has-tooltip", - "data-toggle": "tooltip", "data-trigger": "focus", - "title": _('Please enter your current password below when changing your email address.') %> - </div> - <div> - <%= f.label :firstname, _('First name')+ " *" %> - <%= f.text_field :firstname, as: :string, - id: "first_time_login_firstname", - autofocus: true, - class: "input-medium has-tooltip", - "data-toggle" => "tooltip", - "data-trigger" => "focus" , - "title" => _('Please enter your first name.') %> - </div> - <div> - <%= f.label :surname, _('Last name') + " *" %> - <%= f.text_field :surname, as: :string, id: "first_time_login_surname", - class: "input-medium has-tooltip", "data-toggle" => "tooltip", - "data-trigger" => "focus" , - "title" => _('Please enter your surname or family name.') %> - </div> - <div> - <%= f.label :org, _('Organisation') + " *" %> - <%= render partial: "shared/accessible_combobox", - locals: {name: "#{resource_name}[org_name]", - id: "#{resource_name}_org_name", - default_selection: @default_org, - models: @orgs, - attribute: 'name', - classes: 'fixed-width-large'} %> - </div> - - <% if MANY_LANGUAGES %> - <div> - <% lang = current_user.language.nil? ? FastGettext.default_locale : current_user.language.abbreviation %> - <%= f.label :language, _('Language') %> - <select id="user_language_id" name="user[language_id]" - class="input-medium"> - <%= @languages.each do |l| %> - <option value="<%= l.id %>"<%= lang == l.abbreviation ? ' selected="selected"' : '' %>> - <%= l.name %> - </option> - <% end %> - </select> - </div> - <% end %> - - <% @identifier_schemes.each do |scheme| %> - <div> - <label> - <% if scheme.logo_url.nil? %> - <i class="fa fa-user" title="<%= scheme.name %>" aria-hidden="true" style="font-size:16px; width:16px; text-align:center;"></i> - <% else %> - <img title="<%= scheme.name %>" src=<%= "#{scheme.logo_url}" %> size="16px" /> - <% end %> - </label> - <div class="identifier-scheme"> - <%= render partial: 'external_identifier', - locals: {scheme: scheme, - id: current_user.identifier_for(scheme)} %> +<div class="row"> + <div class="col-md-12"> + <ul class="nav nav-tabs" role="tablist"> + <li role="personal-details" class="active"> + <a href="#personal-details" role="tab" aria-controls="personal-details" data-toggle="tab"><%= _('Personal Details') %></a> + </li> + <li role="password-details"> + <a href="#password-details" role="tab" aria-controls="password-details" data-toggle="tab"><%= _('Password') %></a> + </li> + <li role="notification-preferences"> + <a href="#notification-preferences" role="tab" aria-controls="notification-preferences" data-toggle="tab"><%= _('Notification Preferences') %></a> + </li> + </ul> + + <div class="tab-content"> + <div id="personal-details" role="tabpanel" class="tab-pane active"> + <div class="panel panel-default"> + <div class="panel-body"> + <%= render partial: 'devise/registrations/personal_details' %> </div> </div> - <% end %> - - <% unless @user.api_token.blank? %> - <div> - <%= f.label :api_token, _('API token') %><%= @user.api_token %> + </div> + <div id="password-details" role="tabpanel" class="tab-pane"> + <div class="panel panel-default"> + <div class="panel-body"> + <%= render partial: 'devise/registrations/password_details' %> + </div> </div> - <div> - <label><%= _('API Information') %></label><%= link_to( _('How to use the API'), controller: "token_permission_types", action: "index")%> + </div> + <div id="notification-preferences" role="tabpanel" class="tab-pane"> + <div class="panel panel-default"> + <div class="panel-body"> + <%= render partial: 'users/notification_preferences' %> + </div> </div> - <% end %> + </div> + </div> - <br /> - - <legend><%= _('If you would like to change your password please complete the following fields.') %></legend> - - <div> - <%= f.label :current_password, _('Current password') %> - <%= f.password_field :current_password, as: :password, class: 'input-medium' %> - </div> - - <div> - <%= f.label :password, _('New password') %> - <%= f.password_field :password, as: :password, autocomplete: "off", class: 'input-medium' %> - </div> - - <div> - <%= f.label :password_confirmation, _('Password confirmation') %> - <%= f.password_field :password_confirmation, as: :password, autocomplete: "off", - class: 'input-medium' %> - </div> - - <div> - <label></label> - <input type="submit" class="form-submit" value="<%= _('Save') %>" /> - </div> - </fieldset> - - <% end %> -</div> + </div> +</div> \ No newline at end of file diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 0c689be..463f8f2 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -1,13 +1,31 @@ -<h1><%= _('Create account') %></h1> -<% unless session["devise.shibboleth_data"].nil? %> - <p class="alert alert-notice"> - <%= (_("%{application_name} doesn't recognise your institutional credentials - either you haven't created an account with us or you haven't linked these details to your existing account.<br/>* If you <strong>do not have</strong> an account with %{application_name}, please complete the form below.<br/>* If you <strong>have</strong> an account with %{application_name}, please <a href='#header-login-form' data-toggle='modal' class='a_orange'>Sign in</a> so we can link your account to your institutional credentials.<br/>Once you have created and/or linked your account, you'll be able to sign in with your institutional credentials directly.") % { :application_name => Rails.configuration.branding[:application][:name] }).html_safe %> - </p> - <% cookies[:show_shib_link] = { value: 'show_shib_link', expires: 3.hours.from_now } %> -<% end %> - -<div class='white_background'> - <br /> - <!-- call the register form block--> - <%= render :partial => 'shared/register_form', locals: {extended: true} %> -</div> +<div class="row"> + <div id="dual_columns" class="container" style="display:table"> + <% unless session["devise.shibboleth_data"].nil? %> + <% cookies[:show_shib_link] = { value: 'show_shib_link', expires: 3.hours.from_now } %> + <div class="col-md-6" style="border-top:1px solid black; border-bottom:1px solid black; border-left:1px solid black; display:table-cell; float:none"> + <h3 class="text-center"><%= _("Do you have a %{application_name} account?") % {application_name: Rails.configuration.branding[:application][:name]} %></h3> + <big><p class="text-center"><i class="fa fa-arrow-circle-down" aria-hidden="true"></i></p></big> + <h2 class="text-center"> + <%= _("Sign in") %></i> + </h2> + <p class="text-center"><%= _("This will link your existing account to your credentials.") %></p> + <p><%= render :partial => 'shared/sign_in_form' %><br></p> + </div> + <div class="col-md-6" style="border:1px solid black; display:table-cell; float:none"> + <h3 class="text-center"><%= _("No %{application_name} account?") % {application_name: Rails.configuration.branding[:application][:name]} %></h3> + <big><p class="text-center"><i class="fa fa-arrow-circle-down" aria-hidden="true"></i></p></big> + <h2 class="text-center"> + <%= _("Create account") %></i> + </h2> + <p class="text-center"><%= _("This will create an account and link it to your credentials.") %></p> + <p><%= render :partial => 'shared/create_account_form', locals: {extended: false} %><br></p> + </div> + <% else %> + <div> + <h2><%= _("Create account") %>  <i class="fa fa-user-plus" aria-hidden="true">  </i></h2> + <%= render :partial => 'shared/create_account_form', locals: {extended: true} %> + </div> + <% end %> + </div> +</div> + diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb deleted file mode 100644 index 90556a7..0000000 --- a/app/views/devise/sessions/new.html.erb +++ /dev/null @@ -1,33 +0,0 @@ -<h1><%= _('Sign in') %></h1> -<div class="dmp_details_body"> - - <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> - <div class="dmp_details"> - <table class="dmp_details_table"> - <tr> - <td class="first"><%= _('Email') %></td> - <td><%= f.email_field :email, :autofocus => true %></td> - </tr> - <tr> - <td class="first"><%= _('Password') %></td> - <td><%= f.password_field :password %></td> - </tr> - <tr> - <td class="first"> </td> - <td><% if devise_mapping.rememberable? -%> - <div> - <%= f.check_box :remember_me %> - <%= f.label :remember_me %> - </div> - <% end -%> - </td> - </tr> - </table> - <div class="div_right"> - <%= f.submit _('Sign in'), :class => "btn btn-primary" %> - </div> - </div> - <% end %> - - <%= render "devise/shared/links" %> -</div> diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb deleted file mode 100644 index 40792b2..0000000 --- a/app/views/devise/unlocks/new.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -<h2><%= t('custom_devise.resend_unlock') %></h2> - -<%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %> - <%= devise_error_messages! %> - - <div><%= f.label :email %><br /> - <%= f.email_field :email, :autofocus => true %></div> - - <div><%= f.submit "Resend unlock instructions" %></div> -<% end %> - -<%= render "devise/shared/links" %> diff --git a/app/views/guidance_groups/_guidance_group_form.html.erb b/app/views/guidance_groups/_guidance_group_form.html.erb new file mode 100644 index 0000000..e7b5d18 --- /dev/null +++ b/app/views/guidance_groups/_guidance_group_form.html.erb @@ -0,0 +1,14 @@ +<div class="form-group"> + <%= 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 %> +</div> + +<div class="checkbox"> + <%= f.label :published, raw("#{f.check_box :published, 'data-toggle': 'tooltip', title: _('Check this box when you are ready for guidance associated with this group to appear on user\'s plans.')} #{_('Published')}") %> +</div> + +<div class="checkbox"> + <%= f.label :optional_subset, raw("#{f.check_box :optional_subset, 'data-toggle': 'tooltip', title: _('If the guidance is only meant for a subset of users e.g. those in a specific college or institute, check this box. Users will be able to select to display this subset guidance when answering questions in the \'create plan\' wizard.')} #{_('Optional Subset (e.g. School/Department)')}") %> +</div> + +<%= f.submit _('Save'), class: 'btn btn-primary' %> \ No newline at end of file diff --git a/app/views/guidance_groups/_show.html.erb b/app/views/guidance_groups/_show.html.erb new file mode 100644 index 0000000..4b21cea --- /dev/null +++ b/app/views/guidance_groups/_show.html.erb @@ -0,0 +1,37 @@ +<%# locals: { group, question, guidance_accordion_id }%> +<div id="guidance-<%= guidance_accordion_id %>-accordion" class="panel-group" role="tablist" aria-multiselectable="true"> + <% if group.keys.length > 1 %> + <div id="guidance-accordion-controls"> + <div class="accordion-controls" data-parent="guidance-<%= guidance_accordion_id %>-accordion"> + <a href="#" data-toggle-direction="show"><%= _('expand all') %></a> + <span>|</span> + <a href="#" data-toggle-direction="hide"><%= _('collapse all') %></a> + </div> + </div> + <% end %> + <% group.keys.each_with_index do |theme, i| %> + <div class="panel panel-default"> + <div class="heading-button" role="button" data-toggle="collapse" + data-parent="<%= question.id %>-guidance" + href="#collapse-guidance-<%= question.id %>-<%= guidance_accordion_id %>-<%= i %>" + aria-expanded="false" + aria-controls="#collapse-guidance-<%= question.id %>-<%= guidance_accordion_id %>-<%= i %>"> + + <div class="panel-heading" role="tab" id="heading-<%= i %>"> + <h2 class="panel-title"> + <%= theme %> + <i class="fa fa-plus pull-right" aria-hidden="true"></i> + </h2> + </div> + </div> + <div id="collapse-guidance-<%= question.id %>-<%= guidance_accordion_id %>-<%= i %>" class="panel-collapse collapse" role="tabpanel" + aria-labelledby="heading-guidance-<%= question.id %>-<%= guidance_accordion_id %>-<%= i %>"> + <div class="panel-body"> + <% group[theme].each do |guidance| %> + <%= raw guidance %> + <% end %> + </div> + </div> + </div> + <% end %> +</div> \ No newline at end of file diff --git a/app/views/guidance_groups/admin_edit.html.erb b/app/views/guidance_groups/admin_edit.html.erb index 4e9b870..1730ddd 100644 --- a/app/views/guidance_groups/admin_edit.html.erb +++ b/app/views/guidance_groups/admin_edit.html.erb @@ -1,75 +1,37 @@ -<%= stylesheet_link_tag "admin" %> -<% javascript 'admin.js' %> - -<h1> - <%= _('Guidance group') %> - <!-- link button to add new guidance --> - <div class="move_2_right"> - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: 'btn btn-primary' %> +<div class="row"> + <div class="col-md-12"> + <h1><%= _('Guidance group') %></h1> + <%= link_to _('View all guidance'), admin_index_guidance_path(current_user.org_id), class: 'btn btn-default pull-right' %> </div> -</h1> +</div> -<div class="div_clear"></div> - -<div class="white_background blue_border"> - <!-- body --> - <div class="dmp_details"> - <%= form_for(@guidance_group, url: admin_update_guidance_group_path(@guidance_group), html: {method: :put}) do |f| %> - - <table class="dmp_details_table guidance_table"> - <tr> - <td class="first"><%= _('Name') %></td> - <td> - <div class="div_left_icon_g"> - <%= f.text_field :name, - as: :string, - class: 'text_field' %> - - </div> - <div class="div_right_icon_g"> - <%= link_to(image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('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 institution or department name.')) %> - </div> - </td> - </tr> - <tr> - <td class="first"><%= _('Published') %></td> - <td> - <div class="div_left_icon_g"> - <%= f.check_box :published %> - </div> - <div class="div_right_icon_g"> - <!--%= link_to( image_tag('help_button.png'), '#', :class => 'guidance_group_subset_popover', :rel => "popover", 'data-html' => "true", 'data-content' => _('If the guidance is only meant for a subset of users e.g. those in a specific college or institute, check this box. Users will be able to select to display this subset guidance when answering questions in the 'create plan' wizard.'))%--> - </div> - </td> - </tr> - - <tr> - <td class="first"><%= _('Optional subset') %></td> - <td> - <div class="div_left_icon_g"> - <%= f.check_box :optional_subset %> <%= _('e.g. School/ Department') %> - </div> - <div class="div_right_icon_g"> - <%= link_to(image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _("If the guidance is only meant for a subset of users e.g. those in a specific college or institute, check this box. Users will be able to select to display this subset guidance when answering questions in the 'create plan' wizard.")) %> - </div> - </td> - </tr> - - </table> - - <!-- submit buttons --> - - <div class="move_2_right"> - <%= f.submit _('Save'), class: 'btn btn-primary' %> - <% if @guidance_group.published == false then %> - <%= f.submit _('Publish'), name: "save_publish", class: "btn btn-primary" %> - <% end %> - <%= link_to _('Cancel'), :back, class: 'btn cancel' %> +<div class="row"> + <div class="col-md-12"> + <%= form_for(@guidance_group, url: admin_update_guidance_group_path(@guidance_group), html: {method: :put, id: "admin_update_guidance_group_form" }) do |f| %> + + <div class="form-group col-xs-8"> + <%= 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 tell the end user where the guidance has come from. We suggest you use the organisation or department name e.g. "OU" or "Maths & Stats"') %> </div> - <br/> + <div class='form-input col-xs-8'> + <%= f.check_box :published, 'data-toggle': 'tooltip', title: _("Check this box when you are ready for guidance associated with this group to appear on user's plans.") %> + <%= f.label _('Published'), for: :published, class: "control-label" %> + </div> + + <div class='form-input col-xs-8'> + <%= f.check_box :optional_subset, 'data-toggle': 'tooltip', title: _("If the guidance is only meant for a subset of users e.g. those in a specific college or institute, check this box. Users will be able to select to display this subset guidance when answering questions in the 'create plan' wizard.") %> + <%= f.label _('Optional Subset'), for: :optional_subset, class: "control-label" %><%= _(' (e.g. School/ Department) ') %> + </div> + + <div class="form-group col-xs-8"> + <%= f.submit _('Save'), class: "btn btn-primary" %> + <% if @guidance_group.published == false then %> + <%= f.submit _('Publish'), name: "save_publish", class: "btn btn-primary" %> + <% end %> + <%= link_to _('Cancel'), admin_index_guidance_path, class: "btn btn-primary", role: 'button' %> + </div> <% end %> </div> </div> + diff --git a/app/views/guidance_groups/admin_new.html.erb b/app/views/guidance_groups/admin_new.html.erb index 2c466cb..2a65260 100644 --- a/app/views/guidance_groups/admin_new.html.erb +++ b/app/views/guidance_groups/admin_new.html.erb @@ -1,52 +1,33 @@ -<%= stylesheet_link_tag "admin" %> -<% javascript 'admin.js' %> - -<h1> - <%= _('Guidance group') %> - <!-- link button to add new guidance --> - <div class="move_2_right"> - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: "btn btn-primary" %> - </div> -</h1> - -<div class="div_clear"></div> - -<div class="white_background blue_border"> - <!-- body --> - <div class="dmp_details"> - <%= form_for :guidance_group, url: {action: "admin_create"} do |f| %> - - <table class="dmp_details_table guidance_table"> - <tr> - <td class="first"><%= _('Name') %></td> - <td><div class="div_left_icon_g"> - <%= f.text_field :name, as: :string, class: "text_field" %> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('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 institution or department name.'))%> - </div> - </td> - </tr> - <tr> - <td class="first"><%= _('Optional subset') %></td> - <td><div class="div_left_icon_g"> - <%= f.check_box :optional_subset %> <%= _('e.g. School/ Department') %> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _("If the guidance is only meant for a subset of users e.g. those in a specific college or institute, check this box. Users will be able to select to display this subset guidance when answering questions in the 'create plan' wizard."))%> - </div> - </td> - </tr> - </table> - - <!-- submit buttons --> - <div class="move_2_right"> - <%= f.submit _('Save'), name: "draft", class: "btn btn-primary" %> - <%= link_to _('Cancel'), :back, class: "btn cancel btn-secondary" %> - </div> - <br /> - <% end %> - </div> +<div class="row"> + <div class="col-md-12"> + <h1><%= _('Guidance group') %></h1> + <%= link_to _('View all guidance'), admin_index_guidance_path(current_user.org_id), class: 'btn btn-default pull-right' %> + </div> </div> + +<div class="row"> + <div class="col-md-12"> + <%= form_for :guidance_group, url: {action: "admin_create"}, id: 'admin_create_guidance_group_form' do |f| %> + <div class="form-group col-xs-8"> + <%= 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 tell the end user where the guidance has come from. We suggest you use the organisation or department name e.g. "OU" or "Maths & Stats"') %> + </div> + + <div class='form-input col-xs-8'> + <%= f.check_box :published, 'data-toggle': 'tooltip', title: _("Check this box when you are ready for guidance associated with this group to appear on user's plans.") %> + <%= f.label _('Published'), for: :published, class: "control-label" %> + </div> + + <div class='form-input col-xs-8'> + <%= f.check_box :optional_subset, 'data-toggle': 'tooltip', title: _("If the guidance is only meant for a subset of users e.g. those in a specific college or institute, check this box. Users will be able to select to display this subset guidance when answering questions in the 'create plan' wizard.") %> + <%= f.label _('Optional Subset'), for: :optional_subset, class: "control-label" %><%= _(' (e.g. School/ Department) ') %> + </div> + + <!-- submit buttons --> + <div class="form-group col-xs-8"> + <%= f.submit _('Save'), name: "draft", class: "btn btn-primary" %> + <%= link_to _('Cancel'), admin_index_guidance_path, class: "btn btn-primary", role: 'button' %> + </div> + <% end %> + </div> +</div> \ No newline at end of file diff --git a/app/views/guidance_groups/admin_show.html.erb b/app/views/guidance_groups/admin_show.html.erb deleted file mode 100644 index d73054d..0000000 --- a/app/views/guidance_groups/admin_show.html.erb +++ /dev/null @@ -1,62 +0,0 @@ -<%= stylesheet_link_tag "admin" %> - -<h1> - <%= _('Guidance group') %> - - <!-- link button to add new guidance --> - <div class="move_2_right"> - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: "btn btn-primary" %> - </div> -</h1> - -<div class="div_clear"></div> - -<div class="white_background blue_border"> - <!-- body --> - <div class="dmp_details"> - - <table class="dmp_details_table"> - <tr> - <td class="first"><%= _('Name') %></td> - <td><%= raw @guidance_group.name %></td> - </tr> - <tr> - <td class="first"><%= _('Published') %></td> - <td> - <% if @guidance_group.published.nil? || !@guidance_group.published then %> - <%= _('No') %> - <% else %> - <%= _('Yes') %> - <% end %> - </td> - </tr> - - <tr> - <td class="first"><%= _('Optional subset') %></td> - <td> - <% if @guidance_group.optional_subset.nil? || !@guidance_group.optional_subset then %> - <%= _('No') %> - <% else %> - <%= _('Yes') %> - <% end %> - </td> - </tr> - <tr> - <td class="first"><%= _('Created') %></td> - <td><%= l @guidance_group.created_at.to_date, formats: :short %></td> - </tr> - <tr> - <td class="first"><%= _('Last updated') %></td> - <td><%= l @guidance_group.updated_at.to_date, formats: :short %></td> - </tr> - - </table> - <div class="move_2_right"> - <%= link_to _('Edit'), admin_edit_guidance_group_path(@guidance_group.id), class: "btn btn-primary" %> - <%= link_to _('Back'), :back, class: "btn cancel" %> - </div> - <br/> - </div> -</div> \ No newline at end of file diff --git a/app/views/guidances/_add_guidance.html.erb b/app/views/guidances/_add_guidance.html.erb deleted file mode 100644 index ca49a90..0000000 --- a/app/views/guidances/_add_guidance.html.erb +++ /dev/null @@ -1,83 +0,0 @@ -<!--add guidance. question is passed as an argument--> -<div class="dmp_details"> - <%= form_for :guidance, url: {action: "admin_create"}, html: {id: "new_guidance_form"} do |f| %> - <table class="dmp_details_table guidance_table"> - <tr> - <td class="first"><%= _('Text') %></td> - <td> - <div class="div_left_icon_g"> - <%= text_area_tag("guidance-text", "", class: "tinymce") %> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _('Enter your guidance here. You can include links where needed.'))%> - </div> - <div class="clearfix"></div> - </td> - </tr> - <tr> - <td class="first"><%= _('Should this guidance apply:') %></td> - <td> - <div class="guindance_by_theme" style="display:none;"> - <div class="div_left_icon_g"> - <%= f.collection_select(:theme_ids, - Theme.all.order("title"), - :id, :title, {prompt: false, include_blank: _('None')}, {multiple: true})%> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _('Select which theme(s) this guidance relates to.'))%> - </div> - <div class="clearfix"></div> - </div> - </td> - </tr> - <tr> - <td class="first"><%= _('Published') %></td> - <td> - <div class="div_left_icon_g"> - <%= f.check_box :published , as: :check_boxes %> - </div> - </td> - </tr> - <tr> - <td class="first"><%= _('Guidance group') %></td> - <td> - <div class="div_left_icon_g"> - <%= f.collection_select(:guidance_group_ids, - GuidanceGroup.where(org_id: current_user.org_id).order("name ASC"), - :id, :name, {prompt: false, include_blank: _('None')}, {multiple: false})%> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _('Select which group this guidance relates to.'))%> - </div> - <div class="clearfix"></div> - </td> - </tr> - </table> - - <!-- submit buttons --> - - <div class="move_2_right"> - <a id="new_guidance_submit" data-toggle="modal" href="#new_guidance_alert_dialog" class="btn btn-primary"><%= _('Save')%></a> - - <%= link_to _('Cancel'), :back, class: "btn cancel" %> - </div> - - <br /> - <%= tinymce :content_css => asset_path('application.css') %> - <% end %> -</div> - - - -<!-- alert for guidance--> -<div id="new_guidance_alert_dialog" data-container="body" data-backdrop="static" class="modal hide fade"> - <div class="modal-header"> - <h3><%= _('Before submitting, please consider:') %></h3> - </div> - <div class="modal-body"> - <ul id="missing_fields_new_guidance"></ul> - </div> - <div class="modal-footer"> - <a href="#" id="return_to_new_guidance" class="btn"><%= _('Ok') %></a> - </div> -</div> \ No newline at end of file diff --git a/app/views/guidances/_edit_guidance.html.erb b/app/views/guidances/_edit_guidance.html.erb deleted file mode 100644 index ffae58d..0000000 --- a/app/views/guidances/_edit_guidance.html.erb +++ /dev/null @@ -1,26 +0,0 @@ -<!--edit annotation. question is passed as an argument--> -<%= form_for(suggested_answer, url: admin_update_suggested_answer_path(suggested_answer), html: { method: :put}) do |f| %> - <%= f.hidden_field :org_id, value: current_user.org_id %> - - <table class="dmp_details_table phase"> - <tr> - <td class="first_template"><%= _('Suggested answer/ Example')%></td> - <td> - <ul> - <li><%= f.select :is_example, {_('Example Answer') => true, _('Suggested answer') => false} %></li> - <li><%= f.text_area :text, rows: 5 %></li> - </ul> - </td> - </tr> - </table> - <br/> - - <!-- submit buttons --> - <div class="move_2_right"> - <%= f.submit _('Save'), class: "btn btn-primary" %> - <%= link_to _('Delete'), admin_destroy_suggested_answer_path(id: suggested_answer.id), - confirm: _("You are about to delete a suggested answer/ example for '%{question_text}'. Are you sure?") % { :question_text => question.text }, method: :delete, class: "btn btn-primary"%> - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - <%= link_to _('Cancel'), "#", class: "btn cancel cancel_edit_suggested_answer" %> - </div> -<%end%> diff --git a/app/views/guidances/admin_edit.html.erb b/app/views/guidances/admin_edit.html.erb deleted file mode 100644 index 769fc6e..0000000 --- a/app/views/guidances/admin_edit.html.erb +++ /dev/null @@ -1,102 +0,0 @@ -<%= stylesheet_link_tag "admin" %> -<% javascript 'admin.js' %> - -<h1> - <%= _('Guidance') %> - <!-- link button to add new guidance --> - <div class="move_2_right"> - <%= link_to _('Add guidance'), - admin_new_guidance_path, - class: 'btn btn-primary' %> - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: 'btn btn-primary' %> - </div> -</h1> - -<div class="div_clear"></div> - -<div class="white_background blue_border"> - <!-- body --> - <div class="dmp_details"> - <%= form_for(@guidance, url: admin_update_guidance_path(@guidance), html: { method: :put , id: 'edit_guidance_form'}) do |f| %> - - <table class="dmp_details_table guidance_table"> - <tr> - <td class="first"><%= _('Text') %></td> - <td class="tinymce"> - <div class="div_left_icon_g"> - <%= text_area_tag("guidance-text", @guidance.text, class: "tinymce") %> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('Enter your guidance here. You can include links where needed.'))%> - </div> - <div class="clearfix"></div> - </td> - </tr> - <tr> - <td class="first"><%= _('Themes') %></td> - <td> - <div class="guindance_by_theme"> - <div class="div_left_icon_g"> - <%= f.collection_select(:theme_ids, @themes, :id, :title, - {prompt: false, include_blank: 'None'}, {multiple: true})%> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('Select which theme(s) this guidance relates to.'))%> - </div> - <div class="clearfix"></div> - </div> - </td> - - </tr> - <tr> - <td class="first"><%= _('Published') %></td> - <td><div class="div_left_icon_g"> - <%= f.check_box :published , as: :check_boxes%> - </div> - </td> - </tr> - <tr> - <td class="first"><%= _('Guidance group') %></td> - <td> - <div class="div_left_icon_g"> - <%= f.collection_select(:guidance_group_id, @guidance_groups, - :id, :name, {prompt: false, include_blank: 'None'}, {multiple: false})%> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('Select which group this guidance relates to.'))%> - </div> - <div class="clearfix"></div> - - </td> - </tr> - - </table> - - <!-- submit buttons --> - - <div class="move_2_right"> - <a id="edit_guidance_submit" data-toggle="modal" href="#edit_guidance_alert_dialog" class="btn btn-primary"><%= _('Save')%></a> - <%= link_to _('Cancel'), :back, class: 'btn cancel' %> - </div> - - <br /> - <%= tinymce :content_css => asset_path('application.css') %> - <%end%> - </div> -</div> - - -<!-- alert for the default template--> -<div id="edit_guidance_alert_dialog" data-container="body" data-backdrop="static" class="modal hide fade"> - <div class="modal-header"> - <h3><%= _('Before submitting, please consider:') %></h3> - </div> - <div class="modal-body"> - <ul id="missing_fields_edit_guidance"></ul> - </div> - <div class="modal-footer"> - <a href="#" id="return_to_edit_guidance" class="btn"><%= _('Ok') %></a> - </div> -</div> diff --git a/app/views/guidances/admin_index.html.erb b/app/views/guidances/admin_index.html.erb index 6010b35..03bf76c 100644 --- a/app/views/guidances/admin_index.html.erb +++ b/app/views/guidances/admin_index.html.erb @@ -1,140 +1,48 @@ -<%= stylesheet_link_tag "admin" %> -<% javascript "admin.js" %> +<div class="row"> + <div class="col-md-12"> + <h1><%= _('Guidance') %></h1> - -<h1> - <%= _('Guidance group list') %> -</h1> - -<div class="div_left"> - <%= raw _("<p>First create a guidance group. This could be institution wide or a subset e.g. a particular College / School, Institute or department. When you create guidance you'll be asked to assign it to a guidance group.</p>")%> -</div> -<div class="div_right"> - <div class="move_2_right"> - <%= link_to _('Add guidance group'), admin_new_guidance_group_path(), class: "btn btn-primary" %> - </div> + <p class="text-justify"> + <%= _("First create a guidance group. This could be organisation wide or a subset e.g. a particular College / School, Institute or department. When you create guidance you'll be asked to assign it to a guidance group.") %> + </p> + </div> </div> -<!-- List of guidance groups --> -<% if @guidance_groups.length > 0 then%> - <table id="dmp_table_2" class="dmp_table tablesorter"> - <thead> - <tr> - <th class="dmp_th_medium"><%= _('Name') %></th> - <th class="dmp_th_small"><%= _('Published') %></th> - <th class="dmp_th_small"><%= _('Optional subset') %></th> - <th class="dmp_th_small"><%= _('Last updated') %></th> - <td class="dmp_th_medium"><%= _('Actions') %></td> - </tr> - </thead> - <tbody> - <% !@guidance_groups.each do |guidance_gr| %> - <tr> - <td class="dmp_td_medium"> - <%= guidance_gr.name %> - </td> - <td class="dmp_td_small"> - <% if guidance_gr.published.nil? || guidance_gr.published == false then%> - <%= _('No')%> - <% else %> - <%= _('Yes')%> - <% end %> - </td> - <td class="dmp_td_small"> - <% if guidance_gr.optional_subset.nil? || guidance_gr.optional_subset == false then%> - <%= _('No')%> - <% else %> - <%= _('Yes')%> - <% end %> - </td> +<div class="row"> + <div class="col-md-12"> + <h2><%= _('Guidance group list') %></h2> + + <!-- List of guidance groups --> + <%= paginable_renderise( + partial: '/paginable/guidance_groups/index', + controller: 'paginable/guidance_groups', + action: 'index', + scope: @guidance_groups, + query_params: { sort_field: 'guidance_groups.name', sort_direction: :asc }) %> + <div> + <a href="<%= admin_new_guidance_group_path %>" class="btn btn-primary"><%= _('Create a guidance group') %></a> + </div> - <td class="dmp_td_small"> - <%= l guidance_gr.updated_at.to_date, formats: :short %> - </td> - <td class="dmp_td_medium"> - <%= link_to _('View'), admin_show_guidance_group_path(guidance_gr), class: "dmp_table_link"%><br/> - <%= link_to _('Edit'), admin_edit_guidance_group_path(guidance_gr), class: "dmp_table_link"%><br/> - <%= link_to _('Delete'), admin_destroy_guidance_group_path(guidance_gr), data: {confirm: _("You are about to delete '%{guidance_group_name}'. This will affect guidance. Are you sure?") % { :guidance_group_name => guidance_gr.name }}, method: :delete, class: "dmp_table_link"%> - </td> - </tr> - <% end %> - </tbody> - </table> -<%end%> + <h2><%= _('Guidance list') %></h2> -<br/> -<div class="div_clear"></div> + <p class="text-justify"> + <%= _('You can write pieces of guidance to be displayed by theme (e.g. generic guidance on storage and backup that should present across the board). Writing generic guidance by theme saves you time and effort as your advice will be automatically displayed across all templates rather than having to write guidance to accompany each.') %> + </p> + <p class="text-justify"> + <%= _('If you do have a need to provide guidance for specific funders that would not be useful to a wider audience (e.g. if you have specific instructions for applicants to BBSRC for example), you can do so by adding guidance to a specific question when you edit your template.') %> + </p> -<h1> - <%= _('Guidance list') %> -</h1> - -<div class="div_left"> - <%= raw _('<p>You can write pieces of guidance to be displayed by theme (e.g. generic guidance on storage and backup that should present across the board). Writing generic guidance by theme saves you time and effort as your advice will be automatically displayed across all templates rather than having to write guidance to accompany each.</p> <p>If you do have a need to provide guidance for specific funders that would not be useful to a wider audience (e.g. if you have specific instructions for applicants to BBSRC for example), you can do so by adding guidance to a specific question when you edit your template.</p>')%> + <!-- List of guidance --> + <%= paginable_renderise( + partial: '/paginable/guidances/index', + controller: 'paginable/guidances', + action: 'index', + scope: @guidances, + query_params: { sort_field: 'guidances.text', sort_direction: :asc }) %> + + <div> + <a href="<%= admin_new_guidance_path %>" class="btn btn-primary"><%= _('Create guidance') %></a> + </div> + </div> </div> -<div class="div_right"> - <!-- link button to add new guidance --> - <div class="move_2_right"> - <%= link_to _('Add guidance'), - admin_new_guidance_path(), - class: "btn btn-primary" %> - </div> -</div> - -<div class="div_clear"></div> - -<!-- List of guidance --> -<% if @guidances.length > 0 then%> - <table id="dmp_table" class="dmp_table tablesorter"> - <thead> - <tr> - <th class="dmp_th_big"><%= _('Text') %></th> - <th class="dmp_th_small"><%= _('Themes') %></th> - <th class="dmp_th_small"><%= _('Guidance group') %></th> - <th class="dmp_th_small"><%= _('Last updated') %></th> - <td class="dmp_th_medium"><%= _('Actions') %></td> - </tr> - </thead> - <tbody> - <% @guidances.each do |guidance| %> - <% if guidance.in_group_belonging_to?(current_user.org_id) then %> - <tr> - <td class="dmp_td_big"> - <%= guidance.text.html_safe%> - </td> - <% if guidance.themes.present? then %> - <td class="dmp_td_small"> - <% guidance.themes.each do |th| %> - <%= th.title %> - <% end %> - </td> - <% else %> - <td class="dmp_td_small"> - - - </td> - <% end %> - <% if guidance.guidance_group.present? then %> - <td class="dmp_td_small"> - <%= guidance.guidance_group.name %> - </td> - <% else %> - <td class="dmp_td_small"> - - - </td> - <% end %> - <td class="dmp_td_small"> - <%= l guidance.updated_at.to_date, formats: :short %> - </td> - <td class="dmp_td_medium"> - <%= link_to _('View'), admin_show_guidance_path(guidance), class: "dmp_table_link"%><br/> - <%= link_to _('Edit'), admin_edit_guidance_path(guidance), class: "dmp_table_link"%><br/> - <%= link_to _('Delete'), admin_destroy_guidance_path(guidance), - data: {confirm: _("You are about to delete '%{guidance_summary}'. Are you sure?") % { :guidance_summary => truncate(sanitize(guidance.text,tags: %w(br a)), length: 20 , omission: _('... (continued)'))} }, method: :delete, class: "dmp_table_link"%> - </td> - </tr> - <% end %> - <% end %> - </tbody> - </table> -<% end %> diff --git a/app/views/guidances/admin_new.html.erb b/app/views/guidances/admin_new.html.erb deleted file mode 100644 index a67fe0e..0000000 --- a/app/views/guidances/admin_new.html.erb +++ /dev/null @@ -1,99 +0,0 @@ -<%= stylesheet_link_tag "admin" %> -<% javascript 'admin.js' %> - -<h1> - <%= _('New guidance') %> - <!-- link button to add new guidance --> - <div class="move_2_right"> - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: 'btn btn-primary' %> - </div> -</h1> - -<div class="div_clear"></div> -<div class="white_background blue_border"> - <!-- body --> - <div class="dmp_details"> - <%= form_for :guidance, url: {action: 'admin_create'}, html: {id: 'new_guidance_form'} do |f| %> - <table class="dmp_details_table guidance_table"> - <tr> - <td class="first"><%= _('Text') %></td> - <td class="tinymce"> - <div class="div_left_icon_g"> - <%= text_area_tag("guidance-text", "", class: "tinymce") %> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('Enter your guidance here. You can include links where needed.'))%> - </div> - <div class="clearfix"></div> - </td> - </tr> - <tr> - <td class="first"><%= _('Themes') %></td> - <td> - <div class="guindance_by_theme"> - <div class="div_left_icon_g"> - <%= f.collection_select(:theme_ids, @themes, - :id, :title, {prompt: false, include_blank: 'None'}, {multiple: true})%> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('Select which theme(s) this guidance relates to.'))%> - </div> - <div class="clearfix"></div> - </div> - </div> - </td> - </tr> - <tr> - <td class="first"><%= _('Published') %></td> - <td><div class="div_left_icon_g"> - <%= f.check_box :published , as: :check_boxes%> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _("Check this box when you are ready for this guidance to appear on user's plans."))%> - </div> - </td> - </tr> - <tr> - <td class="first"><%= _('Guidance group') %></td> - <td><div class="div_left_icon_g"> - <%= f.collection_select(:guidance_group_id, @guidance_groups, - :id, :name, {prompt: false, include_blank: 'None'}, {multiple: false})%> - </div> - <div class="div_right_icon_g"> - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('Select which group this guidance relates to.'))%> - </div> - <div class="clearfix"></div> - - </td> - </tr> - </table> - - <!-- submit buttons --> - - <div class="move_2_right"> - <a id="new_guidance_submit" data-toggle="modal" href="#new_guidance_alert_dialog" class="btn btn-primary"><%= _('Save')%></a> - - <%= link_to _('Cancel'), :back, class: 'btn cancel' %> - </div> - - <br /> - <%= tinymce :content_css => asset_path('application.css') %> - <%end%> - </div> -</div> - - -<!-- alert for guidance--> -<div id="new_guidance_alert_dialog" data-container="body" data-backdrop="static" class="modal hide fade"> - <div class="modal-header"> - <h3><%= _('Before submitting, please consider:') %></h3> - </div> - <div class="modal-body"> - <ul id="missing_fields_new_guidance"></ul> - </div> - <div class="modal-footer"> - <a href="#" id="return_to_new_guidance" class="btn"><%= _('Ok') %></a> - </div> -</div> \ No newline at end of file diff --git a/app/views/guidances/admin_show.html.erb b/app/views/guidances/admin_show.html.erb deleted file mode 100644 index 4742641..0000000 --- a/app/views/guidances/admin_show.html.erb +++ /dev/null @@ -1,63 +0,0 @@ -<%= stylesheet_link_tag "admin" %> - -<h1> - <%= _('Guidance') %> - - <!-- link button to add new guidance --> - <div class="move_2_right"> - <%= link_to _('Add guidance'), - admin_new_guidance_path, - class: "btn btn-primary" %> - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: "btn btn-primary" %> - </div> -</h1> - -<div class="div_clear"></div> - -<div class="white_background blue_border"> - <!-- body --> - <div class="dmp_details"> - - <table class="dmp_details_table"> - <tr> - <td class="first"><%= _('Text') %></td> - <td><%= raw @guidance.text %></td> - </tr> - <tr> - <td class="first"><%= _('Themes') %></td> - <td><%= @guidance.themes.order(:title).collect{|t| t.title}.join(', ') %></td> - </tr> - <tr> - <td class="first"><%= _('Guidance group') %></td> - <td> - <%= @guidance.guidance_group.name %> - </td> - </tr> - <tr> - <td class="first"><%= _('Published') %></td> - <td><%if @guidance.published == false || @guidance.published.nil? then%> - <%= _('No')%> - <% else %> - <%= _('Yes')%> - <% end %> - </td> - </tr> - <tr> - <td class="first"><%= _('Created') %></td> - <td><%= l @guidance.created_at.to_date, formats: :short %></td> - </tr> - <tr> - <td class="first"><%= _('Last updated') %></td> - <td><%= l @guidance.updated_at.to_date, formats: :short %></td> - </tr> - - </table> - <div class="move_2_right"> - <%= link_to _('Edit'), admin_edit_guidance_path(@guidance.id), class: "btn btn-primary"%> - <%= link_to _('Back'), :back, class: "btn cancel" %> - </div> - <br /> - </div> -</div> \ No newline at end of file diff --git a/app/views/guidances/new_edit.html.erb b/app/views/guidances/new_edit.html.erb new file mode 100644 index 0000000..1da2a29 --- /dev/null +++ b/app/views/guidances/new_edit.html.erb @@ -0,0 +1,31 @@ +<%# locals: { guidance, themes, guidance_groups, options } %> +<div class="row"> + <div class="col-xs-12"> + <h1><%= _('Guidance') %></h1> + <%= link_to _('View all guidance'), admin_index_guidance_path(current_user.org_id), class: 'btn btn-default pull-right' %> + </div> +</div> +<div class="row"> + <div class="col-xs-12"> + <%= form_for(guidance, url: options[:url], html: { method: options[:method] , id: 'new_edit_guidance'}) do |f| %> + <div class="form-group" data-toggle="tooltip" title="<%= _('Enter your guidance here. You can include links where needed.') %>"> + <%= f.label :text, class: 'control-label' %> + <%= text_area_tag("guidance-text", guidance.text, class: "tinymce form-control", 'aria-required': true, rows: 10) %> + </div> + <%= render partial: 'org_admin/shared/theme_selector', + locals: { f: f, all_themes: themes, + popover_message: _('Select one or more themes that are relevant to this guidance. This will display your generic organisation-level guidance, as well as that from other sources e.g. the %{org_name} guidance or any Schools/Departments that you provide guidance for.') % { org_name: (current_user.org.abbreviation.present? ? current_user.org.abbreviation : current_user.org.name ) } } %> + <div class="form-group"> + <%= f.label _('Guidance group'), for: :guidance_group_id, class: 'control-label' %> + <%= f.collection_select(:guidance_group_id, guidance_groups, + :id, :name, {prompt: false}, {multiple: false, 'data-toggle': 'tooltip', title: _('Select which group this guidance relates to.'), class: 'form-control', 'aria-required': true})%> + </div> + <div class="checkbox"> + <%= f.label :published, raw("#{f.check_box :published, as: :check_boxes, 'data-toggle': 'tooltip', title: _("Check this box when you are ready for this guidance to appear on user's plans.")} #{_('Published?')}") %> + </div> + + <%= f.submit _('Save'), name: "edit_guidance_submit", class: "btn btn-primary" %> + <%= link_to _('Cancel'), admin_index_guidance_path, class: "btn btn-primary", role: 'button' %> + <%end%> + </div> +</div> \ No newline at end of file diff --git a/app/views/home/_welcome.html.erb b/app/views/home/_welcome.html.erb new file mode 100644 index 0000000..6348ccd --- /dev/null +++ b/app/views/home/_welcome.html.erb @@ -0,0 +1,12 @@ +<div> + <h1><%= _('Welcome.')%></h1> + <p> + <%= raw _('<p>%{application_name} has been developed by the <strong>%{organisation_name}</strong> to help you write data management plans.</p>') % {:application_name => Rails.configuration.branding[:application][:name], :organisation_name => Rails.configuration.branding[:organisation][:name]} %> + <label for="linklist"><%=_('Getting started:')%></label> + <ul id="linklist"> + <% Rails.application.config.branding[:organisation][:welcome_links].each do |wlink| %> + <li><a href="<%= wlink[:url] %>"><%= wlink[:title] %></a></li> + <% end %> + </ul> + </p> +</div> diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 92bfeb2..b8cda22 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -1,56 +1,9 @@ -<div class="content"> - <div class="home_left_column"> - <div class="welcome-message"> - - <h2><%= _('Welcome.')%></h2> - <%= raw _('<p>%{application_name} has been jointly developed by the <strong>%{organisation_name}</strong> to help you write data management plans.</p>') % {:application_name => Rails.configuration.branding[:application][:name], :organisation_name => Rails.configuration.branding[:organisation][:name]} %> - <div class="screencast_home_page"> - <p><%= _('Screencast on how to use %{application_name}') % {:application_name => Rails.configuration.branding[:application][:name]} %></p> - <div class="clear"></div> - <video controls="controls" poster="<%= asset_path('screencast.jpg')%>" style="width:100%; height:200" title="1662"> - <source src="<%= asset_path('index.files/html5video/screencast.mp4')%>" type="video/mp4" /> - <source src="<%= asset_path('index.files/html5video/screencast.webm')%>" type="video/webm" /> - <source src="<%= asset_path('index.files/html5video/screencast.ogv')%>" type="video/ogg" /> - <object type="application/x-shockwave-flash" data="<%= asset_path('index.files/html5video/flashfox.swf')%>" width="100%" height="200" style="position:relative;"> - <param name="movie" value="<%= asset_path('index.files/html5video/flashfox.swf')%>" /> - <param name="allowFullScreen" value="true" /> - <param name="flashVars" value="autoplay=true&controls=true&fullScreenEnabled=true&posterOnEnd=true&loop=false&poster=<%= asset_path('screencast.jpg')%>&src=<%= asset_path('index.files/html5video/screencast.mp4')%>" /> - <embed src="<%= asset_path('index.files/html5video/flashfox.swf')%>" width="100%" height="200" style="position:relative;" flashVars="autoplay=true&controls=true&fullScreenEnabled=true&posterOnEnd=true&loop=false&poster=<%= asset_path('screencast.jpg')%>&src=<%= asset_path('index.files/html5video/screencast.mp4')%>" allowFullScreen="true" wmode="transparent" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer_en" /> - <img alt="screencast" src="<%= asset_path('screencast.jpg')%>" style="position:absolute;left:0;" width="100%" title="<%= _('Your browser does not support the video tag.')%>" /> - </object> - </video> - </div> - </div> - </div> - <div class="home_right_column"> - <div class="accordion" id="homepage-accordion"> - <div class="accordion-group"> - <div class="accordion-heading"> - <a class="accordion-toggle" data-toggle="collapse" data-parent="#homepage-accordion" href="#collapse-login"> - <%= _('Sign in')%><span class="minus-laranja"> </span></a> - </div> - <div id="collapse-login" class="accordion-home collapse in"> - <div class="accordion-inner"> - <%= render :partial => 'shared/login_form' %> - </div> - </div> - </div> - <div class="accordion-group"> - <div class="accordion-heading"> - <a class="accordion-toggle" data-toggle="collapse" data-parent="#homepage-accordion" href="#collapse-register"> - <%= _('Create account') %><span class="plus-laranja"> </span></a> - <p> - <%= _('New to %{application_name}? Create an account today.') % {:application_name => Rails.configuration.branding[:application][:name]} %> - </p> - - </div> - <div id="collapse-register" class="accordion-home collapse"> - <div class="accordion-inner"> - <%= render :partial => 'shared/register_form', locals: {extended: false} %> - </div> - </div> - </div> - </div> - </div> - <div class="two-column-clear"></div> -</div> \ No newline at end of file +<div class="row"> + <div class="col-md-8"> + <%= render partial: 'home/welcome' %> + </div> + + <div class="col-md-4"> + <%= render partial: 'shared/access_controls' %> + </div> +</div> diff --git a/app/views/kaminari/_first_page.html.erb b/app/views/kaminari/_first_page.html.erb new file mode 100644 index 0000000..958224e --- /dev/null +++ b/app/views/kaminari/_first_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "First" page + - available local variables + url: url to the first page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<span class="first"> + <%= link_to_unless current_page.first?, _('First'), url, remote: remote %> +</span> diff --git a/app/views/kaminari/_gap.html.erb b/app/views/kaminari/_gap.html.erb new file mode 100644 index 0000000..34b90be --- /dev/null +++ b/app/views/kaminari/_gap.html.erb @@ -0,0 +1,8 @@ +<%# Non-link tag that stands for skipped pages... + - available local variables + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<span class="page gap"><%= _('...') %></span> diff --git a/app/views/kaminari/_last_page.html.erb b/app/views/kaminari/_last_page.html.erb new file mode 100644 index 0000000..a37f875 --- /dev/null +++ b/app/views/kaminari/_last_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "Last" page + - available local variables + url: url to the last page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<span class="last"> + <%= link_to_unless current_page.last?, _('Last'), url, remote: remote %> +</span> diff --git a/app/views/kaminari/_next_page.html.erb b/app/views/kaminari/_next_page.html.erb new file mode 100644 index 0000000..6f1fdbb --- /dev/null +++ b/app/views/kaminari/_next_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "Next" page + - available local variables + url: url to the next page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<span class="next"> + <%= link_to_unless current_page.last?, _('Next'), url, rel: 'next', remote: remote %> +</span> diff --git a/app/views/kaminari/_page.html.erb b/app/views/kaminari/_page.html.erb new file mode 100644 index 0000000..393bfc4 --- /dev/null +++ b/app/views/kaminari/_page.html.erb @@ -0,0 +1,12 @@ +<%# Link showing page number + - available local variables + page: a page object for "this" page + url: url to this page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<span class="page<%= ' current' if page.current? %>"> + <%= link_to_unless page.current?, page, url, {remote: remote, rel: page.rel} %> +</span> diff --git a/app/views/kaminari/_paginator.html.erb b/app/views/kaminari/_paginator.html.erb new file mode 100644 index 0000000..fb46ee2 --- /dev/null +++ b/app/views/kaminari/_paginator.html.erb @@ -0,0 +1,25 @@ +<%# The container tag + - available local variables + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote + paginator: the paginator that renders the pagination tags inside +-%> +<%= paginator.render do -%> + <nav class="pagination" style="margin: 0;"> + <%= first_page_tag unless current_page.first? %> + <%= prev_page_tag unless current_page.first? %> + <% each_page do |page| -%> + <% if page.display_tag? -%> + <%= page_tag page %> + <% elsif !page.was_truncated? -%> + <%= gap_tag %> + <% end -%> + <% end -%> + <% unless current_page.out_of_range? %> + <%= next_page_tag unless current_page.last? %> + <%= last_page_tag unless current_page.last? %> + <% end %> + </nav> +<% end -%> diff --git a/app/views/kaminari/_prev_page.html.erb b/app/views/kaminari/_prev_page.html.erb new file mode 100644 index 0000000..28602af --- /dev/null +++ b/app/views/kaminari/_prev_page.html.erb @@ -0,0 +1,11 @@ +<%# Link to the "Previous" page + - available local variables + url: url to the previous page + current_page: a page object for the currently displayed page + total_pages: total number of pages + per_page: number of items to fetch per page + remote: data-remote +-%> +<span class="prev"> + <%= link_to_unless current_page.first?, _('Previous'), url, rel: 'prev', remote: remote %> +</span> diff --git a/app/views/layouts/_branding.html.erb b/app/views/layouts/_branding.html.erb index 4774bee..7d89c63 100644 --- a/app/views/layouts/_branding.html.erb +++ b/app/views/layouts/_branding.html.erb @@ -1,23 +1,110 @@ -<!-- Org branding info --> -<div class="branding_container"> - <% if user_signed_in? %> - <% if !current_user.org.nil? %> - <!-- Organisation Logo --> - <% if current_user.org.logo.present? %> - <div class="header_org_logo"> - <%= link_to(image_tag(current_user.org.logo.thumb('100x100%').url), current_user.org.target_url) %> - </div> +<nav id="org-navbar" class="navbar navbar-default" role="navigation"> + <div class="container-fluid"> + <!-- Brand and toggle get grouped for better mobile display --> + <div class="navbar-header" > + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#org-navbar-menu" aria-expanded="false"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <% if user_signed_in? && !current_user.org.nil? %> + <% if current_user.org.logo.present? %> + <%= link_to(image_tag(current_user.org.logo.thumb('100x100%').url, + alt: current_user.org.name, + class: "org-logo", + title: current_user.org.name), + current_user.org.target_url) %> + <% else %> + <h4 id="banner-org-name"><%= current_user.org.name %></h4> + <% end %> + <% end %> + </div> + + <!-- Collect the nav links, forms, and other content for toggling --> + <div class="collapse navbar-collapse" id="org-navbar-menu"> + <!-- Organisational links --> + <% if user_signed_in? and !current_user.org.links.nil? and !current_user.org.links['org'].blank? %> + <ul class="nav navbar-nav"> + <% i=1; current_user.org.links['org'].each do |link| %> + <% if !link.blank? && i <= max_number_links %> + <% if i.odd? %> + </li><li class="org-links"> + <% end %> + <%= link_to link['link'], :target=>'_blank', :class => 'org-a' do %> + <i class="fa fa-globe" aria-hidden="true"> </i> + <%= link['text'].blank? ? link['link'] : link['text'] %> + <% end; i+=1 %> + <% end %> + <% end %> + <% if i.odd? %> + </li><li class="org-links"> + <% end %> + <% if !current_user.org.contact_email.blank? %> + <%= mail_to current_user.org.contact_email, :class => 'org-a' do %> + <i class="fa fa-envelope" aria-hidden="true"> </i> + <%= current_user.org.contact_name ? current_user.org.contact_name : current_user.org.contact_email %> + <% end %> + <% end %> + </ul> <% end %> - <!-- Banner text --> - <% if current_user.org.banner_text.present? %> - <div class="header_org_banner_text"> - <span class="header_org_banner_text_vertical_align"> - <%= raw current_user.org.banner_text %> - </span> - </div> - <%end%> - - <%end%> - <%end%> -</div> \ No newline at end of file + <!-- Navigation for organisation admin --> + <ul class="nav navbar-nav navbar-right"> + <!-- if org admin --> + <% if current_user.can_org_admin? %> + <li class="dropdown navbar-default" id="admin-dropdown"> + <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" id="admin-menu" href="#"> + <i class="fa fa-lock" aria-hidden="true"> </i> + <%= _('Admin') %> + <span class="caret"></span> + </a> + <ul class="dropdown-menu" aria-labelledby="admin-menu"> + <% if current_user.can_org_admin? %> + <li <%= 'class=active' if isActivePage(org_admin_plans_path) %>> + <%= link_to _('Plans'), org_admin_plans_path %> + </li> + <% end %> + <% if current_user.can_modify_templates? %> + <li <%= 'class=active' if isActivePage(org_admin_templates_path) %>> + <%= link_to _('Templates'), org_admin_templates_path %> + </li> + <% end %> + <% if current_user.can_modify_guidance? %> + <li <%= 'class=active' if isActivePage(admin_index_guidance_path(current_user.org_id)) %>> + <%= link_to _('Guidance'), admin_index_guidance_path(current_user.org_id) %> + </li> + <% end %> + <% if current_user.can_super_admin? %> + <li <%= 'class=active' if isActivePage(admin_edit_org_path(current_user.org_id)) %>> + <%= link_to _('Organisations'), super_admin_orgs_path %> + </li> + <% else %> + <% if current_user.can_modify_org_details? %> + <li <%= 'class=active' if isActivePage(admin_edit_org_path(current_user.org_id)) %>> + <%= link_to _('Organisation details'), admin_edit_org_path(current_user.org_id) %> + </li> + <% end %> + <% end %> + <% if current_user.can_grant_permissions? %> + <li <%= 'class=active' if isActivePage(admin_index_users_path) %>> + <%= link_to _('Users'), admin_index_users_path, class: 'main_nav_last_li' %> + </li> + <% end %> + <% if current_user.can_super_admin? %> + <li <%= 'class=active' if isActivePage(super_admin_themes_path) %>> + <%= link_to(_('Themes'), super_admin_themes_path) %> + </li> + <% end %> + <% if current_user.can_org_admin? || current_user.can_super_admin? %> + <li <%= 'class=active' if isActivePage(usage_index_path) %>> + <%= link_to(_('Usage'), usage_index_path) %> + </li> + <% end %> + </ul> + </li> + <% end %> <!-- if org admin --> + </ul> + </div><!-- /.navbar-collapse --> + </div><!-- /.container-fluid --> +</nav> diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb index 4866e06..862d5c5 100644 --- a/app/views/layouts/_footer.html.erb +++ b/app/views/layouts/_footer.html.erb @@ -1,43 +1,31 @@ +<% + # Override the default Rails route helper for the contact_us page IF an alternate contact_us url was defined + # in the branding config file + contact_us = Rails.application.config.branding[:organisation][:contact_us_url] || contact_us_path +%> + <!-- footer container --> -<footer class="footer"> - <div class="left_side_footer"> - <div class="first_row"> - <ul class="footer-links"> - <li><a href="<%= contact_us_path %>"><%= _('Contact us') %></a></li> - <li class="muted">|</li> - <li><a href="<%= terms_path %>"><%= _('Terms of use') %></a></li> - </ul> - </div> - <div class="second_row"> - <p id="dcc_link">© 2004 - <%= Time.now.year %><%= link_to " #{Rails.configuration.branding[:organisation][:copywrite_name]}", "#{Rails.configuration.branding[:organisation][:url]}"%> - </p> - </div> - </div> - <div class="right_side_footer"> - <p><%= link_to( image_tag("dcc_logo.png"), "http://www.dcc.ac.uk/", title: "Digital Curation Centre", ) %> - <%= link_to( image_tag("uc3_logo.jpg", class: "footer_logo"), "http://www.cdlib.org/uc3/", title: "California Digital Library", id: "footer_right_uc3", ) %></p> - </div> - -</footer> - -<% if !user_signed_in? then %> - <div id="header-login-form" class="modal hide fade"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> - <h3><%= _('Sign in')%></h3> - </div> - <div class="modal-body"> - <%= render :partial => "shared/login_form" %> - </div> - </div> - <div id="header-register-form" class="modal hide fade"> - <div class="modal-header"> - <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> - <h3><%= _('Create account')%></h3> - </div> - <div class="modal-body"> - <%= render :partial => "shared/register_form", locals: {extended: false} %> - </div> - </div> - -<% end %> +<div id="footer-navbar" class="navbar-inverse navbar-fixed-bottom" role="navigation"> + <div class="container-fluid"> + <ul class="nav navbar-nav"> + <li><%= link_to "© 2004 - #{Time.now.year} #{Rails.configuration.branding[:organisation][:name]}".sanitize, "#{Rails.configuration.branding[:organisation][:url]}"%></li> + <li>•</li> + <li><a href="<%= about_us_path %>"><%= _('About') %></a></li> + <li><a href="<%= contact_us %>"><%= _('Contact us') %></a></li> + <li><a href="<%= terms_path %>"><%= _('Terms of use') %></a></li> + <li><a href="#"><%= _('Privacy policy') %></a></li> + <li><a href="#"><%= _('Accessibility') %></a></li> + <li><a href="https://github.com/DMPRoadmap/roadmap"><%= _('Github') %></a></li> + </ul> + <ul class="nav navbar-nav navbar-right"> + <li> + <%= link_to( image_tag("uc3_logo_white.png", class: "img-responsive", alt: 'University of California Curation Center'), + "http://www.cdlib.org/uc3/", title: "University of California Curation Center", target: '_blank') %> + </li> + <li> + <%= link_to( image_tag("dcc_logo_white.png", class: "img-responsive", alt: 'Digital Curation Centre'), + "http://www.dcc.ac.uk/", title: "Digital Curation Centre", target: '_blank') %> + </li> + </ul> + </div> +</div> diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 09bc946..d5f5b8a 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -1,23 +1,9 @@ -<!-- header information --> - -<div class="main_header"> - <div class="header_left"> - <div class="home_logo"> - <%= link_to(image_tag("logo.jpg"), root_path)%> - </div> - </div> - <div class="header_right"> - <div class="org_branding"> - <%= render "layouts/branding" %> - </div> - - <div class="signin_signout_header_link"> - <%= render "layouts/signin_signout" %> - </div> - - <div class="main_navigation"> - <%= render "layouts/navigation" %> - </div> - - </div> -</div> +<!-- header information (bootstrap 3 column layout) --> +<div class="main-nav"> + <%= render partial: "layouts/navigation" %> +</div> +<div class="org-branding"> + <% if user_signed_in? && !current_user.org.nil? && !current_user.org.is_other %> + <%= render partial: "layouts/branding" , locals: {max_number_links: MAX_NUMBER_LINKS_FUNDER,} %> + <% end %> +</div> diff --git a/app/views/layouts/_navigation.html.erb b/app/views/layouts/_navigation.html.erb index 57bfe0b..3438aae 100644 --- a/app/views/layouts/_navigation.html.erb +++ b/app/views/layouts/_navigation.html.erb @@ -1,69 +1,70 @@ -<div id="main-nav-tabs" class="main_nav_tabs"> - <ul class="nav nav-tabs" data-tabs="tabs"> - <!-- Navigation for organisation admin --> - <% if (user_signed_in? && current_user.can_org_admin? && ( action_name.include? "admin_" ) ) %> - <% if current_user.can_modify_templates? %> - <li <%= 'class=active' if isActivePage(admin_index_template_path(current_user.org_id)) %>> - <%= link_to _('Templates'), admin_index_template_path(current_user.org_id) %> - </li> - <% end %> - <% if current_user.can_modify_guidance? %> - <li <%= 'class=active' if isActivePage(admin_index_guidance_path(current_user.org_id)) %>> - <%= link_to _('Guidance'), admin_index_guidance_path(current_user.org_id) %> - </li> - <% end %> - <% if current_user.can_modify_org_details? %> - <li <%= 'class=active' if isActivePage(admin_show_org_path(current_user.org_id)) %>> - <%= link_to _('Organisation details'), admin_show_org_path(current_user.org_id) %> - </li> - <% end %> - <% if current_user.can_grant_permissions? %> - <li <%= 'class=active' if isActivePage(admin_index_users_path) %>> - <%= link_to _('Users'), admin_index_users_path, class: 'main_nav_last_li' %> - </li> - <% end %> - <% else %> - <% if user_signed_in? %> - <li <%= 'class=active' if isActivePage(plans_path) %>> - <%= link_to _('View plans'), plans_path %> +<nav id="app-navbar" class="navbar navbar-inverse"> + <div class="container-fluid" > + <!-- Brand and toggle get grouped for better mobile display --> + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-menu" aria-expanded="false"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <%= link_to(image_tag("logo.png", + alt: "#{Rails.configuration.branding[:application][:name]} #{_('logo')}", + class: "app-logo", + title: Rails.configuration.branding[:application][:name]), + root_path) %> + </div> + + <!-- Collect the nav links, forms, and other content for toggling --> + <div class="collapse navbar-collapse" id="app-navbar-menu"> + <ul class="nav navbar-nav"> + <% if user_signed_in? %> + <li class="<%= ' active' if isActivePage(root_path) %>"> + <%= link_to _('My Dashboard'), plans_path %> </li> <li <%= 'class=active' if isActivePage(new_plan_path) %>> - <%= link_to _('Create plan'), new_plan_path %> + <%= link_to _('Create plans'), new_plan_path %> </li> - <% else %> - <!-- Home page --> - <li <%= 'class=active' if isActivePage(root_path) %>> + <li class="dropdown" id="reference-dropdown"> + <a href="#" class="dropdown-toggle" role="button" id="reference-menu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <i class="fa fa-book" aria-hidden="true"> </i> + <%= _("Reference") %> + <span class="caret"></span> + </a> + <ul class="dropdown-menu inverse-dropdown" aria-labelledby="reference-menu"> + <li <%= 'class=active' if isActivePage(public_plans_path) %>> + <%= link_to _('Public DMPs'), public_plans_path %> + </li> + <li <%= 'class=active' if isActivePage(public_templates_path) %>> + <%= link_to _('DMP Templates'), public_templates_path %> + </ul> + </a> + </li> + <% else %> + <li <%= 'class=active' if isActivePage(root_path, true) || isActivePage(plans_path, true) %>> <%= link_to _('Home'), root_path %> </li> - <% end %> - <!-- about_us page --> - <li <%= 'class=active' if isActivePage(about_us_path) %>> - <%= link_to _('About'), about_us_path %> - </li> - <!-- roadmap page --> - <li <%= 'class=active' if isActivePage(roadmap_path) %>> - <%= link_to _('Future plans'), roadmap_path %> - </li> - <!-- help page --> - <li <%= 'class=active' if isActivePage(help_path) %>> + <li <%= 'class=active' if isActivePage(public_plans_path, true) %>> + <%= link_to _('Public DMPs'), public_plans_path %> + </li> + <li <%= 'class=active' if isActivePage(public_templates_path, true) %>> + <%= link_to _('DMP Templates'), public_templates_path %> + </li> + <% end %> + <!-- help page --> + <li <%= 'class=active' if isActivePage(help_path, true) %>> <%= link_to _('Help'), help_path %> </li> - <% if MANY_LANGUAGES %> - <!-- language dropdown --> - <li class="dropdown sign_in"> - <a class="dropdown-toggle main_nav" data-toggle="dropdown" href="#"> - <%= _('Change language') %> - <b class="caret caret-orange"></b> - </a> - <ul class="dropdown-menu" style="padding: 0;"> - <% LANGUAGES.each do |l| %> - <li <%= 'class=active' if FastGettext.locale == l.abbreviation %>> - <%= link_to l.name, locale_path(l.abbreviation), method: :patch, class: 'main_nav lang-dropdown-link'%> - </li> - <% end %> - </ul> - </li> + </ul> + <ul class="nav navbar-nav navbar-right"> + <%= render "layouts/signin_signout" %> + </ul> + + <% unless isActivePage(root_path, true) %> + <%= render partial: 'shared/access_controls', layout: 'shared/modal', locals: { id: "header-signin", title: _('Sign in')} %> <% end %> - <% end %> - </ul> -</div> + </div><!-- /.navbar-collapse --> + </div><!-- /.container-fluid --> +</nav> + + diff --git a/app/views/layouts/_paginable.html.erb b/app/views/layouts/_paginable.html.erb new file mode 100644 index 0000000..d50573b --- /dev/null +++ b/app/views/layouts/_paginable.html.erb @@ -0,0 +1,61 @@ +<% + # Custom layout to be included on any view that needs pagination + # locals: { controller, action, paginable, scope } +%> +<% total = paginable ? scope.total_count : scope.length %> +<div class="paginable"> + <div class="paginable-search"> + <div class="row"> + <div class="col-md-12"> + <%= render(partial: '/shared/search', locals: { controller: controller, action: action }) if searchable? || total > Kaminari.config.default_per_page %> + </div> + </div> + </div> + <div class="paginable-results"> + <div class="row"> + <div class="col-md-12"> + <% if scope.length > 0 %> + <%= yield %> + <% else %> + <p class="bg-info"> + <%= _('There are no records associated') %> + </p> + <% end %> + </div> + </div> + <div class="row"> + <div class="col-md-12"> + <div class="pull-left"> + <% if total > Kaminari.config.default_per_page %> + <% if searchable? %> + <ul class="list-inline"> + <% if paginable %> + <li><%= paginable_search_link(_('View all search results'), 'ALL') %></li> + <% else %> + <%= paginable_search_link(_('View less search results'), 1) %> + <% end %> + <li><%= link_to(_('Clear search results'), paginable_base_url(1), { 'data-remote': true, class: 'clear' }) %></li> + </ul> + <% else %> + <% if paginable %> + <%= link_to(_('View all'), paginable_base_url('ALL'), { 'data-remote': true }) %> + <% else %> + <%= link_to(_('View less'), paginable_base_url(1), { 'data-remote': true }) %> + <% end %> + <% end %> + <% else %> + <% if searchable? %> + <%= link_to(_('Clear search results'), paginable_base_url(1), { 'data-remote': true, class: 'clear' }) %> + <% end %> + <% end %> + </div> + <div class="pull-right"> + <% if paginable %> + <%= paginate(scope, params: { controller: controller, action: action }, remote: true) %> + <% end %> + </div> + <div class="clearfix"></div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/app/views/layouts/_signin_signout.html.erb b/app/views/layouts/_signin_signout.html.erb index ec99f37..6bacdc2 100644 --- a/app/views/layouts/_signin_signout.html.erb +++ b/app/views/layouts/_signin_signout.html.erb @@ -1,45 +1,45 @@ +<!-- language dropdown --> +<% if MANY_LANGUAGES %> + <li class="dropdown" id="change-language"> + <a href="#" class="dropdown-toggle" role="button" id="language-menu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <i class="fa fa-language" aria-hidden="true"> </i> + <%= _('Language') %> + <span class="caret"></span> + </a> + <ul class="dropdown-menu inverse-dropdown" aria-labelledby="language-menu"> + <% LANGUAGES.each do |l| %> + <li <%= 'class=active' if FastGettext.locale == l.abbreviation %>> + <%= link_to l.name, locale_path(l.abbreviation), method: :patch %> + </li> + <% end %> + </ul> + </li> +<% end %> + <!-- Sign in / out --> -<div class="signIn"> - <ul class="navbar"> - <% if user_signed_in? %> - <li class="dropdown sign_in"> - <a class="dropdown-toggle" data-toggle="dropdown" href="#"><%= _('Signed in as ')%> - <%= current_user.name %> <b class="caret"></b> - </a> - <ul class="dropdown-menu" id="signIn_dropdown"> - <li><%= link_to _('Edit profile'), edit_user_registration_path, class: "signIn_dropdown_link" %></li> - <% if current_user.can_super_admin? %> - <li><%= link_to _('Super admin area'), "/admin", class: "signIn_dropdown_link" %></li> - <% end %> - <% if current_user.can_org_admin? && !current_user.org_id.nil? %> - <% if current_user.can_modify_org_details? && current_user.org.abbreviation.blank? %> - <li><%= link_to _("Admin area"), admin_show_org_path(current_user.org_id), class: "signIn_dropdown_link" %></li> - <% elsif current_user.can_modify_templates?%> - <li><%= link_to _("Admin area"), admin_index_template_path(current_user.org_id), class: "signIn_dropdown_link" %></li> - <% elsif current_user.can_modify_guidance? %> - <li><%= link_to _("Admin area"), admin_index_guidance_path(current_user.org_id), class: "signIn_dropdown_link" %></li> - <% elsif current_user.can_modify_org_details? %> - <li><%= link_to _("Admin area"), admin_show_org_path(current_user.org_id), class: "signIn_dropdown_link" %></li> - <% elsif current_user.can_grant_permissions? %> - <li><%= link_to _("Admin area"), admin_index_users_path, class: "signIn_dropdown_link" %></li> - <% end %> - <% end %> - <li><%= link_to _('Sign out'), destroy_user_session_path, method: :delete, class: "signIn_dropdown_link" %></li> - </ul> - </li> - <% else %> +<% if user_signed_in? %> + <li class="dropdown" id="signin-signout"> + <a href="#" class="dropdown-toggle" role="button" id="user-menu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <i class="fa fa-user" aria-hidden="true"> </i> + <%= current_user.name(false) %> + <span class="caret"></span> + </a> + <ul class="dropdown-menu inverse-dropdown" aria-labelledby="user-menu"> <li> - <% unless controller_name == "home" && action_name == "index" then %> - <a href="#header-login-form" data-toggle="modal"><%= _('Sign in')%></a> / - <a href="#header-register-form" data-toggle="modal"><%= _('Create account')%></a> - <% else %> - <%= raw " " %> - <% end %> + <%= link_to '<i class="fa fa-pencil-square-o" aria-hidden="true"> </i> '.html_safe + _('Edit profile'), edit_user_registration_path %> </li> - <% end %> - </ul> -</div> -<!-- Quick'n'dirty touchscreen hack for Bootstrap's buggy dropdown --> - <style> - .dropdown-backdrop{ position: static; } - </style> + <li> + <%= link_to '<i class="fa fa-sign-in" aria-hidden="true"> </i> '.html_safe + _('Logout'), destroy_user_session_path, method: :delete %> + </li> + </ul> + </li> +<% else %> + <% if !isActivePage(root_path, true) %> + <li> + <a href="#header-signin" data-toggle="modal" data-target="#header-signin"> + <i class="fa fa-sign-in" aria-hidden="true"> </i> + <%= _('Sign in') %> + </a> + </li> + <% end %> +<% end %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 483511b..801fba4 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,84 +1,110 @@ <!DOCTYPE html> -<html class="dmponline" lang="<%= FastGettext.locale %>"> - <head> - <title><%= _('%{application_name}') % { :application_name => Rails.configuration.branding[:application][:name] } %> + + + + <%= _('%{application_name}') % { :application_name => Rails.configuration.branding[:application][:name] } %> + <%= favicon_link_tag "favicon.ico" %> - - - - - <%= stylesheet_link_tag "application" %> - <%= javascript_include_tag "application"%> - - <%= javascript_include_tag "jquery.tablesorter"%> - + - + - <%= yield(:head) %> - <%= csrf_meta_tags %> + + <%= stylesheet_link_tag fingerprinted_asset('application') %> + <%= javascript_include_tag fingerprinted_asset('vendor') %> + <%= javascript_include_tag fingerprinted_asset('application') %> + <%= csrf_meta_tags %> - - - - - - - + + + + - - <%= render "layouts/header" %> - - + +
+ <%= render partial: "layouts/header" %> +
-
- <% if notice %> -

<%= raw notice %>

- <% end %> - <% if alert %> -

<%= raw alert %>

- <% end %> - -
- <%= yield %> -
-
- - - <%= render "layouts/footer" %> - - + <% + has_alert = (alert || flash[:alert] || flash[:error]) + has_notice = (notice || flash[:notice]) + %> + + +
+
+ + <%= has_alert ? _('Error:') : _('Notice:') %> + <%= raw (has_alert ? alert : notice) %> +
+ <%= yield %> +
+ + +
+ <%= render "layouts/footer" %> +
+ + <% + constants_json = { + PASSWORD_MIN_LENGTH: 8, + PASSWORD_MAX_LENGTH: 128, + MAX_NUMBER_ORG_URLS: 3, + MAX_NUMBER_GUIDANCE_SELECTIONS: 6, + + VALIDATION_MESSAGE_DEFAULT: _('Please enter a valid value.'), + VALIDATION_MESSAGE_EMAIL: _('You must enter a valid email address.'), + VALIDATION_MESSAGE_URL: _('You must enter a valid URL (e.g. https://organisation.org).'), + VALIDATION_MESSAGE_NUMBER: _('Please enter a valid number.'), + VALIDATION_MESSAGE_PASSWORD: _('The password must be between 8 and 128 characters.'), + VALIDATION_MESSAGE_PASSWORDS_MATCH: _('The passwords must match.'), + VALIDATION_MESSAGE_RADIO: _('Please choose one of the options.'), + VALIDATION_MESSAGE_CHECKBOX: _('Please check the box to continue.'), + VALIDATION_MESSAGE_SELECT: _('Please select a value from the list.'), + VALIDATION_MESSAGE_TEXT: _('This field is required.'), + + SHOW_PASSWORD_MESSAGE: _('Show password'), + SHOW_SELECT_ORG_MESSAGE: _('Select an organisation from the list.'), + SHOW_OTHER_ORG_MESSAGE: _('My organisation isn\'t listed'), + + PLAN_VISIBILITY_WHEN_TEST: _('N/A'), + PLAN_VISIBILITY_WHEN_NOT_TEST: _('Private'), + PLAN_VISIBILITY_WHEN_NOT_TEST_TOOLTIP: _('Private: restricted to me and people I invite.'), + + SHIBBOLETH_DISCOVERY_SERVICE_HIDE_LIST: _('Hide list.'), + SHIBBOLETH_DISCOVERY_SERVICE_SHOW_LIST: _('See the full list of partner institutions.'), + + 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.') + }.to_json + %> + + + diff --git a/app/views/notes/_add.html.erb b/app/views/notes/_add.html.erb deleted file mode 100644 index e335daa..0000000 --- a/app/views/notes/_add.html.erb +++ /dev/null @@ -1,23 +0,0 @@ - - -<% - new_note = Note.new - questionid = question.id -%> - -<%= form_for( :new_note, - url: notes_path, - remote: true, - html: {method: :post, class: "add_note_form roadmap-form"}, - id: "new_note_form_#{questionid}") do |f| %> - <%= f.hidden_field :user_id, value: current_user.id %> - <%= f.hidden_field :question_id, value: questionid %> - <%= f.hidden_field :plan_id, value: plan_id %> - -
- <%= label_tag "#{questionid}new_note_text", _('Share note with collaborators') %> - <%= text_area_tag "#{questionid}new_note_text", nil, class: "tinymce" %> - <%= tinymce :content_css => asset_path('application.css') %> - -
-<% end %> diff --git a/app/views/notes/_archive.html.erb b/app/views/notes/_archive.html.erb index eae2154..2075a78 100644 --- a/app/views/notes/_archive.html.erb +++ b/app/views/notes/_archive.html.erb @@ -1,20 +1,14 @@ -<%= form_for(note, - url: archive_note_path(note), - remote: true, - class: "archive_note_form", - id: "archive_note_form_#{note.id}") do |f| %> - - <%= f.hidden_field :id, :value => note.id %> - <%= f.hidden_field :archived_by, :value => current_user.id %> - - <%= render :partial => "/notes/view", locals: {note: note} %> - -

<%= _('Are you sure you want to remove this note?')%>

- - -
- <%= f.submit _('Remove'), onclick: "archive_note(#{note.id}, #{question_id})", :class => "btn btn-primary archive_comment_submit_button" %> - <%= link_to _('Cancel'), "#", onclick: "cancel_archive_note(#{note.id})", :class => "cancel_archive_comment btn cancel" %> -
-
-<% end %> +<%# locals: { note } %> +<% if !note.nil? %> + <%= form_for(note, url: archive_note_path(note), method: :patch, html: { class: 'archive_note' }) do |f| %> + <%= f.hidden_field :archived_by, :value => current_user.id %> + + <%= render partial: "notes/show", locals: { note: note }, formats: [:html] %> + +

<%= _('Are you sure you want to remove this comment?')%>

+
+ <%= f.button(_('Remove'), class: "btn btn-default", type: "submit") %> + <%= f.button(_('Cancel'), class: "btn btn-default", type: "button") %> +
+ <% end %> +<% end %> \ No newline at end of file diff --git a/app/views/notes/_edit.html.erb b/app/views/notes/_edit.html.erb index 17a75bc..f62bce3 100644 --- a/app/views/notes/_edit.html.erb +++ b/app/views/notes/_edit.html.erb @@ -1,15 +1,13 @@ - - -<%= form_for(note, - url: note_path(note), - remote: true, - html: {method: :put, class: "edit_note_form roadmap-form"}, - id: "edit_note_form_#{note.id}") do |f| %> - -
- <%= f.hidden_field :id, :value => note.id %> - - <%= text_area_tag("#{note.id}_note_text".to_sym, note.text , class: "tinymce") %> - <%= f.submit _('Save'), :class => "form-submit edit_note_submit_button" %> -
-<%end%> +<%# locals: { note } %> +<% if !note.nil? %> + <%= form_for(note, url: note_path(note), method: :put) do |f| %> +
+ <%= f.label(:text, _('Edit comment to share with collaborators')) %> + <%= f.text_area(:text, class: 'form-control', id: "note-#{note.id}") %> +
+
+ <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %> + <%= f.button(_('Cancel'), class: "btn btn-default", type: "button") %> +
+ <% end %> +<% end %> \ No newline at end of file diff --git a/app/views/notes/_layout.html.erb b/app/views/notes/_layout.html.erb new file mode 100644 index 0000000..e303546 --- /dev/null +++ b/app/views/notes/_layout.html.erb @@ -0,0 +1,27 @@ +<%# locals: { plan, question, answer } %> +<% notes = answer.non_archived_notes %> +
+
+
+
+ <%= render partial: "/notes/list", locals: { question_id: question.id, notes: notes, plan: plan }, formats: [:html] %> +
+
+ <% if plan.commentable_by?(current_user) %> +
+
+
" data-question-id="<%= question.id %>"> + <%= render partial: "/notes/new", locals: { question: question, answer: answer, plan: plan }, formats: [:html] %> +
+
+
+
+
+
+ <%= link_to(_('Add Comment'), "#note_new#{question.id}", class: "btn btn-default note_new_link", role: "button", style: "visibility: hidden") %> +
+
+
+ <% end %> +
+
\ No newline at end of file diff --git a/app/views/notes/_list.html.erb b/app/views/notes/_list.html.erb index daf6a20..1650112 100644 --- a/app/views/notes/_list.html.erb +++ b/app/views/notes/_list.html.erb @@ -1,85 +1,60 @@ - - -<% if notes.count > 1 then%> - <% style_to_add = "height:150px; overflow-y:auto;" %> -<%else%> - <% style_to_add = "" %> -<%end%> - -
- -
- - - <% notes.each do |note|%> - - - - - <%end%> - -
- <% user = note.user.name %> - <%= user %>
- (<%= l note.updated_at, format: :custom %>) -
- - <% if note.archived %> - - <% if note.archived_by == current_user.id %> - <%= _('Note removed by you')%> - <% else %> - <% archived_by_user = User.find(note.archived_by) %> - <%= _('Note removed by')%> <%= archived_by_user.name %> - <%end%> - - <%else%> - - <%= link_to _('View'),"#question-form-#{question_id}", onclick: "view_note_button(#{note.id}, #{question_id})", :class => "dmp_table_link view_comment_button" %> - - <% if current_user.id == note.user_id %> - <%= link_to _('Edit'),"#question-form-#{question_id}", onclick: "edit_note(#{note.id}, #{question_id})", :class => "dmp_table_link edit_comment_button" %> - <%= link_to _('Remove'),"#question-form-#{question_id}", onclick: "archive_note(#{note.id}, #{question_id})", :class => "dmp_table_link archive_comment_button" %> - <% end%> - - <% if plan.administerable_by?(current_user.id) && current_user.id != note.user_id %> - <%= hidden_field_tag :note_id, note.id, :class => "comment_id" %> - <%= link_to _('Remove'),"#question-form-#{question_id}", onclick: "archive_note(#{note.id}, #{question_id})", :class => "dmp_table_link archive_comment_button" %> - <% end%> - <%end%> - -
-
- -
- - -<% notes_not_archived = notes.select { |n| n.archived.nil? } %> -<% latest_note = notes_not_archived.sort { |x,y| y.updated_at <=> x.updated_at }.first %> -<% if !latest_note.nil? then%> -
- <%= render :partial => "/notes/view", locals: {note: latest_note} %> -
-
-<%end%> - -<%notes.each do |note|%> - - - - - - - - - -<%end%> +<%# locals: { question_id, notes, plan } %> +<% notes.each do |note| %> + <% if !note.archived %> +
+
+
    +
  • +
    +
      +
    • <%= note.user.name %>
    • +
    • (<%= l note.updated_at, format: :short %>)
    • +
    +
    +
    +
      +
    • <%= link_to(_('Show'), "#note_show#{note.id}", class: 'note_show_link') %>
    • + <% if plan.commentable_by?(current_user) %> + <% if current_user.id == note.user_id %> +
    • <%= link_to(_('Edit'), "#note_edit#{note.id}", class: 'note_edit_link') %>
    • +
    • <%= link_to(_('Remove'), "#note_archive#{note.id}", class: 'note_archive_link') %>
    • + <% else %> + <% if plan.administerable_by?(current_user.id) %> +
    • <%= link_to(_('Remove'), "#note_archive#{note.id}", class: 'note_archive_link') %>
    • + <% end %> + <% end %> + <% end %> +
    +
    +
    +
  • +
+
+
+ <% end %> +<% end %> +<% notes.each do |note| %> + <% if !note.archived %> +
+
+
" data-question-id="<%= question_id %>" style="display:none;"> + <%= render partial: "/notes/show", locals: { note: note }, formats: [:html] %> +
+
+
+
+
+
" data-question-id="<%= question_id %>" style="display:none;"> + <%= render partial: "/notes/edit", locals: { note: note }, formats: [:html] %> +
+
+
+
+
+
" data-question-id="<%= question_id %>" style="display:none;"> + <%= render partial: "/notes/archive", locals: { note: note }, formats: [:html] %> +
+
+
+ <% end %> +<% end %> diff --git a/app/views/notes/_new.html.erb b/app/views/notes/_new.html.erb new file mode 100644 index 0000000..3e66e94 --- /dev/null +++ b/app/views/notes/_new.html.erb @@ -0,0 +1,14 @@ +<%# locals: { question, answer, plan } %> +<%= form_for(Note.new, url: notes_path) do |f| %> + <%= f.hidden_field :user_id, value: current_user.id %> + <%= f.hidden_field :question_id, value: question.id %> + <%= f.hidden_field :answer_id, value: answer.id %> + <%= f.hidden_field :plan_id, value: plan.id %> +
+ <%= f.label(:text, _('Add comments to share with collaborators')) %> + <%= f.text_area(:text, class: 'form-control note', id: "note-#{question.id}") %> +
+
+ <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %> +
+<% end %> \ No newline at end of file diff --git a/app/views/notes/_show.html.erb b/app/views/notes/_show.html.erb new file mode 100644 index 0000000..614835e --- /dev/null +++ b/app/views/notes/_show.html.erb @@ -0,0 +1,12 @@ +<%# locals: { note } %> +<% if !note.nil? %> + <% user = User.find(note.user_id) %> +
    +
  • <%= raw note.text %>
  • +
  • + + <%= "#{user.name} at #{l(note.updated_at, format: :short)}" %> + +
  • +
+<% end %> diff --git a/app/views/notes/_title.html.erb b/app/views/notes/_title.html.erb new file mode 100644 index 0000000..a4348c2 --- /dev/null +++ b/app/views/notes/_title.html.erb @@ -0,0 +1,3 @@ +<%# locals: { answer } %> +<% notes = answer.non_archived_notes %> +<%= _('Comments') %><%= notes.length > 0 ? " (#{notes.length})" : '' %> \ No newline at end of file diff --git a/app/views/notes/_view.html.erb b/app/views/notes/_view.html.erb deleted file mode 100644 index 0610f0c..0000000 --- a/app/views/notes/_view.html.erb +++ /dev/null @@ -1,11 +0,0 @@ - -<% user = User.find(note.user_id) %> -
-
-
-
    -
  • <%= _('Noted by:')%>

  • -
  • <%= user.name%> (<%= l note.updated_at, format: :custom %>)
  • -
  • <%= raw note.text %>
  • -
-
diff --git a/app/views/notes/archive.js.erb b/app/views/notes/archive.js.erb deleted file mode 100644 index f2687fa..0000000 --- a/app/views/notes/archive.js.erb +++ /dev/null @@ -1,29 +0,0 @@ - -// remove all tinymce explicitly or re-init will not work later -tinymce.remove(".tinymce"); - -// render the list of notes and invisible view and edit sections -<% listlabel = "#comment-question-area-#{@question.id}" %> -$("<%=listlabel%>").html( - "<%= escape_javascript( render partial: '/phases/note', locals: {question: @question, answer: @answer, plan: @plan } ) %>" -); - -// TODO: this duplicates what is in the .yml file -// DRY it out! -tinymce.init({ - selector: ".tinymce", - statusbar: false, - menubar: false, - toolbar: "bold italic | bullist numlist | link | table", - plugins: [ "table", "link", "paste" ], - target_list: false, - autoresize_min_height: 130, - extended_valid_elements: [ "a[href|target=_blank]" ], - paste_auto_cleanup_on_paste : true, - paste_remove_styles: true, - paste_retain_style_properties: "none", - paste_convert_middot_lists: true, - paste_remove_styles_if_webkit: true, - paste_remove_spans: true, - paste_strip_class_attributes: "all" -}); diff --git a/app/views/notes/create.js.erb b/app/views/notes/create.js.erb deleted file mode 100644 index 4cd1b85..0000000 --- a/app/views/notes/create.js.erb +++ /dev/null @@ -1,31 +0,0 @@ - -// rewrite the number of notes heading e.g. Notes(3) -<% noteslabel = "#notes_number_#{@question.id}" %> -$("<%=noteslabel%>").html("Notes (<%= @num_notes %>)"); - -// need to remove the existing tinymce editor otherwise -tinymce.remove(".tinymce"); - -// render the list of notes and invisible view and edit sections -<% listlabel = "#comment-question-area-#{@question.id}" %> -$("<%=listlabel%>").html( - "<%= escape_javascript( render partial: '/phases/note', locals: {question: @question, answer: @answer, plan: @plan } ) %>" -); - -tinymce.init({ - selector: ".tinymce", - statusbar: false, - menubar: false, - toolbar: "bold italic | bullist numlist | link | table", - plugins: [ "table", "link", "paste" ], - target_list: false, - autoresize_min_height: 130, - extended_valid_elements: [ "a[href|target=_blank]" ], - paste_auto_cleanup_on_paste : true, - paste_remove_styles: true, - paste_retain_style_properties: "none", - paste_convert_middot_lists: true, - paste_remove_styles_if_webkit: true, - paste_remove_spans: true, - paste_strip_class_attributes: "all" -}); diff --git a/app/views/notes/update.js.erb b/app/views/notes/update.js.erb deleted file mode 100644 index 45c5eb5..0000000 --- a/app/views/notes/update.js.erb +++ /dev/null @@ -1,27 +0,0 @@ - -// need to remove the existing tinymce editor otherwise -tinymce.remove(".tinymce"); - -// render the list of notes and invisible view and edit sections -<% listlabel = "#comment-question-area-#{@question.id}" %> -$("<%=listlabel%>").html( - "<%= escape_javascript( render partial: '/phases/note', locals: {question: @question, answer: @answer, plan: @plan } ) %>" -); - -tinymce.init({ - selector: ".tinymce", - statusbar: false, - menubar: false, - toolbar: "bold italic | bullist numlist | link | table", - plugins: [ "table", "link", "paste" ], - target_list: false, - autoresize_min_height: 130, - extended_valid_elements: [ "a[href|target=_blank]" ], - paste_auto_cleanup_on_paste : true, - paste_remove_styles: true, - paste_retain_style_properties: "none", - paste_convert_middot_lists: true, - paste_remove_styles_if_webkit: true, - paste_remove_spans: true, - paste_strip_class_attributes: "all" -}); diff --git a/app/views/org_admin/plans/index.html.erb b/app/views/org_admin/plans/index.html.erb new file mode 100644 index 0000000..cd2ff28 --- /dev/null +++ b/app/views/org_admin/plans/index.html.erb @@ -0,0 +1,46 @@ +
+
+

<%= _('%{org_name} Plans') % { org_name: current_user.org.name } %>

+
+
+ +
+
+ <% if @feedback_plans.length > 0 %> +

<%= _('Notifications') %>

+
+
+ + + + + + + + + <% @feedback_plans.each do |notice| %> + + + + + + + + + <% end %> + +
<%= _('Plan') %><%= _('Requestor') %><%= _('Type') %><%= _('Actions') %>
<%= link_to notice.name, plan_path(notice) %><%= notice.owner.name(false) %><%= _('Feedback requested') %><%= link_to _('Complete'), feedback_complete_org_admin_plan_path(notice), 'data-toggle': 'tooltip', title: _('Notify the plan owner that I have finished providing feedback') %>
+
+
+ <% end %> + <% if @plans.length > 0 %> + <%= link_to _('Download plans'), org_admin_download_plans_path(format: :csv), target: '_blank', class: 'btn btn-default pull-right' %> + <%= paginable_renderise( + partial: '/paginable/plans/org_admin', + controller: 'paginable/plans', + action: 'org_admin', + scope: @plans, + query_params: { sort_field: 'plans.updated_at', sort_direction: :desc }) %> + <% end %> +
+
diff --git a/app/views/org_admin/shared/_theme_selector.html.erb b/app/views/org_admin/shared/_theme_selector.html.erb new file mode 100644 index 0000000..5828516 --- /dev/null +++ b/app/views/org_admin/shared/_theme_selector.html.erb @@ -0,0 +1,30 @@ +<%# locals: all_themes & popover_message %> +
+ <%= f.label _('Themes'), for: :theme_ids, class: 'control-label' %> + <%= render partial: 'shared/popover', + locals: { message: popover_message, placement: 'right' }%> +
+ <% if all_themes.length > 0 %> + <% + cntr = 0 + nbr_of_cols = (all_themes.length.to_f / MAX_NUMBER_THEMES_PER_COLUMN.to_f).ceil + col_size = (12 / (nbr_of_cols > 4 ? 3 : nbr_of_cols)).round + %> +
+ <% all_themes.each do |theme| %> + <% if cntr >= MAX_NUMBER_THEMES_PER_COLUMN %> +
+
+ <% cntr = 0 %> + <% end %> +
+ <%= f.label :theme_ids, raw("#{f.check_box :theme_ids, {multiple: true}, theme.id, nil} #{theme.title}") %> +
+ <% cntr += 1 %> + <% end %> +
+ <% else %> +

<%= _('No themes have been defined. Please contact your administrator for assistance.') %>

+ <% end %> +
+
\ No newline at end of file diff --git a/app/views/org_admin/templates/_admin_nav_tabs.html.erb b/app/views/org_admin/templates/_admin_nav_tabs.html.erb new file mode 100644 index 0000000..012229d --- /dev/null +++ b/app/views/org_admin/templates/_admin_nav_tabs.html.erb @@ -0,0 +1,17 @@ + diff --git a/app/views/org_admin/templates/_edit.html.erb b/app/views/org_admin/templates/_edit.html.erb new file mode 100644 index 0000000..befa1c5 --- /dev/null +++ b/app/views/org_admin/templates/_edit.html.erb @@ -0,0 +1,67 @@ +
+
+ <% if template == current && template.customization_of.nil? %> + + <% end %> +
+
+
+
+
+ <%= render partial: "org_admin/templates/show_template", locals: { template: template, current: current, template_hash: template_hash }%> +
+
+
+
+
+ <% if template_hash[:template][:phases].present? && template_hash[:template][:phases].length > 1 %> + + <% end %> +
+
+ +
+
+
+ + <% if template_hash[:template][:phases].present? %> + <% i = 0 %> + <% template_hash[:template][:phases].each do |phase_no, phase_hash| %> + <% phase = phase_hash[:data] %> +
+
+ + +
+
" class="panel-collapse collapse<%= i == 0 ? 'in' : '' %>" role="tabpanel" aria-labelledby="<%= "headingPhase#{phase.id}" %>"<%= i == 0 ? 'aria-expanded="true"' : '' %>> +
+ <%= render partial: 'org_admin/templates/show_phases_sections', locals: { phase: phase, phase_hash: phase_hash, template: template, current: current } %> +
+
+
+ <% i += 1 %> + <% end %> + <% end %> +
+
+
\ No newline at end of file diff --git a/app/views/org_admin/templates/_edit_template.html.erb b/app/views/org_admin/templates/_edit_template.html.erb new file mode 100644 index 0000000..c5b6de8 --- /dev/null +++ b/app/views/org_admin/templates/_edit_template.html.erb @@ -0,0 +1,84 @@ + +<%= form_for(template, url: org_admin_template_path(template), html: { 'data-method': :put, remote: true }) do |f| %> +
+ <%= f.label(:title, _('Title'), class: "control-label") %> + <%= f.text_field(:title, class: "form-control", "aria-required": false, 'data-toggle': 'tooltip', title: _('Please enter a title for your template.')) %> +
+ +
+ <%= f.label(:description, _('Description'), class: "control-label") %> +
+ <%= text_area_tag("template-desc", template.description, { class: 'template', "aria-required": false }) %> +
+
+ + <% if current_user.org.funder? && !current_user.org.funder_only? %> + +
+ <%= f.label _('Visibility'), class: 'control-label' %> + <%= render partial: 'shared/popover', + locals: { message: _('Checking this box prevents the template from appearing in the public list of templates.'), + placement: 'right' }%> +
+ <%= f.label(:visibility, + raw("#{check_box_tag('template_visibility', '0', (template.visibility == 'organisationally_visible'))} #{_('for internal %{org_name} use only') % {org_name: @template.org.name}}")) %> +
+
+ <% end %> + +
+ <%= label_tag(:status, _('Status'), class: "control-label") %> +

+ <% if template_hash[:live].nil? %> + <%= _('Unpublished') %> + <% elsif template_hash[:current].dirty? %> + <%= _('You have unpublished changes') %> + <% else %> + <%= _('Published') %> + <% end %> +

+
+ +
+ <%= label_tag(:created_at, _('Created at'), class: "control-label") %> +

+ <%= l template.created_at.to_date, formats: :short %> +

+
+ +
+ <%= label_tag(:updated, _('Last updated'), class: "control-label") %> +

+ <%= l template.updated_at.to_date, formats: :short %> +

+
+ + <% if template.org.funder? %> +
+ <%= render(partial: '/shared/links', + locals: { + context: 'funder', + title: _('Funder Links'), + links: template.links['funder'], + max_number_links: MAX_NUMBER_LINKS_FUNDER, + tooltip: _('Add links to funder websites that provide additional information about the requirements for this template') }) %> +
+
+ <%= render(partial: '/shared/links', + locals: { + context: 'sample_plan', + title: _('Sample Plan Links'), + links: template.links['sample_plan'], + max_number_links: MAX_NUMBER_LINKS_SAMPLE_PLAN, + tooltip: _('Add links to sample plans if provided by the funder.') }) %> +
+ <%= hidden_field_tag('template-links', ActiveSupport::JSON.encode(template.links)) %> + <% end %> + +
+
+ <%= f.button(_('Save'), class: 'btn btn-default', type: "submit") %> + <%= link_to(_('Cancel'), '#', { class: 'btn btn-default template_show_link', role: "button" }) %> +
+
+<% end %> \ No newline at end of file diff --git a/app/views/org_admin/templates/_show_phases_sections.html.erb b/app/views/org_admin/templates/_show_phases_sections.html.erb new file mode 100644 index 0000000..e0452ec --- /dev/null +++ b/app/views/org_admin/templates/_show_phases_sections.html.erb @@ -0,0 +1,55 @@ + +
+
+

+ <%= raw phase.description %> +

+
+
+
+
+
+ <% phase_button_text = template.customization_of.nil? ? _('Show Phase') : _('Customize Phase') %> + <%= link_to phase_button_text, admin_show_phase_path(id: phase.id), { class: "btn btn-default", role: 'button' } %> + <% if template == current && phase.modifiable %> + <%= link_to _('Delete phase'), admin_destroy_phase_path(phase_id: phase.id), + data: { confirm: _("You are about to delete the '%{phase_title}' phase. This will remove all of the sections and questions listed below. Are you sure?") % { phase_title: phase.title }, + length: 20, omission: _('... (continued)') }, method: :delete, class: 'btn btn-default', role: 'button' %> + <% end %> +
+
+
+
+
+ <% if phase_hash[:sections].length > 0 %> +
+ + + + + + + + + <% (phase_hash[:sections].values.sort_by { |key| key[:data][:number] }).each do |section| %> + + + + + <% end %> + +
<%= _('Sections')%><%= _('Questions')%>
+

<%= section[:data].title %>

+
+ <% if section[:questions].present? %> +
    + <% (section[:questions].values.sort_by { |key| key[:data][:number] }).each do |question| %> +
  • <%= raw question[:data].text %>
  • + <% end %> +
+ <% end %> +
+
+ <% end %> +
+
diff --git a/app/views/org_admin/templates/_show_template.html.erb b/app/views/org_admin/templates/_show_template.html.erb new file mode 100644 index 0000000..d2af801 --- /dev/null +++ b/app/views/org_admin/templates/_show_template.html.erb @@ -0,0 +1,36 @@ + +
+
<%= _('Title') %>
+
<%= template.title %>
+
<%= _('Description') %>
+
+ <%= (!template.description.nil? && template.description != "" ? raw( template.description) : '-') %> +
+
<%= _('Status') %>
+
+ <% if template_hash[:live].nil? %> + <%= _('Unpublished') %> + + <% elsif template_hash[:current].dirty? %> + <%= _('You have unpublished changes') %> + + <% else %> + <%= _('Published') %> + <% end %> +
+ <% if current_user.org.funder? && !current_user.org.funder_only? %> + +
<%= _('Visibility') %>
+
<%= (template.visibility == 'organisationally_visible' ? _('for internal %{org_name} use only') % {org_name: @template.org.name} : _('available to the public') + (template_hash[:live].nil? ? ' (once published)' : '')) %>
+ <% end %> +
<%= _('Created at') %>
+
<%= l template.created_at.to_date, formats: :short %>
+
<%= _('Last updated') %>
+
<%= l template.updated_at.to_date, formats: :short %>
+
+ +<% if template == current && template.customization_of.nil? %> +
+ <%= link_to(_('Edit template details'), '#', { class: "btn btn-default template_edit_link", role: "button" }) %> +
+<% end %> diff --git a/app/views/org_admin/templates/container.html.erb b/app/views/org_admin/templates/container.html.erb new file mode 100644 index 0000000..14b6881 --- /dev/null +++ b/app/views/org_admin/templates/container.html.erb @@ -0,0 +1,22 @@ +<%= current_tab = '' unless current_tab.present? %> +
+
+

<%= template.title %>

+ <%= link_to _('View all templates'), org_admin_templates_path(r: current_tab), class: 'btn btn-default pull-right' %> +
+
+
+
+ + <%= render partial: "/org_admin/templates/admin_nav_tabs", locals: local_assigns %> +
+
+
+
+ <%= render partial: partial_path, locals: local_assigns %> +
+
+
+
+
+
\ No newline at end of file diff --git a/app/views/org_admin/templates/history.html.erb b/app/views/org_admin/templates/history.html.erb new file mode 100644 index 0000000..850b935 --- /dev/null +++ b/app/views/org_admin/templates/history.html.erb @@ -0,0 +1,29 @@ +
+
+

+ <%= _('Template History') %> +
+ <%= link_to _('View all templates'), "#{org_admin_templates_path}##{@current_tab}", class: "btn btn-primary" %> +
+

+

<%= _('Here you can view previously published versions of your template. These can no longer be modified.')%>

+
+
+ +
+
+ + <% if @templates.length > 0 %> + <%= paginable_renderise( + partial: '/paginable/templates/history', + controller: 'paginable/templates', + action: 'history', + path_params: { id: @template.id }, + query_params: { sort_field: :version, sort_direction: :desc }, + scope: @templates, + locals: { current: @current }) %> + <% else %> +

<%= _('This template is new and does not yet have any publication history.') %>

+ <% end %> +
+
diff --git a/app/views/org_admin/templates/index.html.erb b/app/views/org_admin/templates/index.html.erb new file mode 100644 index 0000000..8fc6a6f --- /dev/null +++ b/app/views/org_admin/templates/index.html.erb @@ -0,0 +1,85 @@ +<% # locals: funder_templates, org_templates, current_user, current_org, orgs, current_tab %> +
+
+

<%= _('Templates') %>

+
+ <% if current_user.can_super_admin? %> +
+

<%= _('If you would like to modify one of the templates below, you must first change your organisation affiliation.') %>

+
+
+ <%= form_for current_user, url: org_swap_user_path(current_user), html: {method: :put, id: 'super-admin-switch-org'} do |f| %> + <%= render partial: "shared/my_org", locals: {f: f, default_org: current_org, orgs: orgs, allow_other_orgs: false} %> + <%= f.submit _('Change affiliation'), class: 'btn btn-default' %> + <% end %> +
+ <% end %> +
+

+ <%= _('If you wish to add an organisational template for a Data Management Plan, use the \'create template\' button. You can create more than one template if desired e.g. one for researchers and one for PhD students. Your template will be presented to users within your organisation when no funder templates apply. If you want to add questions to funder templates use the \'customise template\' options below.') %> +

+
+
+<% selected_tab = current_tab || (current_user.can_super_admin? ? 'all-templates' : 'organisation-templates') %> +
+
+ + +
+ <% if current_user.can_super_admin? %> +
+

<%= _('All Templates') %>

+ <%= paginable_renderise( + partial: 'paginable/templates/all', + controller: 'paginable/templates', + action: 'all', + scope: all_templates, + query_params: { sort_field: 'templates.title', sort_direction: :asc }, + locals: {current_org: current_org.id, published: published, scopes: scopes[:all], hide_actions: true}) %> +
+ <% end %> +
+

<%= current_user.can_super_admin? ? _('%{org_name} Templates') % { org_name: current_user.org.name } : _('Own Templates') %>

+ <%= paginable_renderise( + partial: 'paginable/templates/orgs', + controller: 'paginable/templates', + action: 'orgs', + scope: own_templates, + query_params: { sort_field: 'templates.title', sort_direction: :asc }, + locals: {current_org: current_org.id, published: published, scopes: scopes[:orgs], hide_actions: false}) %> +
+ + <% if !current_org.funder_only? %> +
+

<%= _('Customizable Templates') %>

+ <%= paginable_renderise( + partial: 'paginable/templates/funders', + controller: 'paginable/templates', + action: 'funders', + scope: customizable_templates, + query_params: { sort_field: 'templates.title', sort_direction: :asc }, + locals: {current_org: current_org.id, customizations: customized_templates, published: published, scopes: scopes[:funders]}) %> +
+ <% end %> +
+ + + <%= _('Create a template') %> + +
+
\ No newline at end of file diff --git a/app/views/org_admin/templates/new.html.erb b/app/views/org_admin/templates/new.html.erb new file mode 100644 index 0000000..facbe19 --- /dev/null +++ b/app/views/org_admin/templates/new.html.erb @@ -0,0 +1,34 @@ +
+
+

+ <%= _('New template') %> +

+
+ <%= link_to _('View all templates'), "#{org_admin_templates_path}#organisation-templates", class: "btn btn-primary" %> +
+
+
+
+
+ <%= raw _('

To create a new template, first enter a title and description. Once you have saved this you will be presented with options to add one or more phases.

')%> + <%= form_for :template, url: {action: "create"} do |f| %> +
+ <%= f.label(:title, _('Title') ,class: 'control-label') %> + <%= f.text_field :title, as: :string, + class: "form-control", "data-toggle": "tooltip", title: _('Please enter a title for your template.') %> +
+
+ <%= f.label(:description, _('Description'), class: 'control-label') %> +
+ <%= text_area_tag('template-desc', '', class: 'form-control template') %> +
+
+
+
+ <%= f.submit _('Save'), class: "btn btn-default", role:"button" %> + <%= link_to _('Cancel'), org_admin_templates_path, class: "btn btn-default", role:"button" %> +
+
+ <% end %> +
+
\ No newline at end of file diff --git a/app/views/orgs/_feedback_form.html.erb b/app/views/orgs/_feedback_form.html.erb new file mode 100644 index 0000000..b9bc43a --- /dev/null +++ b/app/views/orgs/_feedback_form.html.erb @@ -0,0 +1,31 @@ +<%= form_for(org, url: url, html: { multipart: true, method: method, id: "edit_org_feedback_form" } ) do |f| %> +

<%= _('Request Feedback') %>

+
+
+ <%= f.label :feedback_enabled, raw("#{f.radio_button(:feedback_enabled, true)} #{_('On')}") %> + <%= f.label :feedback_enabled, raw("#{f.radio_button(:feedback_enabled, false)} #{_('Off')}") %> +
+
+
+

<%= _('Request Expert Feedback - Automated Email:') %>

+
+
+ <%= f.label :feedback_email_subject, _('Subject'), class: "control-label" %> + + <%= f.text_field :feedback_email_subject, class: "form-control", placeholder: _(feedback_confirmation_default_subject) % { application_name: Rails.configuration.branding[:application][:name] } %> +
+
+
+ <% tip = _(feedback_confirmation_default_message) % { user_name: _('%{user_name}'), plan_name: _('%{plan_name}'), organisation_email: org.contact_email } %> +
+ <%= f.label :feedback_email_msg, _('Message'), class: "control-label" %> + <%= f.text_area :feedback_email_msg, class: "form-control" %> +
+
+
+
+
+ <%= f.button(_('Save'), id:"save_org_submit", class: "btn btn-primary", type: "submit") %> +
+
+<% end %> \ No newline at end of file diff --git a/app/views/orgs/_org_link.html.erb b/app/views/orgs/_org_link.html.erb new file mode 100644 index 0000000..44b70eb --- /dev/null +++ b/app/views/orgs/_org_link.html.erb @@ -0,0 +1,15 @@ + \ No newline at end of file diff --git a/app/views/orgs/_profile_form.html.erb b/app/views/orgs/_profile_form.html.erb new file mode 100644 index 0000000..fc4bd65 --- /dev/null +++ b/app/views/orgs/_profile_form.html.erb @@ -0,0 +1,110 @@ +<%= form_for(org, url: url, html: { multipart: true, method: method, id: "edit_org_profile_form" } ) do |f| %> +
+
+ <%= f.label :name, _('Organisation full name'), class: "control-label" %> + <%= f.text_field :name, id: "org_name", class: "form-control", "aria-required": true %> +
+
+
+
+ <%= f.label :abbreviation, _('Organisation abbreviated name'), class: "control-label" %> + <%= f.text_field :abbreviation, id: "org_abbreviation", class: "form-control", "aria-required": true %> +
+
+ +
+
+ <%= f.label :logo, _('Organization logo'), class: "control-label" %> + + <% if org.logo.present? %> +
+ <%= image_tag org.logo.url, alt: "#{org.name} #{_('logo')}" %> +
+ <%= f.label :remove_logo, raw("#{f.check_box :remove_logo, title: _("This will remove your organisation's logo")} #{_('Remove logo')}") %> + - <%= _('or') %> - + <%= f.file_field :logo %> +
+ <% else %> + <%= f.file_field :logo %> + <% end %> +
+
+ +
+
+ <%= render(partial: '/shared/links', + locals: { + context: 'org', + title: _('Organisation URLs'), + links: (org.links.present? ? org.links['org'] : []), + max_number_links: MAX_NUMBER_LINKS_FUNDER, + tooltip: _('Links will be displayed next to your organisation\'s logo') }) %> + <%= hidden_field_tag('org_links', value: org.links) %> +
+
+ +
+
+

<%= _("Administrator contact") %>

+
+
+
+
+ <%= f.label :contact_email, _('Contact email'), class: "control-label" %> + <%= f.text_field :contact_email, class: "form-control" %> +
+
+ <%= f.label :contact_name, _('Link text'), class: "control-label" %> + <%= f.text_field :contact_name, class: "form-control" %> +
+
+ +
+

<%= _('Organisational Configuration Information') %>

+ <% if current_user.can_super_admin? %> + <% if Rails.application.config.shibboleth_use_filtered_discovery_service %> + <% shibboleth = org.org_identifiers.select{ |ids| ids.identifier_scheme == IdentifierScheme.find_by(name: 'shibboleth')} %> + <% shib_id = (shibboleth.first.present? ? shibboleth.first.identifier : '') %> + <% shib_domain = shibboleth.first.present? ? (shibboleth.first.attrs.present? ? JSON.parse(shibboleth.first.attrs)['domain'] : '') : '' %> +
+
+ <%= f.label :shib_id, _('Shibboleth Entity Id'), class: "control-label" %> + <%= text_field_tag :shib_id, shib_id, class: "form-control", placeholder: _('Example: urn:mace:incommon:my-org.org') %> +
+
+
+
+ <%= f.label :shib_domain, _('Shibboleth Domain'), class: "control-label" %> + <%= text_field_tag :shib_domain, shib_domain, class: "form-control", placeholder: _('Example: my-org.org') %> +
+
+ <% end %> +
+
+ <%= f.label _('Organisation Types'), for: :funder, class: 'control-label' %> +
+ <%= f.label :funder, raw("#{check_box_tag :funder, 2, org.funder?, class: 'org_types'} #{_('Funder')}") %> +
+
+ <%= f.label :institution, raw("#{check_box_tag :institution, 1, org.institution?, class: 'org_types'} #{_('Institution')}") %> +
+
+ <%= f.label :organisation, raw("#{check_box_tag :organisation, 4, org.organisation?, class: 'org_types'} #{_('Organisation')}") %> +
+ <%= f.hidden_field :org_type, 'data-validation': 'text', 'data-validation-error': _('You must select at least one organisation type') %> +
+
+ <% else %> +
+
<%= _('Organisation type(s)') %>
+
<%= org.org_type_to_s %>
+
+ <% end %> +
+ +
+
+ <%= f.button(_('Save'), id:"save_org_submit", class: "btn btn-primary", type: "submit") %> +
+
+<% end %> \ No newline at end of file diff --git a/app/views/orgs/admin_edit.html.erb b/app/views/orgs/admin_edit.html.erb index 3bbc468..0a5164e 100644 --- a/app/views/orgs/admin_edit.html.erb +++ b/app/views/orgs/admin_edit.html.erb @@ -1,83 +1,36 @@ -<%= stylesheet_link_tag "admin" %> -<% javascript 'admin.js' %> - -

- <%= _('Organisation details') %> -

- -
- -
- <%= form_for(@org, url: admin_update_org_path(@org), html: { multipart: true, id: "edit_org_details", method: :put}) do |f| %> - - - - - - - - - - - - - - - <% if @org.logo.present? %> - - - - - - - - - <%end%> - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<%= _('Name') %><%= f.text_field :name, as: :string, class: 'text_field has-tooltip', data_toggle: "tooltip", title: _("Please enter your organisation's name.") %>
<%= _('Abbreviation') %> -
- <%= f.text_field :abbreviation, as: :string, class: 'text_field' %> -
-
-
<%= _('Logo') %><%= image_tag @org.logo.url %>
<%= f.check_box :remove_logo %>   <%= _('If you decide to use the default DMPRoadmap logo, please check this box to remove your current logo.') %>
<%= _('Upload a new logo file') %><%= f.file_field :logo %>
<%= _('Top banner text') %><%= text_area_tag("org_banner_text", @org.banner_text, class: "tinymce") %>
<%= _('Website') %><%= f.text_field :target_url, as: :string, class: 'text_field has-tooltip', data_toggle: "tooltip", title: _('Please enter a valid web address.') %>
<%= _('Contact Email') %><%= f.text_field :contact_email, as: :string, class: 'text_field has-tooltip', data_toggle: "tooltip", title: _('The email address of an administrator at your organisation. Your users will use this address if they have questions.') %>
<%= _('Organisation type') %><%= @org.type %>
- -
- - -
- <%= f.submit _('Save'), class: 'btn btn-primary' %> - <%= link_to _('Cancel'), :back, class: 'btn btn-primary' %> -
- <% end %> - +
+
+

<%= org.id.present? ? _('Organisation details') : _('New organisation') %>

+ <% if current_user.can_super_admin? %> + <%= link_to _('View all organisations'), super_admin_orgs_path, class: 'btn btn-default pull-right' %> + <% end %>
-<%= tinymce :content_css => asset_path('application.css') %> +
+
+ + +
+
+ <%= render partial: 'orgs/profile_form', locals: local_assigns %> +
+ + <% if org.id.present? %> +
+ <%= render partial: 'orgs/feedback_form', locals: local_assigns %> +
+ <% end %> +
+
+
diff --git a/app/views/orgs/admin_show.html.erb b/app/views/orgs/admin_show.html.erb deleted file mode 100644 index a3da37b..0000000 --- a/app/views/orgs/admin_show.html.erb +++ /dev/null @@ -1,86 +0,0 @@ -<%= stylesheet_link_tag "admin" %> - -

- <%= _('Organisation details') %> -

-<%= _('These are the basic details for your organisation.')%> -
-
-
- -
- - - - <% if @org.name.present? then %> - - - - - <% if @org.logo.present? then %> - - <% end %> - - <% end %> - - <% if @org.abbreviation.present? then %> - - - - - <% else %> - - - - <% end %> - - <% if @org.banner_text.present? then %> - - - - - <% end %> - - <% if @org.target_url.present? then %> - - - - - <% end %> - - <% if @org.contact_email.present? then %> - - - - - <% end %> - - <% if @org.org_type != 0 then %> - - - - - <% end %> - - <% if @org.parent_id.present? then %> - - - - - <% end %> - - - - - -
<%= _('Name') %><%= @org.name %>
<%= _('Abbreviation') %><%= @org.abbreviation %>
<%= _('Abbreviation') %><%= _('Please add an abbreviation to your org for display with annotations!')%>
<%= _('Top banner text') %><%= raw @org.banner_text %>
<%= _('Website') %><%= @org.target_url %>
<%= _('Contact Email') %><%= @org.contact_email %>
<%= _('Organisation type') %><%= @org.type %>
<%= _('Main organisation') %><%= @org.parent.name %>
<%= _('Last updated') %><%= l @org.updated_at.to_date, formats: :short %>
- -
-
- -
- <%= link_to _('Edit'), admin_edit_org_path(current_user.org), class: 'btn btn-primary'%> -
-
-
-
\ No newline at end of file diff --git a/app/views/orgs/shibboleth_ds.html.erb b/app/views/orgs/shibboleth_ds.html.erb new file mode 100644 index 0000000..615127a --- /dev/null +++ b/app/views/orgs/shibboleth_ds.html.erb @@ -0,0 +1,82 @@ +
+
+

<%= _('Find your organisation to sign in') %>

+
+
+ +
+
+ <%= form_for 'shibboleth_ds', url: shibboleth_ds_path, html: {id: 'shibboleth_ds'} do |f| %> +
+ <%= f.label(:org_name, _('Look up your organisation here'), class: "control-label") %> + + <% if @orgs.length <= 10 %> + + + <% else %> + <%= render partial: "shared/accessible_combobox", + locals: {name: 'org_name', + id: 'org_name', + default_selection: nil, + models: @orgs, + attribute: 'name', + required: true, + classes: ''} %> + <% end %> + + <%= f.button(_('Go'), class: "btn btn-default", type: "submit") %> + + <% if @orgs.length > 10 %> +

+ - <%= _('or') %> - +
+ <%= _('See the full list of participating organisations') %> +

+ <% end %> + +
+ <% end %> + + + +
+ +

+ <%= _('Organisation not in the list?') %>  + +

+
+
diff --git a/app/views/paginable/guidance_groups/_index.html.erb b/app/views/paginable/guidance_groups/_index.html.erb new file mode 100644 index 0000000..9f8e918 --- /dev/null +++ b/app/views/paginable/guidance_groups/_index.html.erb @@ -0,0 +1,57 @@ +
+ + + + + + + + + + + + <% scope.each do |guidance_gr| %> + + + + + + + + <% end %> + +
<%= _('Name') %><%= _('Status') %> <%= paginable_sort_link('published') %><%= _('Optional subset') %> <%= paginable_sort_link('optional_subset') %><%= _('Last updated') %> <%= paginable_sort_link('updated_at') %><%= _('Actions') %>
<%= guidance_gr.name %> + <% if guidance_gr.published.nil? || guidance_gr.published == false %> + <%= _('Unpublished')%> + <% else %> + <%= _('Published')%> + <% end %> + + <% if guidance_gr.optional_subset.nil? || guidance_gr.optional_subset == false %> + <%= _('No')%> + <% else %> + <%= _('Yes')%> + <% end %> + <%= l guidance_gr.updated_at.to_date, formats: :short %> + +
+
\ No newline at end of file diff --git a/app/views/paginable/guidances/_index.html.erb b/app/views/paginable/guidances/_index.html.erb new file mode 100644 index 0000000..98761b9 --- /dev/null +++ b/app/views/paginable/guidances/_index.html.erb @@ -0,0 +1,71 @@ +
+ + + + + + + + + + + + + <% scope.each do |guidance| %> + <% if guidance.in_group_belonging_to?(current_user.org_id) %> + + + <% if guidance.themes.present? %> + + <% else %> + + <% end %> + <% if guidance.guidance_group.present? %> + + <% else %> + + <% end %> + + + + + <% end %> + <% end %> + +
<%= _('Text') %> <%= paginable_sort_link('guidances.text') %><%= _('Themes') %><%= _('Guidance group') %> <%= paginable_sort_link('guidance_groups.name') %><%= _('Status') %> <%= paginable_sort_link('guidances.published') %><%= _('Last updated') %> <%= paginable_sort_link('guidances.updated_at') %><%= _('Actions') %>
<%= guidance.text.html_safe %> + <% guidance.themes.each do |th| %> + <%= th.title %> + <% end %> + -<%= guidance.guidance_group.name %>- + <% if guidance.published.nil? || guidance.published == false %> + <%= _('Unpublished')%> + <% else %> + <%= _('Published')%> + <% end %> + + <%= l guidance.updated_at.to_date, formats: :short %> + + +
+
\ No newline at end of file diff --git a/app/views/paginable/orgs/_index.html.erb b/app/views/paginable/orgs/_index.html.erb new file mode 100644 index 0000000..7dd18b6 --- /dev/null +++ b/app/views/paginable/orgs/_index.html.erb @@ -0,0 +1,42 @@ +
+ + + + + + + + + + + + <% scope.each do |org| %> + + + + + + + + <% end %> + +
<%= _('Organisation') %> <%= paginable_sort_link('orgs.name') %><%= _('Administrator Email') %> <%= paginable_sort_link('orgs.contact_email') %><%= _('Organisation Type(s)') %> <%= paginable_sort_link('orgs.org_type') %><%= _('Templates') %><%= _('Actions') %>
<%= org.name %><%= org.contact_email %><%= org.org_type_to_s %><%= org.templates.collect(&:dmptemplate_id).uniq.length %> + +
+
\ No newline at end of file diff --git a/app/views/paginable/plans/_org_admin.html.erb b/app/views/paginable/plans/_org_admin.html.erb new file mode 100644 index 0000000..fefee45 --- /dev/null +++ b/app/views/paginable/plans/_org_admin.html.erb @@ -0,0 +1,30 @@ +
+ + + + + + + + + + + + + <% scope.each do |plan| %> + + + + + + + + + <% end %> + +
<%= _('Project Title') %> <%= paginable_sort_link('plans.title') %><%= _('Template') %> <%= paginable_sort_link('templates.title') %><%= _('Organisation') %><%= _('Owner') %><%= _('Updated') %> <%= paginable_sort_link('plans.updated_at') %><%= _('Visibility') %>
+ <%= link_to "#{plan.title.length > 60 ? "#{plan.title[0..59]} ..." : plan.title}", plan_path(plan) %> + <%= plan.template.title %><%= plan.users.first.org.name %><%= plan.users.first.name(false) %><%= l(plan.updated_at.to_date, formats: :short) %> + <%= plan.visibility === 'is_test' ? _('Test') : raw(display_visibility(plan.visibility)) %> +
+
\ No newline at end of file diff --git a/app/views/paginable/plans/_organisationally_or_publicly_visible.html.erb b/app/views/paginable/plans/_organisationally_or_publicly_visible.html.erb new file mode 100644 index 0000000..b8fb98f --- /dev/null +++ b/app/views/paginable/plans/_organisationally_or_publicly_visible.html.erb @@ -0,0 +1,33 @@ +<% if current_user.org_id.present? %> +
+
+
+ + + + + + + + + + + + <% scope.each do |plan| %> + + + + + + + + <% end %> + +
<%= _('Project Title') %> <%= paginable_sort_link('plans.title') %><%= _('Template') %> <%= paginable_sort_link('templates.title') %><%= _('Owner') %><%= _('Updated') %> <%= paginable_sort_link('plans.updated_at') %><%= _('Download') %>
<%= plan.title.length > 40 ? "#{plan.title[0..39]} ..." : plan.title %><%= plan.template.title %><%= plan.owner.present? ? plan.owner.name : _('Unknown') %><%= l(plan.updated_at.to_date, formats: :short) %> + <%= link_to _('PDF'), plan_export_path(plan, format: :pdf), target: '_blank' %> +
+
+
+
+<% end %> + diff --git a/app/views/paginable/plans/_privately_visible.html.erb b/app/views/paginable/plans/_privately_visible.html.erb new file mode 100644 index 0000000..31ff50b --- /dev/null +++ b/app/views/paginable/plans/_privately_visible.html.erb @@ -0,0 +1,81 @@ +
+ + + + + + + + + + + + + + + <% scope.each do |plan| %> + + + + + + + + + + + <% end %> + +
<%= _('Project Title') %> <%= paginable_sort_link('plans.title') %><%= _('Template') %> <%= paginable_sort_link('templates.title') %><%= _('Edited') %> <%= paginable_sort_link('plans.updated_at') %><%= _('Role') %><%= _('Test') %><%= _('Visibility') %><%= _('Shared') %> + <%= _('Actions') %> +
+ <%= link_to "#{plan.title.length > 60 ? "#{plan.title[0..59]} ..." : plan.title}", + plan_path(plan) %> + <%= plan.template.title %><%= l(plan.updated_at.to_date, formats: :short) %><%= display_role(plan.roles.find_by(user: current_user)) %> + <% if plan.administerable_by?(current_user.id) then %> + <%= form_for plan, url: set_test_plan_path(plan), html: { method: :post, class: 'set_test_plan', remote: true } do |f| %> + <%= check_box_tag(:is_test, "1", (plan.visibility === 'is_test')) %> + <%= f.submit(_('Update'), style: 'display:none') %> + <% end %> + <% else %> + <%= plan.visibility === 'is_test' ? _('Yes') : _('No') %> + <% end %> + + <%= plan.visibility === 'is_test' ? _('N/A') : raw(display_visibility(plan.visibility)) %> + + <% if plan.shared %> + <%= _("Yes") %> + <% else %> + <%= _('No') %> + <% end %> + + +
+
\ No newline at end of file diff --git a/app/views/paginable/plans/_publicly_visible.html.erb b/app/views/paginable/plans/_publicly_visible.html.erb new file mode 100644 index 0000000..1f817de --- /dev/null +++ b/app/views/paginable/plans/_publicly_visible.html.erb @@ -0,0 +1,26 @@ +
+ + + + + + + + + + + + <% scope.each do |plan| %> + + + + + + + + <% end %> + +
<%= _('Project Title') %> <%= paginable_sort_link('plans.title') %><%= _('Template') %> <%= paginable_sort_link('templates.title') %><%= _('Organisation') %><%= _('Owner') %><%= _('Download') %>
<%= plan.title %><%= plan.template.title %><%= (plan.owner.nil? || plan.owner.org.nil? ? _('Not Applicable') : plan.owner.org.name) %><%= (plan.owner.nil? ? _('Unknown') : plan.owner.name(false)) %> + <%= link_to _('PDF'), plan_export_path(plan, format: :pdf), class: "dmp_table_link", target: '_blank' %> +
+
\ No newline at end of file diff --git a/app/views/paginable/templates/_all.html.erb b/app/views/paginable/templates/_all.html.erb new file mode 100644 index 0000000..e567bc0 --- /dev/null +++ b/app/views/paginable/templates/_all.html.erb @@ -0,0 +1,50 @@ +<% # locals: templates, current_org %> + +
+ + + <% if scopes.present? %> + + <% end %> + + + + + + + + + <% scope.each do |template| %> + + + + + + + <% end %> + +
<%= _('Template Name') %> <%= paginable_sort_link('templates.title') %><%= _('Organisation') %> <%= paginable_sort_link('orgs.name') %><%= _('Status') %> <%= paginable_sort_link('templates.published') %><%= _('Edited Date') %> <%= paginable_sort_link('templates.updated_at') %>
+ <%= template.title %> + + <%= template.org.name %> + + <% if template.dirty? %> + <%= _('Unpublished changes') %> + <% elsif template.published? %> + <%= _('Published') %> + <% else %> + <%= _('Unpublished') %> + <% end %> + + <% last_temp_updated = template.updated_at %> + <%= l last_temp_updated.to_date, formats: :short %> +
+
\ No newline at end of file diff --git a/app/views/paginable/templates/_funders.html.erb b/app/views/paginable/templates/_funders.html.erb new file mode 100644 index 0000000..d0beef2 --- /dev/null +++ b/app/views/paginable/templates/_funders.html.erb @@ -0,0 +1,93 @@ +<% # locals: templates, current_org %> + +
+ + + <% if scopes.present? %> + + <% end %> + + + + + + + + + + <% scope.each do |template| %> + <% customization = customizations[template.dmptemplate_id] %> + + + + + + + + <% end %> + +
<%= _('Template Name') %> <%= paginable_sort_link('templates.title') %><%= _('Funder') %> <%= paginable_sort_link('orgs.name') %><%= _('Status') %><%= _('Edited Date') %> <%= paginable_sort_link('templates.updated_at') %>
+ <%= "#{template.is_default? ? '* ' : ''}#{template.title}" %> + + <%= raw template.org.name %> + + <% if customization.present? %> + + <% if customization.updated_at < template.updated_at %> + <%= _('Original funder template has changed!')%> + <% elsif !template.published? %> + + <%= b_label = _('Funder version is unpublished') %> + <% elsif customization.dirty? %> + <%= _('You have unpublished changes') %> + <% elsif customization.published? %> + <%= _('Published') %> + <% else %> + <%= _('Unpublished') %> + <% end %> + <% else %> + <%= _('Not Customised') %> + <% end %> + + <% last_temp_updated = template.updated_at %> + <%= l last_temp_updated.to_date, formats: :short %> + + +
+
\ No newline at end of file diff --git a/app/views/paginable/templates/_history.html.erb b/app/views/paginable/templates/_history.html.erb new file mode 100644 index 0000000..bf54288 --- /dev/null +++ b/app/views/paginable/templates/_history.html.erb @@ -0,0 +1,38 @@ +<%# locals: { controller, action, paginable, scope, current } %> +
+ + + + + + + + + + + + <% scope.each do |org_template| %> + + + + + + + + <% end %> + +
<%= _('Title') %> <%= paginable_sort_link('title') %><%= _('Version') %> <%= paginable_sort_link('version') %><%= _('Published') %> <%= paginable_sort_link('published') %><%= _('Last updated') %> <%= paginable_sort_link('updated_at') %><%= _('Actions') %>
+ <%= org_template.title%> + <% if org_template == current && !org_template.published%> +     <%=_('Draft')%> + <% end %> + + <%= org_template.version %> + + <%= (org_template.published? ? _('Yes') : _('No')) %> + + <%= l org_template.updated_at.to_date, formats: :short %> + + <%= link_to (org_template == current ? _('Edit') : _('View')), edit_org_admin_template_path(id: org_template), class: "dmp_table_link"%> +
+
\ No newline at end of file diff --git a/app/views/paginable/templates/_orgs.html.erb b/app/views/paginable/templates/_orgs.html.erb new file mode 100644 index 0000000..7361570 --- /dev/null +++ b/app/views/paginable/templates/_orgs.html.erb @@ -0,0 +1,80 @@ +<% # locals: templates, current_org %> + +
+ + + <% if scopes.present? %> + + <% end %> + + + + + + + + + + <% scope.each do |template| %> + + + + + + + + <% end %> + +
<%= _('Template Name') %> <%= paginable_sort_link('templates.title') %><%= _('Description') %> <%= paginable_sort_link('templates.description') %><%= _('Status') %><%= _('Edited Date') %> <%= paginable_sort_link('templates.updated_at') %>
+ <%= template.title %> + + <%= raw(template.description) %> + + <% if published[template.dmptemplate_id].present? && !template.published? %> + <%= _('Unpublished changes') %> + <% elsif template.published? %> + <%= _('Published') %> + <% else %> + <%= _('Unpublished') %> + <% end %> + + <% last_temp_updated = template.updated_at %> + <%= l last_temp_updated.to_date, formats: :short %> + + +
+
\ No newline at end of file diff --git a/app/views/paginable/templates/_publicly_visible.html.erb b/app/views/paginable/templates/_publicly_visible.html.erb new file mode 100644 index 0000000..58c030f --- /dev/null +++ b/app/views/paginable/templates/_publicly_visible.html.erb @@ -0,0 +1,33 @@ +
+ + + + + + + + + + + + + <% scope.each do |template| %> + + + + + + + + + <% end %> + +
<%= _('Template Name') %> <%= paginable_sort_link('templates.title') %><%= _('Download') %><%= _('Organisation Name') %> <%= paginable_sort_link('orgs.name') %><%= _('Last Updated') %> <%= paginable_sort_link('templates.updated_at') %><%= _('Funder Links') %><%= _('Sample Plans') %>
<%= _('(if available)') %>
<%= template.title %> + <%= link_to _('DOCX'), template_export_path(template.dmptemplate_id, format: :docx), target: '_blank' %>
+ <%= link_to _('PDF'), template_export_path(template.dmptemplate_id, format: :pdf), target: '_blank' %> +
<%= template.org.name %><%= l(template.updated_at.to_date, formats: :short) %> + <%= raw links_to_a_elements(template.links['funder'], '
') %> +
+ <%= raw links_to_a_elements(template.links['sample_plan'], '
') %> +
+
\ No newline at end of file diff --git a/app/views/paginable/themes/_index.html.erb b/app/views/paginable/themes/_index.html.erb new file mode 100644 index 0000000..54c4610 --- /dev/null +++ b/app/views/paginable/themes/_index.html.erb @@ -0,0 +1,16 @@ + + + + + + + + + <% scope.each do |theme| %> + + + + + <% end %> + +
<%= _('Name') %> <%= paginable_sort_link('title') %><%= _('Guidance') %> <%= paginable_sort_link('description') %>
<%= link_to(theme.title, edit_super_admin_theme_path(theme)) %><%= raw(theme.description) %>
\ No newline at end of file diff --git a/app/views/paginable/users/_index.html.erb b/app/views/paginable/users/_index.html.erb new file mode 100644 index 0000000..377b95f --- /dev/null +++ b/app/views/paginable/users/_index.html.erb @@ -0,0 +1,57 @@ +
+
+
+ + + + + + + + + + + + + <% scope.each do |user| %> + <% if !user.nil? then%> + + + + + + <%# The content of this column get updated through AJAX whenever the permission for an user are updated %> + + + + <% end %> + <% end %> + +
<%= _('Name') %> <%= paginable_sort_link('firstname') %><%= _('Email') %> <%= paginable_sort_link('email') %><%= _('Last activity') %> <%= paginable_sort_link('last_sign_in_at') %><%= _('Plans') %><%= _('Current Privileges') %><%= _('Edit Privileges') %>
+ <% if !user.name.nil? %> + <%= user.name(false) %> + <% end %> + + <%= user.email %> + + <% if !user.last_sign_in_at.nil? %> + <%= l user.last_sign_in_at.to_date, :formats => :short %> + <% end %> + + <% unless user.roles.nil? %> + <%= user.roles.where(Role.not_reviewer_condition).length %> + <% end %> + + <%= render partial: 'users/current_privileges', locals: { user: user } %> + + <%# Do not allow a user to change their own permissions or a super admin's permissions if they are not a super admin %> + <% unless current_user == user || + !current_user.can_super_admin? && user.can_super_admin? %> + <% b_label = _('Edit') %> + <%= link_to( b_label, admin_grant_permissions_user_path(user)) %> + <% end %> +
+ +
+
+
diff --git a/app/views/phases/_admin_add.html.erb b/app/views/phases/_admin_add.html.erb new file mode 100644 index 0000000..2ab4baf --- /dev/null +++ b/app/views/phases/_admin_add.html.erb @@ -0,0 +1,30 @@ +

<%= _('Phase details') %>

+

<%= _('When you create a new phase for your template, a version will automatically be created. Once you complete the form below you will be provided with options to create sections and questions.') %>

+<%= form_for :phase, { url: admin_create_phase_path(r: current_tab) } do |f| %> + <%= f.hidden_field :template_id, value: @template.id%> +
+ <%= f.label(:title, _('Title') ,class: "control-label") %> + <%= f.text_field(:title, class: "form-control", 'aria-required': false, 'data-toggle': 'tooltip', title: _('Enter a title for the phase e.g. intial DMP, full DMP... This is what users will see in the tabs when completing a plan. If you only have one phase, call it something generic e.g. Glasgow DMP')) %> +
+ +
+
+ <%= f.label(:number, _('Order of display'), class: "control-label") %> +
+
+ <%= f.number_field(:number, in: 1..5, class: "form-control", 'aria-required': false, 'data-toggle': 'tooltip', title: _('This allows you to order the phases of your template.')) %> +
+
+ +
+ <%= f.label(:description, _('Description'), class: "control-label") %> +
"> + <%= text_area_tag('phase-desc', '', class: "tinymce form-control") %> +
+
+ +
+ <%= f.button(_('Save'), class: 'btn btn-default', type: "submit") %> + <%= link_to(_('Cancel'), edit_org_admin_template_path(@template), { class: 'btn btn-default', role: "button" }) %> +
+<% end %> \ No newline at end of file diff --git a/app/views/phases/_admin_show.html.erb b/app/views/phases/_admin_show.html.erb new file mode 100644 index 0000000..d29df78 --- /dev/null +++ b/app/views/phases/_admin_show.html.erb @@ -0,0 +1,85 @@ +<% # locals: { phase, template, edit, current_section } %> +

<%= _('Phase details')%>

+
+
+ <% if phase.modifiable && edit %> + + <% end %> +
+
+
+
+
+ <%= render partial: "phases/show_phase", locals: { phase: phase, edit: edit, current_tab: current_tab } %> +
+
+
+
+
+ <% if phase.sections.length > 1 %> + + <% end %> +
+
+ +
+
+
+ <% phase.sections.each do |section| %> +
+ +
" + class="panel-collapse collapse<%= " in" if section.id == current_section %>" + role="tabpanel" + aria-labelledby="<%= "headingSection#{section.id}" %>"> +
+ <% if edit && section.modifiable %> + <%= render partial: 'sections/edit_section', locals: { template: template, section: section, edit: edit, phase: phase, current_tab: current_tab } %> + <% else %> + <%= render partial: 'sections/show_section', locals: { template: template, section: section, current_tab: current_tab, edit: edit } %> + <% end %> +
+
+
+ <% end %> +
+ <% if edit || template.customization_of.present? %> +
+
+
+ <%= link_to(_('Add Section'), "#section_new#{phase.id}", { class: "btn btn-default section_new_link", role: "button" }) %> +
+
+
+ +
+
+ <% end %> +
+
diff --git a/app/views/phases/_answer_form.html.erb b/app/views/phases/_answer_form.html.erb deleted file mode 100644 index 566b757..0000000 --- a/app/views/phases/_answer_form.html.erb +++ /dev/null @@ -1,120 +0,0 @@ - - -
- <% - if answer.blank? - answer = Answer.new({ plan_id: plan.id, question_id: question.id, user_id: current_user.id }) - if question.default_value.present? - answer.text = question.default_value - end - end - %> -
-
" class="answer-locking">
-
"> - <%= render(partial: 'answers/new_edit', locals: { question: question, answer: answer, readonly: readonly }) %> -
-
" class="answer-status"> - <%= render(partial: 'answers/status', locals: { answer: answer }) %> -
-
-
- - -
- -
- <% comments = answer.notes.all %> - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - <% active_tab = nil %> -
    - <% annotations = question.annotations.where(type: Annotation.types[:guidance]) %> - <% if annotations.present? || question_guidances.present? %> - <% active_tab = 'guidance_tab' %> -
  • - <%= link_to _('Guidance'), "#", class: "right_column_tab_link" %> -
  • - <% else %> - <% active_tab = 'note_tab' %> - <% end %> -
  • "> - <% if comments.count > 0 %> - <%= link_to "#{_('Notes')} (#{comments.count})" , "#", id: "notes_number_#{question.id}", class: "right_column_tab_link" %> - <% else %> - <%= link_to _('Share note'), "#", id: "notes_number_#{question.id}", class: "right_column_tab_link" %> - <% end %> -
  • -
-
- - - -
-
- - - <% num_annotations = 0 %> - <% if annotations.present? %> - <% annotations.each do |annotation| %> - -
- - - - - -
-
<%= raw annotation.text %>
-
- <% num_annotations += 1%> -
- <% end %> - <% end %> - - - <% guidance_accordion_id = num_annotations %> - <% question_guidances.each_pair do |theme, group| %> - <% group.each do |gobj| %> -
- -
-
<%= raw gobj[:text] %>
-
- <% guidance_accordion_id += 1 %> -
- <% end %> - <% end %> - -
-
- -
- <%= render partial: "note", locals: {question: question, answer: answer, plan: plan, suffix: "" }%> -
-
- - -<% if last_question_id == question.id then %> -
-<% else %> -
-<% end %> diff --git a/app/views/phases/_edit_phase.html.erb b/app/views/phases/_edit_phase.html.erb index 40ae572..ec55c1f 100644 --- a/app/views/phases/_edit_phase.html.erb +++ b/app/views/phases/_edit_phase.html.erb @@ -1,46 +1,29 @@ - - -<%= form_for(phase, url: admin_update_phase_path(phase.id), html: { method: :put}) do |f| %> - -

- <%= _('Phase details')%> -

-
- <%= raw _("

Here you set the title that users will see. If you intend to have multiple phases for you DMP, this should be clear in the title and description.

")%> -

-
-
- - - - - - - - - - - - - - -
<%= _('Title') %><%= f.text_field :title, - as: :string, - class: 'text_field has-tooltip', 'data-toggle' => "tooltip", 'title' => _('Enter a title for the phase e.g. intial DMP, full DMP... This is what users will see in the tabs when completing a plan. If you only have one phase, call it something generic e.g. Glasgow DMP') %>
<%= _('Order of display') %><%= f.number_field :number, in: 0..5, class: "number_field has-tooltip", 'data-toggle' => "tooltip", 'title' => _('This allows you to order the phases of your template.') %>
<%= _('Description') %> -
- <%= text_area_tag("phase-desc", phase.description, class: "tinymce") %> -
-
- <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _("Enter a basic description. This will be presented to users on the 'Admin Plan' tab, above the summary of the sections and questions which they will be asked to answer."))%> -
-
-
-
- - -
- <%= f.submit _('Save'), class: 'btn btn-primary' %> - <%= link_to _('Cancel'), admin_show_phase_path(phase), class: 'btn cancel' %> +<%= form_for :phase, { url: admin_update_phase_path(phase.id, r: current_tab), html: { method: :put }} do |f| %> +
+ <%= f.label(:title, _('Title') ,class: "control-label") %> + <%= f.text_field(:title, class: "form-control", 'aria-required': false, 'data-toggle': 'tooltip', title: _('Enter a title for the phase e.g. intial DMP, full DMP... This is what users will see in the tabs when completing a plan. If you only have one phase, call it something generic e.g. Glasgow DMP')) %>
+
+
+ <%= f.label(:number, _('Order of display'), class: "control-label") %> +
+
+ <%= f.number_field(:number, in: 1..5, class: "form-control", 'aria-required': false, 'data-toggle': 'tooltip', title: _('This allows you to order the phases of your template.')) %> +
+
+ +
+ <%= f.label(:description, _('Description'), class: "control-label") %> +
"> + <%= text_area_tag('phase-desc', phase.description, class: 'phase tinymce form-control') %> +
+
+ +
+
+ <%= f.button(_('Save'), class: 'btn btn-default', type: "submit") %> + <%= link_to _('Cancel'), '#', class: 'btn btn-default phase_show_link', role: 'button' %> +
+
<% end %> \ No newline at end of file diff --git a/app/views/phases/_edit_plan_answers.html.erb b/app/views/phases/_edit_plan_answers.html.erb new file mode 100644 index 0000000..1c746c2 --- /dev/null +++ b/app/views/phases/_edit_plan_answers.html.erb @@ -0,0 +1,86 @@ +<%# locals: { plan, phase, readonly, question_guidance, guidance_groups } %> +
+
+
+
+
+ +
+ <% if plan.present? && phase.present? %> +
+ <%= render :partial => "/plans/progress", locals: { plan: plan, current_phase: phase } %> +
+ <% end %> +
+
+
+
+
+ <% phase.sections.each do |section| %> + <% sectionid = section.id %> +
+ +
+
+
<%= raw section.description %>
+ + <% section.questions.each_with_index do |question, i| %> + <% # Load the answer or create a new one + answer = answers[question.id] if plan.present? + if answer.blank? + answer = Answer.new({ plan: plan, question: question }) + end + %> +
+
+ +
+
" class="answer-locking">
+
" class="answer-form"> + <%= render(partial: '/answers/new_edit', locals: { question: question, answer: answer, readonly: readonly, locking: false }) %> +
+
" class="mt-10"> + <%= render(partial: '/answers/status', locals: { answer: answer }) %> +
+
+
+
+ + <%= render partial: '/phases/guidances_notes', locals: { plan: plan, template: phase.template, question: question, answer: answer, question_guidance: question_guidance, guidance_groups: guidance_groups } %> +
+
+ <%= raw('
') if i != section.questions.length - 1 %> + <% end %> +
+
+
+ <% end %> +
+
+
\ No newline at end of file diff --git a/app/views/phases/_guidances_notes.html.erb b/app/views/phases/_guidances_notes.html.erb new file mode 100644 index 0000000..7209d46 --- /dev/null +++ b/app/views/phases/_guidances_notes.html.erb @@ -0,0 +1,95 @@ +<%# locals: { plan, template, question, answer, question_guidance, guidance_groups } %> +<% annotations = question.annotations.where(type: Annotation.types[:guidance]) %> +<% guidance_set = question_guidance[question.id] || {} %> +<% guidances_active = (annotations.present? || guidance_set.length > 0) %> +
+ + +
+
+ +
+ <% if annotations.present? %> +
+
+
+ + <% num_annotations = 0 %> + <% i = 0 %> + <% annotations.each do |annotation| %> + <%= render partial: 'annotations/show', + locals: { + template: template, + example_answer: (annotation.example_answer? ? annotation : nil), + guidance: (annotation.guidance? ? annotation : nil), + for_plan: true + } %> + <% num_annotations += 1%> + <% i += 1 %> + <% end %> +
+
+
+ <% end %> + <% guidance_accordion_id = 0 %> + <% guidance_set.keys.each_with_index do |group, i| %> + <% obj = guidance_groups.select{ |gg| gg.name == group }.first %> + <% if obj.present? %> + <% accordion_id = "#{question.id}-#{obj.id}" %> +
+
+
+ <%= render partial: 'guidance_groups/show', + locals: { group: guidance_set[group], question: question, guidance_accordion_id: "#{accordion_id}-#{i}" } %> +
+
+
+ <% end %> + <% end %> +
+
+ + <% if plan.present? %> +
+ <%= render partial: '/notes/layout', locals: { plan: plan, question: question, answer: answer } %> +
+ <% end %> +
+
\ No newline at end of file diff --git a/app/views/phases/_note.html.erb b/app/views/phases/_note.html.erb deleted file mode 100644 index c296d9b..0000000 --- a/app/views/phases/_note.html.erb +++ /dev/null @@ -1,29 +0,0 @@ - <% if answer.present? && answer.notes.any? %> - <% notes = answer.notes.all.to_a.sort! {|x,y| y.updated_at <=> x.updated_at } %> - <% questionid = question.id %> - <%= hidden_field_tag :question_id, questionid, class: "question_id" %> - - -
- <%= link_to _('Add note'), - "#question-form-#{questionid}", - class: "btn btn-primary add_comment_button", - onclick: "add_note_button(#{questionid})" - %> -
- -
- - - <%= render :partial => "/notes/list", locals: {question_id: question.id, notes: notes, plan: plan} %> - - -
- - - - <% else%> - <%= render :partial => "/notes/add", locals: {answer: answer, question: question, plan_id: plan.id }%> - <% end%> diff --git a/app/views/phases/_overview.html.erb b/app/views/phases/_overview.html.erb new file mode 100644 index 0000000..829b724 --- /dev/null +++ b/app/views/phases/_overview.html.erb @@ -0,0 +1,28 @@ +<%# locals: { phase } %> +
+
+

+ <%= _('Instructions') %> + <%= _('Write plan') %> +

+

+ <%= raw(phase.description) %> +

+
+
+
+
+
    + <% phase.sections.each do |s| %> +
  • + <%= s.title %> +
      + <% s.questions.each do |q| %> +
    • <%= raw(q.text) %>
    • + <% end %> +
    +
  • + <% end %> +
+
+
\ No newline at end of file diff --git a/app/views/phases/_show_phase.html b/app/views/phases/_show_phase.html deleted file mode 100644 index ecb5dd4..0000000 --- a/app/views/phases/_show_phase.html +++ /dev/null @@ -1,37 +0,0 @@ - - -

- <%= _('Phase details')%> - - - <% if @phase.modifiable && @edit %> -
- <%= link_to _('Edit phase details'), '#', class: "btn btn-primary", id: "edit_phase_button"%> -
- <% end %> -

- -<% if @phase.template.org.not_funder %> -
- <%= raw _('

Here you set the title that users will see. If you intend to have multiple phases for you DMP, this should be clear in the title and description.

')%> -

-<% end %> - -
-
- - - - - - - - - - - - - - -
<%= _('Title') %><%= @phase.title %>
<%= _('Order of display') %><%= @phase.number %>
<%= _('Description') %><%= raw @phase.description %>
-
diff --git a/app/views/phases/_show_phase.html.erb b/app/views/phases/_show_phase.html.erb new file mode 100644 index 0000000..325a3b5 --- /dev/null +++ b/app/views/phases/_show_phase.html.erb @@ -0,0 +1,18 @@ +
+
<%= _('Title') %>
+
<%= phase.title %>
+
<%= _('Order of display') %>
+
<%= phase.number %>
+
<%= _('Description') %>
+
<%= raw phase.description %>
+
+<% if phase.modifiable && edit %> +
+
    +
  • <%= link_to(_('Edit phase details'), '#', { class: "btn btn-default phase_edit_link", role: "button" }) %>
  • +
  • <%= link_to(_('Preview'), admin_preview_phase_path(phase, r: current_tab), { class: 'btn btn-default phase_preview_link', role: 'button' }) %>
  • +
+
+<% end %> + + diff --git a/app/views/phases/admin_add.html.erb b/app/views/phases/admin_add.html.erb deleted file mode 100644 index 2263b8d..0000000 --- a/app/views/phases/admin_add.html.erb +++ /dev/null @@ -1,74 +0,0 @@ -<%- model_class = Phase -%> -<%= stylesheet_link_tag "admin" %> -<% javascript "admin.js" %> - -

- <%= @template.title %> - -
- <%= link_to _('View all templates'), - admin_index_template_path, - class: "btn btn-primary" %> -
-

- -
- - -<%= render partial: "templates/admin_nav_tabs", locals: {template: @template, active: "add_plan"} %> - - -
-
- - -
-
- - - <%= form_for :phase, {url: admin_create_phase_path} do |f| %> -

- <%= _('Phase details')%> -

- <%= raw _('When you create a new phase for your template, a version will automatically be created. Once you complete the form below you will be provided with options to create sections and questions.')%> -
-
- <%= f.hidden_field :template_id, value: @template.id%> - - - - - - - - - - - - - -
<%= _('Title') %><%= f.text_field :title, - as: :string, - class: "text_field has-tooltip", "data-toggle" => "tooltip", "title" => _('Enter a title for the phase e.g. intial DMP, full DMP... This is what users will see in the tabs when completing a plan. If you only have one phase, call it something generic e.g. Glasgow DMP') %>
<%= _('Order of display') %><%= f.number_field :number, in: 1..5, class: "number_field has-tooltip", "data-toggle" => "tooltip", title: _('This allows you to order the phases of your template.') %>
<%= _('Description') %> -
- <%= text_area_tag("phase-desc","" , class: "tinymce") %> - <%= tinymce :content_css => asset_path('application.css') %> -
-
- <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _("Enter a basic description. This will be presented to users on the 'Admin Plan' tab, above the summary of the sections and questions which they will be asked to answer."))%> -
-
-
-
- - -
- <%= f.submit _('Save'), class: "btn btn-primary" %> - <%= link_to _('Cancel'), admin_template_template_path(@template), class: "btn cancel" %> -
- - <%end%> -
-
-
-
\ No newline at end of file diff --git a/app/views/phases/admin_preview.html.erb b/app/views/phases/admin_preview.html.erb index 59ce2d0..8619c4c 100644 --- a/app/views/phases/admin_preview.html.erb +++ b/app/views/phases/admin_preview.html.erb @@ -1,59 +1,28 @@ -<%- model_class = Phase -%> -<%= stylesheet_link_tag "admin" %> - -

- <%= @template.title %> - -
- <%= link_to _('Back to edit view'), - admin_show_phase_path(id: @phase.id, edit: "true"), - class: 'btn btn-primary' %> - <%= link_to _('View all templates'), - admin_index_template_path, - class: 'btn btn-primary' %> -
-

- -
- - -<%= render partial: "templates/admin_nav_tabs", locals: {template: @template, active: @phase.id} %> - - -
-
- <% sections = @phase.sections %> - <% sections.order(:number).each do |section| %> -
- -
-
- <%= raw section.description %> -
-
- <% last_question_id = section.questions.order("number DESC").first.id%> - - <% section.questions.order("number").each do |question| %> - - <%= render partial: 'questions/preview_question', locals: {question: question}%> - <% if last_question_id == question.id then %> -
- <% else %> -
- <% end %> - - <% end %> -
-
-
- <% end %> -
+
+
+

<%= @template.title %>

+
+
    +
  • <%= link_to _('Back to edit phase'), admin_show_phase_path(id: @phase.id, r: @current_tab), class: 'btn btn-primary' %>
  • +
  • <%= link_to _('View all templates'), org_admin_templates_path(r: @current_tab), class: 'btn btn-primary' %>
  • +
+
+
+
+
+
+ + <%= render partial: "/org_admin/templates/admin_nav_tabs", locals: { template: @template, active: @phase.id, edit: false, current_tab: @current_tab } %> + +
+
+
+
+ <%= render partial: '/phases/edit_plan_answers', locals: { plan: nil, phase: @phase, readonly: true, question_guidance: {}, edit: false, guidance_groups: [] } %> +
+
+
+
+ +
\ No newline at end of file diff --git a/app/views/phases/admin_show.html.erb b/app/views/phases/admin_show.html.erb deleted file mode 100644 index 60dc90d..0000000 --- a/app/views/phases/admin_show.html.erb +++ /dev/null @@ -1,69 +0,0 @@ -<%- model_class = Phase -%> -<%= stylesheet_link_tag "admin" %> -<% javascript 'admin.js' %> - -<%= tinymce :content_css => asset_path('application.css') %> - -

- <%= @phase.template.title %> - -
- <%= link_to _('View all templates'), - admin_index_template_path, - class: 'btn btn-primary' %> -
-

- -
- - -<%= render partial: "templates/admin_nav_tabs", locals: {template: @phase.template, active: @phase.id, edit: @edit, current: @current} %> - - -
-
- - -
-
- - -
- <%= render partial: "phases/show_phase", locals: {phase: @phase}%> -
- <% if @phase.modifiable && @edit %> - - <% end %> -
-
- - - <% @sections.order("number ASC").each do |section| %> - <% if @edit && section.modifiable %> - <%= render partial: 'sections/edit_section', locals: {section: section, edit: @edit, phase: @phase} %> - <% else %> - <%= render partial: 'sections/show_section', locals: {section: section}%> - <% end %> - <% end %> - -
-
- - -<% if @edit || @phase.template.customization_of.present? %> - - - - -
-
- <%= link_to _('Add section'),'#', id: 'add_section_button', class: 'btn btn-primary' %> -
-
-<% end %> - - diff --git a/app/views/phases/edit.html.erb b/app/views/phases/edit.html.erb index e1b5279..c135c06 100644 --- a/app/views/phases/edit.html.erb +++ b/app/views/phases/edit.html.erb @@ -1,121 +1,12 @@ -<%- model_class = Plan -%> -<% javascript('plans.js') %> -<% javascript('answers/status.js') %> -<% javascript ('notes/index.js') %> - - - -<%= render :partial => "/plans/plan_title", locals: {plan: @plan} %> - - -
- <%= render :partial => "/plans/progress", locals: { plan: @plan } %> -
- - -<%= render :partial => "/plans/plan_nav_tabs", locals: {plan: @plan, active: @phase.title} %> - - -
- -
- <% sections = @phase.sections.sort_by {|section| section.number} %> - <% sections.each do |section| %> - - <% sectionid = section.id %> - -
- - - - -
-
- <%= raw section.description %> -
- -
- - - - - - - -
- <% questions = section.questions.sort_by {|question| question.number } %> - <% questions.each do |question| %> - <% if question.id == session[:question_id_comments].to_i then id_css = "current_question" end %> -
- - <% guidances = @question_guidance[question.id] %> - - <%= render partial: 'answer_form', - locals: { - plan: @plan, - question: question, - answer: @answers[question.id], - question_guidances: guidances, - last_question_id: section.questions.last.id, - readonly: @readonly - } - %> -
- <% end %> -
-
-
-
- - - - - <% end %> -
-
- -
- <%= _('Export') %> +<%# locals: { plan, phase, readonly, question_guidance } %> +
+
+

<%= plan.title %>

+
-<%= render :partial => "plans/export", locals: {plan: @plan, plan_data: @plan_data, phase: @phase } %> +
+
+ <%= render partial: 'edit_plan_answers', layout: 'plans/navigation', locals: local_assigns %> +
+
\ No newline at end of file diff --git a/app/views/plans/_answer_form_ro.html.erb b/app/views/plans/_answer_form_ro.html.erb deleted file mode 100644 index e035e1c..0000000 --- a/app/views/plans/_answer_form_ro.html.erb +++ /dev/null @@ -1,51 +0,0 @@ - -<% answer = @plan.answer(question.id) %> - -
- - <% q_format = question.question_format%> - -
-

<%= question.text %>

- -
- <% if q_format.title == "Check box" || q_format.title == "Multi select box" || - q_format.title == "Radio buttons" || q_format.title == "Dropdown" %> -
    - <% if answer.question_options.is_a? Array then %> -
  • <%= answer.question_options.text %>
  • - <% else %> - <% answer.question_options.each do |o| %> -
  • <%= o.text %>
  • - <% end %> - <% end %> -
- <% end %> - -
- <%= raw answer.text %> -
-
-
- - <% if answer.created_at.nil? then %> - <%= _('Not answered yet') %> - <% else %> - <%= _('Answered')%><%= answer.created_at %><%= _(' by')%><%= answer.user.name %> - <% end %> - - - -
- - -<% if last_question_id == question.id then %> -
-<% else %> -
-<% end %> diff --git a/app/views/plans/_available_templates.html.erb b/app/views/plans/_available_templates.html.erb deleted file mode 100644 index 59eb6fa..0000000 --- a/app/views/plans/_available_templates.html.erb +++ /dev/null @@ -1,20 +0,0 @@ -<%= _('Which DMP template would you like to use?') %> - - - - - -
- <%= _('We found multiple DMP templates corresponding to your funder.') %> -
- - \ No newline at end of file diff --git a/app/views/plans/_download_form.html.erb b/app/views/plans/_download_form.html.erb new file mode 100644 index 0000000..b6965e8 --- /dev/null +++ b/app/views/plans/_download_form.html.erb @@ -0,0 +1,104 @@ +<%= form_tag( export_plan_path(@plan), method: :get, target: '_blank', id: 'download_form') do |f| %> +

<%= _("Download settings") %>

+ + <% if @phase_options.length > 1 %> +
+ <%= label_tag(:phase_id, _("Select phase to download")) %> + <%= select_tag(:phase_id, options_for_select(@phase_options, @phase_options[0])) %> +
+ <% else %> + <%= hidden_field_tag(:phase_id, @phase_options[0][1]) %> + <% end %> + +

<%= _("Optional plan components") %>

+
+ <%= label_tag 'export[project_details]', raw("#{check_box_tag 'export[project_details]', true, false} #{_('project details coversheet')}") %> +
+
+ <%= label_tag 'export[question_headings]', raw("#{check_box_tag 'export[question_headings]', true, true} #{_('question text and section headings')}") %> +
+
+ <%= label_tag 'export[unanswered_questions]', raw("#{check_box_tag 'export[unanswered_questions]', true, true} #{_('unanswered questions')}") %> +
+ <% if @plan.template.customization_of.present? %> +
+ <%= label_tag 'export[custom_sections]', raw("#{check_box_tag 'export[custom_sections]', true, false} #{_('supplementary section(s) not requested by funding organisation')}") %> +
+ <% end %> + +

<%= _('Format') %>

+
+
+ <%= select_tag :format, options_for_select(ExportedPlan::VALID_FORMATS, :pdf), + class: 'form-control' %> +
+
+ +
+

<%= _('PDF formatting') %>

+
+
+

<%= _('Font') %>

+
+
+

<%= _('Margin (mm)') %>

+
+
+
+
+ <%= label_tag "export[formatting][font_face]", _('Face'), class: 'control-label' %> + <%= select_tag "export[formatting][font_face]", + options_for_select(Settings::Template::VALID_FONT_FACES, + @export_settings.formatting[:font_face]), + class: 'form-control', + "data-default": @plan.template.settings(:export).formatting[:font_face] %> +
+
+ <%= label_tag "export[formatting][font_size]", _('Size') + " (pt)", class: 'control-label' %> + <%= select_tag "export[formatting][font_size]", + options_for_select(Settings::Template::VALID_FONT_SIZE_RANGE.to_a, @export_settings.formatting[:font_size]), + class: 'form-control', + "data-default": @plan.template.settings(:export).formatting[:font_size] %> +
+ +
+ <%= label_tag "export[formatting][margin][top]", _('Top'), + class: 'control-label' %> + <%= select_tag "export[formatting][margin][top]", + options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a, + @export_settings.formatting[:margin][:top]), + class: 'form-control', + "data-default": @plan.template.settings(:export).formatting[:margin][:top] %> +
+
+ <%= label_tag "export[formatting][margin][bottom]", _('Bottom'), + class: 'control-label' %> + <%= select_tag "export[formatting][margin][bottom]", + options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a, + @export_settings.formatting[:margin][:bottom]), + class: 'form-control', + "data-default": @plan.template.settings(:export).formatting[:margin][:bottom] %> +
+
+ <%= label_tag "export[formatting][margin][left]", _('Left'), + class: 'control-label' %> + <%= select_tag "export[formatting][margin][left]", + options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a, + @export_settings.formatting[:margin][:left]), + class: 'form-control', + "data-default": @plan.template.settings(:export).formatting[:margin][:left] %> +
+
+ <%= label_tag "export[formatting][margin][right]", _('Right'), + class: 'control-label' %> + <%= select_tag "export[formatting][margin][right]", + options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a, + @export_settings.formatting[:margin][:right]), + class: 'form-control', + "data-default": @plan.template.settings(:export).formatting[:margin][:rigth] %> +
+
+
+ + <%= button_tag(_('Download Plan'), class: "btn btn-primary", type: "submit") %> +<% end %> diff --git a/app/views/plans/_edit_details.html.erb b/app/views/plans/_edit_details.html.erb new file mode 100644 index 0000000..02e60a5 --- /dev/null +++ b/app/views/plans/_edit_details.html.erb @@ -0,0 +1,191 @@ +
+
+ <%= form_for plan, html: {method: :put, class: 'form-horizontal edit_plan' } do |f| %> +
+
+ <%= f.label(:title, _('Project title'), class: 'control-label') %> +
+
+ <%= f.text_field(:title, class: "form-control", "aria-required": true, 'data-toggle': 'tooltip', + title: _('If applying for funding, state the name exactly as in the grant proposal.')) %> +
+ <%= f.hidden_field :visibility %> + <%= f.label(:is_test, raw("#{check_box_tag(:is_test,1, @plan.is_test?)} #{_('mock project for testing, practice, or educational purposes')}"), class: 'control-label') %> +
+
+
+
+
+ <%= f.label(:funder_name, _('Funder'), class: 'control-label') %> +
+
+ <%= f.text_field( + :funder_name, + class: "form-control", + "aria-required": false) %> +
+
+
+
+ <%= f.label(:grant_number, _('Grant number'), class: 'control-label') %> +
+
+ <%= f.text_field(:grant_number, class: "form-control", "aria-required": false, 'data-toggle': 'tooltip', + title: _('Grant reference number if applicable [POST-AWARD DMPs ONLY]')) %> +
+
+
+
+ <%= f.label(:description, _('Project abstract'), class: 'control-label') %> +
+
"> + <%= f.text_area( + :description, rows: 6, + class: 'form-control tinymce', + "aria-required": false) %> +
+
+
+
+ <%= f.label(:identifier, _('ID'), class: 'control-label') %> +
+
+ <%= f.text_field(:identifier, class: "form-control", "aria-required": false, 'data-toggle': "tooltip", + title: _('A pertinent ID as determined by the funder and/or organisation.')) %> +
+
+ +

<%= _('Principal investigator') %>

+
+
+ <%= f.label(:principal_investigator, _('Name'), class: 'control-label') %> +
+
+ <%= f.text_field( + :principal_investigator, + class: "form-control", + "aria-required": false) %> +
+
+
+
+ <%= f.label(:principal_investigator_identifier, _('ORCID iD'), class: 'control-label') %> +
+
+ <%= f.text_field( + :principal_investigator_identifier, + class: "form-control", + "aria-required": false) %> +
+
+
+
+ <%= f.label(:principal_investigator_email, _('Email'), class: 'control-label') %> +
+
+ <%= f.email_field( + :principal_investigator_email, + class: "form-control", + "aria-required": false, + "data-validation": "email") %> +
+
+
+
+ <%= f.label(:principal_investigator_phone, _('Phone'), class: 'control-label') %> +
+
+ <%= f.phone_field( + :principal_investigator_phone, + class: "form-control", + "aria-required": false) %> +
+
+ +

<%= _('Data contact person') %>

+
+ <% checked = ((@plan.data_contact.present? || @plan.data_contact_phone.present? || @plan.data_contact_email.present?) ? 1 : 0) %> + <%= label_tag(:show_data_contact, raw("#{check_box_tag(:show_data_contact, checked, checked == 0)} #{_('Same as Principal Investigator')}"), class: 'control-label') %> +
+
+
+ <%= f.label(:data_contact, _('Name'), class: 'control-label') %> +
+
+ <%= f.text_field( + :data_contact, + class: "form-control", + "aria-required": false) %> +
+
+
+
+ <%= f.label(:data_contact_email, _('Email'), class: 'control-label') %> +
+
+ <%= f.email_field( + :data_contact_email, + class: "form-control", + "aria-required": false, + "data-validation": "email") %> +
+
+
+
+ <%= f.label(:data_contact_phone, _('Phone'), class: 'control-label') %> +
+
+ <%= f.phone_field( + :data_contact_phone, + class: "form-control", + "aria-required": false) %> +
+
+ <%= f.button(_('Submit'), class: "btn btn-default", type: "submit") %> +
+
+

<%= _('Plan Guidance Configuration') %>

+

<%= _('To help you write your plan, %{application_name} can show you guidance from a variety of organisations.') % + {application_name: Rails.configuration.branding[:application][:name]} %> +

+

<%= _('Select up to 6 organisations to see their guidance.') %>

+
    + <%= render partial: "guidance_choices", + locals: {choices: @important_ggs, form: f, + current_selections: @selected_guidance_groups} %> +
+ +

<%= _('Find guidance from additional organisations below') %>

+ <%= link_to 'See the full list', '#', 'data-toggle' => 'modal', 'data-target' => '#modal-full-guidances', class: 'modal-guidances-window' %> + +
+ <%= f.button(_('Submit'), class: "btn btn-default", type: "submit") %> +
+ + + + <% end %> +
diff --git a/app/views/plans/_export.html.erb b/app/views/plans/_export.html.erb deleted file mode 100644 index cd7ed05..0000000 --- a/app/views/plans/_export.html.erb +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/app/views/plans/_form.html.erb b/app/views/plans/_form.html.erb deleted file mode 100644 index 4c4404a..0000000 --- a/app/views/plans/_form.html.erb +++ /dev/null @@ -1,32 +0,0 @@ -<%= form_for @plan, :html => { :class => 'form-horizontal' } do |f| %> -
- <%= f.label :locked, :class => 'control-label' %> -
- <%= f.check_box :locked, :class => 'check_box' %> -
-
-
- <%= f.label :project_id, :class => 'control-label' %> -
- <%= f.number_field :project_id, :class => 'number_field' %> -
-
-
- <%= f.label :version_id, :class => 'control-label' %> -
- <%= f.number_field :version_id, :class => 'number_field' %> -
-
-
- <%= f.label :slug, :class => 'control-label' %> -
- <%= f.text_field :slug, :class => 'text_field' %> -
-
- -
- <%= f.submit nil, :class => 'btn btn-primary' %> - <%= link_to _('helpers.links.cancel'), - plans_path, :class => 'btn' %> -
-<% end %> diff --git a/app/views/plans/_guidance_choices.html.erb b/app/views/plans/_guidance_choices.html.erb new file mode 100644 index 0000000..bf00fb3 --- /dev/null +++ b/app/views/plans/_guidance_choices.html.erb @@ -0,0 +1,23 @@ +<% choices.each do |org, groups| %> + <% if groups && groups.size == 1 %> +
  • + <%= check_box_tag "guidance_group_ids[]", groups[0].id, + current_selections.include?(groups[0].id), class: 'guidance-choice' %> + <%= org.name %> +
  • + <% elsif groups %> +
  • + <%= org.name %> +
      + <% groups.each do |group| %> +
    • + └─ + <%= check_box_tag "guidance_group_ids[]", group.id, + current_selections.include?(group.id), class: 'guidance-choice' %> + <%= group.name %> +
    • + <% end %> +
    +
  • + <% end%> +<% end %> \ No newline at end of file diff --git a/app/views/plans/_guidance_settings.html.erb b/app/views/plans/_guidance_settings.html.erb deleted file mode 100644 index 7b48331..0000000 --- a/app/views/plans/_guidance_settings.html.erb +++ /dev/null @@ -1,9 +0,0 @@ - \ No newline at end of file diff --git a/app/views/plans/_navigation.html.erb b/app/views/plans/_navigation.html.erb new file mode 100644 index 0000000..d21eab0 --- /dev/null +++ b/app/views/plans/_navigation.html.erb @@ -0,0 +1,33 @@ +<% phases = Phase.titles(plan.template.id) %> + +
    +
    +
    +
    + <%= yield %> +
    +
    +
    +
    diff --git a/app/views/plans/_overview_details.html.erb b/app/views/plans/_overview_details.html.erb new file mode 100644 index 0000000..6949ebf --- /dev/null +++ b/app/views/plans/_overview_details.html.erb @@ -0,0 +1,54 @@ +
    +
    +

    <%= plan.template.title %>

    +
    +
    +
    +
    +

    + <%= _('This plan is based on the "%{template_title}" template provided by %{org_name}.') %{ :template_title => plan.template.title, :org_name => plan.template.org.name } %> +

    +

    + <%= raw(plan.template.description) %> +

    +
    +
    +
    +
    + <% if plan.template.phases.size == 1 %> + <%= render(partial: '/phases/overview', locals: { plan_id: plan.id, phase: plan.template.phases.first }) %> + <% else %> +
    + <% plan.template.phases.each do |p| %> +
    + +
    +
    + <%= render(partial: '/phases/overview', locals: { plan_id: plan.id, phase: p }) %> +
    +
    +
    + <% end %> +
    + <% end %> +
    +
    \ No newline at end of file diff --git a/app/views/plans/_plan_details.html.erb b/app/views/plans/_plan_details.html.erb deleted file mode 100644 index b23b647..0000000 --- a/app/views/plans/_plan_details.html.erb +++ /dev/null @@ -1,337 +0,0 @@ -<% javascript('plans/edit.js') %> - -
    - - - - -
    "> - <%= form_for @plan, url: {controller: :plans, action: :update }, - html: {method: :put, class: "roadmap-form"} do |f| %> -
    - - -
    - <%= _('Please fill in the basic project details below') %> - -
    - <%= f.label :title, _('Plan name') %> - <%= f.text_field :title, class: "input-large has-tooltip", 'data-toggle': "tooltip", - 'title': _('If applying for funding, state the name exactly as in the grant proposal.') %> -
    -
    - <%= f.label :identifier, _('ID') %> - <%= f.text_field :identifier, class: 'input-medium has-tooltip', 'data-toggle': "tooltip", - 'title': _('A pertinent ID as determined by the funder and/or institution.') %> -
    -
    - <%= f.label :grant_number, _('Grant number') %> - <%= f.text_field :grant_number, class: 'input-medium has-tooltip', - 'data-toggle': "tooltip", - 'title': _('Grant reference number if applicable [POST-AWARD DMPs ONLY]') %> -
    -
    - <%= f.label :principal_investigator, _('Principal Investigator/Researcher') %> - <%= f.text_field :principal_investigator, class: 'input-medium has-tooltip', - 'data-toggle': "tooltip", - 'title': _('Name of Principal Investigator(s) or main researcher(s) on the project.') %> -
    -
    - <%= f.label :principal_investigator_identifier, _('Principal Investigator/Researcher ID') %> - <%= f.text_field :principal_investigator_identifier, class: 'input-medium has-tooltip', - 'data-toggle': "tooltip", 'title': _('E.g ORCID http://orcid.org/.') %> -
    -
    - <%= f.label :data_contact, _('Plan data contact') %> - <%= f.text_field :data_contact, class: 'input-medium has-tooltip', - 'data-toggle': "tooltip", - 'title': _('Name (if different to above), telephone and email contact details') %> -
    -
    - <%= f.label :description, _('Description') %> - <%= f.text_area :description, { rows: 7, class: 'input-large has-tooltip', - 'data-toggle': "tooltip", 'data-html': "true", - 'title': _("

    Questions to consider:

    • - What is the nature of your research project?
    • - What research questions are you addressing?
    • - For what purpose are the data being collected or created?

    Guidance:

    Briefly summarise the type of study (or studies) to help others understand the purposes for which the data are being collected or created.

    ")} %> -
    - -
    - - - -
    -
    -
    - - <%end%> -
    - - - -
    "> -
    -

    -
    - - -
    - -
    - - -
    - - - - - - - - - - - - - - - - - - - - <% if !@plan.principal_investigator_identifier.nil? && @plan.principal_investigator_identifier != "" then %> - - - - - <%end%> - - - - - - - - -
    <%= _('Plan name') %><%= @plan.title %>
    <%= _('ID') %> - <% if !@plan.identifier.nil? && @plan.identifier != "" then %> - <%= @plan.identifier %> - <%else%> - - - <%end%> -
    <%= _('Grant number') %> - <% if !@plan.grant_number.nil? && @plan.grant_number!= "" then %> - <%= @plan.grant_number %> - <%else%> - - - <%end%> -
    <%= _('Principal Investigator/Researcher') %><% if !@plan.principal_investigator.nil? && @plan.principal_investigator != "" then %> - <%= @plan.principal_investigator %> - <%else%> - - - <%end%> -
    <%= _('Principal Investigator/Researcher ID') %> - <%= @plan.principal_investigator_identifier %> -
    <%= _('Plan data contact') %><% if !@plan.data_contact.nil? && @plan.data_contact != "" then%> - <%= @plan.data_contact %> - <%else%> - - - <%end%> -
    <%= _('Description') %><% if !@plan.description.nil? && @plan.description != "" then%> - <%= @plan.description %> - <%else%> - - - <%end%> -
    -
    -
    - - - - <%= form_tag( update_guidance_choices_plan_path(@plan), method: :put) do %> -
    -

    <%=_('Guidance Choices')%>

    - - - <% @important_ggs.each do |org, groups| %> - - - <% end %> - <% end %> - - - <% end %> - -
    - <% if groups && groups.size == 1 %> - <%= check_box_tag "guidance_group_ids[]", groups[0].id, @selected_guidance_groups.include?(groups[0].id) %> - <%= org.name %> - <% elsif groups %> - <%= org.name %> - <% groups.each do |group| %> -
    - └─ <%= check_box_tag "guidance_group_ids[]", group.id, @selected_guidance_groups.include?(group.id) %> - <%= group.name %> -
    -
    -
    -
    - -
    -
    - - - <% @all_ggs_grouped_by_org.each do |org, groups| %> - - - <% end %> - <% end %> - - - <% end %> - -
    - <% if groups && groups.size == 1 %> - <%= check_box_tag "guidance_group_ids[]", groups[0].id, @selected_guidance_groups.include?(groups[0].id) %> - <%= org.name %> - <% elsif groups %> - <%= org.name %> - <% groups.each do |group| %> -
    - └─ <%= check_box_tag "guidance_group_ids[]", group.id, @selected_guidance_groups.include?(group.id) %> - <%= group.name %> -
    -
    -
    -
    -
    - <%= submit_tag _('Save'), class: "btn btn-primary"%> - <% end %> - -
    - -

    <%= _('This plan is based on:')%>

    - -

    - <% if based_on.org != plan.template.org %> - <%= _('A version of ') %> "<%= based_on.title %>" <%= based_on.title.downcase.include?(_('template')) ? '' : _('template') %><%= _(' that has been customised by ') %> <%= plan.template.org.name %>. - <% else %> - <%= _('The')%> "<%= plan.template.title %>" <%= (plan.template.is_default ? _('generic template') : plan.template.title.downcase.include?(_('template')) ? '' : _('template')) %> <%= _(' provided by ') %><%= plan.template.org.name %>. - <% end %> -

    - -
    - - - <% phases = plan.template.phases %> - <% if phases.count == 1 %> -
    - <%= raw plan.template.description %> -
    - <% phases.each do |phase| %> -
    - <%= link_to _('Answer questions'), edit_plan_phase_path(plan,phase), :class => 'btn btn-primary' %> - <%= _('Export') %> -
    - <%= render :partial => "plans/export", locals: {plan: plan, phase: phases[0] } %> -
    -

    <%= raw phase.description %>

    - <% if phase.sections.any? %> - - - - - - - - - <% phase.sections.each do |section| %> - - - - - <%end%> - -
    <%= _('Sections')%><%= _('Questions')%>
    -

    <%= section.title %>

    -
    - <% if section.questions.any? %> -
      - <% section.questions.each do |ques|%> -
    • - <%= raw ques.text %> -
    • - <%end%> -
    - <%end%> -
    - <%end%> - <%end%> - <%else%> -
    - <%= raw plan.template.description %> -
    - <% phases.each do |phase| %> -
    -
    - -
    -
    -
    - <%= link_to _('Answer questions'), edit_plan_phase_path(plan,phase), :class => 'btn btn-primary' %> - <%= _('Export') %> -
    - <%= render :partial => "plans/export", locals: {plan: plan, phase: phase} %> -
    -

    <%= raw phase.description %> -

    - - <% if phase.sections.any? %> - - - - - - - - - <% phase.sections.each do |section| %> - - - - - <%end%> - -
    <%= _('Sections')%><%= _('Questions')%>
    -

    <%= section.title %>

    -
    - <% if section.questions.any? %> -
      - <% section.questions.each do |ques|%> -
    • - - <%= raw ques.text %> -
    • - <%end%> -
    - <%end%> -
    - <%end%> -
    -
    -
    -
    - <%end%> -
    - diff --git a/app/views/plans/_plan_list_head.html.erb b/app/views/plans/_plan_list_head.html.erb deleted file mode 100644 index 6b05f86..0000000 --- a/app/views/plans/_plan_list_head.html.erb +++ /dev/null @@ -1,6 +0,0 @@ - - <% ['name', 'owner', 'shared', 'last_edited'].each do |column| %> - <%= plan_list_column_heading(column) %> - <% end %> - <%= _('Select an action')%> - diff --git a/app/views/plans/_plan_list_item.html.erb b/app/views/plans/_plan_list_item.html.erb deleted file mode 100644 index ad1dff2..0000000 --- a/app/views/plans/_plan_list_item.html.erb +++ /dev/null @@ -1,27 +0,0 @@ - - <% ['name', 'owner', 'shared', 'last_edited'].each do |column| %> - <%= plan_list_column_body(column, plan) %> - <% end %> - - <% if plan.editable_by?(current_user.id) then %> - <%= link_to _('Edit'), plan_path(plan), :class => "dmp_table_link"%> - - <% if plan.administerable_by?(current_user.id) then %> - <%= link_to _('Share'), share_plan_path(plan), :class => "dmp_table_link"%> - <% end %> - - <%= link_to _('Export'), show_export_plan_path(plan), :class => "dmp_table_link"%> - - <% if plan.administerable_by?(current_user.id) then %> - <%= link_to _('Delete'), plan_path(plan), :class => "dmp_table_link", - :method => :delete, :data => { - :confirm => _('Are you sure you wish to delete this plan? If the plan is being shared with other users, by deleting it from your list, the plan will be deleted from their plan list as well') - }%> - <% end %> - <% else %> - <%= link_to _('View'), plan_path(plan), :class => "dmp_table_link"%> - <%= link_to _('Export'), show_export_plan_path(plan), :class => "dmp_table_link"%> - <% end %> - - - diff --git a/app/views/plans/_plan_nav_tabs.html.erb b/app/views/plans/_plan_nav_tabs.html.erb deleted file mode 100644 index 3186154..0000000 --- a/app/views/plans/_plan_nav_tabs.html.erb +++ /dev/null @@ -1,39 +0,0 @@ - - diff --git a/app/views/plans/_plan_title.html.erb b/app/views/plans/_plan_title.html.erb deleted file mode 100644 index d134aa1..0000000 --- a/app/views/plans/_plan_title.html.erb +++ /dev/null @@ -1,8 +0,0 @@ - -
    -
    -

    - <%= plan.title %> -

    -
    -
    diff --git a/app/views/plans/_progress.html.erb b/app/views/plans/_progress.html.erb index 256c00e..1ad2218 100644 --- a/app/views/plans/_progress.html.erb +++ b/app/views/plans/_progress.html.erb @@ -1,9 +1,14 @@ +<%# locals: { plan, current_phase } %> <% - nanswers = plan.num_answered_questions() - nquestions = plan.num_questions() + nanswers = current_phase.num_answered_questions(plan) + nquestions = current_phase.num_questions() + value=(nanswers.to_f/nquestions*100).round(2) %> -<% answered = %(#{nanswers}/#{nquestions})%> -
    - <%= answered -%> <%= _('questions answered')%> - +
    ;"> + <%= "#{nanswers}/#{nquestions} #{_('answered')}" %>
    + + diff --git a/app/views/plans/_share_form.html.erb b/app/views/plans/_share_form.html.erb new file mode 100644 index 0000000..46002cf --- /dev/null +++ b/app/views/plans/_share_form.html.erb @@ -0,0 +1,108 @@ +

    <%= _('Set plan visibility') %>

    +

    <%= _('Public or organisational visibility is intended for finished plans. You must answer at least %{percentage}%% of the questions to enable these options. Note: test plans are set to private visibility by default.') % { :percentage => Rails.application.config.default_plan_percentage_answered } %>

    +<% allow_visibility = @plan.visibility_allowed? %> +<%= form_for(@plan, url: visibility_plan_path, method: :post, html: { id: 'set_visibility', remote: true }) do |f| %> + > +
    +
    + <%= f.label :visibility_privately_visible, raw("#{f.radio_button :visibility, :privately_visible}\ + #{_('Private: visible to me, specified collaborators and administrators at my organisation')}") %> +
    +
    + <%= f.label :visibility_organisationally_visible, raw("#{f.radio_button :visibility, :organisationally_visible} #{_('Organisation: anyone at my organisation can view')}") %> +
    +
    + <%= f.label :visibility_publicly_visible, raw("#{f.radio_button :visibility, :publicly_visible} #{_('Public: anyone can view')}") %> +
    +
    +
    + <%= f.submit(_('Update'), style: 'display:none') %> +
    + +<% end %> + +

    <%= _('Manage collaborators')%>

    +

    <%= _('Invite specific people to read, edit, or administer your plan. Invitees will receive an email notification that they have access to this plan.') %>

    +<% administerable = @plan.administerable_by?(current_user) %> +<% if @plan.roles.any? then %> + + + + + + <% if administerable %> + + <% end %> + + + + <% @plan_roles.each do |role| %> + + + + <% end %> + + <% end %> + +
    <%= _('Email address')%><%= _('Permissions')%><%= _('Actions') %>
    <%= role.user.name %> + <% if role.creator? %> + <%= _('Owner') %> + <% else %> + <% if administerable && role.user != current_user %> + <%= form_for role, url: { controller: :roles, action: :update, id: role.id }, remote: true, html: { method: :put } do |f| %> +
    + <%= f.hidden_field :id %> + <%= f.select :access_level, {"#{_('Co-owner')}": 3, "#{_('Editor')}": 2, "#{_('Read only')}": 1}, {}, {id: "#{role.id}-can-edit", class: "toggle-existing-user-access" } %> +
    + <% end %> + <% else %> + <%= display_role(role) %> + <% end %> + <% end %> + <% if administerable %> +
    + <% unless role.creator? || role.user == current_user then %> + <%= link_to _('Remove'), role, method: :delete, data: { confirm: _('Are you sure?') }, :class => "a-orange" %> + <% end %> +
    +<% end %> + +

    <%= _('Invite collaborators') %>

    +<% new_role = Role.new %> +<% new_role.plan = @plan %> +<%= form_for new_role, url: {controller: :roles, action: :create }, html: {method: :post} do |f| %> +
    + <%= f.hidden_field :plan_id %> + <%= f.fields_for :user do |user| %> + <%= user.label :email, _('Email'), class: 'control-label' %> + <%= user.email_field :email, for: :user, name: "user", class: "form-control", "aria-required": true %> + <% end %> +
    + +
    +

    <%= _('Permissions') %>

    +
    + <%= f.label :access_level, raw("#{f.radio_button :access_level, 3, "aria-required": true} #{_('Co-owner: can edit project details, change visibility, and add collaborators')}") %> +
    +
    + <%= f.label :access_level, raw("#{f.radio_button :access_level, 2} #{_('Editor: can comment and make changes')}") %> +
    +
    + <%= f.label :access_level, raw("#{f.radio_button :access_level, 1} #{_('Read only: can view and comment, but not make changes')}") %> +
    + + <%= f.button(_('Submit'), class: "btn btn-primary", type: "submit") %> +
    +
    +<% end %> + +<% if plan.owner_and_coowners.include?(current_user) && current_user.org.present? && current_user.org.feedback_enabled? %> +

    <%= _('Request expert feedback') %>

    +

    <%= _('Click below to give data management staff at your organisation access to read and comment on your plan.') %>

    +

    <%= _('You can continue to edit and download the plan in the interim.') %>

    + +
    + <%= link_to _('Request feedback'), request_feedback_plan_path, class: "btn btn-default#{' disabled' if @plan.feedback_requested?}" %> + <%= _("Feedback has been requested.") if @plan.feedback_requested? %> +
    +<% end %> \ No newline at end of file diff --git a/app/views/plans/_show_details.html.erb b/app/views/plans/_show_details.html.erb new file mode 100644 index 0000000..8c9d78d --- /dev/null +++ b/app/views/plans/_show_details.html.erb @@ -0,0 +1,32 @@ +
    +
    <%= _('Project Title') %>
    +
    <%= plan.title %>
    +
    <%= _('Funder') %>
    +
    <%= plan.funder_name %>
    +
    <%= _('Grant Number') %>
    +
    <%= plan.grant_number %>
    +
    <%= _('Project Abstract') %>
    +
    <%= raw plan.description %>
    +
    <%= _('ID') %>
    +
    <%= plan.identifier %>
    +
    +
    +

    <%= _('Principal Investigator') %>

    +
    +
    <%= _('Name') %>
    +
    <%= plan.principal_investigator %>
    +
    <%= _('ORCID iD') %>
    +
    <%= plan.principal_investigator_identifier %>
    +
    <%= _('Email') %>
    +
    <%= plan.principal_investigator_email %>
    +
    +
    +

    <%= _('Data Contact Person') %>

    +
    +
    <%= _('Name') %>
    +
    <%= plan.data_contact %>
    +
    <%= _('Phone') %>
    +
    <%= plan.data_contact_phone %>
    +
    <%= _('Email') %>
    +
    <%= plan.data_contact_email %>
    +
    diff --git a/app/views/plans/_toolbar.html.erb b/app/views/plans/_toolbar.html.erb deleted file mode 100644 index f324927..0000000 --- a/app/views/plans/_toolbar.html.erb +++ /dev/null @@ -1,8 +0,0 @@ -
    -
    - <%= search_field_tag(:filter, params[:filter], placeholder: _('Filter plans')) %> - - <%= _('Cancel') -%> - -
    -
    diff --git a/app/views/plans/create.html.erb b/app/views/plans/create.html.erb deleted file mode 100644 index 875aaa2..0000000 --- a/app/views/plans/create.html.erb +++ /dev/null @@ -1,20 +0,0 @@ - -
    - -

    Choose one of these templates:

    - - -<%= form_tag(controller: "plans", action: "create", method: "post", class: "choose_template_form") do %> -
      - <% @templates.each do |t| %> -
    • - <%= radio_button_tag(:template_id, t.id) %> - <%= label_tag("template_id_#{t.id}", t.title) %> -
    • - <% end %> -
    - <%= submit_tag("Create Plan") %> -<% end %> - - -
    diff --git a/app/views/plans/create.js.erb b/app/views/plans/create.js.erb deleted file mode 100644 index 11be185..0000000 --- a/app/views/plans/create.js.erb +++ /dev/null @@ -1,16 +0,0 @@ -$("#available-templates").fadeOut(); - -<% if @templates.nil? %> - $(".main_page_content").prepend('

    <%= raw notice %>

    '); - -<% elsif @templates.count > 1 %> - // Clear the existing contents of the modal and then display template combobox - $("#available-templates").html("<%= escape_javascript(render partial: 'available_templates') %>").fadeIn(); - -<% else %> - // Only one template so fill in the id - $("#plan_template_id").val("<%= @templates.first.id %>"); -<% end %> - -// Force the submit button toggle -$("#plan_template_id").change(); \ No newline at end of file diff --git a/app/views/plans/download.html.erb b/app/views/plans/download.html.erb new file mode 100644 index 0000000..d10ee74 --- /dev/null +++ b/app/views/plans/download.html.erb @@ -0,0 +1,12 @@ +
    +
    + +

    <%= @plan.title %>

    +
    +
    + +
    +
    + <%= render partial: 'download_form', layout: 'navigation', locals: { plan: @plan } %> +
    +
    diff --git a/app/views/plans/edit.html.erb b/app/views/plans/edit.html.erb deleted file mode 100644 index 96cfa9c..0000000 --- a/app/views/plans/edit.html.erb +++ /dev/null @@ -1,154 +0,0 @@ -<%- model_class = Plan -%> -<% javascript "plans.js" %> - - - - -<%= render :partial => "plan_title", locals: {plan: @plan} %> -

    <%=@readonly%>

    - - -<% status = @plan.status %> -
    - <%space_used = status["space_used"].to_i - space_title = _('approx. %{space_used}%% of available space used (max %{num_pages} pages)') % { space_used: space_used, num_pages: @plan.template.settings(:export).max_pages } - answered = %(#{status["num_answers"]}/#{status["num_questions"]})%> -
    - <%= answered -%> <%= _('questions answered')%> - ;" title="<%= answered -%> <%= _('questions answered') %>"> -
    -
    -
    - = 100 ? "class=bar-full-text" : "" -%>><%= space_title -%> - " style="width: <%= space_used -%>%;" title="<%= space_title -%>"> -
    -
    - - -<%= render :partial => "plan_nav_tabs", locals: {plan: @plan, active: @phase.id } %> - - - - -
    -
    - <% sections = @phase.sections %> - <% sections.each do |section| %> - - - <% if session[:question_id_comments].to_i != 0 then %> - <% question_from_comment = Question.find(session[:question_id_comments])%> - <% if section.id == question_from_comment.section_id then %> - <%= hidden_field_tag :comment_section_id, question_from_comment.section_id, :class => "comment_section_id" %> - <%end%> - <% end%> - - - -
    - - <% num_section_questions = @plan.status["sections"][section.id]["num_questions"] %> - <% num_section_answers = @plan.status["sections"][section.id]["num_answers"] %> - <% question_word = "questions" %> - <% if num_section_questions == 1 then %> - <% question_word = "question" %> - <% end %> - <% section_status = "#{num_section_questions} #{question_word}, #{num_section_answers} answered" %> - - - - - -
    -
    - <%= raw section.description %> -
    -
    - - -
    -

    <%= t ('helpers.loading')%>

    -
    - - - - -
    - <% section.questions.order("number").each do |question| %> - <% if question.id == session[:question_id_comments].to_i then id_css = "current_question" end %> -
    - <% partialname = "answer_form" - if @readonly - partialname += "_ro" - end - %> - - <%= render partial: partialname, - locals: { - plan: @plan, - question: question, - last_question_id: section.questions.order("number DESC").first.id - } - %> -
    - <% end %> -
    - -
    -
    -
    - - - - <% end %> -
    - <%= tinymce :content_css => asset_path('application.css') %> -
    - -<%= render :partial => "export", locals: {plan: @plan, phase: @phase} %> - - -<% session.delete(:question_id_comments)%> diff --git a/app/views/plans/export.docx.erb b/app/views/plans/export.docx.erb deleted file mode 100644 index 05f5a3c..0000000 --- a/app/views/plans/export.docx.erb +++ /dev/null @@ -1,46 +0,0 @@ -

    <%= @plan.title %>

    -

    <%= @plan.template.title %>

    - -<% details = @exported_plan.admin_details %> -<% if details.present? %> -

    <%= _('Admin Details') %>

    - <% details.each do |field| %> - <% value = @exported_plan.send(field) %> -

    - <%= admin_field_t(field.to_s) %>: - <%= value.present? ? value : _('-') %> -

    - <% end %> -<% end %> - - - -<% @exported_plan.sections.each do |section| %> - <% questions = @exported_plan.questions_for_section(section.id) - if questions.present? - %> -

    <%= section.title %>

    - <% questions.each do |question| %> -

    <%= raw question.text %>

    - <% answer = @plan.answer(question.id, false) %> - <% if answer.nil? %> -

    <%= _('Question not answered') %>

    - <% else %> - <% q_format = question.question_format %> - <% if q_format.option_based? %> -
      - <% answer.question_options.each do |option| %> -
    • <%= raw option.text %>
    • - <% end %> -
    - <% if question.option_comment_display %> -

    <%= raw answer.text %>

    - <% end %> - <% else %> -

    <%= raw answer.text %>

    - <% end %> - <% end%> -

    - <% end %> - <% end %> -<% end %> diff --git a/app/views/plans/export.erb b/app/views/plans/export.erb new file mode 100644 index 0000000..870f3b9 --- /dev/null +++ b/app/views/plans/export.erb @@ -0,0 +1,2 @@ + +<%= render partial: '/shared/export/plan', locals: local_assigns %> \ No newline at end of file diff --git a/app/views/plans/export.html.erb b/app/views/plans/export.html.erb deleted file mode 100644 index 27a6b1b..0000000 --- a/app/views/plans/export.html.erb +++ /dev/null @@ -1,76 +0,0 @@ -
    -

    <%= @plan.title %>

    -

    <%= @plan.template.title %>

    -
    -
    - <% - details = @exported_plan.admin_details - if details.present? - %> -

    <%= _('Admin Details') %>

    - - - - - - - - - <% - details.each do |field| - value = @exported_plan.send(field) - %> - - - - - <% end %> - -
    <%= _('Title')%><%= _('Description')%>

    - <%= admin_field_t(field.to_s) -%>

    <%= value.present? ? value : _('-') %>
    - <% end %> - <% @exported_plan.sections.each do |section| %> - <% questions = @exported_plan.questions_for_section(section.id) - if questions.present? %> -

    <%= section.title %>

    - - - - - - - - - <% questions.each do |question| %> - - - - - <% end %> - -
    <%= _('Questions')%><%= _('Answers')%>
    -

    - <%= raw question.text %>

    -
    - <% answer = @plan.answer(question.id, false) %> - <% if answer.nil? %> -

    <%= _('Question not answered') %>

    - <% else %> - <% q_format = question.question_format %> - <% if q_format.option_based? %> -
      - <% answer.question_options.each do |option| %> -
    • <%= option.text %>
    • - <% end %> -
    - <% if question.option_comment_display == true %> - <%= raw answer.text %> - <% end %> - <% else%> - <%= raw answer.text %> - <% end%> - <% end %> -
    - <% end %> - <% end %> -
    -
    -
    diff --git a/app/views/plans/export.json.jbuilder b/app/views/plans/export.json.jbuilder deleted file mode 100644 index 7277da5..0000000 --- a/app/views/plans/export.json.jbuilder +++ /dev/null @@ -1,47 +0,0 @@ -json.project_title @exported_plan.plan.project.title -json.phase_title @exported_plan.plan.version.phase.title - -details = @exported_plan.admin_details - -if details.present? - json.details do - details.each do |field| - value = @exported_plan.send(field) - json.set! field.to_sym, value if value.present? - end - end -end - -json.sections do - @exported_plan.sections.each do |section| - json.set! section.number do - json.title section.title - json.questions do - @exported_plan.questions_for_section(section.id).each do |question| - json.set! question.number do - json.question_text question.text - - answer = @exported_plan.plan.answer(question.id, false) - q_format = question.question_format - - if answer.present? - if (q_format.title == "Check box" || q_format.title == "Multi select box" || - q_format.title == "Radio buttons" || q_format.title == "Dropdown") - json.selections do - answer.options.each do |o| - json.set! o.number, o.text - end - end - if question.option_comment_display == true - json.comment_text (answer.try(:text) || 'No comment') - end - else - json.answer_text (answer.try(:text) || 'Question not answered') - end - end - end - end - end - end - end -end diff --git a/app/views/plans/export.pdf.erb b/app/views/plans/export.pdf.erb deleted file mode 100644 index b9ab084..0000000 --- a/app/views/plans/export.pdf.erb +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - <%= @plan.title %> - - - - -

    <%= @plan.title %>

    - <% @exported_plan.admin_details.each do |field| - value = @exported_plan.send(field) - if value.present? %> -

    <%= admin_field_t(field.to_s) -%> <%= value -%>

    - <% else %> -

    <%= admin_field_t(field.to_s) -%> <%= _('-') %>

    - <% end %> - <% end %> - - <% @exported_plan.sections.each do |section| %> - <% questions = @exported_plan.questions_for_section(section.id) - if questions.present? - %> -

    <%= section.title %>

    - <% questions.each do |question| %> -
    -

    <%= raw question.text %>

    - <% answer = @plan.answer(question.id, false) %> - <% if answer.nil? then %> -

    <%= _('Question not answered.') -%>

    - <% else %> - <% q_format = question.question_format %> - <% if q_format.option_based? %> -
      - <% answer.question_options.each do |option| %> -
    • <%= option.text %>
    • - <% end %> -
    - - <% if question.option_comment_display == true then%> - <% if !answer.text.nil? then %> - <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> - <%end%> - <%end%> - <%else%> - - <% if !answer.text.nil? then %> - <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> - <%end%> - <% end %> - <% end %> -
    - <% end %> - <% end %> - <% end %> - - \ No newline at end of file diff --git a/app/views/plans/index.html.erb b/app/views/plans/index.html.erb index a05c523..a00ec53 100644 --- a/app/views/plans/index.html.erb +++ b/app/views/plans/index.html.erb @@ -1,38 +1,44 @@ -<%- model_class = Plan -%> -<% javascript "toolbar.js" %> -

    - <%= _('My plans') %> -

    - - -<% if @plans.count > 0 %> - -

    - <%= raw _('

    The table below lists the plans that you have created, and any that have been shared with you by others.
    These can be edited, shared, exported or deleted at anytime.

    ')%> -

    - - <%= render(partial: "toolbar") %> - - - <%= render(partial: "plan_list_head") %> - - - <% @plans.each do |plan| %> - <%= render(partial: "plan_list_item", locals: { plan: plan } ) %> - <% end %> - -
    - - -<% else %> -

    - <%= raw _("

    Welcome.
    You are now ready to create your first DMP.
    Click the 'Create plan' button below to begin.

    ")%> -

    -<% end %> - - -

    - <%= link_to _('Create plan'), - new_plan_path, - :class => "btn btn-primary" %> -

    +
    +
    +

    <%= _('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 %> +

    +
    +
    +
    +
    + <%= paginable_renderise( + partial: '/paginable/plans/privately_visible', + controller: 'paginable/plans', + action: 'privately_visible', + scope: @plans, + query_params: { sort_field: 'plans.updated_at', sort_direction: :desc }) %> +
    +
    +
    +
    + <%= link_to _('Create plan'), new_plan_path, class: "btn btn-primary" %> +
    +
    +
    +
    + <% if @organisationally_or_publicly_visible.length > 0 && !current_user.org.is_other? %> +

    <%= _('%{org_title} Plans') %{ :org_title => current_user.org.name } %>

    +

    <%= _('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.') %>

    + <%= paginable_renderise( + partial: '/paginable/plans/organisationally_or_publicly_visible', + controller: 'paginable/plans', + action: 'organisationally_or_publicly_visible', + scope: @organisationally_or_publicly_visible, + query_params: { sort_field: 'plans.title', sort_direction: :asc }) %> + <% end %> +
    +
    diff --git a/app/views/plans/new.html.erb b/app/views/plans/new.html.erb index a40b41b..4a9f9c9 100644 --- a/app/views/plans/new.html.erb +++ b/app/views/plans/new.html.erb @@ -1,90 +1,92 @@ -<% javascript "plans/new_plan.js" %> +
    +
    +

    <%= _('Create a new plan') %>

    -
    -

    <%= _('Create a new plan') %>

    - -

    - <%= _("Before you get started, we need to ask a few questions to set you up with the best DMP template for your needs.") %> -

    - - <%= form_for @plan, html: {method: :post, class: "roadmap-form"}, remote: true do |f| %> -
    - - -
    - <%= _('What research project are you planning?') %> - - - - -
    - <%= _('If applying for funding, state the title exactly as in the proposal.') %> -
    -
    - - -
    - <%= _('Primary research organisation') %> - - - - <%= render partial: "shared/accessible_combobox", - locals: {name: 'plan[org_name]', - id: 'plan_org_name', - default_selection: @default_org, - models: @orgs, - attribute: 'name', - classes: 'fixed-width-large'} %> - - - - -
    - - -
    - <%= _('Funding organisation') %> - - - - <%= render partial: "shared/accessible_combobox", - locals: {name: 'plan[funder_name]', - id: 'plan_funder_name', - default_selection: nil, - models: @funders, - attribute: 'name', - classes: 'fixed-width-large'} %> - - - -
    - - -
    -
    - -
    - - - - <%= render partial: 'shared/accessible_submit_button', - locals: {id: 'create_plan_submit', - val: 'Create Plan', - disabled_initially: true, - tooltip: _('You can not continue until you have filled in all of the required information.')} %> - - <% end %> +

    + <%= _("Before you get started, we need some information about your research project to set you up with the best DMP template for your needs.") %> +

    +
    +
    +
    + <%= form_for Plan.new, url: plans_path do |f| %> + +

    <%= _('What research project are you planning?') %>

    +
    +
    + <%= f.text_field(:title, class: 'form-control', 'aria-describedby': 'project-title', 'aria-required': 'true', + 'data-toggle': 'tooltip', + title: _('If applying for funding, state the project title exactly as in the proposal.')) %> +
    +
     
    +
    + <%= label_tag(:is_test, raw("#{check_box_tag(:is_test, "1", false)} #{_('mock project for testing, practice, or educational purposes')}")) %> +
    +
    + + +

    <%= _('Select the primary research organisation') %>

    +
    +
    + <%= render partial: "shared/accessible_combobox", + locals: {name: 'plan[org_name]', + id: 'plan_org_name', + default_selection: @default_org, + models: @orgs, + attribute: 'name', + required: true, + error: _('You must select a research organisation from the list.'), + tooltip: _('Please select a valid research organisation from the list.')} %> +
    +
    - <%= _('or') %> -
    +
    +
    + <%= label_tag(:plan_no_org, raw("#{check_box_tag(:plan_no_org)} #{_('My research organisation is not on the list')} #{_(' or ')} #{_('no research organisation is associated with this plan')}")) %> +
    +
    +
    + + +

    <%= _('Select the primary funding organisation') %>

    +
    +
    + <%= render partial: "shared/accessible_combobox", + locals: {name: 'plan[funder_name]', + id: 'plan_funder_name', + default_selection: nil, + models: @funders, + attribute: 'name', + required: true, + error: _('You must select a funding organisation from the list.'), + tooltip: _('Please select a valid funding organisation from the list.')} %> +
    +
    - <%= _('or') %> -
    +
    +
    + <%= label_tag(:plan_no_funder, raw("#{check_box_tag(:plan_no_funder)} #{_('No funder associated with this plan')}")) %> +
    +
    +
    + + +
    + <%= hidden_field_tag 'template-option-target', org_admin_template_options_path %> +

    <%= _('Which DMP template would you like to use?') %>

    +
    +
    + <%= select_tag(:plan_template_id, "", name: 'plan[template_id]', + class: 'form-control', 'aria-describedby': 'template-selection') %> +
    +
    + <%= _('We found multiple DMP templates corresponding to your funder.') %> +
    +
    +
    + + <%= f.hidden_field(:visibility, value: @is_test ? 'is_test' : Rails.application.config.default_plan_visibility) %> + <%= f.button(_('Create plan'), class: "btn btn-primary", type: "submit") %> + <%= link_to _('Cancel'), plans_path, class: 'btn btn-default' %> + <% end %> +
    +
    diff --git a/app/views/plans/overview.html.erb b/app/views/plans/overview.html.erb new file mode 100644 index 0000000..c9ebc5f --- /dev/null +++ b/app/views/plans/overview.html.erb @@ -0,0 +1,12 @@ +<%# locals: { plan } %> +
    +
    +

    <%= plan.title %>

    +
    +
    + +
    +
    + <%= render partial: 'overview_details', layout: 'plans/navigation', locals: local_assigns %> +
    +
    \ No newline at end of file diff --git a/app/views/plans/share.html.erb b/app/views/plans/share.html.erb index 3c3f301..0a679b3 100644 --- a/app/views/plans/share.html.erb +++ b/app/views/plans/share.html.erb @@ -1,79 +1,12 @@ -<%- model_class = Plan -%> -<% javascript('plans/share.js') %> - -<%= render :partial => "plan_title", locals: {plan: @plan} %> - - -<%= render :partial => "plan_nav_tabs", locals: {plan: @plan, plan_data: @plan_data, active: "share"} %> - -
    - - - <%= raw _('

    You can give other people access to your plan here. There are three permission levels.

    • Users with "read only" access can only read the plan.
    • Editors can contribute to the plan.
    • Co-owners can also contribute to the plan, but additionally can edit the plan details and control access to the plan.

    Add each collaborator in turn by entering their email address below, choosing a permission level and clicking "Add collaborator".

    Those you invite will receive an email notification that they have access to this plan, inviting them to register with %{application_name} if they don\'t already have an account. A notification is also issued when a user\'s permission level is changed.

    ') % { application_name: Rails.configuration.branding[:application][:name] } %> - -
    -

    <%= _('Collaborators')%>

    -
    - <% if @plan.roles.any? then %> - - - - - - - - - - - <% plan_roles = @plan.roles.all %> - <% plan_roles.each do |role| %> - - - - - - <% end %> - -
    <%= _('Email address')%><%= _('Permissions')%>
    <%= role.user.name %> - <% if role.creator? then %> - <%= _('Owner')%> - <% else %> - <%= form_for role, url: {controller: :roles, action: :update, id: role.id }, html: {method: :put} do |f| %> -
    - <%= f.select :access_level, {"#{_('Co-owner')}": 3, "#{_('Editor')}": 2, "#{_('Read only')}": 1}, {}, {id: "#{role.id}-can-edit", class: "toggle-access-level has-tooltip", 'data-toggle': "tooltip", 'title': _('Editors can contribute to plans. Co-owners have additional rights to edit plan details and control access.') } %> -
    - <% end %> - <% end %> -
    - <% unless role.creator? || role.user == current_user then %> - <%= link_to _('Remove user access'), role, method: :delete, data: { confirm: _('Are you sure?') }, :class => "a-orange" %> - <% end %> -
    - <% end %> -
    - -
    - -
    - <% new_role = Role.new %> - <% new_role.plan = @plan %> - <%= form_for new_role, url: {controller: :roles, action: :create }, - html: {method: :post, class: 'roadmap-form'} do |f| %> -
    - <%= _('Add collaborator') %> - - <%= f.hidden_field :plan_id %> - - <%= f.fields_for :user do |user| %> - <%= user.label :email, _('Email') %> - <%= user.email_field :email, for: :user, name: "user", class: "left-indent" %> - <% end %> - - <%= f.label :access_level, _('Permissions') %> - <%= f.select :access_level, [[_('Co-owner'), 3], [ _('Editor') , 2], [ _('Read only'), 1]], {}, {class: 'has-tooltip left-indent', 'data-toggle': "tooltip", 'title': _('Editors can contribute to plans. Co-owners have additional rights to edit plan details and control access.') } %> - -
    - <% end %> -
    +
    +
    + +

    <%= @plan.title %>

    + +
    +
    + <%= render partial: 'share_form', layout: 'navigation', locals: { plan: @plan } %> +
    +
    \ No newline at end of file diff --git a/app/views/plans/show.html.erb b/app/views/plans/show.html.erb index c955251..f44bae3 100644 --- a/app/views/plans/show.html.erb +++ b/app/views/plans/show.html.erb @@ -1,10 +1,16 @@ -<%- model_class = Plan -%> +
    +
    + +

    <%= @plan.title %>

    +
    +
    - -<%= render :partial => "plan_title", locals: {plan: @plan} %> - - -<%= render :partial => "plan_nav_tabs", locals: {plan: @plan, active: "details"} %> - - -<%= render :partial => "plan_details", locals: {plan: @plan, based_on: @based_on, selected_guidance_groups: @selected_guidance_groups, all_guidance_groups: @all_guidance_groups} %> +
    +
    + <% if @plan.editable_by?(current_user) %> + <%= render partial: 'edit_details', layout: 'navigation', locals: {plan: @plan, visibility: @visibility} %> + <% else %> + <%= render partial: 'show_details', layout: 'navigation', locals: { plan: @plan, visibility: @visibility } %> + <% end %> +
    +
    \ No newline at end of file diff --git a/app/views/plans/show_export.html.erb b/app/views/plans/show_export.html.erb deleted file mode 100644 index f6c6f67..0000000 --- a/app/views/plans/show_export.html.erb +++ /dev/null @@ -1,34 +0,0 @@ -<%- model_class = Plan -%> - - -<%= render :partial => "plan_title", locals: {plan: @plan} %> - - -<%= render :partial => "plan_nav_tabs", locals: {plan: @plan, active: "export"} %> - - -
    - - <%= raw _("

    From here you can download your plan in various formats. This may be useful if you need to submit your plan as part of a grant application.
    Select what format you wish to use and click to 'Export'.

    ")%> - - <% if @plan.template.phases.count == 1 %> - <%= render :partial => "/shared/export_links", locals: {plan: @plan, phase: @plan.template.phases.first} %> - <%else%> - <% @plan.template.phases.each do |phase| %> -
    -
    - -
    -
    - <%= render :partial => "/shared/export_links", locals: {plan: @plan, phase: phase} %> -
    -
    -
    -
    - <%end%> - <%end%> -
    \ No newline at end of file diff --git a/app/views/projects/_form.html.erb b/app/views/projects/_form.html.erb deleted file mode 100644 index 48e401f..0000000 --- a/app/views/projects/_form.html.erb +++ /dev/null @@ -1,33 +0,0 @@ -
    - <%= form_for @project, :html => { :class => 'form-horizontal' } do |f| %> -
    - <%= f.label _('Title'), :class => 'left-label' %> -
    - <%= f.text_field :title, :class => 'text_field has-tooltip', 'data-toggle' => "tooltip", 'title' => _('If applying for funding, state the name exactly as in the grant proposal.') %> -
    -
    -
    - <%= f.label _('Grant number'), :class => 'left-label' %> -
    - <%= f.text_field :grant_number, :class => 'text_field has-tooltip', 'data-toggle' => "tooltip", 'title' => _('Grant reference number if applicable [POST-AWARD DMPs ONLY]') %> -
    -
    -
    - <%= f.label _('Note'), :class => 'left-label' %> -
    - <%= f.text_area :note, :class => 'text_area', :rows => 10 %> -
    -
    -
    - <%= f.label _('helpers.is_test'), class: 'left-label' %> -
    - <%= f.check_box :is_test, class: 'check_box has-tooltip', 'data-toggle': 'tooltip' title: _('helpers.project.is_test_help_text') %> -
    -
    -
    - <%= f.submit _('Save'), :class => 'btn btn-primary' %> - <%= link_to _('Cancel'), - projects_path, :class => 'btn' %> -
    - <% end %> -
    diff --git a/app/views/projects/_project_details.html.erb b/app/views/projects/_project_details.html.erb deleted file mode 100644 index 564e67e..0000000 --- a/app/views/projects/_project_details.html.erb +++ /dev/null @@ -1,299 +0,0 @@ - -
    -
    "> -
    -

    - <%= _('This page gives you an overview of your plan. It tells what your plan is based on and gives an overview of the questions that you will be asked.')%> -

    -
    - - -
    - <% if @project.administerable_by(current_user.id) then %> - <%= _('Edit plan details') %> - <% end %> -
    - -
    - - - - - - - - - - - - - - - - - - <% if !@project.principal_investigator_identifier.nil? && @project.principal_investigator_identifier != "" then %> - - - - - <%end%> - - - - - - - - - - - - -
    <%= _('Plan name') %><%= @project.title %>
    <%= _('ID') %> - <% if !@project.identifier.nil? && @project.identifier != "" then %> - <%= @project.identifier %> - <%else%> - - - <%end%> -
    <%= _('Grant number') %> - <% if !@project.grant_number.nil? && @project.grant_number!= "" then %> - <%= @project.grant_number %> - <%else%> - - - <%end%> -
    <%= _('Principal Investigator/Researcher') %><% if !@project.principal_investigator.nil? && @project.principal_investigator != "" then %> - <%= @project.principal_investigator %> - <%else%> - - - <%end%> -
    <%= _('Principal Investigator/Researcher ID') %> - <%= @project.principal_investigator_identifier %> -
    <%= _('Plan data contact') %><% if !@project.data_contact.nil? && @project.data_contact != "" then%> - <%= @project.data_contact %> - <%else%> - - - <%end%> -
    <%= _('Description') %><% if !@project.description.nil? && @project.description != "" then%> - <%= @project.description %> - <%else%> - - - <%end%> -
    <%= _('Visibility') %><%= (@project.is_test? ? _('Test/Practice (your plan is not visible to other users) See our Terms of Use.') : (@project.organisationally_visible? ? _('Organisational (visibile to others within your organisation)') : (@project.publicly_visible? ? _('Public (Your DMP will appear on the Public DMPs page of this site)') : (@project.privately_visible? ? _('Private (owners, co-owners, and administrators only) See our Terms of Use.') : "Defaulting to: #{_('Not specified (will be visible to others within your organisation by default)')}")))) %>
    -
    -
    - <% if @project.administerable_by(current_user.id) then %> -
    "> - -
    -

    - <%= raw _("Please fill in the basic project details below and click 'Update' to save")%> -

    -
    - -
    - <%= semantic_form_for @project, :url => {:controller => :projects, :action => :update }, :html=>{:method=>:put} do |f| %> - <%= f.inputs do %> -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    <%= _('Plan name') %> - <%= f.text_field :title, :class => 'text_field has-tooltip', 'data-toggle' => "tooltip", 'title' => _('If applying for funding, state the name exactly as in the grant proposal.') %> -
    <%= _('ID') %> - <%= f.text_field :identifier, :class => 'text_field has-tooltip', 'data-toggle' => "tooltip", 'title' => _('A pertinent ID as determined by the funder and/or institution.') %> -
    <%= _('Grant number') %> - <%= f.text_field :grant_number, :class => 'text_field has-tooltip', 'data-toggle' => "tooltip", 'title' => _('Grant reference number if applicable [POST-AWARD DMPs ONLY]') %> -
    <%= _('Principal Investigator/Researcher') %> - <%= f.text_field :principal_investigator, :class => 'text_field has-tooltip', 'data-toggle' => "tooltip", 'title' => _('Name of Principal Investigator(s) or main researcher(s) on the project.') %> -
    <%= _('Principal Investigator/Researcher ID') %> - <%= f.text_field :principal_investigator_identifier, :class => 'text_field has-tooltip', 'data-toggle' => "tooltip", 'title' => _('E.g ORCID http://orcid.org/.') %> -
    <%= _('Plan data contact') %> - <%= f.text_field :data_contact, :class => 'text_field has-tooltip', 'data-toggle' => "tooltip", 'title' => _('Name (if different to above), telephone and email contact details') %> -
    <%= _('Description') %> - <%= f.text_area :description, { :rows => 7, :class => 'text_area has-tooltip', 'data-toggle' => "tooltip", 'data-html' => "true", 'title'=> _("

    Questions to consider:

    • - What is the nature of your research project?
    • - What research questions are you addressing?
    • - For what purpose are the data being collected or created?

    Guidance:

    Briefly summarise the type of study (or studies) to help others understand the purposes for which the data are being collected or created.

    ")} %> -
    <%= _('Visibility') %> -
    - /><%= _('Public (Your DMP will appear on the Public DMPs page of this site)') %> -
    -
    - /><%= _('Organisational (visibile to others within your organisation)') %> -
    -
    - /><%= _('Test/Practice (your plan is not visible to other users) See our Terms of Use.') %> -
    -
    - /><%= _('Private (owners, co-owners, and administrators only) See our Terms of Use.') %> -
    -
    -
    - <% end %> - - <%= f.actions do %> -
    - <%= f.submit _('Save'), :class => 'btn btn-primary' %> - <%= _('Cancel') %> -
    - <%end%> - <%end%> -
    - <% end %> -

    <%= _('This plan is based on:')%>

    - - - <%if @project.dmptemplate.organisation.organisation_type.name == constant("organisation_types.funder")%> - - - - - <%end%> - <%if !@project.organisation_id.nil? %> - - - - - <%end%> -
    <%= constant("organisation_types.funder") %><%= @project.dmptemplate.organisation.name %>
    <%= constant("organisation_types.institution") %><%= @project.organisation.name %>
    ->>>>>>> final_schema - - - <% if @project.plans.any? %> - <% if @project.plans.count == 1 then %> - <% @project.plans.each do |plan| %> - -
    - <%= link_to _('Answer questions'), [:edit, @project, plan], :class => 'btn btn-primary' %> - <%= _('Export') %> -
    - <%= render :partial => "plans/export", locals: {plan: plan} %> -
    -

    <%= raw plan.version.phase.description %>

    - <% if !plan.sections.nil? %> - - - - - - - - - <% plan.sections.each do |section| %> - - - - - <%end%> - -
    <%= _('Sections')%><%= _('Questions')%>
    -

    <%= section.title %>

    -
    - <% if section.questions.any? %> - <% questions = section.questions.sort_by(&:number) %> -
      - <% questions.each do |ques|%> -
    • - <%= raw ques.text %> -
    • - <%end%> -
    - <%end%> -
    - <%end%> - <%end%> - <%else%> -
    - <%= raw @project.dmptemplate.description %> -
    - <% @project.plans.each do |plan| %> -
    -
    - -
    -
    -
    - <%= link_to _('Answer questions'), [:edit, @project, plan], :class => 'btn btn-primary' %> - <%= _('Export') %> -
    - <%= render :partial => "plans/export", locals: {plan: plan} %> -
    -

    <%= raw plan.version.phase.description %> -

    - - <% if !plan.sections.nil? %> - - - - - - - - - <% plan.sections.each do |section| %> - - - - - <%end%> - -
    <%= _('Sections')%><%= _('Questions')%>
    -

    <%= section.title %>

    -
    - <% if section.questions.any? %> - <% questions = section.questions.sort_by(&:number) %> -
      - <% questions.each do |ques|%> -
    • - - <%= ques.text %> -
    • - <%end%> -
    - <%end%> -
    - <%end%> -
    -
    -
    -
    - <%end%> - <%end%> - <%end%> -
    diff --git a/app/views/projects/_project_nav_tabs.html.erb b/app/views/projects/_project_nav_tabs.html.erb deleted file mode 100644 index e9f1b3d..0000000 --- a/app/views/projects/_project_nav_tabs.html.erb +++ /dev/null @@ -1,46 +0,0 @@ - - diff --git a/app/views/projects/_project_title.html.erb b/app/views/projects/_project_title.html.erb deleted file mode 100644 index 9a18861..0000000 --- a/app/views/projects/_project_title.html.erb +++ /dev/null @@ -1,8 +0,0 @@ - -
    -
    -

    - <%= project.title %> -

    -
    -
    \ No newline at end of file diff --git a/app/views/projects/edit.html.erb b/app/views/projects/edit.html.erb deleted file mode 100644 index c5e940d..0000000 --- a/app/views/projects/edit.html.erb +++ /dev/null @@ -1,9 +0,0 @@ -<%- model_class = Project -%> - - -<%= render :partial => "/projects/project_title", locals: {project: @project} %> - - -<%= render :partial => "project_nav_tabs", locals: {project: @project, active: "edit_project"} %> - -<%= render :partial => 'form' %> diff --git a/app/views/projects/export.html.erb b/app/views/projects/export.html.erb deleted file mode 100644 index 722883c..0000000 --- a/app/views/projects/export.html.erb +++ /dev/null @@ -1,41 +0,0 @@ -<%- model_class = Project -%> - - -<%= render :partial => "/projects/project_title", locals: {project: @project} %> - - -<%= render :partial => "project_nav_tabs", locals: {project: @project, active: "export_page"} %> - - -
    - - <%= raw _("

    From here you can download your plan in various formats. This may be useful if you need to submit your plan as part of a grant application.
    Select what format you wish to use and click to 'Export'.

    ")%> - - - <% if @project.plans.any? %> - <% if @project.plans.count == 1 then %> - <% @project.plans.each do |plan| %> - <%= render :partial => "/shared/export_links", locals: {plan: plan} %> - <% end %> - <%else%> - <% @project.plans.each do |plan| %> -
    -
    - -
    -
    - <%= render :partial => "/shared/export_links", locals: {plan: plan} %> -
    -
    -
    -
    - <%end%> - <%end%> - <%end%> - -
    \ No newline at end of file diff --git a/app/views/projects/share.html.erb b/app/views/projects/share.html.erb deleted file mode 100644 index 95fdf57..0000000 --- a/app/views/projects/share.html.erb +++ /dev/null @@ -1,71 +0,0 @@ -<%- model_class = Project -%> - - -<%= render :partial => "/projects/project_title", locals: {project: @project} %> - - -<%= render :partial => "project_nav_tabs", locals: {project: @project, active: "share_project"} %> -
    - - - <%= raw _('

    You can give other people access to your plan here. There are three permission levels.

    • Users with "read only" access can only read the plan.
    • Editors can contribute to the plan.
    • Co-owners can also contribute to the plan, but additionally can edit the plan details and control access to the plan.

    Add each collaborator in turn by entering their email address below, choosing a permission level and clicking "Add collaborator".

    Those you invite will receive an email notification that they have access to this plan, inviting them to register with %{application_name} if they don\'t already have an account. A notification is also issued when a user\'s permission level is changed.

    ') % { application_name: => Rails.configuration.branding[:application][:name]) }%> - -
    -

    <%= _('Collaborators')%>

    -
    - <% if @project.roles.any? then %> - - - - - - - - - - - <% project_people = @project.roles.all.select {|pu| pu.user_id != nil}%> - <% project_people.each do |group| %> - - - - - - <% end %> - -
    <%= _('Email address')%><%= _('Permissions')%>
    <%= group.user.name %> - <% if group.project_creator then %> - <%= _('Owner')%> - <% else %> - <%= form_for group, :url => {:controller => :roles, :action => :update, :id => group.id }, :html=>{:method=>:put} do |f| %> - <%= f.select :access_level, {_('Co-owner') => 3, _('Edit') => 2, _('Read only') => 1}, {}, {:id => "#{group.id}-can-edit", :class => "toggle-existing-user-access has-tooltip", 'data-toggle' => "tooltip", 'title' => _('Editors can contribute to plans. Co-owners have additional rights to edit plan details and control access.') } %> - <% end %> - <% end %> - - <% unless group.project_creator || group.user == current_user then %> - <%= link_to _('Remove user access'), group, method: :delete, data: { confirm: _('Are you sure?') }, :class => "a-orange" %> - <% end %> -
    - <% end %> -
    - -
    -

    <%= _('Add collaborator')%>

    - -
    - <% new_role = Role.new %> - <% new_role.project = @project %> - <%= semantic_form_for new_role, :url => {:controller => :roles, :action => :create }, :html=>{:method=>:post} do |f| %> - <%= f.inputs do %> - <%= f.input :project_id, :as => :hidden %> - <%= f.email_field :email, :label => false, placeholder: _('Email') %> -

    <%= _('Permissions')%>:

    - <%= f.select :access_level, [[_('Co-owner'), 3], [ _('Edit') , 2], [ _('Read only'), 1]], {}, {:class => 'has-tooltip', 'data-toggle' => "tooltip", 'title' => _('Editors can contribute to plans. Co-owners have additional rights to edit plan details and control access.') } %> - <% end %> - <%= f.actions do %> - <%= f.action :submit, :label => _('Add collaborator'), :button_html => { :class => "btn btn-primary" } %> - <% end %> - <% end %> -
    -
    -
    diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb deleted file mode 100644 index 8faab88..0000000 --- a/app/views/projects/show.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -<%= javascript_include_tag "projects" %> - -<%- model_class = Project -%> - - -<%= render :partial => "/projects/project_title", locals: {project: @project} %> - - -<%= render :partial => "project_nav_tabs", locals: {project: @project, active: "show_project"} %> - - -<%= render "project_details" %> \ No newline at end of file diff --git a/app/views/public_pages/plan_export.pdf.erb b/app/views/public_pages/plan_export.pdf.erb new file mode 100644 index 0000000..870f3b9 --- /dev/null +++ b/app/views/public_pages/plan_export.pdf.erb @@ -0,0 +1,2 @@ + +<%= render partial: '/shared/export/plan', locals: local_assigns %> \ No newline at end of file diff --git a/app/views/public_pages/plan_index.html.erb b/app/views/public_pages/plan_index.html.erb new file mode 100644 index 0000000..3ba73bd --- /dev/null +++ b/app/views/public_pages/plan_index.html.erb @@ -0,0 +1,27 @@ +
    +
    +

    <%= raw _('Public DMPs') %>

    + + <% if @plans.count > 0 %> +

    + <%= _('Public DMPs are plans created using the %{application_name} and shared publicly by their owners. They are not vetted for quality, completeness, or adherence to funder guidelines.') % {application_name: Rails.application.config.branding[:application][:name]} %> +

    + <% else %> +

    + <%= _("There are currently no public DMPs.")%> +

    + <% end %> +
    +
    + +
    +
    + <% if @plans.count > 0 %> + <%= paginable_renderise( + partial: '/paginable/plans/publicly_visible', + controller: 'paginable/plans', + action: 'publicly_visible', + scope: @plans) %> + <% end %> +
    +
    \ No newline at end of file diff --git a/app/views/public_pages/template_export.docx.erb b/app/views/public_pages/template_export.docx.erb new file mode 100644 index 0000000..fea397f --- /dev/null +++ b/app/views/public_pages/template_export.docx.erb @@ -0,0 +1,50 @@ + + + + + + <%= @template.title %> + + + + + <% @template.phases.each do |phase| %> + +
    +

    <%= "#{@template.org.name}: #{@template.title}" %><%= @template.phases.length > 1 ? " - #{phase.title}" : "" %>

    + + <% phase.sections.each do |section| %> +

    <%= section.title %>

    + <% section.questions.each do |question|%> +
    +

    <%= raw question.text %>

    + <% q_format = question.question_format %> + <% if q_format.option_based? %> +
      + <% question.question_options.each do |option| %> +
    • <%= option.text %>
    • + <% end %> +
    + <% end %> +
    + +
    + <% question.annotations.each do |annotation| %> + <% unless annotation.text.chomp.strip.gsub(/<\/?p>/, '').gsub(//, '').blank? %> +
    +

    <%= annotation.type == 0 ? _('Example Answer') : _('Guidance') %>:

    + <%= raw annotation.text %> + <% end %> + <% end %> +
    + +
    + <% end %> + <% end %> + <% end %> + + \ No newline at end of file diff --git a/app/views/public_pages/template_export.pdf.erb b/app/views/public_pages/template_export.pdf.erb new file mode 100644 index 0000000..9561aa1 --- /dev/null +++ b/app/views/public_pages/template_export.pdf.erb @@ -0,0 +1,90 @@ + + + + + + <%= @template.title %> + + + + + <% @template.phases.each do |phase| %> +
    +

    <%= "#{@template.org.name}: #{@template.title}" %><%= @template.phases.length > 1 ? " - #{phase.title}" : "" %>

    + <% phase.sections.each do |section| %> +

    <%= section.title %>

    + <% section.questions.each do |question| %> +
    +

    <%= raw question.text %>

    + <% q_format = question.question_format %> + <% if q_format.option_based? %> +
      + <% question.question_options.each do |option| %> +
    • <%= option.text %>
    • + <% end %> +
    + <% end %> +
    +
    + <% question.annotations.each do |annotation| %> + <% unless annotation.text.chomp.strip.gsub(/<\/?p>/, '').gsub(//, '').blank? %> +

    <%= annotation.type == 0 ? _('Example Answer') : _('Guidance') %>:

    +
    <%= raw annotation.text.chomp.strip %>
    + <% end %> + <% end %> +
    + <% end %> + <% end %> + <% end %> + + diff --git a/app/views/public_pages/template_index.html.erb b/app/views/public_pages/template_index.html.erb new file mode 100644 index 0000000..6ee943f --- /dev/null +++ b/app/views/public_pages/template_index.html.erb @@ -0,0 +1,21 @@ +
    +
    +

    <%= raw _('DMP Templates') %>

    + <% if @templates.count > 0 %> +

    <%= _('Templates are provided by a funder, an organisation, or a trusted party.') %>

    + <% else %> +

    <%= _('There are currently no public Templates.')%>

    + <% end %> +
    +
    +
    +
    + <% if @templates.count > 0 %> + <%= paginable_renderise( + partial: '/paginable/templates/publicly_visible', + controller: 'paginable/templates', + action: 'publicly_visible', + scope: @templates) %> + <% end %> +
    +
    \ No newline at end of file diff --git a/app/views/question_options/_option_fields.html.erb b/app/views/question_options/_option_fields.html.erb index 1d00634..0282d82 100644 --- a/app/views/question_options/_option_fields.html.erb +++ b/app/views/question_options/_option_fields.html.erb @@ -1,10 +1,43 @@ - - - <%= f.number_field :number, in: 1..20, class: "number_field option"%> - <%= f.text_field :text, as: :string, class: "small_text_field" %> - <%= f.check_box :is_default %> - - <%= f.hidden_field :_destroy %> - <%= _('Remove') %> - - +
    +
    + <%= _('Order')%> +
    +
    + <%= _('Text')%> +
    +
    + <%= _('Default')%> +
    +
    +
    +
    +<% if q.question_options.count == 0 %> + <% 2.times {q.question_options.build } %> +<% end %> +<% i = 0 %> + <% q.question_options.to_a.sort_by{|op| op['number']}.each do |options_q| %> + <%= f.fields_for :question_options, options_q do |op|%> + <% i = i + 1 %> + <% options_q.number = i %> +
    +
    + <%= op.number_field :number, in: 1..20, class: 'form-control' %> +
    +
    + <%= op.text_field :text, as: :string, class: 'form-control' %> +
    +
    + <%= op.check_box :is_default %> +
    +
    + <%= op.hidden_field :_destroy %> + <%= _('Remove') %> +
    +
    + <% end %> + <% end %> + diff --git a/app/views/questions/_add_question.html.erb b/app/views/questions/_add_question.html.erb index 6675827..5a9c3f1 100644 --- a/app/views/questions/_add_question.html.erb +++ b/app/views/questions/_add_question.html.erb @@ -5,159 +5,88 @@ **Arguments transferred: an instance of 'section' **Copyright: Digital Curation Centre and California Digital Library --> - <% @new_question = Question.new %> <% @new_question.number = section.questions.count + 1 %> - - -<%= form_for @new_question, url: admin_create_question_path , html: {id: "new_question_#{section.id}"} do |f| %> +<%= form_for @new_question, url: admin_create_question_path(r: current_tab) , html: {id: "new_question_#{section.id}", class: 'question_form'} do |f| %> <%= f.hidden_field :section_id, value: section.id %> -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    <%= _('Question number')%><%= f.number_field :number, in: 1..50, class: "number_field has-tooltip", "data-toggle" => "tooltip", title: _('This allows you to order questions within a section.') %> + <%= hidden_field_tag :section_id, section.id, class: "section_id_new" %> -
    -
    <%= _('Question text')%> - <%= text_area_tag('question[text]', "", id: "new_question_text_#{section.id}", class: "tinymce") %> -
    -
    <%= _('Answer format')%><%= f.hidden_field :section_id, value: section.id, class: "section_id" %> -
    -
    - <%= f.select :question_format_id, - options_from_collection_for_select(QuestionFormat.all.order("title"), :id, :title, QuestionFormat.id_for(QuestionFormat.formattypes[:textarea])), - {}, class: "question_format" %> -
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _("You can choose from:
    • - text area (large box for paragraphs);
    • - text field (for a short answer);
    • - checkboxes where options are presented in a list and multiple values can be selected;
    • - radio buttons where options are presented in a list but only one can be selected;
    • - dropdown like this box - only one option can be selected;
    • - multiple select box allows users to select several options from a scrollable list, using the CTRL key;
    "))%> -
    -
    -
    - - -
    -
    -
    <%= _('Default answer')%> -
    -
    - <%= text_field_tag("question-default-value-textfield", @new_question.default_value, class: "default_answer_textfield", style: 'display:none') %> - <%= text_area_tag("question-default-value-textarea", @new_question.default_value, class: "default_answer_textarea tinymce") %> -
    -
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _('Anything you enter here will display in the answer box. If you want an answer in a certain format (e.g. tables), you can enter that style here.'))%> -
    -
    -
    -
    - <%= _('Example Answer')%> - -
    - <%= text_area_tag(:example_answer, "", class: "tinymce") %> -
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _('You can add an example answer to help users respond. These will be presented above the answer box and can be copied/ pasted.'))%> -
    -
    -
    -
    <%= _('Guidance')%> -
    - <%= text_area_tag(:guidance, "", class: "tinymce") %> -
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _("Enter specific guidance to accompany this question. If you have guidance by themes too, this will be pulled in based on your selections below so it's best not to duplicate too much text."))%> -
    -
    -
    -
    <%= _('Themes')%> -
    - <%= f.collection_select(:theme_ids, - Theme.all.order("title"), - :id, :title, {prompt: false, include_blank: _('None')}, {multiple: true})%> -
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _("

    Select themes that are relevant to this question.

    This allows your generic institution-level guidance to be drawn in, as well as that from other sources e.g. the %{organisation_abbreviation} or any Schools/Departments that you provide guidance for.

    You can select multiple themes by using the CTRL button.

    "))%> -
    -
    -
    - - -
    - <%= hidden_field_tag :section_id, section.id, class: "section_id" %> - <%= f.submit _('Save'), class: "btn btn-primary new_question_save_button" %> - <%= hidden_field_tag :section_id, section.id, class: "section_id_new" %> - <%= link_to _('Cancel'), '#', class: "btn cancel cancel_add_new_question" %> -
    + +
    + <%= f.label(:number, _('Question Number'), class: "control-label") %> + <%= f.number_field(:number, in: 1..50, class: "form-control", "aria-required": true, 'aria-required': true) %>
    -<% end %> + +
    + <%= f.label(:text, _('Question text'), class: "control-label") %> + <%= f.text_area(:text, class: "question") %> +
    + +
    + <%= f.label(:question_format_id, _('Answer format'), class: "control-label") %> + <%= f.select :question_format_id, + options_from_collection_for_select(QuestionFormat.all.order("title"), + :id, + :title, + QuestionFormat.id_for(QuestionFormat.formattypes[:textarea])), + {}, + class: "form-control", + 'data-toggle': 'tooltip', + 'data-html': true, + title: _("You can choose from:
    • - text area (large box for paragraphs);
    • - text field (for a short answer);
    • - checkboxes where options are presented in a list and multiple values can be selected;
    • - radio buttons where options are presented in a list but only one can be selected;
    • - dropdown like this box - only one option can be selected;
    • - multiple select box allows users to select several options from a scrollable list, using the CTRL key;
    ") + %> +
    + + + + + +
    + <%= f.label(:default_value, _('Default answer'), class: "control-label") %> +
    + + <%= text_field_tag("question-default-value-textfield", @new_question.default_value, class: 'form-control') %> + + + <%= text_area_tag("question-default-value-textarea", @new_question.default_value, class: 'question') %> + +
    +
    + +
    + <%= f.label(:example_answer, _('Example Answer'), class: "control-label") %> +
    + <%= text_area_tag(:example_answer, "", id: "question_example_answer", class: 'question') %> +
    +
    + +
    + <%= f.label(:guidance, _('Guidance'), class: "control-label") %> +
    "> + <%= text_area_tag(:guidance, "", id: "question_guidance", class: 'question') %> +
    +
    + +
    + <%= render partial: 'org_admin/shared/theme_selector', + locals: { f: f, all_themes: Theme.all.order("title"), + popover_message: _('Select one or more themes that are relevant to this question. This will allow similarly themed organisation-level guidance to appear alongside your question.') } %> +
    +
    +
    + <%= f.submit _('Save'), class: "btn btn-default", role:'button' %> + <%= link_to _('Cancel'), '#', class: "btn btn-default new_question_cancel", role: 'button' %> +
    +
    +<% end %> diff --git a/app/views/questions/_edit_question.html.erb b/app/views/questions/_edit_question.html.erb index 0d721d4..3de20c4 100644 --- a/app/views/questions/_edit_question.html.erb +++ b/app/views/questions/_edit_question.html.erb @@ -5,167 +5,109 @@ **Arguments transferred: an instance of 'question' **Copyright: Digital Curation Centre and California Digital Library --> - -<%= form_for(question, url: admin_update_question_path(question), html: { method: :put}) do |f| %> -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    <%= _('Question number')%><%= f.number_field :number, in: 1..50, class: "number_field has-tooltip", "data-toggle" => "tooltip", "title" => _('This allows you to order questions within a section.') %> -
    -
    <%= _('Question text')%> - <%= f.text_area(:text, class: "tinymce") %> -
    -
    <%= _('Answer format')%> -
    -
    - <%= f.hidden_field :id,{ class: "quest_id" } %> +
    +
    +
    +
    + <%= form_for(question, url: admin_update_question_path(question, r: current_tab), html: { method: :put, class: 'question_form' }) do |f| %> + <%= f.hidden_field :id,{ class: "quest_id" } %> + <%= hidden_field_tag :question_id, question.id, class: "question_id" %> + +
    + <%= f.label(:number, _('Question Number'), class: "control-label") %> + <%= f.number_field(:number, in: 1..50, class: "form-control", "aria-required": true, 'aria-required': true) %> +
    + +
    + <%= f.label(:text, _('Question text'), class: "control-label") %> + <%= f.text_area(:text, class: "question") %> +
    + +
    + <%= f.label(:question_format_id, _('Answer format'), class: "control-label") %> <%= f.select :question_format_id, - options_from_collection_for_select(QuestionFormat.all.order("title"), :id, :title, question.question_format_id), - {}, class: "question_format", id: "#{question.id}-select-format"%> -
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _("You can choose from:
    • - text area (large box for paragraphs);
    • - text field (for a short answer);
    • - checkboxes where options are presented in a list and multiple values can be selected;
    • - radio buttons where options are presented in a list but only one can be selected;
    • - dropdown like this box - only one option can be selected;
    • - multiple select box allows users to select several options from a scrollable list, using the CTRL key;
    "))%> -
    - + options_from_collection_for_select(QuestionFormat.all.order("title"), + :id, + :title, + question.question_format_id), + {}, + class: "form-control", + 'data-toggle': 'tooltip', + 'data-html': true, + title: _("You can choose from:
    • - text area (large box for paragraphs);
    • - text field (for a short answer);
    • - checkboxes where options are presented in a list and multiple values can be selected;
    • - radio buttons where options are presented in a list but only one can be selected;
    • - dropdown like this box - only one option can be selected;
    • - multiple select box allows users to select several options from a scrollable list, using the CTRL key;
    ") + %>
    - - -
    - - - - - - - - - - - <% if question.question_options.count == 0 %> - <% 2.times {question.question_options.build } %> - - <% end %> - <% i = 0 %> - <% question.question_options.to_a.sort_by{|op| op["number"]}.each do |options_q|%> - <%= f.fields_for :question_options, options_q do |op|%> - <% i = i + 1%> - <% options_q.number = i %> - <%= render "question_options/option_fields", f: op %> - <% end %> - <% end %> - - -
    <%= _('Order')%><%= _('Text')%><%= _('Default')%><%= link_to( image_tag("help_button.png"), "#", class: "question_options_popover", "data-toggle": "popover",rel: "popover", "data-html" => "true", "data-content" => _('Enter any options that you wish to display. If you want to pre-set one option as selected, check the default box.'))%>
    - - <%= _('Add option') %> - -
    - - <%= f.check_box :option_comment_display, as: :check_boxes%><%= f.label _('Display additional comment area.'), class: "checkbox inline"%> -
    -
    -
    -
    <%= _('Default answer')%> -
    -
    - <%= text_field_tag("question-default-value-textfield", question.default_value, class: "default_answer_textfield", style: question.question_format.textfield? ? '' : 'display:none;') %> - <%= text_area_tag("question-default-value-textarea", question.default_value, class: "default_answer_textarea tinymce", style: question.question_format.textarea? ? '' : 'display:none;') %> + +
    +
    + <%= render "/question_options/option_fields", f: f, q: question %> +
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _('Anything you enter here will display in the answer box. If you want an answer in a certain format (e.g. tables), you can enter that style here.'))%> + <% comment_disp = question.question_format.option_based? || question.question_format.rda_metadata? %> +
    +
    -
    -
    -
    <%= _('Example Answer')%>
    - <% example_answer = question.get_example_answer(current_user.org.id) %> - <% if example_answer.nil? %> - <% example_answer = question.annotations.build %> - <% example_answer.type = :example_answer %> - <% end %> - <%= f.fields_for :annotations, example_answer do |s|%> - <%= s.hidden_field :org_id, value: current_user.org.id %> -
      -
    • <%= s.text_area(:text, class: "tinymce") %> -
    • -
    - <% end %> + +
    + <%= f.label(:default_value, _('Default answer'), class: "control-label") %> +
    + + <%= text_field_tag("question-default-value-textfield", question.default_value, class: 'form-control') %> + + + <%= text_area_tag("question-default-value-textarea", question.default_value, class: 'question') %> + +
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _('You can add an example or suggested answer to help users respond. These will be presented above the answer box and can be copied/ pasted.'))%> + +
    + <%= f.label(:example_answer, _('Example Answer'), class: "control-label") %> +
    + <% example_answer = question.get_example_answer(current_user.org.id) %> + <% if example_answer.nil? %> + <% example_answer = question.annotations.build %> + <% example_answer.type = :example_answer %> + <% end %> + <%= f.fields_for :annotations, example_answer do |s|%> + <%= s.hidden_field :org_id, value: current_user.org.id %> + <%= s.text_area(:text, class: 'question') %> + <% end %> +
    -
    -
    -
    <%= _('Guidance')%> -
    - <% guidance = question.get_guidance_annotation(current_user.org_id) %> - <% guidance_text = guidance.present? ? guidance.text : "" %> - <%= text_area_tag("question-guidance-#{question.id}", guidance_text , class: "tinymce") %> + +
    + <%= f.label(:guidance, _('Guidance'), class: "control-label") %> +
    "> + <% guidance = question.get_guidance_annotation(current_user.org_id) %> + <% guidance_text = guidance.present? ? guidance.text : "" %> + <%= text_area_tag("question-guidance-#{question.id}", guidance_text , class: 'question') %> +
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _("Enter specific guidance to accompany this question. If you have guidance by themes too, this will be pulled in based on your selections below so it's best not to duplicate too much text."))%> + +
    + <%= render partial: 'org_admin/shared/theme_selector', + locals: { f: f, all_themes: Theme.all.order("title"), + popover_message: _('Select one or more themes that are relevant to this question. This will allow similarly themed organisation-level guidance to appear alongside your question.') } %>
    -
    -
    -
    <%= _('Themes')%>
    - <%= f.collection_select(:theme_ids, - Theme.all.order("title"), - :id, :title, {prompt: false, include_blank: "None"}, {multiple: true})%> +
    +
    + <%= f.submit _('Save'), class: "btn btn-default", role:'button' %> + <% if !question.section.phase.template.published? %> + <%= link_to _('Delete'), admin_destroy_question_path(question_id: question.id, r: current_tab), + confirm: _("You are about to delete '%{question_text}'. Are you sure?") % { :question_text => question.text }, method: :delete, class: "btn btn-default", role:'button'%> + <% end %> + <%= link_to _('Cancel'), '#', class: "btn btn-default edit_question_cancel", role: 'button' %> +
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _("

    Select themes that are relevant to this question.

    This allows your generic institution-level guidance to be drawn in, as well as that from other sources e.g. the %{organisation_abbreviation} or any Schools/Departments that you provide guidance for.

    You can select multiple themes by using the CTRL button.

    "))%> -
    -
    -
    - -
    - <%= f.submit _('Save'), class: "btn btn-primary" %> - <% if !question.section.phase.template.published? %> - <%= link_to _('Delete'), admin_destroy_question_path(question_id: question.id), - confirm: _("You are about to delete '%{question_text}'. Are you sure?") % { :question_text => question.text }, method: :delete, class: "btn btn-primary"%> - <% end %> - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - <%= link_to _('Cancel'), "#", class: "btn cancel cancel_edit_question" %> + <% end %> + +
    - - - -<%= render partial: "guidances/guidance_display", locals: {question: question} %> -<% end %> +
    + <%#= render partial: "guidances/guidance_display", locals: {question: question} %> +
    +
    diff --git a/app/views/questions/_new_edit_question_option_based.html.erb b/app/views/questions/_new_edit_question_option_based.html.erb new file mode 100644 index 0000000..171bf4d --- /dev/null +++ b/app/views/questions/_new_edit_question_option_based.html.erb @@ -0,0 +1,45 @@ +<%# locals: { f, question, answer } %> +<% options = question.question_options.by_number %> +<% if question.question_format.checkbox? %> + <%= f.label(:question_option_ids, raw(question.text), class: 'control-label') %> + <% options.each do |op| %> +
    + +
    + <% end %> +<% elsif question.question_format.radiobuttons? %> + <%= f.label(:question_option_ids, raw(question.text), class: 'control-label') %> + <% options.each do |op| %> +
    + +
    + <% end %> +<% elsif question.question_format.dropdown? || question.question_format.multiselectbox? %> + <% + options_html = "" + options.each do |op| + options_html += answer.has_question_option(op.id) ? + "" : + "" + end + %> + <%= f.label(:question_option_ids, raw(question.text), class: 'control-label') %> + <%= select_tag('answer[question_option_ids]', + raw(options_html), + { multiple: question.question_format.multiselectbox?, + include_blank: question.question_format.dropdown?, + class: 'form-control' }) %> +<% end %> +
    +<% if question.option_comment_display %> + <% text = question.question_format.rda_metadata? ? answer.answer_hash['text'] : answer.text %> + <%= label_tag('answer[text]', _('Additional Information'), class: 'control-label') %> + <%= text_area_tag('answer[text]', text, id: "answer-text-#{question.id}", class: "form-control tinymce_answer") %> +<% end %> +
    diff --git a/app/views/questions/_new_edit_question_textarea.html.erb b/app/views/questions/_new_edit_question_textarea.html.erb new file mode 100644 index 0000000..13f0934 --- /dev/null +++ b/app/views/questions/_new_edit_question_textarea.html.erb @@ -0,0 +1,17 @@ +<%# locals: { f, question, answer, locking } %> +<%# + When locking variable is true, this partial renders a plain paragraph with the question answer or default value instead. + Since the partial answers/_locking reuses the partial answers/_new_edit which reuses this partial within, when a stale answer is found + the browser renders two forms (first the stale answer form followed by the latest answer saved). This reusability comes with a side-effect, + i.e. the browser might end up with duplicate ids for the form controls and therefore re-loading tinymce for the locking form becomes + rather cumbersome. As such, this workaround, simplifies the logic when a stale answer is found by rendering the html of the answer directly + within a paragraph. +%> +
    + <%= f.label(:text, raw(question.text), class: 'control-label') %> + <% if locking %> + <%= raw("

    #{answer.text || question.default_value}

    ") %> + <% else %> + <%= text_area_tag('answer[text]', answer.text || question.default_value, id: "answer-text-#{question.id}", class: "form-control tinymce_answer") %> + <% end %> +
    \ No newline at end of file diff --git a/app/views/questions/_new_edit_question_textfield.html.erb b/app/views/questions/_new_edit_question_textfield.html.erb new file mode 100644 index 0000000..c0ebe99 --- /dev/null +++ b/app/views/questions/_new_edit_question_textfield.html.erb @@ -0,0 +1,5 @@ +<%# locals: { f, question, answer } %> +
    + <%= f.label(:text, raw(question.text), class: 'control-label') %> + <%= text_field_tag('answer[text]', strip_tags(answer.text || question.default_value), class: 'form-control') %> +
    \ No newline at end of file diff --git a/app/views/questions/_preview_question.html.erb b/app/views/questions/_preview_question.html.erb index d4a709c..1461bf6 100644 --- a/app/views/questions/_preview_question.html.erb +++ b/app/views/questions/_preview_question.html.erb @@ -1,116 +1,116 @@ - - -
    - - - <% q_format = question.question_format%> -
    -
    - - <% if q_format.title == _('Check box') || q_format.title == _('Multi select box') || q_format.title == _('Radio buttons') || q_format.title == _('Dropdown') %> - <% options = question.question_options.order("number") %> - - - <% if q_format.title == _('Check box') %> - <%if !options.nil? %> -
    - -
      - <% options.each do |op|%> -
    1. - <% end %> -
    -
    - <% end %> - - <% elsif q_format.title == _('Multi select box') %> - <% if !options.nil? %> - - - <% end %> - - <% elsif q_format.title == _('Radio buttons') %> - <% if !options.nil? %> -
    - -
      - <% options.each do |op|%> -
    1. - <% end %> -
    -
    - <% end %> - - <% elsif q_format.title == _('Dropdown') %> - <% if !options.nil? %> - - - <% end %> - <% end %> - - - <% if question.annotations.where(type: Annotation.types[:example_answer]).any? %> - <% annotation = question.annotations.where(type: Annotation.types[:example_answer]).order(:created_at).first %> -
    - - <%= _('example answer')%> - -
    -

    - <%= raw annotation.text %> -

    -
    -
    - <% end %> - -
    - - <% else %> - - - <% annotations = question.annotations.find_by(org_id: current_user.org_id) %> - <% if !annotations.blank? %> -
    - - <%= _('example answer')%> - -
    -

    - <%= raw annotations.text %> -

    -
    -
    - <% end %> - - <% end %> - - - - - <% if q_format.title == _('Text field') %> - - - <% elsif q_format.title == _('Text area') %> - - <% end %> -
    - - <%= link_to _('Save'), "#", class: "btn btn-primary", onclick: "event.preventDefault();" %> - - <%= _('Not answered yet') %> - -
    -
    -
    - - - -<%= render partial: "guidances/guidance_display", locals: {question: question}%> + + +
    + + + <% q_format = question.question_format %> +
    +
    + + <% if q_format.option_based? %> + <% options = question.question_options.order("number") %> + + + <% if q_format.checkbox? %> + <%if !options.nil? %> +
    + +
      + <% options.each do |op|%> +
    1. + <% end %> +
    +
    + <% end %> + + <% elsif q_format.multiselectbox? %> + <% if !options.nil? %> + + + <% end %> + + <% elsif q_format.radiobuttons? %> + <% if !options.nil? %> +
    + +
      + <% options.each do |op|%> +
    1. + <% end %> +
    +
    + <% end %> + + <% elsif q_format.dropdown? %> + <% if !options.nil? %> + + + <% end %> + <% end %> + + + <% if question.annotations.where(type: Annotation.types[:example_answer]).any? %> + <% annotation = question.annotations.where(type: Annotation.types[:example_answer]).order(:created_at).first %> +
    + + <%= _('example answer')%> + +
    +

    + <%= raw annotation.text %> +

    +
    +
    + <% end %> + +
    + + <% else %> + + + <% annotations = question.annotations.find_by(org_id: current_user.org_id) %> + <% if !annotations.blank? %> +
    + + <%= _('example answer')%> + +
    +

    + <%= raw annotations.text %> +

    +
    +
    + <% end %> + + <% end %> + + + + + <% if q_format.title == _('Text field') %> + + + <% elsif q_format.title == _('Text area') %> + + <% end %> +
    + + <%= link_to _('Save'), "#", class: "btn btn-primary", onclick: "event.preventDefault();" %> + + <%= _('Not answered yet') %> + +
    +
    +
    + + + +<%= render partial: "guidances/guidance_display", locals: {question: question}%> diff --git a/app/views/questions/_show_question.html.erb b/app/views/questions/_show_question.html.erb index 6e2be90..96bbb48 100644 --- a/app/views/questions/_show_question.html.erb +++ b/app/views/questions/_show_question.html.erb @@ -4,141 +4,101 @@ **Arguments transferred: 'question' **Copyright: Digital Curation Centre and California Digital Library --> - -
    - - - - - - - - - - - - - - <% if q_format.title == _('Text field') || q_format.title == _('Text area') %> - <% if !question.default_value.nil? %> - - - - - <% end %> - <% end %> - - - - - - - - <% if !question.section.phase.template.org.funder? %> - <% example_answer = question.get_example_answer(@original_org.id) %> - <% if example_answer.present? && example_answer.text.present? %> - - - - - <% end %> - <% end %> - - <% guidance = question.get_guidance_annotation(@original_org.id) %> - <% if guidance.present? %> - - - - - <% end %> - - <% themes_q = question.themes %> - <% if !themes_q.nil? %> - - - - - <% end %> -
    <%= _('Question number')%><%= question.number %>
    <%= _('Question text')%><%= raw question.text %> -
    -
    - <% q_format = question.question_format %> - <% if q_format.title == _('Check box') || q_format.title == _('Multi select box') || q_format.title == _('Radio buttons') || q_format.title == _('Dropdown') %> -
      - <% if question.question_options.is_a? QuestionOption %> -
    • - <%= question.question_options.text %>
    • +
      +
      +
      +
      + <% q_format = question.question_format %> +
      +
      <%= _('Question number')%>
      +
      <%= question.number %>
      +
      <%= _('Question text')%>
      +
      <%= raw question.text %>
      + +
      +
      + + <% if q_format.textfield? || q_format.textarea? %> + <% if !question.default_value.nil? %> +
      <%= _('Default value')%>
      +
      <%= raw question.default_value %>
      + <% end %> + <% end %> + +
      <%= _('Answer format')%>
      +
      + <%= q_format.title %> + <% if q_format.option_based? %> + <%= _('Additional comment area will be displayed.')%> <% else %> - <% if !question.question_options.to_a.nil? %> - <% question.question_options.to_a.sort_by{|op| op['number']}.each do |o| %> -
    • - <%= o.text %>
    • - <% end %> + <%= _('No additional comment area will be displayed.')%> + <% end %> +
      + + <% if !question.section.phase.template.org.funder? %> + <% example_answer = question.get_example_answer(@original_org.id) %> + <% if example_answer.present? && example_answer.text.present? %> +
      <%= _('example answer')%>
      +
      <%= raw example_answer.text %>
      <% end %> + <% end %> + + <% guidance = question.get_guidance_annotation(@original_org.id) %> + <% if guidance.present? %> +
      <%= _('Guidance')%>
      +
      <%= raw guidance.text %>
      + <% end %> + + <% themes_q = question.themes %> + <% if !themes_q.nil? %> +
      <%= _('Themes')%>
      +
      <%= themes_q.join(', ') %>
      + <% end %> +
      +
      +
      +
      +
      + + <% example_answer = question.get_example_answer(current_user.org_id) %> + <% guidance = question.get_guidance_annotation(current_user.org_id) %> + <% editing = (example_answer.present? || guidance.present?) %> + <% if !question.modifiable %> +

      <%= _('Annotations') %>

      +
      + <%= render partial: 'annotations/show', + locals: { example_answer: example_answer, + guidance: guidance, + template: template, + for_plan: false } %> + <% unless question.modifiable %> +
      + <%= link_to _('%{add_or_edit} Annotations') % { add_or_edit: (editing ? 'Edit' : 'Add') }, + "#annotations_div_#{question.id}", class: "btn btn-default annotations_button", role:"button" %> +
      <% end %> -
    - <% end %> -
    -
    <%= _('Default value')%><%= raw question.default_value %>
    <%= _('Answer format')%><%= q_format.title %> -
    - <% if q_format.title == _('Check box') || q_format.title == _('Multi select box') || q_format.title == _('Radio buttons') || q_format.title == _('Dropdown') %> - <% if question.option_comment_display == true %> - <%= _('Additional comment area will be displayed.')%> - <% else %> - <%= _('No additional comment area will be displayed.')%> - <% end %> + + <% end %> -
    -
    - <%= _('example answer')%> - <%= raw example_answer.text %>
    <%= _('Guidance')%><%= raw guidance.text %>
    <%= _('Themes')%><% i = 1%> - <% themes_q.each do |t|%> - <%= t.title %> - <% if themes_q.count > i %> - , - <% i +=1 %> - <% end %> +
    + <% if (question.modifiable && edit) %> + <%= link_to _('Edit question'), "#question_edit#{question.id}", class: "btn btn-default question_edit_link", role: "button" %> + <%= link_to _('Delete question'), admin_destroy_question_path(question_id: question.id, r: current_tab), + confirm: _("You are about to delete '%{question_text}'. Are you sure?") % { :question_text => question.text }, method: :delete, class: "btn btn-default", role:"button" %> <% end %> -
    -
    - - <% example_answer = question.get_example_answer(current_user.org_id) %> - <% guidance = question.get_guidance_annotation(current_user.org_id) %> - <% if !question.modifiable %> - <% if example_answer.present? || guidance.present? %> -
    - <%= render partial: 'annotations/show_annotation', locals: {example_answer: example_answer, guidance: guidance, question: question} %> -
    - - <% end %> - - <% end %> - -
    - - <% if (@edit && question.modifiable) %> -
    - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - <%= link_to _('Edit question'), '# ', class: "btn btn-primary edit_question_button"%> - - <%= link_to _('Delete question'), admin_destroy_question_path(question_id: question.id), - confirm: _("You are about to delete '%{question_text}'. Are you sure?") % { :question_text => question.text }, method: :delete, class: "btn btn-primary"%> -
    - <% else %> - <% if example_answer.nil? && guidance.nil? %> -
    -
    - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - <%= link_to _('Add Annotations'), '# ', class: "btn btn-primary add_annotations_button"%>
    - <% end %> - <% end %> -
    +
    +
    +
    + <%#= render partial: 'guidances/guidance_display', locals: {question: question} %> +
    - - - -<%= render partial: 'guidances/guidance_display', locals: {question: question}%> diff --git a/app/views/sections/_add_section.html.erb b/app/views/sections/_add_section.html.erb index 93564b8..d64c5a2 100644 --- a/app/views/sections/_add_section.html.erb +++ b/app/views/sections/_add_section.html.erb @@ -1,55 +1,36 @@ - -<% @new_section = Section.new %> -<% @new_section.number = phase.sections.count + 1 %> - - -<%= form_for @new_section, url: admin_create_section_path do |f| %> +<% new_section = Section.new %> +<% new_section.number = phase.sections.count + 1 %> +<%= form_for new_section, { url: admin_create_section_path(r: current_tab), html: { class: 'form-horizontal' }} do |f| %> <%= f.hidden_field :phase_id, value: phase.id %> - -
    -
    -
    -
    - - <%= f.text_field :title, as: :string, class: "text_field", placeholder: _('New section title')%> +
    +
    +

    +
    + <%= f.text_field(:title, { class: "form-control", 'aria-required': false, placeholder: _('Enter a title for the section') } ) %>
    - - -

    - +
    +
    -
    -
    - - - - - - - - - -
    <%= _('Order of display') %> - <% range = @phase.template.customization_of.present? ? 0..15 : 1..15 %> - <%= f.number_field :number, in: range, class: "number_field has-tooltip", "data-toggle" => "tooltip", title: _('This allows you to order sections.') %> -
    <%= _('Description') %> -
    - <%= text_area_tag("section-desc", "" , class: "tinymce") %> -
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _("
    Enter a basic description. This could be a summary of what is covered in the section or instructions on how to answer. This text will be displayed in the coloured banner once a section is opened to edit.
    "))%> -
    -
    - -
    - <%= f.submit _('Save'), class: "btn btn-primary" %> - <%= link_to _('Cancel'), "#", id: "", class: "btn cancel" %> +
    +
    + <%= f.label(:number, _('Order of display'), class: "control-label") %> +
    +
    + <%= f.number_field(:number, in: 1..15, class: "form-control", 'aria-required': false, 'data-toggle': 'tooltip', title: _('This allows you to order sections.')) %> +
    + +
    + <%= f.label(:description, _('Description'), class: "control-label") %> +
    "> + <%= text_area_tag('section-desc', '', class: 'tinymce form-control') %>
    -
    +
    + +
    + <%= f.button(_('Save'), class: 'btn btn-default', type: "submit") %> + <%= link_to(_('Cancel'), '#', { class: 'btn btn-default section_new_cancel', role: "button" }) %>
    -<% end %> - - +<% end %> \ No newline at end of file diff --git a/app/views/sections/_edit_section.html.erb b/app/views/sections/_edit_section.html.erb index 4bc7cfc..b97d078 100644 --- a/app/views/sections/_edit_section.html.erb +++ b/app/views/sections/_edit_section.html.erb @@ -1,117 +1,68 @@ - +
    +
    +

    <%= _('Section details') %>

    +
    +
    +
    +
    + <%= form_for(section, url: admin_update_section_path(section, phase: phase, r: current_tab), html: { method: :put }) do |f| %> +
    + <%= f.label(:title, _('Title') ,class: "control-label") %> + <%= f.text_field(:title, { class: "form-control", 'aria-required': false, placeholder: _('Enter a title for the section'), 'data-toggle': 'tooltip', title: _('Enter a title for the section')} ) %> +
    - -<%= form_for(section, url: admin_update_section_path(section, phase: phase, edit: edit), html: { method: :put}) do |s| %> - <% if @open && @section_id == section.id then%> - <% toggle = 'accordion-body section-collapse in collapse'%> - <% else %> - <% toggle = 'accordion-body collapse section-collapse' %> - <% end %> - - <% if @new_sec %> - <% toggle = 'accordion-body section-collapse in collapse'%> - <% end %> - -
    -
    -
    -
    - <%= s.text_field :title, as: :string, class: 'text_field', placeholder: _('New section title') %> - +
    +
    + <%= f.label(:number, _('Order of display'), class: "control-label") %> +
    +
    + <%= f.number_field(:number, in: 1..15, class: "form-control", 'aria-required': false, 'data-toggle': 'tooltip', title: _('This allows you to order sections.')) %>
    - -
    - -
    -
    - - - - <% range = @phase.template.customization_of.present? ? 0..15 : 1..15 %> - - - - - - -
    <%= _('Order of display') %><%= s.number_field :number, in: range, class: "number_field has-tooltip", 'data-toggle' => "tooltip", 'title' => _('This allows you to order sections.') %>
    <%= _('Description') %> -
    - <%= text_area_tag("section-desc-#{section.id}", section.description , class: "tinymce") %> -
    -
    - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _("
    Enter a basic description. This could be a summary of what is covered in the section or instructions on how to answer. This text will be displayed in the coloured banner once a section is opened to edit.
    "))%> -
    -
    - -
    - <%= s.submit _('Save'), class: 'btn btn-primary' %> - <% if section.modifiable %> - <%= link_to _('Delete'), admin_destroy_section_path(section_id: section.id), - confirm: _("You are about to delete '%{section_title}'. This will affect questions linked to this section. Are you sure?") % { :section_title => section.title }, method: :delete, class: "btn btn-primary"%> - <% end %> - <%= link_to _('Cancel'), :back, class: 'btn cancel' %> +
    + <%= f.label(:description, _('Description'), class: "control-label") %> +
    "> + <%= text_area_tag('section-desc', section.description, class: "section tinymce form-control") %>
    -<% end %> -
    - <% @questions = section.questions.order("number")%> - <% if @questions.length > 0 %> - <% question_left = @questions.length %> - <% @questions.each do |question| %> -
    -
    "> - - <%= render partial: 'questions/show_question', locals: {question: question} %> -
    +
    - -
    - - <% if question_left.to_i > 1 %> -
    - <% else %> -
    - <% end %> - <% question_left = question_left - 1 %> - - <% end %> - <% end %> - - - <% if section.modifiable %> - - <% if @questions.length != 0 %> -
    - <% end%> - - +
    +

    <%= _('Questions') %>

    + <% questions = section.questions.order('number') %> + <% if questions.present? %> + <% questions.each do |question| %> +
    +
    "> + <%= render partial: 'questions/show_question', locals: {template: template, question: question, current_tab: current_tab, edit: edit} %>
    - - -
    -
    - <%= hidden_field_tag :section_id, section.id, class: "section_id" %> - <%= link_to _('Add question'), '#', class: "btn btn-primary add_question_button" %> -
    +
    " style="display: none;"> + <%= render partial: 'questions/edit_question', locals: {template: template, question: question, current_tab: current_tab} %>
    + <%= raw("
    ") if questions.last.id == question.id %> <% end %> - -
    + <% end %> +
    +
    +<% if edit %> +
    +
    +
    + <%= link_to(_('Add Question'), '#', { class: 'btn btn-default question_new_link', role: "button" }) %> +
    +
    +<% end %> \ No newline at end of file diff --git a/app/views/sections/_progress.html.erb b/app/views/sections/_progress.html.erb index 9c9fac4..d02cbac 100644 --- a/app/views/sections/_progress.html.erb +++ b/app/views/sections/_progress.html.erb @@ -1,14 +1,7 @@ - - <% num_section_questions = section.questions.size() %> - <% num_section_answers = section.num_answered_questions(plan) %> - <% question_word = "questions" %> - <% if num_section_questions == 1 then %> - <% question_word = "question" %> - <% end %> - <% section_status = "#{num_section_questions} #{question_word}, #{num_section_answers} answered" %> - <%= section.title %> - <% if num_section_questions.to_i > num_section_answers.to_i then %> - (<%= section_status %>) - <% else %> - (<%= section_status %>) - <% end %> +<%# locals: { section, plan } %> + +<% num_section_questions = section.questions.size() %> +<% num_section_answers = section.num_answered_questions(plan) %> + + (<%= num_section_answers %> / <%= num_section_questions %>) + \ No newline at end of file diff --git a/app/views/sections/_show_section.html.erb b/app/views/sections/_show_section.html.erb index 8659bc3..4848a8c 100644 --- a/app/views/sections/_show_section.html.erb +++ b/app/views/sections/_show_section.html.erb @@ -1,53 +1,29 @@ - -<% if @open && @section_id == section.id %> - <% toggle = 'accordion-body section-collapse in collapse'%> -<% else %> - <% toggle = 'accordion-body collapse section-collapse' %> -<% end %> -
    - -
    -
    +
    +
    +

    <%= raw section.description %> -

    -
    - <% section_questions = section.questions.order("number") %> - <% if section_questions.present? %> - <% last_question_id = section_questions.last.id %> - <% section_questions.each do |question| %> -
    - -
    "> - - <% if question.modifiable %> - <%= render partial: 'questions/edit_question', locals: {question: question} %> - <% else %> - - <%= render partial: 'questions/show_question', locals: {question: question} %> - <% end %> -
    - -
    - - <% if last_question_id == question.id %> -
    - <% else %> -
    - <% end %> - <% end %> - <% end %> -
    +

    +
    +
    +

    <%= _('Questions') %>

    + <% questions = section.questions.order('number') %> + <% if questions.present? %> + <% questions.each do |question| %> +
    +
    "> + <%= render partial: 'questions/show_question', locals: {template: template, question: question, current_tab: current_tab, edit: edit} %> +
    + <% if question.modifiable && edit %> +
    " style="display: none;"> + <%= render partial: 'questions/edit_question', locals: {template: template, question: question, current_tab: current_tab} %> +
    + <% end %> + <% if questions.last.id != question.id %> +
    + <% end %> + <% end %> + <% end %> +
    +
    \ No newline at end of file diff --git a/app/views/settings/phases/_export_formatting_form.html.erb b/app/views/settings/phases/_export_formatting_form.html.erb deleted file mode 100644 index 492b416..0000000 --- a/app/views/settings/phases/_export_formatting_form.html.erb +++ /dev/null @@ -1,110 +0,0 @@ -<% - @export_settings ||= plan.settings(:export) - admin_settings = @export_settings.fields[:admin] - question_settings = @export_settings.fields[:questions] || :all - filename = @export_settings.title.present? ? @export_settings.title : plan.title -%> -<% @export_settings.errors.full_messages.each do |error| %> -
    <%= error %>
    -<% end %> - -<%= form_for(@export_settings, url: settings_plan_path(plan), method: :put, as: :export) do |f| %> -
    - <%= submit_tag(_('Reset'), class: "btn btn-primary") %> - <%= submit_tag(_('Save'), class: "btn btn-primary", "data-toggle" => "collapse", "data-target" => "#settings-accordion-plan-#{plan.id}") %> -
    - -
    -

    <%= _('File Name') %>

    -
    -
      -
    1. - <%= label_tag("export[title]", _('File Name')) %> -
      - <%= text_field_tag("export[title]", filename) %> -
      -
    2. -
    -
    -
    -
    -

    <%= _('Included Elements') %>

    -
    -
    - -
      - <% Settings::Template::VALID_ADMIN_FIELDS.each do |field| - name = "export[fields][admin][#{field}]" - %> -
    1. - <%= label_tag(name, t("helpers.plan.export.#{field}")) %> - <%= check_box_tag(name, true, admin_settings.include?(field.to_sym)) %> -
    2. - <% end %> -
    -
    -
    - -
      - <% phase.sections.each do |section| %> -
    1. -
      - - - -
        - <% section.questions.each do |question| - selected = question_settings == :all || question_settings.member?(question.id) - name = "export[fields][questions][#{question.id}]" - %> -
      1. - <%= label_tag(name, strip_tags(question.text).html_safe, title: strip_tags(question.text).html_safe) %> - <%= check_box_tag(name, true, selected) %> -
      2. - <% end %> -
      -
      -
    2. - <% end %> -
    -
    -
    -

    <%= _('PDF Formatting') %>

    -
    - <%= _('Font') -%> -
    - <%= label_tag("export[formatting][font_face]", _('Face')) %> - <%= select_tag("export[formatting][font_face]", options_for_select(Settings::Template::VALID_FONT_FACES, @export_settings.formatting[:font_face]), { "data-default" => plan.template.settings(:export).formatting[:font_face] }) %> -
    -
    - <%= label_tag("export[formatting][font_size]", _('Size') + " (pt)") %> - <%= select_tag("export[formatting][font_size]", options_for_select(Settings::Template::VALID_FONT_SIZE_RANGE.to_a, @export_settings.formatting[:font_size]), { "data-default" => plan.template.settings(:export).formatting[:font_size] }) %> -
    -
    -
    - <%= _('Margin') -%> (mm) -
    - <%= label_tag("export[formatting][margin][top]", _('Top')) %> - <%= select_tag("export[formatting][margin][top]", options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a, @export_settings.formatting[:margin][:top]), { "data-default" => plan.template.settings(:export).formatting[:margin][:top] }) %> -
    -
    - <%= label_tag("export[formatting][margin][bottom]", _('Bottom')) %> - <%= select_tag("export[formatting][margin][bottom]", options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a, @export_settings.formatting[:margin][:bottom]), { "data-default" => plan.template.settings(:export).formatting[:margin][:bottom] }) %> -
    -
    - <%= label_tag("export[formatting][margin][left]", _('Left')) %> - <%= select_tag("export[formatting][margin][left]", options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a, @export_settings.formatting[:margin][:left]), { "data-default" => plan.template.settings(:export).formatting[:margin][:left] }) %> -
    -
    - <%= label_tag("export[formatting][margin][right]", _('Right')) %> - <%= select_tag("export[formatting][margin][right]", options_for_select(Settings::Template::VALID_MARGIN_RANGE.to_a, @export_settings.formatting[:margin][:right]), { "data-default" => plan.template.settings(:export).formatting[:margin][:rigth] }) %> -
    -
    -
    -
    - <%= submit_tag(_('Reset'), class: "btn btn-primary") %> - <%= submit_tag(_('Save'), class: "btn btn-primary", "data-toggle" => "collapse", "data-target" => "#settings-accordion-plan-#{plan.template.id}") %> -
    - -
    -<% end %> diff --git a/app/views/settings/phases/show.html.erb b/app/views/settings/phases/show.html.erb deleted file mode 100644 index c719d53..0000000 --- a/app/views/settings/phases/show.html.erb +++ /dev/null @@ -1,6 +0,0 @@ - - -

    <%= _('File Name') -%> - <%= @plan.title -%>

    -

    <%= _('Description') -%>

    - -<%= render "export_formatting_form" %> diff --git a/app/views/settings/phases/show.partial.erb b/app/views/settings/phases/show.partial.erb deleted file mode 100644 index 9017f61..0000000 --- a/app/views/settings/phases/show.partial.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render "export_formatting_form.html.erb" %> diff --git a/app/views/shared/_access_controls.html.erb b/app/views/shared/_access_controls.html.erb new file mode 100644 index 0000000..7ab3e50 --- /dev/null +++ b/app/views/shared/_access_controls.html.erb @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/app/views/shared/_accessible_combobox.html.erb b/app/views/shared/_accessible_combobox.html.erb index e06ec20..af6653a 100644 --- a/app/views/shared/_accessible_combobox.html.erb +++ b/app/views/shared/_accessible_combobox.html.erb @@ -1,66 +1,38 @@ <% if !models.nil? %> + <% required = required ||= false %> + <% classes = classes ||= '' %> + <% title = tooltip ||= '' %> + <% error = error ||= _('Please select an item from the list.') %> + <% json = {} %> - <% models.map{|m| json[m[attribute]] = m.id} %> + <% models.map{|m| json["#{m[attribute]}"] = m.id} %> <% models.each do |model| %> - <% end %> + + + " name="<%= name.gsub("_#{attribute}]", "_id]") %>" - value="<%= default_selection.id unless default_selection.nil? %>" /> - + value="<%= default_selection.id unless default_selection.nil? %>" aria-required="<%= required %>" + data-validation="js-combobox" data-validation-error="<%= error %>" /> - <% else %> <%= _('No items available.') %> <% end %> \ No newline at end of file diff --git a/app/views/shared/_accessible_submit_button.html.erb b/app/views/shared/_accessible_submit_button.html.erb deleted file mode 100644 index 2966a91..0000000 --- a/app/views/shared/_accessible_submit_button.html.erb +++ /dev/null @@ -1,25 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/views/shared/_create_account_form.html.erb b/app/views/shared/_create_account_form.html.erb new file mode 100644 index 0000000..ce35c59 --- /dev/null +++ b/app/views/shared/_create_account_form.html.erb @@ -0,0 +1,36 @@ +<%= form_for resource, as: 'user', url: registration_path("user"), html: {autocomplete: "off", id: "create_account_form"} do |f| %> + +
    + <%= f.label(:firstname, _('First Name'), class: "control-label") %> + <%= f.text_field(:firstname, class: "form-control", "aria-required": true) %> +
    +
    + <%= f.label(:surname, _('Last Name'), class: "control-label") %> + <%= f.text_field(:surname, class: "form-control", "aria-required": true) %> +
    +
    + <%= f.label(:email, _('Email'), class: "control-label") %> + <%= f.email_field(:email, class: "form-control", "aria-required": true) %> +
    +
    + <%= render partial: "shared/my_org", + locals: {f: f, default_org: @default_org, + orgs: Org.where("parent_id IS NULL").order("sort_name ASC, name ASC"), allow_other_orgs: true} %> +
    + +
    + <%= f.label(:password, _('Password'), class: "control-label") %> + <%= f.password_field(:password, class: "form-control", "aria-required": true) %> +
    +
    + +
    +
    + <%= f.label(:accept_terms, + raw("#{ f.check_box(:accept_terms, "aria-required": true, "data-validation-error": _('You must agree to the term and conditions.')) } #{_('I accept the')} #{_('terms and conditions')}")) %> +
    + + <%= f.button(_('Create account'), class: "btn btn-default", type: "submit") %> +<% end %> diff --git a/app/views/shared/_export_links.html.erb b/app/views/shared/_export_links.html.erb deleted file mode 100644 index 6908e62..0000000 --- a/app/views/shared/_export_links.html.erb +++ /dev/null @@ -1,13 +0,0 @@ -<% javascript("export_configure") %> -
    - <%= label_tag(:format, _('Format')) %> - <%= select_tag(:format, options_for_select(ExportedPlan::VALID_FORMATS, :pdf), class: 'export-format-selection') %> - - - -
    - -
    -

    Export Settings <%= plan_settings_indicator(plan) -%>

    - <%= render(partial: "settings/phases/export_formatting_form", locals: { plan: plan, phase: phase }) %> -
    diff --git a/app/views/shared/_links.html.erb b/app/views/shared/_links.html.erb new file mode 100644 index 0000000..1f3300c --- /dev/null +++ b/app/views/shared/_links.html.erb @@ -0,0 +1,50 @@ +<%# locals: { context, title, links, max_number_links, tooltip } %> + \ No newline at end of file diff --git a/app/views/shared/_login_form.html.erb b/app/views/shared/_login_form.html.erb deleted file mode 100644 index 5bf1449..0000000 --- a/app/views/shared/_login_form.html.erb +++ /dev/null @@ -1,36 +0,0 @@ -<%= form_for(resource, :as => "user", :url => user_session_path) do |f| %> -
      -
    • - <%= devise_error_messages! %> -
    • -
    • - <%= f.email_field :email, placeholder: (_('Email address') + ' *') , :as => :email%> -
    • -
    • - <%= f.password_field :password, placeholder: (_('Password') + ' *'), :as => :password %> -
    • - -
    • - <%= f.check_box :remember_me %><%= f.label :remember_me, :class => "remember_div" do %> - <%= _('Remember me') %> - <%end%> -
    • -
    • - <%= f.submit _('Sign in'), :class => "btn btn-primary" %> -
    • - <% if Rails.application.config.shibboleth_enabled %> - <% if request.fullpath != "/users/sign_up?nosplash=true" && session[:shibboleth_data].nil? then%> -
    • - -
    • - <%else%> - <%= f.hidden_field :shibboleth_id, :value => session[:shibboleth_data][:uid] %> - <%end%> - <% end %> -
    -<% end %> diff --git a/app/views/shared/_modal.html.erb b/app/views/shared/_modal.html.erb new file mode 100644 index 0000000..f0787c4 --- /dev/null +++ b/app/views/shared/_modal.html.erb @@ -0,0 +1,23 @@ + + \ No newline at end of file diff --git a/app/views/shared/_my_org.html.erb b/app/views/shared/_my_org.html.erb new file mode 100644 index 0000000..11a7051 --- /dev/null +++ b/app/views/shared/_my_org.html.erb @@ -0,0 +1,19 @@ +<%= f.label :org_name, _('Organisation'), class: 'control-label' %> +<%= render partial: "shared/accessible_combobox", + locals: {name: "user[org_name]", + id: "user_org_name", + default_selection: default_org, + models: orgs, + attribute: 'name'} %> + +<% if allow_other_orgs %> +
    + <%= _('My organisation isn\'t listed.') %> + +
    + <%= f.text_field :other_organisation, autocomplete: "off", class: "form-control hide", + placeholder: _('Please enter the name of your organisation') %> +
    + +
    +<% end %> diff --git a/app/views/shared/_popover.html.erb b/app/views/shared/_popover.html.erb new file mode 100644 index 0000000..ab1ac6e --- /dev/null +++ b/app/views/shared/_popover.html.erb @@ -0,0 +1,7 @@ +<%# available locals: message, placement %> +<% if message.present? %> + + + +<% end %> \ No newline at end of file diff --git a/app/views/shared/_register_form.html.erb b/app/views/shared/_register_form.html.erb deleted file mode 100644 index e8eac9e..0000000 --- a/app/views/shared/_register_form.html.erb +++ /dev/null @@ -1,62 +0,0 @@ -<% javascript('shared/register_form.js') %> - -<%= form_for(resource, :as => "user", :url => registration_path("user"), :htmlb => {:autocomplete =>"off"}, :html => {class: "roadmap-form"}) do |f| %> -
      -
    • - <%= devise_error_messages! %> -
    • -
    • - <%= f.email_field :email, placeholder: (_('Email') + ' *'), :as => :email, :class => 'text_field has-tooltip reg-input', 'data-toggle' => "tooltip", 'data-container' => "body", 'title' => _('Please enter your email') %> - -
    • - <% if extended then %> - <% unless session[:shibboleth_data].nil? %> - <%= f.hidden_field :shibboleth_id, :value => session[:shibboleth_data][:uid] %> - <% end %> -
    • - <%= f.text_field :firstname, placeholder: _('First name'), :as => :string, :class => 'text_field' %> -
    • -
    • - <%= f.text_field :surname, placeholder: _('Last name'), :as => :string, :class => 'text_field' %> -
    • - <% end %> - <% if resource.user_identifiers.count > 0 %> - <% scheme = resource.user_identifiers.identifier_scheme.name %> - <%= f.hidden_field "user_identifiers[#{scheme}]", value: resource.user_identifiers.first.identifier%> - <% end %> -
    • - <%= collection_select(:user, :org_id, Org.where("parent_id IS NULL").order("sort_name ASC, name ASC"), :id, :name, {include_blank: _('Organisation')}, { :class => 'typeahead org_sign_up', 'data-allow-clear': false }) %> -
    • - - <% other_organisations = Org.where("parent_id IS ? AND is_other = ?", nil, true).pluck(:id) %> - -
    • - <%= f.password_field :password, placeholder: (_('Password') + ' *'), :autocomplete => "off", :as => :password, :class => 'text_field has-tooltip reg-input', 'data-toggle' => "tooltip", 'data-container' => "body", 'title' => _('Your password must contain at least 8 characters.') %> - -
    • -
    • - <%= f.password_field :password_confirmation, placeholder: (_('Password confirmation') + ' *'), :as => :password, :class => 'text_field has-tooltip reg-input', 'data-toggle' => "tooltip", 'data-container' => "body", 'title' => _('Your password must contain at least 8 characters.') %> - -
    • -
    • - <%= f.check_box :accept_terms %> <%= f.label :accept_terms, :class => "remember_div" do %> - <%= raw _(' I accept the terms and conditions *') %> - <%end%> -
    • -
    • - <%= render partial: 'shared/accessible_submit_button', - locals: {id: 'sign_up_submit', - val: _('Create account'), - disabled_initially: true, - tooltip: _('You can not continue until you have filled in all of the required information.')} %> -
    • -
    -<% end %> diff --git a/app/views/shared/_search.html.erb b/app/views/shared/_search.html.erb new file mode 100644 index 0000000..a244049 --- /dev/null +++ b/app/views/shared/_search.html.erb @@ -0,0 +1,12 @@ +<% # locals: { controller, action } %> +<%= form_tag(url_for(controller: controller, action: action, page: '1'), method: :get, remote: true, class: 'form-inline') do %> +
    +
    + + + + <%= text_field_tag(:search, nil, class: 'form-control', 'aria-describedby': 'search-addon', 'aria-required': true) %> +
    +
    + <%= submit_tag(_('Search'), class: 'btn btn-default', style: 'margin-top: 8px;') %> +<% end %> \ No newline at end of file diff --git a/app/views/shared/_sign_in_form.html.erb b/app/views/shared/_sign_in_form.html.erb new file mode 100644 index 0000000..48428a5 --- /dev/null +++ b/app/views/shared/_sign_in_form.html.erb @@ -0,0 +1,32 @@ +<%= form_for resource, as: 'user', url: user_session_path, html: {id: "sign_in_form"} do |f| %> +
    + <%= f.label(:email, _('Email'), class: 'control-label') %> + <%= f.email_field(:email, class: 'form-control', "aria-required": true, id: nil) %> +
    +
    + <%= f.label(:password, _('Password'), class: 'control-label') %> + <%= f.password_field(:password, class: 'form-control', "aria-required": true, id: nil) %> +
    +
    + <%= link_to _('Forgot password?'), new_password_path('user') %> +
    +
    + <%= label_tag 'remember_email', raw("#{check_box_tag 'remember_email'}#{_('Remember email')}") %> +
    + <%= f.button(_('Sign in'), class: "btn btn-default", type: "submit") %> + + <% if Rails.application.config.shibboleth_enabled %> + <% if session['devise.shibboleth_data'].nil? %> +

    - <%= _('or') %> -

    +
    + + <% target = (Rails.application.config.shibboleth_use_filtered_discovery_service ? shibboleth_ds_path : user_shibboleth_omniauth_authorize_path) %> + <%= _('Sign in with your institutional credentials') %> + +
    + <% else %> + <%= f.hidden_field :shibboleth_id, :value => session['devise.shibboleth_data']['uid'] %> + <% end %> + <% end %> + +<% end %> diff --git a/app/views/shared/_table_filter.html.erb b/app/views/shared/_table_filter.html.erb new file mode 100644 index 0000000..19cf031 --- /dev/null +++ b/app/views/shared/_table_filter.html.erb @@ -0,0 +1,18 @@ +
    +
    +
      +
    • <%= label_tag :filter, _('Filter plans'), class: "sr-only" %>
    • +
    • <%= search_field_tag(:filter, nil, placeholder: placeholder, class: "form-control filter") %>
    • +
    • + + + +
    • +
    +
    +
    diff --git a/app/views/shared/export/_plan.erb b/app/views/shared/export/_plan.erb new file mode 100644 index 0000000..fb9acb4 --- /dev/null +++ b/app/views/shared/export/_plan.erb @@ -0,0 +1,92 @@ +<% + font_face = (@formatting[:font_face].present? ? "#{@formatting[:font_face]}" : 'Arial, Helvetica, Sans-Serif') + font_size = (@formatting[:font_size].present? ? "#{@formatting[:font_size]}" : '12') + margin_top = '5' + margin_bottom = '10' + margin_left = '12' + margin_right = '12' + + if @formatting[:margin].present? + margin_top = (@formatting[:margin][:top].is_a?(Integer) ? @formatting[:margin][:top] * 4 : @formatting[:margin][:top]) if @formatting[:margin][:top].present? + margin_right = (@formatting[:margin][:right].is_a?(Integer) ? @formatting[:margin][:right] * 4 : @formatting[:margin][:right]) if @formatting[:margin][:right].present? + margin_bottom = (@formatting[:margin][:bottom].is_a?(Integer) ? @formatting[:margin][:bottom] * 4 : @formatting[:margin][:bottom]) if @formatting[:margin][:bottom].present? + margin_left = (@formatting[:margin][:left].is_a?(Integer) ? @formatting[:margin][:left] * 4 : @formatting[:margin][:left]) if @formatting[:margin][:left].present? + end +%> + + + + + + <%= @plan.title %> + <%= render partial: '/shared/export/plan_styling', + locals: { + font_face: font_face, + font_size: "#{font_size}pt", + margin: "#{margin_top}px #{margin_right}px #{margin_bottom}px #{margin_left}px" + } %> + + + <% if @show_coversheet %> + <%= render partial: '/shared/export/plan_coversheet' %> + <% end %> + + <% @hash[:phases].each do |phase| %> +
    + +

    <%= (@hash[:phases].length > 1 ? "#{@plan.title} - #{phase[:title]}" : @plan.title) %>

    +
    + <% phase[:sections].each do |section| %> + <% if @show_sections_questions %> +

    <%= section[:title] %>

    + <% end %> + + <% section[:questions].each do |question| %> +
    + <% if @show_sections_questions && !@public_plan %> + <%# text in this case is an array to accomodate for option_based %> + <% if question[:text].length > 1 %> +
      + <% question[:text].each do |txt| %> +
    • <%= txt %>
    • + <% end %> +
    + <% else %> +

    <%= raw question[:text][0].gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") if question[:text].present? && question[:text][0].present? %>

    + <% end %> +
    + <% end %> + + <% answer = @plan.answer(question[:id], false) %> + <% blank = (answer.present? && answer.is_valid?) ? answer.text.gsub(/<\/?p>/, '').gsub(//, '').chomp.blank? : true %> + <% options = answer.present? ? answer.question_options : [] %> + <% if blank && @show_unanswered && options.blank? %> +

    <%= _('Question not answered.') -%>

    + <% elsif !blank %> + <% if options.present?%> +
      + <% options.each do |opt| %> +
    • <%= opt.text %>
    • + <% end %> +
    + <% end %> + <% if question[:format].rda_metadata? %> + <% ah = answer.answer_hash %> + <% if ah['standards'].present? %> +
      + <% ah['standards'].each do |id, title| %> +
    • <%= title %>
    • + <% end %> +
    + <% end %> +

    <%= raw ah['text'] %>

    + <% else %> +

    <%= raw answer.text %>

    + <% end %> + <% end %> +
    + <% end %> + <% end %> + <% end %> + + diff --git a/app/views/shared/export/_plan_coversheet.erb b/app/views/shared/export/_plan_coversheet.erb new file mode 100644 index 0000000..fef0952 --- /dev/null +++ b/app/views/shared/export/_plan_coversheet.erb @@ -0,0 +1,32 @@ +
    +

    <%= @plan.title %>

    +

    + <%= _("A Data Management Plan created using ") + Rails.configuration.branding[:application][:name] %> +

    +
    +
    +

    <%= @hash[:attribution].length > 1 ? _("Creators: ") : _('Creator:') %> <%= @hash[:attribution].join(', ') %>


    + +

    <%= _("Affiliation: ") + @hash[:affiliation] %>


    + + <% if @hash[:funder].present? %> +

    <%= _("Template: ") + @hash[:funder] %>


    + <% else %> +

    <%= _("Template: ") + @hash[:template] + @hash[:customizer] %>


    + <% end %> + + <% if @plan.grant_number.present? %> +

    <%= _("Grant number: ") + @plan.grant_number %>


    + <% end %> + + <% if @plan.description.present? %> +

    <%= _("Project abstract: ") %>

    +
    <%= raw(@plan.description) %>

    + <% end %> + +

    <%= _("Last modified: ") + l(@plan.updated_at.to_date, formats: :short) %>


    + +

    <%= _("Copyright information:") %>

    +

    <%= _(" The above plan creator(s) have agreed that others may use as much of the text of this plan as they would like in their own plans, and customise it as necessary. You do not need to credit the creator(s) as the source of the language used, but using any of the plan's text does not imply that the creator(s) endorse, or have any relationship to, your project or proposal") %>

    +
    +
    \ No newline at end of file diff --git a/app/views/shared/export/_plan_styling.erb b/app/views/shared/export/_plan_styling.erb new file mode 100644 index 0000000..3ed931b --- /dev/null +++ b/app/views/shared/export/_plan_styling.erb @@ -0,0 +1,61 @@ + \ No newline at end of file diff --git a/app/views/shared/export/_plan_txt.erb b/app/views/shared/export/_plan_txt.erb new file mode 100644 index 0000000..45cd172 --- /dev/null +++ b/app/views/shared/export/_plan_txt.erb @@ -0,0 +1,59 @@ +<%= "#{@plan.title}" %> +<%= "----------------------------------------------------------\n" %> +<% if @show_coversheet %> +<%= @hash[:attribution].length > 1 ? _("Creators: ") : _('Creator:') %> <%= @hash[:attribution].join(', ') %> +<%= _("Affiliation: ") + @hash[:affiliation] %> + <% if @hash[:funder].present? %> +<%= _("Template: ") + @hash[:funder] %> + <% else %> +<%= _("Template: ") + @hash[:template] + @hash[:customizer] %> + <% end %> + <% if @plan.grant_number.present? %> +<%= _("Grant number: ") + @plan.grant_number %> + <% end %> + <% if @plan.description.present? %> +<%= _("Project abstract: ") %> +<%= "\t" + strip_tags(@plan.description) + "\n" %> + <% end %> +<%= _("Last modified: ") + l(@plan.updated_at.to_date, formats: :short) %> +<%= _("Copyright information:") %> +<%= "\t" + _(" The above plan creator(s) have agreed that others may use as much of the text of this plan as they would like in their own plans, and customise it as necessary. You do not need to credit the creator(s) as the source of the language used, but using any of the plan's text does not imply that the creator(s) endorse, or have any relationship to, your project or proposal") %> +<%= "----------------------------------------------------------\n" %> +<% end %> + +<% @hash[:phases].each do |phase| %> +<%= (@hash[:phases].length > 1 ? "#{phase[:title]}" : "") %> + <% phase[:sections].each do |section| %> + <% if @show_sections_questions %> +<%= "#{section[:title]}\n" %> + <% end %> + <% section[:questions].each do |question| %> + <% if @show_sections_questions %> + <%# text in this case is an array to accomodate for option_based %> + <% if question[:text].length > 1 %> + <% question[:text].each do |txt| %> +<%= "#{strip_tags(txt.gsub(//, '\n'))}\n" %> + <% end %> + <% else %> +<%= "#{strip_tags(question[:text][0].gsub(/(\s||<\/td>| )*(<\/tr>|)/,""))}\n" if question[:text].present? && question[:text][0].present? %> + <% end %> + <% end %> + <% answer = @plan.answer(question[:id], false) %> + <% blank = (answer.present? && answer.is_valid?) ? answer.text.gsub(/<\/?p>/, '').gsub(//, '\n').chomp.blank? : true %> + <% if blank && @show_unanswered %> +<%= " #{_("Question not answered.")}\n\n" %> + <% elsif !blank %> + <% if answer.question_options.length > 0 %> + <% answer.question_options.each do |opt| %> +<%= " #{opt.text}\n" %> + <% end %> + <% else %> +<%= " #{strip_tags(answer.text.gsub(/<\/?p>/, '').gsub(//, '\n').chomp)}\n\n" if answer.text.present? %> + <% end %> + <% end %> + <% end %> + <% end %> +<% end %> + +<%= "----------------------------------------------------------" %> +<%= _("A Data Management Plan created using %{application_name}") % { application_name: Rails.configuration.branding[:application][:name] } %> \ No newline at end of file diff --git a/app/views/static_pages/about_us.html.erb b/app/views/static_pages/about_us.html.erb index 3b32241..e6f108e 100644 --- a/app/views/static_pages/about_us.html.erb +++ b/app/views/static_pages/about_us.html.erb @@ -1,67 +1,34 @@ - - - -

    - <%= _('About %{application_name}') % { :application_name => Rails.configuration.branding[:application][:name] } %> -

    -