diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index c2cecc0619..eeb6c760ef 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -56,7 +56,7 @@ //= require calendar //= require inplace //= require strain -//= require checkbox +//= require batch_asset_selection //= require cytoscape.js-2.5.0/cytoscape //= require cytoscape_isa_graph //= require bives diff --git a/app/assets/javascripts/batch_asset_selection.js b/app/assets/javascripts/batch_asset_selection.js new file mode 100644 index 0000000000..e6bb2e2c27 --- /dev/null +++ b/app/assets/javascripts/batch_asset_selection.js @@ -0,0 +1,121 @@ +$j(document).ready(function () { + $j('.batch-selection-select-children').click(BatchAssetSelection.selectChildren); + $j('.batch-selection-deselect-children').click(BatchAssetSelection.deselectChildren); + $j('.batch-selection-collapse-children').click(BatchAssetSelection.collapseRecursively); + $j('.batch-selection-expand-children').click(BatchAssetSelection.expandRecursively); + $j('.batch-selection-show-permissions').click(function (event) { + event.preventDefault(); + $j('.batch-selection-permission-list', $j(this).closest('.batch-selection-scope')).show(); + }) + $j('.batch-selection-hide-permissions').click(function (event) { + event.preventDefault(); + $j('.batch-selection-permission-list', $j(this).closest('.batch-selection-scope')).hide(); + }) + $j('.batch-selection-hide-blocked').click(BatchAssetSelection.hideBlocked).click(); // Trigger on page load + $j('.batch-selection-show-blocked').click(BatchAssetSelection.showBlocked); + $j('.batch-selection-collapse-toggle').click(function () { + BatchAssetSelection.toggleCollapse(this); + return false; + }); + $j('.batch-selection-check-btn').click(function (event) { + if (event.target.nodeName.includes('BUTTON')) { + $j(this).find(':checkbox').click(); + } + }); + $j('.batch-selection-check-btn :checkbox').click(function () { + BatchAssetSelection.checkRepeatedItems(this.className, this.checked); + }); + $j('.batch-selection-managed-by-toggle').click(function (event) { + event.preventDefault(); + $j('.batch-selection-managed-by-list:first', $j(this).closest('.batch-selection-scope')).toggle(); + }); + $j('.batch-selection-permissions-toggle').click(function (event) { + event.preventDefault(); + $j('.batch-selection-permission-list:first', $j(this).closest('.batch-selection-scope')).toggle(); + }); +}); + +const BatchAssetSelection = { + blockedSelectors: '.not-visible, .not-manageable, .already-published', + selectChildren: function (event) { + event.preventDefault(); + BatchAssetSelection.setChildren($j(this).closest('.batch-selection-scope'), true); + }, + + deselectChildren: function (event) { + event.preventDefault(); + BatchAssetSelection.setChildren($j(this).closest('.batch-selection-scope'), false); + }, + + setChildren: function (scope, value) { + const children = $j(':checkbox', scope); + const classes = new Set(); + for (let child of children) { + classes.add(child.className); + } + + classes.forEach(c => BatchAssetSelection.checkRepeatedItems(c, value)); + }, + + checkRepeatedItems: function (className, check) { + document.getElementById('batch-asset-selection') + .querySelectorAll('.' + className).forEach(e => e.checked = check); + }, + + toggleManagers: function () { + $j('.batch-selection-managed-by-list', $j(this).closest('.batch-selection-asset')).toggle(); + + return false; + }, + + toggleCollapse: function (element, state) { + if (state === undefined) { + state = !element.classList.contains('open'); + } + element.classList.toggle('open', state); + $j(element).closest('.batch-selection-scope').children('.batch-selection-collapse').toggle(state); + }, + + collapseRecursively: function () { + const scope = $j(this).closest('.batch-selection-scope').children('.batch-selection-collapse'); + const toggles = $j('.batch-selection-collapse-toggle', scope); + for (let toggle of toggles) { + BatchAssetSelection.toggleCollapse(toggle, false); + } + + return false; + }, + + expandRecursively: function () { + const scope = $j(this).closest('.batch-selection-scope').children('.batch-selection-collapse'); + const toggles = $j('.batch-selection-collapse-toggle', scope); + for (let toggle of toggles) { + BatchAssetSelection.toggleCollapse(toggle, true); + } + + return false; + }, + + hideBlocked: function () { + const children = $j(this).closest('.batch-selection-scope') + .find(BatchAssetSelection.blockedSelectors) + .closest('.batch-selection-asset'); + for (let child of children) { + const element = $j(child); + // Don't hide if any non-blocked children + if (!$j(':checkbox', element).length) { + element.hide(); + } + } + + return false; + }, + + showBlocked: function () { + $j(this).closest('.batch-selection-scope') + .find(BatchAssetSelection.blockedSelectors) + .closest('.batch-selection-asset').show(); + + return false; + } +} diff --git a/app/assets/javascripts/checkbox.js b/app/assets/javascripts/checkbox.js deleted file mode 100644 index d5751b2fa2..0000000000 --- a/app/assets/javascripts/checkbox.js +++ /dev/null @@ -1,42 +0,0 @@ -$j(document).ready(function () { - $j("a.selectChildren").click(function (event) { - event.preventDefault(); - selectChildren(this,$j(this).data("cb_parent_selector")) - }) - $j("a.deselectChildren").click(function (event) { - event.preventDefault(); - deselectChildren(this,$j(this).data("cb_parent_selector")) - }) - $j('#jstree').on('click', 'a.selectChildren', function (event) { - event.preventDefault(); - selectChildren(this,$j(this).data("cb_parent_selector")) - }) - $j('#jstree').on('click', 'a.deselectChildren', function (event) { - event.preventDefault(); - deselectChildren(this,$j(this).data("cb_parent_selector")) - }) -}) - -function selectChildren(select_all_element,cb_parent_selector){ - let children_checkboxes = $j(':checkbox', $j(select_all_element).parents(cb_parent_selector)) - for(let checkbox of children_checkboxes){ - let checkbox_element = { className: checkbox.className, checked: true } - checkRepeatedItems(checkbox_element) - } -} - -function deselectChildren(deselect_all_element,cb_parent_selector){ - let children_checkboxes = $j(':checkbox', $j(deselect_all_element).parents(cb_parent_selector)) - for(let checkbox of children_checkboxes){ - let checkbox_element = { className: checkbox.className, checked: false } - checkRepeatedItems(checkbox_element) - } -} - -function checkRepeatedItems(checkbox_element) { - let repeated_elements = document.getElementsByClassName(checkbox_element.className) - let check = checkbox_element.checked - for(let element of repeated_elements){ - element.checked = check - } -} \ No newline at end of file diff --git a/app/assets/stylesheets/publishing.scss b/app/assets/stylesheets/publishing.scss index 1de7aec917..3a9e3bed42 100644 --- a/app/assets/stylesheets/publishing.scss +++ b/app/assets/stylesheets/publishing.scss @@ -17,14 +17,6 @@ ul.publishing_options li.secondary { } -.published { - color: limegreen; -} - -span.published { - color: green; -} - span.approve { color: limegreen; font-weight: bolder; @@ -64,13 +56,12 @@ ul.item_for_decision { .publishing_options { padding: 0.5em 1em; - &.publishable { - background-color: #fafaff; - border-left-color: $btn-success-bg; + &.not-manageable { + border-left-color: $btn-warning-bg; } - &.not-publishable { - border-left-color: $btn-warning-bg; + &.not-visible { + border-left-color: $btn-danger-bg; } &.already-published { @@ -78,18 +69,6 @@ ul.item_for_decision { } } -.publish-colour { - color: $btn-success-bg; -} - -.publish-checkbox { - background-color: $btn-success-bg; - color: $btn-success-color; - padding: 0px 10px; - border-radius: 5px; - display: inline-block; -} - ul.decided_item { padding-left: 1em; } @@ -108,3 +87,43 @@ ul.decided_item li.type_and_title { padding: 0.5em 15px; margin: 0.3em 0em; } + +.batch-selection-buttons { + margin-bottom: 1em; + display: flex; + gap: 1em; +} + +.batch-selection-asset-row { + display: flex; + gap: 1em; + align-items: center; + .visibility_icon { + margin: 0; + } +} + +.batch-selection-permission-list, +.batch-selection-managed-by-list, +.batch-selection-children { + margin-left: 3em; +} + +.batch-selection-check-btn { + input { + margin: 0; + vertical-align: middle; + } +} + +.batch-selection-collapse-toggle { + cursor: pointer; + + .glyphicon-menu-down { display: none; }; + .glyphicon-menu-right { display: inline; }; + + &.open { + .glyphicon-menu-down { display: inline; }; + .glyphicon-menu-right { display: none; }; + } +} diff --git a/app/assets/stylesheets/sharing.scss b/app/assets/stylesheets/sharing.scss index d396101a0c..6f56e31c5f 100644 --- a/app/assets/stylesheets/sharing.scss +++ b/app/assets/stylesheets/sharing.scss @@ -210,17 +210,3 @@ padding-left: 16px; } } - -.parent-btn-checkbox { - padding: 1px 6px 0px 6px; - border-radius: 5px; - display: inline-block; - height: 25px; -} -.parent-btn-dropdown { - padding: 2px 6px 10px 0px; - border-radius: 5px; - display: inline-block; - height: 25px; -} - diff --git a/app/controllers/single_pages_controller.rb b/app/controllers/single_pages_controller.rb index a16e525ce7..591eb2600c 100644 --- a/app/controllers/single_pages_controller.rb +++ b/app/controllers/single_pages_controller.rb @@ -4,6 +4,7 @@ class SinglePagesController < ApplicationController include Seek::AssetsCommon include Seek::Sharing::SharingCommon + include Seek::Publishing::PublishingCommon include Seek::Data::SpreadsheetExplorerRepresentation before_action :set_up_instance_variable diff --git a/app/helpers/assets_helper.rb b/app/helpers/assets_helper.rb index b3b74a9228..05c4d64ee1 100644 --- a/app/helpers/assets_helper.rb +++ b/app/helpers/assets_helper.rb @@ -94,18 +94,6 @@ def publishing_item_param(item) "publish[#{item.class.name}][#{item.id}]" end - def sharing_item_param(item) - if item.try(:is_isa?) - "share_isa[#{item.class.name}][#{item.id}]" - elsif (item.respond_to? (:investigations)) && (!item.investigations.any?) - "share_not_isa[#{item.class.name}][#{item.id}]" - elsif !item.respond_to? (:investigations) - "share_not_isa[#{item.class.name}][#{item.id}]" - else - "share_isa[#{item.class.name}][#{item.id}]" - end - end - def include_downloadable_item?(items) has_downloadable_item = false items.each do |item| @@ -346,4 +334,10 @@ def controlled_vocab_annotation_items(controlled_vocab_terms) end.join(', ').html_safe end + def batch_selection_collapse_toggle + content_tag(:span, class: 'batch-selection-collapse-toggle open') do + concat content_tag(:span, '', class: 'glyphicon glyphicon-menu-down', 'aria-hidden' => 'true') + concat content_tag(:span, '', class: 'glyphicon glyphicon-menu-right', 'aria-hidden' => 'true') + end + end end diff --git a/app/helpers/policy_helper.rb b/app/helpers/policy_helper.rb index 1a70d38ff7..7c431caa93 100644 --- a/app/helpers/policy_helper.rb +++ b/app/helpers/policy_helper.rb @@ -159,7 +159,7 @@ def project_policy_json(project) hash.to_json.html_safe end - def permission_title(permission, member_prefix: false, icon: false) + def permission_title(permission, member_prefix: false, icon: false, link: false) if permission.is_a?(Permission) type = permission.contributor_type contributor = permission.contributor @@ -168,18 +168,31 @@ def permission_title(permission, member_prefix: false, icon: false) contributor = permission end - if type == 'Person' + option = { target: :_blank } + case type + when 'Person' text = "#{contributor.first_name} #{contributor.last_name}" - elsif type == 'WorkGroup' - text = "#{member_prefix ? 'Members of ' : ''}#{contributor.project.title} @ #{contributor.institution.title}" + text = link_to(h(text), contributor, option).html_safe if link + when 'WorkGroup' + institution = contributor.institution + project = contributor.project + if link + text = "#{member_prefix ? 'Members of ' : ''}#{link_to(h(project.title), project, option)} @ #{link_to(h(institution.title), institution, option)}".html_safe + else + text = "#{member_prefix ? 'Members of ' : ''}#{project.title} @ #{institution.title}" + end else - text = "#{member_prefix ? 'Members of ' : ''}#{contributor.title}" + if link + text = "#{member_prefix ? 'Members of ' : ''}#{link_to(h(contributor.title), contributor, option)}".html_safe + else + text = "#{member_prefix ? 'Members of ' : ''}#{contributor.title}" + end end if icon content_tag(:span, class: 'type-icon-wrapper') do image_tag(asset_path(icon_filename_for_key(type.underscore)), class: 'type-icon') - end.html_safe + " #{text}" + end.html_safe + " #{text}".html_safe else text end diff --git a/app/helpers/sharing_permissions_helper.rb b/app/helpers/sharing_permissions_helper.rb deleted file mode 100644 index 63389141bb..0000000000 --- a/app/helpers/sharing_permissions_helper.rb +++ /dev/null @@ -1,225 +0,0 @@ -module SharingPermissionsHelper - - ITEMS_NOT_IN_ISA_HASH = { - "id": "not_isa-tree", - "data": { - "loadable": false - }, - "li_attr": { - class:"root-node" - }, - "a_attr": { - - }, - "children": [ - ], - "text": "Not in ISA", - "state": { - "opened": true - } - } - - ALL_INVESTIGATIONS_HASH = { - "id": "isa-tree", - "data": { - "loadable": false - }, - "li_attr": { - class:"root-node" - }, - "a_attr": { - - }, - "children": [ - ], - "text": "ISA", - "state": { - "opened": true - } - } - - def build_tree_json(hash, root_item) - - objects = hash[:nodes].map(&:object) - real_edges = hash[:edges].select { |e| objects.include?(e[0]) } - - roots = hash[:nodes].select do |n| - real_edges.none? { |_parent, child| child == n.object } - end - - nodes = roots.map { |root| create_tree_node(hash, root.object, root_item) }.flatten - nodes.to_json - end - - def create_tree_node(hash, object, root_item = nil) - - child_edges = hash[:edges].select do |parent, _child| - parent == object - end - - node = hash[:nodes].detect { |n| n.object == object } - - entry = { - id: unique_node_id(object), - data: { loadable: false }, - li_attr: { 'data-node-id' => node_id(object) }, - children: [] - } - - entry[:text] = object.title - entry[:icon] = asset_path(resource_avatar_path(object) || icon_filename_for_key("#{object.class.name.downcase}_avatar")) - - filtered_child_edges = child_edges.reject { |c| (c[1].instance_of? Publication) || (c[1].instance_of?(Seek::ObjectAggregation))} - child_edges_with_permission = filtered_child_edges.select { |c| c[1].can_manage? } - - unless child_edges_with_permission.blank? - entry[:children] += child_edges_with_permission.map { |c| create_tree_node(hash, c[1], root_item) } - end - - if node.child_count > 0 - if node.child_count > child_edges.count - entry[:children] << { - id: unique_child_count_id(object), - parent: entry[:id], - text: "Show #{node.child_count - child_edges.count} more", - a_attr: { class: 'child-count-leaf' }, - li_attr: { 'data-node-id' => child_count_id(object) }, - data: { child_count: true } - } - end - entry[:state] = { opened: false } - else - entry[:state] = { opened: false } - end - entry - end - - def add_permissions_to_tree_json (parent_node) - - parent_node.each do |node| - node["li_attr"][:class] = "asset-node-row" - node["a_attr"] = {} - node["a_attr"][:class] = "asset-node" - - if !node["children"].nil? && node["children"].size > 0 - add_permissions_to_tree_json (node["children"]) - end - add_asset_permission_nodes(node) - end - parent_node - end - - def add_asset_permission_nodes(parent_node) - asset_type = parent_node["id"].split("-")[0] - asset_id = parent_node["id"].split("-")[1].to_i - - # get asset instance - asset = safe_class_lookup(asset_type.camelize).find(asset_id) - parent_node["text"] = "#{h(asset.title)} #{icon_link_to("", "new_window", asset , options = {target:'blank',class:'asset-icon',:onclick => 'window.open(this.href, "_blank");'})}" - - permissions_array = get_permission(asset) - parent_node["children"] = permissions_array + parent_node["children"] - parent_node - end - - def asset_node_json(resource_type, resource_items) - - parent = { - id: resource_type+"-not_isa", - data: { loadable: false }, - li_attr: {class:"asset-type-node"}, - a_attr: {}, - children: [], - text: resource_type - } - - resource_items.each do |item| - - entry_item = { - id: unique_node_id(item), - data: { loadable: false }, - li_attr: { 'data-node-id' => node_id(item), class:"asset-node-row"}, - a_attr: {class:"asset-node"}, - children: [] , - icon: asset_path(resource_avatar_path(item) || icon_filename_for_key("#{item.class.name.downcase}_avatar")), - text: "#{h(item.title)} #{icon_link_to("", "new_window", item , options = {target:'blank',class:'asset-icon',:onclick => 'window.open(this.href, "_blank");'})}" - } - - permissions_array = get_permission(item) - entry_item[:children] = permissions_array - parent[:children].append(entry_item) - end - parent - end - - - def create_policy_node(item, policy_text,sharing_policy_changed) - - entry = { - id: unique_policy_node_id(item), - data: { loadable: false }, - li_attr: { 'data-node-id' => "Permission-"+node_id(item), class:"hide_permission" }, - a_attr: { class:"permission-node #{sharing_policy_changed}"}, - children: [], - text:policy_text - } - entry - end - - def get_permission (item) - - policy =[] - - # policy - downloadable = item.try(:is_downloadable?) - policy_text = "#{Policy.get_access_type_wording(item.policy.access_type, downloadable)} by Public" - sharing_policy_changed = (@batch_sharing_permission_changed && (@items_for_sharing.include? item) && !@policy_params[:access_type].nil?)? "sharing_permission_changed" : "" - - policy.append(create_policy_node(item,policy_text,sharing_policy_changed)) - - #permission - option = {:onclick => 'window.open(this.href, "_blank");'} - - item.policy.permissions.map do |permission| - case permission.contributor_type - when Permission::PROJECT - m = Project.find(permission.contributor_id) - policy_text ="#{Policy.get_access_type_wording(permission.access_type, downloadable)} by Project #{link_to(h(m.title), m, option)}" - sharing_policy_changed = @batch_sharing_permission_changed && (@items_for_sharing.include? item) ? PolicyHelper::permission_changed_item_class(permission, @policy_params) : "" - policy.append(create_policy_node(item,policy_text,sharing_policy_changed)) - when Permission::WORKGROUP - m = WorkGroup.find(permission.contributor_id) - institution = Institution.find(m.institution_id) - project = Project.find(m.project_id) - policy_text ="#{Policy.get_access_type_wording(permission.access_type, downloadable)} by #{link_to(h(project.title), project,option)} @ #{link_to(h(institution.title), institution,option)}" - sharing_policy_changed = @batch_sharing_permission_changed && (@items_for_sharing.include? item) ? PolicyHelper::permission_changed_item_class(permission, @policy_params) : "" - policy.append(create_policy_node(item,policy_text,sharing_policy_changed)) - when Permission::INSTITUTION - m = Institution.find(permission.contributor_id) - policy_text ="#{Policy.get_access_type_wording(permission.access_type, downloadable)} by Institution #{link_to(h(m.title), m,option)}" - sharing_policy_changed = @batch_sharing_permission_changed && (@items_for_sharing.include? item) ? PolicyHelper::permission_changed_item_class(permission, @policy_params) : "" - policy.append(create_policy_node(item,policy_text,sharing_policy_changed)) - when Permission::PERSON - m = Person.find(permission.contributor_id) - policy_text ="#{Policy.get_access_type_wording(permission.access_type, downloadable)} by People #{link_to(h(m.title), m,option)}" - sharing_policy_changed = @batch_sharing_permission_changed && (@items_for_sharing.include? item) ? PolicyHelper::permission_changed_item_class(permission, @policy_params) : "" - policy.append(create_policy_node(item,policy_text,sharing_policy_changed)) - when Permission::PROGRAMME - m = Programme.find(permission.contributor_id) - policy_text ="#{Policy.get_access_type_wording(permission.access_type, downloadable)} by Programme #{link_to(h(m.title), m,option)}" - sharing_policy_changed = @batch_sharing_permission_changed && (@items_for_sharing.include? item) ? PolicyHelper::permission_changed_item_class(permission, @policy_params) : "" - policy.append(create_policy_node(item,policy_text,sharing_policy_changed)) - end - end - policy - end - - - private - - def unique_policy_node_id(object) - "Permission-#{node_id(object)}-#{rand(2**32).to_s(36)}" - end - - -end diff --git a/app/views/assets/_batch_asset_selection.html.erb b/app/views/assets/_batch_asset_selection.html.erb new file mode 100644 index 0000000000..88485bd4c7 --- /dev/null +++ b/app/views/assets/_batch_asset_selection.html.erb @@ -0,0 +1,105 @@ +<% + publishing ||= false + show_hide_blocked ||= false + show_permissions ||= false + show_managers ||= false +-%> + +
- You can select an item to be published by checking the Publish - checkbox beside that item. + You can select an item to be published by checking the checkbox beside that item.
-- You are about the publish the following items. -
-<%= form_tag :action => :publish do %> -