+
+ <%=render partial: 'layouts/notices'%> +
Ontologies loading

please wait...

-
- <%=render partial: 'layouts/notices'%> -
From 31f745a89b4a3a3829e277164582db021cf2e6f6 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Mon, 20 Nov 2023 15:30:52 -0800 Subject: [PATCH 52/59] Update Gemfile.lock --- Gemfile.lock | 63 +++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 23d09607af..b6c78f6e00 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -79,7 +79,7 @@ GEM zeitwerk (~> 2.3) addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) - airbrussh (1.4.2) + airbrussh (1.5.0) sshkit (>= 1.6.1, != 1.7.0) ast (2.4.2) autoprefixer-rails (10.4.15.0) @@ -92,7 +92,7 @@ GEM sassc-rails (>= 2.0.0) brakeman (6.0.1) builder (3.2.4) - capistrano (3.17.3) + capistrano (3.18.0) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) @@ -127,17 +127,16 @@ GEM crass (1.0.6) cube-ruby (0.0.3) daemons (1.4.1) - dalli (3.2.5) - date (3.3.3) + dalli (3.2.6) + date (3.3.4) diff-lcs (1.5.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) + domain_name (0.6.20231109) ed25519 (1.3.0) erubi (1.12.0) erubis (2.7.0) eventmachine (1.2.7) - excon (0.103.0) - execjs (2.9.0) + excon (0.104.0) + execjs (2.9.1) faraday (2.7.11) base64 faraday-net_http (>= 2.0, < 3.1) @@ -148,7 +147,7 @@ GEM faraday-multipart (1.0.4) multipart-post (~> 2) faraday-net_http (3.0.2) - ffi (1.15.5) + ffi (1.16.3) flamegraph (0.9.5) globalid (1.2.1) activesupport (>= 6.1) @@ -181,7 +180,7 @@ GEM listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.21.3) + loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) lz4-ruby (0.3.3) @@ -195,32 +194,29 @@ GEM method_source (1.0.0) mime-types (3.5.1) mime-types-data (~> 3.2015) - mime-types-data (3.2023.0808) + mime-types-data (3.2023.1003) mini_mime (1.1.5) minitest (5.20.0) multi_json (1.15.0) multipart-post (2.3.0) mysql2 (0.5.5) - net-imap (0.3.7) + net-imap (0.4.5) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout net-scp (4.0.0) net-ssh (>= 2.6.5, < 8.0.0) - net-smtp (0.3.3) + net-smtp (0.4.0) net-protocol net-ssh (7.2.0) netrc (0.11.0) - newrelic_rpm (9.5.0) - nio4r (2.5.9) - nokogiri (1.15.4-arm64-darwin) - racc (~> 1.4) - nokogiri (1.15.4-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.15.4-x86_64-linux) + newrelic_rpm (9.6.0) + base64 + nio4r (2.6.0) + nokogiri (1.15.5-x86_64-darwin) racc (~> 1.4) oj (3.16.1) open_uri_redirections (0.2.1) @@ -233,8 +229,8 @@ GEM coderay (~> 1.1) method_source (~> 1.0) psych (3.3.4) - public_suffix (5.0.3) - racc (1.7.1) + public_suffix (5.0.4) + racc (1.7.3) rack (2.2.8) rack-mini-profiler (3.1.1) rack (>= 1.2.0) @@ -273,14 +269,14 @@ GEM rake (>= 12.2) thor (~> 1.0) rainbow (3.1.1) - rake (13.0.6) + rake (13.1.0) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) rdoc (6.3.3) recaptcha (5.9.0) json - regexp_parser (2.8.1) + regexp_parser (2.8.2) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) http-cookie (>= 1.0.2, < 2.0) @@ -315,7 +311,7 @@ GEM rubocop-ast (>= 1.28.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.29.0) + rubocop-ast (1.30.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) @@ -350,33 +346,30 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sshkit (1.21.5) + sshkit (1.21.6) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) stackprof (0.2.25) - temple (0.10.2) + temple (0.10.3) terser (1.1.19) execjs (>= 0.3.0, < 3) thin (1.8.2) daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) - thor (1.2.2) + thor (1.3.0) tilt (2.3.0) - timeout (0.4.0) + timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (2.4.2) + unicode-display_width (2.5.0) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) will_paginate (3.3.1) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.11) + zeitwerk (2.6.12) PLATFORMS arm64-darwin-22 From d7b0ef776800ffdcfe8fbdd39a0898b0c7729f2f Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 29 Nov 2023 12:13:51 -0800 Subject: [PATCH 53/59] Adjust font size for autocomplete results list Resolves #296 --- .../JqueryPlugins/autocomplete/jquery.autocomplete.css | 3 ++- vendor/assets/stylesheets/jquery.autocomplete.css | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/javascripts/JqueryPlugins/autocomplete/jquery.autocomplete.css b/public/javascripts/JqueryPlugins/autocomplete/jquery.autocomplete.css index c200654ab8..00089595fe 100644 --- a/public/javascripts/JqueryPlugins/autocomplete/jquery.autocomplete.css +++ b/public/javascripts/JqueryPlugins/autocomplete/jquery.autocomplete.css @@ -28,7 +28,8 @@ display: block; width: 100%; font: menu; - font-size: 12px; + font-size: 1rem; + font-weight: 400; overflow: hidden; text-align:left; } diff --git a/vendor/assets/stylesheets/jquery.autocomplete.css b/vendor/assets/stylesheets/jquery.autocomplete.css index c200654ab8..00089595fe 100644 --- a/vendor/assets/stylesheets/jquery.autocomplete.css +++ b/vendor/assets/stylesheets/jquery.autocomplete.css @@ -28,7 +28,8 @@ display: block; width: 100%; font: menu; - font-size: 12px; + font-size: 1rem; + font-weight: 400; overflow: hidden; text-align:left; } From 783f3359accd3f57196baf4fd29325c769952dd6 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Mon, 11 Dec 2023 16:49:35 -0800 Subject: [PATCH 54/59] Fix login requirement for notes email subscriptions The sanitize method was stripping the href attribute. See #298. --- app/helpers/notes_helper.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index a1b5a68d54..7313a4f039 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -132,10 +132,11 @@ def get_note_type_text(note_type) def subscribe_button(ontology_id) user = session[:user] + if user.nil? - # subscribe button must redirect to login - return sanitize("") + return link_to('Subscribe to notes emails', login_index_path, class: 'link_button') end + # Init subscribe button parameters. sub_text = "Subscribe" params = "data-bp_ontology_id='#{ontology_id}' data-bp_is_subbed='false' data-bp_user_id='#{user.id}'" From 1fb2d56bab603e452188e9d302874d67fbd74d78 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Tue, 12 Dec 2023 17:13:04 -0800 Subject: [PATCH 55/59] Encode user ID portion of subscriptions params Resolves #300 --- app/assets/javascripts/bp_notes.js | 3 ++- app/views/users/show.html.haml | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/bp_notes.js b/app/assets/javascripts/bp_notes.js index a4776468c3..7862380f37 100644 --- a/app/assets/javascripts/bp_notes.js +++ b/app/assets/javascripts/bp_notes.js @@ -341,13 +341,14 @@ function subscribeToNotes(button) { var ontologyId = jQuery(button).attr("data-bp_ontology_id"); var isSubbed = jQuery(button).attr("data-bp_is_subbed"); var userId = jQuery(button).attr("data-bp_user_id"); + let encodedUserId = encodeURIComponent(userId); jQuery(".notes_sub_error").html(""); jQuery(".notes_subscribe_spinner").show(); jQuery.ajax({ type: "POST", - url: "/subscriptions?user_id="+userId+"&ontology_id="+ontologyId+"&subbed="+isSubbed, + url: `/subscriptions?user_id=${encodedUserId}&ontology_id=${ontologyId}&subbed=${isSubbed}`, dataType: "json", success: function(data) { jQuery(".notes_subscribe_spinner").hide(); diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 75bf767a6d..c4d7866b80 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -76,11 +76,13 @@ var ontologyId = jQuery(button).attr("data-bp_ontology_id"); var isSubbed = jQuery(button).attr("data-bp_is_subbed"); var userId = jQuery(button).attr("data-bp_user_id"); + let encodedUserId = encodeURIComponent(userId); + jQuery(".subscribe_error").html(""); jQuery(".subscribe_spinner").show(); jQuery.ajax({ type: "POST", - url: "/subscriptions?user_id="+userId+"&ontology_id="+ontologyId+"&subbed="+isSubbed, + url: `/subscriptions?user_id=${encodedUserId}&ontology_id=${ontologyId}&subbed=${isSubbed}`, dataType: "json", success: function(data) { jQuery(".subscribe_spinner").hide(); From 4856bf9dc903a4f212067dc622c47b3bf78336e0 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 13 Dec 2023 14:07:23 -0800 Subject: [PATCH 56/59] Fix Uncaught TypeError on notes emails subscribe See #298 --- app/assets/javascripts/bp_notes.js | 15 +++++++++++---- app/helpers/notes_helper.rb | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/bp_notes.js b/app/assets/javascripts/bp_notes.js index 7862380f37..29c0a12485 100644 --- a/app/assets/javascripts/bp_notes.js +++ b/app/assets/javascripts/bp_notes.js @@ -357,10 +357,17 @@ function subscribeToNotes(button) { var subbedVal = (isSubbed == "true") ? "false" : "true"; jQuery("a.subscribe_to_notes").attr("data-bp_is_subbed", subbedVal); - // Change button text - var txt = jQuery("a.subscribe_to_notes span.ui-button-text").html(); - var newButtonText = txt.match("Unsubscribe") ? txt.replace("Unsubscribe", "Subscribe") : txt.replace("Subscribe", "Unsubscribe"); - jQuery("a.subscribe_to_notes span.ui-button-text").html(newButtonText); + /* + * Update link text. + * Note that there are two links that allow users to subscribe/unsubscribe from notes emails. Given any ontology, + * one link is located on the top-level Notes tab, and the other link is located on the Notes sub-tab on the + * right-hand side of the Classes tab. Both links are handled by the subscriptions controller, and the text of + * both should be updated on success. + */ + const matches = document.querySelectorAll('a.subscribe_to_notes'); + matches.forEach(match => { + match.textContent = (subbedVal === 'true') ? 'Unsubscribe from notes emails' : 'Subscribe to notes emails'; + }); }, error: function(data) { jQuery(".notes_subscribe_spinner").hide(); diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 7313a4f039..f1f89cedba 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -148,14 +148,14 @@ def subscribe_button(ontology_id) ont = LinkedData::Client::Models::Ontology.find_by_acronym(ontology_id).first end subscribed = subscribed_to_ontology?(ont.acronym, user) # application_helper - sub_text = subscribed ? "Unsubscribe" : "Subscribe" + sub_text = subscribed ? "Unsubscribe from" : "Subscribe to" params = "data-bp_ontology_id='#{ont.acronym}' data-bp_is_subbed='#{subscribed}' data-bp_user_id='#{user.id}'" rescue # pass, fallback init done above begin block to scope parameters beyond the begin/rescue block end spinner = '' error = "" - return " #{spinner} #{error}".html_safe + return " #{spinner} #{error}".html_safe end def delete_button From 32ef25f69b74b7bba4704a274f550b7426d6eccd Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 13 Dec 2023 15:14:52 -0800 Subject: [PATCH 57/59] Fix some RuboCop warnings --- app/controllers/subscriptions_controller.rb | 44 ++++++++------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 945043258e..52c0e48774 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -1,48 +1,38 @@ -class SubscriptionsController < ApplicationController +# frozen_string_literal: true +class SubscriptionsController < ApplicationController def create - # Try to get the user linked data instance - user_id = params[:user_id] - u = LinkedData::Client::Models::User.find(user_id) - raise Exception if u.nil? - - # Try to get the ontology linked data instance - ontology_id = params[:ontology_id] - if ontology_id.start_with? 'http' - ont = LinkedData::Client::Models::Ontology.find(ontology_id) - else - ont = LinkedData::Client::Models::Ontology.find_by_acronym(ontology_id).first - end - raise Exception if ont.nil? + u = LinkedData::Client::Models::User.find(params[:user_id]) + ont = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology_id]).first # Is this request to add or remove a subscription? - subscribed = params[:subbed] # string (not boolean) - if subscribed.eql?("true") + subscribed = params[:subbed] + if subscribed.eql?('true') # Already subscribed, so this request must be a delete # Note that this routine removes ALL subscriptions for the ontology, regardless of type. # Previous way to delete subscription: error when u.update if more than 1 subscription in the subscription array: - #u.subscription.delete_if {|sub| sub[:ontology].split('/').last.eql?(ont.acronym) } + # u.subscription.delete_if {|sub| sub[:ontology].split('/').last.eql?(ont.acronym) } # So here we re-generate a new subscription Array (instead of directly updating it, which causes error) all_subs = [] u.subscription.each do |subs| # Add all subscription to the array, but not the one to be deleted if !subs.ontology.split('/').last.eql?(ont.acronym) - all_subs.push({ontology: subs.ontology, notification_type: subs.notification_type}) + all_subs.push({ ontology: subs.ontology, notification_type: subs.notification_type }) end end u.subscription = all_subs else # Not subscribed yet, so this request must be for adding subscription # Old way: - #subscription = {ontology: ont.acronym, notification_type: "NOTES"} #NOTIFICATION_TYPES[:notes]} - #u.subscription.push(subscription) + # subscription = {ontology: ont.acronym, notification_type: "NOTES"} #NOTIFICATION_TYPES[:notes]} + # u.subscription.push(subscription) # This way was not working, updating subscription is failing when more than 1 subscription in the array # And we were updating with different types of object in the subscription array : OpenStruct and hash # So we are generating an array with only hash - all_subs = [{ontology: ont.acronym, notification_type: "NOTES"}] # the new subscription + all_subs = [{ ontology: ont.acronym, notification_type: 'NOTES' }] # the new subscription u.subscription.each do |subs| # add all existing subscriptions - all_subs.push({ontology: subs.ontology, notification_type: subs.notification_type}) + all_subs.push({ ontology: subs.ontology, notification_type: subs.notification_type }) end u.subscription = all_subs end @@ -53,27 +43,25 @@ def create if response_success?(error_response) updated_sub = true session[:user].subscription = u.subscription - #session[:user] = u + # session[:user] = u # NOTES: # - Cannot update session[:user] as above. The session user object is special because it only # gets set when someone logs in and the user object returned when authenticating is the # only one that will contain the api key for security reasons. So we actually need to use # the update_from_params method, can’t just set the object to the user linked data instance. # - #session[:user].update_from_params(params[:user]) + # session[:user].update_from_params(params[:user]) # update_from_params first gets all attributes from the REST service for the object being updated, # then sets the values provided in the params hash where the param keys match setter names on the # object (in this case, for example, :subscription would set the @subscription value on the instance). # That’s all it does, no saving or anything. else updated_sub = false - #errors = response_errors(error_response) end - rescue + rescue StandardError updated_sub = false end - render :json => { :updated_sub => updated_sub, :user_subscriptions => u.subscription } + render json: { updated_sub: updated_sub, user_subscriptions: u.subscription } end - end From a576e39133235996248ac642e2b5a3f72541d979 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Thu, 14 Dec 2023 16:27:33 -0800 Subject: [PATCH 58/59] Refactor subscribed_to_ontology? method Account for the case where the user parameter doesn't have the ontology attribute loaded on the list of associated subscription objects --- app/helpers/application_helper.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1e93c2aac0..3948fe3a2b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -276,15 +276,16 @@ def subscribe_ontology_button(ontology_id, user = nil) end def subscribed_to_ontology?(ontology_acronym, user) - user.bring(:subscription) if user.subscription.nil? - # user.subscription is an array of subscriptions like {ontology: ontology_id, notification_type: "NOTES"} - return false if user.subscription.nil? or user.subscription.empty? - user.subscription.each do |sub| - #sub = {ontology: ontology_acronym, notification_type: "NOTES"} - sub_ont_acronym = sub[:ontology].split('/').last # make sure we get the acronym, even if it's a full URI - return true if sub_ont_acronym == ontology_acronym - end - return false + return false if user.subscription.blank? + + # In some cases this method is called with user objects that don't have the :ontology attribute loaded on + # the associated subscription objects. Calling find is the only way (?) to ensure that we get a user where the + # :ontology attribute is loaded for all subscriptions. + ontology_attributes_missing = user.subscription.any? { |sub| sub[:ontology].nil? } + user = LinkedData::Client::Models::User.find(user.id) if ontology_attributes_missing + + sub = user.subscription.select { |sub| sub[:ontology].split('/').last.eql? ontology_acronym } + sub.length.positive? ? 'true' : 'false' end def ontolobridge_instructions_template(ontology) From 58bd5df66b917347587e773e785eeb39a4b538e7 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Tue, 16 Jan 2024 10:22:58 -0800 Subject: [PATCH 59/59] Sync: bring OntoPortal up-to-date with BioPortal releases 6.13.2 and onward (#17) * Bump bootstrap from 4.1.0 to 5.2.3 * Refactor home page cards * Refactor top navigation bar * Fix close buttons on dismissible alerts * Refactor sticky footer code * Repair Admin tabbed interface * Refactor form to fix spacing issues * Remove unused styles * Remove unnecessary nested fluid container * Refactor form to fix spacing issues * Fix alignment issue with annotator plus spinner * Fix broken tabbed navigation for ontology tabs * Remove text decoration in the ontology info bar * Clean up display of ontology information buttons * Restore bold font in ontology metadata cards * Restore bold font for submissions header * Remove anchor text decoration for metadata cards * Restore border at the top of the submissions table * Remove bottom border from show more subs cell * Restore top borders to details and metrics tables * Fix add proposal dropdown button * Fix class permalink dialog * Fix create new mapping dialog * Fix add and remove synonym dialogs * Adjust buttons on create mapping dialog * Refactor create new mapping form * Add back platforms overwritten during merge * Refactor horizontal form to fix spacing issues * Restore padding in class details pane * Refactor class search form to fix spacing issues * Restore appearance of search links * Restore left alignment in ontology column * add download link for OntoPortal Virtual Appliance v3.2.1 * Restore link appearance in ontology column * Refactor form to fix spacing and alignment issues * Fix footer layout and presentation issues * Remove text-decoration for ontology visits Also prevent wrapping of column header text * Fix node obsoletion form * Fix node rename form * jQuery toggle no longer needed * Fix create and remove synonym dialog forms * Fix jump to input group * Remove text decoration for details and mappings * Replace Uglifier with Terser Fixes Uglifier::Error that prevents asset precompilation * Pin graphql gem to less than 2.1 * Pin base64 gem to 0.1.0 See https://github.com/ncbo/bioportal_web_ui/issues/293 * Remove text decoration from ontology list * Restore space underneath filter text box * Add download link for Virtual Appliance v3.2.2 * Use regular dropdown instead of split type * Display site notices above loading splash div Prevents a jumpy page appearance when the ontologies loading message is displayed * Update Gemfile.lock * Adjust font size for autocomplete results list Resolves #296 * Fix login requirement for notes email subscriptions The sanitize method was stripping the href attribute. See #298. * Encode user ID portion of subscriptions params Resolves #300 * Fix Uncaught TypeError on notes emails subscribe See #298 * Fix some RuboCop warnings * Refactor subscribed_to_ontology? method Account for the case where the user parameter doesn't have the ontology attribute loaded on the list of associated subscription objects --------- Co-authored-by: Alex Skrenchuk --- Gemfile | 7 +- Gemfile.lock | 119 ++--- app/assets/javascripts/admin/licenses.js | 8 +- app/assets/javascripts/bioportal.js.erb | 2 +- app/assets/javascripts/bp_annotatorplus.js | 6 +- app/assets/javascripts/bp_notes.js | 18 +- .../javascripts/bp_ontology_viewer.js.erb | 4 +- app/assets/javascripts/bp_search.js.erb | 2 +- app/assets/javascripts/submissions.js.erb | 2 +- app/assets/stylesheets/admin.scss | 1 + app/assets/stylesheets/annotator.scss | 8 +- app/assets/stylesheets/bioportal.scss | 21 +- .../stylesheets/bootstrap_overrides.scss | 56 +-- app/assets/stylesheets/home.scss | 12 + app/assets/stylesheets/mappings.scss | 10 +- app/assets/stylesheets/ontologies.scss | 33 +- app/assets/stylesheets/recommender.scss | 13 +- app/assets/stylesheets/search.scss | 45 +- app/controllers/subscriptions_controller.rb | 44 +- app/helpers/application_helper.rb | 19 +- app/helpers/change_requests_helper.rb | 6 +- app/helpers/notes_helper.rb | 9 +- app/views/admin/index.html.haml | 27 +- app/views/admin/licenses/_notice.html.haml | 3 +- app/views/annotator/index.html.haml | 78 ++-- app/views/annotatorplus/index.html.haml | 354 +++++++-------- .../application/_footer_appliance.html.haml | 6 +- app/views/application/_kgcl_dialogs.html.haml | 4 +- .../change_requests/_create_synonym.html.haml | 49 +-- .../_node_obsoletion.html.haml | 6 +- .../change_requests/_node_rename.html.haml | 14 +- app/views/change_requests/_notice.html.haml | 3 +- .../change_requests/_remove_synonym.html.haml | 43 +- .../change_requests/create_synonym.js.erb | 4 +- .../change_requests/node_obsoletion.js.erb | 3 - app/views/change_requests/node_rename.js.erb | 3 - .../change_requests/remove_synonym.js.erb | 4 +- app/views/concepts/_details.html.haml | 6 +- app/views/home/index.html.haml | 112 ++--- app/views/layouts/_footer.html.haml | 70 +-- app/views/layouts/_header.html.erb | 13 +- app/views/layouts/_notices.html.haml | 3 +- app/views/layouts/_ontology_viewer.html.haml | 101 +++-- app/views/layouts/_topnav.html.haml | 105 +++-- app/views/layouts/angular.html.erb | 12 +- app/views/layouts/appliance.html.haml | 14 +- .../mappings/_concept_mappings.html.haml | 2 +- app/views/mappings/_form.html.haml | 63 ++- app/views/ontologies/_metadata.html.haml | 16 +- app/views/ontologies/_submissions.html.haml | 2 +- app/views/ontologies/browse.html.erb | 407 +++++++++--------- app/views/ontologies/visualize.html.haml | 9 +- app/views/recommender/index.html.haml | 64 +-- app/views/search/index.html.haml | 41 +- app/views/submissions/_form.html.haml | 219 +++++----- app/views/users/show.html.haml | 4 +- app/views/virtual_appliance/index.html.haml | 10 + app/views/visits/index.html.haml | 4 +- config/environments/appliance.rb | 2 +- config/environments/production.rb | 2 +- config/environments/staging.rb | 2 +- .../autocomplete/jquery.autocomplete.css | 3 +- .../stylesheets/jquery.autocomplete.css | 3 +- 63 files changed, 1236 insertions(+), 1099 deletions(-) diff --git a/Gemfile b/Gemfile index c06158c332..b14475d221 100644 --- a/Gemfile +++ b/Gemfile @@ -6,12 +6,12 @@ source 'https://rubygems.org' gem 'rails', '6.1.7.3' gem 'sass-rails', '~> 5.0' -gem 'uglifier', '>= 1.0.3' +gem 'terser' # See https://github.com/rails/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby # gem 'duktape' -gem 'bootstrap', '~> 4.1.0' +gem 'bootstrap', '~> 5.2.3' gem 'chart-js-rails' gem 'jquery-rails' gem 'jquery-ui-rails' @@ -26,9 +26,12 @@ gem 'select2-rails' # To use debugger # gem 'debugger' +gem 'base64', '0.1.0' gem 'cube-ruby', require: 'cube' gem 'dalli' gem 'flamegraph' +# Version 2.1 breaks graphql-client. See: https://github.com/github/graphql-client/issues/310. +gem 'graphql', '~> 2.0.27' gem 'graphql-client' gem 'haml', '~> 5.1' gem 'i18n' diff --git a/Gemfile.lock b/Gemfile.lock index 885e31aa7b..b6c78f6e00 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -79,20 +79,20 @@ GEM zeitwerk (~> 2.3) addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) - airbrussh (1.4.2) + airbrussh (1.5.0) sshkit (>= 1.6.1, != 1.7.0) ast (2.4.2) - autoprefixer-rails (10.4.13.0) + autoprefixer-rails (10.4.15.0) execjs (~> 2) - base64 (0.1.1) + base64 (0.1.0) bcrypt_pbkdf (1.1.0) - bootstrap (4.1.3) - autoprefixer-rails (>= 6.0.3) - popper_js (>= 1.12.9, < 2) - sass (>= 3.5.2) + bootstrap (5.2.3) + autoprefixer-rails (>= 9.1.0) + popper_js (>= 2.11.6, < 3) + sassc-rails (>= 2.0.0) brakeman (6.0.1) builder (3.2.4) - capistrano (3.17.3) + capistrano (3.18.0) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) @@ -127,18 +127,18 @@ GEM crass (1.0.6) cube-ruby (0.0.3) daemons (1.4.1) - dalli (3.2.5) - date (3.3.3) + dalli (3.2.6) + date (3.3.4) diff-lcs (1.5.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) + domain_name (0.6.20231109) ed25519 (1.3.0) erubi (1.12.0) erubis (2.7.0) eventmachine (1.2.7) - excon (0.102.0) - execjs (2.8.1) - faraday (2.7.10) + excon (0.104.0) + execjs (2.9.1) + faraday (2.7.11) + base64 faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) faraday-excon (2.1.0) @@ -147,11 +147,11 @@ GEM faraday-multipart (1.0.4) multipart-post (~> 2) faraday-net_http (3.0.2) - ffi (1.15.5) + ffi (1.16.3) flamegraph (0.9.5) - globalid (1.1.0) - activesupport (>= 5.0) - graphql (2.0.26) + globalid (1.2.1) + activesupport (>= 6.1) + graphql (2.0.27) graphql-client (0.18.0) activesupport (>= 3.0) graphql @@ -180,7 +180,7 @@ GEM listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.21.3) + loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) lz4-ruby (0.3.3) @@ -194,46 +194,43 @@ GEM method_source (1.0.0) mime-types (3.5.1) mime-types-data (~> 3.2015) - mime-types-data (3.2023.0808) + mime-types-data (3.2023.1003) mini_mime (1.1.5) - minitest (5.19.0) + minitest (5.20.0) multi_json (1.15.0) multipart-post (2.3.0) mysql2 (0.5.5) - net-imap (0.3.7) + net-imap (0.4.5) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout net-scp (4.0.0) net-ssh (>= 2.6.5, < 8.0.0) - net-smtp (0.3.3) + net-smtp (0.4.0) net-protocol net-ssh (7.2.0) netrc (0.11.0) - newrelic_rpm (9.4.2) - nio4r (2.5.9) - nokogiri (1.15.4-arm64-darwin) - racc (~> 1.4) - nokogiri (1.15.4-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.15.4-x86_64-linux) + newrelic_rpm (9.6.0) + base64 + nio4r (2.6.0) + nokogiri (1.15.5-x86_64-darwin) racc (~> 1.4) - oj (3.16.0) + oj (3.16.1) open_uri_redirections (0.2.1) parallel (1.23.0) - parser (3.2.2.3) + parser (3.2.2.4) ast (~> 2.4.1) racc - popper_js (1.16.1) + popper_js (2.11.8) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) psych (3.3.4) - public_suffix (5.0.3) - racc (1.7.1) + public_suffix (5.0.4) + racc (1.7.3) rack (2.2.8) rack-mini-profiler (3.1.1) rack (>= 1.2.0) @@ -272,14 +269,14 @@ GEM rake (>= 12.2) thor (~> 1.0) rainbow (3.1.1) - rake (13.0.6) + rake (13.1.0) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) rdoc (6.3.3) recaptcha (5.9.0) json - regexp_parser (2.8.1) + regexp_parser (2.8.2) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) http-cookie (>= 1.0.2, < 2.0) @@ -303,19 +300,18 @@ GEM rspec-mocks (~> 3.12) rspec-support (~> 3.12) rspec-support (3.12.1) - rubocop (1.56.2) - base64 (~> 0.1.1) + rubocop (1.57.2) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.2.3) + parser (>= 3.2.2.4) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.28.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.29.0) + rubocop-ast (1.30.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) @@ -332,6 +328,14 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + sassc (2.4.0) + ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt select2-rails (4.0.13) sexp_processor (4.17.0) spawnling (2.1.5) @@ -342,33 +346,30 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - sshkit (1.21.5) + sshkit (1.21.6) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) stackprof (0.2.25) - temple (0.10.2) + temple (0.10.3) + terser (1.1.19) + execjs (>= 0.3.0, < 3) thin (1.8.2) daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) - thor (1.2.2) - tilt (2.2.0) - timeout (0.4.0) + thor (1.3.0) + tilt (2.3.0) + timeout (0.4.1) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - uglifier (4.2.0) - execjs (>= 0.3.0, < 3) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (2.4.2) + unicode-display_width (2.5.0) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) will_paginate (3.3.1) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.11) + zeitwerk (2.6.12) PLATFORMS arm64-darwin-22 @@ -376,8 +377,9 @@ PLATFORMS x86_64-linux DEPENDENCIES + base64 (= 0.1.0) bcrypt_pbkdf (>= 1.0, < 2.0) - bootstrap (~> 4.1.0) + bootstrap (~> 5.2.3) brakeman capistrano (~> 3.17) capistrano-bundler @@ -392,6 +394,7 @@ DEPENDENCIES dalli ed25519 (>= 1.2, < 2.0) flamegraph + graphql (~> 2.0.27) graphql-client haml (~> 5.1) html2haml @@ -420,9 +423,9 @@ DEPENDENCIES sass-rails (~> 5.0) select2-rails stackprof + terser thin - uglifier (>= 1.0.3) will_paginate (~> 3.0) BUNDLED WITH - 2.4.18 + 2.4.19 diff --git a/app/assets/javascripts/admin/licenses.js b/app/assets/javascripts/admin/licenses.js index 2a37f8de45..e65537b187 100644 --- a/app/assets/javascripts/admin/licenses.js +++ b/app/assets/javascripts/admin/licenses.js @@ -1,11 +1,11 @@ jQuery(".admin.index").ready(function() { - jQuery('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { - var href = jQuery(e.target).data('href'); - if (typeof href !== 'undefined' && href === '/admin/licenses') { + jQuery('button[data-bs-toggle="tab"]').on('shown.bs.tab', function (e) { + const target = jQuery(e.target).data('bsTarget'); + if (typeof target !== 'undefined' && target === '#licensing') { jQuery.ajax({ method: 'GET', - url: href, + url: 'admin/licenses', success: function(data){}, error: function(data){} }); diff --git a/app/assets/javascripts/bioportal.js.erb b/app/assets/javascripts/bioportal.js.erb index 591932f077..1decff1d53 100644 --- a/app/assets/javascripts/bioportal.js.erb +++ b/app/assets/javascripts/bioportal.js.erb @@ -430,7 +430,7 @@ function bpPopWindow(e) { newWindow.focus() }; } -jQuery("div.footer-support, div.dropdown-menu.dropdown-menu-right").on("click", "a.pop_window", { w: "800", h: "600" }, bpPopWindow) +jQuery("div.footer-support, ul.dropdown-menu.dropdown-menu-end").on("click", "a.pop_window", { w: "800", h: "600" }, bpPopWindow) /************************************************************** * Standardized BP modal popups diff --git a/app/assets/javascripts/bp_annotatorplus.js b/app/assets/javascripts/bp_annotatorplus.js index bc051caa9f..8294106560 100644 --- a/app/assets/javascripts/bp_annotatorplus.js +++ b/app/assets/javascripts/bp_annotatorplus.js @@ -56,7 +56,7 @@ function get_annotations() { } jQuery("#annotations_container").hide(); - jQuery(".annotator_spinner").show(); + jQuery(".annotator_spinner").css('display', 'inline-block'); ajax_process_halt(); var params = {}, @@ -148,12 +148,12 @@ function get_annotations() { success: function(data) { set_last_params(params); display_annotations(data, bp_last_params); - jQuery(".annotator_spinner").hide(200); + jQuery(".annotator_spinner").hide(); jQuery("#annotations_container").show(300); }, error: function(data) { set_last_params(params); - jQuery(".annotator_spinner").hide(200); + jQuery(".annotator_spinner").hide(); jQuery("#annotations_container").hide(); jQuery("#annotator_error").html(" Problem getting annotations, please try again"); } diff --git a/app/assets/javascripts/bp_notes.js b/app/assets/javascripts/bp_notes.js index a4776468c3..29c0a12485 100644 --- a/app/assets/javascripts/bp_notes.js +++ b/app/assets/javascripts/bp_notes.js @@ -341,13 +341,14 @@ function subscribeToNotes(button) { var ontologyId = jQuery(button).attr("data-bp_ontology_id"); var isSubbed = jQuery(button).attr("data-bp_is_subbed"); var userId = jQuery(button).attr("data-bp_user_id"); + let encodedUserId = encodeURIComponent(userId); jQuery(".notes_sub_error").html(""); jQuery(".notes_subscribe_spinner").show(); jQuery.ajax({ type: "POST", - url: "/subscriptions?user_id="+userId+"&ontology_id="+ontologyId+"&subbed="+isSubbed, + url: `/subscriptions?user_id=${encodedUserId}&ontology_id=${ontologyId}&subbed=${isSubbed}`, dataType: "json", success: function(data) { jQuery(".notes_subscribe_spinner").hide(); @@ -356,10 +357,17 @@ function subscribeToNotes(button) { var subbedVal = (isSubbed == "true") ? "false" : "true"; jQuery("a.subscribe_to_notes").attr("data-bp_is_subbed", subbedVal); - // Change button text - var txt = jQuery("a.subscribe_to_notes span.ui-button-text").html(); - var newButtonText = txt.match("Unsubscribe") ? txt.replace("Unsubscribe", "Subscribe") : txt.replace("Subscribe", "Unsubscribe"); - jQuery("a.subscribe_to_notes span.ui-button-text").html(newButtonText); + /* + * Update link text. + * Note that there are two links that allow users to subscribe/unsubscribe from notes emails. Given any ontology, + * one link is located on the top-level Notes tab, and the other link is located on the Notes sub-tab on the + * right-hand side of the Classes tab. Both links are handled by the subscriptions controller, and the text of + * both should be updated on success. + */ + const matches = document.querySelectorAll('a.subscribe_to_notes'); + matches.forEach(match => { + match.textContent = (subbedVal === 'true') ? 'Unsubscribe from notes emails' : 'Subscribe to notes emails'; + }); }, error: function(data) { jQuery(".notes_subscribe_spinner").hide(); diff --git a/app/assets/javascripts/bp_ontology_viewer.js.erb b/app/assets/javascripts/bp_ontology_viewer.js.erb index 687266fbfa..e595cf3c23 100644 --- a/app/assets/javascripts/bp_ontology_viewer.js.erb +++ b/app/assets/javascripts/bp_ontology_viewer.js.erb @@ -254,7 +254,9 @@ jQuery(document).ready(function() { if (typeof jQuery.bioportal.ont_pages[content_section].init === 'function') { jQuery.bioportal.ont_pages[content_section].init(jQuery.bioportal.ont_pages[content_section]); } - jQuery("#ont-" + content_section + "-tab").tab("show"); + const triggerEl = document.querySelector(`#navbar-ontology a[data-bs-target="#ont_${content_section}_content"]`); + const tabTrigger = new bootstrap.Tab(triggerEl); + bootstrap.Tab.getInstance(triggerEl).show(); // Retrieve AJAX content if not already displayed if ($.QueryString["skip_ajax_tabs"] != 'true') { diff --git a/app/assets/javascripts/bp_search.js.erb b/app/assets/javascripts/bp_search.js.erb index df60f482fb..c298bc5f0d 100644 --- a/app/assets/javascripts/bp_search.js.erb +++ b/app/assets/javascripts/bp_search.js.erb @@ -370,7 +370,7 @@ function performSearch() { // Display error message if no results found if (data.collection.length === 0) { result_count.html(""); - jQuery("#search_results").html("

