diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index c2140f7..e44ec67 100644 --- a/app/controllers/plans_controller.rb +++ b/app/controllers/plans_controller.rb @@ -24,6 +24,8 @@ # Get the current user's org @default_org = current_user.org if @orgs.include?(current_user.org) + @is_test = params[:test] ||= false + respond_to :html end @@ -37,6 +39,9 @@ @plan.data_contact = current_user.email @plan.funder_name = plan_params[:funder_name] + @plan_params.visibility = (plan_params[:visibility].blank? ? Rails.application.config.default_plan_visibility : + plan_params[:visibility]) + # If a template hasn't been identified look for the available templates if plan_params[:template_id].blank? template_options(plan_params[:org_id], plan_params[:funder_id]) @@ -158,8 +163,11 @@ @plan = Plan.find(params[:id]) authorize @plan + attrs = plan_params + attrs['visibility'] = Rails.application.config.default_plan_visibility if plan_params['visibility'].blank? + respond_to do |format| - if @plan.update_attributes(params[:plan]) + if @plan.update_attributes(attrs) format.html { redirect_to @plan, :editing => false, notice: _('Plan was successfully updated.') } format.json { head :no_content } else @@ -402,11 +410,25 @@ end end end + + # AJAX access to update the plan's visibility + # POST /plans/:id + def visibility + plan = Plan.find(params[:id]) + authorize plan + plan.visibility = "#{plan_params[:visibility]}" + if plan.save + render json: {code: 1, msg: ''} + else + render json: {code: 0, msg: _("Unable to change the plan's Test status")} + 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) end # different versions of the same template have the same dmptemplate_id diff --git a/app/helpers/plans_helper.rb b/app/helpers/plans_helper.rb index bff80a5..d14421a 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,18 +17,34 @@ content_tag(:small, t("helpers.settings.plans.#{key}")) end - # display the role of the user for a given plan def display_role(role) - case role.access_level - when 3 - access = 'Co-owner' - when 2 - access = 'Editor' - when 1 - access = 'Read only' + 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 _('My Inst.') + when 'publicly_visible' + return _('Public') + else + return _('Private') # Both Test and Private + end + end + end diff --git a/app/models/plan.rb b/app/models/plan.rb index c13ffc0..b2691ff 100644 --- a/app/models/plan.rb +++ b/app/models/plan.rb @@ -1164,7 +1164,6 @@ # Only run this before_validation because rails fires this before save/create if self.id.nil? self.title = "My plan (#{self.template.title})" if self.title.nil? && !self.template.nil? - self.visibility = 1 end end diff --git a/app/policies/plan_policy.rb b/app/policies/plan_policy.rb index 8d05ac8..e6d6d15 100644 --- a/app/policies/plan_policy.rb +++ b/app/policies/plan_policy.rb @@ -51,6 +51,10 @@ def duplicate? @plan.editable_by?(@user.id) end + + def visibility? + @plan.administerable_by?(@user.id) + end # TODO: These routes are no lonmger used =begin diff --git a/app/views/branding b/app/views/branding new file mode 120000 index 0000000..dd1660a --- /dev/null +++ b/app/views/branding @@ -0,0 +1 @@ +/Users/briley/Documents/workspace/dmptool_config/app/views/branding/ \ No newline at end of file diff --git a/app/views/plans/_plan_details.html.erb b/app/views/plans/_plan_details.html.erb index ee420d1..64cd46f 100644 --- a/app/views/plans/_plan_details.html.erb +++ b/app/views/plans/_plan_details.html.erb @@ -1,3 +1,5 @@ +<% javascript 'plans/edit.js' %> +
@@ -23,6 +25,20 @@ 'title': _('If applying for funding, state the name exactly as in the grant proposal.') %>
+ + /> +
+
+ + +
+ /> <%= _('Public anyone can view') %>
+ /> <%= _('Limited: anyone in my institution can view') %>
+ /> <%= _('Private: restricted to me and people I invite') %>
+
+ <%= f.hidden_field :visibility %> +
+
<%= 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.') %> @@ -89,6 +105,28 @@ + <%= _('Test plan') %> + <%= @plan.visibility == 'is_test' ? _('Yes') : _('No') %> + + + <%= _('Visibility') %> + + <%= + case @plan.visibility + when 'publicly_visible' + _('Public: anyone can view') + when 'organisationally_visible' + _('Limited: anyone in my institution can view') + when 'is_test' + _('Not applicable: this is a test plan') + else + _('Private: restricted to me and people I invite') + end + %> + + + + <%= _('ID') %> <% if !@plan.identifier.nil? && @plan.identifier != "" then %> 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 05e6bcd..0000000 --- a/app/views/plans/_plan_list_item.html.erb +++ /dev/null @@ -1,29 +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"%> - - <%= link_to _('Duplicate'), duplicate_plan_path(plan), method: :post, remote: true, :class => "dmp_table_link"%> - - <% if plan.owned_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_title.html.erb b/app/views/plans/_plan_title.html.erb index d134aa1..8600196 100644 --- a/app/views/plans/_plan_title.html.erb +++ b/app/views/plans/_plan_title.html.erb @@ -6,3 +6,10 @@
+ +<% if @plan.visibility == 'is_test' %> +
+ + <%= _('This is a') %> <%= _('test plan') %>. +
+<% 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..9d77329 100644 --- a/app/views/plans/index.html.erb +++ b/app/views/plans/index.html.erb @@ -1,5 +1,7 @@ <%- model_class = Plan -%> <% javascript "toolbar.js" %> +<% javascript "plans/index.js" %> +

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

