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..c7184a9 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,79 @@ # ------------------------------------------------ # 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.1' +gem 'omniauth-shibboleth', '~> 1.2.1' +gem 'omniauth-orcid', '~> 1.2.1' #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 'wkhtmltopdf-binary', '~> 0.12.3' +gem 'thin', '~> 1.7' +gem 'wicked_pdf', '~> 1.1.0' gem 'htmltoword', '>= 0.7' -gem 'feedjira' +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 '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..a67f290 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) @@ -147,8 +144,6 @@ nokogiri rubyzip (>= 1.0) i18n (0.7.0) - i18n-js (3.0.0.rc14) - i18n (~> 0.6, >= 0.6.6) jbuilder (2.6.0) activesupport (>= 3.0.0, < 5.1) multi_json (~> 1.2) @@ -172,13 +167,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) @@ -318,17 +306,8 @@ thor (0.19.1) thread_safe (0.3.5) 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) 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,58 +330,51 @@ 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.7) + 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.1) + omniauth-orcid (~> 1.2.1) + 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) + 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) @@ -410,4 +382,4 @@ ruby 2.2.2p95 BUNDLED WITH - 1.14.6 + 1.13.2 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..5e3b826 100644 --- a/app/controllers/annotations_controller.rb +++ b/app/controllers/annotations_controller.rb @@ -2,108 +2,40 @@ 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 + redirect_to "#{admin_show_phase_path(question.section.phase.id)}?section_id=#{question.section.id}" 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 + redirect_to "#{admin_show_phase_path(parent_ids[0])}?section_id=#{parent_ids[1]}" end private @@ -117,4 +49,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..c61f03c 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -1,46 +1,77 @@ 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 + 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.save! + @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 + rescue ActiveRecord::RecordNotFound + @answer = Answer.new(p_params.merge({ user_id: current_user.id })) + @answer.lock_version = 1 + authorize @answer + @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 }, 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/application_controller.rb b/app/controllers/application_controller.rb index 2d3dcb0..6f8af58 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,21 @@ end def after_sign_in_path_for(resource) - session[:previous_url] || root_path + return request.referer || root_path unless request.referer.eql?(new_user_session_path) + root_path end def after_sign_up_path_for(resource) - session[:previous_url] || root_path + return request.referer || root_path unless request.referer.eql?(new_user_registration_path) + root_path end def after_sign_in_error_path_for(resource) - session[:previous_url] || root_path + request.referer || root_path end def after_sign_up_error_path_for(resource) - session[:previous_url] || root_path + request.referer || root_path end def authenticate_admin! @@ -80,6 +82,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 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..5f2108d --- /dev/null +++ b/app/controllers/concerns/paginable.rb @@ -0,0 +1,22 @@ +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 + # 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], scope: nil, locals: {}) + raise ArgumentError, 'scope should be an instance of ActiveRecord::Relation class' unless scope.is_a?(ActiveRecord::Relation) + raise ArgumentError, 'locals should be an instance of Hash' unless locals.is_a?(Hash) + render(layout: '/layouts/paginable', partial: partial, locals: { + controller: controller, + action: action, + # The scope is paginable if it has been chained with page method from kaminari which contains methods such as total_pages + paginable: scope.respond_to?(:total_pages), + scope: scope }.merge(locals)) + end + 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..90a7051 100644 --- a/app/controllers/guidances_controller.rb +++ b/app/controllers/guidances_controller.rb @@ -10,80 +10,122 @@ @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 - 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 - redirect_to admin_show_guidance_path(@guidance), notice: _('Guidance was successfully created.') + guidance_group = GuidanceGroup.where(id: guidance.guidance_group_id).first + unless guidance_group.nil? + if !guidance_group.published? || guidance_group.published.nil? + guidance_group.published = true + guidance_group.save + end + end + + if guidance.save + 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.') - 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') + guidance = Guidance.find(params[:id]) + authorize guidance + guidance.text = params["guidance-text"] + + guidance_group = GuidanceGroup.where(id: guidance.guidance_group_id).first + unless guidance_group.nil? + if !guidance_group.published? || guidance_group.published.nil? + guidance_group.published = true + guidance_group.save + end + end - render action: "admin_edit" + if guidance.update_attributes(guidance_params) + flash[:notice] = success_message(_('guidance'), _('saved')) + redirect_to(action: :admin_index) + else + 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 + if guidance.destroy + 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 + + 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..996ed7b 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -1,73 +1,100 @@ 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! + if params[:note][:answer_id].present? + @answer = Answer.find(params[:note][:answer_id]) + end + 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(r, 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 +103,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..54609ba --- /dev/null +++ b/app/controllers/org_admin/plans_controller.rb @@ -0,0 +1,63 @@ +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')}", + "#{_('Updated')}", + "#{_('Visibility')}" + ] + + plans = CSV.generate do |csv| + csv << header_cols + org.plans.includes(template: :org).each do |plan| + owner = plan.owner + csv << [ + "#{plan.title}", + "#{plan.template.title}", + "#{plan.owner.org.name}", + "#{plan.owner.name}", + "#{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..8cc5c73 --- /dev/null +++ b/app/controllers/org_admin/templates_controller.rb @@ -0,0 +1,571 @@ +module OrgAdmin + class TemplatesController < ApplicationController + include Paginable + 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, + scopes: { all: all_templates_hash[:scopes], orgs: own_hash[:scopes], funders: customizable_hash[:scopes] } + } + end + + # GET /org_admin/templates/new + # ----------------------------------------------------- + def new + authorize Template + 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'), _('created')) + else + @hash = @template.to_hash + flash[:alert] = failed_create_error(@template, _('template')) + 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) + + 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 + @template_hash = @template.to_hash + render('container', + locals: { + partial_path: 'edit', + template: @template, + current: @current, + template_hash: @template_hash + }) + 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.')}) + 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') }) + 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'), _('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'))}) + end + end + end + + # DELETE /org_admin/templates/:id + # ----------------------------------------------------- + def destroy + @template = Template.find(params[:id]) + 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'), _('removed')) + redirect_to org_admin_templates_path + else + @hash = @template.to_hash + flash[:alert] = failed_destroy_error(@template, _('template')) + render org_admin_templates_path + end + else + flash[:alert] = _('You cannot delete historical versions of this template.') + redirect_to org_admin_templates_path + end + else + flash[:alert] = _('You cannot delete a template that has been used to create plans.') + redirect_to org_admin_templates_path + 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).order(:version) + @current = Template.current(@template.dmptemplate_id) + end + + # GET /org_admin/templates/:id/customize + # ----------------------------------------------------- + def 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 edit_org_admin_template_path(customisation) + 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]) + 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) + end + + # GET /org_admin/templates/all/:page (AJAX) + # ----------------------------------------------------- + def all + authorize Template + # 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: 'templates_list', + scope: hash[:templates], + locals: { current_org: current_user.org.id, + customizations: hash[:customizations], + published: published, + scopes: hash[:scopes], + hide_actions: true } + end + + # GET /org_admin/templates/funders/:page (AJAX) + # ----------------------------------------------------- + def funders + authorize Template + # 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: 'funder_templates_list', + scope: hash[:templates], + locals: { current_org: current_user.org.id, + customizations: hash[:customizations], + published: published, + scopes: hash[:scopes] } + end + + # GET /org_admin/templates/orgs/:page (AJAX) + # ----------------------------------------------------- + def orgs + authorize Template + # 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: 'templates_list', + scope: hash[:templates], + locals: { current_org: current_user.org.id, + customizations: hash[:customizations], + published: published, + scopes: hash[:scopes], + hide_actions: false } + end + + # PUT /org_admin/templates/:id/copy (AJAX) + # ----------------------------------------------------- + def copy + @template = Template.find(params[:id]) + 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 was successfully copied.' + redirect_to edit_org_admin_template_path(id: new_copy.id, edit: true), notice: _('Information was successfully created.') + else + flash[:alert] = failed_create_error(new_copy, _('template')) + end + + end + + # PUT /org_admin/templates/:id/publish (AJAX) + # ----------------------------------------------------- + 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.') + + 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 "#{org_admin_templates_path}#{template.customization_of.present? ? '#funder-templates' : '#organisation-templates'}" + end + end + + # PUT /org_admin/templates/:id/unpublish (AJAX) + # ----------------------------------------------------- + def unpublish + template = Template.find(params[:id]) + authorize template + + if template.nil? + flash[:alert] = _('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 "#{org_admin_templates_path}#{template.customization_of.present? ? '#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? + 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 + 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 + end + end + + # If no templates were available use the generic templates + 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 + + # Applies scoping to the template list + def apply_scoping(scope, customizable = false, all = false) + if customizable + # Retrieve all of the publicly visible published funder templates + orgs = Org.funder.where.not(id: current_user.org.id) + # Include the default template in the list of funder templates + orgs << Template.default.org unless current_user.org == Template.default.org + + templates = Template.get_public_published_template_versions(orgs) + + # If the user is an Org Admin look for customizations to funder templates + customizations = {} + if current_user.can_org_admin? + families = templates.collect(&:dmptemplate_id).uniq + Template.org_customizations(families, current_user.org_id).each do |customization| + customizations[customization.customization_of] = customization if customization.present? + end + end + + scopes = calculate_table_scopes(templates, customizations) + + # We scope based on the customizations if the user is NOT a super admin + if params[:scope].present? && params[:scope] != 'all' + if current_user.can_super_admin? + templates = templates.where(published: true) if params[:scope] == 'published' + templates = templates.where(published: false) if params[:scope] == 'unpublished' + else + scoped = templates.select do |t| + c = customizations[t.dmptemplate_id] + (params[:scope] == 'unpublished' && (!c.present? || !c.published?)) || (params[:scope] == 'published' && c.present? && c.published?) + end + templates = Template.where(id: scoped.collect(&:id)) + end + end + + else + # If we're collecting all templates + if all + templates = Template.get_latest_template_versions(Org.all) + else + templates = Template.get_latest_template_versions(Org.where(id: current_user.org.id)) + end + + scopes = calculate_table_scopes(templates, {}) + + if params[:scope].present? && params[:scope] != 'all' + templates = templates.where(published: true) if params[:scope] == 'published' + templates = templates.where(published: false) if params[:scope] == 'unpublished' + end + end + + { templates: templates, + customizations: customizations || {}, + scopes: scopes } + end + + # Gets the nbr of templates and nbr of published/unpublished templates + def calculate_table_scopes(templates, customizations) + scopes = { all: templates.length, published: 0, unpublished: 0, dmptemplate_ids: templates.collect(&:dmptemplate_id).uniq } + templates.each do |t| + # If we have customizations use their status + if customizations.respond_to?(:keys) + c = customizations[t.dmptemplate_id] + # If the template was not customized then its unpublished + if c.nil? + scopes[:unpublished] += 1 + else + scopes[:published] += 1 if c.published? + scopes[:unpublished] += 1 unless c.published? + end + else + # Otherwise just use the template's published status + scopes[:published] += 1 if t.published? + scopes[:unpublished] += 1 unless t.published? + end + end + scopes + end + + def get_publication_dates(family_ids) + published = {} + lives = Template.live(family_ids) + lives.each do |live| + published[live.dmptemplate_id] = live.updated_at + end + published + 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..8a6deae 100644 --- a/app/controllers/orgs_controller.rb +++ b/app/controllers/orgs_controller.rb @@ -1,20 +1,14 @@ 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.links = {"org": []} unless @org.links.present? end ## @@ -23,30 +17,69 @@ 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.') + if @org.update_attributes(attrs) + redirect_to "#{admin_edit_org_path(@org)}\##{tab}", notice: success_message(_('organisation'), _('saved')) 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 institutions 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 institution does not seem to be properly configured.') + redirect_to shibboleth_ds_path + end + + else + flash[:notice] = _('Please choose an institution') + 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, + :feedback_enabled, :feedback_email_subject, :feedback_email_msg) end end diff --git a/app/controllers/paginable/plans_controller.rb b/app/controllers/paginable/plans_controller.rb new file mode 100644 index 0000000..e9e4311 --- /dev/null +++ b/app/controllers/paginable/plans_controller.rb @@ -0,0 +1,23 @@ +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? + if params[:page] == 'ALL' + plans = current_user.active_plans + else + plans = current_user.active_plans.page(params[:page]) + end + paginable_renderise(partial: 'privately_visible', scope: plans) + 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? + if params[:page] == 'ALL' + plans = Plan.organisationally_or_publicly_visible(current_user) + else + plans = Plan.organisationally_or_publicly_visible(current_user).page(params[:page]) + end + paginable_renderise(partial: 'organisationally_or_publicly_visible', scope: plans) + end +end \ No newline at end of file diff --git a/app/controllers/paginable/themes_controller.rb b/app/controllers/paginable/themes_controller.rb new file mode 100644 index 0000000..8a4bb30 --- /dev/null +++ b/app/controllers/paginable/themes_controller.rb @@ -0,0 +1,13 @@ +module Paginable + class ThemesController < ApplicationController + include Paginable + # /paginable/themes/index/:page + def index + authorize(Theme) + themes = params[:page] == 'ALL' ? + Theme.updated_at_desc : + Theme.updated_at_desc.page(params[:page]) + paginable_renderise(partial: 'index', scope: themes) + 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..7637573 --- /dev/null +++ b/app/controllers/paginable/users_controller.rb @@ -0,0 +1,11 @@ +class Paginable::UsersController < ApplicationController + include Paginable + # /paginable/users/index/:page + def index + authorize User + users = params[:page] == 'ALL' ? + current_user.org.users.includes(:roles) : + current_user.org.users.includes(:roles).page(params[:page]) + paginable_renderise(partial: 'index', scope: users) + end +end \ No newline at end of file diff --git a/app/controllers/phases_controller.rb b/app/controllers/phases_controller.rb index 82633c7..7fd7078 100644 --- a/app/controllers/phases_controller.rb +++ b/app/controllers/phases_controller.rb @@ -4,78 +4,31 @@ after_action :verify_authorized - # GET /plans/:plan_id/phases/:id/edit - def edit + # GET /plans/:plan_id/phases/:id/edit + def edit + plan = Plan.load_for_phase(params[:plan_id], params[:id]) - @plan = Plan.load_for_phase(params[:plan_id], params[:id]) # authorization done on plan so found in plan_policy - authorize @plan + authorize plan phase_id = params[:id].to_i - @phase = @plan.template.phases.first - @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 - + phase = plan.template.phases.select {|p| p.id == phase_id}.first + 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) + if !user_signed_in? then respond_to do |format| - format.html { redirect_to edit_user_registration_path } + format.html { redirect_to edit_user_registration_path } end + else + render('/phases/edit', locals: { plan: plan, phase: phase, readonly: readonly, + question_guidance: plan.guidance_by_question_as_hash, + guidance_groups: guidance_groups }) end end + # GET /plans/PLANID/phases/PHASEID/status.json def status @plan = Plan.eager_load(params[:plan_id]) @@ -89,34 +42,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(: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 @@ -125,6 +58,14 @@ else @original_org = @phase.template.org 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 + }) end @@ -143,6 +84,11 @@ @phase.template = @template authorize @phase @phase.number = @template.phases.count + 1 + render('/org_admin/templates/container', + locals: { + partial_path: 'admin_add', + template: @template + }) end @@ -157,11 +103,11 @@ @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), 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) end end @@ -175,7 +121,7 @@ @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), notice: success_message(_('phase'), _('saved')) else @sections = @phase.sections @template = @phase.template @@ -185,13 +131,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) end end @@ -204,7 +150,7 @@ @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), notice: success_message(_('phase'), _('deleted')) else @sections = @phase.sections @@ -214,7 +160,7 @@ @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 diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index 3c713bb..3bae0a0 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 = current_user.active_plans.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.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? @@ -61,39 +73,37 @@ @plan.assign_creator(current_user) # pre-select org's guidance - ggs = GuidanceGroup.where(org_id: plan_params[:org_id], - optional_subset: false, - published: true) + ggs = GuidanceGroup.where(org_id: org_id, 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 @@ -105,11 +115,15 @@ 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 = [] @@ -119,85 +133,56 @@ @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]) + @all_ggs_grouped_by_org.delete(org) + 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 + + # Save the guidance group selections + guidance_group_ids = params[:guidance_group_ids].blank? ? [] : params[:guidance_group_ids].map(&:to_i) + save_guidance_selections(guidance_group_ids) 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 } + 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[:notice] = failed_update_error(@plan, _('plan')) + flash[:alert] = failed_update_error(@plan, _('plan')) format.html { render action: "edit" } + format.json {render json: {code: 0, msg: failed_update_error(@plan, _('plan'))}} 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 +191,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,10 +225,12 @@ 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 @@ -252,9 +239,8 @@ @plan = Plan.find(params[:id]) authorize @plan - # If no format is specified, default to PDF - params[:format] = 'pdf' if params[:format].nil? - + # We should re-work this into something more useful than creating a new one + # every time a plan gets exported @exported_plan = ExportedPlan.new.tap do |ep| ep.plan = @plan ep.phase_id = params[:phase_id] @@ -267,14 +253,23 @@ end end + # setup some variables we will need in the export views + # here, if custom sections are included, we want all sections, otherwise, + # we only want those which are not modifiable, as they are the original template + @sections = params[:export][:custom_sections].present? || @plan.template.customization_of.nil? ? @exported_plan.sections.order(:number) : Phase.find(params[:phase_id]).sections.where(modifiable: false) # prefetch questions? + @unanswered_questions = params[:export][:unanswered_questions].present? + @question_headings = params[:export][:question_headings].present? + @show_details = params[:export][:project_details].present? + + 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.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 @formatting = @plan.settings(:export).formatting @@ -289,19 +284,118 @@ 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}.') % + @phase_options = @plan.phases.order(:number).pluck(:title,:id) + redirect_to download_plan_path(@plan), alert: _('%{format} is not a valid exporting format. Available formats to export are %{available_formats}.') % {format: params[:format], available_formats: ExportedPlan::VALID_FORMATS.to_s} 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 + 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 + def save_guidance_selections(guidance_group_ids) + 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 + 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 +458,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..fe8c78a --- /dev/null +++ b/app/controllers/public_pages_controller.rb @@ -0,0 +1,125 @@ +class PublicPagesController < ApplicationController + after_action :verify_authorized, except: [:template_index, :plan_index] + + # GET template_index + # ----------------------------------------------------- + def template_index + template_ids = Template.where(org_id: Org.funder.pluck(:id), visibility: Template.visibilities[:publicly_visible]).valid.pluck(:dmptemplate_id).uniq << Template.where(is_default: true, published: true).pluck(:dmptemplate_id) + @templates = [] + template_ids.flatten.each do |tid| + t = Template.live(tid) + @templates << t unless t.nil? + end + @templates = @templates.sort{|x,y| x.title <=> y.title } if @templates.count > 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]}}).find(@template.id) + + begin + file_name = @template.title.gsub(/ /, "_") + respond_to do |format| + format.docx { render docx: 'template_export', filename: "#{file_name}.docx" } + format.pdf do + @formatting = Settings::Template::DEFAULT_SETTINGS[: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 # What scenario is this triggered in? it's common to our export pages + #send back to public_index page + redirect_to template_index_path, alert: _('Unable to download the DMP Template at this time.') + end + + end + + # GET plan_export/:id + # ------------------------------------------------------------- + def plan_export + @plan = Plan.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 + # This creates exported_plans with no user. + # Note for reviewers, The ExportedPlan model actually serves no purpose, except + # to store preferences for PDF export. These preferences could be moved into + # the prefs table for individual users, and a more semsible structure implimented + # to track the exports & formats(html/pdf/ect) of users. + @exported_plan = ExportedPlan.new.tap do |ep| + ep.plan = @plan + ep.phase_id = @plan.phases.first.id + ep.format = :pdf + plan_settings = @plan.settings(:export) + + Settings::Template::DEFAULT_SETTINGS.each do |key, value| + ep.settings(:export).send("#{key}=", plan_settings.send(key)) + end + end + # need to determine which phases to export + @a_q_ids = Answer.where(plan_id: @plan.id).pluck(:question_id).uniq + @a_s_ids = Question.where(id: @a_q_ids).pluck(:section_id).uniq + a_p_ids = Section.where(id: @a_s_ids).pluck(:phase_id).uniq + @phases = Phase.includes(sections: :questions).where(id: a_p_ids).order(:number) + # name of owner and any co-owners + @creator_text = @plan.owner.name(false) + @plan.roles.administrator.not_creator.each do |role| + @creator_text += ", " + role.user.name(false) + end + # Org name of plan owner + @affiliation = @plan.owner.org.name + # set the funder name + @funder = @plan.template.org.funder? ? @plan.template.org.name : nil + # set the template name and customizer name if applicable + @template = @plan.template.title + @customizer = "" + cust_questions = @plan.questions.where(modifiable: true).pluck(:id) + # if the template is customized, and has custom answered questions + if @plan.template.customization_of.present? && Answer.where(plan_id: @plan.id, question_id: cust_questions).present? + @customizer = _(" Customised By: ") + @plan.template.org.name + end + + + begin + @exported_plan.save! + file_name = @plan.title.gsub(/ /, "_") + + respond_to do |format| + 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 + # send to the public_index page + redirect_to public_index_plan_path, alert: _('Unable to download the DMP at this time.') + end + end + + # GET /plans_index + # ------------------------------------------------------------------------------------ + def plan_index + @plans = Plan.publicly_visible + @plans = @plans.sort{|x,y| x.title <=> y.title } if @plans.count > 1 + end +end diff --git a/app/controllers/questions_controller.rb b/app/controllers/questions_controller.rb index f3250bb..54a8887 100644 --- a/app/controllers/questions_controller.rb +++ b/app/controllers/questions_controller.rb @@ -24,7 +24,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), notice: success_message(_('question'), _('created')) else @edit = (@question.section.phase.template.org == current_user.org) @open = true @@ -34,16 +34,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) end rescue ActionController::ParameterMissing => e - flash[:notice] = e.message + flash[:alert] = e.message end end @@ -83,7 +83,7 @@ @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), notice: success_message(_('question'), _('saved')) else @edit = (@phase.template.org == current_user.org) @open = true @@ -91,13 +91,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) end end @@ -111,9 +111,9 @@ @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), 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), alert: failed_destroy_error(@question, 'question') end end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index a628929..09f7e01 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,18 +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') + 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, + flash[:notice] = _('It does not look like you have setup an account with us yet. Please fill in the following information to complete your registration.') + UserIdentifier.create(identifier_scheme: oauth[:provider].upcase, identifier: oauth[:uid], user: @user) end @@ -35,19 +41,20 @@ # POST /resource def create - #logger.debug "#{sign_up_params}" - if sign_up_params[:accept_terms] != "1" then + if !sign_up_params[:accept_terms] then redirect_to after_sign_up_error_path_for(resource), alert: _('You must accept the terms and conditions to register.') 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 - else + existing_user = User.where_case_insensitive('email', sign_up_params[:email]).first + if existing_user.present? + if existing_user.accept_terms? redirect_to after_sign_up_error_path_for(resource), alert: _('That email address is already registered.') + return + else + existing_user.destroy # Destroys the existing user since the accept terms are nil/false. + # Note any existing role for that user will be deleted too. Added to accommodate issue at: + # https://github.com/DMPRoadmap/roadmap/issues/322 end - else + end build_resource(sign_up_params) if resource.save if resource.active_for_authentication? @@ -57,26 +64,28 @@ 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,32 +116,21 @@ 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 # user did not change their email so no pwd required + successfully_updated = current_user.update_without_password(update_params) end else # password not required successfully_updated = current_user.update_without_password(update_params) @@ -154,18 +152,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 # Sign in the user bypassing validation in case his password changed + 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 render "edit" 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..20b105e 100644 --- a/app/controllers/sections_controller.rb +++ b/app/controllers/sections_controller.rb @@ -14,20 +14,20 @@ @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.') + :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) end end @@ -36,26 +36,26 @@ 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 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), 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) end end @@ -70,7 +70,7 @@ @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), notice: success_message(_('section'), _('deleted')) else @edit = (@phase.template.org == current_user.org) @open = true @@ -78,7 +78,7 @@ @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 diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index a4154d9..0c9730d 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,17 +1,8 @@ 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 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/themes_controller.rb b/app/controllers/super_admin/themes_controller.rb new file mode 100644 index 0000000..754e79c --- /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.updated_at_desc.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/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..3cafb36 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,7 @@ # 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') + flash[:notice] = _('It does not look like you have setup an account with us yet. Please fill in the following information to complete your registration.') redirect_to new_user_registration_url # Otherwise sign them in @@ -36,7 +37,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 +52,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..598ded8 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,7 @@ # 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) + @users = current_user.org.users.includes(:roles).page(1) end ## @@ -49,9 +52,65 @@ 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 + redirect_to({controller: 'users', action: 'admin_index'}, {notice: success_message(_('permissions'), _('saved'))}) # helpers.success key does not exist, replaced with a generic string else - flash[:notice] = failed_update_error(@user, _('user')) + flash[:alert] = 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 + org = Org.find(org_swap_params[:org_id]) + 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..1d1e2fe 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -15,19 +15,22 @@ 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 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..a8a6692 --- /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 => _('Grant permissions'), + :modify_templates => _('Modify templates'), + :modify_guidance => _('Modify guidance'), + :use_api => _('API rights'), + :change_org_details => _('Change 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..0187c2a 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 "#{_('Institution')}" + 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 _('Institution: anyone at my institution 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/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..7f8bac9 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -1,5 +1,5 @@ class Answer < ActiveRecord::Base - + ## # Associations belongs_to :question @@ -13,30 +13,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 +66,15 @@ else # (e.g. textarea or textfield question formats) return self.text.present? end - else - return false end + return false + end + # Returns all the notes for an instance answer whose archived is nil or false. The Array is ordered by updated_at (descending) + def non_archived_notes + answer = Answer.includes(:notes).where({ id: self.id, notes: { archived: [nil, false] } }).order('notes.updated_at DESC').first + if !answer.nil? + return answer.notes.to_a + end + return [] 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..bc42e4b 100644 --- a/app/models/exported_plan.rb +++ b/app/models/exported_plan.rb @@ -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(/
  • /, ' * ') ) 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..24c1cc0 100644 --- a/app/models/guidance.rb +++ b/app/models/guidance.rb @@ -98,7 +98,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 +119,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 +129,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..8974a37 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 @@ -25,7 +25,7 @@ - + ## # Converts the current guidance group to a string containing the display name. @@ -49,7 +49,7 @@ # @return [Array] 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 +57,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 +106,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..1a41c1e 100644 --- a/app/models/org.rb +++ b/app/models/org.rb @@ -2,11 +2,15 @@ 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 @@ -19,16 +23,19 @@ 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, + :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 - + :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 @@ -37,7 +44,6 @@ 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 +60,15 @@ # 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) } + 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 @@ -121,98 +126,6 @@ end ## - # finds all organisations who have a parent of the passed organisation type - # - # @param [String] the name of an organisation type - # @return [Array] -=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] - 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] - 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
    ] 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] 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] published dmptemplates @@ -228,16 +141,31 @@ 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 + 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..872abe2 --- /dev/null +++ b/app/models/org_identifier.rb @@ -0,0 +1,14 @@ +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) + # Make sure that the attributes are stored as a hash! + self.attrs = (hash.is_a?(Hash) ? hash : {attribute: hash.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..1beaae0 100644 --- a/app/models/phase.rb +++ b/app/models/phase.rb @@ -30,7 +30,9 @@ 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? @@ -106,4 +108,23 @@ return phase_copy end + # Returns the number of answered question for the phase. It is assumed that the plan_id passed + # has this phase instance. + def num_answered_questions(plan_id) + n = 0 + self.sections.each do |s| + n+= s.num_answered_questions(plan_id) + end + return n + 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 b32f3f6..09224df 100644 --- a/app/models/plan.rb +++ b/app/models/plan.rb @@ -1,5 +1,5 @@ class Plan < ActiveRecord::Base - + include ConditionalUserMailer before_validation :set_creation_defaults ## @@ -20,7 +20,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 @@ -28,17 +27,18 @@ ## # Possibly needed for active_admin # -relies on protected_attributes gem as syntax depricated in rails 4.2 - attr_accessible :locked, :project_id, :version_id, :version, :plan_sections, + attr_accessible :locked, :project_id, :version_id, :version, :plan_sections, :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 enum visibility: [:organisationally_visible, :publicly_visible, :is_test, :privately_visible] - #TODO: work out why this messes up plan creation : + #TODO: work out why this messes up plan creation : # briley: Removed reliance on :users, its really on :roles (shouldn't have a plan without at least a creator right?) It should be ok like this though now # validates :template, :title, presence: true @@ -54,6 +54,20 @@ # 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) } + # Retrieves any plan organisationally or publicly visible for a given org id + scope :organisationally_or_publicly_visible, -> (user) { + Plan.includes(:template) + .where({ + visibility: [visibilities[:organisationally_visible], visibilities[:publicly_visible]], + "templates.org_id": user.org_id}) + .where(['NOT EXISTS (SELECT 1 FROM roles WHERE plan_id = plans.id AND user_id = ?)', user.id]) + .order(:title => :asc) + } + + # 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 +94,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 +119,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 +187,158 @@ end return ggroups.uniq 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 } - 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 - end - end - end - 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 - # 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 } + # 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 + + if self.save! + # Send an email confirmation to the owners and co-owners + deliver_if(recipients: self.owner_and_coowners, key: 'users.feedback_requested') do |r| + UserMailer.feedback_confirmation(r, self, user).deliver_now + 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 - - return guidances end - - - ## - # adds the given guidance to a hash indexed by a passed guidance group and theme - # - # @param guidance_array [{GuidanceGroup => {Theme => Array}}] 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}}] 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 + deliver_if(recipients: self.owner_and_coowners, key: 'users.feedback_provided') do |r| + UserMailer.feedback_notification(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.where(published: true, id: guidance_groups_ids) + + # 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 +346,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 @@ -369,7 +493,7 @@ format = rec.qformat answer = nil - if qa_map.has_key?(qid) + if qa_map.has_key?(qid) answer = qa_map[qid] end @@ -423,7 +547,7 @@ opt_hash[aid] << optid end - status["questions"].each_key do |questionid| + status["questions"].each_key do |questionid| answerid = status["questions"][questionid]["answer_id"] status["questions"][questionid]["answer_option_ids"] = opt_hash[answerid] end @@ -433,199 +557,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 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 @@ -636,83 +567,6 @@ user_id = user_id.id if user_id.is_a?(User) 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 +596,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 +623,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] 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 +652,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,27 +679,6 @@ 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 n = 0 @@ -991,20 +719,73 @@ end def self.load_for_phase(id, phase_id) - Plan.includes( - [template: [ - {phases: {sections: {questions: [{answers: :notes}, :annotations, :question_format, :themes]}}}, - {customizations: :org}, - :org - ], - plans_guidance_groups: {guidance_group: {guidances: :themes}} - ]).where(id: id, phases: { id: phase_id }).first +# Plan.includes( +# [template: [ +# {phases: {sections: {questions: [{answers: :notes}, :annotations, :question_format, :themes]}}}, +# {customizations: :org}, +# :org +# ], +# plans_guidance_groups: {guidance_group: {guidances: :themes}} +# ]).where(id: id, phases: { id: phase_id }).first + + Plan.joins(:phases).where('plans.id = ? AND phases.id = ?', id, phase_id).includes(:template, :sections, :questions, :answers, :notes).first 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 => _('institutional'), + :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 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 @@ -1048,12 +829,12 @@ end role.save - - # This is necessary because we're creating the associated record but not assigning it + + # This is necessary because we're creating the associated record but not assigning it # to roles. Auto-saving like this may be confusing when coding upstream in a controller, - # view or api. Should probably change this to: + # view or api. Should probably change this to: # self.roles << role - # and then let the save be called manually via: + # and then let the save be called manually via: # plan.save! #self.reload end @@ -1077,66 +858,64 @@ ## - # 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 # -------------------------------------------------------- def set_creation_defaults # Only run this before_validation because rails fires this before save/create - if self.id.nil? + 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/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/template.rb b/app/models/template.rb index f8a7e6a..7f0d120 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,6 +39,56 @@ 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]).order(:title => :asc) } + + # 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) + } # 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) @@ -40,7 +101,11 @@ # 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 +117,41 @@ # 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).order('orgs.name, templates.title') 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).order('orgs.name, templates.title') + 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]).order('orgs.name, templates.title') + end + ## # deep copy the given template and all of it's associations # @@ -149,6 +242,7 @@ self.visibility = 1 self.is_default = false 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? diff --git a/app/models/theme.rb b/app/models/theme.rb index bd787f6..5a95c48 100644 --- a/app/models/theme.rb +++ b/app/models/theme.rb @@ -15,12 +15,7 @@ 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 :updated_at_desc, -> { self.all.order(updated_at: 'DESC') } ## # returns the title of the theme # diff --git a/app/models/user.rb b/app/models/user.rb index f35abe3..3089e93 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,17 @@ 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']) + } # EVALUATE CLASS AND INSTANCE METHODS BELOW # @@ -89,6 +86,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) @@ -214,17 +220,6 @@ 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! @@ -244,8 +239,9 @@ 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) + deliver_if(recipients: self, key: 'users.admin_privileges') do |r| + UserMailer.api_token_granted_notification(r).deliver_now + end end end @@ -264,6 +260,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 +293,9 @@ ## # 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 - 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/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..3bcc0d1 100644 --- a/app/policies/guidance_policy.rb +++ b/app/policies/guidance_policy.rb @@ -35,6 +35,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 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..fd67d33 100644 --- a/app/policies/org_policy.rb +++ b/app/policies/org_policy.rb @@ -30,5 +30,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/plan_policy.rb b/app/policies/plan_policy.rb index aa99a2b..5d63d92 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,33 @@ 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 + 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..4721351 --- /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.template.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..abe3184 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -19,6 +19,11 @@ @user.can_grant_permissions? && (@users.org_id == @user.org_id) 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 Annotations') %>

    -<%= form_tag admin_create_annotation_path , class: 'add_annotation_form' do %> - - - - - - - - - -
    <%= _('Example Answer')%> -
      -
    • <%= text_area_tag :example_answer_text, nil, rows: 5 %> -
    • -
    -
    <%= _('Guidance')%> -
      -
    • <%= text_area_tag :guidance_text, nil, rows: 5 %> -
    • -
    -
    -
    - - -
    - <%= 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" %> -
    -<%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 @@ - -<%= 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 %> - - - - - - - - - - -
    <%= _('Example Answer')%> -
      -
    • <%= text_area_tag :example_answer_text, example_answer_text, rows: 5 %>
    • -
    -
    <%= _('Guidance')%> -
      -
    • <%= text_area_tag :guidance_text, guidance_text, rows: 5 %>
    • -
    -
    -
    - - -
    - <%= 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' %> -
    -<% 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 @@ + +<%= form_tag options[:url], method: options[:method] do %> +
    +
    + <%= label_tag :example_answer_text, _('Example answer'), class: "control-label" %> + <%= text_area_tag :example_answer_text, (example_answer.present? ? example_answer.text : ''), class: "question" %> +
    + +
    + <%= label_tag :guidance_text, _('Guidance'), class: 'control-label' %> + <%= text_area_tag :guidance_text, (guidance.present? ? guidance.text : ''), class: 'question' %> +
    + +
    +
    + <%= 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' %> +
    +
    +
    +<% 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? %> + > + <% if example_answer.present? %> +
    <%= _('Example answer') %>
    +
    <%= raw example_answer.text %>
    + <% end %> + <% if guidance.present? %> +
    <%= _('Guidance') %>
    +
    <%= raw guidance.text %>
    + <% end %> + +<% 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 @@ - -

    <%= _('Annotations') %>

    - - <% if example_answer.present? %> - - - - - <% end %> - <% if guidance.present? %> - - - - - <% end %> -
    - <%= _('Example Answer')%> - <%= raw example_answer.text %>
    - <%= _('Guidance')%> - <%= raw guidance.text %>
    -
    - -
    - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - <%= link_to _('Edit Annotations'), '# ', class: "btn btn-primary edit_form_for_annotations"%> -
    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..1a34b49 100644 --- a/app/views/answers/_locking.html.erb +++ b/app/views/answers/_locking.html.erb @@ -2,4 +2,4 @@

    <%= _('The following answer cannot be saved') %>

    <%= render partial: '/answers/new_edit', locals: { question: question, answer: answer, readonly: true } %>

    <%= _('since %{name} saved the answer below while you were editing. Please, combine your changes and then save the answer again.') % { name: user.name} %>

    - + \ 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..9968de8 100644 --- a/app/views/answers/_new_edit.html.erb +++ b/app/views/answers/_new_edit.html.erb @@ -1,101 +1,33 @@ +<%# locals: { question, answer, readonly } %> -<% 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| %> -
    - <% 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 %> - - -

    - <%= raw question.text %> -

    - - - <% 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? %> -
    - - <%="#{annotation.org.abbreviation} "%> <%=_('Example of answer')%> - - -
    -

    - <%= raw annotation.text %> -

    -
    -
    - <% end %> - <% end %> - - <% if question.option_based? %> - <% options = question.question_options.by_number %> - <% if q_format.checkbox? %> -
      - <% options.each do |op| %> -
    1. - <%= f.check_box(:question_option_ids, { multiple: true, checked: answer.has_question_option(op.id), disabled: readonly }, op.id, nil) %> - <%= raw op.text %> -
    2. - <% end %> -
    - <% elsif q_format.radiobuttons? %> -
      - <% options.each do |op| %> -
    1. - <%= 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 %> -
    2. - <% end %> -
    - <% elsif q_format.dropdown? || q_format.multiselectbox? %> - <% - options_html = "" - options.each do |op| - options_html += answer.has_question_option(op.id) ? - "" : - "" - end - %> - <%= select_tag('answer[question_option_ids]', raw(options_html), - {multiple: q_format.multiselectbox?, include_blank: q_format.dropdown?, disabled: readonly }) %> - <% end %> - - <% if question.option_comment_display == true %> - <%= label_tag('answer[text]', _('Comment')) %> - <% if readonly %> -

    <%= raw(answer.text) %>

    - <% 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 %> - - <% if q_format.textfield? %> - <% if readonly %> -

    <%= strip_tags(answer.text) %>

    - <% else %> - <%= text_field_tag('answer[text]', strip_tags(answer.text)) %> - <% end %> - <% elsif q_format.textarea? %> - <% if readonly %> -

    <%= raw(answer.text) %>

    - <% 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 %> - - <% end %> -
    - <% 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 %> + <% end %> + + <% annotation = question.first_example_answer %> + <% if annotation.present? && annotation.text.present? %> +
    + + <%="#{annotation.org.abbreviation} "%> <%=_('example answer')%> + +
    + <%= raw annotation.text %> +
    +
    + <% end %> +
    > + <% if question.option_based? %> + <%= 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 }) %> + <% end %> + <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %> +
    +<% end %> \ No newline at end of file diff --git a/app/views/answers/_status.html.erb b/app/views/answers/_status.html.erb index ddf8dcf..3d3dcc1 100644 --- a/app/views/answers/_status.html.erb +++ b/app/views/answers/_status.html.erb @@ -1,9 +1,12 @@ - - - -
    - <% if answer.updated_at.blank? %> - <%= _('Not answered yet') %> - <% else %> - <%= _('Answered')%> <%= answer.updated_at.iso8601 %><%= _(' by')%> <%= answer.user.name %> - <% end %> \ No newline at end of file +<%# locals: { answer } %> + + + +<% if answer.is_valid? %> +

    + <%= _('Answered')%> + <%= _(' by %{user_name}') %{ :user_name => answer.user.name } if answer.user.present? %> +

    +<% 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 %> +
    + <%= 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) %> +
    +<% end %> +
    + <%= 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) %> +
    +<% if ContactUs.require_subject %> +
    + <%= f.label(:subject, _('Subject')) %> + <%= f.text_field(:subject, + class: "form-control", + "aria-required": true) %> +
    +<% end %> +
    + <%= f.label(:message, _('Message')) %> + <%= f.text_area(:message, + class: "form-control", + rows: 10, + "aria-required": true) %> +
    +<% if !user_signed_in? then %> +
    + <%= label_tag(nil, _('Security check')) %> + <%= recaptcha_tags %> +
    +<% 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 @@ + +
    + <%= Rails.configuration.branding[:organisation][:name] %>
    + <% [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 %>
    +<% end %> +<% end %> +
    +<% +telephone=Rails.configuration.branding[:organisation][:telephone] +if telephone.present? %> + <%= "#{_('Helpline')} #{telephone}" %> +<% end %> +<% +email=Rails.configuration.branding[:organisation][:email] +if email.present? %> +
    + <%= _('Email') %> + "><%= email %> +
    +<% end %> +<% +link=Rails.configuration.branding[:organisation][:google_maps_link] +if link.present? %> + +<% 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" %> - -

    - <%= _("Contact Us") %> -

    -

    - <%= raw _('%{application_name} is provided by the %{organisation_name}. You can find out more about us on our website. 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]} %> -

    -
    - -
    -
    - <%= form_for @contact, url: contacts_path, html: {class: "roadmap-form"} do |f| %> -
    - <% if ContactUs.require_name %> -
    - <%= 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? %> -

    <%= f.object.errors[:name].join(_(" and ")) %>

    - <% end %> -
    - <% end %> - -
    - <%= 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? %> -

    <%= f.object.errors[:email].join(_(" and ")) %>

    - <% end %> -
    - - <% if ContactUs.require_subject %> -
    - <%= f.label :subject, (_('Subject') + content_tag(:abbr, "*", class: "required")).html_safe %> - <%= f.text_field :subject %> - <% if f.object.errors[:subject].present? %> -

    <%= f.object.errors[:subject].join(_(" and ")) %>

    - <% end %> -
    - <% end %> - -
    - <%= 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? %> -

    <%= f.object.errors[:message].join(_(" and ")) %>

    - <% end %> -
    - - <% if !user_signed_in? then %> -
    - <%= t('helpers.security_check') %> - <%= recaptcha_tags %> -
    - <% end %> - -
    - - <%= render partial: 'shared/accessible_submit_button', - locals: {id: 'create_contact_submit', - val: 'Submit', - disabled_initially: true, - tooltip: _('Fill in the required fields')} %> -
    -
    - - <% end %> -
    +
    +
    +

    <%= _("Contact Us") %>

    +

    + <%= raw _('%{application_name} is provided by the %{organisation_name}.
    You can find out more about us on our website. 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]} %> +

    - - -
    -
    - <%= raw _("
    • %{organisation_name}
    • -
    • %{organisation_address_line1}
    • -
    • %{organisation_address_line2}
    • -
    • %{organisation_address_line3}
    • -
    • %{organisation_address_line4}
    • -
    • %{organisation_address_country}
    • -
    -

    Helpline: %{organisation_telephone}

    -

    Email %{organisation_email}

    ") % - { 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]} %> - -
    - - -
    -
    -
    -
    +
    +
    + <%= render :partial => "contact_us/contacts/new_left" %> +
    +
    +
    + <%= render :partial => "contact_us/contacts/new_right" %> +
    +
    +
    \ 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 @@ -

    <%= t('.contact_us') %>

    -<%= 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 @@ -

    <%= t('.contact_us') %>

    -<%= 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 @@ -

    <%= t('custom_devise.resend_confirmation') %>

    - -<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %> - <%= devise_error_messages! %> - -
    <%= f.label :email %>
    - <%= f.email_field :email, :autofocus => true %>
    - -
    <%= f.submit t('custom_devise.resend_confirmation') %>
    -<% 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 @@ -

    <%= t "devise.invitations.edit.header" %>

    -
    - <%= form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => { :method => :put } do |f| %> - <%= devise_error_messages! %> - <%= f.hidden_field :invitation_token %> - -

    <%= f.label :password %>
    - <%= f.password_field :password %>

    - -

    <%= f.label :password_confirmation %>
    - <%= f.password_field :password_confirmation %>

    - -

    <%= f.submit t("devise.invitations.edit.submit_button") %>

    - <% end %> -
    +
    +
    +

    <%= _("Create an account to view the plan") %>

    +

    <%= _("You will need to create an account in order to accept your invitation to view the data management plan (DMP).") %>

    +
    +
    + +
    +
    + <%= 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 %> + +
    + <%= f.label(:password, _('New password'), class: 'control-label') %> + <%= f.password_field(:password, class: 'form-control', "aria-required": true, "data-validation": "password") %> +
    +
    + <%= f.label(:password_confirmation, _('Password confirmation'), class: 'control-label') %> + <%= f.password_field(:password_confirmation, class: 'form-control', "aria-required": true, "data-validation": "password") %> +
    + + <%= f.button(_('Create account'), class: "btn btn-default", type: "submit") %> + <% end %> +
    +
    + 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 @@ -

    <%= t "devise.invitations.new.header" %>

    -
    - <%= 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| -%> -

    <%= f.label field %>
    - <%= f.text_field field %>

    - <% end -%> - -

    <%= f.submit t("devise.invitations.new.submit_button") %>

    - <% end %> - -
    \ 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 %> -

    <%= _("Hello") %> <%= @resource.email %>,

    -

    <%= _("A colleague has invited you to contribute to their Data Management Plan at ") %> <%= link_to Rails.configuration.branding[:application][:name], root_url %>.

    -

    <%= 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") %>).

    -

    <%= _("If you don't want to accept the invitation, please ignore this email.") %>
    <%= _("Your account won't be created until you access the link above and set your password.") %>

    -

    <%=_('All the best,')%>
    <%= _('The ')%><%= Rails.configuration.branding[:application][:name] %><%=_(' team')%>.

    - -<% end %> +

    + <%= _('Hello %{user_email}') %{ :user_email => @resource.email } %> +

    +

    + <%= _('A colleague has invited you to contribute to their Data Management Plan in %{tool_name}') %{ :tool_name => tool_name } %> +

    +

    + <%= 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 }) %> +

    +

    + <%= _('All the best') %> +
    + <%= _('The %{tool_name} team') %{:tool_name => tool_name} %> +

    +

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

    +<% 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..5b9c77b 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 %> -

    <%= _("Hello ") %><%= @resource.email %>

    - -

    <%= _("Someone has requested a link to change your ") %><%= Rails.configuration.branding[:application][:name] %><%= _(" password. You can do this through the link below.") %>

    - -

    <%= link_to _('Change my password'), edit_password_url(@resource, :reset_password_token => @token) %>

    - -

    <%= _("If you didn't request this, please ignore this email.") %>

    -

    - <%= _("Many thanks,") %>
    <%= _('The ') %><%= Rails.configuration.branding[:application][:name] %><%= _(' team') %> + <%= _('Hello %{user_email}') %{ :user_email => @resource.email } %> +

    +

    + <%= _('Someone has requested a link to change your %{tool_name} password. You can do this through the link below.') %{ :tool_name => tool_name } %> +

    +

    <%= link_to _('Change my password'), edit_password_url(@resource, :reset_password_token => @token) %>

    +

    <%= _("If you didn't request this, please ignore this email.") %>

    +

    + <%= _('All the best') %> +
    + <%= _('The %{tool_name} team') %{:tool_name => tool_name} %> +

    +

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

    <% end %> \ No newline at end of file diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb index a82a78f..92d1218 100644 --- a/app/views/devise/passwords/edit.html.erb +++ b/app/views/devise/passwords/edit.html.erb @@ -1,26 +1,32 @@ -

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

    -
    +
    +
    +

    <%= _('Change your password') %>

    +
    +
    - <%= 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 %> -
    - - - - - - - - - -
    <%= t('helpers.new_password') %><%= f.password_field :password, :autofocus => true %>
    <%= t('helpers.password') %><%= f.password_field :password_confirmation %>
    -
    - <%= f.submit t('helpers.save'), :class => 'btn btn-primary' %> -
    -
    - <% end %> +
    +
    + <%= 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" %> +
    + <%= f.label(:password, _('New password'), class: 'control-label') %> + <%= f.password_field(:password, class: 'form-control', "aria-required": true) %> +
    +
    + <%= f.label(:password_confirmation, _('Password confirmation'), class: 'control-label') %> + <%= f.password_field(:password_confirmation, class: 'form-control', "aria-required": true) %> +
    +
    + +
    -
    + <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %> + + <%= render "devise/shared/links" %> + <% end %> +
    +
    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 @@ -

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

    -
    +
    +
    + <% unless @user.errors[:email].empty? %> +

    <%= _('The email address you entered is not registered.') %>

    + <% end %> + +

    <%= _('Forgot your password?') %>

    - <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> - <%= devise_error_messages! %> -
    - - - - - -
    <%= t("helpers.email") %> -

    <%= t("helpers.send_password_info")%>

    - -
    <%= f.email_field :email, :autofocus => true , :style => "width:95%;" %>
    - - -
    -
    - <%= f.submit t("helpers.send"), :class => "btn btn-primary", :id => "send_btn" %> -
    - <% end %> +

    <%= _('Please enter your email below and we will send you instructions on how to reset your password.') %>

    +
    +
    -<%= render "devise/shared/links" %> - -
    +
    +
    + <%= form_for resource, as: 'user', url: user_password_path, html: {method: :post, id: "user_request_reset_password_form"} do |f| %> +
    + <%= f.label(:email, _('Email'), class: 'control-label') %> + <%= f.email_field(:email, class: 'form-control', "aria-required": true) %> +
    + + <%= f.button(_('Send'), class: "btn btn-default", type: "submit") %> + <% end %> +
    +
    \ 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 deleted file mode 100644 index 226b270..0000000 --- a/app/views/devise/registrations/_external_identifier.html.erb +++ /dev/null @@ -1,21 +0,0 @@ -
    - <% 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: "") %> - <% end %> - <%= link_to ''.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 %> -
    diff --git a/app/views/devise/registrations/_external_identifier_orcid.html.erb b/app/views/devise/registrations/_external_identifier_orcid.html.erb new file mode 100644 index 0000000..16a6e02 --- /dev/null +++ b/app/views/devise/registrations/_external_identifier_orcid.html.erb @@ -0,0 +1,11 @@ +<% if id.nil? || id.identifier == '' %> + <%= link_to 'Create or connect your ORCID iD', Rails.application.routes.url_helpers.send("user_#{scheme.name.downcase}_omniauth_authorize_path"), id:"connect-orcid-button", target: '_blank', title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: ""), 'data-toggle': "tooltip" %> +<% else %> + <% if scheme.user_landing_url.nil? %> + <%= _("Your account has been linked to #{scheme.description}.") %> + <% else %> + <%= link_to (image_tag("#{scheme.logo_url}", id: 'orcid-id-logo', alt: scheme.description)) + "#{scheme.user_landing_url}/#{id.identifier}", "#{scheme.user_landing_url}/#{id.identifier}", id: 'orcid-id', target: '_blank', title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: " "), 'data-toggle': "tooltip" %> + <% end %> + <% title = _("Unlink your account from #{scheme.description}. You can link again at any time.") %> + <%= link_to ''.html_safe, destroy_user_identifier_path(id), method: :delete, title: title, 'aria-label': title, 'data-toggle': "tooltip", data: {confirm: _("Are you sure you want to unlink #{scheme.description} ID?")} %> +<% end %> diff --git a/app/views/devise/registrations/_external_identifier_shibboleth.html.erb b/app/views/devise/registrations/_external_identifier_shibboleth.html.erb new file mode 100644 index 0000000..0d09d9e --- /dev/null +++ b/app/views/devise/registrations/_external_identifier_shibboleth.html.erb @@ -0,0 +1,19 @@ +<% if id.nil? || id.identifier == '' %> + <%= link_to "#{_("Link your institutional credentials")}", + Rails.application.routes.url_helpers.send( + "user_#{scheme.name.downcase}_omniauth_authorize_path" + ), 'data-toggle': "tooltip", + title: _('Link your institutional credentials to access your account with them.') + %> +<% else %> + <% if scheme.user_landing_url.nil? %> + <%= _("Your account has been linked to #{scheme.description}.") %> + <% else %> + <%= link_to "#{_("Your account has been linked to your institution.")}", "#{scheme.user_landing_url}/#{id.identifier}", target: '_blank', 'data-toggle': "tooltip", + title: _('Your account has been linked to your institution. You can now login with that method.') %> + <% end %> + <% title = _("Unlink your account from your institution. You can link again at any time.") %> + <%= link_to ''.html_safe, + destroy_user_identifier_path(id), method: :delete, title: title, 'aria-label': title, 'data-toggle': "tooltip", + id: 'unlink-shibboleth', data: {confirm: _("Are you sure you want to unlink your institutional credentials?")} %> +<% 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..cc29f9f --- /dev/null +++ b/app/views/devise/registrations/_password_confirmation.html.erb @@ -0,0 +1,23 @@ + \ 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..72005e7 --- /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| %> + +

    <%= _('If you would like to change your password please complete the following fields.') %>

    + + <%= hidden_field_tag :skip_personal_details, "true" %> + +
    + <%= f.label(:current_password, _('Current password'), class: 'control-label') %> + <%= f.password_field(:current_password, class: "form-control", "aria-required": true ) %> +
    + +
    + <%= f.label(:password, _('New password'), class: 'control-label') %> + <%= f.password_field(:password, class: "form-control", "aria-required": true , id: 'user_new_password') %> +
    + +
    + <%= f.label(:password_confirmation, _('Password confirmation'), class: 'control-label') %> + <%= f.password_field(:password_confirmation, class: "form-control", "aria-required": true, ) %> +
    + +
    +
    + +
    +
    + +
    + <%= f.button(_('Save'), class: 'btn btn-default', type: "submit") %> +
    +<% 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..7884d37 --- /dev/null +++ b/app/views/devise/registrations/_personal_details.html.erb @@ -0,0 +1,76 @@ +<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {method: :put, id: 'personal_details_registration_form' }) do |f| %> +

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

    + +

    <%= _('You can edit any of the details below.') %>

    + <%= hidden_field_tag :unlink_flag, "false", id: 'unlink_flag' %> + <%= hidden_field_tag :skip_personal_details, "false" %> + +
    + <%= 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 %> +
    + +
    + <%= f.label(:firstname, _('First name'), class: 'control-label') %> + <%= f.text_field(:firstname, class: "form-control", "aria-required": true, value: @user.firstname) %> +
    + +
    + <%= f.label(:surname, _('Last name'), class: 'control-label') %> + <%= f.text_field(:surname, class: "form-control", "aria-required": true, value: @user.surname) %> +
    + + <% org_admin = (current_user.can_org_admin? && !current_user.can_super_admin?) %> +
    > + <%= render partial: "shared/my_org", locals: {f: f, default_org: @default_org, orgs: @orgs, allow_other_orgs: true} %> +
    + + <% if MANY_LANGUAGES %> +
    + <% 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") %> +
    + <% end %> + + <% @identifier_schemes.each do |scheme| %> +
    + <% if scheme.name == 'shibboleth' %> + + <% else %> + <%= label_tag(:scheme_name, scheme.name.capitalize, class: 'control-label') %> + <% end %> + +
    + <%= render partial: "external_identifier_#{scheme.name}", + locals: { scheme: scheme, + id: current_user.identifier_for(scheme)} %> +
    +
    + <% end %> + + <% unless @user.api_token.blank? %> +
    + <%= f.label(:api_token, _('API token'), class: 'control-label') %> + <%= @user.api_token %> +
    + <%= label_tag(:api_information, _('API Information'), class: 'control-label') %> + + <% end %> + +
    + <%= f.button(_('Save'), class: 'btn btn-default', type: "submit") %> +
    + + <%= 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..5937d18 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -1,117 +1,36 @@ -
    -

    <%= _('Edit profile') %>

    - -

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

    - - <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {method: :put, class: "roadmap-form white_background"}) do |f| %> - -
    - <%= _('You can edit any of the details below.') %> - - <%= hidden_field_tag :unlink_flag, "false", id: "unlink_flag" %> - -
    - <%= 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.') %> -
    -
    - <%= 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.') %> -
    -
    - <%= 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.') %> -
    -
    - <%= 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'} %> -
    - - <% if MANY_LANGUAGES %> -
    - <% lang = current_user.language.nil? ? FastGettext.default_locale : current_user.language.abbreviation %> - <%= f.label :language, _('Language') %> - -
    - <% end %> - - <% @identifier_schemes.each do |scheme| %> -
    - -
    - <%= render partial: 'external_identifier', - locals: {scheme: scheme, - id: current_user.identifier_for(scheme)} %> -
    -
    - <% end %> - - <% unless @user.api_token.blank? %> -
    - <%= f.label :api_token, _('API token') %><%= @user.api_token %> -
    -
    - <%= link_to( _('How to use the API'), controller: "token_permission_types", action: "index")%> -
    - <% end %> - -
    - - <%= _('If you would like to change your password please complete the following fields.') %> - -
    - <%= f.label :current_password, _('Current password') %> - <%= f.password_field :current_password, as: :password, class: 'input-medium' %> -
    - -
    - <%= f.label :password, _('New password') %> - <%= f.password_field :password, as: :password, autocomplete: "off", class: 'input-medium' %> -
    - -
    - <%= f.label :password_confirmation, _('Password confirmation') %> - <%= f.password_field :password_confirmation, as: :password, autocomplete: "off", - class: 'input-medium' %> -
    - -
    - - -
    -
    - - <% end %> +
    +
    +

    <%= _('Edit profile') %>

    +
    + +
    +
    + + +
    +
    + <%= render partial: 'devise/registrations/personal_details' %> +
    + +
    + <%= render partial: 'devise/registrations/password_details' %> +
    + +
    + <%= render partial: 'users/notification_preferences' %> +
    +
    + +
    +
    \ No newline at end of file diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb deleted file mode 100644 index 0c689be..0000000 --- a/app/views/devise/registrations/new.html.erb +++ /dev/null @@ -1,13 +0,0 @@ -

    <%= _('Create account') %>

    -<% unless session["devise.shibboleth_data"].nil? %> -

    - <%= (_("%{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.
    * If you do not have an account with %{application_name}, please complete the form below.
    * If you have an account with %{application_name}, please Sign in so we can link your account to your institutional credentials.
    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 %> -

    - <% cookies[:show_shib_link] = { value: 'show_shib_link', expires: 3.hours.from_now } %> -<% end %> - -
    -
    - - <%= render :partial => 'shared/register_form', locals: {extended: true} %> -
    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 @@ -

    <%= _('Sign in') %>

    -
    - - <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> -
    - - - - - - - - - - - - - -
    <%= _('Email') %><%= f.email_field :email, :autofocus => true %>
    <%= _('Password') %><%= f.password_field :password %>
    <% if devise_mapping.rememberable? -%> -
    - <%= f.check_box :remember_me %> - <%= f.label :remember_me %> -
    - <% end -%> -
    -
    - <%= f.submit _('Sign in'), :class => "btn btn-primary" %> -
    -
    - <% end %> - - <%= render "devise/shared/links" %> -
    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 @@ -

    <%= t('custom_devise.resend_unlock') %>

    - -<%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %> - <%= devise_error_messages! %> - -
    <%= f.label :email %>
    - <%= f.email_field :email, :autofocus => true %>
    - -
    <%= f.submit "Resend unlock instructions" %>
    -<% 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..e64f904 --- /dev/null +++ b/app/views/guidance_groups/_guidance_group_form.html.erb @@ -0,0 +1,14 @@ +
    + <%= 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 institution or department name.'), 'aria-required': true %> +
    + +
    + <%= 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')}") %> +
    + +
    + <%= 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)')}") %> +
    + +<%= 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..2238f91 --- /dev/null +++ b/app/views/guidance_groups/_show.html.erb @@ -0,0 +1,40 @@ +<%# locals: { group, question, guidance_accordion_id }%> +
    + <% if group.keys.length > 1 %> + + <% end %> + + <% i = 0 %> + <% group.keys.each do |theme| %> + + +
    +
    + <% group[theme].each do |guidance| %> + <%= raw guidance %> + <% end %> +
    +
    + <% i += 1 %> + <% end %> +
    \ 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..f8a99a6 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' %> - -

    - <%= _('Guidance group') %> - -
    - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: 'btn btn-primary' %> +
    +
    +

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

    + <%= link_to _('View all guidance'), admin_index_guidance_path(current_user.org_id), class: 'btn btn-default pull-right' %>
    -

    +
    -
    - -
    - -
    - <%= form_for(@guidance_group, url: admin_update_guidance_group_path(@guidance_group), html: {method: :put}) do |f| %> - - - - - - - - - - - - - - - - -
    <%= _('Name') %> -
    - <%= f.text_field :name, - as: :string, - class: 'text_field' %> - -
    -
    - <%= 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.')) %> -
    -
    <%= _('Published') %> -
    - <%= f.check_box :published %> -
    -
    - -
    -
    <%= _('Optional subset') %> -
    - <%= f.check_box :optional_subset %> <%= _('e.g. School/ Department') %> -
    -
    - <%= 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.")) %> -
    -
    - - - -
    - <%= 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' %> +
    +
    + <%= form_for(@guidance_group, url: admin_update_guidance_group_path(@guidance_group), html: {method: :put, id: "admin_update_guidance_group_form" }) do |f| %> + +
    + <%= 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 institution or department name.') %>
    -
    +
    + <%= 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" %> +
    + +
    + <%= 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) ') %> +
    + +
    + <%= 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' %> +
    <% end %>
    + diff --git a/app/views/guidance_groups/admin_new.html.erb b/app/views/guidance_groups/admin_new.html.erb index 2c466cb..0cbb485 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' %> - -

    - <%= _('Guidance group') %> - -
    - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: "btn btn-primary" %> -
    -

    - -
    - -
    - -
    - <%= form_for :guidance_group, url: {action: "admin_create"} do |f| %> - - - - - - - - - - -
    <%= _('Name') %>
    - <%= f.text_field :name, as: :string, class: "text_field" %> -
    -
    - <%= 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.'))%> -
    -
    <%= _('Optional subset') %>
    - <%= f.check_box :optional_subset %> <%= _('e.g. School/ Department') %> -
    -
    - <%= 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."))%> -
    -
    - - -
    - <%= f.submit _('Save'), name: "draft", class: "btn btn-primary" %> - <%= link_to _('Cancel'), :back, class: "btn cancel btn-secondary" %> -
    -
    - <% end %> -
    +
    +
    +

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

    + <%= link_to _('View all guidance'), admin_index_guidance_path(current_user.org_id), class: 'btn btn-default pull-right' %> +
    + +
    +
    + <%= form_for :guidance_group, url: {action: "admin_create"}, id: 'admin_create_guidance_group_form' do |f| %> +
    + <%= 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 institution or department name.') %> +
    + +
    + <%= 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" %> +
    + +
    + <%= 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) ') %> +
    + + +
    + <%= f.submit _('Save'), name: "draft", class: "btn btn-primary" %> + <%= link_to _('Cancel'), admin_index_guidance_path, class: "btn btn-primary", role: 'button' %> +
    + <% end %> +
    +
    \ 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" %> - -

    - <%= _('Guidance group') %> - - -
    - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: "btn btn-primary" %> -
    -

    - -
    - -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    <%= _('Name') %><%= raw @guidance_group.name %>
    <%= _('Published') %> - <% if @guidance_group.published.nil? || !@guidance_group.published then %> - <%= _('No') %> - <% else %> - <%= _('Yes') %> - <% end %> -
    <%= _('Optional subset') %> - <% if @guidance_group.optional_subset.nil? || !@guidance_group.optional_subset then %> - <%= _('No') %> - <% else %> - <%= _('Yes') %> - <% end %> -
    <%= _('Created') %><%= l @guidance_group.created_at.to_date, formats: :short %>
    <%= _('Last updated') %><%= l @guidance_group.updated_at.to_date, formats: :short %>
    -
    - <%= link_to _('Edit'), admin_edit_guidance_group_path(@guidance_group.id), class: "btn btn-primary" %> - <%= link_to _('Back'), :back, class: "btn cancel" %> -
    -
    -
    -
    \ 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 @@ - -
    - <%= form_for :guidance, url: {action: "admin_create"}, html: {id: "new_guidance_form"} do |f| %> - - - - - - - - - - - - - - - - - -
    <%= _('Text') %> -
    - <%= text_area_tag("guidance-text", "", class: "tinymce") %> -
    -
    - <%= 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.'))%> -
    -
    -
    <%= _('Should this guidance apply:') %> - -
    <%= _('Published') %> -
    - <%= f.check_box :published , as: :check_boxes %> -
    -
    <%= _('Guidance group') %> -
    - <%= 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})%> -
    -
    - <%= link_to( image_tag("help_button.png"), "#", "data-toggle": "popover", rel: "popover", "data-html" => "true", "data-content" => _('Select which group this guidance relates to.'))%> -
    -
    -
    - - - -
    - <%= _('Save')%> - - <%= link_to _('Cancel'), :back, class: "btn cancel" %> -
    - -
    - <%= tinymce :content_css => asset_path('application.css') %> - <% end %> -
    - - - - - \ 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 @@ - -<%= 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 %> - - - - - - -
    <%= _('Suggested answer/ Example')%> -
      -
    • <%= f.select :is_example, {_('Example Answer') => true, _('Suggested answer') => false} %>
    • -
    • <%= f.text_area :text, rows: 5 %>
    • -
    -
    -
    - - -
    - <%= 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" %> -
    -<%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' %> - -

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

    - -
    - -
    - -
    - <%= form_for(@guidance, url: admin_update_guidance_path(@guidance), html: { method: :put , id: 'edit_guidance_form'}) do |f| %> - - - - - - - - - - - - - - - - - - - - -
    <%= _('Text') %> -
    - <%= text_area_tag("guidance-text", @guidance.text, class: "tinymce") %> -
    -
    - <%= 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.'))%> -
    -
    -
    <%= _('Themes') %> -
    -
    - <%= f.collection_select(:theme_ids, @themes, :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 which theme(s) this guidance relates to.'))%> -
    -
    -
    -
    <%= _('Published') %>
    - <%= f.check_box :published , as: :check_boxes%> -
    -
    <%= _('Guidance group') %> -
    - <%= f.collection_select(:guidance_group_id, @guidance_groups, - :id, :name, {prompt: false, include_blank: 'None'}, {multiple: false})%> -
    -
    - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('Select which group this guidance relates to.'))%> -
    -
    - -
    - - - -
    - <%= _('Save')%> - <%= link_to _('Cancel'), :back, class: 'btn cancel' %> -
    - -
    - <%= tinymce :content_css => asset_path('application.css') %> - <%end%> -
    -
    - - - - diff --git a/app/views/guidances/admin_index.html.erb b/app/views/guidances/admin_index.html.erb index 6010b35..93139ef 100644 --- a/app/views/guidances/admin_index.html.erb +++ b/app/views/guidances/admin_index.html.erb @@ -1,140 +1,190 @@ -<%= stylesheet_link_tag "admin" %> -<% javascript "admin.js" %> +
    +
    +

    <%= _('Guidance') %>

    - -

    - <%= _('Guidance group list') %> -

    - -
    - <%= raw _("

    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.

    ")%> -
    -
    -
    - <%= link_to _('Add guidance group'), admin_new_guidance_group_path(), class: "btn btn-primary" %> -
    +

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

    +
    - -<% if @guidance_groups.length > 0 then%> - - - - - - - - - - - - <% !@guidance_groups.each do |guidance_gr| %> - - - - +
    +
    +

    <%= _('Guidance group list') %>

    + + + <% if @guidance_groups.length > 0 then%> +
    +
    <%= _('Name') %><%= _('Published') %><%= _('Optional subset') %><%= _('Last updated') %><%= _('Actions') %>
    - <%= guidance_gr.name %> - - <% if guidance_gr.published.nil? || guidance_gr.published == false then%> - <%= _('No')%> - <% else %> - <%= _('Yes')%> - <% end %> - - <% if guidance_gr.optional_subset.nil? || guidance_gr.optional_subset == false then%> - <%= _('No')%> - <% else %> - <%= _('Yes')%> - <% end %> -
    + + <% if @guidance_groups.length > TABLE_FILTER_MIN_ROWS %> + + + + <% end %> + + + + + + + + + + <% !@guidance_groups.each do |guidance_gr| %> + + + + + + - - - <% end %> - -
    + <%= render(partial: "shared/table_filter", + locals: {path: admin_index_guidance_path(current_user.org_id), placeholder: _('Filter guidance groups')}) %> +
    <%= _('Name') %><%= _('Status') %><%= _('Optional subset') %><%= _('Last updated') %><%= _('Actions') %>
    + <%= guidance_gr.name %> + + <% if guidance_gr.published.nil? || guidance_gr.published == false then%> + <%= _('Unpublished')%> + <% else %> + <%= _('Published')%> + <% end %> + + <% if guidance_gr.optional_subset.nil? || guidance_gr.optional_subset == false then%> + <%= _('No')%> + <% else %> + <%= _('Yes')%> + <% end %> + + <%= l guidance_gr.updated_at.to_date, formats: :short %> + + - <%= l guidance_gr.updated_at.to_date, formats: :short %> - - <%= link_to _('View'), admin_show_guidance_group_path(guidance_gr), class: "dmp_table_link"%>
    - <%= link_to _('Edit'), admin_edit_guidance_group_path(guidance_gr), class: "dmp_table_link"%>
    - <%= 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"%> -
    -<%end%> + +
    + + + <% end %> + + +
    + <%end%> + + -
    -
    +

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

    -

    - <%= _('Guidance list') %> -

    +

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

    +

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

    -
    - <%= raw _('

    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.

    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.

    ')%> + + <% if @guidances.length > 0 then%> +
    + + + <% if @guidances.length > TABLE_FILTER_MIN_ROWS %> + + <%= render(partial: "shared/table_filter", + locals: {path: admin_index_guidance_path(current_user.org_id), placeholder: _('Filter guidance')}) %> + + <% end %> + + + + + + + + + + + <% @guidances.each do |guidance| %> + <% if guidance.in_group_belonging_to?(current_user.org_id) then %> + + + <% if guidance.themes.present? then %> + + <% else %> + + <% end %> + <% if guidance.guidance_group.present? then %> + + <% else %> + + <% end %> + + + + + <% end %> + <% end %> + +
    <%= _('Text') %><%= _('Themes') %><%= _('Guidance group') %><%= _('Status') %><%= _('Last updated') %><%= _('Actions') %>
    + <%= guidance.text.html_safe%> + + <% guidance.themes.each do |th| %> + <%= th.title %> + <% end %> + + - + + <%= guidance.guidance_group.name %> + + - + + <% if guidance.published.nil? || guidance.published == false then%> + <%= _('Unpublished')%> + <% else %> + <%= _('Published')%> + <% end %> + + <%= l guidance.updated_at.to_date, formats: :short %> + + +
    +
    + <% end %> + + +
    -
    - -
    - <%= link_to _('Add guidance'), - admin_new_guidance_path(), - class: "btn btn-primary" %> -
    -
    - -
    - - -<% if @guidances.length > 0 then%> - - - - - - - - - - - - <% @guidances.each do |guidance| %> - <% if guidance.in_group_belonging_to?(current_user.org_id) then %> - - - <% if guidance.themes.present? then %> - - <% else %> - - <% end %> - <% if guidance.guidance_group.present? then %> - - <% else %> - - <% end %> - - - - <% end %> - <% end %> - -
    <%= _('Text') %><%= _('Themes') %><%= _('Guidance group') %><%= _('Last updated') %><%= _('Actions') %>
    - <%= guidance.text.html_safe%> - - <% guidance.themes.each do |th| %> - <%= th.title %> - <% end %> - - - - - <%= guidance.guidance_group.name %> - - - - - <%= l guidance.updated_at.to_date, formats: :short %> - - <%= link_to _('View'), admin_show_guidance_path(guidance), class: "dmp_table_link"%>
    - <%= link_to _('Edit'), admin_edit_guidance_path(guidance), class: "dmp_table_link"%>
    - <%= 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"%> -
    -<% 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' %> - -

    - <%= _('New guidance') %> - -
    - <%= link_to _('View all guidance'), - admin_index_guidance_path, - class: 'btn btn-primary' %> -
    -

    - -
    -
    - -
    - <%= form_for :guidance, url: {action: 'admin_create'}, html: {id: 'new_guidance_form'} do |f| %> - - - - - - - - - - - - - - - - - -
    <%= _('Text') %> -
    - <%= text_area_tag("guidance-text", "", class: "tinymce") %> -
    -
    - <%= 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.'))%> -
    -
    -
    <%= _('Themes') %> -
    -
    - <%= f.collection_select(:theme_ids, @themes, - :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 which theme(s) this guidance relates to.'))%> -
    -
    -
    - -
    <%= _('Published') %>
    - <%= f.check_box :published , as: :check_boxes%> -
    -
    - <%= 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."))%> -
    -
    <%= _('Guidance group') %>
    - <%= f.collection_select(:guidance_group_id, @guidance_groups, - :id, :name, {prompt: false, include_blank: 'None'}, {multiple: false})%> -
    -
    - <%= link_to( image_tag('help_button.png'), '#', "data-toggle": "popover", rel: "popover", 'data-html' => "true", 'data-content' => _('Select which group this guidance relates to.'))%> -
    -
    - -
    - - - -
    - <%= _('Save')%> - - <%= link_to _('Cancel'), :back, class: 'btn cancel' %> -
    - -
    - <%= tinymce :content_css => asset_path('application.css') %> - <%end%> -
    -
    - - - - \ 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" %> - -

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

    - -
    - -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    <%= _('Text') %><%= raw @guidance.text %>
    <%= _('Themes') %><%= @guidance.themes.order(:title).collect{|t| t.title}.join(', ') %>
    <%= _('Guidance group') %> - <%= @guidance.guidance_group.name %> -
    <%= _('Published') %><%if @guidance.published == false || @guidance.published.nil? then%> - <%= _('No')%> - <% else %> - <%= _('Yes')%> - <% end %> -
    <%= _('Created') %><%= l @guidance.created_at.to_date, formats: :short %>
    <%= _('Last updated') %><%= l @guidance.updated_at.to_date, formats: :short %>
    -
    - <%= link_to _('Edit'), admin_edit_guidance_path(@guidance.id), class: "btn btn-primary"%> - <%= link_to _('Back'), :back, class: "btn cancel" %> -
    -
    -
    -
    \ 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..af133d4 --- /dev/null +++ b/app/views/guidances/new_edit.html.erb @@ -0,0 +1,31 @@ +<%# locals: { guidance, themes, guidance_groups, options } %> +
    +
    +

    <%= _('Guidance') %>

    + <%= link_to _('View all guidance'), admin_index_guidance_path(current_user.org_id), class: 'btn btn-default pull-right' %> +
    +
    +
    +
    + <%= form_for(guidance, url: options[:url], html: { method: options[:method] , id: 'new_edit_guidance'}) do |f| %> +
    + <%= f.label :text, class: 'control-label' %> + <%= text_area_tag("guidance-text", guidance.text, class: "tinymce form-control", 'aria-required': true, rows: 10) %> +
    + <%= 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 institution-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 ) } } %> +
    + <%= 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})%> +
    +
    + <%= 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?')}") %> +
    + + <%= 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%> +
    +
    \ 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 @@ +
    +

    <%= _('Welcome.')%>

    +

    + <%= raw _('

    %{application_name} has been developed by the %{organisation_name} to help you write data management plans.

    ') % {:application_name => Rails.configuration.branding[:application][:name], :organisation_name => Rails.configuration.branding[:organisation][:name]} %> + + +

    +
    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 @@ -
    -
    -
    - -

    <%= _('Welcome.')%>

    - <%= raw _('

    %{application_name} has been jointly developed by the %{organisation_name} to help you write data management plans.

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

    <%= _('Screencast on how to use %{application_name}') % {:application_name => Rails.configuration.branding[:application][:name]} %>

    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    - <%= render :partial => 'shared/login_form' %> -
    -
    -
    -
    -
    - - <%= _('Create account') %> -

    - <%= _('New to %{application_name}? Create an account today.') % {:application_name => Rails.configuration.branding[:application][:name]} %> -

    - -
    -
    -
    - <%= render :partial => 'shared/register_form', locals: {extended: false} %> -
    -
    -
    -
    -
    -
    -
    \ No newline at end of file +
    +
    + <%= render partial: 'home/welcome' %> +
    + +
    + <%= render partial: 'shared/access_controls' %> +
    +
    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 +-%> + + <%= link_to_unless current_page.first?, _('First'), url, remote: remote %> + 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 +-%> +<%= _('...') %> 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 +-%> + + <%= link_to_unless current_page.last?, _('Last'), url, remote: remote %> + 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 +-%> + + <%= link_to_unless current_page.last?, _('Next'), url, rel: 'next', remote: remote %> + 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 +-%> + + <%= link_to_unless page.current?, page, url, {remote: remote, rel: page.rel} %> + 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 -%> + +<% 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 +-%> + + <%= link_to_unless current_page.first?, _('Previous'), url, rel: 'prev', remote: remote %> + diff --git a/app/views/layouts/_branding.html.erb b/app/views/layouts/_branding.html.erb index 4774bee..a746176 100644 --- a/app/views/layouts/_branding.html.erb +++ b/app/views/layouts/_branding.html.erb @@ -1,23 +1,99 @@ - -
    - <% if user_signed_in? %> - <% if !current_user.org.nil? %> - - <% if current_user.org.logo.present? %> - +
    + diff --git a/app/views/layouts/_es5_scripts.html.erb b/app/views/layouts/_es5_scripts.html.erb new file mode 100644 index 0000000..3214a12 --- /dev/null +++ b/app/views/layouts/_es5_scripts.html.erb @@ -0,0 +1,20 @@ + + + + <%= javascript_include_tag 'jquery.min.js' %> + <%= javascript_include_tag 'rails.js' %> + <%= javascript_include_tag 'jquery-ui.min.js' %> + <%= javascript_include_tag 'placeholder.min.js' %> + <%#= javascript_include_tag 'jquery.tablesorter.min.js' %> + <%#= javascript_include_tag 'jquery.tablesorter.widgets.min.js' %> + <%#= javascript_include_tag 'bootstrap.min.js' %> + + + diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb index 4866e06..1c5a3bd 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 +%> + -
    - - - -
    - -<% if !user_signed_in? then %> - - - -<% end %> + 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 @@ - - -
    -
    - -
    -
    -
    - <%= render "layouts/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 %> +
    diff --git a/app/views/layouts/_navigation.html.erb b/app/views/layouts/_navigation.html.erb index 57bfe0b..6f91d01 100644 --- a/app/views/layouts/_navigation.html.erb +++ b/app/views/layouts/_navigation.html.erb @@ -1,69 +1,70 @@ - +
    +
    + + + diff --git a/app/views/layouts/_paginable.html.erb b/app/views/layouts/_paginable.html.erb new file mode 100644 index 0000000..e899eb1 --- /dev/null +++ b/app/views/layouts/_paginable.html.erb @@ -0,0 +1,33 @@ +<% + # Custom layout to be included on any view that needs pagination + # locals: { controller, action, paginable, scope } +%> +
    +
    +
    + <%= yield %> +
    +
    +
    +
    +
    +
    + <% total = paginable ? scope.total_count : scope.length %> + <% if total > Kaminari.config.default_per_page %> + <% if paginable %> + <%= link_to(_('View all'), url_for(controller: controller, action: action, page: 'ALL'), { 'data-remote': true }) %> + <% else %> + <%= link_to(_('View less'), url_for(controller: controller, action: action, page: 1), { 'data-remote': true }) %> + <% end %> + <% end %> +
    +
    + <% if paginable %> + <%= paginate(scope, params: { controller: controller, action: action }, remote: true) %> + <% end %> +
    +
    +
    +
    +
    +
    \ 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..a8b148e 100644 --- a/app/views/layouts/_signin_signout.html.erb +++ b/app/views/layouts/_signin_signout.html.erb @@ -1,45 +1,45 @@ + +<% if MANY_LANGUAGES %> + +<% end %> + - - - +
  • + <%= link_to ' '.html_safe + _('Logout'), destroy_user_session_path, method: :delete %> +
  • + +
  • +<% else %> + <% if !isActivePage(root_path) %> +
  • + + + <%= _('Sign in') %> + +
  • + <% end %> +<% end %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 483511b..31cc785 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,84 +1,76 @@ - - - <%= _('%{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" %> +
    + 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..93c1f62 --- /dev/null +++ b/app/views/notes/_layout.html.erb @@ -0,0 +1,23 @@ +<%# 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..73132f3 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: :custom %>)
      • +
      +
      +
      +
        +
      • <%= 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 %> \ No newline at end of file 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..648406b --- /dev/null +++ b/app/views/notes/_show.html.erb @@ -0,0 +1,12 @@ +<%# locals: { note } %> +<% if !note.nil? %> + <% user = User.find(note.user_id) %> + +<% end %> \ No newline at end of file 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..6f3cdc5 --- /dev/null +++ b/app/views/org_admin/plans/index.html.erb @@ -0,0 +1,79 @@ +
    +
    +

    <%= _('%{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' %> +
    + + + <% if @plans.length > TABLE_FILTER_MIN_ROWS %> + + + + <% end %> + + + + + + + + + + + <% @plans.each do |plan| %> + + + + + + + + + <% end %> + +
    + <%= render(partial: "shared/table_filter", + locals: { placeholder: _('Filter plans')}) %> +
    <%= _('Project Title') %><%= _('Template') %><%= _('Organisation') %><%= _('Owner') %><%= _('Updated') %><%= _('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.latest_update.to_date, formats: :short) %> + <%= plan.visibility === 'is_test' ? _('Test') : raw(display_visibility(plan.visibility)) %> +
    +
    + <% 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..70dd9d1 --- /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..bb4b6a7 --- /dev/null +++ b/app/views/org_admin/templates/_edit.html.erb @@ -0,0 +1,44 @@ +
    +
    + <% 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].each do |phase_no, phase_hash| %> + <% phase = phase_hash[:data] %> +
    + +
    " class="panel-collapse collapse" role="tabpanel" aria-labelledby="<%= "headingPhase#{phase.id}" %>"> +
    + <%= render partial: 'org_admin/templates/show_phases_sections', locals: { phase: phase, phase_hash: phase_hash, template: template, current: current } %> +
    +
    +
    + <% end %> + <% end %> +
    +
    +
    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..0862398 --- /dev/null +++ b/app/views/org_admin/templates/_edit_template.html.erb @@ -0,0 +1,78 @@ + +<%= form_for(template, url: org_admin_template_path(template), html: { 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 }) %> +
    +
    + +
    + <%= f.label _('Visibility'), class: 'control-label' %> +
    + <%= 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}}")) %> +
    +
    + +
    + <%= label_tag(:status, _('Status'), class: "control-label") %> +

    + <% if template_hash[:live].nil? %> + <%= _('Unpublished') %> + <% elsif template_hash[:current].dirty? %> + <%= _('You have un-published 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/_funder_templates_list.html.erb b/app/views/org_admin/templates/_funder_templates_list.html.erb new file mode 100644 index 0000000..30ad19a --- /dev/null +++ b/app/views/org_admin/templates/_funder_templates_list.html.erb @@ -0,0 +1,99 @@ +<% # locals: templates, current_org %> + +
    + + + <% if scope.length > TABLE_FILTER_MIN_ROWS %> + + + + <% end %> + <% if scopes.present? %> + + <% end %> + + + + + + + + + + <% scope.each do |template| %> + <% customization = customizations[template.dmptemplate_id] %> + + + + + + + + <% end %> + +
    + <%= render(partial: "shared/table_filter", locals: { placeholder: _('Search')}) %> +
    <%= _('Template Name') %><%= _('Funder') %><%= _('Status') %><%= _('Edited Date') %>
    + <%= "#{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 un-published') %> + <% elsif customization.dirty? %> + <%= _('You have un-published 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/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..e1ab1ad --- /dev/null +++ b/app/views/org_admin/templates/_show_phases_sections.html.erb @@ -0,0 +1,57 @@ + +
    +
    +

    + <%= 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'), admin_destroy_phase_path(phase_id: phase.id), { + confirm: _("You are about to delete '%{phase_title}'. This will affect versions, sections and questions linked to this phase. Are you sure?") % { phase_title: phase.title }, + 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..f61514b --- /dev/null +++ b/app/views/org_admin/templates/_show_template.html.erb @@ -0,0 +1,35 @@ + +
    +
    <%= _('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 un-published changes') %> + + <% else %> + <%= _('Published') %> + <% end %> +
    + <% if template.org.funder? %> +
    <%= _('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/_templates_list.html.erb b/app/views/org_admin/templates/_templates_list.html.erb new file mode 100644 index 0000000..fdbb1a3 --- /dev/null +++ b/app/views/org_admin/templates/_templates_list.html.erb @@ -0,0 +1,88 @@ +<% # locals: templates, current_org %> + +
    + + + <% if scope.length > TABLE_FILTER_MIN_ROWS %> + + + + <% end %> + <% if scopes.present? %> + + <% end %> + + + + + + <% if !hide_actions %><% end %> + + + + <% scope.each do |template| %> + + + + + + <% if !hide_actions %> + + <% end %> + + <% end %> + +
    + <%= render(partial: "shared/table_filter", locals: { placeholder: _('Search')}) %> +
    <%= _('Template Name') %><%= current_user.can_super_admin? ? _('Organisation') : _('Description') %><%= _('Status') %><%= _('Edited Date') %>
    + <%= template.title %> + + <%= current_user.can_super_admin? ? template.org.name : raw(template.description) %> + + <% if !published[template.dmptemplate_id].present? %> + <%= _('Unpublished') %> + <% elsif template.dirty? %> + <%= _('Unpublished changes') %> + <% else %> + <%= _('Published') %> + <% 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/org_admin/templates/container.html.erb b/app/views/org_admin/templates/container.html.erb new file mode 100644 index 0000000..80d2eb6 --- /dev/null +++ b/app/views/org_admin/templates/container.html.erb @@ -0,0 +1,17 @@ +
    +
    +

    <%= template.title %>

    + <%= link_to _('View all templates'), org_admin_templates_path, 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..5db6adb --- /dev/null +++ b/app/views/org_admin/templates/history.html.erb @@ -0,0 +1,63 @@ +
    +
    +

    <%= _('Template History') %>

    +

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

    +
    +
    + +
    +
    + + <% if @templates.length > 0 then %> +
    + + + + + + + + + + + + <% @templates.each do |org_template| %> + + + + + + + + <%end%> + +
    <%= _('Title') %><%= _('Version') %><%= _('Published') %><%= _('Last updated') %><%= _('Actions') %>
    + <%= org_template.title%> + <% if org_template == @current && !org_template.published%> +     <%=_('Draft')%> + <% end %> + + <%= org_template.version %> + + <%= (org_template.published? ? _('Yes') : _('No')) %> + + <% last_temp_updated = org_template.updated_at %> + <% org_template.phases.each do |phase|%> + <% if org_template.updated_at.to_date < phase.updated_at.to_date %> + <% last_temp_updated = phase.updated_at %> + <% end %> + <% end %> + <%= l last_temp_updated.to_date, formats: :short %> + + <%= link_to (org_template == @current ? _('Edit') : _('View')), edit_org_admin_template_path(id: org_template), class: "dmp_table_link"%> +
    +
    + + <% else %> +

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

    + <% end %> +
    + <%= link_to _('View all templates'), org_admin_templates_path, class: "btn btn-default" %> +
    +
    +
    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..85e5e7c --- /dev/null +++ b/app/views/org_admin/templates/index.html.erb @@ -0,0 +1,88 @@ +<% # locals: funder_templates, org_templates, current_user, current_org, orgs %> +
    +
    +

    <%= _('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 institutional 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 institution when no funder templates apply. If you want to add questions to funder templates use the \'customise template\' options below.') %> +

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

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

    + <% if all_templates.length > 0 %> + <%= paginable_renderise( + partial: 'templates_list', + controller: 'org_admin/templates', + action: 'all', + scope: all_templates, + locals: {current_org: current_org.id, published: published, scopes: scopes[:all], hide_actions: true}) %> + <% else %> +

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

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

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

    + <% if own_templates.length > 0 %> + <%= paginable_renderise( + partial: 'templates_list', + controller: 'org_admin/templates', + action: 'orgs', + scope: own_templates, + locals: {current_org: current_org.id, published: published, scopes: scopes[:orgs], hide_actions: false}) %> + <% else %> +

    <%= _('There are currently no templates defined for your organisation.') %>

    + <% end %> +
    + +
    +

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

    + <% if customizable_templates.length > 0 %> + <%= paginable_renderise( + partial: 'funder_templates_list', + controller: 'org_admin/templates', + action: 'funders', + scope: customizable_templates, + locals: {current_org: current_org.id, customizations: customized_templates, published: published, scopes: scopes[:funders]}) %> + <% else %> +

    <%= _('There are currently no customisable templates.') %>

    + <% 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..d97cf97 --- /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, 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..026d1f3 --- /dev/null +++ b/app/views/orgs/_feedback_form.html.erb @@ -0,0 +1,31 @@ +<%= form_for(@org, url: admin_update_org_path(@org), html: { multipart: true, method: :put, 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..4654570 --- /dev/null +++ b/app/views/orgs/_profile_form.html.erb @@ -0,0 +1,84 @@ +<%= form_for(@org, url: admin_update_org_path(@org), html: { multipart: true, method: :put, 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" %> +
    +
    + +
    +
    + <%= 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['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", 'aria-required': true %> +
    +
    + <%= f.label :contact_name, _('Link text'), class: "control-label" %> + <%= f.text_field :contact_name, class: "form-control" %> +
    +
    + +
    +
    + <%= f.button(_('Save'), id:"save_org_submit", class: "btn btn-primary", type: "submit") %> +
    +
    + + <% if current_user.can_super_admin? %> +
    +

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

    +
    + <% shibboleth = @org.org_identifiers.select{ |ids| ids.identifier_scheme == IdentifierScheme.find_by(name: 'shibboleth')} %> + <% if Rails.application.config.shibboleth_use_filtered_discovery_service && shibboleth.size > 0 %> +
    <%= _('Entity ID') %>
    +
    <%= shibboleth.first.identifier %>
    +
    <%= _('Sibboleth domain') %>
    +
    <%= JSON.parse(shibboleth.first.attrs)['domain'] if shibboleth.first.attrs.present? %>
    + <% end %> +
    <%= _('Organisation type') %>
    +
    <%= @org.type %>
    +
    +
    + <% end %> +<% 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..f2fdd51 100644 --- a/app/views/orgs/admin_edit.html.erb +++ b/app/views/orgs/admin_edit.html.erb @@ -1,83 +1,33 @@ -<%= 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 %> - +
    +
    +

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

    -<%= tinymce :content_css => asset_path('application.css') %> +
    +
    + + +
    +
    + <%= render partial: 'profile_form' %> +
    + +
    + <%= render partial: 'feedback_form' %> +
    + +
    + <%= render partial: 'users/notification_preferences' %> +
    +
    +
    +
    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..a5f8e72 --- /dev/null +++ b/app/views/orgs/shibboleth_ds.html.erb @@ -0,0 +1,82 @@ +
    +
    +

    Find your institution to sign in

    +
    +
    + +
    +
    + <%= form_for 'shibboleth_ds', url: shibboleth_ds_path, html: {id: 'shibboleth_ds'} do |f| %> +
    + <%= f.label(:org_name, _('Look up your institution 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 institutions') %> +

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

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

    +
    +
    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..9075978 --- /dev/null +++ b/app/views/paginable/plans/_organisationally_or_publicly_visible.html.erb @@ -0,0 +1,46 @@ +<% if current_user.org_id.present? %> +
    +
    +

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

    +
    +
    +
    +
    +
    + + + <% if scope.length > TABLE_FILTER_MIN_ROWS %> + + + + <% end %> + + + + + + + + + + <% scope.each do |plan| %> + + + + + + + + <% end %> + +
    + <%= render(partial: "shared/table_filter", + locals: { placeholder: _('Filter plans')}) %> +
    <%= _('Project Title') %><%= _('Template') %><%= _('Owner') %><%= _('Updated') %><%= _('Download') %>
    <%= link_to "#{plan.title.length > 40 ? "#{plan.title[0..39]} ..." : plan.title}", plan_path(plan) %><%= plan.template.title %><%= plan.owner.present? ? plan.owner.name : _('Unknown') %><%= l(plan.latest_update.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..93fe83b --- /dev/null +++ b/app/views/paginable/plans/_privately_visible.html.erb @@ -0,0 +1,88 @@ +
    + + + <% if scope.length > TABLE_FILTER_MIN_ROWS %> + + + + <% end %> + + + + + + + + + + + + + <% scope.each do |plan| %> + + + + + + + + + + + <% end %> + +
    + <%= render(partial: "shared/table_filter", locals: { placeholder: _('Filter plans')}) %> +
    <%= _('Project Title') %><%= _('Template') %><%= _('Edited') %><%= _('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.latest_update.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/themes/_index.html.erb b/app/views/paginable/themes/_index.html.erb new file mode 100644 index 0000000..b50ff64 --- /dev/null +++ b/app/views/paginable/themes/_index.html.erb @@ -0,0 +1,16 @@ + + + + + + + + + <% scope.each do |theme| %> + + + + + <% end %> + +
    <%= _('Name') %><%= _('Guidance') %>
    <%= 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..6b0caa3 --- /dev/null +++ b/app/views/paginable/users/_index.html.erb @@ -0,0 +1,67 @@ +
    +
    +

    <%= _('List of users') %>

    +

    + <%= _('Below is a list of users registered for your organisation. You can sort the data by each field.')%> +

    +
    +
    + +
    +
    +
    + + + <% if scope.count > TABLE_FILTER_MIN_ROWS %> + + + + <% end %> + + + + + + + + + + <% scope.each do |user| %> + <% if !user.nil? then%> + + + + + + + + <% end %> + <% end %> + +
    + <%= render(partial: "shared/table_filter", + locals: {path: admin_index_users_path, + placeholder: _('Filter users')}) %> +
    <%= _('Name') %><%= _('Email address') %><%= _('Last logged in') %><%= _('How many plans?') %><%= _('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 %> + + <% if !user.roles.nil? %> + <%= user.roles.length %> + <% end %> + + <% unless current_user == user %> + <% b_label = _('Edit')%> + <%= link_to b_label, admin_grant_permissions_user_path(user)%> + <% end %> +
    +
    +
    +
    \ No newline at end of file diff --git a/app/views/phases/_admin_add.html.erb b/app/views/phases/_admin_add.html.erb new file mode 100644 index 0000000..f1b324f --- /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 } 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..b768f8d --- /dev/null +++ b/app/views/phases/_admin_show.html.erb @@ -0,0 +1,73 @@ +<% # locals: { phase, template, edit, current_section } %> +

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

    +
    +
    + <% if phase.modifiable && edit %> + + <% end %> +
    +
    +
    +
    +
    + <%= render partial: "phases/show_phase", locals: { phase: phase, edit: edit } %> +
    +
    +
    + +
    +
    +
    + <% 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 } %> + <% else %> + <%= render partial: 'sections/show_section', locals: { template: template, section: section } %> + <% 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 c95732b..0000000 --- a/app/views/phases/_answer_form.html.erb +++ /dev/null @@ -1,123 +0,0 @@ - - -
    - <% - answers = question.plan_answers(plan.id) - if answers.present? - answer = answers.first - else - 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..03c4811 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), 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..ce5f6d9 --- /dev/null +++ b/app/views/phases/_edit_plan_answers.html.erb @@ -0,0 +1,96 @@ +<%# 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.order(:number).each do |section| %> + <% sectionid = section.id %> +
    + +
    +
    +
    +
    <%= raw section.description %>
    + + + + <% section.questions.each do |question| %> + <% # Load the answer or create a new one + answers = question.plan_answers(plan.id) if plan.present? + if answers.present? + answer = answers.first + else + 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 }) %> +
    +
    "> + <%= 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 } %> +
    +
    + <% 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..b6d53f1 --- /dev/null +++ b/app/views/phases/_guidances_notes.html.erb @@ -0,0 +1,92 @@ +<%# 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 do |group| %> + <% obj = guidance_groups.select{ |gg| gg.name == group }.first %> + <% i = 0 %> + <% if obj.present? %> +
    + <%= render partial: 'guidance_groups/show', + locals: { group: guidance_set[group], question: question, guidance_accordion_id: guidance_accordion_id } %> + <% guidance_accordion_id += 1 %> + <% i += 1 %> +
    + <% 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..6b8d0f2 --- /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), { 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..b98f0bc 100644 --- a/app/views/phases/admin_preview.html.erb +++ b/app/views/phases/admin_preview.html.erb @@ -1,59 +1,25 @@ -<%- 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), class: 'btn btn-primary' %>
    • +
    • <%= link_to _('View all templates'), org_admin_templates_path, class: 'btn btn-primary' %>
    • +
    +
    +
    +
    +
    +
    +
    + + <%= render partial: "/org_admin/templates/admin_nav_tabs", locals: { template: @template, active: @phase.id } %> + + <%= render partial: '/phases/edit_plan_answers', locals: { plan: nil, phase: @phase, readonly: true, question_guidance: {}, + 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 5a4c267..c135c06 100644 --- a/app/views/phases/edit.html.erb +++ b/app/views/phases/edit.html.erb @@ -1,118 +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} %> - - -
    - -
    - <% @phase.sections.order(:number).each do |section| %> - - <% sectionid = section.id %> - -
    - - - - -
    -
    - <%= raw section.description %> -
    - -
    - - - - - - - -
    - <% section.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, - 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..f82f145 --- /dev/null +++ b/app/views/plans/_edit_details.html.erb @@ -0,0 +1,167 @@ +
    +
    + <%= 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 institution.')) %> +
    +
    + +

    <%= _('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} %> +
    + + <%#= _('See guidance from additional organisations') %> + + +
    + <% end %> +
    \ 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 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..eed6e73 --- /dev/null +++ b/app/views/plans/_guidance_choices.html.erb @@ -0,0 +1,29 @@ +<% 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' %> + <%= form.label org.name, for: groups[0].id, + class: 'inline checkbox-label regular-text guidance-group-label' %> +
  • + <% elsif groups %> +
  • + + + +
      + <% groups.each do |group| %> +
    • + └─ + <%= check_box_tag "guidance_group_ids[]", group.id, + current_selections.include?(group.id), class: 'guidance-choice' %> + <%= form.label group.name, for: group.id, + class: "left-indent checkbox-label regular-text guidance-group-label" %> +
    • + <% 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..7a9cb3c --- /dev/null +++ b/app/views/plans/_navigation.html.erb @@ -0,0 +1,29 @@ +<% 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..526ed8b --- /dev/null +++ b/app/views/plans/_overview_details.html.erb @@ -0,0 +1,49 @@ +
    +
    +

    <%= 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 c25f416..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..c008f6e 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.id) + 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..6b87622 --- /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 one question to enable these options.') %>

    +<% 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 index 05f5a3c..e75c5a4 100644 --- a/app/views/plans/export.docx.erb +++ b/app/views/plans/export.docx.erb @@ -2,7 +2,7 @@

    <%= @plan.template.title %>

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

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

    <% details.each do |field| %> <% value = @exported_plan.send(field) %> @@ -15,32 +15,33 @@ -<% @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 %> +<% @sections.each do |section| %> + <% if @question_headings %> +

    <%= section.title %>

    + <% end %> + <% section.questions.each do |question| %> + <% answer = @plan.answer(question.id, false) %> + <% if answer.nil? && !@unanswered_questions then next end %> + <% if @question_headings %> + <%= raw question.text %> + <% end %> + <% 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 %> diff --git a/app/views/plans/export.html.erb b/app/views/plans/export.html.erb index 27a6b1b..cd8b28d 100644 --- a/app/views/plans/export.html.erb +++ b/app/views/plans/export.html.erb @@ -1,76 +1,82 @@
    -

    <%= @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 %> -
    -
    -
    +

    <%= @plan.title %>

    +

    <%= @plan.template.title %>

    +
    +
    + <% details = @exported_plan.admin_details %> + <% if details.present? && @show_details %> +

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

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

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

    <%= value.present? ? value : _('-') %>
    + <% end %> + <% @sections.each do |section| %> + <% if @question_headings %> +

    <%= section.title %>

    + <% end %> + + + + <% if @question_headings %> + + + <% else %> + + <% end %> + + + + <% section.questions.order(:number).each do |question| %> + + <% answer = @plan.answer(question.id, false) %> + <% if !@unanswered_questions && answer.blank? + next # skip unanswered questions + end %> + <% if @question_headings %> + + <% end %> + + + <% end %> + +
    <%= _('Questions')%><%= _('Answers')%>
    +

    - <%= raw question.text %>

    +
    + <% 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 %> +
    +
    +
    \ No newline at end of file diff --git a/app/views/plans/export.json.jbuilder b/app/views/plans/export.json.jbuilder index 7277da5..405d931 100644 --- a/app/views/plans/export.json.jbuilder +++ b/app/views/plans/export.json.jbuilder @@ -15,7 +15,9 @@ json.sections do @exported_plan.sections.each do |section| json.set! section.number do - json.title section.title + if @question_headings + json.title section.title + end json.questions do @exported_plan.questions_for_section(section.id).each do |question| json.set! question.number do @@ -23,7 +25,7 @@ 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") diff --git a/app/views/plans/export.pdf.erb b/app/views/plans/export.pdf.erb index b9ab084..aa5f46d 100644 --- a/app/views/plans/export.pdf.erb +++ b/app/views/plans/export.pdf.erb @@ -19,50 +19,53 @@

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

    + <% if @show_details %> + <% @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 %> <% 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 %> -
    + <% @sections.each do |section| %> + <% if @question_headings %> +

    <%= section.title %>

    + <% end %> + <% section.questions.each do |question| %> + <% answer = @plan.answer(question.id, false) %> + <% if answer.nil? && !@unanswered_questions then next end %> +
    + <% if @question_headings %> +

    <%= raw question.text %>

    + <% end %> + <% 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 %> + <% if !answer.text.nil? %> + <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> + <% end %> + <% end %> + <% else %> + + <% if !answer.text.nil? %> + <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> + <% end %> <% 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..bd64ba1 100644 --- a/app/views/plans/index.html.erb +++ b/app/views/plans/index.html.erb @@ -1,38 +1,42 @@ -<%- model_class = Plan -%> -<% javascript "toolbar.js" %> -

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

    +
    +
    +

    <%= _('My Dashboard') %>

    + +

    + <% if @plans.count > 0 %> + <%= _('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.')%> + <% 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) %> +
    +
    +
    +
    + <%= link_to _('Create plan'), new_plan_path, class: "btn btn-primary" %> +
    +
    +
    +
    + <% if @organisationally_or_publicly_visible.length > 0 %> + <%= paginable_renderise( + partial: '/paginable/plans/organisationally_or_publicly_visible', + controller: 'paginable/plans', + action: 'organisationally_or_publicly_visible', + scope: @organisationally_or_publicly_visible) %> + <% end %> +
    +
    - -<% 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" %> -

    diff --git a/app/views/plans/new.html.erb b/app/views/plans/new.html.erb index a40b41b..be0af7f 100644 --- a/app/views/plans/new.html.erb +++ b/app/views/plans/new.html.erb @@ -1,90 +1,91 @@ -<% 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") %> + <% 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..af374d6 --- /dev/null +++ b/app/views/public_pages/plan_export.pdf.erb @@ -0,0 +1,79 @@ + + + + + + <%= @plan.title %> + + + + +
    +

    <%= @plan.title %>

    +

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

    +
    +
    +

    <%= _("Creator(s): ") + @creator_text %>


    +

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


    + <% if @funder.present? %> +

    <%= _("Funder: ") + @funder %>


    + <% else %> +

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


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

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


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

    <%= _("Description: ") + @plan.description %>


    + <% end %> +

    <%= _("Last Updated: ") + @plan.updated_at.to_date.to_s %>


    + +

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

    + <% @phases.each do |phase| %> +
    +

    <%= phase.title %>

    + <% Section.where(phase_id: phase.id, id: @a_s_ids).order(:number).each do |section| %> +

    <%= section.title %>

    + <% Question.where(section_id: section, id: @a_q_ids).order(:number).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| %> +
    • <%= option.text %>
    • + <% end %> +
    + + <% if question.option_comment_display == true %> + <% if !answer.text.nil? %> + <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> + <% end %> + <% end %> + <% else %> + + <% if !answer.text.nil? %> + <%= raw answer.text.gsub(/(\s||<\/td>| )*(<\/tr>|)/,"") %> + <% end %> + <% end %> + <% end %> +
    + <% end %> + <% end %> + <% end %> + + 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..5ef3a08 --- /dev/null +++ b/app/views/public_pages/plan_index.html.erb @@ -0,0 +1,56 @@ +
    +
    +

    <%= 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 %> +
    + + + <% if @plans.count > TABLE_FILTER_MIN_ROWS %> + + + + <% end %> + + + + + + + + + + <% @plans.each do |plan| %> + + + + + + + + <% end %> + +
    + <%= render(partial: "shared/table_filter", + locals: {path: public_plans_path, placeholder: _('Filter plans')}) %> +
    <%= _('Project Title') %><%= _('Template') %><%= _('Institution') %><%= _('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' %> +
    +
    + <% 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..b2932b6 --- /dev/null +++ b/app/views/public_pages/template_export.docx.erb @@ -0,0 +1,30 @@ +
    +

    <%= @template.title %>

    +

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

    +
    +

    <%= _("Organisation: ") + @template.org.name %>


    +<% if @template.description.present? %> +

    <%= _("Description: ")%><%= raw(@template.description) %>


    +<% end %> +
    + +<% @template.phases.each do |phase| %> + +
    +

    <%= 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 %> +
    + <% end %> + <% end %> +<% end %> 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..3252a60 --- /dev/null +++ b/app/views/public_pages/template_export.pdf.erb @@ -0,0 +1,52 @@ + + + + + + <%= @template.title %> + + + + +
    +

    <%= @template.title %>

    +

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

    +
    +
    +

    <%= _("Organisation: ") + @template.org.name %>


    + <% if @template.description.present? %> +

    <%= _("Description: ")%><%= raw(@template.description) %>

    + <% end %> + + <% @template.phases.each do |phase| %> +
    +

    <%= 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 %> +
    + <% 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..8fc3b31 --- /dev/null +++ b/app/views/public_pages/template_index.html.erb @@ -0,0 +1,57 @@ +
    +
    +

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

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

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

    + <% else %> +

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

    + <% end %> +
    +
    + +
    +
    + <% if @templates.count > 0 %> +
    + + + <% if @templates.count > TABLE_FILTER_MIN_ROWS %> + + + + <% end %> + + + + + + + + + + <% @templates.each do |template| %> + + + + + + + + <% end %> +
    + <%= render(partial: "shared/table_filter", + locals: {path: public_templates_path, placeholder: _('Filter templates')}) %> +
    <%= _('Template Name') %><%= _('Download') %><%= _('Organisation Name') %><%= _('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 %> + <%= raw links_to_a_elements(template.links['funder'], '
    ') %> +
    + <%= raw links_to_a_elements(template.links['sample_plan'], '
    ') %> +
    +
    + <% 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..95d9921 100644 --- a/app/views/questions/_add_question.html.erb +++ b/app/views/questions/_add_question.html.erb @@ -5,159 +5,89 @@ **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 , 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) %>
    + +
    + <%= 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 institution-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..55b6c95 100644 --- a/app/views/questions/_edit_question.html.erb +++ b/app/views/questions/_edit_question.html.erb @@ -5,167 +5,108 @@ **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), 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.'))%> + +
    + <%= 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') %> + +
    -
    -
    -
    <%= _('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(: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 %> +
    -
    - <%= 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(: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') %> +
    -
    -
    -
    <%= _('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") %> + +
    + <%= 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 institution-level guidance to appear alongside your 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."))%> +
    +
    + <%= 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), + 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' %> +
    -
    -
    -
    <%= _('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.

    "))%> -
    -
    -
    - -
    - <%= 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..b418360 --- /dev/null +++ b/app/views/questions/_new_edit_question_option_based.html.erb @@ -0,0 +1,44 @@ +<%# 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 %> + <%= label_tag('answer[text]', _('Additional Information'), class: 'control-label') %> + <%= text_area_tag('answer[text]', answer.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..755e6b3 --- /dev/null +++ b/app/views/questions/_new_edit_question_textarea.html.erb @@ -0,0 +1,5 @@ +<%# locals: { f, question, answer } %> +
    + <%= f.label(:text, raw(question.text), class: 'control-label') %> + <%= text_area_tag('answer[text]', answer.text || question.default_value, id: "answer-text-#{question.id}", class: "form-control tinymce_answer") %> +
    \ 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..f53382e 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) %> + <%= 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), + 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..3bfa3fc 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, 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..1e638a1 100644 --- a/app/views/sections/_edit_section.html.erb +++ b/app/views/sections/_edit_section.html.erb @@ -1,117 +1,67 @@ - +
    +
    +

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

    +
    +
    +
    +
    + <%= form_for(section, url: admin_update_section_path(section, phase: phase), 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.')) %>
    -
    - - - - + +
    + <%= f.label(:description, _('Description'), class: "control-label") %> +
    "> + <%= text_area_tag('section-desc', section.description, class: "section tinymce form-control") %> +
    + +
    +
    + <%= f.button(_('Save'), class: 'btn btn-default', type: "submit") %> + <%= link_to _('Delete'), admin_destroy_section_path(section_id: section.id), 'data-method': 'delete', rel: 'nofollow', + 'data-confirm': _("You are about to delete '%{section_title}'. This will affect questions linked to this section. Are you sure?") % { :section_title => section.title }, class: 'btn btn-default', role:'button' %> + <%= link_to _('Cancel'), admin_show_phase_path(section.phase), class: 'btn btn-default', role: 'button' %> +
    +
    + <% end %> +
    +
    +

    <%= _('Questions') %>

    + <% questions = section.questions.order('number') %> + <% if questions.present? %> + <% questions.each do |question| %> +
    +
    "> + <%= render partial: 'questions/show_question', locals: {template: template, question: question} %> +
    +
    " style="display: none;"> + <%= render partial: 'questions/edit_question', locals: {template: template, question: question} %> +
    + <%= raw("
    ") if questions.last.id == question.id %> + <% end %> + <% end %> +
    +
    +
    +
    +
    + <%= link_to(_('Add Question'), '#', { class: 'btn btn-default question_new_link', role: "button" }) %>
    - - -
    -
    - - - - <% 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' %> -
    -<% 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%> - - - - -
    -
    - <%= hidden_field_tag :section_id, section.id, class: "section_id" %> - <%= link_to _('Add question'), '#', class: "btn btn-primary add_question_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 7617def..50167dd 100644 --- a/app/views/sections/_progress.html.erb +++ b/app/views/sections/_progress.html.erb @@ -1,14 +1,8 @@ - - <% num_section_questions = section.questions.size() %> - <% num_section_answers = section.num_answered_questions(plan.id) %> - <% 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 %> \ No newline at end of file +<%# locals: { section, plan } %> + +<% num_section_questions = section.questions.size() %> +<% num_section_answers = section.num_answered_questions(plan.id) %> + + (<%= 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..c8f14c0 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} %> +
    + <% if question.modifiable %> +
    " style="display: none;"> + <%= render partial: 'questions/edit_question', locals: {template: template, question: question} %> +
    + <% 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..624939d --- /dev/null +++ b/app/views/shared/_access_controls.html.erb @@ -0,0 +1,23 @@ + \ 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..55e7e7a --- /dev/null +++ b/app/views/shared/_create_account_form.html.erb @@ -0,0 +1,37 @@ +<%= 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..67c3b98 --- /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..7f9b506 --- /dev/null +++ b/app/views/shared/_my_org.html.erb @@ -0,0 +1,16 @@ +<%= 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/_sign_in_form.html.erb b/app/views/shared/_sign_in_form.html.erb new file mode 100644 index 0000000..e032ead --- /dev/null +++ b/app/views/shared/_sign_in_form.html.erb @@ -0,0 +1,41 @@ +<%= form_for resource, as: 'user', url: user_session_path, html: {id: "sign_in_form"} do |f| %> + <% if Rails.application.config.shibboleth_enabled %> + +

    <%= _('Sign in with') %>

    + +
    + + <% if request.fullpath != "/users/sign_up?nosplash=true" && session[:shibboleth_data].nil? then%> + <% target = (Rails.application.config.shibboleth_use_filtered_discovery_service ? shibboleth_ds_path : user_shibboleth_omniauth_authorize_path) %> + <%= _('Your Institution') %> + <%else%> + <%= f.hidden_field :shibboleth_id, :value => session[:shibboleth_data][:uid] %> + <%end%> + +
    + +

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

    + <% end %> + +
    + <%= f.label(:email, _('Email'), class: 'control-label') %> + <%= f.email_field(:email, class: 'form-control', "aria-required": true) %> +
    +
    + <%= label_tag 'remember_email', raw("#{check_box_tag 'remember_email'}#{_('Remember email')}") %> +
    +
    + <%= f.label(:password, _('Password'), class: 'control-label') %> + <%= f.password_field(:password, class: 'form-control', "aria-required": true) %> +
    +
    + +
    + + <%= f.button(_('Sign in'), class: "btn btn-default", type: "submit") %> +
    + <%= link_to _('Forgot password?'), new_password_path('user') %> +
    +<% 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/static_pages/about_us.html.erb b/app/views/static_pages/about_us.html.erb index 3b32241..7a119dd 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] } %> -

    -