No matches found

"); + jQuery("#search_results").html("

No matches found

"); } else { if (jQuery("#ontology_ontologyId").val() === null) { resultsOntCount = jQuery(""); diff --git a/app/assets/javascripts/submissions.js.erb b/app/assets/javascripts/submissions.js.erb index d6b4dca397..7dd111f65a 100644 --- a/app/assets/javascripts/submissions.js.erb +++ b/app/assets/javascripts/submissions.js.erb @@ -72,7 +72,7 @@ function addContact(event) { var removeButton = newContact.querySelector("button").cloneNode(true); removeButton.classList.replace("btn-success", "btn-danger"); removeButton.classList.replace("add-contact", "remove-contact"); - removeButton.classList.add("ml-1") + removeButton.classList.add("ms-1") removeButton.querySelector("i").classList.replace("fa-plus", "fa-minus"); newContact.appendChild(removeButton); diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index ffbb6188c7..91a93dda30 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -65,6 +65,7 @@ table.dataTable tbody tr.selected { .ontologies_list_container .dataTables_filter { float: right; padding-top: 3px; + padding-bottom: 5px; } .ontologies_list_container .dataTables_filter input { diff --git a/app/assets/stylesheets/annotator.scss b/app/assets/stylesheets/annotator.scss index d07f55d7c1..ea011e55f0 100644 --- a/app/assets/stylesheets/annotator.scss +++ b/app/assets/stylesheets/annotator.scss @@ -1,9 +1,5 @@ -div#semanticTypes_chzn.chzn-container input.default { - font-style: oblique !important; -} - -div#semanticTypes_chzn.chzn-container-active input.default { - font-style: normal !important; +select#semantic_types + .chosen-container { + display: block; } div#annotations_filter, div#annotations_info { diff --git a/app/assets/stylesheets/bioportal.scss b/app/assets/stylesheets/bioportal.scss index d47ea136a3..9b6a6cabc7 100644 --- a/app/assets/stylesheets/bioportal.scss +++ b/app/assets/stylesheets/bioportal.scss @@ -912,9 +912,9 @@ button.fg-button-menu, button.fg-button { /************************************ * Top navigation bar ************************************/ -.btn-bp-login { +div#topNavigationToggler a.btn.btn-login { color: #C58612; - border-color: #C58612 !important; + border-color: #C58612; &:hover { color: #234979; @@ -926,23 +926,6 @@ button.fg-button-menu, button.fg-button { background-color: #234979; } -.nav_text a { - font-family: 'Droid Sans', sans-serif; - font-size: 1.1em; - padding: 25px 15px; - position: relative; - text-decoration: none; - top: 11px; -} - -.nav_text a:hover { - color: lightGray !important; -} - -.nav_text_active a { - color: lightGray !important; -} - /************************************ * Ontology picker ************************************/ diff --git a/app/assets/stylesheets/bootstrap_overrides.scss b/app/assets/stylesheets/bootstrap_overrides.scss index 4281c826f5..403c35cbf2 100644 --- a/app/assets/stylesheets/bootstrap_overrides.scss +++ b/app/assets/stylesheets/bootstrap_overrides.scss @@ -1,24 +1,15 @@ -/* Sticky footer styles from Bootstrap: - https://getbootstrap.com/docs/4.1/examples/sticky-footer/sticky-footer.css +/* Style for Bootstrap sticky footer + https://getbootstrap.com/docs/5.2/examples/sticky-footer-navbar/ -------------------------------------------------- */ -html { - position: relative; - min-height: 100%; -} -body { - margin-bottom: body_margin_bottom(); /* Margin bottom by footer height */ +main > .container-fluid { + padding: 80px 15px 0; } + .footer { - position: absolute; - bottom: 0; - width: 100%; - height: 300px; /* Set the fixed height of the footer here */ - color: #757575; background-color: #ccc; + color: #757575; } -/* Footer --------------------------------------------------- */ footer.footer h6 { font-weight: bold; } @@ -27,37 +18,12 @@ footer.footer .list-inline-item:not(:last-child) { margin-right: 1rem; } -@media only screen and (max-width: 992px) { - body { - margin-bottom: 350px; - } - .footer { - height: 350px; - } -} - -@media only screen and (max-width: 768px) { - body { - margin-bottom: 560px; - } - .footer { - height: 560px; - } -} +/* TODO: remove styles below this comment when we eliminate Angular */ -@media only screen and (max-width: 576px) { - body { - margin-bottom: 625px; - } - .footer { - height: 625px; - } +.ontologies.index main.grid-container { + padding: 80px 15px 0; } -/* Customized styles for appliance mode --------------------------------------------------- */ -.footer-appliance { - height: 60px; /* Set the fixed height of the footer here */ - background-color: #f5f5f5; - padding: 20px 0; +.ontologies.index div.splash { + height: unset; } diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss index f26409b717..f3df640efc 100644 --- a/app/assets/stylesheets/home.scss +++ b/app/assets/stylesheets/home.scss @@ -2,3 +2,15 @@ i.fa.fa-caret-square-o-down { vertical-align: middle; margin-left: 2px; } + +table#ontology-visits { + a { + text-decoration: none; + } + a:hover { + text-decoration: underline; + } + thead tr { + white-space: nowrap; + } +} diff --git a/app/assets/stylesheets/mappings.scss b/app/assets/stylesheets/mappings.scss index c4dbece772..3a3dda6816 100644 --- a/app/assets/stylesheets/mappings.scss +++ b/app/assets/stylesheets/mappings.scss @@ -33,13 +33,21 @@ div#mapping_load { } div#mappingCount { - text-align: center; overflow: hidden; padding-top: 10px; } #mapping_count_table { width: 100%; + + tbody > tr :nth-child(1) { + a { + text-decoration: none; + } + a:hover { + text-decoration: underline; + } + } } /* Create New Mapping dialog */ diff --git a/app/assets/stylesheets/ontologies.scss b/app/assets/stylesheets/ontologies.scss index 7c33cbac5c..3e187c6f25 100644 --- a/app/assets/stylesheets/ontologies.scss +++ b/app/assets/stylesheets/ontologies.scss @@ -36,6 +36,7 @@ $ont-show-bg-color: #e9ecef; a { color: #343a40; + text-decoration: none; } } @@ -50,8 +51,8 @@ $ont-show-bg-color: #e9ecef; } .ont-info-links { - a + a { - margin-left: 1rem; + a:hover { + color: var(--bs-btn-hover-color); } } @@ -116,6 +117,10 @@ $ont-show-bg-color: #e9ecef; font-size: .9em; padding-left: 10px; } + + a { + text-decoration: none; + } } .ont-details-card { @@ -166,6 +171,7 @@ $ont-show-bg-color: #e9ecef; #ontology_versions td.show_more_subs { text-align: right; background-color: white; + border-bottom: none; } /************************************ @@ -215,6 +221,15 @@ $ont-show-bg-color: #e9ecef; float: left; width: 100%; overflow: auto; + + #details_content, #mappings_content { + a { + text-decoration: none; + } + a:hover { + text-decoration: underline; + } + } } #mappings_top.tab { @@ -268,11 +283,15 @@ $ont-show-bg-color: #e9ecef; * Browse page (index) ************************************/ -.ontologies.index #angular-notices { - margin-left: 15px; - margin-right: 15px; -} - #ontology-browse-help i { margin-left: .25em; } + +.ontology.grid-parent h2 { + a { + text-decoration: none; + } + a:hover { + text-decoration: underline; + } +} diff --git a/app/assets/stylesheets/recommender.scss b/app/assets/stylesheets/recommender.scss index 9c49a7aca7..3d9d04b17a 100644 --- a/app/assets/stylesheets/recommender.scss +++ b/app/assets/stylesheets/recommender.scss @@ -1,7 +1,3 @@ -.optionsBox { - margin-top: 15px; -} - #inputTextHighlighted { display:none; background-color: #e2ebf0; @@ -35,3 +31,12 @@ #recommender-help i { margin-left: .25em; } + +.recommender.index div#bd form, table#recommendations { + a { + text-decoration: none; + } + a:hover { + text-decoration: underline; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/search.scss b/app/assets/stylesheets/search.scss index 40dc92dbe0..3699757380 100644 --- a/app/assets/stylesheets/search.scss +++ b/app/assets/stylesheets/search.scss @@ -43,8 +43,14 @@ div.search_result { margin-bottom: 1.5em; } -div.class_link a { - font-size: 18px; +div.class_link { + a { + font-size: 18px; + text-decoration: none; + } + a:hover { + text-decoration: underline; + } } span.class_def { @@ -94,9 +100,16 @@ div.additional_results_link { margin-top: 5px; } -.search_result_links a { - font-size: 11px !important; - color: green; +.search_result_links { + a { + color: green; + font-size: 14px; + text-decoration: none; + } + a:hover { + color: green; + text-decoration: underline; + } } .hide_link { @@ -139,8 +152,24 @@ div#search_categories_chzn .chzn-drop { padding-bottom: 7px; } -#result_stats a { - color: gray; +#result_stats { + a { + color: gray; + text-decoration: none; + } + a:hover { + text-decoration: underline; + } +} + +#classSearchHelpBlock { + a { + float: right; + text-decoration: none; + } + a:hover { + text-decoration: underline; + } } #ontology_counts { @@ -168,7 +197,7 @@ div#search_categories_chzn .chzn-drop { } .concept_uri { - font-size: 9pt; + font-size: 12px; color: gray; } diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 945043258e..52c0e48774 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -1,48 +1,38 @@ -class SubscriptionsController < ApplicationController +# frozen_string_literal: true +class SubscriptionsController < ApplicationController def create - # Try to get the user linked data instance - user_id = params[:user_id] - u = LinkedData::Client::Models::User.find(user_id) - raise Exception if u.nil? - - # Try to get the ontology linked data instance - ontology_id = params[:ontology_id] - if ontology_id.start_with? 'http' - ont = LinkedData::Client::Models::Ontology.find(ontology_id) - else - ont = LinkedData::Client::Models::Ontology.find_by_acronym(ontology_id).first - end - raise Exception if ont.nil? + u = LinkedData::Client::Models::User.find(params[:user_id]) + ont = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology_id]).first # Is this request to add or remove a subscription? - subscribed = params[:subbed] # string (not boolean) - if subscribed.eql?("true") + subscribed = params[:subbed] + if subscribed.eql?('true') # Already subscribed, so this request must be a delete # Note that this routine removes ALL subscriptions for the ontology, regardless of type. # Previous way to delete subscription: error when u.update if more than 1 subscription in the subscription array: - #u.subscription.delete_if {|sub| sub[:ontology].split('/').last.eql?(ont.acronym) } + # u.subscription.delete_if {|sub| sub[:ontology].split('/').last.eql?(ont.acronym) } # So here we re-generate a new subscription Array (instead of directly updating it, which causes error) all_subs = [] u.subscription.each do |subs| # Add all subscription to the array, but not the one to be deleted if !subs.ontology.split('/').last.eql?(ont.acronym) - all_subs.push({ontology: subs.ontology, notification_type: subs.notification_type}) + all_subs.push({ ontology: subs.ontology, notification_type: subs.notification_type }) end end u.subscription = all_subs else # Not subscribed yet, so this request must be for adding subscription # Old way: - #subscription = {ontology: ont.acronym, notification_type: "NOTES"} #NOTIFICATION_TYPES[:notes]} - #u.subscription.push(subscription) + # subscription = {ontology: ont.acronym, notification_type: "NOTES"} #NOTIFICATION_TYPES[:notes]} + # u.subscription.push(subscription) # This way was not working, updating subscription is failing when more than 1 subscription in the array # And we were updating with different types of object in the subscription array : OpenStruct and hash # So we are generating an array with only hash - all_subs = [{ontology: ont.acronym, notification_type: "NOTES"}] # the new subscription + all_subs = [{ ontology: ont.acronym, notification_type: 'NOTES' }] # the new subscription u.subscription.each do |subs| # add all existing subscriptions - all_subs.push({ontology: subs.ontology, notification_type: subs.notification_type}) + all_subs.push({ ontology: subs.ontology, notification_type: subs.notification_type }) end u.subscription = all_subs end @@ -53,27 +43,25 @@ def create if response_success?(error_response) updated_sub = true session[:user].subscription = u.subscription - #session[:user] = u + # session[:user] = u # NOTES: # - Cannot update session[:user] as above. The session user object is special because it only # gets set when someone logs in and the user object returned when authenticating is the # only one that will contain the api key for security reasons. So we actually need to use # the update_from_params method, can’t just set the object to the user linked data instance. # - #session[:user].update_from_params(params[:user]) + # session[:user].update_from_params(params[:user]) # update_from_params first gets all attributes from the REST service for the object being updated, # then sets the values provided in the params hash where the param keys match setter names on the # object (in this case, for example, :subscription would set the @subscription value on the instance). # That’s all it does, no saving or anything. else updated_sub = false - #errors = response_errors(error_response) end - rescue + rescue StandardError updated_sub = false end - render :json => { :updated_sub => updated_sub, :user_subscriptions => u.subscription } + render json: { updated_sub: updated_sub, user_subscriptions: u.subscription } end - end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1e93c2aac0..3948fe3a2b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -276,15 +276,16 @@ def subscribe_ontology_button(ontology_id, user = nil) end def subscribed_to_ontology?(ontology_acronym, user) - user.bring(:subscription) if user.subscription.nil? - # user.subscription is an array of subscriptions like {ontology: ontology_id, notification_type: "NOTES"} - return false if user.subscription.nil? or user.subscription.empty? - user.subscription.each do |sub| - #sub = {ontology: ontology_acronym, notification_type: "NOTES"} - sub_ont_acronym = sub[:ontology].split('/').last # make sure we get the acronym, even if it's a full URI - return true if sub_ont_acronym == ontology_acronym - end - return false + return false if user.subscription.blank? + + # In some cases this method is called with user objects that don't have the :ontology attribute loaded on + # the associated subscription objects. Calling find is the only way (?) to ensure that we get a user where the + # :ontology attribute is loaded for all subscriptions. + ontology_attributes_missing = user.subscription.any? { |sub| sub[:ontology].nil? } + user = LinkedData::Client::Models::User.find(user.id) if ontology_attributes_missing + + sub = user.subscription.select { |sub| sub[:ontology].split('/').last.eql? ontology_acronym } + sub.length.positive? ? 'true' : 'false' end def ontolobridge_instructions_template(ontology) diff --git a/app/helpers/change_requests_helper.rb b/app/helpers/change_requests_helper.rb index acd0af48f1..6259d0c09f 100644 --- a/app/helpers/change_requests_helper.rb +++ b/app/helpers/change_requests_helper.rb @@ -18,7 +18,7 @@ def add_synonym_button role: 'button', class: 'btn btn-link', aria: { label: 'Create synonym' }, - data: { toggle: 'modal', target: '#changeRequestModal' }, + data: { 'bs-toggle': 'modal', 'bs-target': '#changeRequestModal' }, remote: 'true') do content_tag(:i, '', class: 'fas fa-plus-circle fa-lg', aria: { hidden: 'true' }).html_safe end @@ -35,7 +35,7 @@ def remove_synonym_button link_to(change_requests_remove_synonym_path(concept_id: @concept.id, concept_label: @concept.prefLabel, ont_acronym: @ontology.acronym, concept_synonyms: @concept.synonym), role: 'button', class: 'btn btn-link', aria: { label: 'Remove synonym' }, - data: { toggle: 'modal', target: '#changeRequestModal' }, remote: 'true') do + data: { 'bs-toggle': 'modal', 'bs-target': '#changeRequestModal' }, remote: 'true') do tag.i class: 'fas fa-minus-circle fa-lg', aria: { hidden: 'true' } end end @@ -43,6 +43,6 @@ def remove_synonym_button def synonym_qualifier_select(form) options = [%w[exact exact], %w[narrow narrow], %w[broad broad], %w[related related]] - form.select :qualifier, options_for_select(options, 0), {}, { class: 'form-control' } + form.select :qualifier, options_for_select(options, 0), {}, { class: 'form-select' } end end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index a1b5a68d54..f1f89cedba 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -132,10 +132,11 @@ def get_note_type_text(note_type) def subscribe_button(ontology_id) user = session[:user] + if user.nil? - # subscribe button must redirect to login - return sanitize("") + return link_to('Subscribe to notes emails', login_index_path, class: 'link_button') end + # Init subscribe button parameters. sub_text = "Subscribe" params = "data-bp_ontology_id='#{ontology_id}' data-bp_is_subbed='false' data-bp_user_id='#{user.id}'" @@ -147,14 +148,14 @@ def subscribe_button(ontology_id) ont = LinkedData::Client::Models::Ontology.find_by_acronym(ontology_id).first end subscribed = subscribed_to_ontology?(ont.acronym, user) # application_helper - sub_text = subscribed ? "Unsubscribe" : "Subscribe" + sub_text = subscribed ? "Unsubscribe from" : "Subscribe to" params = "data-bp_ontology_id='#{ont.acronym}' data-bp_is_subbed='#{subscribed}' data-bp_user_id='#{user.id}'" rescue # pass, fallback init done above begin block to scope parameters beyond the begin/rescue block end spinner = '' error = "" - return " #{spinner} #{error}".html_safe + return " #{spinner} #{error}".html_safe end def delete_button diff --git a/app/views/admin/index.html.haml b/app/views/admin/index.html.haml index 083806f658..5b6c6a8748 100644 --- a/app/views/admin/index.html.haml +++ b/app/views/admin/index.html.haml @@ -13,16 +13,23 @@ %div.row %div.col - - %ul.nav.nav-tabs{id: "admin-tabs", role: "tablist"} - %li.nav-item - =link_to("Site Administration", "#site-admin", id: "site-admin-tab", class: "nav-link active", role: "tab", data: { toggle: "tab" }, aria: { controls: "site-admin", selected: "true" }) - %li.nav-item - =link_to("Ontology Administration", "#ontology-admin", id: "ontology-admin-tab", class: "nav-link", role: "tab", data: { toggle: "tab" }, aria: { controls: "ontology-admin", selected: "false" }) - %li.nav-item - =link_to("Licensing", "#licensing", id: "licensing-admin-tab", class: "nav-link", role: "tab", data: { toggle: "tab", href: admin_licenses_path() }, aria: { controls: "licensing", selected: "false" }) - %li.nav-item - =link_to("Users", "#users", id: "users-admin-tab", class: "nav-link", role: "tab", data: { toggle: "tab" }, aria: { controls: "users", selected: "false" }) + %ul{class: "nav nav-tabs", id: "admin-tabs", role: "tablist"} + %li{class: "nav-item", role: "presentation"} + = tag.button("Site administration", class: "nav-link active", id: "site-admin-tab", "data-bs-toggle": "tab", + "data-bs-target": "#site-admin", type: "button", role: "tab", "aria-controls": "site-admin", + "aria-selected": "true") + %li{class: "nav-item", role: "presentation"} + = tag.button("Ontology administration", class: "nav-link", id: "ontology-admin-tab", "data-bs-toggle": "tab", + "data-bs-target": "#ontology-admin", type: "button", role: "tab", + "aria-controls": 'ontology-admin', "aria-selected": "false") + %li{class: "nav-item", role: "presentation"} + = tag.button("Licensing", class: "nav-link", id: "licensing-admin-tab", "data-bs-toggle": "tab", + "data-bs-target": "#licensing", type: "button", role: "tab", "aria-controls": "licensing", + "aria-selected": "false") + %li{class: "nav-item", role: "presentation"} + = tag.button("Users", class: "nav-link", id: "users-admin-tab", "data-bs-toggle": "tab", + "data-bs-target": "#users", type: "button", role: "tab", "aria-controls": "users", + "aria-selected": "false") %div#adminTabContent.tab-content -# Site Administration tab diff --git a/app/views/admin/licenses/_notice.html.haml b/app/views/admin/licenses/_notice.html.haml index 59f6e62895..b4025e835b 100644 --- a/app/views/admin/licenses/_notice.html.haml +++ b/app/views/admin/licenses/_notice.html.haml @@ -1,5 +1,4 @@ - if notice.present? %div.alert.alert-success.alert-dismissible.fade.show.mt-3.mb-0{role: "alert"} = notice - = button_tag(type: "button", class: "close", aria: {label: "Close"}, data: {dismiss: "alert"}) do - %span{aria: {hidden: "true"}} × + = button_tag(type: "button", class: "btn-close", "aria-label": "Close", "data-bs-dismiss": "alert") diff --git a/app/views/annotator/index.html.haml b/app/views/annotator/index.html.haml index 44ab7a02ea..bc20b775f7 100644 --- a/app/views/annotator/index.html.haml +++ b/app/views/annotator/index.html.haml @@ -12,52 +12,58 @@ = link_to(Rails.configuration.settings.links[:help_annotator], id: "annotator-help", "aria-label": "View annotator help") do %i.fas.fa-question-circle.fa-lg{"aria-hidden": "true"} -%form{style: "margin-bottom: 25px"} - %div.form-group - = text_area_tag("annotation_text", nil, rows: 15, class: "form-control", placeholder: "Enter or paste text to be annotated", aria: {describedby: "inputTextHelpBlock"}) - %small#inputTextHelpBlock.form-text - %a#insert_text_link{href: "javascript:void(0);"} insert sample text +%form.mb-4 + %div.mb-3 + = text_area_tag("annotation_text", nil, rows: 10, class: "form-control", + placeholder: "Enter or paste text to be annotated", 'aria-describedby': "inputTextHelpBlock") + %div#inputTextHelpBlock + = link_to('insert sample text', 'javascript:void(0);', id: 'insert_text_link', class: 'form-text') - %div.form-check.form-check-inline - = check_box_tag("longest_only", "all", false, class: "form-check-input") - %label.form-check-label{for: "longest_only"} Match longest only - %div.form-check.form-check-inline - = check_box_tag("whole_word_only", "all", false, class: "form-check-input") - %label.form-check-label{for: "whole_word_only"} Match partial words - %div.form-check.form-check-inline - = check_box_tag("mappings", "all", false, id: "mappings_all", class: "form-check-input") - %label.form-check-label{for: "mappings_all"} Include mappings - %div.form-check.form-check-inline - = check_box_tag("exclude_numbers", "all", false, class: "form-check-input") - %label.form-check-label{for: "exclude_numbers"} Exclude numbers - %div.form-check.form-check-inline - = check_box_tag("exclude_synonyms", "all", false, class: "form-check-input") - %label.form-check-label{for: "exclude_synonyms"} Exclude synonyms + %div.mb-3 + %div.form-check.form-check-inline + = check_box_tag("longest_only", "all", false, class: "form-check-input") + %label.form-check-label{for: "longest_only"} Match longest only + %div.form-check.form-check-inline + = check_box_tag("whole_word_only", "all", false, class: "form-check-input") + %label.form-check-label{for: "whole_word_only"} Match partial words + %div.form-check.form-check-inline + = check_box_tag("mappings", "all", false, id: "mappings_all", class: "form-check-input") + %label.form-check-label{for: "mappings_all"} Include mappings + %div.form-check.form-check-inline + = check_box_tag("exclude_numbers", "all", false, class: "form-check-input") + %label.form-check-label{for: "exclude_numbers"} Exclude numbers + %div.form-check.form-check-inline + = check_box_tag("exclude_synonyms", "all", false, class: "form-check-input") + %label.form-check-label{for: "exclude_synonyms"} Exclude synonyms - %div.form-group.pt-4 - = render partial: "shared/ontology_picker", locals: { :custom_ontologies => @annotator_ontologies } + %div.mb-3 + = render partial: 'shared/ontology_picker', locals: { custom_ontologies: @annotator_ontologies } - if @sem_type_ont - %div.form-group - %label{for: "semantic_types"} Select UMLS semantic types - = select_tag("semantic_types", options_for_select(@semantic_types_for_select), multiple: "true", data: {placeholder: "Start typing to select UMLS semantic types"}, class: "form-control", style: "width: 432px") + %div.mb-3 + %label{for: "semantic_types", class: 'form-label'} Select UMLS semantic types + = select_tag("semantic_types", options_for_select(@semantic_types_for_select), multiple: "true", + data: {placeholder: "Start typing to select UMLS semantic types"}, class: "form-select", + style: "width: 432px") - %div.form-group - %label{for: "class_hierarchy_max_level"} Include ancestors up to level + %div.mb-3 + %label{for: "class_hierarchy_max_level", class: 'form-label'} Include ancestors up to level - options = [["none", 0], *(1..10).map {|i| [i, i]}, ["all", 999]] - = select_tag("class_hierarchy_max_level", options_for_select(options, 0), class: "form-control", style: "width: 432px") + = select_tag("class_hierarchy_max_level", options_for_select(options, 0), class: "form-select", + style: "width: 432px") - if @recognizers.length > 1 - %div.form-group - %label{for: "recognizer"} Entity recognizer + %div.mb-3 + %label{for: "recognizer", class: 'form-label'} Entity recognizer - default_recognizer = @recognizers.include?("mgrep") ? "mgrep" : @recognizers.first - = select_tag("recognizer", options_for_select(@recognizers.map {|r| [r,r]}, default_recognizer), class: "form-control", style: "width: 432px") + = select_tag("recognizer", options_for_select(@recognizers.map { |r| [r, r] }, default_recognizer), + class: "form-select", style: "width: 432px") - / annotator_button calls 'get_annotations' in bp_annotator.js - %input#annotator_button{type: "button", value: "Get Annotations", class: "btn btn-primary"}/ - %span.annotator_spinner{style: "display: none;"} - %img{src: asset_path('spinners/spinner_000000_16px.gif'), style: "vertical-align: middle;"}/ - %span#annotator_error.annotator_error{style: "color: red; vertical-align: middle; margin-left: 3px;"} + %div.mb-3 + %input#annotator_button{type: "button", value: "Get Annotations", class: "btn btn-primary"}/ + %span.annotator_spinner{style: "display: none;"} + %img{src: asset_path('spinners/spinner_000000_16px.gif'), style: "vertical-align: middle;"}/ + %span#annotator_error.annotator_error{style: "color: red; vertical-align: middle; margin-left: 3px;"} %div.row %div.col diff --git a/app/views/annotatorplus/index.html.haml b/app/views/annotatorplus/index.html.haml index 0fab2ce093..5b966f73ce 100644 --- a/app/views/annotatorplus/index.html.haml +++ b/app/views/annotatorplus/index.html.haml @@ -3,174 +3,186 @@ %head = javascript_include_tag "bp_annotatorplus" -%div.container-fluid - %div.row - %div.col - %h2.mt-3 Annotator + - %p - = t('annotator.index.intro').html_safe - = link_to(Rails.configuration.settings.links[:help_annotator],id: "annotator-help", - "aria-label": "View annotator help") do - %i.fa.fa-question-circle.fa-lg{"aria-hidden": "true"} - %div.row - %div.col - %form - %div.form-group - = text_area_tag("annotation_text", nil, rows: 10, class: "form-control", placeholder: "Enter or paste text to be annotated", "aria-describedby": "annotateTextHelpBlock") - %small#annotateTextHelpBlock.form-text - %a#insert_text_link{href: "javascript:void(0);"} insert sample text - - %a#advancedOptionsLink{:href => "javascript:void(0);"} Show advanced options >> - - %div#advanced-options-container.mt-4 - %div.form-group - %div.custom-control.custom-checkbox.custom-control-inline - = check_box_tag("longest_only", "all", false, class: "custom-control-input") - %label.custom-control-label{for: "longest_only"} Match longest only - - %div.custom-control.custom-checkbox.custom-control-inline - = check_box_tag("whole_word_only", "all", false, class: "custom-control-input") - %label.custom-control-label{for: "whole_word_only"} Match partial words - - %div.custom-control.custom-checkbox.custom-control-inline - = check_box_tag("mappings", "all", false, id: "mappings_all", class: "custom-control-input") - %label.custom-control-label{for: "mappings_all"} Include mappings - - %div.custom-control.custom-checkbox.custom-control-inline - = check_box_tag("exclude_numbers", "all", false, class: "custom-control-input") - %label.custom-control-label{for: "exclude_numbers"} Exclude numbers - - %div.custom-control.custom-checkbox.custom-control-inline - = check_box_tag("exclude_synonyms", "all", false, class: "custom-control-input") - %label.custom-control-label{for: "exclude_synonyms"} Exclude synonyms - - %div.form-group - %label{for: "ontology_ontologyId"} Select ontologies - = select_tag("ontology_ontologyId", options_from_collection_for_select(@annotator_ontologies, :acronym, lambda { |ont| "#{ont.name} (#{ont.acronym})" }), | - multiple: true, class: "form-control", "aria-describedby": "selectOntologiesHelpBlock") | - %small#selectOntologiesHelpBlock.form-text.text-muted Start typing to select ontologies or leave blank to use all - - - if @sem_type_ont - %div.form-group - %label{for: "semantic_types"} Select UMLS semantic types - = select_tag("semantic_types", options_for_select(@semantic_types_for_select), multiple: "true", class: "form-control", "aria-describedby": "selectSemanticTypesHelpBlock") - %small#selectSemanticTypesHelpBlock.form-text.text-muted Start typing to select UMLS semantic types or leave blank to use all - - %div.form-group - %label{for: "semantic_groups"} Select UMLS semantic groups - = select_tag("semantic_groups", options_for_select(@semantic_groups_for_select), multiple: "true", class: "form-control", "aria-describedby": "selectSemanticGroupsHelpBlock") - %small#selectSemanticGroupsHelpBlock.form-text.text-muted Start typing to select UMLS semantic groups or leave blank to use all - - %div.form-group - %label{for: "class_hierarchy_max_level"} Include ancestors up to level: - - options = [["none", 0], *(1..10).map {|i| [i, i]}, ["all", 999]] - = select_tag("class_hierarchy_max_level", options_for_select(options, 0), class: "form-control") - - %div.form-group - %label{for: "score"} Include score: - - options = [["none", ""], ["old", "old"], ["cvalue", "cvalue"], ["cvalueh", "cvalueh"]] - = select_tag(:score, options_for_select(options, 0), class: "form-control", "aria-describedby": "includeScoreHelpBlock") - %small#includeScoreHelpBlock.form-text.text-muted Score annotations following the previous 2009 NCBO measure (old) or the C-Value measure (cvalue). If hierarchy expansion is used, then prefer cvalueh. - - %div.form-group - %label{for: "score_threshold"} Filter by score threshold: - = number_field_tag(:score_threshold, 0, id: "score_threshold", class: "form-control", "aria-describedby": "scoreThresholdHelpBlock") - %small#scoreThresholdHelpBlock.form-text.text-muted Specify the minimum score value for annotations - - %div.form-group - %label{for: "confidence_threshold"} Filter confidence threshold: - = number_field_tag(:confidence_threshold, 0, min: 0, max: 100, id: "confidence_threshold", class: "form-control", "aria-describedby": "confidenceThresholdHelpBlock") - %small#confidenceThresholdHelpBlock.form-text.text-muted Specify the minimum position in the scoring distribution (between 1 and 100) - - - if @recognizers.length > 1 - %div.form-group - %label{for: "recognizer"} Entity recognizer: - - default_recognizer = @recognizers.include?("mgrep") ? "mgrep" : @recognizers.first - = select_tag("recognizer", options_for_select(@recognizers.map {|r| [r, r]}, default_recognizer), class: "form-control") - - %div.form-group - %div.custom-control.custom-checkbox - = check_box_tag("negation", :all, false, class: "custom-control-input") - %label.custom-control-label{for: "negation"} Detect negation - %small#negationHelp.form-text.text-muted Detect if a concept has been negated (affirmed, negated, possible) - - %div.custom-control.custom-checkbox - = check_box_tag("experiencer", :all, false, class: "custom-control-input") - %label.custom-control-label{for: "experiencer"} Detect experiencer - %small#experiencerHelp.form-text.text-muted Detect who experienced the each identified concept (patient, other) - - %div.custom-control.custom-checkbox - = check_box_tag("temporality", :all, false, class: "custom-control-input") - %label.custom-control-label{for: "temporality"} Detect temporality - %small#temporalityHelp.form-text.text-muted Detect when the annotated concept occurred (recent, historical, hypothetical) - - %div.my-4 - = button_tag("Get annotations", type: "button", id: "annotator_button", class: "btn btn-primary btn-lg") - %span.annotator_spinner - %img{src: asset_path('spinners/spinner_000000_16px.gif')}/ - %span#annotator_error.annotator_error - - %div.row - %div.col - #annotations_container - #result_counts - %h4 Annotations - #filter_list{:style => "font-size: 9pt; color: gray; display: none; clear: both; margin: -15px 0 5px"} - %span#filter_title> Results are filtered by: - \  - %span#filter_names - #results_error{:style => "color: red; margin-bottom: 7px;"} - %table#annotations.zebra{:style => "min-width: 700px; width: 100%;"} - %thead - %tr - %th - Class - %span.popup_container - %span.bp_popup_link_container - %a#filter_classes.bp_popup_link{:href => "javascript:void(0);"} filter - %div#classes_filter_list.bp_popup_list - %th - Ontology - %span.popup_container - %span.bp_popup_link_container - %a#filter_ontologies.bp_popup_link{:href => "javascript:void(0);"} filter - %div#ontology_filter_list.bp_popup_list - %th{class: "match_type"} - Type - %span.popup_container - %span.bp_popup_link_container - %a#filter_match_type.bp_popup_link{:href => "javascript:void(0);"} filter - %div#match_type_filter_list.bp_popup_list - %th UMLS Sem Type - %th{class: "match_context"} Context - %th - Matched Class - %span.popup_container - %span.bp_popup_link_container - %a#filter_matched_classes.bp_popup_link{:href => "javascript:void(0);"} filter - %div#matched_classes_filter_list.bp_popup_list - %th - Matched Ontology - %span.popup_container - %span.bp_popup_link_container - %a#filter_matched_ontologies.bp_popup_link{:href => "javascript:void(0);"} filter - %div#matched_ontology_filter_list.bp_popup_list - %th Score - %th Negation - %th Experiencer - %th Temporality - %tbody - %div.my-3 - %b Format results as: - %div.my-3 - %span#download_links_tabdelimited.link_button.ui_button - %span#download_links_json.link_button.ui_button - %span#download_links_rdf.link_button.ui_button - %span#download_links_text.link_button.ui_button - -# %span#download_links_xml.link_button.ui_button - %div.mt-3 - Reproduce these results using the - %span#annotator_parameters - %div.mb-4 - Additional parameters explained at - = link_to('Annotator API documentation', "#{$REST_URL}/documentation#nav_annotator", target: "_blank") +%div.row + %div.col + %h2.mt-3 Annotator + + %p + = t('annotator.index.intro').html_safe + = link_to(Rails.configuration.settings.links[:help_annotator],id: "annotator-help", + "aria-label": "View annotator help") do + %i.fa.fa-question-circle.fa-lg{"aria-hidden": "true"} +%div.row + %div.col + %form + %div.mb-3 + = text_area_tag("annotation_text", nil, rows: 10, class: "form-control", + placeholder: "Enter or paste text to be annotated", "aria-describedby": "annotateTextHelpBlock") + %div#annotateTextHelpBlock.form-text + %a#insert_text_link{href: "javascript:void(0);", class: 'link-secondary'} insert sample text + + %div.mb-4 + %a#advancedOptionsLink{href: "javascript:void(0);"} Show advanced options >> + + %div#advanced-options-container + %div.mb-3 + %div.form-check.form-check-inline + = check_box_tag("longest_only", "all", false, class: "form-check-input") + %label.form-check-label{for: "longest_only"} Match longest only + + %div.form-check.form-check-inline + = check_box_tag("whole_word_only", "all", false, class: "form-check-input") + %label.form-check-label{for: "whole_word_only"} Match partial words + + %div.form-check.form-check-inline + = check_box_tag("mappings", "all", false, id: "mappings_all", class: "form-check-input") + %label.form-check-label{for: "mappings_all"} Include mappings + + %div.form-check.form-check-inline + = check_box_tag("exclude_numbers", "all", false, class: "form-check-input") + %label.form-check-label{for: "exclude_numbers"} Exclude numbers + + %div.form-check.form-check-inline + = check_box_tag("exclude_synonyms", "all", false, class: "form-check-input") + %label.form-check-label{for: "exclude_synonyms"} Exclude synonyms + + %div.mb-3 + %label.form-label{for: "ontology_ontologyId"} Select ontologies + = select_tag("ontology_ontologyId", options_from_collection_for_select(@annotator_ontologies, :acronym, lambda { |ont| "#{ont.name} (#{ont.acronym})" }), | + multiple: true, class: "form-select", "aria-describedby": "selectOntologiesHelpBlock") | + %div#selectOntologiesHelpBlock.form-text Start typing to select ontologies or leave blank to use all + + - if @sem_type_ont + %div.mb-3 + %label.form-label{for: "semantic_types"} Select UMLS semantic types + = select_tag("semantic_types", options_for_select(@semantic_types_for_select), multiple: "true", + class: "form-select", "aria-describedby": "selectSemanticTypesHelpBlock") + %div#selectSemanticTypesHelpBlock.form-text + Start typing to select UMLS semantic types or leave blank to use all + + %div.mb-3 + %label.form-label{for: "semantic_groups"} Select UMLS semantic groups + = select_tag("semantic_groups", options_for_select(@semantic_groups_for_select), multiple: "true", + class: "form-select", "aria-describedby": "selectSemanticGroupsHelpBlock") + %div#selectSemanticGroupsHelpBlock.form-text + Start typing to select UMLS semantic groups or leave blank to use all + + %div.mb-3 + %label.form-label{for: "class_hierarchy_max_level"} Include ancestors up to level: + - options = [["none", 0], *(1..10).map {|i| [i, i]}, ["all", 999]] + = select_tag("class_hierarchy_max_level", options_for_select(options, 0), class: "form-select") + + %div.mb-3 + %label.form-label{for: "score"} Include score: + - options = [["none", ""], ["old", "old"], ["cvalue", "cvalue"], ["cvalueh", "cvalueh"]] + = select_tag(:score, options_for_select(options, 0), class: "form-select", + "aria-describedby": "includeScoreHelpBlock") + %div#includeScoreHelpBlock.form-text + Score annotations following the previous 2009 NCBO measure (old) or the C-Value measure (cvalue). If + hierarchy expansion is used, then prefer cvalueh. + + %div.mb-3 + %label.form-label{for: "score_threshold"} Filter by score threshold: + = number_field_tag(:score_threshold, 0, id: "score_threshold", class: "form-control", + "aria-describedby": "scoreThresholdHelpBlock") + %div#scoreThresholdHelpBlock.form-text Specify the minimum score value for annotations + + %div.mb-3 + %label.form-label{for: "confidence_threshold"} Filter confidence threshold: + = number_field_tag(:confidence_threshold, 0, min: 0, max: 100, id: "confidence_threshold", + class: "form-control", "aria-describedby": "confidenceThresholdHelpBlock") + %div#confidenceThresholdHelpBlock.form-text + Specify the minimum position in the scoring distribution (between 1 and 100) + + - if @recognizers.length > 1 + %div.mb-3 + %label.form-label{for: "recognizer"} Entity recognizer: + - default_recognizer = @recognizers.include?("mgrep") ? "mgrep" : @recognizers.first + = select_tag("recognizer", options_for_select(@recognizers.map {|r| [r, r]}, default_recognizer), + class: "form-select") + + %div.mb-3 + %div.form-check + = check_box_tag("negation", :all, false, class: "form-check-input") + %label.form-check-label{for: "negation"} Detect negation + %div#negationHelp.form-text Detect if a concept has been negated (affirmed, negated, possible) + + %div.form-check + = check_box_tag("experiencer", :all, false, class: "form-check-input") + %label.form-check-label{for: "experiencer"} Detect experiencer + %div#experiencerHelp.form-text Detect who experienced the each identified concept (patient, other) + + %div.form-check + = check_box_tag("temporality", :all, false, class: "form-check-input") + %label.form-check-label{for: "temporality"} Detect temporality + %div#temporalityHelp.form-text Detect when the annotated concept occurred (recent, historical, hypothetical) + + %div.mb-4 + = tag.input(value: 'Get annotations', type: "button", id: "annotator_button", class: "btn btn-primary") + %span.annotator_spinner + %img{src: asset_path('spinners/spinner_000000_16px.gif')}/ + %span#annotator_error.annotator_error + +%div.row + %div.col + #annotations_container + #result_counts + %h4 Annotations + #filter_list{:style => "font-size: 9pt; color: gray; display: none; clear: both; margin: -15px 0 5px"} + %span#filter_title> Results are filtered by: + \  + %span#filter_names + #results_error{:style => "color: red; margin-bottom: 7px;"} + %table#annotations.zebra{:style => "min-width: 700px; width: 100%;"} + %thead + %tr + %th + Class + %span.popup_container + %span.bp_popup_link_container + %a#filter_classes.bp_popup_link{:href => "javascript:void(0);"} filter + %div#classes_filter_list.bp_popup_list + %th + Ontology + %span.popup_container + %span.bp_popup_link_container + %a#filter_ontologies.bp_popup_link{:href => "javascript:void(0);"} filter + %div#ontology_filter_list.bp_popup_list + %th{class: "match_type"} + Type + %span.popup_container + %span.bp_popup_link_container + %a#filter_match_type.bp_popup_link{:href => "javascript:void(0);"} filter + %div#match_type_filter_list.bp_popup_list + %th UMLS Sem Type + %th{class: "match_context"} Context + %th + Matched Class + %span.popup_container + %span.bp_popup_link_container + %a#filter_matched_classes.bp_popup_link{:href => "javascript:void(0);"} filter + %div#matched_classes_filter_list.bp_popup_list + %th + Matched Ontology + %span.popup_container + %span.bp_popup_link_container + %a#filter_matched_ontologies.bp_popup_link{:href => "javascript:void(0);"} filter + %div#matched_ontology_filter_list.bp_popup_list + %th Score + %th Negation + %th Experiencer + %th Temporality + %tbody + %div.my-3 + %b Format results as: + %div.my-3 + %span#download_links_tabdelimited.link_button.ui_button + %span#download_links_json.link_button.ui_button + %span#download_links_rdf.link_button.ui_button + %span#download_links_text.link_button.ui_button + -# %span#download_links_xml.link_button.ui_button + %div.mt-3 + Reproduce these results using the + %span#annotator_parameters + %div.mb-4 + Additional parameters explained at + = link_to('Annotator API documentation', "#{$REST_URL}/documentation#nav_annotator", target: "_blank") diff --git a/app/views/application/_footer_appliance.html.haml b/app/views/application/_footer_appliance.html.haml index ccd6baade4..c2183457ec 100644 --- a/app/views/application/_footer_appliance.html.haml +++ b/app/views/application/_footer_appliance.html.haml @@ -3,12 +3,12 @@