diff --git a/Gemfile.lock b/Gemfile.lock index 180cb13d9..12d7421ad 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -420,7 +420,7 @@ GEM json redcarpet (3.6.0) regexp_parser (2.9.2) - reline (0.5.9) + reline (0.5.10) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) @@ -433,7 +433,7 @@ GEM rouge (4.3.0) rspec-core (3.13.1) rspec-support (~> 3.13.0) - rspec-expectations (3.13.2) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-mocks (3.13.1) @@ -448,17 +448,17 @@ GEM rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.1) - rubocop (1.66.0) + rubocop (1.66.1) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.4, < 3.0) - rubocop-ast (>= 1.32.1, < 2.0) + rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.32.2) + rubocop-ast (1.32.3) parser (>= 3.3.1.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) @@ -489,7 +489,7 @@ GEM simplecov-cobertura (2.1.0) rexml simplecov (~> 0.19) - simplecov-html (0.12.3) + simplecov-html (0.13.1) simplecov_json_formatter (0.1.4) snaky_hash (2.0.1) hashie @@ -502,11 +502,12 @@ GEM actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) - sshkit (1.23.0) + sshkit (1.23.1) base64 net-scp (>= 1.1.2) net-sftp (>= 2.1.2) net-ssh (>= 2.8.0) + ostruct stimulus-rails (1.3.4) railties (>= 6.0.0) string-similarity (2.1.0) @@ -518,7 +519,7 @@ GEM execjs (>= 0.3.0, < 3) thor (1.3.2) tilt (2.4.0) - time (0.3.0) + time (0.4.0) date timeout (0.4.1) turbo-rails (2.0.6) @@ -550,7 +551,7 @@ GEM will_paginate (3.3.1) xpath (3.2.0) nokogiri (~> 1.8) - yard (0.9.36) + yard (0.9.37) zeitwerk (2.6.18) PLATFORMS diff --git a/app/assets/images/icons/slices.svg b/app/assets/images/icons/slices.svg index e92cb02f9..ce6a9a3d9 100644 --- a/app/assets/images/icons/slices.svg +++ b/app/assets/images/icons/slices.svg @@ -1,4 +1,4 @@ - - + + diff --git a/app/assets/stylesheets/application.css.scss.erb b/app/assets/stylesheets/application.css.scss.erb index c576aaf8c..ad06feff6 100755 --- a/app/assets/stylesheets/application.css.scss.erb +++ b/app/assets/stylesheets/application.css.scss.erb @@ -58,6 +58,7 @@ @import "agent_tooltip"; @import "content_finder"; @import "tools"; +@import "taxonomy"; @import "portal_configuration"; /* Bootstrap and Font Awesome */ diff --git a/app/assets/stylesheets/taxonomy.scss b/app/assets/stylesheets/taxonomy.scss new file mode 100644 index 000000000..6043973aa --- /dev/null +++ b/app/assets/stylesheets/taxonomy.scss @@ -0,0 +1,101 @@ +.taxonomy-page-container { + display: flex; + justify-content: center; + } +.taxonomy-page-subcontainer { + width: 1248px; + padding: 20px 50px; +} +.taxonomy-page-title .text{ + font-size: 25px; + font-weight: 700; +} + +.taxonomy-page-title .line{ + height: 2px; + width: 57px; + background-color: var(--primary-color); + border-radius: 10px; +} +.taxonomy-page-decription{ + color: #888888; + margin: 20px 0; +} +.taxonomy-section{ + display: flex; +} +.taxonomy-section .second-row{ + margin-left: 10px; +} +.taxonomy-card{ + border: 1px solid #DFDFDF; + padding: 15px 20px; + width: 569px; + border-radius: 5px; + margin-top: 10px; +} + +.taxonomy-card .title-bar{ + display: flex; + justify-content: space-between; + align-items: center; +} +.taxonomy-card .title{ + color: var(--primary-color); + font-size: 18px; + font-weight: bold; +} + +.taxonomy-card .ontologies{ + display: flex; + align-items: center; + color: var(--primary-color); + margin: 3px 0; +} +.taxonomy-card .ontologies svg{ + width: 15px; + height: 15px; + margin-right: 10px; +} + +.taxonomy-card .ontologies svg path{ + fill: var(--primary-color); +} +.taxonomy-card .description{ + color: #666666; + padding-bottom: 5px; +} +.taxonomy-slice-svg{ + width: 35px; + height: 35px; + margin-bottom: 2px; +} + +.taxonomy-children-reveal{ + display: flex; + align-items: center; + cursor: pointer; + color: var(--primary-color); +} +.taxonomy-children-reveal svg{ + margin-left: 5px; + margin-top: 3px; +} + +.taxonomy-children .taxonomy-card{ + border: none; + padding: 0px 18px; + width: unset; +} + +.taxonomy-children-container{ + display: flex; + margin-top: 10px +} + +.taxonomy-children-line{ + width: 1px; + background-color: var(--primary-color); +} + + diff --git a/app/components/display/search_result_component.rb b/app/components/display/search_result_component.rb index cb67545d3..74f0e725b 100644 --- a/app/components/display/search_result_component.rb +++ b/app/components/display/search_result_component.rb @@ -33,9 +33,9 @@ def reuses_id def details_button link_to_modal(nil, "/ajax/class_details?modal=true&ontology=#{@ontology_acronym}&conceptid=#{@uri}&styled=false", data: { show_modal_title_value: @title, show_modal_size_value: 'modal-xl' }) do - content_tag(:div, class: 'button', style: @portal_color ? "background-color: #{@portal_light_color} !important" : '') do + content_tag(:div, class: 'button') do concat inline_svg_tag('icons/details.svg') - concat content_tag(:div, class: 'text', style: @portal_color ? "color: #{@portal_color} !important" : '') { t('search.result_component.details') } + concat content_tag(:div, class: 'text') { t('search.result_component.details') } end end end @@ -57,9 +57,9 @@ def mappings_button def visualize_button link_to_modal(nil, "/ajax/biomixer/?ontology=#{@ontology_acronym}&conceptid=#{@uri}", data: { show_modal_title_value: @title, show_modal_size_value: 'modal-xl' }) do - content_tag(:div, class: 'button', style: @portal_color ? "background-color: #{@portal_light_color} !important" : '') do + content_tag(:div, class: 'button') do concat inline_svg_tag('icons/visualize.svg') - concat content_tag(:div, class: 'text', style: @portal_color ? "color: #{@portal_color} !important" : '') { t('search.result_component.visualize') } + concat content_tag(:div, class: 'text') { t('search.result_component.visualize') } end end end diff --git a/app/components/display/taxonomy_card_component.rb b/app/components/display/taxonomy_card_component.rb new file mode 100644 index 000000000..df896191b --- /dev/null +++ b/app/components/display/taxonomy_card_component.rb @@ -0,0 +1,10 @@ +class Display::TaxonomyCardComponent < ViewComponent::Base + def initialize(taxonomy: , ontologies_names: ) + @taxonomy = taxonomy + @ontologies_names = ontologies_names + end + + def reveal_id + @taxonomy.id + end +end diff --git a/app/components/display/taxonomy_card_component/taxonomy_card_component.html.haml b/app/components/display/taxonomy_card_component/taxonomy_card_component.html.haml new file mode 100644 index 000000000..05903f4f4 --- /dev/null +++ b/app/components/display/taxonomy_card_component/taxonomy_card_component.html.haml @@ -0,0 +1,26 @@ +.taxonomy-card{'data-controller': 'reveal-component'} + .title-bar + .title + = "#{@taxonomy.name} (#{@taxonomy.acronym})" + %a{href: "https://#{@taxonomy.acronym}.#{$UI_URL.sub("https://", "")}"} + = inline_svg_tag('icons/slices.svg', class: "taxonomy-slice-svg #{@taxonomy.is_slice ? '' : 'd-none'}") + %a.ontologies{href: "/ontologies?#{@taxonomy.id.split('/')[-2]}=#{@taxonomy.acronym}"} + = inline_svg_tag('icons/ontology.svg') + .number-of-ontologies + = "#{@taxonomy.ontologies.length} ontologies" + .description + = render TextAreaFieldComponent.new(value: @taxonomy.description) + .ontologies-cards + - @taxonomy.ontologies.each do |ontology| + = render ChipButtonComponent.new(url: "/ontologies/#{ontology.split('/').last}", text: ontology.split('/').last, tooltip: @ontologies_names[ontology], type: "clickable") + + - if @taxonomy.children + .taxonomy-children-reveal{'data-action': "click->reveal-component#toggle", 'data-id': reveal_id} + .text + = t('taxonomy.show_sub_categories') + = inline_svg_tag 'icons/arrow-down.svg' + .taxonomy-children-container + .taxonomy-children-line + .taxonomy-children.d-none{id: reveal_id} + - @taxonomy.children.each do |child| + = render Display::TaxonomyCardComponent.new(taxonomy: child, ontologies_names: @ontologies_names) \ No newline at end of file diff --git a/app/components/ontology_browse_card_component.rb b/app/components/ontology_browse_card_component.rb index a8ff90e4f..dfc2c05d0 100644 --- a/app/components/ontology_browse_card_component.rb +++ b/app/components/ontology_browse_card_component.rb @@ -17,11 +17,7 @@ def ontology end def external_ontology? - !@portal_name.blank? - end - - def external_portal_name - @portal_name + !internal_ontology?(@ontology[:id]) || (Array(@ontology[:sources]).size > 1) end def onto_link diff --git a/app/components/ontology_browse_card_component/ontology_browse_card_component.html.haml b/app/components/ontology_browse_card_component/ontology_browse_card_component.html.haml index f609a25c7..6a08ccc83 100644 --- a/app/components/ontology_browse_card_component/ontology_browse_card_component.html.haml +++ b/app/components/ontology_browse_card_component/ontology_browse_card_component.html.haml @@ -18,7 +18,7 @@ = t('components.show_more') - - unless ontology[:fairScore].nil? || ontology[:fairScore].zero? || ontology[:acronym] == 'AGROVOC' + - unless ontology[:fairScore].nil? || ontology[:fairScore].zero? || ontology[:acronym] == 'AGROVOC' .browse-fair %p.browse-fair-title = t('components.fair_score') diff --git a/app/controllers/admin/categories_controller.rb b/app/controllers/admin/categories_controller.rb index 9a4e357c9..f2c0d3ea0 100644 --- a/app/controllers/admin/categories_controller.rb +++ b/app/controllers/admin/categories_controller.rb @@ -15,7 +15,6 @@ def index def new @category = LinkedData::Client::Models::Category.new - respond_to do |format| format.html { render "new", :layout => false } end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 650ee87b5..5bdbaf07f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -183,33 +183,6 @@ def request_portals helpers.request_portals end - def check_http_file(url) - session = Net::HTTP.new(url.host, url.port) - session.use_ssl = true if url.port == 443 - session.start do |http| - response_valid = http.head(url.request_uri).code.to_i < 400 - return response_valid - end - end - - def check_ftp_file(uri) - ftp = Net::FTP.new(uri.host, uri.user, uri.password) - ftp.login - begin - file_exists = ftp.size(uri.path) > 0 - rescue - # Check using another method - path = uri.path.split("/") - filename = path.pop - path = path.join("/") - ftp.chdir(path) - files = ftp.dir - # Dumb check, just see if the filename is somewhere in the list - files.each { |file| return true if file.include?(filename) } - end - file_exists - end - def parse_response_body(response) return nil if response.nil? diff --git a/app/controllers/concerns/search_aggregator.rb b/app/controllers/concerns/search_aggregator.rb index 7faebfd42..4e2ac084b 100644 --- a/app/controllers/concerns/search_aggregator.rb +++ b/app/controllers/concerns/search_aggregator.rb @@ -29,7 +29,7 @@ def aggregate_results(query, results, is_federate) ontologies = aggregate_by_ontology(results) grouped_results = add_subordinate_ontologies(query, ontologies) - all_ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym,name', include_views: true, display_links: false, display_context: false, federate: is_federate) + all_ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym,name', include_views: true, display_links: false, display_context: false) search_results = grouped_results.map do |group| format_search_result(group, all_ontologies) @@ -77,7 +77,7 @@ def search_result_elem(class_object, ontology_acronym, title) portal_name = portal_name_from_uri(class_object.links['ui']) { uri: class_object.id.to_s, - title: title.empty? ? label : "#{label} - #{title}", + title: title.nil? || title.empty? ? "#{label} - #{ontology_acronym}" : "#{label} - #{title}", ontology_acronym: ontology_acronym, link: link, definition: class_object.definition, @@ -93,7 +93,7 @@ def is_federation_external_class(class_object) def ontology_name_acronym(ontologies, selected_acronym) ontology = ontologies.select { |x| x.acronym.eql?(selected_acronym.split('/').last) }.first - "#{ontology.name} (#{ontology.acronym})" + "#{ontology.name} (#{ontology.acronym})" if ontology end def aggregate_by_ontology(results) diff --git a/app/controllers/concerns/submission_filter.rb b/app/controllers/concerns/submission_filter.rb index e1ec2314e..bc3d38d88 100644 --- a/app/controllers/concerns/submission_filter.rb +++ b/app/controllers/concerns/submission_filter.rb @@ -59,8 +59,8 @@ def submissions_paginate_filter(params) [@page.collection, @page.totalCount, count, filter_params] end - def ontologies_filter_url(filters, page: 1, count: false) - helpers.ontologies_filter_url(filters, page: page, count: count) + def ontologies_with_filters_url(filters, page: 1, count: false) + helpers.ontologies_with_filters_url(filters, page: page, count: count) end private diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 5fec834d6..d7e4beeb3 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -47,7 +47,7 @@ def index end def set_cookies - session[:cookies_accepted] = params[:cookies] if params[:cookies] + cookies.permanent[:cookies_accepted] = params[:cookies] if params[:cookies] render 'cookies', layout: nil end diff --git a/app/controllers/taxonomy_controller.rb b/app/controllers/taxonomy_controller.rb new file mode 100644 index 000000000..06832abe8 --- /dev/null +++ b/app/controllers/taxonomy_controller.rb @@ -0,0 +1,46 @@ +class TaxonomyController < ApplicationController + + layout :determine_layout + + def index + initialize_taxonomy + @category_section_active = request.path.eql?('/categories') + end + + private + + def initialize_taxonomy + @groups = LinkedData::Client::Models::Group.all + slices = LinkedData::Client::Models::Slice.all + slices_acronyms = slices.map { |slice| slice.acronym.downcase } + @groups.each do |group| + if slices_acronyms.include?(group.acronym.downcase) + group[:is_slice] = true + end + end + @categories = LinkedData::Client::Models::Category.all(display: 'name,acronym,description,ontologies,parentCategory') + @categories = nest_categories_children(@categories) + ontologies = LinkedData::Client::Models::Ontology.all + @ontologies_names = {} + ontologies.each do |o| + @ontologies_names[o.id] = o.name + end + end + + def nest_categories_children(categories) + category_index = {} + categories.each do |category| + category_index[category[:id]] = category + end + categories.each do |category| + if category.parentCategory + parent = category_index[category.parentCategory] + parent[:children] ||= [] + parent[:children] << category + end + end + categories.reject! { |category| category.parentCategory } + categories + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f3cff847a..0f0a6f0c9 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -154,35 +154,7 @@ def link_last_part(url) def at_slice? !@subdomain_filter.nil? && !@subdomain_filter[:active].nil? && @subdomain_filter[:active] == true end - - - # TODO this helper is not used but can be usefully - def truncate_with_more(text, options = {}) - length ||= options[:length] ||= 30 - trailing_text ||= options[:trailing_text] ||= " ... " - link_more ||= options[:link_more] ||= "[more]" - link_less ||= options[:link_less] ||= "[less]" - more_text = " #{link_more}#{text} #{link_less}" - more = text.length > length ? more_text : "" - output = "#{truncate(text, :length => length, :omission => trailing_text)}" + more + "" - end - - def chips_component(id: , name: , label: , value: , checked: false , tooltip: nil, &block) - content_tag(:div, data: { controller: 'tooltip' }, title: tooltip) do - check_input(id: id, name: name, value: value, label: label, checked: checked, &block) - end - end - - def group_chip_component(id: nil, name: , object: , checked: , value: nil, title: nil, &block) - title ||= "#{object["name"]} (#{object["id"]})" - value ||= (object["value"] || object["acronym"] || object["id"]) - - chips_component(id: id || value, name: name, label: object["acronym"], - checked: checked, - value: value, tooltip: title, &block) - end - alias :category_chip_component :group_chip_component - + def add_comment_button(parent_id, parent_type) if session[:user].nil? link_to t('application.add_comment'), login_index_path(redirect: request.url), class: "secondary-button regular-button slim" @@ -571,4 +543,8 @@ def cancel_button_component(class_name: nil, id: , value:, data: nil) end end + def categories_select(id: nil, name: nil, selected: 'None') + categories_for_select = LinkedData::Client::Models::Category.all.map{|x| ["#{x.name} (#{x.acronym})", x.id]}.unshift(["None", '']) + render Input::SelectComponent.new(id: id, name: name, value: categories_for_select, selected: selected) + end end diff --git a/app/helpers/fair_score_helper.rb b/app/helpers/fair_score_helper.rb index 7abb76eb6..ef5b81e2d 100644 --- a/app/helpers/fair_score_helper.rb +++ b/app/helpers/fair_score_helper.rb @@ -16,18 +16,22 @@ def get_fairness_json(ontologies_acronyms, apikey = user_apikey) if Rails.cache.exist?("fairness-#{ontologies_acronyms.gsub(',', '-')}-#{apikey}") out = read_large_data("fairness-#{ontologies_acronyms.gsub(',', '-')}-#{apikey}") else - out = "{}" + out = '{}' begin time = Benchmark.realtime do conn = Faraday.new do |conn| conn.options.timeout = 30 end response = conn.get(get_fairness_service_url(apikey) + "&ontologies=#{ontologies_acronyms}&combined") - out = response.body.force_encoding('ISO-8859-1').encode('UTF-8') - cache_large_data("fairness-#{ontologies_acronyms.gsub(',', '-')}-#{apikey}", out) + if response.status.eql?(200) + out = response.body.force_encoding('ISO-8859-1').encode('UTF-8') + unless out.empty? || out.strip.eql?('{}') + cache_large_data("fairness-#{ontologies_acronyms.gsub(',', '-')}-#{apikey}", out) + end + end end puts "Call fairness service for: #{ontologies_acronyms} (#{time}s)" - rescue + rescue StandardError Rails.logger.warn t('fair_score.fairness_unreachable_warning') end end @@ -140,7 +144,7 @@ def print_score(score) def fairness_link(style: '', ontology: nil) custom_style = "font-size: 50px; line-height: 0.5; margin-left: 6px; #{style}".strip ontology = ontology || 'all' - render IconWithTooltipComponent.new(icon: "json.svg",link: "#{get_fairness_service_url}&ontologies=#{ontology}&combined=true", target: '_blank', title: t('fair_score.go_to_api'), size:'small', style: custom_style) + render IconWithTooltipComponent.new(icon: 'json.svg',link: "#{get_fairness_service_url}&ontologies=#{ontology}&combined=true", target: '_blank', title: t('fair_score.go_to_api'), size:'small', style: custom_style) end private diff --git a/app/helpers/federation_helper.rb b/app/helpers/federation_helper.rb index 331787213..d4470c44c 100644 --- a/app/helpers/federation_helper.rb +++ b/app/helpers/federation_helper.rb @@ -1,11 +1,28 @@ module FederationHelper def federated_portals - $FEDERATED_PORTALS || {} + $FEDERATED_PORTALS ||= LinkedData::Client.settings.federated_portals + end + + def internal_portal_config(id) + return unless internal_ontology?(id) + + { + name: portal_name, + api: rest_url, + apikey: $API_KEY, + ui: $UI_URL, + color: "var(--primary-color)", + 'light-color': 'var(--light-color)', + } end def federated_portal_config(name_key) - $FEDERATED_PORTALS[name_key.to_sym] + federated_portals[name_key.to_sym] + end + + def portal_name_from_uri(uri) + URI.parse(uri).hostname.split('.').first end def federated_portal_name(key) @@ -23,14 +40,10 @@ def federated_portal_light_color(key) config[:'light-color'] if config end - def portal_name_from_uri(uri) - URI.parse(uri).hostname.split('.').first - end - def ontology_portal_config(id) rest_url = id.split('/')[0..-3].join('/') - $FEDERATED_PORTALS.select{|_, config| config[:api].start_with?(rest_url)}.first + federated_portals.select{|_, config| config[:api].start_with?(rest_url)}.first end def ontology_portal_name(id) @@ -56,7 +69,7 @@ def ontoportal_ui_link(id) end def internal_ontology?(id) - id.start_with?(helpers.rest_url) + id.start_with?(rest_url) end def federated_ontology?(id) diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index 4e05ad775..33adc9efd 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -104,8 +104,8 @@ def ontologies_browse_skeleton(pagesize = 5) end end - def ontologies_filter_url(filters, page: 1, count: false) - url = 'ontologies_filter?' + def ontologies_with_filters_url(filters, page: 1, count: false) + url = '/ontologies_filter?' url += "page=#{page}" if page url += "count=#{page}" if count if filters @@ -780,5 +780,12 @@ def id_to_acronym(id) id.split('/').last end + def browse_taxonomy_tooltip(texonomy) + content_tag(:div, class: 'd-flex') do + content_tag(:div, "See more information about #{texonomy} in ", class: 'mr-1') + + content_tag(:a, 'here', href: "/#{texonomy}", target: '_blank') + end + end + end diff --git a/app/javascript/component_controllers/index.js b/app/javascript/component_controllers/index.js index eaf1728f0..c0d3bcd20 100644 --- a/app/javascript/component_controllers/index.js +++ b/app/javascript/component_controllers/index.js @@ -26,7 +26,6 @@ import clipboard_component_controller from '../../components/clipboard_component import range_slider_component_controller from '../../components/input/range_slider_component/range_slider_component_controller' import RDFHighlighter from '../../components/display/rdf_highlighter_component/rdf_highlighter_component_controller' - application.register("rdf-highlighter", RDFHighlighter) application.register('turbo-modal', TurboModalController) application.register('file-input', FileInputLoaderController) @@ -42,4 +41,4 @@ application.register('progress-pages', Progress_pages_component_controller) application.register('reveal-component', Reveal_component_controller) application.register('table-component', Table_component_controller) application.register('clipboard', clipboard_component_controller) -application.register('range-slider', range_slider_component_controller) +application.register('range-slider', range_slider_component_controller) \ No newline at end of file diff --git a/app/views/admin/categories/_form.html.haml b/app/views/admin/categories/_form.html.haml index a1abde741..75974b5a3 100644 --- a/app/views/admin/categories/_form.html.haml +++ b/app/views/admin/categories/_form.html.haml @@ -30,6 +30,11 @@ = t('admin.categories.form.description') %td.top = f.text_area :description, class: "form-control" + %tr + %th + = t('admin.categories.form.parent_category') + %td.top + = categories_select(id: 'category_parent_select', name: 'category[parentCategory]', selected: @category&.parentCategory) - unless new_record %tr %th diff --git a/app/views/home/cookies.html.haml b/app/views/home/cookies.html.haml index 0a625a2be..d02f687a9 100644 --- a/app/views/home/cookies.html.haml +++ b/app/views/home/cookies.html.haml @@ -1,5 +1,5 @@ = turbo_frame_tag :cookies_modal do - - if session[:cookies_accepted].nil? # don't re-render if a true/false selected + - if cookies[:cookies_accepted].nil? # don't re-render if a true/false selected %section.cookies-modal %h4 = t('cookies_modal.title') diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 07d28d73d..1340bdbb2 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -57,7 +57,7 @@ <%= render partial: 'layouts/topnav' %> -<%= turbo_frame_tag :cookies_modal, src: cookies_path if session[:cookies_accepted].nil? && !(Rails.env.development? || Rails.env.test?) %> +<%= turbo_frame_tag :cookies_modal, src: cookies_path if cookies[:cookies_accepted].nil? && !(Rails.env.development? || Rails.env.test?) %>
<%= render partial: 'layouts/notices' %> \ No newline at end of file diff --git a/app/views/layouts/_notices.html.haml b/app/views/layouts/_notices.html.haml index 8969a67d0..3700404d4 100644 --- a/app/views/layouts/_notices.html.haml +++ b/app/views/layouts/_notices.html.haml @@ -1,4 +1,4 @@ -- if Rails.env.appliance? +- if session[:user]&.admin? && Rails.env.appliance? = license_notification(current_license()) - flash.each do |key, message| diff --git a/app/views/layouts/_topnav.html.haml b/app/views/layouts/_topnav.html.haml index 4d0a01374..c73008dfe 100644 --- a/app/views/layouts/_topnav.html.haml +++ b/app/views/layouts/_topnav.html.haml @@ -60,4 +60,6 @@ = link_to(t('layout.header.cite_us'), $FOOTER_LINKS[:sections][:about][:cite_us], target: "_blank") - s.item do = link_to(t('layout.header.release_notes'), $FOOTER_LINKS[:sections][:products][:release_notes], target: "_blank") + - s.item do + = link_to(t('taxonomy.groups_and_categories'), '/groups') diff --git a/app/views/layouts/appliance.html.haml b/app/views/layouts/appliance.html.haml index d40fbdce6..3e306068c 100644 --- a/app/views/layouts/appliance.html.haml +++ b/app/views/layouts/appliance.html.haml @@ -26,7 +26,7 @@ %body{:class => "#{controller_name} #{action_name}"} = render partial: "layouts/topnav" - = turbo_frame_tag :cookies_modal, src: cookies_path if session[:cookies_accepted].nil? + = turbo_frame_tag :cookies_modal, src: cookies_path if cookies[:cookies_accepted].nil? %div.flex-grow-1 = render partial: "layouts/notices" diff --git a/app/views/ontologies/browser/_ontologies.html.haml b/app/views/ontologies/browser/_ontologies.html.haml index 88ed7011c..3ca8685bc 100644 --- a/app/views/ontologies/browser/_ontologies.html.haml +++ b/app/views/ontologies/browser/_ontologies.html.haml @@ -1,6 +1,6 @@ = render InfiniteScrollComponent.new(id: 'ontologies_list', collection: @ontologies, - next_url: ontologies_filter_url(@request_params, page: @page.nextPage), + next_url: ontologies_with_filters_url(@request_params, page: @page.nextPage), current_page: @page.page, next_page: @page.nextPage) do |c| - if @page.page.eql?(1) diff --git a/app/views/ontologies/browser/browse.html.haml b/app/views/ontologies/browser/browse.html.haml index e5537d86c..e0695e73d 100644 --- a/app/views/ontologies/browser/browse.html.haml +++ b/app/views/ontologies/browser/browse.html.haml @@ -50,26 +50,29 @@ - @filters.each do |key, values| - if session[:user]&.admin? || key != :missingStatus - %div{data:{controller: "show-filter-count browse-filters", action: "change->show-filter-count#updateCount change->browse-filters#dispatchFilterEvent"}, id: "#{key}_filter_container"} - = render DropdownContainerComponent.new(id: "browse-#{key}-filter", is_open: values[2].positive?) do |d| - - d.title do - = render Display::HeaderComponent.new do - %span - = browse_filter_section_label(key) - %span.badge.badge-primary{"data-show-filter-count-target":"countSpan", style: "#{values[2] && values[2].positive? ? '' : 'display: none;'}"} - = values[2] - - .browse-filter-checks-container.px-1 + .browse-filter{data:{controller: "show-filter-count browse-filters", action: "change->show-filter-count#updateCount change->browse-filters#dispatchFilterEvent"}, id: "#{key}_filter_container", style: "#{"border-color: var(--admin-color);" if key == :missingStatus}"} + .browse-filter-title-bar{"data-target" => "#browse-#{key}-filter", "data-toggle" => "collapse"} + %p + = browse_filter_section_label(key) + %span.badge.badge-primary{"data-show-filter-count-target":"countSpan", style: "#{values[2] && values[2].positive? ? '' : 'display: none;'}"} + = values[2] + .d-flex.align-items-center + - if key.eql?(:categories) || key.eql?(:groups) + .mr-2 + = render Display::InfoTooltipComponent.new(text: browse_taxonomy_tooltip(key.to_s)) + = inline_svg_tag 'arrow-down.svg' + .collapse{id: "browse-#{key}-filter", class: "#{values[2].positive? ? 'show': ''}"} + .browse-filter-checks-container - values.first.each do |object| - title = (key.eql?(:categories) || key.eql?(:groups)) ? nil : '' - = group_chip_component(name: key, object: object, checked: [link_last_part(object["id"]),link_last_part(object["value"])].include?(values[1]) , title: title) do |c| + = group_chip_component(name: key, object: object, checked: values[1].any?(link_last_part(object["id"])) || values[1].any?(link_last_part(object["value"])) , title: title) do |c| - c.count do %span.badge.badge-light.ml-1 = turbo_frame_tag "count_#{key}_#{link_last_part(object["id"])}", busy: true %span.show-if-loading = render LoaderComponent.new(small:true) %div{data:{controller: "show-filter-count browse-filters", action: "change->show-filter-count#updateCount change->browse-filters#dispatchFilterEvent"}, id: "portals_filter_container"} - = render DropdownContainerComponent.new(id: "browse-portal-filter", is_open: !request_portals.empty?, title: "Results from external portals") do + = render DropdownContainerComponent.new(id: "browse-portal-filter", is_open: !request_portals.empty?, title: t('federation.results_from_external_portals')) do .browse-filter-checks-container.px-1 - federated_portals.each do |key, config| = group_chip_component(name: "portals", object: { "acronym" => config[:name], "value" => key }, checked: request_portals.include?(key.to_s), title: '') diff --git a/app/views/ontologies/sections/_metadata.html.haml b/app/views/ontologies/sections/_metadata.html.haml index 044772ea6..be2083e5c 100755 --- a/app/views/ontologies/sections/_metadata.html.haml +++ b/app/views/ontologies/sections/_metadata.html.haml @@ -36,7 +36,7 @@ document.querySelector("[data-target='#projects_section']")?.click() } = properties_dropdown('methodology',t("ontologies.sections.methodology_and_provenance"), t("ontologies.sections.info_tooltip_properties_dropdown"), @methodology_properties) - = properties_dropdown('community',t("ontologies.sections.community"), t("ontologies.sections.info_tooltip_properties_dropdown"), nil ) do |c| + = properties_dropdown('community',t("ontologies.sections.community"), t("ontologies.sections.info_tooltip_community_dropdown", site: portal_name), nil ) do |c| - properties_list_component(c, @community_properties, truncate: false) - unless Array(@ontology.group).empty? - c.row do @@ -44,7 +44,7 @@ = horizontal_list_container(@ontology.group) do |v| = render ChipButtonComponent.new(text: show_group_name(v), type: "static", tooltip: show_group_name(v)) - = properties_dropdown('content',t("ontologies.sections.content"), t("ontologies.sections.info_tooltip_properties_dropdown"), nil) do |c| + = properties_dropdown('content',t("ontologies.sections.content"), t("ontologies.sections.info_tooltip_properties_dropdown", site: portal_name), nil) do |c| - properties_list_component(c, @content_properties.reject{|k, v| %w[keyClasses metadataVoc].include?(k.to_s)}) - c.row do = render FieldContainerComponent.new(label: attr_label('metadataVoc', attr_metadata: attr_metadata("metadataVoc"), show_tooltip: false)) do diff --git a/app/views/properties/_show.html.haml b/app/views/properties/_show.html.haml index d448430fe..f74964f85 100644 --- a/app/views/properties/_show.html.haml +++ b/app/views/properties/_show.html.haml @@ -14,6 +14,5 @@ - t.add_row({th: t('properties.type')}, {td: @property.type }) - t.add_row({th: t('properties.preferred_name')}, {td: display_in_multiple_languages(@property.label)}) unless @property.label.blank? - t.add_row({th: t('properties.definitions')}, {td: display_in_multiple_languages(@property.definition)}) unless @property.definition.blank? - - t.add_row({th: t('properties.parent')}, {td: display_in_multiple_languages(@property.parents)}) unless @property.parents.blank? - - t.add_row({th: t('properties.domain')}, {td: get_link_for_cls_ajax(@property.domain, @acronym)}) unless @property.domain.blank? - - t.add_row({th: t('properties.range')}, {td: get_link_for_cls_ajax(@property.range, @acronym)}) unless @property.range.blank? \ No newline at end of file + - t.add_row({th: t('properties.domain')}, {td: get_link_for_cls_ajax(@property.domain, @acronym, '_top')}) unless @property.domain.blank? + - t.add_row({th: t('properties.range')}, {td: get_link_for_cls_ajax(@property.range, @acronym, '_top')}) unless @property.range.blank? diff --git a/app/views/search/index.html.haml b/app/views/search/index.html.haml index e11944d80..b8895ced4 100644 --- a/app/views/search/index.html.haml +++ b/app/views/search/index.html.haml @@ -17,7 +17,7 @@ = ontologies_selector(id:'search_page_ontologies' ,name: 'ontologies[]', selected: params[:ontologies]&.split(',')) .filter-container .title - Results from external portals + = t('federation.results_from_external_portals') .field.d-flex - federated_portals.each do |key, config| = group_chip_component(name: "portals[]", object: { "acronym" => config[:name], "value" => key }, checked: request_portals.include?(key.to_s), title: '') diff --git a/app/views/taxonomy/_taxonomies.html.haml b/app/views/taxonomy/_taxonomies.html.haml new file mode 100644 index 000000000..0dd56f687 --- /dev/null +++ b/app/views/taxonomy/_taxonomies.html.haml @@ -0,0 +1,10 @@ +.taxonomy-section + - pairs, impairs = taxonomies.each_with_index.partition { |_, index| index.even? } + - taxonomies_first_row = pairs.map(&:first) + - taxonomies_second_row = impairs.map(&:first) + .first-row + - taxonomies_first_row.each do |taxonomy| + = render Display::TaxonomyCardComponent.new(taxonomy: taxonomy, ontologies_names: @ontologies_names) + .second-row + - taxonomies_second_row.each do |taxonomy| + = render Display::TaxonomyCardComponent.new(taxonomy: taxonomy, ontologies_names: @ontologies_names) \ No newline at end of file diff --git a/app/views/taxonomy/index.html.haml b/app/views/taxonomy/index.html.haml new file mode 100644 index 000000000..967f63d33 --- /dev/null +++ b/app/views/taxonomy/index.html.haml @@ -0,0 +1,24 @@ +.taxonomy-page-container + .taxonomy-page-subcontainer + .taxonomy-page-title + .text + = t('taxonomy.groups_and_categories') + .line + .taxonomy-page-decription + = t('taxonomy.description') + + = render TabsContainerComponent.new do |c| + - c.item(title: 'Groups', selected: !@category_section_active) + - c.item_content do + = render partial: '/taxonomy/taxonomies', locals: { taxonomies: @groups } + + - c.item(title: 'Categories', selected: @category_section_active) + - c.item_content do + = render partial: '/taxonomy/taxonomies', locals: { taxonomies: @categories } +:javascript + document.getElementById('categories_tab').addEventListener('click', function(event) { + window.history.pushState({ path: '/categories' }, '', '/categories'); + }) + document.getElementById('groups_tab').addEventListener('click', function(event) { + window.history.pushState({ path: '/groups' }, '', '/groups'); + }) \ No newline at end of file diff --git a/config/bioportal_config_env.rb.sample b/config/bioportal_config_env.rb.sample index 2d4517fbc..352805b19 100644 --- a/config/bioportal_config_env.rb.sample +++ b/config/bioportal_config_env.rb.sample @@ -40,52 +40,6 @@ $NCBO_API_KEY = ENV['NCBO_API_KEY'] $FAIRNESS_DISABLED = ENV['FAIRNESS_DISABLED'] $FAIRNESS_URL = ENV['FAIRNESS_URL'] - -# Federation configuration -$FEDERATED_PORTALS = { - bioportal: { - name: "BioPortal", - api: 'https://data.bioontology.org/', - ui: 'https://bioportal.bioontology.org/', - apikey: '8b5b7825-538d-40e0-9e9e-5ab9274a9aeb', - color: '#234979', - 'light-color': '#E9F2FA', - }, - agroportal: { - name: "AgroPortal", - api: 'https://data.agroportal.lirmm.fr', - ui: 'https://agroportal.lirmm.fr', - apikey: '1de0a270-29c5-4dda-b043-7c3580628cd5', - color: '#349696', - 'light-color': '#F1F6FA', - }, - ecoportal: { - name: "EcoPortal", - api: 'https://data.ecoportal.lifewatch.eu/', - ui: 'https://ecoportal.lifewatch.eu', - apikey: "43a437ba-a437-4bf0-affd-ab520e584719", - color: '#2076C9', - 'light-color': '#E9F2FA', - }, - # earthportal: { - # name: 'EarthPortal', - # api: 'https://earthportal.eu:8443/', - # ui: 'https://earthportal.eu', - # apikey: "c9147279-954f-41bd-b068-da9b0c441288", - # color: '#404696', - # 'light-color': '#404696' - # }, - biodivportal: { - name: "BioDivPortal", - api: 'https://data.biodivportal.gfbio.org/', - ui: 'https://biodivportal.gfbio.org/', - apikey: "47a57aa3-7b54-4f34-b695-dbb5f5b7363e", - color: '#349696', - 'light-color': '#EBF5F5', - } -} - - # Used to define other bioportal that can be mapped to # Example to map to ncbo bioportal : {"ncbo" => {"api" => "http://data.bioontology.org", "ui" => "http://bioportal.bioontology.org", "apikey" => ""} # Then create the mapping using the following class in JSON : "http://purl.bioontology.org/ontology/MESH/C585345": "ncbo:MESH" diff --git a/config/locales/en.yml b/config/locales/en.yml index aaa6d077f..fb54a1cef 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -203,6 +203,7 @@ en: description: Description created: Created ontologies: Ontologies + parent_category: Parent category edit: edit_category: Edit category save: Save @@ -1275,6 +1276,7 @@ en: community: Community content: Content info_tooltip_properties_dropdown: Metadata properties primarily encompass the design, methods, and actions to create the ontology. This includes elements such as the tools and software employed by the creator of the ontology during its configuration. + info_tooltip_community_dropdown: Metadata properties related to community information such as the code repository and issue tracker, the mailing list or the target audience. This includes also the Groups in %{site}. See more information about Groups and Categories. visits: Visits views: Views of %{acronym} create_new_view: Create new view @@ -1490,4 +1492,11 @@ en: paragraph1: "%{portal} uses cookies to help you navigate efficiently and for audience measurement. You will find detailed information about all cookies in the \"Privacy policy\" link" paragraph2: "The cookies are functional and non-optional. By staying on %{portal}, you acknowledge the information was delivered to you." accept_button: "Accept" - privacy_link: "Privacy policy" \ No newline at end of file + privacy_link: "Privacy policy" + taxonomy: + groups_and_categories: Groups and Categories + description: In AgroPortal, ontologies are organized in groups and tagged with categories. Typically, groups associate ontologies from the same project or organization for better identification of the provenance. Whereas categories are about subjects/topics and enable to classify ontologies. As of 2016, AgroPortal's categories were established in cooperation with FAO AIMS. In 2024, we moved to UNESCO nomenclature for fields of science and technology. Groups and categories, along with other metadata, can be used on the “Browse” page of AgroPortal to filter out the list of ontologies. + show_sub_categories: Show sub categories + + federation: + results_from_external_portals: Results from external portals \ No newline at end of file diff --git a/config/locales/fr.yml b/config/locales/fr.yml index a529cb847..34c68bafa 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -209,6 +209,7 @@ fr: description: Description created: Créé ontologies: Ontologies + parent_category: Catégorie parent edit: edit_category: Modifier la catégorie save: Sauvegarder @@ -1301,6 +1302,7 @@ fr: community: Communauté content: Contenu info_tooltip_properties_dropdown: Propriétés de métadonnées englobant principalement la conception, les méthodes et les actions pour créer l'ontologie. Cela inclut des éléments tels que les outils et logiciels utilisés par le créateur de l'ontologie lors de sa configuration. + info_tooltip_community_dropdown: Propriétés des métadonnées liées aux informations communautaires telles que le dépôt de code et le suivi des problèmes, la liste de diffusion ou le public cible. Cela inclut également les Groupes dans %{site}. Voir plus d'informations sur les Groupes et les Catégories. visits: Visites views: Vues de %{acronym} create_new_view: Créer une nouvelle vue @@ -1529,4 +1531,10 @@ fr: paragraph1: "%{portal} utilise des cookies pour vous aider à naviguer efficacement et pour mesurer l'audience. Vous trouverez des informations détaillées sur tous les cookies dans le lien \"Vie privé\"" paragraph2: "Les cookies sont fonctionnels et non facultatifs. En restant sur %{portal}, vous reconnaissez que les informations vous ont été transmises." accept_button: "Accepter" - privacy_link: "Vie privé" \ No newline at end of file + privacy_link: "Vie privé" + taxonomy: + groups_and_categories: Groupes et Catégories + description: Dans AgroPortal, les ontologies sont organisées en groupes et étiquetées avec des catégories. Typiquement, les groupes associent des ontologies provenant du même projet ou de la même organisation pour une meilleure identification de la provenance. Tandis que les catégories concernent des sujets/thématiques et permettent de classifier les ontologies. En 2016, les catégories d'AgroPortal ont été établies en coopération avec FAO AIMS. En 2024, nous sommes passés à la nomenclature de l'UNESCO pour les domaines des sciences et des technologies. Les groupes et les catégories, ainsi que d'autres métadonnées, peuvent être utilisés sur la page “Parcourir” d'AgroPortal pour filtrer la liste des ontologies. + show_sub_categories: Afficher les sous-catégories + federation: + results_from_external_portals: Résultats provenant de portails externes diff --git a/config/routes.rb b/config/routes.rb index bc191bc10..b28c26ec0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -208,6 +208,9 @@ get '/login_as/:login_as' => 'login#login_as', constraints: { login_as: /[\d\w\.\-\%\+ ]+/ } post '/login/send_pass', to: 'login#send_pass' + get '/groups' => 'taxonomy#index' + get '/categories' => 'taxonomy#index' + # Search get 'search', to: 'search#index' get 'ajax/search/ontologies/content', to: 'search#json_ontology_content_search'