@@ -14,12 +16,50 @@ <%= render(partial: "toolbar") %> - <%= render(partial: "plan_list_head") %> + + + + + + + - <% @plans.each do |plan| %> - <%= render(partial: "plan_list_item", locals: { plan: plan } ) %> - <% end %> + <% @plans.each do |plan| %> + + + + + + + + + + <% end %>
<%= _('Plan') %><%= _('Template') %><%= _('Edited') %><%= _('Role') %><%= _('Visibility') %><%= _('Test') %><%= _('Select an Action') %>
<%= plan.title %><%= plan.template.title %><%= l(plan.latest_update.to_date, formats: :short) %><%= display_role(plan.roles.find_by(user: current_user)) %><%= display_visibility(plan.visibility) %> + <%= plan.administerable_by?(current_user.id) ? '' : 'disabled="true"' %> /> + + <% 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"%> + + <%= link_to _('Duplicate'), duplicate_plan_path(plan), method: :post, remote: true, :class => "dmp_table_link"%> + + <% if plan.owned_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 %> +
@@ -33,6 +73,11 @@

<%= link_to _('Create plan'), - new_plan_path, - :class => "btn btn-primary" %> + new_plan_path, + class: "btn btn-primary" %> + + <%= link_to _('Create test plan'), + "#{new_plan_path}?test=true", + class: "left-indent", + id: "create-test" %>

diff --git a/app/views/plans/new.html.erb b/app/views/plans/new.html.erb index 2c51daf..ba02a8f 100644 --- a/app/views/plans/new.html.erb +++ b/app/views/plans/new.html.erb @@ -1,8 +1,15 @@ -<% javascript "plans/new_plan.js" %> +<% javascript "plans/new.js" %>

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

+ <% if @is_test %> +
+ + <%= _('This is a') %> <%= _('test plan') %>. +
+ <% end %> +

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

@@ -88,6 +95,7 @@
+ <%= render partial: 'shared/accessible_submit_button', locals: {id: 'create_plan_submit', diff --git a/config/application.rb b/config/application.rb index b0c8544..3b1c1d8 100644 --- a/config/application.rb +++ b/config/application.rb @@ -87,10 +87,12 @@ answers/status.js devise/passwords/new.js devise/registrations/edit.js - plans/new_plan.js contacts/new_contact.js home/index.js orgs/shibboleth_ds.js + plans/edit.js + plans/index.js + plans/new.js shared/login_form.js shared/register_form.js) @@ -123,5 +125,12 @@ # Load Branded terminology (e.g. organization name, application name, etc.) config.branding = config_for(:branding).deep_symbolize_keys + + # The default visibility setting for new plans + # organisationally_visible - Any member of the user's org can view, export and duplicate the plan + # publicly_visibile - (NOT advisable because plans will show up in Public DMPs page by default) + # is_test - (NOT advisable because test plans are excluded from statistics) + # privately_visible - Only the owner and people they invite can access the plan + config.default_plan_visibility = 'organisationally_visible' end end diff --git a/config/routes.rb b/config/routes.rb index cfb0a8c..9619b82 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -208,6 +208,7 @@ post 'duplicate' get 'export' post 'invite' + post 'visibility', constraints: {format: [:json]} end collection do diff --git a/lib/assets/javascripts/plans/edit.js b/lib/assets/javascripts/plans/edit.js new file mode 100644 index 0000000..492bc3a --- /dev/null +++ b/lib/assets/javascripts/plans/edit.js @@ -0,0 +1,24 @@ +$(document).ready(function(){ + + $("#is_test").on('click, change', function(e){ + toggleVisibility(); + // If the test box is checked then update the visibility to 'is_test' + if($("#is_test").is(':checked')){ + $('#plan_visibility').val('is_test'); + }else{ + $('#plan_visibility').val(''); + } + }); + + $("input[name='visibility']").on('change', function(e){ + $('#plan_visibility').val($("input[name='visibility']:checked").val()); + }); + + toggleVisibility(); + + function toggleVisibility(){ + let test = $("#is_test").is(':checked'); + // If the test checkbox is true then disable the visibility dropdown + $("input[name='visibility']").attr('aria-disabled', test).attr('disabled', test); + } +}); \ No newline at end of file diff --git a/lib/assets/javascripts/plans/index.js b/lib/assets/javascripts/plans/index.js new file mode 100644 index 0000000..df4b4d6 --- /dev/null +++ b/lib/assets/javascripts/plans/index.js @@ -0,0 +1,19 @@ +$(document).ready(function(){ + $("input[type='checkbox']").on('click, change', function(e){ + let self = this; + let id = $(this).attr("id").replace("is_test-", ""); + let params = {plan: {visibility: $(this).is(':checked') ? 'is_test' : 'privately_visible'}}; + + // Update the visbility to test or private + $.post("/plans/" + id + "/visibility", params, function(data){ + if(data['code'] === 1){ + // If the save was successful make sure the Visibility text gets updated to 'Private' + $(self).parent().siblings("#visibility-" + id).html(__('Private')); + }else{ + // Display an error message + $("#main-page-alert").show().html(data['msg']); + e.preventDefault(); + } + }); + }); +}); \ No newline at end of file diff --git a/lib/assets/javascripts/plans/new.js b/lib/assets/javascripts/plans/new.js new file mode 100644 index 0000000..7bf6e19 --- /dev/null +++ b/lib/assets/javascripts/plans/new.js @@ -0,0 +1,65 @@ +$(document).ready(function(){ + $("#available-templates").hide(); + + // retrieve the template options and toggle the submit button on page reload + handleComboboxChange(); + handleCheckboxClick("org", $("#plan_no_org").prop("checked")); + handleCheckboxClick("funder", $("#plan_no_funder").prop("checked")); + + // When the hidden org and funder id fields change toogle the submit button + $("#plan_org_id, #plan_funder_id").change(function(){ + handleComboboxChange(); + }); + + // Make sure the checkbox is unchecked if we're entering text + $(".js-combobox").keyup(function(){ + var whichOne = $(this).prop('id').split('_')[1]; + $("#plan_no_" + whichOne).prop("checked", false); + }); + + // If the user clicks the no Org/Funder checkbox disable the dropdown + // and hide clear button + $("#plan_no_org, #plan_no_funder").click(function(){ + var whichOne = $(this).prop('id').split('_')[2]; + handleCheckboxClick(whichOne, this.checked); + }); + + $("#plan_template_id").change(function(){ + $("#create_plan_submit").attr('aria-disabled', ($(this).val().trim().length <= 0)); + }); +}); + +// Only display the submit button if the user has made each decision +// ------------------------------------------------------------- +function handleComboboxChange(){ + // If the (no_org checkbox is checked OR an org was selected) AND + // (no_funder checkbox is checked OR a funder was selected) AND + // (the template selector is not visible OR a template has been selected) + var retrieve = ($("#plan_no_org").prop("checked") || + $("#plan_org_id").val().trim().length > 0) && + ($("#plan_no_funder").prop("checked") || + $("#plan_funder_id").val().trim().length > 0); + + if(retrieve){ + if($("#plan_template_id").val().trim().length <= 0){ + $("form").submit(); + } + + }else{ + $("#available-templates").fadeOut(); + $("#plan_template_id").val(""); + } +} + +// Clear the combobox and disable it if the box was checked +// ------------------------------------------------------------- +function handleCheckboxClick(name, checked){ + $("#plan_" + name + "_name").prop("disabled", checked); + $("#plan_template_id").val("").change(); + + if(checked){ + $("#plan_" + name + "_name").val(""); + $("#plan_" + name + "_id").val("").change(); + $("#plan_" + name + "_name").siblings(".combobox-clear-button").hide(); + } +} diff --git a/lib/assets/javascripts/plans/new_plan.js b/lib/assets/javascripts/plans/new_plan.js deleted file mode 100644 index 7bf6e19..0000000 --- a/lib/assets/javascripts/plans/new_plan.js +++ /dev/null @@ -1,65 +0,0 @@ -$(document).ready(function(){ - $("#available-templates").hide(); - - // retrieve the template options and toggle the submit button on page reload - handleComboboxChange(); - handleCheckboxClick("org", $("#plan_no_org").prop("checked")); - handleCheckboxClick("funder", $("#plan_no_funder").prop("checked")); - - // When the hidden org and funder id fields change toogle the submit button - $("#plan_org_id, #plan_funder_id").change(function(){ - handleComboboxChange(); - }); - - // Make sure the checkbox is unchecked if we're entering text - $(".js-combobox").keyup(function(){ - var whichOne = $(this).prop('id').split('_')[1]; - $("#plan_no_" + whichOne).prop("checked", false); - }); - - // If the user clicks the no Org/Funder checkbox disable the dropdown - // and hide clear button - $("#plan_no_org, #plan_no_funder").click(function(){ - var whichOne = $(this).prop('id').split('_')[2]; - handleCheckboxClick(whichOne, this.checked); - }); - - $("#plan_template_id").change(function(){ - $("#create_plan_submit").attr('aria-disabled', ($(this).val().trim().length <= 0)); - }); -}); - -// Only display the submit button if the user has made each decision -// ------------------------------------------------------------- -function handleComboboxChange(){ - // If the (no_org checkbox is checked OR an org was selected) AND - // (no_funder checkbox is checked OR a funder was selected) AND - // (the template selector is not visible OR a template has been selected) - var retrieve = ($("#plan_no_org").prop("checked") || - $("#plan_org_id").val().trim().length > 0) && - ($("#plan_no_funder").prop("checked") || - $("#plan_funder_id").val().trim().length > 0); - - if(retrieve){ - if($("#plan_template_id").val().trim().length <= 0){ - $("form").submit(); - } - - }else{ - $("#available-templates").fadeOut(); - $("#plan_template_id").val(""); - } -} - -// Clear the combobox and disable it if the box was checked -// ------------------------------------------------------------- -function handleCheckboxClick(name, checked){ - $("#plan_" + name + "_name").prop("disabled", checked); - $("#plan_template_id").val("").change(); - - if(checked){ - $("#plan_" + name + "_name").val(""); - $("#plan_" + name + "_id").val("").change(); - $("#plan_" + name + "_name").siblings(".combobox-clear-button").hide(); - } -} diff --git a/lib/assets/stylesheets/roadmap-form.scss b/lib/assets/stylesheets/roadmap-form.scss index 9bfec3f..d8f854c 100644 --- a/lib/assets/stylesheets/roadmap-form.scss +++ b/lib/assets/stylesheets/roadmap-form.scss @@ -128,6 +128,9 @@ .input-medium { width: 30%; } + select.input-medium { + width: 32%; + } .input-small { width: 10%; } @@ -452,6 +455,12 @@ width: 95%; } +/* View plans */ +/* ------------------------------------------------ */ +div.main_page_content p #create-test { + vertical-align: middle; +} + /* Create plan */ /* ------------------------------------------------ */ #create_plan { @@ -490,3 +499,19 @@ } } } + +/* Edit plan details */ +/* ------------------------------------------------ */ +.edit-plan-details form.roadmap-form fieldset.side-by-side div { + label.radio-label { + vertical-align: top; + } + + .inline-radios { + margin-left: -5px; + } + + input[type='radio'] { + margin: 0 10px 5px -5px; + } +} \ No newline at end of file diff --git a/lib/assets/stylesheets/roadmap.scss b/lib/assets/stylesheets/roadmap.scss index d52d951..a54c03e 100644 --- a/lib/assets/stylesheets/roadmap.scss +++ b/lib/assets/stylesheets/roadmap.scss @@ -74,6 +74,33 @@ text-align: center; } +.left-indent { + margin-left: 15px; +} + +div.roadmap-info-box { + position: absolute; + float: right; + top: 200px; + right: 12%; + background-color: $white; + padding: 8px 20px; + border-radius: 5px; + vertical-align: middle; + + span { + padding-left: 30px; + } + .fa { + position: absolute; + top: 10px; + background: transparent; + color: $black; + font-size: 16pt; + margin-right: 15px; + } +} + table.dmp_table{ width: 100%; border-collapse: separate; @@ -81,4 +108,22 @@ border-bottom: 0px; margin-bottom: 20px; table-layout: fixed; + + thead th, tbody td { + padding: 6px 15px 6px 6px; + overflow: hidden; + } + + .col-tiny { + width: 5%; + } + .col-small { + width: 10%; + } + .col-medium { + width: 20%; + } + th.col-large { + width: 35%; + } } diff --git a/test/functional/plans_controller_test.rb b/test/functional/plans_controller_test.rb index 9756da0..8507036 100644 --- a/test/functional/plans_controller_test.rb +++ b/test/functional/plans_controller_test.rb @@ -74,6 +74,7 @@ assert assigns(:orgs) assert assigns(:funders) assert assigns(:default_org) + assert assigns(:is_test) end # POST /plans (plans_path) @@ -91,6 +92,9 @@ assert_response :success assert assigns(:plan) assert_equal "Testing Create", Plan.last.title, "expected the record to have been created" + + # assert that the default visibility is used when none is specified + assert_equal Rails.application.config.default_plan_visibility, Plan.last.visibility, "Expected the plan to have been assigned the default visibility" end # GET /plan/:id (plan_path)