From ff680baa51e352a43396b5d81d1726acf60924ec Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Mon, 13 May 2024 12:44:40 -0700 Subject: [PATCH 01/61] Refactor the account creation page --- app/assets/stylesheets/account.scss | 30 +++++++++++++ app/assets/stylesheets/application.css.scss | 1 + app/controllers/users_controller.rb | 27 +++--------- app/views/users/new.html.haml | 49 +++++++++++++++++---- 4 files changed, 78 insertions(+), 29 deletions(-) create mode 100644 app/assets/stylesheets/account.scss diff --git a/app/assets/stylesheets/account.scss b/app/assets/stylesheets/account.scss new file mode 100644 index 000000000..525a78c0a --- /dev/null +++ b/app/assets/stylesheets/account.scss @@ -0,0 +1,30 @@ +.signup { + display: flex; + align-items: center; + justify-content: center; + padding-top: 40px; + padding-bottom: 40px; +} + +.form-signup { + width: 100%; + max-width: 400px; + padding: 15px; + margin: 0 auto; + + a { + text-decoration: none; + } + + a:hover { + text-decoration: underline; + } + + h4, p { + text-align: center; + } +} + +.form-signup .enable-lists { + color: red; +} diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 99eabc57e..8843c1dc8 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -24,6 +24,7 @@ /* BioPortal */ @import "admin"; +@import "account"; @import "annotator"; @import "bioportal"; @import "concepts"; diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 4d769260d..7c2a6b504 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -60,19 +60,14 @@ def create if response_error?(@user_saved) @errors = response_errors(@user_saved) # @errors = {acronym: "Username already exists, please use another"} if @user_saved.status == 409 - render action: "new" + render 'new' else - # Attempt to register user to list - if params[:user][:register_mail_list] - Notifier.register_for_announce_list(@user.email).deliver rescue nil - end - flash[:notice] = 'Account was successfully created' session[:user] = LinkedData::Client::Models::User.authenticate(@user.username, @user.password) redirect_to_browse end else - render action: "new" + render 'new' end end @@ -156,8 +151,7 @@ def custom_ontologies private def user_params - p = params.require(:user).permit(:firstName, :lastName, :username, :email, :email_confirmation, :password, - :password_confirmation, :register_mail_list, :admin) + p = params.require(:user).permit(:firstName, :lastName, :username, :email, :password, :admin) p.to_h end @@ -183,21 +177,12 @@ def get_ontology_list(ont_hash) def validate(params) errors = [] - if params[:email].nil? || params[:email].length < 1 || !params[:email].match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i) - errors << "Please enter an email address" - end - if !params[:email].eql?(params[:email_confirmation]) - errors << "Your Email and Email Confirmation do not match" - end - if params[:password].nil? || params[:password].length < 1 - errors << "Please enter a password" - end - if !params[:password].eql?(params[:password_confirmation]) - errors << "Your Password and Password Confirmation do not match" + if params[:email].length < 1 || !params[:email].match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i) + errors << "invalid email address" end if using_captcha? if !verify_recaptcha - errors << "Please fill in the proper text from the supplied image" + errors << "reCAPTCHA verification failed, please try again" end end diff --git a/app/views/users/new.html.haml b/app/views/users/new.html.haml index 8f09e6e47..de7a11c26 100644 --- a/app/views/users/new.html.haml +++ b/app/views/users/new.html.haml @@ -1,12 +1,45 @@ - @title = "Register" -%div{:style => "padding: 1em;"} - %h1 - New #{$SITE} Account - = form_for(:user, :url => users_path) do |f| - - unless @errors.nil? - .enable-lists{:style => "color: red; padding: 1em;"} - Errors creating your account: + +%div.signup + = form_with scope: :user, url: users_path, class: 'form-signup' do |f| + %h4.mb-4 Create an account + + -# TODO: display errors in a standard Rails flash message + - if @errors.present? + %div.enable-lists + Errors creating account: %ul - for error in @errors %li= error - = render :partial => 'form', :locals => {:f => f} + + %div.form-floating.mb-3 + = f.text_field(:firstName, class: 'form-control', placeholder: 'First name', required: 'true') + = f.label(:firstName, 'First name', class: 'form-label') + + %div.form-floating.mb-3 + = f.text_field(:lastName, class: 'form-control', placeholder: 'Last name (optional)') + = f.label(:lastName, 'Last name (optional)', class: 'form-label') + + %div.form-floating.mb-3 + = f.text_field(:username, class: 'form-control', placeholder: 'Username', required: 'true') + = f.label(:username, class: 'form-label') + + %div.form-floating.mb-3 + = f.email_field(:email, class: 'form-control', placeholder: 'Email address', required: 'true') + = f.label(:email, class: 'form-label') + + %div.form-floating.mb-3 + = f.password_field(:password, class: 'form-control', placeholder: 'Password', required: 'true', + autocomplete: 'new-password') + = f.label(:password, class: 'form-label') + + - if using_captcha? + %div.mb-4 + = recaptcha_tags + + %div.d-grid + = submit_tag('Sign Up', class: 'btn btn-lg btn-primary btn-block') + + %p.text-muted.mt-2 + Already have an account? + = link_to('Log in', login_index_path) From 9ea90edfa08126f45212cb7a4c8e3605add97c39 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Mon, 13 May 2024 12:46:59 -0700 Subject: [PATCH 02/61] Remove superfluous comments --- app/controllers/users_controller.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 7c2a6b504..3e5bf6941 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -5,8 +5,6 @@ class UsersController < ApplicationController layout :determine_layout - # GET /users - # GET /users.xml def index @users = LinkedData::Client::Models::User.all respond_to do |format| @@ -15,8 +13,6 @@ def index end end - # GET /users/1 - # GET /users/1.xml def show @user = if session[:user].admin? && params.has_key?(:id) LinkedData::Client::Models::User.find_by_username(params[:id]).first @@ -34,12 +30,10 @@ def show @user_projects = projects.select {|p| p.creator.include? @user.id } end - # GET /users/new def new @user = LinkedData::Client::Models::User.new end - # GET /users/1;edit def edit @user = LinkedData::Client::Models::User.find(params[:id]) @user ||= LinkedData::Client::Models::User.find_by_username(params[:id]).first @@ -49,8 +43,6 @@ def edit end end - # POST /users - # POST /users.xml def create @errors = validate(user_params) @user = LinkedData::Client::Models::User.new(values: user_params) @@ -71,8 +63,6 @@ def create end end - # PUT /users/1 - # PUT /users/1.xml def update @user = LinkedData::Client::Models::User.find(params[:id]) @user = LinkedData::Client::Models::User.find_by_username(params[:id]).first if @user.nil? @@ -109,7 +99,6 @@ def update end end - # DELETE /users/1 def destroy response = {errors: '', success: ''} @user = LinkedData::Client::Models::User.find(params[:id]) From a11aa39d5310bc0d513259e547471e07ccbbf869 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Mon, 13 May 2024 13:06:15 -0700 Subject: [PATCH 03/61] Fix some RuboCop warnings --- app/controllers/users_controller.rb | 46 ++++++++++++++++------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 3e5bf6941..68f50f49b 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UsersController < ApplicationController before_action :unescape_id, only: [:edit, :show, :update] before_action :verify_owner, only: [:edit, :show] @@ -23,11 +25,11 @@ def show @user_ontologies = @user.customOntology ## Copied from home controller , account action - onts = LinkedData::Client::Models::Ontology.all; - @admin_ontologies = onts.select {|o| o.administeredBy.include? @user.id } + onts = LinkedData::Client::Models::Ontology.all + @admin_ontologies = onts.select { |o| o.administeredBy.include? @user.id } - projects = LinkedData::Client::Models::Project.all; - @user_projects = projects.select {|p| p.creator.include? @user.id } + projects = LinkedData::Client::Models::Project.all + @user_projects = projects.select { |p| p.creator.include? @user.id } end def new @@ -38,7 +40,7 @@ def edit @user = LinkedData::Client::Models::User.find(params[:id]) @user ||= LinkedData::Client::Models::User.find_by_username(params[:id]).first - if (params[:password].eql?("true")) + if (params[:password].eql?('true')) @user.validate_password = true end end @@ -47,7 +49,7 @@ def create @errors = validate(user_params) @user = LinkedData::Client::Models::User.new(values: user_params) - if @errors.size < 1 + if @errors.empty? @user_saved = @user.save if response_error?(@user_saved) @errors = response_errors(@user_saved) @@ -67,7 +69,7 @@ def update @user = LinkedData::Client::Models::User.find(params[:id]) @user = LinkedData::Client::Models::User.find_by_username(params[:id]).first if @user.nil? @errors = validate_update(user_params) - if @errors.size < 1 + if @errors.empty? if params[:user][:password] error_response = @user.update(values: { password: params[:user][:password] }) @@ -85,7 +87,7 @@ def update if response_error?(error_response) @errors = response_errors(error_response) # @errors = {acronym: "Username already exists, please use another"} if error_response.status == 409 - render action: "edit" + render 'edit' else flash[:notice] = 'Account was successfully updated' @@ -95,15 +97,15 @@ def update redirect_to user_path(@user.username) end else - render action: "edit" + render 'edit' end end def destroy - response = {errors: '', success: ''} + response = { errors: String.new(''), success: String.new('') } @user = LinkedData::Client::Models::User.find(params[:id]) @user = LinkedData::Client::Models::User.find_by_username(params[:id]).first if @user.nil? - if(session[:user].admin?) + if session[:user].admin? @user.delete response[:success] << 'User deleted successfully ' @@ -150,44 +152,46 @@ def unescape_id def verify_owner return if current_user_admin? + if session[:user].nil? || (!session[:user].id.eql?(params[:id]) && !session[:user].username.eql?(params[:id])) redirect_to controller: 'login', action: 'index', redirect: "/accounts/#{params[:id]}" end end def get_ontology_list(ont_hash) - return "" if ont_hash.nil? + return '' if ont_hash.nil? + ontologies = [] ont_hash.each do |ont, checked| ontologies << ont if checked.to_i == 1 end - ontologies.join(";") + ontologies.join(';') end def validate(params) errors = [] if params[:email].length < 1 || !params[:email].match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i) - errors << "invalid email address" + errors << 'invalid email address' end if using_captcha? if !verify_recaptcha - errors << "reCAPTCHA verification failed, please try again" + errors << 'reCAPTCHA verification failed, please try again' end end - return errors + errors end def validate_update(params) errors = [] if params[:email].nil? || params[:email].length < 1 || !params[:email].match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i) - errors << "Please enter an email address" + errors << 'Please enter an email address' end if !params[:password].eql?(params[:password_confirmation]) - errors << "Your Password and Password Confirmation do not match" + errors << 'Your Password and Password Confirmation do not match' end - return errors + errors end def update_role(user) @@ -196,9 +200,9 @@ def update_role(user) if session[:user].admin? user_roles = user_roles.dup if user.admin? - user_roles.map!{ |role| role == "ADMINISTRATOR" ? "LIBRARIAN" : role} + user_roles.map! { |role| role == 'ADMINISTRATOR' ? 'LIBRARIAN' : role } else - user_roles.map!{ |role| role == "LIBRARIAN" ? "ADMINISTRATOR" : role} + user_roles.map! { |role| role == 'LIBRARIAN' ? 'ADMINISTRATOR' : role } end end From 74d4bd16c5d7d665746eec9aa3373d2e9f075f0c Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Fri, 14 Jun 2024 16:39:53 -0700 Subject: [PATCH 04/61] Display GitHub and ORCID identifiers --- app/views/users/show.html.haml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index c4d7866b8..37ef8d14e 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -14,6 +14,12 @@ %tr %th Account Name: %td= @user.username + %tr + %th GitHub username: + %td= @user.githubId + %tr + %th ORCID iD: + %td= @user.orcidId %tr %td{colspan: "2", style: "text-align: right;"} = link_to "Edit Information", edit_user_path(url_encode(@user.username)) From 881fb0d81e8eafb3b03fc4cbe899a2f3d12a862b Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 19 Jun 2024 16:36:38 -0700 Subject: [PATCH 05/61] Create users_helper.rb Add helper methods for text associated with custom ontology sets. --- app/helpers/users_helper.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 app/helpers/users_helper.rb diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 000000000..25f5fa46f --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module UsersHelper + def custom_ontology_set_intro_text + tag.div do + concat(tag.p do + concat(tag.span('Customize your display: ', class: 'fw-bold text-muted')) + concat(tag.span("pick the ontologies you want to see and #{$SITE} will hide all other ontologies.", + class: 'text-muted')) + end) + concat(tag.p('Please note: you must be logged in to use this feature', class: 'fst-italic text-muted')) + end + end + + def custom_ontology_set_slice_text + tag.p class: 'mb-5' do + concat('Please visit the ') + concat(link_to('main site', "#{$UI_URL}/account")) + concat(' to modify your custom ontology set') + end + end +end From 3cc788ff2d16310a85387dbf6b203d56d2c3d447 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Mon, 1 Jul 2024 12:20:37 -0700 Subject: [PATCH 06/61] Refactor account show and edit pages --- app/assets/stylesheets/account.scss | 23 ++++ app/controllers/home_controller.rb | 12 +- app/controllers/passwords_controller.rb | 45 +++++++ app/controllers/users_controller.rb | 27 ++-- app/helpers/application_helper.rb | 3 +- app/views/passwords/edit.html.haml | 18 +++ app/views/users/edit.html.haml | 170 ++++++++---------------- app/views/users/show.html.haml | 123 ++++++++--------- config/routes.rb | 2 + 9 files changed, 230 insertions(+), 193 deletions(-) create mode 100644 app/controllers/passwords_controller.rb create mode 100644 app/views/passwords/edit.html.haml diff --git a/app/assets/stylesheets/account.scss b/app/assets/stylesheets/account.scss index 525a78c0a..f371532d9 100644 --- a/app/assets/stylesheets/account.scss +++ b/app/assets/stylesheets/account.scss @@ -28,3 +28,26 @@ .form-signup .enable-lists { color: red; } + +.edit-user-info { + display: flex; + align-items: center; + justify-content: center; + + form { + width: 100%; + max-width: 640px; + padding: 15px; + margin: 0 auto; + } +} + +.account-info { + a { + text-decoration: none; + } + + a:hover { + text-decoration: underline; + } +} \ No newline at end of file diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index eca05fcba..c0d98a519 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -107,11 +107,15 @@ def account @user_ontologies = @user.customOntology @user_ontologies ||= [] - onts = LinkedData::Client::Models::Ontology.all - @admin_ontologies = onts.select { |o| o.administeredBy.include? @user.id } + @admin_ontologies = LinkedData::Client::Models::Ontology.where do |o| + o.administeredBy.include? @user.id + end + @admin_ontologies.sort! { |a, b| a.name.downcase <=> b.name.downcase } - projects = LinkedData::Client::Models::Project.all - @user_projects = projects.select { |p| p.creator.include? @user.id } + @user_projects = LinkedData::Client::Models::Project.where do |p| + p.creator.include? @user.id + end + @user_projects.sort! { |a, b| a.name.downcase <=> b.name.downcase } render 'users/show' end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb new file mode 100644 index 000000000..d6127809d --- /dev/null +++ b/app/controllers/passwords_controller.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +class PasswordsController < ApplicationController + before_action :require_logged_in_user + before_action :set_user + + layout :determine_layout + + def edit; end + + def update + if params[:password] != params[:password_confirmation] + flash.now[:warning] = 'New password and password confirmation do not match. Please try again.' + render 'edit' + return + end + + response = @user.update(values: { password: params[:password] }) + if response_error?(response) + @errors = response_errors(response) + render 'edit' + else + flash[:success] = 'Password successfully updated!' + redirect_to user_path(@user.username) + end + end + + private + + def password_params + p = params.permit(:password, :password_confirmation) + p.to_h + end + + def require_logged_in_user + if session[:user].blank? + flash[:warning] = 'You must be logged in to access that page' + redirect_to login_index_path + end + end + + def set_user + @user = session[:user] + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 68f50f49b..50c2e14ea 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -24,12 +24,16 @@ def show @all_ontologies = LinkedData::Client::Models::Ontology.all(ignore_custom_ontologies: true) @user_ontologies = @user.customOntology - ## Copied from home controller , account action - onts = LinkedData::Client::Models::Ontology.all - @admin_ontologies = onts.select { |o| o.administeredBy.include? @user.id } + # Copied from home controller, account action + @admin_ontologies = LinkedData::Client::Models::Ontology.where do |o| + o.administeredBy.include? @user.id + end + @admin_ontologies.sort! { |a, b| a.name.downcase <=> b.name.downcase } - projects = LinkedData::Client::Models::Project.all - @user_projects = projects.select { |p| p.creator.include? @user.id } + @user_projects = LinkedData::Client::Models::Project.where do |p| + p.creator.include? @user.id + end + @user_projects.sort! { |a, b| a.name.downcase <=> b.name.downcase } end def new @@ -40,7 +44,7 @@ def edit @user = LinkedData::Client::Models::User.find(params[:id]) @user ||= LinkedData::Client::Models::User.find_by_username(params[:id]).first - if (params[:password].eql?('true')) + if params[:password].eql? 'true' @user.validate_password = true end end @@ -89,7 +93,7 @@ def update # @errors = {acronym: "Username already exists, please use another"} if error_response.status == 409 render 'edit' else - flash[:notice] = 'Account was successfully updated' + flash[:notice] = 'Account successfully updated!' if session[:user].username == @user.username session[:user].update_from_params(user_params) @@ -142,7 +146,8 @@ def custom_ontologies private def user_params - p = params.require(:user).permit(:firstName, :lastName, :username, :email, :password, :admin) + p = params.require(:user).permit(:firstName, :lastName, :username, :email, :password, :password_confirmation, + :admin, :githubId, :orcidId) p.to_h end @@ -184,11 +189,11 @@ def validate(params) def validate_update(params) errors = [] - if params[:email].nil? || params[:email].length < 1 || !params[:email].match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i) - errors << 'Please enter an email address' + if params[:email].length < 1 || !params[:email].match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i) + errors << 'invalid email address' end if !params[:password].eql?(params[:password_confirmation]) - errors << 'Your Password and Password Confirmation do not match' + errors << 'Your password and password confirmation do not match' end errors diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3948fe3a2..4ad4a0de2 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -321,7 +321,8 @@ def flash_class(level) 'notice' => 'alert-info', 'success' => 'alert-success', 'error' => 'alert-danger', - 'alert' => 'alert-danger' + 'alert' => 'alert-danger', + 'warning' => 'alert-warning' } bootstrap_alert_class[level] end diff --git a/app/views/passwords/edit.html.haml b/app/views/passwords/edit.html.haml new file mode 100644 index 000000000..83b776e7d --- /dev/null +++ b/app/views/passwords/edit.html.haml @@ -0,0 +1,18 @@ +%div.container + %div.edit-user-info + = form_with url: password_path, method: 'patch', class: 'my-4' do |f| + - if @errors.present? + %div{class: 'alert alert-danger mb-4'} + - for error in @errors + %div= error + %h2{class: 'pb-2 border-bottom'} Change password + = tag.p "Enter a new password for your #{$SITE} account (#{@user.username})", class: 'text-muted mb-4' + %div.mb-3 + = f.label :password, 'New password', class: 'form-label' + = f.password_field :password, class: 'form-control', required: 'true' + %div.mb-4 + = f.label :password_confirmation, 'Confirm new password', class: 'form-label' + = f.password_field :password_confirmation, class: 'form-control', required: 'true' + %div + = submit_tag('Update', class: 'btn btn-primary me-1') + = link_to 'Cancel', user_path(@user.username), class: 'btn btn-primary' diff --git a/app/views/users/edit.html.haml b/app/views/users/edit.html.haml index cd3f31a33..c3875a604 100644 --- a/app/views/users/edit.html.haml +++ b/app/views/users/edit.html.haml @@ -1,32 +1,5 @@ -:css - .required { - color: black; - } - table.form { - width: auto; - } - table.form td { - width: 500px; - } - :javascript - /* refer to input fields by name for rules */ - /* refer to input fields by id for equalTo */ jQuery(document).ready(function() { - jQuery("#user_info").validate({ - rules: { - "user[email]": { - required: true, - email: true, - }, - "user[password]": "required", - "user[password_confirmation]": { - required: true, - equalTo: "#user_password", - } - } - }) - jQuery('#user_info').on('submit', function (event) { event.preventDefault() let admin_checkbox = jQuery('input[id=user_admin]') @@ -52,94 +25,65 @@ }) }); - (function (jQuery) { - // custom css expression for a case-insensitive contains() - jQuery.expr[':'].Contains = function(a,i,m){ - return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0; - }; - - function listFilter(header, list) { // header is any element, list is an unordered list - // create and add the filter form to the header - var form = jQuery("
").attr({"class":"filterform","action":"#"}), - input = jQuery("").attr({"class":"filterinput","type":"text"}); - jQuery(form).append(input).appendTo(header); - - jQuery(input) - .change( function () { - var filter = jQuery(this).val(); - if(filter) { - // this finds all links in a list that contain the input, - // and hide the ones not containing the input while showing the ones that do - jQuery(list).find("a:not(:Contains(" + filter + "))").closest("li").hide(); - jQuery(list).find("a:Contains(" + filter + ")").closest("li").show(); - } else { - jQuery(list).find("li").show(); - } - return false; - }) - .keyup( function () { - // fire the above change event after every letter - jQuery(this).change(); - }); - } - }) - - - -%div{:style => "padding: 1em;"} - = form_for(:user, :url => user_path(@user.username), :html => { :id => "user_info", :method => :put }) do |f| - - unless @errors.nil? - .enable-lists{:style => "color: red; padding: 1em;"} - Errors creating your account: - %ul - - for error in @errors - %li= error - %table.form - - if @user.validate_password.nil? - %tr - %th - First Name - %td.top= text_field :user, :firstName, value: @user.firstName - %tr - %th - Last Name - %td= text_field :user, :lastName, value: @user.lastName - %tr - %th - Email Address * - %td - = text_field :user, :email, value: @user.email +%div.container + %div.edit-user-info + = form_with scope: :user, url: user_path(@user.username), method: 'put', id: 'user_info', class: 'my-4' do |f| + - if @errors.present? + .enable-lists + Errors updating account: + %ul + - for error in @errors + %li= error + - if @user.validate_password.blank? + %h2.mb-4 Edit account + %div.mb-3 + = f.label :firstName, 'First name', class: 'form-label' + = f.text_field :firstName, class: 'form-control', required: 'true' + %div.mb-3 + = f.label :lastName, 'Last name', class: 'form-label' + = f.text_field :lastName, class: 'form-control' + %div.mb-3 + = f.label :username, class: 'form-label' + = f.text_field :username, class: 'form-control', disabled: 'true' + %div.mb-3 + = f.label :email, 'Email address', class: 'form-label' + = f.email_field :email, class: 'form-control', required: 'true' + %div.mb-3 + = f.label :githubId, 'GitHub username', class: 'form-label' + = f.text_field :githubId, class: 'form-control', 'aria-describedby': 'githubUsernameHelp', + pattern: '[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}', + title: 'Characters: only alphanumeric and single hyphens. Length: 1-39' + %div.form-text{id: 'githubUsernameHelp'} + User handle on GitHub, for example: 'octocat' + %div.mb-3 + = f.label :orcidId, 'ORCID iD', class: 'form-label' + = f.text_field :orcidId, class: 'form-control', 'aria-describedby': 'orcidHelp', + pattern: '\d{4}-\d{4}-\d{4}-\d{4}', title: 'Format: XXXX-XXXX-XXXX-XXXX' + %div.form-text{id: 'orcidHelp'} + 16-digit ORCID, for example: 0000-1234-5678-9101 - if session[:user].admin? - %tr - %th - Grant admin privileges - %td - = check_box :user, :admin?, { class: (@user.admin? ? 'admin' : '') } - \   - = hidden_field :user, :username, value: @user.username + %div.form-check.mb-3 + = f.check_box :admin?, class: (@user.admin? ? 'admin form-check-input' : 'form-check-input') + = f.label :admin, 'Grant admin privileges', class: 'form-check-label' + %div.mb-5 + = submit_tag('Update', class: 'btn btn-primary me-1') + = link_to 'Cancel', user_path(@user.username), class: 'btn btn-primary' - else - = hidden_field :user, :username, value: @user.username + %h3{class: 'pb-2 mb-3 border-bottom'} + Password + = tag.p("Change the password for account: #{@user.username}", class: 'text-muted') = hidden_field :user, :firstName, value: @user.firstName = hidden_field :user, :lastName, value: @user.lastName + = hidden_field :user, :username, value: @user.username = hidden_field :user, :email, value: @user.email - %tr - %th - Password: * - %td.top - = password_field :user, :password -    - %tr - %th - Re-enter Password: * - %td - = password_field :user, :password_confirmation -    - - %tr - %td{:align => "left", :colspan => "2"} - = submit_tag "Update", :class=>"greenbtn btn btn-outline-secondary btn-sm " - \     - - unless params[:password].eql?("true") - = link_to "Change Password", edit_user_path(@user.username, password: true) - -# use @user.username unstead of sessions - %br/ + = hidden_field :user, :githubId, value: @user.githubId + = hidden_field :user, :orcidId, value: @user.orcidId + %div.mb-3 + = f.label :password, 'New password', class: 'form-label' + = f.password_field :password, class: 'form-control', required: 'true' + %div.mb-4 + = f.label :password_confirmation, 'Confirm new password', class: 'form-label' + = f.password_field :password_confirmation, class: 'form-control', required: 'true' + %div + = submit_tag('Change password', class: 'btn btn-primary me-1') + = link_to 'Cancel', user_path(@user.username), class: 'btn btn-primary' diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 37ef8d14e..774f214d3 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,73 +1,79 @@ - @title = "Account Information" -%div{style: "padding: 1em;"} - %table.form +%div{class: 'container account-info my-4'} + %h4{class: 'pb-2 mb-4 border-bottom'} + Account basics + = tag.small("(#{@user.username})", class: 'text-muted') + + %table.table %tr - %th.top First Name: - %td.top= @user.firstName + %th First name + %td= @user.firstName %tr - %th Last Name: + %th Last name %td= @user.lastName %tr - %th Email Address: + %th Email %td= @user.email %tr - %th Account Name: - %td= @user.username - %tr - %th GitHub username: + %th GitHub username %td= @user.githubId %tr - %th ORCID iD: + %th ORCID iD %td= @user.orcidId - %tr - %td{colspan: "2", style: "text-align: right;"} - = link_to "Edit Information", edit_user_path(url_encode(@user.username)) - %br/ - %span{style: "font-size: large; font-weight: bold;"} - API Key - %span{style: "font-weight: normal; padding-left: 1em; font-size: x-small; color: gray;"} Your API Key can be used to access the NCBO API (REST) Services - %p{style: "padding: .5em 0 0 1em;"} - = session[:user].apikey - %br/ + %div{class: 'd-flex justify-content-end'} + = link_to('Edit account', edit_user_path(@user.username), class: 'btn btn-sm btn-outline-secondary') + -# = link_to('Change password', edit_user_path(@user.username, password: true), + -# class: 'btn btn-sm btn-outline-secondary ms-2') + = link_to('Change password', edit_password_path, class: 'btn btn-sm btn-outline-secondary ms-2') - - unless @user.subscription.nil? || @user.subscription.empty? - %h2 Subscriptions - %table + -# User's API key + %h4{class: 'pb-2 my-4 border-bottom'} API Key + = tag.p("Your API key can be used to access the #{$SITE} RESTful API and services", class: 'text-muted') + %p= session[:user].apikey + + -# User's subscriptions + - unless @user.subscription.blank? + %h4{class: 'pb-2 mt-5 mb-4 border-bottom'} Subscriptions + %table.table + %thead.table-light + %tr + %th Ontology + %th Subscription type + %th   - @user.subscription.each do |subscription| - ont = (!subscription[:ontology].nil? ? subscription[:ontology].split('/').last: nil) # ensure we get the acronym - type = (!subscription[:notification_type].nil? ? subscription[:notification_type].downcase : nil) - %tr{style: "padding: 5px;"} - %td{style: "padding: 5px;"} - %a{href: "/ontologies/#{ont}"}= ont - %td{style: "padding: 5px;"} - - if type == "notes" - %a{href: "/ontologies/#{ont}?p=notes"}= type + %tr + %td + = link_to(ont, ontology_path(ont)) + %td + - if type == 'notes' + = link_to(type, ontology_path(ont, p: 'notes')) - else = type - %td{style: "padding: 5px;"} - .subscribe_to_ontology{style: "float: left;"} + %td + .subscribe_to_ontology = raw subscribe_ontology_button(ont, @user) - %br/ - - unless @user_projects.nil? || @user_projects.empty? - %h2#user_projects{style: "font-size: large;"} - Projects Created - %ul{style: "padding-left: 1em;"} - - @user_projects.each do |project| + -# Projects this user created + - unless @user_projects.blank? + %h4{class: 'pb-2 mt-5 mb-4 border-bottom'} Projects + = tag.p("Projects you created in #{$SITE}", class: 'text-muted') + %ul.list-unstyled + - @user_projects.each do |p| %li - %a{href: "/projects/#{project.acronym}"}= project.name - %br/ + = link_to(p.name, project_path(p.acronym)) - - unless @admin_ontologies.nil? || @admin_ontologies.empty? - %h2#admin_ontologies{style: "font-size: large;"} - Submitted Ontologies - %ul{style: "padding-left: 1em;"} + -# Ontologies this user administers + - unless @admin_ontologies.blank? + %h4{class: 'pb-2 mt-5 mb-4 border-bottom'} Ontologies + = tag.p("Ontologies you have administrative access to in #{$SITE}", class: 'text-muted') + %ul.list-unstyled - @admin_ontologies.each do |ont| %li - %a{href: "/ontologies/#{ont.acronym}"}= ont.name - %br + = link_to(ont.name, ontology_path(ont.acronym)) :javascript jQuery(document).ready(function(){ @@ -112,33 +118,22 @@ jQuery("#custom_ontologies").hide(); } - %h2#custom_ontology_set{style: "font-size: large;"} - Custom Ontology Set + %h4{class: 'pb-2 mt-5 mb-4 border-bottom'} Custom ontology set #custom_ontologies.enable-lists - if at_slice? - %p{style: "padding-left: 7px; font-size: 10pt; margin: -3px 0 7px;"} - Please - %a{href: "#{$UI_URL}/account"} visit the main site - to modify your Custom Ontology Set. + = custom_ontology_set_slice_text - else - %p{style: "padding-left: 7px; font-size: 9pt; color: gray; margin: -3px 0 7px;"} - %b - Customize your #{$SITE} display: - Pick the ontologies that you want to see and #{$SITE} will hide all other ontologies.
- %b - Note: - this feature works only when you are logged in. - %p - %span{style: "font-weight: normal; font-size: 9pt; padding-left: 7px;"} - %a#edit_custom_ontologies{href: "javascript:void(0);"} select ontologies - - if @user_ontologies && !@user_ontologies.empty? + = custom_ontology_set_intro_text + = button_tag('Select ontologies', type: 'button', id: 'edit_custom_ontologies', class: 'btn btn-primary') + - if @user_ontologies.present? %ul - @user_ontologies.each do |ont| - ont = LinkedData::Client::Models::Ontology.get(ont) %li #{ont.name} (#{ont.acronym}) - else - %p{style: "padding-left: 7px;"} You haven't picked any ontologies yet + = tag.p("You haven't picked any ontologies yet", class: 'mt-3 mb-5') + #custom_ontologies_picker{style: "left: -9999px; position: absolute;"} = form_tag custom_ontologies_path(url_encode(@user.username)) do - selected = @user.customOntology.map {|o| LinkedData::Client::Models::Ontology.get(o).acronym} diff --git a/config/routes.rb b/config/routes.rb index f26fb3ad8..4e126f8bf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -107,6 +107,8 @@ post '/accounts/:id/custom_ontologies' => 'users#custom_ontologies', :as => :custom_ontologies get '/login_as/:login_as' => 'login#login_as', constraints: { login_as: /[\d\w\.\-\%\+ ]+/ } post '/login/send_pass', to: 'login#send_pass' + get 'password', to: 'passwords#edit', as: :edit_password + patch 'password', to: 'passwords#update' # Search get 'search', to: 'search#index' From 74b8471a6e2d0d544386d4f9099e67fdc75cd53f Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Tue, 2 Jul 2024 12:17:52 -0700 Subject: [PATCH 07/61] Remove the unused iconv gem Resolves #320 --- Gemfile | 1 - Gemfile.lock | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 710616a59..44ea7be0d 100644 --- a/Gemfile +++ b/Gemfile @@ -53,7 +53,6 @@ gem 'graphql', '~> 2.0.27' gem 'graphql-client' gem 'haml', '~> 5.1' gem 'i18n' -gem 'iconv' gem 'iso-639', '~> 0.3.6' gem 'multi_json' gem 'mysql2', '0.5.5' diff --git a/Gemfile.lock b/Gemfile.lock index e8efaa91b..7ad70af20 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -179,7 +179,6 @@ GEM domain_name (~> 0.5) i18n (1.14.1) concurrent-ruby (~> 1.0) - iconv (1.0.8) io-console (0.7.2) irb (1.12.0) rdoc @@ -394,6 +393,7 @@ GEM PLATFORMS arm64-darwin-22 + arm64-darwin-23 x86_64-darwin-21 x86_64-linux @@ -422,7 +422,6 @@ DEPENDENCIES haml (~> 5.1) html2haml i18n - iconv iso-639 (~> 0.3.6) jquery-rails jquery-ui-rails From 2d218925b7a1627007cbb73da224a0878e926024 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 3 Jul 2024 16:20:55 -0700 Subject: [PATCH 08/61] Render the new password edit page for resets --- app/controllers/login_controller.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/login_controller.rb b/app/controllers/login_controller.rb index 2a9587268..d8b70c04b 100755 --- a/app/controllers/login_controller.rb +++ b/app/controllers/login_controller.rb @@ -95,9 +95,8 @@ def reset_password token = params[:tk] @user = LinkedData::Client::HTTP.post("/users/reset_password", {username: username, email: email, token: token}) if @user.is_a?(LinkedData::Client::Models::User) - @user.validate_password = true login(@user) - render "users/edit" + render 'passwords/edit' else flash[:notice] = @user.errors.first + ". Please reset your password again." redirect_to "/lost_pass" From 2f0f39d748bde0c92208c74843c98d5fb117c468 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 3 Jul 2024 16:47:17 -0700 Subject: [PATCH 09/61] Fix some RuboCop warnings --- app/controllers/login_controller.rb | 58 ++++++++++++++--------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/app/controllers/login_controller.rb b/app/controllers/login_controller.rb index d8b70c04b..53aad8a26 100755 --- a/app/controllers/login_controller.rb +++ b/app/controllers/login_controller.rb @@ -1,5 +1,6 @@ -class LoginController < ApplicationController +# frozen_string_literal: true +class LoginController < ApplicationController layout :determine_layout def index @@ -7,7 +8,7 @@ def index if params[:redirect] # Get the original, encoded redirect uri = URI.parse(request.url) - orig_params = Hash[uri.query.split("&").map {|e| e.split("=",2)}].symbolize_keys + orig_params = Hash[uri.query.split("&").map { |e| e.split("=", 2) }].symbolize_keys session[:redirect] = orig_params[:redirect] else session[:redirect] = request.referer @@ -17,31 +18,30 @@ def index # logs in a user def create @errors = validate(params[:user]) - if @errors.size < 1 + if @errors.empty? logged_in_user = LinkedData::Client::Models::User.authenticate(params[:user][:username], params[:user][:password]) if logged_in_user && !logged_in_user.errors login(logged_in_user) - redirect = "/" + redirect = '/' if session[:redirect] redirect = CGI.unescape(session[:redirect]) end - redirect_to redirect else - @errors << "Invalid account name/password combination" - render :action => 'index' + @errors << 'Invalid account name/password combination' + render action: 'index' end else - render :action => 'index' + render action: 'index' end end # Login as the provided username (only for admin users) def login_as unless session[:user] && session[:user].admin? - redirect_to "/" + redirect_to '/' return end @@ -54,8 +54,8 @@ def login_as session[:user].apikey = session[:admin_user].apikey end - #redirect_to request.referer rescue redirect_to "/" - redirect_to "/" + # redirect_to request.referer rescue redirect_to "/" + redirect_to '/' end # logs out a user @@ -67,25 +67,24 @@ def destroy flash[:success] = "Logged out #{old_user.username}, returned to #{session[:user].username}".html_safe else session[:user] = nil - flash[:success] = "You have successfully logged out" + flash[:success] = 'You have successfully logged out' end - redirect_to request.referer || "/" + redirect_to request.referer || '/' end - def lost_password - end + def lost_password; end # Sends a new password to the user def send_pass username = params[:user][:account_name] email = params[:user][:email] - resp = LinkedData::Client::HTTP.post("/users/create_reset_password_token", {username: username, email: email}) + resp = LinkedData::Client::HTTP.post('/users/create_reset_password_token', { username: username, email: email }) if resp.nil? - redirect_to login_index_path, notice: "Please check your email for a message with reset instructions" + redirect_to login_index_path, notice: 'Please check your email for a message with reset instructions' else - flash[:notice] = resp.errors.first + ". Please try again." - redirect_to "/lost_pass" + flash[:notice] = "#{resp.errors.first}. Please try again." + redirect_to '/lost_pass' end end @@ -93,13 +92,13 @@ def reset_password username = params[:un] email = params[:em] token = params[:tk] - @user = LinkedData::Client::HTTP.post("/users/reset_password", {username: username, email: email, token: token}) + @user = LinkedData::Client::HTTP.post('/users/reset_password', { username: username, email: email, token: token }) if @user.is_a?(LinkedData::Client::Models::User) login(@user) render 'passwords/edit' else - flash[:notice] = @user.errors.first + ". Please reset your password again." - redirect_to "/lost_pass" + flash[:notice] = "#{@user.errors.first}. Please reset your password again." + redirect_to '/lost_pass' end end @@ -107,6 +106,7 @@ def reset_password def login(user) return unless user + session[:user] = user custom_ontologies_text = session[:user].customOntology && !session[:user].customOntology.empty? ? "The display is now based on your Custom Ontology Set." : "" notice = "Welcome " + user.username.to_s + "! " + custom_ontologies_text @@ -114,17 +114,15 @@ def login(user) end def validate(params) - errors=[] + errors = [] - if params[:username].nil? || params[:username].length <1 - errors << "Please enter an account name" + if params[:username].empty? + errors << 'Please enter an account name' end - if params[:password].nil? || params[:password].length <1 - errors << "Please enter a password" + if params[:password].empty? + errors << 'Please enter a password' end - return errors + errors end - - end From 2faa257fa4e25bc113429836984fe46835f26eeb Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 3 Jul 2024 17:07:11 -0700 Subject: [PATCH 10/61] Use newer style syntax for render --- app/controllers/login_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/login_controller.rb b/app/controllers/login_controller.rb index 53aad8a26..6c331fb19 100755 --- a/app/controllers/login_controller.rb +++ b/app/controllers/login_controller.rb @@ -31,10 +31,10 @@ def create redirect_to redirect else @errors << 'Invalid account name/password combination' - render action: 'index' + render 'index' end else - render action: 'index' + render 'index' end end From 9605a9c8532d3942508245903c869974805c392c Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 3 Jul 2024 17:46:28 -0700 Subject: [PATCH 11/61] Rename login index with HAML extension --- app/views/login/{index.html.erb => index.html.haml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/views/login/{index.html.erb => index.html.haml} (100%) diff --git a/app/views/login/index.html.erb b/app/views/login/index.html.haml similarity index 100% rename from app/views/login/index.html.erb rename to app/views/login/index.html.haml From 43ba3c509a585c1a09d879ec178b512a85ef6c82 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 3 Jul 2024 17:48:33 -0700 Subject: [PATCH 12/61] Convert file content from ERB to HAML --- app/views/login/index.html.haml | 57 +++++++++++++-------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/app/views/login/index.html.haml b/app/views/login/index.html.haml index 08110c66a..9832ccd01 100644 --- a/app/views/login/index.html.haml +++ b/app/views/login/index.html.haml @@ -1,34 +1,23 @@ -<%@title = "Login"%> - -<%= form_for(:user, :url => {:controller => 'login',:action=>'create'}) do |f| %> -<%unless @errors.nil?%> -
-Errors On Form -
    - -<%for error in @errors%> -
  • <%=error%>
  • -<%end%> - -
-
-<%end%> -
- - - - - - - - - - - - -
Account Name:<%= text_field 'user', 'username', :size => 25 %>
Password:<%= password_field 'user','password', :size => 25, :autocomplete => "off" %>
<%= submit_tag "Login"%>
- Not Registered Yet? <%=link_to "Sign Up", new_user_path%>

- <%=link_to "Lost Password", '/lost_pass'%>

-
- -<% end %> \ No newline at end of file +- @title = "Login" += form_for(:user, :url => {:controller => 'login',:action=>'create'}) do |f| + - unless @errors.nil? + %div{:style => "color:red;"} + Errors On Form + %ul + - for error in @errors + %li= error + %center + %table.form{:style => "margin-top:30px", :width => "400px"} + %tr + %th{:align => "right", :nowrap => "nowrap"} Account Name: + %td.top= text_field 'user', 'username', :size => 25 + %tr + %th{:align => "right", :nowrap => "nowrap"} Password: + %td= password_field 'user','password', :size => 25, :autocomplete => "off" + %tr + %td{:align => "right", :colspan => "2"}= submit_tag "Login" + %span{:style => "font-size:1.2em;"} + Not Registered Yet? #{link_to "Sign Up", new_user_path} + %br/ + %br/ + %span{:style => "font-size:1.2em;"}= link_to "Lost Password", '/lost_pass' From ef8c0b3cd647e6f2b0d0f89ea2b964ad703ada1a Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 3 Jul 2024 18:06:24 -0700 Subject: [PATCH 13/61] Replace form_for with form_with --- app/views/login/index.html.haml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/login/index.html.haml b/app/views/login/index.html.haml index 9832ccd01..06d6293cb 100644 --- a/app/views/login/index.html.haml +++ b/app/views/login/index.html.haml @@ -1,5 +1,6 @@ - @title = "Login" -= form_for(:user, :url => {:controller => 'login',:action=>'create'}) do |f| + += form_with scope: :user, url: login_index_path, method: :post do |f| - unless @errors.nil? %div{:style => "color:red;"} Errors On Form @@ -10,10 +11,10 @@ %table.form{:style => "margin-top:30px", :width => "400px"} %tr %th{:align => "right", :nowrap => "nowrap"} Account Name: - %td.top= text_field 'user', 'username', :size => 25 + %td.top= f.text_field :username, :size => 25 %tr %th{:align => "right", :nowrap => "nowrap"} Password: - %td= password_field 'user','password', :size => 25, :autocomplete => "off" + %td= f.password_field :password, :size => 25, :autocomplete => "off" %tr %td{:align => "right", :colspan => "2"}= submit_tag "Login" %span{:style => "font-size:1.2em;"} From b46d4857e5fc3e883109fd36be6c7d84372a6d4e Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 10 Jul 2024 14:43:02 -0700 Subject: [PATCH 14/61] Refactor the login page --- app/assets/stylesheets/application.css.scss | 1 + app/assets/stylesheets/login.scss | 30 +++++++++++++ app/views/login/index.html.haml | 49 ++++++++++++--------- 3 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 app/assets/stylesheets/login.scss diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 8843c1dc8..6686c3dac 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -30,6 +30,7 @@ @import "concepts"; @import "footer"; @import "home"; +@import "login"; @import "mappings"; @import "notes"; @import "notice"; diff --git a/app/assets/stylesheets/login.scss b/app/assets/stylesheets/login.scss new file mode 100644 index 000000000..a2c2ae9df --- /dev/null +++ b/app/assets/stylesheets/login.scss @@ -0,0 +1,30 @@ +.signin { + display: flex; + align-items: center; + justify-content: center; + padding-top: 40px; + padding-bottom: 40px; +} + +.form-signin { + width: 100%; + max-width: 400px; + padding: 15px; + margin: 0 auto; + + a { + text-decoration: none; + } + + a:hover { + text-decoration: underline; + } + + h4, p { + text-align: center; + } +} + +.form-signin .enable-lists { + color: red; +} diff --git a/app/views/login/index.html.haml b/app/views/login/index.html.haml index 06d6293cb..dc5e593b6 100644 --- a/app/views/login/index.html.haml +++ b/app/views/login/index.html.haml @@ -1,24 +1,29 @@ - @title = "Login" -= form_with scope: :user, url: login_index_path, method: :post do |f| - - unless @errors.nil? - %div{:style => "color:red;"} - Errors On Form - %ul - - for error in @errors - %li= error - %center - %table.form{:style => "margin-top:30px", :width => "400px"} - %tr - %th{:align => "right", :nowrap => "nowrap"} Account Name: - %td.top= f.text_field :username, :size => 25 - %tr - %th{:align => "right", :nowrap => "nowrap"} Password: - %td= f.password_field :password, :size => 25, :autocomplete => "off" - %tr - %td{:align => "right", :colspan => "2"}= submit_tag "Login" - %span{:style => "font-size:1.2em;"} - Not Registered Yet? #{link_to "Sign Up", new_user_path} - %br/ - %br/ - %span{:style => "font-size:1.2em;"}= link_to "Lost Password", '/lost_pass' +%div.signin + = form_with scope: :user, url: login_index_path, class: 'form-signin' do |f| + %h4.mb-4 + = "Log in to #{$SITE}" + + -# TODO: display errors in a standard Rails flash message + - if @errors.present? + %div.enable-lists + Errors on form: + %ul + - for error in @errors + %li= error + + %div.form-floating.mb-3 + = f.text_field(:username, class: 'form-control', placeholder: 'Username', required: true) + = f.label(:username) + %div.form-floating.mb-3 + = f.password_field(:password, class: 'form-control', placeholder: 'Password', required: true, + autocomplete: 'current-password') + = f.label(:password) + %div.d-grid.mb-3 + = submit_tag('Log in', class: 'btn btn-lg btn-primary') + %p.text-muted.mb-1 + = "New #{$SITE} user?" + = link_to('Create account', new_user_path) + %p.text-muted + = link_to('Forgot your password?', lost_pass_path) From 377bcb83ad767db086cd514823e25c828bfac2b9 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 10 Jul 2024 15:35:10 -0700 Subject: [PATCH 15/61] Rename lost_password with HAML extension --- .../login/{lost_password.html.erb => lost_password.html.haml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/views/login/{lost_password.html.erb => lost_password.html.haml} (100%) diff --git a/app/views/login/lost_password.html.erb b/app/views/login/lost_password.html.haml similarity index 100% rename from app/views/login/lost_password.html.erb rename to app/views/login/lost_password.html.haml From 00d83aaf4dbce9bdafdf6c202e14edd0c2ecd740 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 10 Jul 2024 15:41:27 -0700 Subject: [PATCH 16/61] Convert file content from ERB to HAML --- app/views/login/lost_password.html.haml | 39 +++++++++++-------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/app/views/login/lost_password.html.haml b/app/views/login/lost_password.html.haml index dbbf4338e..147165df8 100644 --- a/app/views/login/lost_password.html.haml +++ b/app/views/login/lost_password.html.haml @@ -1,22 +1,17 @@ -<%= form_for(:user , :url=>{:controller=>'login',:action=>'send_pass'}) do%> - -
- - - - - - - - - - - -
Email: *<%=text_field 'user', :email%>
Account Name: *<%=text_field 'user', :account_name%>
- <%=submit_tag "Email Password Reset Instructions"%> -
-
* required
-
- - -<%end%> \ No newline at end of file += form_for(:user , :url=>{:controller=>'login',:action=>'send_pass'}) do + %center + %table.form{:style => "margin: 4em 4em 1em;", :width => "400px"} + %tr + %th + Email: + %span.asterik * + %td.top= text_field 'user', :email + %tr + %th + Account Name: + %span.asterik * + %td= text_field 'user', :account_name + %tr + %td{:align => "right", :colspan => "2"} + = submit_tag "Email Password Reset Instructions" + .asterik{:style => "margin-bottom: 2em;"} * required From 6e6d166342a3d5b1a40c8a7c30b1afbf06741810 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 10 Jul 2024 15:49:30 -0700 Subject: [PATCH 17/61] Replace form_for with form_with --- app/views/login/lost_password.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/login/lost_password.html.haml b/app/views/login/lost_password.html.haml index 147165df8..3819044ae 100644 --- a/app/views/login/lost_password.html.haml +++ b/app/views/login/lost_password.html.haml @@ -1,16 +1,16 @@ -= form_for(:user , :url=>{:controller=>'login',:action=>'send_pass'}) do += form_with scope: :user , url: login_send_pass_path do |f| %center %table.form{:style => "margin: 4em 4em 1em;", :width => "400px"} %tr %th Email: %span.asterik * - %td.top= text_field 'user', :email + %td.top= f.email_field :email %tr %th Account Name: %span.asterik * - %td= text_field 'user', :account_name + %td= f.text_field :account_name %tr %td{:align => "right", :colspan => "2"} = submit_tag "Email Password Reset Instructions" From a06d0d1881653ac00d9c06983758b5fd40805b24 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Thu, 11 Jul 2024 15:22:06 -0700 Subject: [PATCH 18/61] Refactor lost password page --- app/assets/stylesheets/login.scss | 11 +++++++++ app/views/login/lost_password.html.haml | 30 +++++++++++-------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/app/assets/stylesheets/login.scss b/app/assets/stylesheets/login.scss index a2c2ae9df..8a8db9b9c 100644 --- a/app/assets/stylesheets/login.scss +++ b/app/assets/stylesheets/login.scss @@ -28,3 +28,14 @@ .form-signin .enable-lists { color: red; } + +.password-reset { + display: flex; + justify-content: center; + padding-top: 40px; + padding-bottom: 40px; + + form { + max-width: 640px; + } +} diff --git a/app/views/login/lost_password.html.haml b/app/views/login/lost_password.html.haml index 3819044ae..d40b2be6e 100644 --- a/app/views/login/lost_password.html.haml +++ b/app/views/login/lost_password.html.haml @@ -1,17 +1,13 @@ -= form_with scope: :user , url: login_send_pass_path do |f| - %center - %table.form{:style => "margin: 4em 4em 1em;", :width => "400px"} - %tr - %th - Email: - %span.asterik * - %td.top= f.email_field :email - %tr - %th - Account Name: - %span.asterik * - %td= f.text_field :account_name - %tr - %td{:align => "right", :colspan => "2"} - = submit_tag "Email Password Reset Instructions" - .asterik{:style => "margin-bottom: 2em;"} * required +%div.password-reset + = form_with scope: :user , url: login_send_pass_path do |f| + %h3 Forgot your password? + %p.text-muted.mb-5 + = "Enter your email and #{$SITE} username to receive a password reset link." + %div.mb-3 + = f.label :email, 'Email', class: 'form-label' + = f.email_field :email, class: 'form-control' + %div.mb-3 + = f.label :account_name, 'Username', class: 'form-label' + = f.text_field :account_name, class: 'form-control' + %div + = submit_tag 'Reset password', class: 'btn btn-primary' From 4330abf2448ddc8a8807fc9b0abbf07b8721b2b1 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Thu, 11 Jul 2024 15:24:35 -0700 Subject: [PATCH 19/61] Remove commented out code --- app/views/users/show.html.haml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 774f214d3..2c1ef22e4 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -24,8 +24,6 @@ %div{class: 'd-flex justify-content-end'} = link_to('Edit account', edit_user_path(@user.username), class: 'btn btn-sm btn-outline-secondary') - -# = link_to('Change password', edit_user_path(@user.username, password: true), - -# class: 'btn btn-sm btn-outline-secondary ms-2') = link_to('Change password', edit_password_path, class: 'btn btn-sm btn-outline-secondary ms-2') -# User's API key From a377417ae63e276fca5fc47e944585ea37b158e6 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Fri, 12 Jul 2024 16:06:19 -0700 Subject: [PATCH 20/61] Add Stimulus clipboard controller --- .../controllers/clipboard_controller.js | 16 ++++++++++++++++ app/javascript/controllers/index.js | 3 +++ 2 files changed, 19 insertions(+) create mode 100644 app/javascript/controllers/clipboard_controller.js diff --git a/app/javascript/controllers/clipboard_controller.js b/app/javascript/controllers/clipboard_controller.js new file mode 100644 index 000000000..30320b5e3 --- /dev/null +++ b/app/javascript/controllers/clipboard_controller.js @@ -0,0 +1,16 @@ +import { Controller } from "@hotwired/stimulus" + +// Connects to data-controller="clipboard" +export default class extends Controller { + static targets = ['source', 'copiedIndicator'] + + copy() { + navigator.clipboard.writeText(this.sourceTarget.textContent); + + this.copiedIndicatorTarget.classList.remove('hidden'); + + setTimeout(() => { + this.copiedIndicatorTarget.classList.add('hidden'); + }, 2000); + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 373c3edc2..c4d0fd2b2 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -3,3 +3,6 @@ // ./bin/rails generate stimulus controllerName import { application } from "./application" + +import ClipboardController from "./clipboard_controller" +application.register("clipboard", ClipboardController) From 94b091bae919de803a63d424a25511b954c73999 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Fri, 12 Jul 2024 16:06:39 -0700 Subject: [PATCH 21/61] Add copy to clipboard for user API keys --- app/views/users/show.html.haml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 2c1ef22e4..f4ea24087 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -29,7 +29,11 @@ -# User's API key %h4{class: 'pb-2 my-4 border-bottom'} API Key = tag.p("Your API key can be used to access the #{$SITE} RESTful API and services", class: 'text-muted') - %p= session[:user].apikey + %div{'data-controller': 'clipboard'} + %span{'data-clipboard-target': 'source'}= session[:user].apikey + %div{class: 'd-flex align-items-center mt-2'} + = tag.button 'Copy to clipboard', 'data-action': 'clipboard#copy', class: 'btn btn-sm btn-outline-secondary' + %span{'data-clipboard-target': 'copiedIndicator', class: 'hidden text-success fw-semibold ms-2'} Copied! -# User's subscriptions - unless @user.subscription.blank? From b67e48575eabf9c6e19b2e42c01ad8cae19f9ba1 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Fri, 12 Jul 2024 17:43:07 -0700 Subject: [PATCH 22/61] Remove logic and form for password updates This is now handled by a separate passwords controller --- app/controllers/users_controller.rb | 26 +++------ app/views/users/edit.html.haml | 87 +++++++++++------------------ 2 files changed, 41 insertions(+), 72 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 50c2e14ea..5d5444304 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -43,10 +43,6 @@ def new def edit @user = LinkedData::Client::Models::User.find(params[:id]) @user ||= LinkedData::Client::Models::User.find_by_username(params[:id]).first - - if params[:password].eql? 'true' - @user.validate_password = true - end end def create @@ -72,22 +68,18 @@ def create def update @user = LinkedData::Client::Models::User.find(params[:id]) @user = LinkedData::Client::Models::User.find_by_username(params[:id]).first if @user.nil? + @errors = validate_update(user_params) if @errors.empty? + user_roles = @user.role - if params[:user][:password] - error_response = @user.update(values: { password: params[:user][:password] }) - else - user_roles = @user.role - - if @user.admin? != (params[:user][:admin].to_i == 1) - user_roles = update_role(@user) - end - - @user.update_from_params(user_params.merge!(role: user_roles)) - error_response = @user.update + if @user.admin? != (params[:user][:admin].to_i == 1) + user_roles = update_role(@user) end + @user.update_from_params(user_params.merge!(role: user_roles)) + error_response = @user.update + if response_error?(error_response) @errors = response_errors(error_response) # @errors = {acronym: "Username already exists, please use another"} if error_response.status == 409 @@ -192,10 +184,6 @@ def validate_update(params) if params[:email].length < 1 || !params[:email].match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i) errors << 'invalid email address' end - if !params[:password].eql?(params[:password_confirmation]) - errors << 'Your password and password confirmation do not match' - end - errors end diff --git a/app/views/users/edit.html.haml b/app/views/users/edit.html.haml index c3875a604..a9476331d 100644 --- a/app/views/users/edit.html.haml +++ b/app/views/users/edit.html.haml @@ -34,56 +34,37 @@ %ul - for error in @errors %li= error - - if @user.validate_password.blank? - %h2.mb-4 Edit account - %div.mb-3 - = f.label :firstName, 'First name', class: 'form-label' - = f.text_field :firstName, class: 'form-control', required: 'true' - %div.mb-3 - = f.label :lastName, 'Last name', class: 'form-label' - = f.text_field :lastName, class: 'form-control' - %div.mb-3 - = f.label :username, class: 'form-label' - = f.text_field :username, class: 'form-control', disabled: 'true' - %div.mb-3 - = f.label :email, 'Email address', class: 'form-label' - = f.email_field :email, class: 'form-control', required: 'true' - %div.mb-3 - = f.label :githubId, 'GitHub username', class: 'form-label' - = f.text_field :githubId, class: 'form-control', 'aria-describedby': 'githubUsernameHelp', - pattern: '[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}', - title: 'Characters: only alphanumeric and single hyphens. Length: 1-39' - %div.form-text{id: 'githubUsernameHelp'} - User handle on GitHub, for example: 'octocat' - %div.mb-3 - = f.label :orcidId, 'ORCID iD', class: 'form-label' - = f.text_field :orcidId, class: 'form-control', 'aria-describedby': 'orcidHelp', - pattern: '\d{4}-\d{4}-\d{4}-\d{4}', title: 'Format: XXXX-XXXX-XXXX-XXXX' - %div.form-text{id: 'orcidHelp'} - 16-digit ORCID, for example: 0000-1234-5678-9101 - - if session[:user].admin? - %div.form-check.mb-3 - = f.check_box :admin?, class: (@user.admin? ? 'admin form-check-input' : 'form-check-input') - = f.label :admin, 'Grant admin privileges', class: 'form-check-label' - %div.mb-5 - = submit_tag('Update', class: 'btn btn-primary me-1') - = link_to 'Cancel', user_path(@user.username), class: 'btn btn-primary' - - else - %h3{class: 'pb-2 mb-3 border-bottom'} - Password - = tag.p("Change the password for account: #{@user.username}", class: 'text-muted') - = hidden_field :user, :firstName, value: @user.firstName - = hidden_field :user, :lastName, value: @user.lastName - = hidden_field :user, :username, value: @user.username - = hidden_field :user, :email, value: @user.email - = hidden_field :user, :githubId, value: @user.githubId - = hidden_field :user, :orcidId, value: @user.orcidId - %div.mb-3 - = f.label :password, 'New password', class: 'form-label' - = f.password_field :password, class: 'form-control', required: 'true' - %div.mb-4 - = f.label :password_confirmation, 'Confirm new password', class: 'form-label' - = f.password_field :password_confirmation, class: 'form-control', required: 'true' - %div - = submit_tag('Change password', class: 'btn btn-primary me-1') - = link_to 'Cancel', user_path(@user.username), class: 'btn btn-primary' + + %h2.mb-4 Edit account + %div.mb-3 + = f.label :firstName, 'First name', class: 'form-label' + = f.text_field :firstName, class: 'form-control', required: 'true' + %div.mb-3 + = f.label :lastName, 'Last name', class: 'form-label' + = f.text_field :lastName, class: 'form-control' + %div.mb-3 + = f.label :username, class: 'form-label' + = f.text_field :username, class: 'form-control', disabled: 'true' + %div.mb-3 + = f.label :email, 'Email address', class: 'form-label' + = f.email_field :email, class: 'form-control', required: 'true' + %div.mb-3 + = f.label :githubId, 'GitHub username', class: 'form-label' + = f.text_field :githubId, class: 'form-control', 'aria-describedby': 'githubUsernameHelp', + pattern: '[a-zA-Z0-9](?:[a-zA-Z0-9]|-(?=[a-zA-Z0-9])){0,38}', + title: 'Characters: only alphanumeric and single hyphens. Length: 1-39' + %div.form-text{id: 'githubUsernameHelp'} + User handle on GitHub, for example: 'octocat' + %div.mb-3 + = f.label :orcidId, 'ORCID iD', class: 'form-label' + = f.text_field :orcidId, class: 'form-control', 'aria-describedby': 'orcidHelp', + pattern: '\d{4}-\d{4}-\d{4}-\d{4}', title: 'Format: XXXX-XXXX-XXXX-XXXX' + %div.form-text{id: 'orcidHelp'} + 16-digit ORCID, for example: 0000-1234-5678-9101 + - if session[:user].admin? + %div.form-check.mb-3 + = f.check_box :admin?, class: (@user.admin? ? 'admin form-check-input' : 'form-check-input') + = f.label :admin, 'Grant admin privileges', class: 'form-check-label' + %div.mb-5 + = submit_tag('Update', class: 'btn btn-primary me-1') + = link_to 'Cancel', user_path(@user.username), class: 'btn btn-primary' From 10acc0e7124d9f4024460e7ec26f4a238736b4ea Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Fri, 12 Jul 2024 17:45:41 -0700 Subject: [PATCH 23/61] Delete _form.html.haml No longer using a common form for new and edit --- app/views/users/_form.html.haml | 38 --------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 app/views/users/_form.html.haml diff --git a/app/views/users/_form.html.haml b/app/views/users/_form.html.haml deleted file mode 100644 index a3306597f..000000000 --- a/app/views/users/_form.html.haml +++ /dev/null @@ -1,38 +0,0 @@ -%table.form{:width => "70%"} - %tr - %th First Name: - %td.top= text_field :user, :firstName, value: @user.firstname - %tr - %th Last Name: - %td= text_field :user, :lastName, value: @user.lastname - %tr - %th Account Name:* - %td= text_field :user, :username, value: @user.username - %tr - %th Email Address:* - %td= text_field :user, :email, value: @user.email - %tr - %th Re-enter Email Address:* - %td= text_field :user, :email_confirmation, value: @user.email_confirmation - %tr - %th Password:* - %td= password_field :user, :password, :autocomplete => "off" - %tr - %th Re-enter Password:* - %td= password_field :user, :password_confirmation, :autocomplete => "off" - - if using_captcha? - %tr - %th - %td= recaptcha_tags - - unless $ANNOUNCE_LIST.nil? || $ANNOUNCE_LIST.empty? - %tr - %td{:colspan => "2", :style => "text-align: right;"} - %div{:style => "float: right; margin-left: 10px; position: relative; top: 5px;"} - %input#user_register_mail_list{:checked => "checked", :name => "user[register_mail_list]", :type => "checkbox", :value => "1"}/ - %div{:style => "overflow: hidden; line-height: 12px;"} - %label{:for => "user_register_mail_list"} - Register for the #{$SITE} announcements email list - %br/ - %span{:style => "font-size: x-small;"} (approximately two emails per month -- confirmation via email required) - %tr - %td{:colspan => "2", :style => "text-align: right;"}= submit_tag "Create" From c76d506d7d48064d55105f80ca004c5c1a8f8b37 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 17 Jul 2024 10:29:50 -0700 Subject: [PATCH 24/61] progress commit --- Gemfile | 12 +- Gemfile.lock | 207 ++- app/assets/javascripts/bp_class_tree.js.erb | 26 +- .../javascripts/bp_ontology_viewer.js.erb | 39 +- app/assets/stylesheets/application.css.scss | 1 + app/controllers/application_controller.rb | 155 +- app/controllers/concepts_controller.rb | 78 +- app/controllers/ontologies_controller.rb | 7 + app/helpers/application_helper.rb | 14 + app/helpers/ontologies_helper.rb | 96 ++ app/javascript/controllers/index.js | 3 + app/views/layouts/_ontology_viewer.html.haml | 27 + config/locales/en.yml | 1536 ++++++++++++++++- db/schema.rb | 14 - yarn.lock | 296 ++-- 15 files changed, 2152 insertions(+), 359 deletions(-) diff --git a/Gemfile b/Gemfile index 710616a59..eb38d193f 100644 --- a/Gemfile +++ b/Gemfile @@ -53,7 +53,6 @@ gem 'graphql', '~> 2.0.27' gem 'graphql-client' gem 'haml', '~> 5.1' gem 'i18n' -gem 'iconv' gem 'iso-639', '~> 0.3.6' gem 'multi_json' gem 'mysql2', '0.5.5' @@ -73,6 +72,17 @@ gem 'terser' gem 'thin' gem 'will_paginate', '~> 3.0' + + + +gem 'net-ftp' +gem 'flag-icons-rails', '~> 3.4' +gem 'inline_svg' + + + + + group :staging, :production do # Application monitoring gem 'newrelic_rpm' diff --git a/Gemfile.lock b/Gemfile.lock index e8efaa91b..201c9a3e3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -83,24 +83,28 @@ GEM i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) - addressable (2.8.5) - public_suffix (>= 2.0.2, < 6.0) - airbrussh (1.5.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + airbrussh (1.5.2) sshkit (>= 1.6.1, != 1.7.0) ast (2.4.2) autoprefixer-rails (10.4.16.0) execjs (~> 2) base64 (0.1.0) - bcrypt_pbkdf (1.1.0) - bootsnap (1.17.1) + bcrypt_pbkdf (1.1.1) + bcrypt_pbkdf (1.1.1-arm64-darwin) + bcrypt_pbkdf (1.1.1-x86_64-darwin) + bigdecimal (3.1.8) + bootsnap (1.18.3) msgpack (~> 1.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.18.0) + brakeman (6.1.2) + racc + builder (3.3.0) + capistrano (3.19.1) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) @@ -119,11 +123,11 @@ GEM sshkit (~> 1.3) capistrano-yarn (2.0.2) capistrano (~> 3.0) - capybara (3.39.2) + capybara (3.40.0) addressable matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) @@ -131,41 +135,45 @@ GEM chart-js-rails (0.1.7) railties (> 3.1) coderay (1.1.3) - concurrent-ruby (1.2.3) + concurrent-ruby (1.3.3) crass (1.0.6) cube-ruby (0.0.3) daemons (1.4.1) - dalli (3.2.6) + dalli (3.2.8) date (3.3.4) - debug (1.9.1) + debug (1.9.2) irb (~> 1.10) reline (>= 0.3.8) - diff-lcs (1.5.0) - domain_name (0.6.20231109) + diff-lcs (1.5.1) + domain_name (0.6.20240107) ed25519 (1.3.0) - erubi (1.12.0) + erubi (1.13.0) erubis (2.7.0) eventmachine (1.2.7) - excon (0.108.0) + excon (0.110.0) execjs (2.9.1) - faraday (2.7.12) - base64 - faraday-net_http (>= 2.0, < 3.1) - ruby2_keywords (>= 0.0.4) + faraday (2.9.2) + faraday-net_http (>= 2.0, < 3.2) faraday-excon (2.1.0) excon (>= 0.27.4) faraday (~> 2.0) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (3.0.2) - ffi (1.16.3) + faraday-net_http (3.1.0) + net-http + ffi (1.17.0) + ffi (1.17.0-arm64-darwin) + ffi (1.17.0-x86_64-darwin) + flag-icons-rails (3.4.6.1) + sass-rails flamegraph (0.9.5) globalid (1.2.1) activesupport (>= 6.1) - graphql (2.0.27) - graphql-client (0.18.0) + graphql (2.0.31) + base64 + graphql-client (0.23.0) activesupport (>= 3.0) - graphql + graphql (>= 1.13.0) haml (5.2.2) temple (>= 0.8.0) tilt @@ -175,27 +183,29 @@ GEM nokogiri (>= 1.6.0) ruby_parser (~> 3.5) http-accept (1.7.0) - http-cookie (1.0.5) + http-cookie (1.0.6) domain_name (~> 0.5) - i18n (1.14.1) + i18n (1.14.5) concurrent-ruby (~> 1.0) - iconv (1.0.8) + inline_svg (1.9.0) + activesupport (>= 3.0) + nokogiri (>= 1.6) io-console (0.7.2) - irb (1.12.0) - rdoc + irb (1.13.2) + rdoc (>= 4.0.0) reline (>= 0.4.2) iso-639 (0.3.6) jquery-rails (4.6.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - jquery-ui-rails (6.0.1) + jquery-ui-rails (7.0.0) railties (>= 3.2.16) jsbundling-rails (1.3.0) railties (>= 6.0.0) json (2.7.2) language_server-protocol (3.17.0.3) - listen (3.8.0) + listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) loofah (2.22.0) @@ -207,19 +217,24 @@ GEM net-imap net-pop net-smtp - marcel (1.0.2) + marcel (1.0.4) matrix (0.4.2) - method_source (1.0.0) - mime-types (3.5.1) + method_source (1.1.0) + mime-types (3.5.2) mime-types-data (~> 3.2015) - mime-types-data (3.2023.1003) + mime-types-data (3.2024.0702) mini_mime (1.1.5) - minitest (5.21.2) + minitest (5.24.1) msgpack (1.7.2) multi_json (1.15.0) - multipart-post (2.3.0) + multipart-post (2.4.1) mysql2 (0.5.5) - net-imap (0.4.7) + net-ftp (0.3.7) + net-protocol + time + net-http (0.4.1) + uri + net-imap (0.4.14) date net-protocol net-pop (0.1.2) @@ -228,23 +243,25 @@ GEM timeout net-scp (4.0.0) net-ssh (>= 2.6.5, < 8.0.0) - net-smtp (0.4.0) + net-sftp (4.0.0) + net-ssh (>= 5.0.0, < 8.0.0) + net-smtp (0.5.0) net-protocol - net-ssh (7.2.0) + net-ssh (7.2.3) netrc (0.11.0) - newrelic_rpm (9.6.0) - base64 - nio4r (2.6.1) - nokogiri (1.16.0-arm64-darwin) + newrelic_rpm (9.11.0) + nio4r (2.7.3) + nokogiri (1.16.6-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.0-x86_64-darwin) + nokogiri (1.16.6-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.0-x86_64-linux) + nokogiri (1.16.6-x86_64-linux) racc (~> 1.4) - oj (3.16.1) + oj (3.16.4) + bigdecimal (>= 3.0) open_uri_redirections (0.2.1) - parallel (1.24.0) - parser (3.3.0.5) + parallel (1.25.1) + parser (3.3.3.0) ast (~> 2.4.1) racc popper_js (2.11.8) @@ -252,10 +269,10 @@ GEM coderay (~> 1.1) method_source (~> 1.0) psych (3.3.4) - public_suffix (5.0.4) - racc (1.7.3) - rack (2.2.8) - rack-mini-profiler (3.1.1) + public_suffix (6.0.0) + racc (1.8.0) + rack (2.2.9) + rack-mini-profiler (3.3.1) rack (>= 1.2.0) rack-test (2.1.0) rack (>= 1.3) @@ -294,39 +311,40 @@ GEM rainbow (3.1.1) rake (13.2.1) rb-fsevent (0.11.2) - rb-inotify (0.10.1) + rb-inotify (0.11.1) ffi (~> 1.0) rdoc (6.3.4.1) recaptcha (5.9.0) json redis (4.8.1) - regexp_parser (2.9.0) - reline (0.5.0) + regexp_parser (2.9.2) + reline (0.5.9) io-console (~> 0.5) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - rexml (3.2.6) - rspec-core (3.12.2) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) + rexml (3.3.1) + strscan + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-mocks (3.12.6) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-rails (6.1.0) + rspec-support (~> 3.13.0) + rspec-rails (6.1.3) actionpack (>= 6.1) activesupport (>= 6.1) railties (>= 6.1) - rspec-core (~> 3.12) - rspec-expectations (~> 3.12) - rspec-mocks (~> 3.12) - rspec-support (~> 3.12) - rspec-support (3.12.1) - rubocop (1.63.3) + rspec-core (~> 3.13) + rspec-expectations (~> 3.13) + rspec-mocks (~> 3.13) + rspec-support (~> 3.13) + rspec-support (3.13.1) + rubocop (1.64.1) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -337,12 +355,14 @@ GEM rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.2) - parser (>= 3.3.0.4) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) ruby-progressbar (1.13.0) - ruby2_keywords (0.0.5) - ruby_parser (3.20.3) + ruby_parser (3.21.0) + racc (~> 1.5) sexp_processor (~> 4.16) + sass-rails (6.0.0) + sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) @@ -352,48 +372,55 @@ GEM sprockets-rails tilt select2-rails (4.0.13) - sexp_processor (4.17.0) + sexp_processor (4.17.1) spawnling (2.1.5) sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + sprockets-rails (3.5.1) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - sshkit (1.21.6) + sshkit (1.23.0) + base64 net-scp (>= 1.1.2) + net-sftp (>= 2.1.2) net-ssh (>= 2.8.0) - stackprof (0.2.25) + stackprof (0.2.26) stimulus-rails (1.3.3) railties (>= 6.0.0) + strscan (3.1.0) temple (0.10.3) - terser (1.1.20) + terser (1.2.3) 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.3.0) - tilt (2.3.0) + thor (1.3.1) + tilt (2.4.0) + time (0.3.0) + date timeout (0.4.1) - turbo-rails (1.5.0) + turbo-rails (2.0.5) actionpack (>= 6.0.0) activejob (>= 6.0.0) railties (>= 6.0.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.5.0) + uri (0.13.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.12) + zeitwerk (2.6.16) PLATFORMS arm64-darwin-22 + arm64-darwin-23 x86_64-darwin-21 x86_64-linux @@ -416,13 +443,14 @@ DEPENDENCIES dalli debug ed25519 (>= 1.2, < 2.0) + flag-icons-rails (~> 3.4) flamegraph graphql (~> 2.0.27) graphql-client haml (~> 5.1) html2haml i18n - iconv + inline_svg iso-639 (~> 0.3.6) jquery-rails jquery-ui-rails @@ -430,6 +458,7 @@ DEPENDENCIES listen multi_json mysql2 (= 0.5.5) + net-ftp newrelic_rpm oj ontologies_api_client! @@ -458,4 +487,4 @@ DEPENDENCIES will_paginate (~> 3.0) BUNDLED WITH - 2.4.19 + 2.5.11 diff --git a/app/assets/javascripts/bp_class_tree.js.erb b/app/assets/javascripts/bp_class_tree.js.erb index e3cbbc8c9..26d4ff526 100644 --- a/app/assets/javascripts/bp_class_tree.js.erb +++ b/app/assets/javascripts/bp_class_tree.js.erb @@ -86,7 +86,20 @@ function initClassTree() { }); }; -function nodeClicked(node_id) { + + + + + + + + + + + + + +function nodeClicked(node_id, lang = "en") { // Get current html and store data in cache (to account for changes since the cache was retrieved) setCacheCurrent(); @@ -132,7 +145,7 @@ function nodeClicked(node_id) { wrapupTabChange(selectedTab); } else { jQuery.blockUI({ message: '

' + '<%= image_tag src="jquery.simple.tree/spinner.gif" %>' + ' Loading Class...

', showOverlay: false }); - jQuery.get('/ajax_concepts/'+jQuery(document).data().bp.ont_viewer.ontology_id+'/?conceptid='+node_id+'&callback=load', + jQuery.get('/ajax_concepts/'+jQuery(document).data().bp.ont_viewer.ontology_id+'/?conceptid='+node_id+'&lang=' + lang + '&callback=load', function(data){ var tabData = data.split("|||"); @@ -181,14 +194,14 @@ function placeTreeView(treeHTML) { } // Retrieve the tree view using ajax -function getTreeView() { +function getTreeView(lang = "en") { jQuery.ajax({ - url: "/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&conceptid="+encodeURIComponent(jQuery(document).data().bp.ont_viewer.concept_id), + url: "/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&lang=" + lang + "&conceptid="+encodeURIComponent(jQuery(document).data().bp.ont_viewer.concept_id), success: function(data) { placeTreeView(data); }, error: function(data) { - jQuery.get("/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&conceptid=root", function(data){ + jQuery.get("/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&lang=" + lang + "&conceptid=root", function(data){ var rootTree = "
Displaying the path to this class has taken too long. You can browse classes below.
" + data; placeTreeView(rootTree); }); @@ -201,6 +214,7 @@ function getTreeView() { // We do this right after writing #sd_content to the dom to make sure it loads before other async portions of the page jQuery(document).ready(function(){ if (pageUsesTreeView()) { - getTreeView(); + var lang = jQuery(document).data().bp.ont_viewer.lang; + getTreeView(lang); } }); diff --git a/app/assets/javascripts/bp_ontology_viewer.js.erb b/app/assets/javascripts/bp_ontology_viewer.js.erb index e595cf3c2..85bda3bb4 100644 --- a/app/assets/javascripts/bp_ontology_viewer.js.erb +++ b/app/assets/javascripts/bp_ontology_viewer.js.erb @@ -66,6 +66,15 @@ function displayTree(data) { jQuery(document).trigger("classes_tab_visible"); + + + + + + + + + var new_concept_id = data.conceptid; var new_concept_link = getConceptLinkEl(new_concept_id); var concept_label; @@ -83,6 +92,10 @@ function displayTree(data) { var purl_prefix = jQuery(document).data().bp.ont_viewer.purl_prefix; var concept_name_title = jQuery(document).data().bp.ont_viewer.concept_name_title; + + var lang = jQuery(document).data().bp.lang; + + // Check to see if we're actually loading a new concept or just displaying the one we already loaded previously if (typeof new_concept_id === 'undefined' || new_concept_id == concept_id) { @@ -122,7 +135,9 @@ function displayTree(data) { } // If the concept is already visible and in cache, then just switch to it - if (getCache(data.conceptid) == null) { +// if (getCache(data.conceptid) == null) { + + var list = jQuery("div#sd_content ul.simpleTree li.root ul"); // Remove existing classes @@ -137,7 +152,7 @@ function displayTree(data) { simpleTreeCollection.get(0).setTreeNodes(list); // Simulate node click - nodeClicked(data.conceptid); + nodeClicked(data.conceptid, lang); // Make "clicked" node active jQuery("a.active").removeClass("active"); @@ -146,22 +161,28 @@ function displayTree(data) { // Clear the search box jQuery("#search_box").val(""); } else { - nodeClicked(data.conceptid); + nodeClicked(data.conceptid, lang); // Clear the search box jQuery("#search_box").val(""); } - } else { - // Are we jumping into the ontology? If so, get the whole tree - jQuery.bioportal.ont_pages["classes"].retrieve_and_publish(); - getConceptLinkEl(new_concept_id) - } + + + +// } else { +// // Are we jumping into the ontology? If so, get the whole tree +// jQuery.bioportal.ont_pages["classes"].retrieve_and_publish(); +// getConceptLinkEl(new_concept_id) +// } + + + } else { jQuery.blockUI({ message: '

" /> Loading Class...

', showOverlay: false }); if (document.getElementById(new_concept_id) !== null) { // We have a visible node that's been clicked, get the details for that node jQuery.bioportal.ont_pages["classes"].published = true; - nodeClicked(new_concept_id); + nodeClicked(new_concept_id, lang); } else { // Get a new copy of the tree because our concept isn't visible // This could be due to using the forward/back button diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 99eabc57e..6b0b8f356 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -19,6 +19,7 @@ *= require thickbox *= require select2 *= require trumbowyg + *= require flag-icon * */ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 488cbda5a..91d19de67 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -349,7 +349,122 @@ def using_captcha? ENV['USE_RECAPTCHA'].present? && ENV['USE_RECAPTCHA'] == 'true' end + + + + + + + + + + # def get_class1(params) + # + # lang = request_lang + # + # if @ontology.flat? + # + # ignore_concept_param = params[:conceptid].nil? || + # params[:conceptid].empty? || + # params[:conceptid].eql?("root") || + # params[:conceptid].eql?("bp_fake_root") + # if ignore_concept_param + # # Don't display any classes in the tree + # @concept = LinkedData::Client::Models::Class.new + # @concept.prefLabel = t('application.search_for_class') + # @concept.obsolete = false + # @concept.id = "bp_fake_root" + # @concept.properties = {} + # @concept.children = [] + # else + # # Display only the requested class in the tree + # @concept = @ontology.explore.single_class({full: true, lang: lang }, params[:conceptid]) + # @concept.children = [] + # end + # @root = LinkedData::Client::Models::Class.new + # @root.children = [@concept] + # + # else + # + # # not ignoring 'bp_fake_root' here + # include = 'prefLabel,hasChildren,obsolete' + # ignore_concept_param = params[:conceptid].nil? || + # params[:conceptid].empty? || + # params[:conceptid].eql?("root") + # if ignore_concept_param + # # get the top level nodes for the root + # # TODO_REV: Support views? Replace old view call: @ontology.top_level_classes(view) + # @roots = @ontology.explore.roots(concept_schemes: params[:concept_schemes]) rescue nil + # + # if @roots.nil? || response_error?(@roots) || @roots.compact&.empty? + # LOG.add :debug, t('application.missing_roots_for_ontology', acronym: @ontology.acronym) + # classes = @ontology.explore.classes.collection + # @concept = classes.first.explore.self(full: true) if classes&.first + # return + # end + # + # @root = LinkedData::Client::Models::Class.new(read_only: true) + # @root.children = @roots.sort{|x,y| (x&.prefLabel || "").downcase <=> (y&.prefLabel || "").downcase} + # + # # get the initial concept to display + # root_child = @root.children&.first + # not_found(t('application.missing_roots')) if root_child.nil? + # + # @concept = root_child.explore.self(full: true, lang: lang) + # # Some ontologies have "too many children" at their root. These will not process and are handled here. + # if @concept.nil? + # LOG.add :debug, t('application.missing_class', root_child: root_child.links.self) + # not_found(t('application.missing_class', root_child: root_child.links.self)) + # end + # else + # # if the id is coming from a param, use that to get concept + # @concept = @ontology.explore.single_class({full: true, lang: lang}, params[:conceptid]) + # if @concept.nil? || @concept.errors + # LOG.add :debug, t('application.missing_class_ontology', acronym: @ontology.acronym, concept_id: params[:conceptid]) + # not_found(t('application.missing_class_ontology', acronym: @ontology.acronym, concept_id: params[:conceptid])) + # end + # + # # Create the tree + # rootNode = @concept.explore.tree(include: include, concept_schemes: params[:concept_schemes], lang: lang) + # if rootNode.nil? || rootNode.empty? + # @roots = @ontology.explore.roots(concept_schemes: params[:concept_schemes]) + # if @roots.nil? || response_error?(@roots) || @roots.compact&.empty? + # LOG.add :debug, t('application.missing_roots_for_ontology', acronym: @ontology.acronym) + # @concept = @ontology.explore.classes.collection.first.explore.self(full: true) + # return + # end + # if @roots.any? {|c| c.id == @concept.id} + # rootNode = @roots + # else + # rootNode = [@concept] + # end + # end + # @root = LinkedData::Client::Models::Class.new(read_only: true) + # @root.children = rootNode.sort{|x,y| (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase} + # end + # end + # + # @concept + # end + + + + + + + + + + + + + + + + + def get_class(params) + lang = request_lang if @ontology.flat? @@ -367,7 +482,7 @@ def get_class(params) @concept.children = [] else # Display only the requested class in the tree - @concept = @ontology.explore.single_class({full: true}, params[:conceptid]) + @concept = @ontology.explore.single_class({full: true, lang: lang}, params[:conceptid]) @concept.children = [] end @root = LinkedData::Client::Models::Class.new @@ -379,10 +494,12 @@ def get_class(params) ignore_concept_param = params[:conceptid].nil? || params[:conceptid].empty? || params[:conceptid].eql?("root") + if ignore_concept_param # get the top level nodes for the root # TODO_REV: Support views? Replace old view call: @ontology.top_level_classes(view) roots = @ontology.explore.roots + if roots.nil? || roots.empty? LOG.add :debug, "Missing roots for #{@ontology.acronym}" not_found @@ -394,7 +511,7 @@ def get_class(params) # get the initial concept to display root_child = @root.children.first - @concept = root_child.explore.self(full: true) + @concept = root_child.explore.self(full: true, lang: lang) # Some ontologies have "too many children" at their root. These will not process and are handled here. if @concept.nil? LOG.add :debug, "Missing class #{root_child.links.self}" @@ -402,14 +519,16 @@ def get_class(params) end else # if the id is coming from a param, use that to get concept - @concept = @ontology.explore.single_class({full: true}, params[:conceptid]) + @concept = @ontology.explore.single_class({full: true, lang: lang}, params[:conceptid]) if @concept.nil? || @concept.errors LOG.add :debug, "Missing class #{@ontology.acronym} / #{params[:conceptid]}" not_found end # Create the tree - rootNode = @concept.explore.tree(include: "prefLabel,hasChildren,obsolete") + rootNode = @concept.explore.tree(include: "prefLabel,hasChildren,obsolete", lang: lang) + + if rootNode.nil? || rootNode.empty? roots = @ontology.explore.roots if roots.nil? || roots.empty? @@ -422,11 +541,35 @@ def get_class(params) rootNode = [@concept] end end + + + + + + # binding.pry + + + + @root = LinkedData::Client::Models::Class.new(read_only: true) @root.children = rootNode.sort{|x,y| (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase} + + + + + # binding.pry + + + + end end + + + # binding.pry + + @concept end @@ -677,4 +820,8 @@ def init_trial_license end end + def request_lang + helpers.request_lang + end + end diff --git a/app/controllers/concepts_controller.rb b/app/controllers/concepts_controller.rb index f518eed67..5eba781eb 100644 --- a/app/controllers/concepts_controller.rb +++ b/app/controllers/concepts_controller.rb @@ -20,6 +20,7 @@ def show if request.xhr? display = params[:callback].eql?('load') ? { full: true } : { display: 'prefLabel' } + display[:language] = request_lang @concept = @ontology.explore.single_class(display, params[:id]) not_found if @concept.nil? show_ajax_request @@ -28,7 +29,27 @@ def show end end + + + + + + + + + + def show_label + + + + + # binding.pry + + + + + @ontology = LinkedData::Client::Models::Ontology.find(params[:ontology]) @ontology ||= LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first not_found unless @ontology @@ -48,16 +69,59 @@ def show_definition render text: cls.definition end + + + + + + + + + + + + + + + + + def show_tree @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first if @ontology.nil? not_found else + + get_class(params) + + + + + # binding.pry + + + + + render partial: 'ontologies/treeview' end end + + + + + + + + + + + + + + def property_tree @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first not_found if @ontology.nil? @@ -103,6 +167,10 @@ def redirect_new_api private + + + + # Load data for a concept or retrieve a concept's children, depending on the value of the :callback parameter. # Children are retrieved for drawing ontology class trees. def show_ajax_request @@ -111,12 +179,20 @@ def show_ajax_request gather_details render partial: 'load' when 'children' - @children = @concept.explore.children(pagesize: 750).collection || [] + @children = @concept.explore.children(pagesize: 750, language: request_lang).collection || [] @children.sort! { |x, y| (x.prefLabel || '').downcase <=> (y.prefLabel || '').downcase } render partial: 'child_nodes' end end + + + + + + + + def gather_details @mappings = get_concept_mappings(@concept) @notes = @concept.explore.notes diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index d61b1bfa5..69da36a19 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -134,6 +134,13 @@ def index def classes get_class(params) + + + # binding.pry + + + + if ["application/ld+json", "application/json"].include?(request.accept) render plain: @concept.to_jsonld, content_type: request.accept and return end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3948fe3a2..5612d667a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -72,9 +72,23 @@ def draw_tree(root, id = nil, type = "Menu") end def build_tree(node, string, id) + + + + + # binding.pry + + + + + if node.children.nil? || node.children.length < 1 return string # unchanged end + + + + node.children.sort! {|a,b| (a.prefLabel || a.id).downcase <=> (b.prefLabel || b.id).downcase} for child in node.children if child.id.eql?(id) diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index 88ea877d9..a1138a9c3 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -1,6 +1,17 @@ # frozen_string_literal: true module OntologiesHelper + + + + + # LANGUAGE_FILTERABLE_SECTIONS = %w[classes properties].freeze + + + + + + def additional_details return '' if $ADDITIONAL_ONTOLOGY_DETAILS.nil? || $ADDITIONAL_ONTOLOGY_DETAILS[@ontology.acronym].nil? @@ -158,4 +169,89 @@ def change_requests_enabled?(ontology_acronym) Rails.configuration.change_request[:ontologies].include? ontology_acronym.to_sym end + + + + + + + + # def current_section + # (params[:p]) ? params[:p] : 'summary' + # end + # + # def ontology_data_sections + # LANGUAGE_FILTERABLE_SECTIONS + # end + # + # def ontology_data_section?(section_title = current_section) + # ontology_data_sections.include?(section_title) + # end + + + + + def edit_sub_languages_button(ontology = @ontology, submission = @submission_latest) + return unless ontology.admin?(session[:user]) + + link = edit_ontology_submission_path(ontology.acronym, submission&.submissionId || '', properties: 'naturalLanguage', container_id: 'application_modal_content') + link_to_modal(nil, link, class: "btn", id:'fair-details-link', + data: { show_modal_title_value: t('ontologies.edit_natural_languages', acronym: ontology.acronym), show_modal_size_value: 'modal-md' }) do + + + render ChipButtonComponent.new(type: 'clickable', class: 'admin-background chip_button_small' ) do + (t('ontologies.edit_available_languages') + content_tag(:i, "", class: "fas fa-lg fa-edit")).html_safe + end + + + + end + end + + + + + + + + + + + + def language_selector_tag(name) + content_language_selector(id: name, name: name) + end + + + # def language_selector_hidden_tag(section) + # hidden_field_tag "language_selector_hidden_#{section}", '', + # data: { controller: "language-change", 'language-change-section-value': section, action: "change->language-change#dispatchLangChangeEvent" } + # end + + + + + + + + + def submission_languages(submission = @submission) + + # binding.pry + + Array(submission&.naturalLanguage).map { |natural_language| natural_language.split('/').last }.compact + end + + + + + + + + + + + + + end diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 373c3edc2..af57e97b0 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -3,3 +3,6 @@ // ./bin/rails generate stimulus controllerName import { application } from "./application" + +import LanguageChangeController from "./language_change_controller" +application.register("language-change", LanguageChangeController) diff --git a/app/views/layouts/_ontology_viewer.html.haml b/app/views/layouts/_ontology_viewer.html.haml index 64f4899d0..c1a0c95d6 100644 --- a/app/views/layouts/_ontology_viewer.html.haml +++ b/app/views/layouts/_ontology_viewer.html.haml @@ -36,6 +36,7 @@ jQuery(document).data().bp.ont_viewer.purl_prefix = "#{(Rails.configuration.settings.purl[:enabled] ? Rails.configuration.settings.purl[:prefix]+"/"+@ontology.acronym : '')}"; jQuery(document).data().bp.ont_viewer.concept_name_title = (jQuery(document).data().bp.ont_viewer.concept_name == "") ? "" : " - " + jQuery(document).data().bp.ont_viewer.concept_name; + jQuery(document).data().bp.ont_viewer.lang = "#{request_lang}"; -# Modal dialog for creating a new mapping (must reside in a top-level position in the document to display properly). %div#createNewMappingModal{class: "modal fade", tabindex: "-1", "aria-labelledby": "createNewMappingLabel"} @@ -139,6 +140,18 @@ class: "nav-link", type: 'button', role: "tab", data: {bp_ont_page: "widgets", "bs-toggle": "tab", "bs-target": "#ont_widgets_content"}, aria: {controls: "ont_widgets_content", selected: "false"}) + + + + + %li{class: 'nav-item', role: 'presentation'} + = language_selector_tag(:content_language) + = flag_icon(lang_code("en")) + + + + + %div.card-body -# Tab panes for displaying ontology content sections %div.tab-content{id: "ontology_content"} @@ -161,6 +174,20 @@ - if content_section.eql?("widgets") = yield + + + + + + + -# %div.tab-pane{id: "ont_languages_content", role: "tabpanel", aria: {labelledby: "ont-languages-tab"}} + -# = language_selector_hidden_tag("Classes") + + + + + + - if Rails.env.appliance? = render partial: "footer_appliance" diff --git a/config/locales/en.yml b/config/locales/en.yml index 8562532e7..e406c3c3f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,111 +1,1473 @@ +--- en: - - activerecord: + show_advanced_options: Show advanced options + hide_advanced_options: Hide advanced options + no_result_was_found: No result was found + + activaterecord: errors: models: license: attributes: encrypted_key: - invalid_license_key: is an invalid license key - no_appliance_id_for_comparison: couldn't be validated. Unable to retrieve virtual appliance ID. - appliance_id_mismatch: is an appliance ID mismatch + invalid_license_key: is an invalid license key. + no_appliance_id_for_comparison: Could not be validated. Unable to retrieve virtual appliance ID. + appliance_id_mismatch: is an appliance id mismatch. + # Make the resource_term 'ontology' configurable throughout the portal, allowing it to be replaced by alternative terms such as 'semantic_resource', 'vocabulary', 'terminology', or 'semantic_artefact' + resource_term: + ontology: ontology + ontology_plural: ontologies + ontology_single: an ontology - admin: - licenses: - create: - success: License successfully renewed! + semantic_resource: semantic resource + semantic_resource_plural: semantic resources + semantic_resource_single: a semantic resource + vocabulary: vocabulary + vocabulary_plural: vocabularies + vocabulary_single: a vocabulary + + terminology: terminology + terminology_plural: terminologies + terminology_single: a terminology + + semantic_artefact: semantic artefact + semantic_artefact_plural: semantic artefacts + semantic_artefact_single: a semantic artefact date: formats: - year_month_day_concise: '%Y-%m-%d' # 2017-03-01 - month_day_year: '%b %-d, %Y' # Mar 1, 2017 - monthfull_day_year: '%B %-d, %Y' # March 1, 2017 + year_month_day_concise: "%Y-%m-%d" # 2017-03-01 + month_day_year: "%b %-d, %Y" # Mar 1, 2017 + monthfull_day_year: "%B %-d, %Y" # March 1, 2017 + language: + translation_not_available: "%{language} translation not available" + portal_language_help_text: Indicate the language in which the interfaces should appear + search_language_help_text: Indicate the language on which to perform the search, restricting text matching exclusively to terms with that language + content_language_help_text_1: Indicate the language on which the content of this resource will be displayed. + content_language_help_text_2: The available languages are specified by the resource admin. + application: + powered_by: Powered by Ontoportal + projects: Projects + loading: loading... + errors_in_fields: Errors in fields %{errors} + add_comment: Add comment + add_new_comment: Add a new comment + reply: Reply + add_proposal: Add proposal + add_new_proposal: Add a new proposal + watching: Watching + beta_badge_text: Beta + beta_badge_tooltip: This feature is experimental and may have issues + ontology_not_found: Ontology %{acronym} not found + concept_not_found: Concept %{concept} not found + error_load: "Error: load failed" + not_found_message: Not Found + try_again: There was an error, please try again + provide_ontology_or_concept: Please provide an ontology id or concept id with an ontology id. + search_for_class: Please search for a class using the Jump To field above + missing_roots_for_ontology: Missing @roots for %{acronym} + missing_roots: Missing roots + missing_class: Missing class %{root_child} + missing_class_ontology: Missing class %{acronym} / %{concept_id} + error_simplify_class: "Failure to simplify class: %{cls}" + no_cache: "No cache or expired cache for ontology: %{id}" + incomplete_simple_ontology: "Incomplete simple ontology: %{id}, %{ont}" + label_xl: + error_valid_label_xl: "Error: You must provide a valid label_xl id" - layouts: - footer: - copyright_html: Copyright © 2005‑2024, The Board of Trustees of Leland Stanford Junior University. All rights reserved. - grant_html: > - The National Center for Biomedical Ontology was founded as one of the National Centers for Biomedical - Computing, supported by the NHGRI, the NHLBI, and the NIH Common Fund under grant U54-HG004028. - notices: - license_contact: > - For more information, email support@ontoportal.org or - visit https://ontoportal.org/licensing. - license_obtain: > - If you are the owner of this OntoPortal installation, you can visit - https://license.ontoportal.org to obtain a license. - license_expired: > - We're sorry, but the license for this OntoPortal installation has expired. If you are the owner of this OntoPortal installation, - please visit https://license.ontoportal.org to renew your license. - license_trial: - one: - This installation of the OntoPortal Appliance is a trial license, which will expire in 1 day. - other: - This installation of the OntoPortal Appliance is a trial license, which will expire in %{count} days. + admin: + query_not_permitted: Query not permitted + update_info_successfully: Update info successfully retrieved + error_update_info: Problem retrieving update info - %{message} + current_version: "Current version: %{version}" + appliance_id: "Appliance ID: %{id}" + no_record_exists: No record exists for ontology %{acronym} + cache_flush_success: UI cache successfully flushed + cache_flush_error: "Problem flushing the UI cache - %{class}: %{message}" + no_flush_command: "The UI cache does not respond to the 'flush_all' command" + cache_reset_success: UI cache connection successfully reset + cache_reset_error: Problem resetting the UI cache connection - %{message} + no_reset_command: The UI cache does not respond to the 'reset' command + clear_goo_cache_success: Goo cache successfully flushed + clear_goo_cache_error: "Problem flushing the Goo cache - %{class}: %{message}" + clear_http_cache_success: HTTP cache successfully flushed + clear_http_cache_error: "Problem flushing the HTTP cache - %{class}: %{message}" + refresh_report_without_ontologies: Refresh of ontologies report started successfully + refresh_report_with_ontologies: "Refresh of report for ontologies: %{ontologies} started successfully" + submission_deleted_successfully: Submission %{id} for ontology %{ont} was deleted successfully + submission_not_found: Submission %{id} for ontology %{ont} was not found in the system + ontology_not_found: Ontology %{ont} was not found in the system + problem_deleting_submission: "Problem deleting submission %{id} for ontology %{ont} - %{class}: %{message}" + problem_refreshing_report: "Problem refreshing report - %{class}: %{message}" + report_successfully_regenerated: Report successfully regenerated on %{report_date_generated} + ontologies_report_retrieved: Ontologies Report - retrieved %{ontologies} ontologies in %{time}s" + problem_retrieving_ontologies: Problem retrieving ontologies report - %{message} + no_ontologies_parameter_passed: "No ontologies parameter passed. Syntax: ?ontologies=ONT1,ONT2,...,ONTN" + ontology_process_success: "Ontology %{ont} %{success_keyword} successfully," + ontology_not_found_system: "Ontology %{ont} was not found in the system, " + ontology_process_error: "Problem %{error_keyword} ontology %{ont} - %{class}: %{message}," + new_ontologies_created_title: "The following ontologies: %{count} were created in this year" + turbo_confirm: "Are you sure you want to %{name}?" + analytics: + total_ontologies: Total Ontologies + ontologies_count: "%{size} new ontology this year" + ontologies_with_errors: Ontologies with errors + total_visits: Total visits + active_users: Active users + visit_users: "%{visits} users since last month" + ontology_visits: Ontology visits + unique_users_visits: Unique users visits + page_visits: Page visits of this month + main: + cache_management: CACHE MANAGEMENT + clear_cache: Flush UI cache + reset_cache: Reset UI cache connection + clear_goo_cache: Flush GOO cache + clear_http_cache: Flush HTTP cache + version_management: VERSION MANAGEMENT + checking_for_updates: Checking for updates ... + memcache: + title: Memcache stats + parse_log: + title: Parse Log for %{acronym} + log_file: "Log file: %{path}" + index: + title: Administration + administration_console: Administration console + analytics: Analytics + site_administration: Site Administration + ontology_administration: Ontology Administration + licensing: Licensing + users: Users + metadata_administration: Metadata Administration + groups: Groups + categories: Categories + persons_and_organizations: Persons & Organizations + sparql: SPARQL + search: Search & Indexation + report_generated_on: "Report generated on:" + licensed_to: Licensed to + appliance_id: Appliance ID + valid_till: Valid till + days_remaining: Days remaining + renew_license: Renew license + groups: + group_created: Group successfully created in %{time}s + group_error_creation: Problem creating the group - %{message} + group_added_successfully: New Group added successfully + group_updated_successfully: Group successfully updated in %{time}s + problem_of_updating: Problem updating the group - %{message} + group_deleted_successfully: group successfully deleted in in %{time}s + problem_of_deleting: Problem deleting the group - %{message} + synchronization_of_groups: Synchronization of groups started successfully + error_parsing: "Error parsing JSON response - %{class}: %{message}" + problem_synchronizing_groups: "Problem synchronizing groups - %{class}: %{message}" + edit_button: Edit + delete: Delete + info_error_delete: "Can't delete this group because still used" + turbo_confirm: Are you sure? + form: + acronym: Acronym + name: Name + description: Description + created: Created + ontologies: Ontologies + new: + create_new_group: Create new group + create_group: Create group + index: + create_a_new_group: Create a new group + synchronize_groups_slices: Synchronize groups with slices + id: ID + count: COUNT + actions: Actions + no_agents: There are currently no agents. + edit: + edit_group: Edit category + save: Save + categories: + category_created: Category successfully created in %{time}s + category_error_creation: Problem creating the category - %{message} + category_added_successfully: New Category added successfully + category_updated_successfully: Category successfully updated in %{time}s + problem_of_updating: Problem updating the category - %{message} + category_deleted_successfully: Category successfully deleted in %{time}s + problem_of_deleting: Problem deleting the category - %{message} + edit_button: "Edit" + delete: Delete + info_error_delete: Can't delete this category because still used + turbo_confirm: Are you sure? + form: + acronym: Acronym + name: Name + description: Description + created: Created + ontologies: Ontologies + edit: + edit_category: Edit category + save: Save + new: + create_new_category: Create new category + create_category: Create category + index: + create_a_new_category: Create a new category + id: ID + count: COUNT + actions: Actions + no_categories: There are currently no categories. + licenses: + license_notification: + license_contact: > + For more information, email support@ontoportal.org or + visit https://ontoportal.org/licensing. + license_obtain: > + If you are the owner of this OntoPortal installation, you can visit + https://license.ontoportal.org to obtain a license. + license_expired: > + We're sorry, but the license for this OntoPortal installation has expired. If you are the owner of this OntoPortal installation, + please visit https://license.ontoportal.org to renew your license. + license_trial: + one: + This installation of the OntoPortal Appliance is a trial license, which will expire in 1 day. + other: + This installation of the OntoPortal Appliance is a trial license, which will expire in %{count} days. + create: + success: License renewed successfully! + label_close: Close + form: + license_from_being_saved: "prohibited this license from being saved:" + paste_your_license: Paste your license key into the text area + submit: Submit + search: + index: + index_data: "INDEX DATA" + collections_management: "COLLECTIONS MANAGEMENT" + name: "Name" + actions: "Actions" + ontology: "Ontology" + agent: "Agent" + ontology_submission: "Ontology submission" + select_model: "Select a model to index" + generate_schema: "Generate schema" + see_schema: "See schema" + see_indexed_data: "See indexed data" + show: + field_name: "Field name" + type: "Type" + multi_valued: "Multi valued" + indexed: "Indexed" + stored: "Stored" + search: + search_query: "Search query" + page: "Page" + page_size: "Page size" + found_documents: "Found %{count} documents" + id: "ID" + properties: "Properties" + show_more: "+ Show more ..." + show_less: "- Show less ..." + + annotator: + title: Annotator + description: Get annotations for text with ontology classes + input_hint: Enter or past a text to be annotated ... + options: Options + whole_word_only: Whole word only + match_longest_only: Match longest only + include_mappings: Include mappings + exclude_numbers: Exclude numbers + exclude_synonyms: Exclude synonyms + select_umls_sementic_types: Select UMLS semantic types + select_umls_sementic_groups: Select UMLS semantic groups + include_ancestors: Include ancestors up to level + include_score: Include score + score_threshold: Filter by score threshold + confidence_threshold: Filter by confidence threshold + fastcontext: FastContext + lemmatize: Lemmatize + get_annotation: Get annotations + total_results: Total results + context: Context + certainty: Certainty + class: Class + ontology: Ontology + negation: Negation + temporality: Temporality + experiencer: Experiencer + score: Score + api_documentation: "Annotator API documentation" + insert_sample_text: Insert sample text + sample_text: There has been many recent studies on the use of microbial antagonists to control diseases incited by soilborne and airborne plant pathogenic bacteria + and fungi, in an attempt to replace existing methods of chemical control and avoid extensive use of fungicides, which often lead to resistance in plant pathogens. + In agriculture, plant growth-promoting and biocontrol microorganisms have emerged as safe alternatives to chemical pesticides. Streptomyces spp. and their + metabolites may have great potential as excellent agents for controlling various fungal and bacterial phytopathogens. + concepts: + error_valid_concept: "Error: You must provide a valid concept id" + missing_roots: Missing roots + date_sorted_list_error: The Classes/Concepts didn't define creation or modifications dates with dcterms + list_error: The Collection didn't define any member + close: Close + request_terms_instructions: | + The text below is displayed to the user as the instructions on how to request new terms for your ontology. + You can change this text for %{acronym} ontology by clicking anywhere on the existing text to + bring up the editing dialog. + Click 'Submit Changes' to save or 'Cancel' to discard your changes. + submit_changes: Submit Changes + cancel: Cancel + request_new_term: Request New Term + use_jump_to: Use the "Jump To" to find a class and display details, visualization, notes, and mappings + instances: Instances + notes: Notes + details: Details + visualization: Visualization + mappings: Mappings + new_term_requests: New Term Requests + permanent_link_class: Get a permanent link to this class + request_term: + new_term_instructions: | +

This ontology integrates with OntoloBridge, allowing community users to suggest additions to the public ontology. Complete the template below to submit a term request directly to the Ontology Manager.

+
+

Term label (required)
Suggested term name. If a term can be described with more than one synonym, enter only the preferred name here.

+
+
+

Term Description (required)
A brief definition, description, or usage of the suggested term. Synonyms of additional terms may be listed in this section.

+
+
+

Superclass (required)
The parent term of the suggested term. The parent term must be an existing entry in the current ontology. The superclass can be selected directly from the class tree.

+
+
+

References (optional)
Provide evidence that the requested term exists, such as IDs of articles or links to other resources describing the term.

+
+
+

Justification (optional)
Provide here any additional information about the requested term.

+
home: + bug: Bug + proposition: Proposition + question: Question + ontology_submissions_request: Ontology submissions request + include_name: Please include your name + include_email: Please include your email + include_comment: Please include your comment + fill_text: Please fill in the proper text from the supplied image + notice_feedback: Feedback has been sent + account_title: Account Information + agroportal_figures: "%{site} in figures" + benefit1: Discover new insights and knowledge by exploring other ontologies or smeantic resources in the repository. + benefit2: Contribute to knowledge sharing and semantic interoperability in your domain. + benefit3: Map your ontology to other relevant ones in the domain and collaborate with other users. + benefit4: Get feedback and suggestions from other users who can use and comment on your ontology. + benefit5: Precisely describe your ontology with relevant metadata and get a FAIR score for your ontology. + fair_details: See details + fairness: FAIR Score + get_annotations: Get annotations + get_recommendations: Get recommendations index: - find_ontology_placeholder: Start typing ontology name, then choose from list - query_placeholder: Enter a class, e.g. Melanoma - tagline: the world's most comprehensive repository of biomedical ontologies - title: Welcome to the %{organization} + tagline: The home of ontologies and semantic artefacts in agri-food and related domains. + title: Welcome to %{site} welcome: Welcome to %{site}, - help: - welcome: Welcome to the National Center for Biomedical Ontology’s %{site}. %{site} is a web-based application for accessing and sharing biomedical ontologies. - getting_started: > - %{site} allows users to browse, upload, download, search, comment on, and create mappings for ontologies. - browse: > - Users can browse and explore individual ontologies by navigating either a tree structure or an animated graphical view. Users can also view mappings and - ontology metadata, and download ontologies. Additionally, users who are signed in may submit a new ontology to the library. - rest_examples_html: View documentation and examples of the %{site} REST API. - announce_list_html: > - To receive notices of new releases or site outages, please subscribe to the - bioontology-support list. - - annotator: + ontology_upload: Do you want to share an ontology? + ontology_upload_benefits: 'By uploading and sharing your ontology to %{site}, you can:' + ontology_upload_button: Submit ontology + discover_ontologies_button: Discover ontologies + ontology_upload_desc: Uploading an ontology or another type of semantic artefact (vocabulary, terminology, thesaurus, ...) is a way of sharing your knowledge with others. + paste_text_prompt: Enter a paragraph of text or some keywords... + recommender_annotator: Recommender and Annotator + support_and_collaborations: Support & Collaborations + ontoportal_instances: Other OntoPortal Instances + see_details: See details + info_tooltip_text: "You are seing the average scores for all the public ontologies in AgroPortal. FAIR scores are computed with the O'FAIRe methodology. More details here: https://github.com/agroportal/fairness" + average: Average + min: Min + max: Max + ontologies: Ontologies + classes: Classes + individuals: Individuals + properties: Properties + projects: Projects + mappings: Mappings + users: Users + twitter_news: News + feedback: + error_on_form: Errors On Form + leave_your_feedback: Leave your feedback + feedback_hi_text: Hi + feedback_info_text: you can use the form below or email us directly at + email: Email + name: Name + proposition_url_page: Error/Proposition page URL + feedback: Feedback + tags: Tags + bug: Bug + proposition: Proposition + question: Question + ontology_submissions_request: Ontology submissions request + optional: (optional) + send: Send + feedback_complete: + title: Feedback sent successfully + message: Thank you for taking the time to share your feedback with us. Our support team will review your message and get back to you as soon as possible with a response. We value your opinion and appreciate your help in improving our services. Thank you for your support! + landscape: + #select_ontologies: Start typing to select ontologies or leave blank to use them all + classes: Number of classes + individuals: Number of individuals + properties: Number of properties + max_depth: Max depth + max_child_count: Max child count + average_child_count: Average child count + classes_with_one_child: Classes with one child + classes_with_more_than_25_children: Classes with more than 25 children + classes_with_no_definition: Classes with no definition + number_of_xioms_triples: Number of axioms (triples) + projects_count: "%{count} projects" + notes_count: "%{count} notes" + reviews_count: "%{count} reviews" + as_contact_count: "%{count} as contact" + as_contributor_count: "%{count} as contributor" + as_creator_count: "%{count} as creator" + as_curator_count: "%{count} as curator" + contributions: "Contributions: %{title}" + published_ontologies: published %{count} ontologies + funded_ontologies: funded %{count} ontologies + endorsed_ontologies: endorsed %{count} ontologies + number_of_ontologies_using_format: Number of ontologies using this format + number_of_ontologies_of_type: Number of ontologies of this ontology type + number_of_ontologies_of_formality_level: Number of ontologies of this formality level + number_of_ontologies_in_catalog: Number of ontologies in this catalog + number_of_ontologies: Number of ontologies + number_of_ontologies_with_class_count_in_range: Number of ontologies with a class count in this range + average: Average + fairness_interface_introduction: This interface shows how an ontology or a group responded successfully to O’FAIRe FAIRness assessment questions + fairness_interface_details: See details for each ontologies on the specific ontology summary pages + fairness_interface_hover_instruction: hover on a principle to see details + average_metrics: Average metrics + category: category + filter_network: Filter network + funding_endorsing_organizations: Organizations funding and endorsing the most ontologies + group: group + groups_and_categories: Groups and Categories + intro: Visualize the panorama of all ontologies on %{site} via metadata aggregation. + more_properties_charts: More properties charts + most_active_ontologies: Most active ontologies + most_active_organizations: Most active organizations + most_active_people: Most active people + most_active_people_as_reviewer: Most active user account + most_mentioned_people: Most mentioned people as contact, creator, contributor or curator + most_mentioned_people_as_reviewer: User account who published notes, reviews, and projects + ontologies_activity_on: Ontology activity + ontologies_by: Ontologies by %{type} + ontologies_contributors: Contributors to ontology development + ontologies_count_by_catalog: Number of ontologies in each semantic artefact catalog + ontologies_formats: Representation language + ontologies_languages: Natural languages + ontologies_licenses: Licenses + ontologies_with_notes_reviews_projects: Ontologies with notes, reviews, and projects + ontology_fairness_evaluator: Ontology FAIRness Evaluator (O’FAIRe) + ontology_formality_levels: Formality levels + ontology_properties_pie_charts: Pie charts retated to object description properties (i.e., meatdata properties used to describe ontology objects). + ontology_relations_network: Ontology relations network + ontology_tools: Most used tools to build ontologies + owl_ontology_author_uris: Properties used to specify object author + owl_ontology_definition_uris: Properties used to specify objects definition + owl_ontology_preflabel_uris: Properties used to specify objects preferred label + owl_ontology_synonym_uris: Properties used to specify object synonyms + properties_usage_proportion: Pie charts related to the most frequent values for certain metadata properties + properties_use: Property usage + relations_between_stored_ontologies: Set of relationships between %{site} ontologies as captured by metadata. Green ontologies are stored in the repository while those in blue are external resources. + size: size + title: "Landscape" + ontologies_properties: Ontologies properties + layout: + header: + account_setting: Account settings + annotator: Annotator + browse: Browse + documentation: Documentation + help: Help + landscape: Landscape + login: Login + logout: Logout + mappings: Mappings + recommender: Recommender + publications: Publications + release_notes: Release Notes + search_prompt: Search in %{portal_name} ... + submit_feedback: Send Feedback + support: Support + cite_us: Cite Us + footer: + products: Products + ontoportal: OntoPortal + release_notes: Release Notes + api: API + tools: Tools + sparql: SPARQL + support: Support + contact_us: Contact Us + wiki: Wiki + documentation: Documentation + agro_documentation: AgroPortal documentation + agreements: Legal + terms: Terms and Conditions + privacy_policy: Privacy Policy + legal_notices: Legal Notices + cite_us: Cite Us + acknowledgments: Acknowledgments + about: About + about_us: About Us + projects: D2KAB + team: Team + notices: + slice: Slice + at: at + visit_the_site: visit the full site + login: + invalid_account_combination: "Invalid account name/password combination" + authentication_failed: "%{provider} authentication failed" + admin_logged_out: Logged out %{old_user}, returned to %{user} + user_logged_out: You have successfully logged out + try_again_notice: ". Please try again." + reset_password_again: ". Please reset your password again." + custom_ontology_set: "The display is now based on your Custom Ontology Set." + welcome: "Welcome " + error_account_name: Please enter an account name + error_password: Please enter a password + enter_email: Enter your username + enter_password: Enter your password + forgot_password: Forgot password? + invalid_login: Errors on the form + no_account: Do not have an account? + password: Password + register: Register + title: Login + username_or_email: Username + login_with_provider: Login with %{provider} + reset_password_message: A password reset email has been sent to your email address, please follow the instructions to reset your password. + back_home_button: Back home + recover_password: Recover password + email: Email + user: User + email_address_associated: Enter the email address associated with your account and we will send an email with instructions to reset your password. + email_placeholder: Enter the email + send_instructions: Send instructions + statistics: + title: "%{portal} Statistics" + lead: (last %{last_years} years) + ontologies: Ontologies + users: Users + projects: Projects + date: Date + ontology_visits: Ontology visits + mappings: + all: All + description: Dive into an overview of the mappings in the bubble view, efficiently locate a specific ontology in the table view or upload your own mappings. + tabs: + bubble_view: Bubbles view + table_view: Table view + upload_mappings: Upload mappings + filter_ontologies: Filter ontologies in the bubble view + filter_bubbles: Filter bubbles + external_mappings: "External Mappings (%{number_with_delimiter})" + interportal_mappings: "Interportal Mappings - %{acronym} (%{number_with_delimiter})" + test_bulk_load: This is the mappings produced to test the bulk load + mapping_created: Mapping created + mapping_updated: Mapping updated + mapping_deleted: "%{map_id} deleted successfully" + mapping_not_found: "Mapping %{id} not found" + error_of_source_and_target: Source and target concepts need to be specified + mapping_issue: "Mapping issue with '%{mapping}' : %{message}" + find_mappings: Find mappings of a class/concept + intro: Find all the mappings of an ontology + loading_mappings: Loading mappings... + no_mappings_available: No mappings available + title: Mappings + upload_mappings: Upload mappings + select_ontologies_list: Select ontologies + bubble_view_legend: + bubble_size: "Bubble size:" + bubble_size_desc: "The global number of mappings with all other ontologies." + color_degree: "Color degree:" + color_degree_desc: "The number of mappings with the selected ontology." + yellow_bubble: "Yellow bubble: " + selected_bubble: "The selected bubble." + less_mappings: "Less mappings" + more_mappings: "More mappings" + count: + ontology: Ontology + mappings: Mappings + no_mappings: There are no mappings to or from this ontology + form: + source_class: Source class + mapping_name: Mapping description (name) + contact_info: Contact info + mapping_source_name: Source name (Mapping set id) + mapping_comment: Comment + mapping_relation: Mapping relation type + save: Save + mapping_table: + mapping_to: Mapping to + relations: Relations + source: Source + type: Type + actions: Actions + no_mappings: There are currently no mappings for this class. + mapping_type_selector: + mapping_type: Mapping type + internal: Internal + interportal: InterPortal + external: External + target_class: Target class + details: Details + ontology_acronym: Ontology (acronym) + class: Class + ontology_acronym_placeholder: Enter the ontology ACRONYM + class_uri_placeholder: Enter the class URI + ontology_uri_placeholder: Enter the ontology URI + show_line: + edit_modal: Edit + delete_button: Delete + turbo_confirm: Are you sure? + edit_mapping: Edit mapping for %{preflabel} + show: + no_mappings_found: No mappings found + bulk_loader: + loader: + example_of_valid_file: See an example of a valid a file + save: Save + loaded_mappings: + mappings_created: "%{size} mappings created successfully" + id: Id + source: Source + target: Target + relation: Relation + properties: Properties + actions: Actions + see_other_properties: See other properties + agents: + not_found_agent: Agent with id %{id} + add_agent: New Agent added successfully + update_agent: Agent successfully updated + agent_usages_updated: Agent usages successfully updated + agent_already_deleted: Agent %{id} already deleted + agent_deleted_successfully: Agent %{id} deleted successfully + ontology_not_valid: "ontology is not valid, here are the errors: " + save: Save + not_used: Not used + see_usages_count: See usages (%{count}) + delete: Delete + delete_error: "Can't delete this %{agent} because still used" + turbo_confirm: Are you sure? + modal_title: "Agent \"%{name}\" usages" + form: + type: Type + person: Person + organization: Organization + name: Name + email: Email + acronym: Acronym + homepage: Homepage + creator: Creator + identifiers: Identifiers + affiliations: Affiliations + index: + create_new_agent: Create New Agent + first_name: First name + usages: Usages + actions: Actions + no_agents: There are currently no agents. + ontology_details: + sections: + classes: Classes + summary: Summary + properties: Properties + instances: Instances + notes: Notes + mappings: Mappings + widgets: Widgets + sparql: Sparql + concepts: Concepts + schemes: Schemes + collections: Collections + concept: + definitions: Definitions + id: ID + in_schemes: In schemes + member_of: Member of + no_preferred_name_for_selected_language: No preferred name for selected language. + obsolete: Obsolete + preferred_name: Preferred name + synonyms: Synonyms + type: Type + metadata: + additional_metadata: Additional Metadata + header: + last_submission_date: Last submission date + projects: + project_not_found: "Project not found: %{id}" + project_successfully_created: Project successfully created + error_unique_acronym: Project with acronym %{acronym} already exists. Please enter a unique acronym. + project_successfully_updated: Project successfully updated + error_delete_project: "Project delete failed: %{errors}" + project_successfully_deleted: Project successfully deleted + contacts: Contacts + create_new_project: Create new project + created: Created + creator: User + delete_admin_only: Delete (admin only) + delete_confirm: Are you sure? + description: Description + description_text: Description text + edit_text: Edit + home_page: Home page index: - intro: Get annotations for biomedical text with classes from the ontologies - annotatorplus_html: Check out AnnotatorPlus beta; a new version of the Annotator with added support for negation, and more! + intro: Browse a selection of projects that use %{site} ontologies + institutions: Institutions + ontologies: Ontologies + project_description: Project Description + self: Projects + title: Projects List + view_projects_help: View projects help - recommender: - intro: Get recommendations for the most relevant ontologies based on an excerpt from a biomedical text or a list of keywords + show: + title: Project %{name} + edit_project: Edit Project + description: "Description:" + institution: "Institution:" + contacts: "Contacts:" + home_page: "Home Page:" + ontologies_used: Ontologies Used + no_ontologies_associated: No ontologies are currently associated with this project - search: + form: + errors_on_form: Errors On Form + name: "Name: *" + acronym: "Acronym: *" + administrators: "Administrators: *" + home_page: "Homepage: *" + example: "Example:" + description: "Description:*" + select_ontologies: Select Ontologies Used + select_administrators: Select administrators + + edit: + title: Editing Project %{name} + editing_project: Editing project + cancel: Cancel + update_project: Update Project + + new: + title: Add your project + new_project: New project + create_project: Create Project + recommender: + title: Recommender + intro: Get recommendations for the most relevant ontologies from an excerpt of text or a list of keywords + options: Options + input: Input + text: Text + keywords: Keywords + output: Output + ontologies: Ontologies + ontology_sets: Ontology sets + hint: Paste a paragraph of text or some keywords ... + weights_configuration: Weights configuration + coverage: Coverage + acceptance: Acceptance + knowledge_detail: Knowledge detail + specialization: Specialization + ontologies_configuration: Ontologies configuration + max_ont_set: Maximum number of ontologies per set + select_ontologies: Select ontologies + get_recommendations: Get recommendations + edit: Edit + results_title: Recommended ontologies + call_annotator: Call Annotator with the same input + cite: Cite + results_table: + ontology: Ontology + final_score: Final score + coverage_score: Coverage score + acceptance_score: Acceptance score + detail_score: Detail score + specialization_score: Specialization score + annotations: Annotations + register: + account_errors: 'Errors creating your account:' + confirm_password: Confirm password + create_account: Create new account + email: Email + first_name: First name + last_name: Last name + mailing_list: Register to the %{site}'s mailing list + accept_terms_and_conditions: I acknowledge and accept + terms_and_conditions: "%{site}'s terms and conditions." + optional: "(Optional)" + password: Password + title: Register + username: Username + users: + account_successfully_created: Account was successfully created + account_successfully_updated: Account was successfully updated + user_deleted_successfully: User deleted successfully + not_permitted: Not permitted + error_saving_custom_ontologies: Error saving Custom Ontologies, please try again + custom_ontologies_cleared: Custom Ontologies were cleared + custom_ontologies_saved: Custom Ontologies were saved + subscribe_flash_message: "You have successfully %{action} %{to_or_from} our user mailing list: %{list}" + error_subscribe: Something went wrong ... + validate_email_address: Please enter an email address + validate_password: Please enter a password + validate_password_confirmation: Your Password and Password Confirmation do not match + recaptcha_validation: Please fill in the proper text from the supplied image + validate_orcid: Please enter a valid ORCID. + validate_username: please enter a valid username + valid_email_adresse: Please enter a valid email adresse + validate_terms_and_conditions: Accepting the terms and conditions is required, please check the box to proceed. + first_name_required: First name field is required + last_name_required: Last name field is required index: - intro: Search for a class in multiple ontologies - search_keywords_placeholder: Enter a class, e.g. Melanoma - categories_placeholder: Start typing to select categories or leave blank to use all - property_definition: Named association between two entities. Examples are "definition" (a relation between a class and some text) and "part-of" (a relation between two classes). - obsolete_definition: > - A class that the authors of the ontology have flagged as being obsolete and which they recommend that people not use. These classes - are often left in ontologies (rather than removing them entirely) so that existing systems that depend on them will continue to function. + first_name: First name + last_name: Last name + username: Username + email: Email + roles: Roles + ontologies: Ontologies + project: Project + created: Created At + actions: Actions + detail: Detail + delete: Delete + login_as: Login as + error_delete_message: Can't delete this user because still used + turbo_confirm: Are you sure? + edit: + user_to_admin: Are you sure you want to make this user an admin? + admin_privileges: Are you sure you want to revoke admin privileges for this user? + errors_creating_account: "Errors creating your account:" + edit_information: Edit personal information + first_name: First name + last_name: Last name + username: Username + email: Email + orcid_id: ORCID ID + github_id: Github ID + new_password: New password + confirm_password: Confirm password + change_password: Change password + show: + my_account: My account + first_name: "First name:" + last_name: "Last name:" + username: "Username:" + email: "Email:" + orcid_id: "ORCID ID:" + github_id: "GitHub ID:" + mailing_list_subscription: Mailing list subscription + mailing_list_description: Register to the %{portal} announcements mailing list. + api_key_description: Your API Key can be used to access %{portal} Web services + api_documentation: API documentation + custom_semantic_resource: Custom ontology set + please: Please + modify_custom_semantic_resource: | + visit the main site + to modify your Custom Ontology Set. + customize_portal_display: "Customize your %{portal} display: Pick the ontologies that you want to see on %{portal} will hide all other ontologies." + select_semantic_resources: Select ontologies + no_semantic_resources: You haven't picked any ontologies yet + note_feature_logged_in: "Note: this feature works only when you are logged in." + save_custom_semantic_resources: Save custom ontologies + not_subscribed: Not subscribed to any ontology + submitted_semantic_resources: Submitted Ontologies + upload_semantic_resources: Upload Ontologies + projects_created: Projects Created + no_project_created: No project created + unsubscribe: Unsubscribe + subscribe: Subscribe + subscriptions: Subscriptions + no_uploaded_resources: You didn't upload any ontology yet + notes: Notes + search: + no_search_class_provided: No search class provided + search_place_holder: Enter a term, e.g. plant height + advanced_options: + search_language: Search language + ontologies: Ontologies + include_in_search_title: Include in search + include_in_search_values: + property_values: Property values + obolete_classses: Obsolete classes + ontology_views: Ontology views + show_only_title: Show only + show_only_values: + exact_matches: Exact matches + classes_with_definitions: Classes with definitions + show_advanced_options: Show options + hide_advanced_options: Hide options + match_in: Match in + ontologies: ontologies + result_component: + details: Details + visualize: Vizualize + more_from_ontology: more from this ontology + reuses_in: Reuses in + notes: + no_notes: No notes to display + filter_hide_archived: Hide Archived + new_class_proposal: New Class Proposal + new_relationship_proposal: New Relationship Proposal + change_property_value_proposal: Change Property Value Proposal + new_comment_added: New comment added successfully + note_deleted_successfully: Note %{note_id} was deleted successfully + note_not_found: Note %{note_id} was not found in the system + comment: Comment + new_comment: + subject: Subject + comment: Comment + save: Save + new_proposal: + reason_for_change: Reason for change + new_target: New target + old_target: Old target + relationship_type: Relationship type + property_id: Property id + new_value: New value + old_value: Old value + class_id: Class id + label: Label + synonym: Synonym + definition: Definition + parent: Parent + save: save + note_line: + alert_text: "Are you sure you want to delete the note ''%{subject}'' created by %{creator}?
This action CAN NOT be undone!!!" + delete: Delete + archived: archived + comment: Comment + thread: + submitted_by: submitted by + reply: + about_note_decorator: about %{note_decorator} + comment: Comment + save: save + cancel: cancel + ontolobridge: + problem_of_creating_new_term: "Problem creating a new term %{endpoint}: %{class} - %{message}" + new_term_instructions_saved: New term request instructions for %{acronym} saved + error_saving_new_term_instructions: Unable to save new term instructions for %{acronym} due to a server error + submissions: + filter: + all_formats: All formats + sort_by_name: Sort by name + sort_by_classes: Sort by classes count + sort_by_instances_concepts: Sort by instances/concepts count + sort_by_submitted_date: Sort by submitted date + sort_by_creation_date: Sort by creation date + sort_by_fair_score: Sort by FAIR score + sort_by_popularity: Sort by popularity + sort_by_notes: Sort by notes + sort_by_projects: Sort by projects + no_submissions_for_ontology: "No submissions for ontology: %{ontology}" + submission_updated_successfully: Submission updated successfully + save_button: Save + id: ID + version: Version + actions: Actions + modified: Modified + submitted: Submitted + download: Download + toggle_dropdown: Toggle Dropdown + go_to_api: Go to API + edit_button: Edit + delete_submission_alert: + content_1: "Are you sure you want to delete submission " + content_2: " for ontology " + content_3: "?
This action CAN NOT be undone!!!" + ontoportal_virtual_appliance: + problem_adding_account: "Problem adding account %{id}: account does not exist" + require_login: You must be logged in to access this section + title: OntoPortal Virtual Appliance Download + intro_paragraph: + main: | + OntoPortal Virtual Appliance distribution contains a pre-installed, + pre-configured version of commonly-used open source NCBO software running + on a Linux operating system. + included_software: "The following software is included on the image:" + ontologies_api: Ontologies API (REST service) + annotator: Annotator + recommender: Recommender + web_user_interface: BioPortal Web User Interface (including ontology visualization, widgets, and Annotator UI) + see_documentation: Please see our + more_information: for more information on working with the Appliance. + download_button_text: Download Latest OntoPortal + archives: + title: Archives + archival_distribution: | + The OntoPortal 2.5 Virtual Appliance is not longer offered for new + users. This archival distribution is available only as a backup for + those who registered for the Appliance before June 2020 + version: version 2.5 + admin_add_users: + title: "Admin: Add Users" + label_account_name: "Account Name:" + add_user_button: Add User + accounts_with_access: "Accounts with access" + export_users_link: "Export Appliance Users As CSV" + bioportal_user: BioPortal User ID + submission_inputs: + edit_ontology_title: Edit ontology + metadata_selector_label: "Filter properties to show" + metadata_selector_placeholder: "Start typing to select properties" + administrators: Administrators + ontology_skos_language_link: Please refer to the documentation for more details. + ontology_skos_language_help: > + SKOS vocabularies submitted to %{portal_name} shall follow a few constraints (e.g., contain a minimum of one skos:ConceptScheme also typed as owl:Ontology) + and top concept assertion. %{link} + ontology_obo_language_link: the OBOinOWL parser. + ontology_obo_language_help: > + OBO ontologies submitted to %{portal_name} will be parsed by the OWL-API which integrates %{link} + The resulting RDF triples will then be loaded in %{portal_name} triple-store. + ontology_owl_language_link: the Protégé + ontology_owl_language_help: > + OWL ontologies submitted to %{portal_name} will be parsed by the OWL-API. An easy way to verify if your ontology will parse is to open it with + %{link} + software which does use the same component. - projects: - index: - intro: Browse a selection of projects that use %{site} resources + ontology_umls_language_link: by the UMLS2RDF tool. + ontology_umls_language_help: > + UMLS-RRF resources are usually produced %{link} + + groups: Groups + visibility: Visibility + accounts_allowed: Add or remove accounts that are allowed to see this ontology in %{portal_name}. + ontology_view_of_another_ontology: Is this ontology a view of another ontology? + contact: Contact + equivalents: Equivalents + validators: Validators + help_text: Help text + contact_name: "%{name} Name" + contact_email: "%{name} Email" + edit_metadata_instruction: "Edit the metadata of your ontology here. Some of these values are used by %{portal_name} functionalities, including for FAIRness assessment. %{link}" + edit_metadata_instruction_link: "See guidelines and recommendations for metadata here." + license_help: "%{portal_name} requires an URI for the license. If you do not find your choice here, %{link}" + license_help_link: "Please pick up an URI from here." + deprecated_help: "An ontology with status retired shall necessarily be also deprecated, but not the opposite." + known_usage_help: "Consider also declaring %{metadata_knownUsage_help}" + known_usage_help_link: "the projects that are using the ontology" + help_creator: "The following properties take for value an 'agent' in %{portal_name} (either a person or an organization). These agents are shared over all the ontologies and suggested with autocompletion if they already exist. Editing an agent here will change it to all the ontologies that agent is involved in." + version_help: "For more information on how to encode versioning information in an ontology, see %{link}" + version_helper_link: "guidelines and recommendations." + instances: + id: ID + type: Type + label: Label + collections: + error_valid_collection: "Error: You must provide a valid collection id" + no_collections_alert: "%{acronym} does not contain collections (skos:Collection)" + id: ID + preferred_name: Preferred name + members_count: Members count + type: Type + change_requests: + change_request_success_message: Your change request was successfully submitted! View the %{url} on GitHub. + label: Label + type: Type + comment: Comment + close: Close + submit: Submit + check_resolvability: + check_resolvability_message_1: "The URL is resolvable and support the following formats: %{supported_format}" + check_resolvability_message_2: "The URL resolvable but is not content negotiable, support only: %{supported_format}" + check_resolvability_message_3: The URL is not resolvable and not content negotiable (returns %{status}). + uri_placeholder: Type a URI to test its resolvability + show_help: Show help + format_not_specified: Format not specified + how_it_works: + title: How it works + content_1: | + The check resolvability tool allows you to test if a given URL is resolvable. It is based on the HTTP HEAD method. + We check the resolvability of a URL by sending a HEAD request to the URL and checking if the response status code is 200 (OK) and + the returned content type is equal to one of the following %{resolvability_formats}. + content_2: | + We have a timeout set to %{resolvability_timeout} seconds, so if the URL is not resolvable within that time, the check will fail. + And a max redirections set to %{resolvability_max_redirections}, so if the URL is not resolvable within that number of redirections, the check will fail. + resolving_uri: + title: Resolving a URI + content: | + In the context of web semantics, dereferencing refers to the process of resolving and obtaining the actual data associated with a Uniform Resource Identifier (URI). In simpler terms, it involves following a link represented by a URI/IRI to retrieve information or resources linked to that identifier. + In the Semantic Web, URIs are used to uniquely identify resources, and dereferencing allows systems to access and retrieve data related to those resources. When a URI/IRI is dereferenced, it typically leads to the retrieval of RDF (Resource Description Framework) data or other structured information that describes the resource in a machine-readable format. This enables systems to understand and process the meaning of the linked data, facilitating the exchange and integration of information on the web. + content_negotiation: + title: Content negotiation + content: | + Content negotiation in the context of the Semantic Web refers to the mechanism by which two communicating parties, such as a client and a server, agree on the most suitable representation of a resource during the process of dereferencing a URI or IRI. This negotiation aims to ensure effective communication between different systems that may prefer different data formats or languages. + In other words, when a client requests a resource by dereferencing a URI, it indicates its preferences for the format or language of the response data through HTTP headers or other negotiation mechanisms. The server, in turn, examines these preferences and selects the most appropriate representation of the resource based on what is available. + different formats can be agreed upon between a client and server when accessing linked data. Common formats include: + RDF/XML: XML-based representation of Resource Description Framework data. + Turtle: Human-readable serialization format for RDF. + JSON-LD: JSON-based format for linked data. + N-Triples and N-Quads: Text formats for expressing RDF triples and quads. + HTML: Markup language for web pages, also used to embed RDF data. + RDFa: Embedding RDF data in HTML or XML using attributes. + SPARQL Query Results XML and JSON: Formats for representing SPARQL query results. + + errors: + error_message: We're sorry but something has gone wrong. + notification_error: We have been notified of this error. + go_home_button: Go home + send_feedback_button: Send a feedback + not_found_page: Page not found + schemes: + error_valid_scheme_id: "Error: You must provide a valid scheme id" + no_main_scheme_alert: no main scheme defined in the URI attribute + no_schemes_alert: "%{acronym} does not contain schemes (skos:ConceptScheme)" + id: ID + preferred_name: Preferred name + type: Type + fair_score: + fairness_unreachable_warning: FAIRness service issue unreachable + go_to_api: Go to API + mod_link: MOD1.4 + see_the_used_properties: see the used properties + metadata_properties: Metadata properties listed in the following are picked from %{mod_link}. + ontology_repository: This ontology repository implements %{mod_link} but does not necessarily encode metadata with the same properties (%{submission_link}). + see_possible_credits: See possible credits + see_metadata_used_properties: See metadata used properties + not_found: not found + "null": "null" + view_fair_scores_definitions: "You are seeing the FAIRness assessment score for this ontology in AgroPortal. FAIR scores are computed with the O'FAIRe methodology. More details here: https://github.com/agroportal/fairness" + ontologies_metadata_curator: + #bulk_edit: Start bulk edit + #use_the_bulk_edit: To use the bulk edit select in the table submissions (the rows) and metadata properties (the columns) for which you want to edit + start_the_bulk_edit: Select in the table submissions (rows) and metadata properties (columns) to start the bulk edit + alert_success_submissions: Submissions were successfully updated + ontologies: Ontologies + get_values: Get values + select_ontologies_and_metadata: select ontologies and the metadata properties + include_all_submissions: Include all submissions + apply_the_change_for_all: Apply the change for all + update_the_current_displayed_content: "will update the current displayed content to all the following submissions:" + save: Save ontologies: - intro: Browse the library of ontologies + showing_ontologies_size: "Showing %{ontologies_size} of %{analytics_size}" + filters: Filters + no_license: No license + view_license: View license + access_rights_information: Additional license and access rights information + referred_to: It can also be referred to as + private_ontology: Private Ontology + formality_levels: Formality levels + categories: Categories + groups: Groups + ontology_types: Ontology types + natural_languages: Natural languages + showing: Showing + metadata_properties: of %{acronym} metadata properties are filled + home_page: Home Page + ontology_processing_failed: "The ontology processing failed, with the current statuses: %{status}" + ontology_parsing_succeeded: "The ontology parsing succeeded, but some processing steps failed, here are the current statuses: %{status}" + upload_an_ontology: Upload an ontology. Sections such as %{ontology} will be available once done. + new_ontology_is_processing: The ontology is processing. Sections such as %{ontology} will be available once processing is complete. + ontology_is_processing: The ontology is processing. Sections such as %{ontology} will be updated once processing is complete. + contact_support: Contact support + edit_natural_languages: Edit natural languages of %{acronym} + edit_available_languages: Click here to edit available languages + add_new_submission: Add new submission + edit_metadata: Edit metadata + go_to_api: Go to API + projects_using_ontology: Projects using %{acronym} + create_new_project: Create new project + no_projects_using_ontology: No projects using %{acronym} + outside: outside + relation_with_other_ontologies: Relation with other ontologies either in %{inside} or %{outside} + ontology_search_prompt: 'Search an ontology or a term (e.g., plant height)' + views: Views + create_new_view: Create new view + expand_all: Expand All + collapse_all: Collapse All + administrator: "Administrator:" + create_new_view_submission: create new view submission + description: "Description:" + definition: "Definition:" + created_by: "Created By:" + submission: Submission + release_date: Release Date + upload_date: Upload Date + downloads: Downloads + admin_links: Admin Links + edit_link: Edit + no_views: No views available for %{acronym}. + fairness_assessment_questions: O'FAIRe FAIRness assessment questions + see_details: See details + collecting_data: We are still collecting data for %{acronym} + location: Location + allow_users: Allow users to view the metadata of your ontology (Summary) only. + metadata_only: Metadata only (No file) + load_from_url: Load from URL + new_versions_loaded: New versions loaded on a nightly basis. + upload_local_file: Upload local file + ontology_submitted: Ontology submitted successfully! + users_can_see: Users can now see + your_ontology: your ontology + exploring_and_searching: | + in our ontology list but they cannot explore or search it. To enable exploring and searching, + please upload a full version of your ontology. + submitting_ontology_view: Thank you for submitting your ontology view to %{site}. + submitting_ontology: Thank you for submitting your ontology to %{site}. + processing_message: | + We will now put your ontology in the queue to be processed. Please keep in mind that it may take up to several + hours before %{site} users will be able to explore and search your ontology. + notification_message: "When your ontology is ready for viewing, you will receive an email notification and it will be available here:" + contact_support_at: "If you have any questions or problems, please email the %{site} support team at:" + collecting_data_message: We are still collecting data for %{acronym} + submit_new_ontology: Submit new ontology + details: Details + general_information: General information + dates_and_contacts: Dates and contacts + uri: URI + change_notes: Change notes + modification_date: Modification date + date_of_original_creation: Date of original creation + concepts_browsers: + select_scheme: Please select a scheme to display + missing_roots: Missing roots for %{acronym} (skos:topConceptOf) + select_collection: Please select a collection to display + placeholder_jump: Jump to + label_jump: "Jump to:" + filter: Filter + browser: + admin_welcome: Welcome admin,this coloring indicates admin-only features + show_private_ontology: Show private ontology only + show_ontology_views: Show ontology views + show_retired_ontologies: Show retired ontologies + search_placeholder: Start typing to filter ontologies, e.g., AGROVOC... + sections: + classes: Classes + individuals: Individuals + properties: Properties + maximum_depth: Maximum depth + maximum_number_of_children: Maximum number of children + average_number_of_children: Average number of children + classes_with_a_single_child: Classes with a single child + classes_with_more_than_25_children: Classes with more than 25 children + classes_with_no_definition: Classes with no definition + identifiers: Identifiers + identifiers_tooltip: Principal identifiers of the ontology. + dates: Dates + person_and_organization: Persons and organizations + other_links: Other links + info_tooltip_links: Metadata properties that highlight the links enabling access to datasets, downloading ontologies, etc + projects_and_usage_information: Projects and usage information + info_tooltip_projects: Details pertaining to the utilization of the ontology. + methodology_and_provenance: Methodology and provenance + 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. + visits: Visits + views: Views of %{acronym} + create_new_view: Create new view + configuration_metadata: Configuration metadata + info_tooltip_configuration: Regroup all the metadata properties used by the portal to configure the behavior of the resource + label_groups: Groups + download_as_csv: Download as CSV + scroll_down_to_see_more: Scroll down to see more - concepts: - request_term: - new_term_instructions: > -

This ontology integrates with OntoloBridge, allowing community users to suggest additions to the public ontology. Complete the template below to submit a term request directly to the ontology maintainer.

-
-

Term Label (required)
Suggested term name. If a term can be described with multiple synonyms, only list the preferred name here.

-
-
-

Term description (required)
A brief definition, description, or usage of your suggested term. Additional term synonyms may be listed in this section.

-
-
-

Superclass (required)
The parent term of the suggested term. The parent term should be an existing entry of the current ontology. The superclass can be selected directly from Bioportal's Classes tree viewer.

-
-
-

References (optional)
Provide evidence for the existence of the requested term such as Pubmed IDs of papers or links to other resources that describe the term.

-
-
-

Justification (optional)
Provide any additional information about the requested term here.

-
- mappings: - intro: Browse mappings between classes in different ontologies + widgets: Widgets are only available for ontologies stored in %{site}. + add_acronym_widgets: Add %{acronym} Web Widgets to your site + widget_block_component_title_1: Jump To + widget_block_component_description_1: Type a class name from %{acronym} and jump to it in %{site} + download: Download the + put_on_server: and put it on your server. + copy_and_paste: Copy the code below and paste it to your HTML page + note: "Note:" + use_quick_jump: If you would like to use Quick Jump across multiple ontologies + enter_list_of_ontologies: You can enter a comma-separated list of ontology ids + set_the_variable: "You can set the variable to 'all' to search all ontologies in %{site}:" + include_definitions: "To include definitions in the Jump To drop-down, add the following variable in Javascript:" + info_pasted_code: In the code that you just pasted, make sure to change the path to the quick_jump.js file to point to the location where you put the file (relative to your HTML file) + example_to_use_code: "For example, if you put the quick_jump.js file in the same directory as your HTML file, this is the code you would use:" + help_visit: For more help visit + widget_block_component_title_2: Autocomplete + widget_block_component_description_2: Fill your form fields with classes from %{acronym} + example_1: Example 1 (start typing the class name to get its full URI) + example_2: Example 2 (get the ID for a class) + example_3: Example 3 (get the preferred name for a class) + include_file: In the header for the page where you want the form field, include the + use_widget: "On your form, for the fields where you want to use the class-selection widget, specify the field's class in the following format:" + for_example: For example, + use_ontology: will use NCI Thesaurus (ontology id is NCIT) and will put the class URI in the field after the user selects the class from the pull-down list. + use_list: "In addition to single ontology ids, you can use a list:" + or: OR + use_all_to_search: "use 'all' to search across all %{site} ontologies:" + autocomplete_widget_accesses: The autocomplete widget accesses ontology content from the latest version of the ontology. + use_following_parameters: "You can use the following parameters to select which value will be placed into the user-visible input field:" + uri: uri + shortid: shortid + name: name + class_uri_description: "Put the complete URI of the class (e.g.," + class_uri_example: 'put the complete URI of the class (e.g., %{link})' + class_shortid_description: put the short id of the class, as used in %{site} (e.g., "Common_Neoplasm"); + class_name_description: put the preferred name of the class (e.g., "Common Neoplasm"); + hidden_elements_description: "In addition to the input element you defined, there are four hidden form elements that are created and then set when a user selects a class from the list. For example, if you create a field with this code:" + hidden_elements_example: "The 'name' attribute is used to create the four following fields (note how the 'a' from the name attribute is appended to the id attribute):" + additional_parameters: Additional parameters are documented on the + more_help_visit: For more help visit + widget_block_component_title_3: Visualization + widget_block_component_description_3: Display a visualization for a given class in %{acronym} + widget_block_component_title_4: Tree Widget + widget_block_component_description_4: Display a class tree with a search field for %{acronym} + can_also_view: You can also view a + detailed_demonstration: detailed demonstration + ncbo_widget_wiki: NCBO Widget Wiki + file: file. + no_class_concept_found: No class/concept found + instances_search_placeholder: Search an Instance in %{acronym} + properties_search_placeholder: Search a property in %{acronym} + schemes_search_placeholder: Search a scheme in %{acronym} + collections_search_placeholder: Search a collection in %{acronym} + + metadata: + fair_score_title: FAIR score + total_score: "Total score : %{score} ( %{normalized_score}%)" + metrics: Metrics + metrics_link_title: Information and evolution diagrams about metrics and other measurements of the ontology. + see_all_metrics: See all the metrics of %{acronym} + show_modal_title_1: All the metrics of %{acronym} + not_calculated_metrics: We have not yet calculated metrics for %{acronym} + show_modal_title_2: "Evolution of the number of %{metric} in %{acronym} per submissions." + submissions: Submissions + submissions_link_title: Set of all the files that have been submitted to %{site}. Each previous metadata record is accessible but only the content of the latest parsed file is accessible via %{site} services + relations_network: Ontology relations network + general_information: General information + initial_created_on: Initial created on + additional_information: For additional information, contact + categories_and_subjects: Categories and subjects + keywords_and_classes: Keywords and classes + languages: Languages + pull_location: Pull location + export_metadata: Export all metadata + abstract: Abstract + description: Description + + htaccess_modal_title: "Rewrite rules for %{acronym} ontology" + instructions_servers: Instructions for %{server} servers + htaccess_redirection_description: "We offer a seamless solution to make your ontology URIs resolvable and content negotiable by allowing URL redirection for your ontology URIs to our Agroportal URIs. To facilitate this process, we've provided you with a set of .htaccess rewrite rules. By following the simple instructions below, you'll be able to implement these rules swiftly and efficiently, ensuring smooth redirection" + + redirection_note: | + This redirection works only for uri with the form of: url/path/resource_id + This will not work for uris in the form of: url/path/to/something#resource_id OR url/path/to/something:resource_id + htaccess_redirection: + instruction_1: "Access .htaccess File:" + instruction_1_content: "Locate the .htaccess file in the root directory of your website, if it doesn't exist create one. This file controls how your web server behaves and is often used for URL rewriting and redirection." + instruction_2: "Copy and Paste Redirect Rules in the .htaccess file:" + instruction_2_content: "Copy the rewrite rules provided in the black rectangle. Open the .htaccess file and paste the copied redirect rules into the file" + instruction_3: "Enable rewrite module (Linux):" + instruction_3_content: "you'll need to ensure that the Apache module called 'rewrite' is enabled by running the command `sudo a2enmod rewrite`" + + nginx_redirection: + instruction_1: "Access Nginx configuration file:" + instruction_1_content: "The location of this file can vary depending on your system, but common locations include /etc/nginx/nginx.conf, /etc/nginx/sites-available/default or /usr/local/etc/nginx/nginx.conf." + instruction_2: "Locate the server block or location block where you want to enable URL rewriting:" + instruction_2_content: "This is typically within the server { ... } block." + instruction_3: "Copy and Paste Redirect Rules :" + instruction_3_content: "Copy the rewrite rules provided in the black rectangle. Inside the appropriate block, add the copied rules to enable URL rewriting" + instruction_4: "Test the configuration for syntax errors:" + instruction_4_content: "Run the following command to test the redirection configuration `sudo nginx -t`" + instruction_5: "Reload Nginx to apply the changes:" + instruction_5_content: "Restart the nginx server to apply the redirection using this command `sudo systemctl reload nginx`" + + components: + check_resolvability: checking resolvability... + error_block: child_data_generator block did not provide all the child arguements + empty_field: The fields %{properties} are empty + save_button: Save + cancel_button: Cancel + go_to_api: Go to API + file_input_message: Drop your file here or, browse files on your device. + all_languages: All languages + select_anguage: Select a language + back: Back + next: Next + finish: Finish + creative_commons_license: Creative Commons License + open_source_license: Open Source License + show_more: "+ Show more ..." + show_less: "- Show less ..." + fair_score: FAIR score + details_details: FAIR details ... + projects: projects + notes: notes + concepts: concepts + instances: instances + classes: classes + submitted: Submitted + by: by + creation_date: Creation date %{date} + view_of_the_ontology: "View of the ontology %{ontology}" + view: View + debug: Debug + loading: Loading + unwatch: UnWatch + watch: Watch + resource: "%{sub_text} this resource" + notified_of_all_updates: Be the first to watch this resource and be notified of all its updates + join_the_count: Join the %{count} users, watching this resource and be notified of all its updates + see_more: See more... + see_less: See less... + tree_view_empty: No result found + copy_original_uri: Copy original URI + copy_portal_uri: Copy %{portal_name} URI + properties: + id: ID + type: Type + preferred_name: Preferred name + definitions: Definitions + parent: Parent + no_properties_alert: "%{acronym} does not contain properties" + visits: + ontology_visits: Ontology visits + name: name + visits: visits + ontologies_selector: + clear_selection: Clear selection + ontologies_advanced_selection: Ontologies advanced selection + search_hint: Filter ontologies ... + show_ontology_view: Show ontology views + hide_retired_ontologies: Hide retired ontologies + tabs_title: + categories: Categories + groups: Groups + format: Format + natural_languages: Natural languages + formality_levels: Formality levels + ontology_types: Ontology types + select_all: Select all + cancel: Cancel + apply: Apply + unselect_all: Unselect all + tools: + search: + title: "Search content (coming)" + description: > + This tool/service enables users to search for any Resource Description Framework (RDF) element within the portal. + Users can search by either the unique identifier (URI) associated with the RDF element or by its label. + It facilitates efficient navigation and retrieval of specific RDF elements, enhancing user experience and productivity within the portal. + converter: + title: "Content converter (coming)" + description: > + The Content Converter tool/service offers the capability to convert any RDF element into various formats such as RDF/XML, Turtle, Ntriples, or JSON. + This functionality allows users to transform RDF data into formats suitable for different purposes or compatible with various systems and applications. + It promotes interoperability and flexibility in handling RDF data within the portal ecosystem. + url_checker: + title: "URI resolvability checker" + description: > + This tool/service verifies the resolvability of Uniform Resource Identifiers (URIs) and their content negotiability. + It checks whether a given URI is accessible and whether the content associated with it can be negotiated based on the client's preferences. + This functionality ensures the reliability and accessibility of linked resources within the RDF ecosystem, aiding in maintaining data integrity and facilitating seamless integration with external resources. + feedback_mail: + name: "Name: %{name}" + email: "Email: %{email}" + location: "Location: %{location}" + tags: Tags + feedback: Feedback \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index fbc53cdab..5d5db5ddc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -28,20 +28,6 @@ t.datetime "updated_at", precision: nil, null: false end - create_table "margin_notes", id: :integer, charset: "utf8", force: :cascade do |t| - t.integer "parent_id" - t.integer "mapping_id" - t.integer "note_type" - t.integer "user_id" - t.integer "ontology_id" - t.integer "ontology_version_id" - t.string "concept_id" - t.string "subject" - t.text "comment" - t.datetime "created_at", precision: nil - t.datetime "updated_at", precision: nil - end - create_table "ontologies", charset: "utf8", force: :cascade do |t| t.string "acronym", null: false t.text "new_term_instructions" diff --git a/yarn.lock b/yarn.lock index 6da874dbf..7da15e91d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,120 +2,120 @@ # yarn lockfile v1 -"@esbuild/aix-ppc64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.0.tgz#509621cca4e67caf0d18561a0c56f8b70237472f" - integrity sha512-fGFDEctNh0CcSwsiRPxiaqX0P5rq+AqE0SRhYGZ4PX46Lg1FNR6oCxJghf8YgY0WQEgQuh3lErUFE4KxLeRmmw== - -"@esbuild/android-arm64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.0.tgz#109a6fdc4a2783fc26193d2687827045d8fef5ab" - integrity sha512-aVpnM4lURNkp0D3qPoAzSG92VXStYmoVPOgXveAUoQBWRSuQzt51yvSju29J6AHPmwY1BjH49uR29oyfH1ra8Q== - -"@esbuild/android-arm@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.0.tgz#1397a2c54c476c4799f9b9073550ede496c94ba5" - integrity sha512-3bMAfInvByLHfJwYPJRlpTeaQA75n8C/QKpEaiS4HrFWFiJlNI0vzq/zCjBrhAYcPyVPG7Eo9dMrcQXuqmNk5g== - -"@esbuild/android-x64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.0.tgz#2b615abefb50dc0a70ac313971102f4ce2fdb3ca" - integrity sha512-uK7wAnlRvjkCPzh8jJ+QejFyrP8ObKuR5cBIsQZ+qbMunwR8sbd8krmMbxTLSrDhiPZaJYKQAU5Y3iMDcZPhyQ== - -"@esbuild/darwin-arm64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.0.tgz#5c122ed799eb0c35b9d571097f77254964c276a2" - integrity sha512-AjEcivGAlPs3UAcJedMa9qYg9eSfU6FnGHJjT8s346HSKkrcWlYezGE8VaO2xKfvvlZkgAhyvl06OJOxiMgOYQ== - -"@esbuild/darwin-x64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.0.tgz#9561d277002ba8caf1524f209de2b22e93d170c1" - integrity sha512-bsgTPoyYDnPv8ER0HqnJggXK6RyFy4PH4rtsId0V7Efa90u2+EifxytE9pZnsDgExgkARy24WUQGv9irVbTvIw== - -"@esbuild/freebsd-arm64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.0.tgz#84178986a3138e8500d17cc380044868176dd821" - integrity sha512-kQ7jYdlKS335mpGbMW5tEe3IrQFIok9r84EM3PXB8qBFJPSc6dpWfrtsC/y1pyrz82xfUIn5ZrnSHQQsd6jebQ== - -"@esbuild/freebsd-x64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.0.tgz#3f9ce53344af2f08d178551cd475629147324a83" - integrity sha512-uG8B0WSepMRsBNVXAQcHf9+Ko/Tr+XqmK7Ptel9HVmnykupXdS4J7ovSQUIi0tQGIndhbqWLaIL/qO/cWhXKyQ== - -"@esbuild/linux-arm64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.0.tgz#24efa685515689df4ecbc13031fa0a9dda910a11" - integrity sha512-uTtyYAP5veqi2z9b6Gr0NUoNv9F/rOzI8tOD5jKcCvRUn7T60Bb+42NDBCWNhMjkQzI0qqwXkQGo1SY41G52nw== - -"@esbuild/linux-arm@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.0.tgz#6b586a488e02e9b073a75a957f2952b3b6e87b4c" - integrity sha512-2ezuhdiZw8vuHf1HKSf4TIk80naTbP9At7sOqZmdVwvvMyuoDiZB49YZKLsLOfKIr77+I40dWpHVeY5JHpIEIg== - -"@esbuild/linux-ia32@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.0.tgz#84ce7864f762708dcebc1b123898a397dea13624" - integrity sha512-c88wwtfs8tTffPaoJ+SQn3y+lKtgTzyjkD8NgsyCtCmtoIC8RDL7PrJU05an/e9VuAke6eJqGkoMhJK1RY6z4w== - -"@esbuild/linux-loong64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.0.tgz#1922f571f4cae1958e3ad29439c563f7d4fd9037" - integrity sha512-lR2rr/128/6svngnVta6JN4gxSXle/yZEZL3o4XZ6esOqhyR4wsKyfu6qXAL04S4S5CgGfG+GYZnjFd4YiG3Aw== - -"@esbuild/linux-mips64el@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.0.tgz#7ca1bd9df3f874d18dbf46af009aebdb881188fe" - integrity sha512-9Sycc+1uUsDnJCelDf6ZNqgZQoK1mJvFtqf2MUz4ujTxGhvCWw+4chYfDLPepMEvVL9PDwn6HrXad5yOrNzIsQ== - -"@esbuild/linux-ppc64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.0.tgz#8f95baf05f9486343bceeb683703875d698708a4" - integrity sha512-CoWSaaAXOZd+CjbUTdXIJE/t7Oz+4g90A3VBCHLbfuc5yUQU/nFDLOzQsN0cdxgXd97lYW/psIIBdjzQIwTBGw== - -"@esbuild/linux-riscv64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.0.tgz#ca63b921d5fe315e28610deb0c195e79b1a262ca" - integrity sha512-mlb1hg/eYRJUpv8h/x+4ShgoNLL8wgZ64SUr26KwglTYnwAWjkhR2GpoKftDbPOCnodA9t4Y/b68H4J9XmmPzA== - -"@esbuild/linux-s390x@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.0.tgz#cb3d069f47dc202f785c997175f2307531371ef8" - integrity sha512-fgf9ubb53xSnOBqyvWEY6ukBNRl1mVX1srPNu06B6mNsNK20JfH6xV6jECzrQ69/VMiTLvHMicQR/PgTOgqJUQ== - -"@esbuild/linux-x64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.0.tgz#ac617e0dc14e9758d3d7efd70288c14122557dc7" - integrity sha512-H9Eu6MGse++204XZcYsse1yFHmRXEWgadk2N58O/xd50P9EvFMLJTQLg+lB4E1cF2xhLZU5luSWtGTb0l9UeSg== - -"@esbuild/netbsd-x64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.0.tgz#6cc778567f1513da6e08060e0aeb41f82eb0f53c" - integrity sha512-lCT675rTN1v8Fo+RGrE5KjSnfY0x9Og4RN7t7lVrN3vMSjy34/+3na0q7RIfWDAj0e0rCh0OL+P88lu3Rt21MQ== - -"@esbuild/openbsd-x64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.0.tgz#76848bcf76b4372574fb4d06cd0ed1fb29ec0fbe" - integrity sha512-HKoUGXz/TOVXKQ+67NhxyHv+aDSZf44QpWLa3I1lLvAwGq8x1k0T+e2HHSRvxWhfJrFxaaqre1+YyzQ99KixoA== - -"@esbuild/sunos-x64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.0.tgz#ea4cd0639bf294ad51bc08ffbb2dac297e9b4706" - integrity sha512-GDwAqgHQm1mVoPppGsoq4WJwT3vhnz/2N62CzhvApFD1eJyTroob30FPpOZabN+FgCjhG+AgcZyOPIkR8dfD7g== - -"@esbuild/win32-arm64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.0.tgz#a5c171e4a7f7e4e8be0e9947a65812c1535a7cf0" - integrity sha512-0vYsP8aC4TvMlOQYozoksiaxjlvUcQrac+muDqj1Fxy6jh9l9CZJzj7zmh8JGfiV49cYLTorFLxg7593pGldwQ== - -"@esbuild/win32-ia32@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.0.tgz#f8ac5650c412d33ea62d7551e0caf82da52b7f85" - integrity sha512-p98u4rIgfh4gdpV00IqknBD5pC84LCub+4a3MO+zjqvU5MVXOc3hqR2UgT2jI2nh3h8s9EQxmOsVI3tyzv1iFg== - -"@esbuild/win32-x64@0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.0.tgz#2efddf82828aac85e64cef62482af61c29561bee" - integrity sha512-NgJnesu1RtWihtTtXGFMU5YSE6JyyHPMxCwBZK7a6/8d31GuSo9l0Ss7w1Jw5QnKUawG6UEehs883kcXf5fYwg== +"@esbuild/aix-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" + integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== + +"@esbuild/android-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" + integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== + +"@esbuild/android-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" + integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== + +"@esbuild/android-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" + integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== + +"@esbuild/darwin-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" + integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== + +"@esbuild/darwin-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" + integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== + +"@esbuild/freebsd-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" + integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== + +"@esbuild/freebsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" + integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== + +"@esbuild/linux-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" + integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== + +"@esbuild/linux-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" + integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== + +"@esbuild/linux-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" + integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== + +"@esbuild/linux-loong64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" + integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== + +"@esbuild/linux-mips64el@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" + integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== + +"@esbuild/linux-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" + integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== + +"@esbuild/linux-riscv64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" + integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== + +"@esbuild/linux-s390x@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" + integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== + +"@esbuild/linux-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" + integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== + +"@esbuild/netbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" + integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== + +"@esbuild/openbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" + integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== + +"@esbuild/sunos-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" + integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== + +"@esbuild/win32-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" + integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== + +"@esbuild/win32-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" + integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== + +"@esbuild/win32-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" + integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== "@hotwired/stimulus@^3.2.2": version "3.2.2" @@ -123,17 +123,17 @@ integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A== "@hotwired/turbo-rails@^8.0.3": - version "8.0.3" - resolved "https://registry.yarnpkg.com/@hotwired/turbo-rails/-/turbo-rails-8.0.3.tgz#e60375f4eea4b30ec0cd6d7e3fdb3d6349a2b57b" - integrity sha512-n5B9HdFsNiGJfXFAriCArmvFZyznIh/OriB5ZVAWz4Fsi4oLkpgmJNw5pibBAM7NMQQGN6cfKa/nhZT4LWcqbQ== + version "8.0.4" + resolved "https://registry.yarnpkg.com/@hotwired/turbo-rails/-/turbo-rails-8.0.4.tgz#d224f524a9e33fe687cec5d706054eb6fe13fa5b" + integrity sha512-GHCv5+B2VzYZZvMFpg/g9JLx/8pl/8chcubSB7T+Xn1zYOMqAKB6cT80vvWUzxdwfm/2KfaRysfDz+BmvtjFaw== dependencies: - "@hotwired/turbo" "^8.0.3" + "@hotwired/turbo" "^8.0.4" "@rails/actioncable" "^7.0" -"@hotwired/turbo@^8.0.3": - version "8.0.3" - resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-8.0.3.tgz#338e07278f4b3c76921328d3c92dbc4831c209d0" - integrity sha512-qLgp7d6JaegKjMToTJahosrFxV3odfSbiekispQ3soOzE5jnU+iEMWlRvYRe/jvy5Q+JWoywtf9j3RD4ikVjIg== +"@hotwired/turbo@^8.0.4": + version "8.0.4" + resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-8.0.4.tgz#5c5361c06a37cdf10dcba4223f1afd0ca1c75091" + integrity sha512-mlZEFUZrJnpfj+g/XeCWWuokvQyN68WvM78JM+0jfSFc98wegm259vCbC1zSllcspRwbgXK31ibehCy5PA78/Q== "@rails/actioncable@^7.0": version "7.1.3" @@ -141,30 +141,30 @@ integrity sha512-ojNvnoZtPN0pYvVFtlO7dyEN9Oml1B6IDM+whGKVak69MMYW99lC2NOWXWeE3bmwEydbP/nn6ERcpfjHVjYQjA== esbuild@^0.20.0: - version "0.20.0" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.0.tgz#a7170b63447286cd2ff1f01579f09970e6965da4" - integrity sha512-6iwE3Y2RVYCME1jLpBqq7LQWK3MW6vjV2bZy6gt/WrqkY+WE74Spyc0ThAOYpMtITvnjX09CrC6ym7A/m9mebA== + version "0.20.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" + integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g== optionalDependencies: - "@esbuild/aix-ppc64" "0.20.0" - "@esbuild/android-arm" "0.20.0" - "@esbuild/android-arm64" "0.20.0" - "@esbuild/android-x64" "0.20.0" - "@esbuild/darwin-arm64" "0.20.0" - "@esbuild/darwin-x64" "0.20.0" - "@esbuild/freebsd-arm64" "0.20.0" - "@esbuild/freebsd-x64" "0.20.0" - "@esbuild/linux-arm" "0.20.0" - "@esbuild/linux-arm64" "0.20.0" - "@esbuild/linux-ia32" "0.20.0" - "@esbuild/linux-loong64" "0.20.0" - "@esbuild/linux-mips64el" "0.20.0" - "@esbuild/linux-ppc64" "0.20.0" - "@esbuild/linux-riscv64" "0.20.0" - "@esbuild/linux-s390x" "0.20.0" - "@esbuild/linux-x64" "0.20.0" - "@esbuild/netbsd-x64" "0.20.0" - "@esbuild/openbsd-x64" "0.20.0" - "@esbuild/sunos-x64" "0.20.0" - "@esbuild/win32-arm64" "0.20.0" - "@esbuild/win32-ia32" "0.20.0" - "@esbuild/win32-x64" "0.20.0" + "@esbuild/aix-ppc64" "0.20.2" + "@esbuild/android-arm" "0.20.2" + "@esbuild/android-arm64" "0.20.2" + "@esbuild/android-x64" "0.20.2" + "@esbuild/darwin-arm64" "0.20.2" + "@esbuild/darwin-x64" "0.20.2" + "@esbuild/freebsd-arm64" "0.20.2" + "@esbuild/freebsd-x64" "0.20.2" + "@esbuild/linux-arm" "0.20.2" + "@esbuild/linux-arm64" "0.20.2" + "@esbuild/linux-ia32" "0.20.2" + "@esbuild/linux-loong64" "0.20.2" + "@esbuild/linux-mips64el" "0.20.2" + "@esbuild/linux-ppc64" "0.20.2" + "@esbuild/linux-riscv64" "0.20.2" + "@esbuild/linux-s390x" "0.20.2" + "@esbuild/linux-x64" "0.20.2" + "@esbuild/netbsd-x64" "0.20.2" + "@esbuild/openbsd-x64" "0.20.2" + "@esbuild/sunos-x64" "0.20.2" + "@esbuild/win32-arm64" "0.20.2" + "@esbuild/win32-ia32" "0.20.2" + "@esbuild/win32-x64" "0.20.2" From b7a0bf5ff684cc872240dca40cbb3c0b9618c1d8 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 17 Jul 2024 10:31:41 -0700 Subject: [PATCH 25/61] progress commit --- .../controllers/language_change_controller.js | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 app/javascript/controllers/language_change_controller.js diff --git a/app/javascript/controllers/language_change_controller.js b/app/javascript/controllers/language_change_controller.js new file mode 100644 index 000000000..82589059d --- /dev/null +++ b/app/javascript/controllers/language_change_controller.js @@ -0,0 +1,84 @@ +import { Controller } from "@hotwired/stimulus" + +// Connects to data-controller="language-change" +export default class extends Controller { + + + connect() { + // used for debugging + // console.log(this.element.value); + jQuery(document).data().bp.lang = this.element.value; + + } + + + dispatchLangChangeEvent() { + + // console.log("lang changed"); + // window.location.reload(); + // + // + // this.element.dispatchEvent(new CustomEvent('lang_changed', { + // bubbles: true, + // cancelable: true, + // detail: { + // data: { + // language: [this.element.value] + // } + // } + // })); + + // initClassTree(); + + + + + jQuery(document).data().bp.lang = this.element.value; + + var url = window.location.href; + url = this.removeURLParameter(url, 'lang'); + if (url.indexOf('?') > -1) { + url += '&lang=' + this.element.value; + } else { + url += '?lang=' + this.element.value; + } + window.location.href = url; + + + // var conceptID = getConcept(); + // var decConceptID = decodeURI(conceptID); + // jQuery(document).data().bp.ont_viewer.concept_id = decConceptID; + // nodeClicked(decConceptID, this.element.value); + // getTreeView(this.element.value); + + + + + // var state = History.getState(); + // displayTree(state.data); + + } + + + removeURLParameter(url, parameter) { + //prefer to use l.search if you have a location/link object + var urlparts = url.split('?'); + if (urlparts.length >= 2) { + + var prefix = encodeURIComponent(parameter) + '='; + var pars = urlparts[1].split(/[&;]/g); + + //reverse iteration as may be destructive + for (var i = pars.length; i-- > 0;) { + //idiom for string.startsWith + if (pars[i].lastIndexOf(prefix, 0) !== -1) { + pars.splice(i, 1); + } + } + + return urlparts[0] + (pars.length > 0 ? '?' + pars.join('&') : ''); + } + return url; + } + +} From 3dbafc3f7050b7f2bc27218001784da066b0f298 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 17 Jul 2024 10:32:19 -0700 Subject: [PATCH 26/61] progress commit --- app/controllers/language_controller.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 app/controllers/language_controller.rb diff --git a/app/controllers/language_controller.rb b/app/controllers/language_controller.rb new file mode 100644 index 000000000..0ccf6b59c --- /dev/null +++ b/app/controllers/language_controller.rb @@ -0,0 +1,21 @@ +class LanguageController < ApplicationController + + # set locale to the language selected by the user + def set_locale_language + language = params[:language].strip.downcase.to_sym + supported_languages = I18n.available_locales + + if language + if supported_languages.include?(language) + cookies.permanent[:locale] = language + else + # in case we want to show a message if the language is not available + flash.now[:notice] = t('language.translation_not_available', language: language) + logger.error flash.now[:notice] + end + end + + redirect_to request.referer || root_path + end + +end From a1ce9d139a3956a0273197f9252811f07c522f63 Mon Sep 17 00:00:00 2001 From: Alex Skrenchuk Date: Thu, 18 Jul 2024 17:00:36 -0700 Subject: [PATCH 27/61] update ontologies_api_ruby_client to v2.3.0 Fixes https://github.com/ncbo/bioportal-project/issues/318 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 44ea7be0d..a913445ce 100644 --- a/Gemfile +++ b/Gemfile @@ -57,7 +57,7 @@ gem 'iso-639', '~> 0.3.6' gem 'multi_json' gem 'mysql2', '0.5.5' gem 'oj' -gem 'ontologies_api_client', github: 'ncbo/ontologies_api_ruby_client', tag: 'v2.2.5' +gem 'ontologies_api_client', github: 'ncbo/ontologies_api_ruby_client', tag: 'v2.3.0' gem 'open_uri_redirections' gem 'pry' gem 'psych', '< 4' diff --git a/Gemfile.lock b/Gemfile.lock index 7ad70af20..35cd0a8a8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GIT remote: https://github.com/ncbo/ontologies_api_ruby_client.git - revision: 115cf36f54f73eb10c503147f54bb6f6672d3d99 - tag: v2.2.5 + revision: 027c749f5de3f644b0392197e9482be51738cbe8 + tag: v2.3.0 specs: ontologies_api_client (2.2.5) activesupport (= 7.0.8) From cf74824c1c498bfc978723da4e8e3a47e0784534 Mon Sep 17 00:00:00 2001 From: mdorf Date: Mon, 22 Jul 2024 16:01:11 -0700 Subject: [PATCH 28/61] initial working commit --- Gemfile | 4 +- Gemfile.lock | 24 +-- app/assets/javascripts/bp_class_tree.js.erb | 15 +- .../javascripts/bp_ontology_viewer.js.erb | 42 ++--- app/controllers/application_controller.rb | 145 +----------------- app/helpers/internationalisation_helper.rb | 43 ++++++ app/helpers/multi_languages_helper.rb | 145 ++++++++++++++++++ app/helpers/ontologies_helper.rb | 90 ++--------- .../controllers/language_change_controller.js | 43 +----- app/views/layouts/_ontology_viewer.html.haml | 28 +--- 10 files changed, 233 insertions(+), 346 deletions(-) create mode 100644 app/helpers/internationalisation_helper.rb create mode 100644 app/helpers/multi_languages_helper.rb diff --git a/Gemfile b/Gemfile index eb38d193f..d53ac8754 100644 --- a/Gemfile +++ b/Gemfile @@ -57,7 +57,9 @@ gem 'iso-639', '~> 0.3.6' gem 'multi_json' gem 'mysql2', '0.5.5' gem 'oj' -gem 'ontologies_api_client', github: 'ncbo/ontologies_api_ruby_client', tag: 'v2.2.5' + +gem 'ontologies_api_client', github: 'ncbo/ontologies_api_ruby_client', branch: 'develop' + gem 'open_uri_redirections' gem 'pry' gem 'psych', '< 4' diff --git a/Gemfile.lock b/Gemfile.lock index 201c9a3e3..3b3076027 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GIT remote: https://github.com/ncbo/ontologies_api_ruby_client.git - revision: 115cf36f54f73eb10c503147f54bb6f6672d3d99 - tag: v2.2.5 + revision: 6b9cea10f4e461610a7b2f6115098e1d0d259c5a + branch: develop specs: ontologies_api_client (2.2.5) activesupport (= 7.0.8) @@ -150,10 +150,11 @@ GEM erubi (1.13.0) erubis (2.7.0) eventmachine (1.2.7) - excon (0.110.0) + excon (0.111.0) execjs (2.9.1) - faraday (2.9.2) + faraday (2.10.0) faraday-net_http (>= 2.0, < 3.2) + logger faraday-excon (2.1.0) excon (>= 0.27.4) faraday (~> 2.0) @@ -191,7 +192,7 @@ GEM activesupport (>= 3.0) nokogiri (>= 1.6) io-console (0.7.2) - irb (1.13.2) + irb (1.14.0) rdoc (>= 4.0.0) reline (>= 0.4.2) iso-639 (0.3.6) @@ -208,6 +209,7 @@ GEM listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) + logger (1.6.0) loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -261,7 +263,7 @@ GEM bigdecimal (>= 3.0) open_uri_redirections (0.2.1) parallel (1.25.1) - parser (3.3.3.0) + parser (3.3.4.0) ast (~> 2.4.1) racc popper_js (2.11.8) @@ -325,7 +327,7 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - rexml (3.3.1) + rexml (3.3.2) strscan rspec-core (3.13.0) rspec-support (~> 3.13.0) @@ -344,13 +346,13 @@ GEM rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.1) - rubocop (1.64.1) + rubocop (1.65.0) 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 (>= 1.8, < 3.0) + regexp_parser (>= 2.4, < 3.0) rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) @@ -358,7 +360,7 @@ GEM rubocop-ast (1.31.3) parser (>= 3.3.1.0) ruby-progressbar (1.13.0) - ruby_parser (3.21.0) + ruby_parser (3.21.1) racc (~> 1.5) sexp_processor (~> 4.16) sass-rails (6.0.0) @@ -372,7 +374,7 @@ GEM sprockets-rails tilt select2-rails (4.0.13) - sexp_processor (4.17.1) + sexp_processor (4.17.2) spawnling (2.1.5) sprockets (4.2.1) concurrent-ruby (~> 1.0) diff --git a/app/assets/javascripts/bp_class_tree.js.erb b/app/assets/javascripts/bp_class_tree.js.erb index 26d4ff526..68ad16600 100644 --- a/app/assets/javascripts/bp_class_tree.js.erb +++ b/app/assets/javascripts/bp_class_tree.js.erb @@ -86,19 +86,6 @@ function initClassTree() { }); }; - - - - - - - - - - - - - function nodeClicked(node_id, lang = "en") { // Get current html and store data in cache (to account for changes since the cache was retrieved) setCacheCurrent(); @@ -201,7 +188,7 @@ function getTreeView(lang = "en") { placeTreeView(data); }, error: function(data) { - jQuery.get("/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&lang=" + lang + "&conceptid=root", function(data){ + jQuery.get("/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&lang=" + lang + "&conceptid=root", function(data) { var rootTree = "
Displaying the path to this class has taken too long. You can browse classes below.
" + data; placeTreeView(rootTree); }); diff --git a/app/assets/javascripts/bp_ontology_viewer.js.erb b/app/assets/javascripts/bp_ontology_viewer.js.erb index 85bda3bb4..d9b29450a 100644 --- a/app/assets/javascripts/bp_ontology_viewer.js.erb +++ b/app/assets/javascripts/bp_ontology_viewer.js.erb @@ -66,15 +66,6 @@ function displayTree(data) { jQuery(document).trigger("classes_tab_visible"); - - - - - - - - - var new_concept_id = data.conceptid; var new_concept_link = getConceptLinkEl(new_concept_id); var concept_label; @@ -91,22 +82,16 @@ function displayTree(data) { var metadata_only = jQuery(document).data().bp.ont_viewer.metadata_only; var purl_prefix = jQuery(document).data().bp.ont_viewer.purl_prefix; var concept_name_title = jQuery(document).data().bp.ont_viewer.concept_name_title; - - var lang = jQuery(document).data().bp.lang; - // Check to see if we're actually loading a new concept or just displaying the one we already loaded previously if (typeof new_concept_id === 'undefined' || new_concept_id == concept_id) { - if (concept_id !== "") { History.replaceState({p:"classes", conceptid:concept_id}, jQuery.bioportal.ont_pages["classes"].page_name + " | " + org_site, "?p=classes" + "&conceptid=" + concept_id); } jQuery.unblockUI(); return; - } else { - var new_concept_param = (typeof new_concept_id === 'undefined') ? "" : "&conceptid=" + new_concept_id; if (typeof new_concept_id !== 'undefined') { @@ -135,9 +120,7 @@ function displayTree(data) { } // If the concept is already visible and in cache, then just switch to it -// if (getCache(data.conceptid) == null) { - - + if (getCache(data.conceptid) == null) { var list = jQuery("div#sd_content ul.simpleTree li.root ul"); // Remove existing classes @@ -166,17 +149,11 @@ function displayTree(data) { // Clear the search box jQuery("#search_box").val(""); } - - - -// } else { -// // Are we jumping into the ontology? If so, get the whole tree -// jQuery.bioportal.ont_pages["classes"].retrieve_and_publish(); -// getConceptLinkEl(new_concept_id) -// } - - - + } else { + // Are we jumping into the ontology? If so, get the whole tree + jQuery.bioportal.ont_pages["classes"].retrieve_and_publish(); + getConceptLinkEl(new_concept_id) + } } else { jQuery.blockUI({ message: '

" /> Loading Class...

', showOverlay: false }); if (document.getElementById(new_concept_id) !== null) { @@ -222,6 +199,12 @@ function showOntologyContent(content_section) { // Instead, fire some history events var nav_ont = function(link) { var page = jQuery(link).attr("data-bp-ont-page"); + + if (jQuery(document).data().bp.ont_viewer.lang_sections.includes(page)) { + jQuery("#navbar-ontology li.lang-dropdown").show(); + } else { + jQuery("#navbar-ontology li.lang-dropdown").hide(); + } History.pushState({p:page}, jQuery.bioportal.ont_pages[page].page_name + " | " + jQuery(document).data().bp.ont_viewer.org_site, "?p=" + page); } @@ -323,6 +306,7 @@ jQuery.bioportal.OntologyPage = function(id, location_path, error_string, page_n this.published = false; this.retrieved = false; this.init = init || null; + this.is_data_section = jQuery(document).data().bp.ont_viewer.lang_sections.includes(id); this.retrieve = function(){ jQuery.ajax({ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 91d19de67..1625423d2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -349,125 +349,10 @@ def using_captcha? ENV['USE_RECAPTCHA'].present? && ENV['USE_RECAPTCHA'] == 'true' end - - - - - - - - - - # def get_class1(params) - # - # lang = request_lang - # - # if @ontology.flat? - # - # ignore_concept_param = params[:conceptid].nil? || - # params[:conceptid].empty? || - # params[:conceptid].eql?("root") || - # params[:conceptid].eql?("bp_fake_root") - # if ignore_concept_param - # # Don't display any classes in the tree - # @concept = LinkedData::Client::Models::Class.new - # @concept.prefLabel = t('application.search_for_class') - # @concept.obsolete = false - # @concept.id = "bp_fake_root" - # @concept.properties = {} - # @concept.children = [] - # else - # # Display only the requested class in the tree - # @concept = @ontology.explore.single_class({full: true, lang: lang }, params[:conceptid]) - # @concept.children = [] - # end - # @root = LinkedData::Client::Models::Class.new - # @root.children = [@concept] - # - # else - # - # # not ignoring 'bp_fake_root' here - # include = 'prefLabel,hasChildren,obsolete' - # ignore_concept_param = params[:conceptid].nil? || - # params[:conceptid].empty? || - # params[:conceptid].eql?("root") - # if ignore_concept_param - # # get the top level nodes for the root - # # TODO_REV: Support views? Replace old view call: @ontology.top_level_classes(view) - # @roots = @ontology.explore.roots(concept_schemes: params[:concept_schemes]) rescue nil - # - # if @roots.nil? || response_error?(@roots) || @roots.compact&.empty? - # LOG.add :debug, t('application.missing_roots_for_ontology', acronym: @ontology.acronym) - # classes = @ontology.explore.classes.collection - # @concept = classes.first.explore.self(full: true) if classes&.first - # return - # end - # - # @root = LinkedData::Client::Models::Class.new(read_only: true) - # @root.children = @roots.sort{|x,y| (x&.prefLabel || "").downcase <=> (y&.prefLabel || "").downcase} - # - # # get the initial concept to display - # root_child = @root.children&.first - # not_found(t('application.missing_roots')) if root_child.nil? - # - # @concept = root_child.explore.self(full: true, lang: lang) - # # Some ontologies have "too many children" at their root. These will not process and are handled here. - # if @concept.nil? - # LOG.add :debug, t('application.missing_class', root_child: root_child.links.self) - # not_found(t('application.missing_class', root_child: root_child.links.self)) - # end - # else - # # if the id is coming from a param, use that to get concept - # @concept = @ontology.explore.single_class({full: true, lang: lang}, params[:conceptid]) - # if @concept.nil? || @concept.errors - # LOG.add :debug, t('application.missing_class_ontology', acronym: @ontology.acronym, concept_id: params[:conceptid]) - # not_found(t('application.missing_class_ontology', acronym: @ontology.acronym, concept_id: params[:conceptid])) - # end - # - # # Create the tree - # rootNode = @concept.explore.tree(include: include, concept_schemes: params[:concept_schemes], lang: lang) - # if rootNode.nil? || rootNode.empty? - # @roots = @ontology.explore.roots(concept_schemes: params[:concept_schemes]) - # if @roots.nil? || response_error?(@roots) || @roots.compact&.empty? - # LOG.add :debug, t('application.missing_roots_for_ontology', acronym: @ontology.acronym) - # @concept = @ontology.explore.classes.collection.first.explore.self(full: true) - # return - # end - # if @roots.any? {|c| c.id == @concept.id} - # rootNode = @roots - # else - # rootNode = [@concept] - # end - # end - # @root = LinkedData::Client::Models::Class.new(read_only: true) - # @root.children = rootNode.sort{|x,y| (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase} - # end - # end - # - # @concept - # end - - - - - - - - - - - - - - - - - def get_class(params) lang = request_lang if @ontology.flat? - ignore_concept_param = params[:conceptid].nil? || params[:conceptid].empty? || params[:conceptid].eql?("root") || @@ -487,9 +372,7 @@ def get_class(params) end @root = LinkedData::Client::Models::Class.new @root.children = [@concept] - else - # not ignoring 'bp_fake_root' here ignore_concept_param = params[:conceptid].nil? || params[:conceptid].empty? || @@ -510,8 +393,8 @@ def get_class(params) # get the initial concept to display root_child = @root.children.first - @concept = root_child.explore.self(full: true, lang: lang) + # Some ontologies have "too many children" at their root. These will not process and are handled here. if @concept.nil? LOG.add :debug, "Missing class #{root_child.links.self}" @@ -528,7 +411,6 @@ def get_class(params) # Create the tree rootNode = @concept.explore.tree(include: "prefLabel,hasChildren,obsolete", lang: lang) - if rootNode.nil? || rootNode.empty? roots = @ontology.explore.roots if roots.nil? || roots.empty? @@ -541,35 +423,10 @@ def get_class(params) rootNode = [@concept] end end - - - - - - # binding.pry - - - - @root = LinkedData::Client::Models::Class.new(read_only: true) @root.children = rootNode.sort{|x,y| (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase} - - - - - # binding.pry - - - - end end - - - - # binding.pry - - @concept end diff --git a/app/helpers/internationalisation_helper.rb b/app/helpers/internationalisation_helper.rb new file mode 100644 index 000000000..d8f8ea326 --- /dev/null +++ b/app/helpers/internationalisation_helper.rb @@ -0,0 +1,43 @@ +module InternationalisationHelper + + # Implement logic to make the term 'ontology' configurable throughout the portal, + # allowing it to be replaced with the variable $RESOURCE_TERM + def self.t(*args, **kwargs) + return I18n.t(*args, **kwargs) unless $RESOURCE_TERM + + begin + original_translation = I18n.t(*args, **kwargs) + downcase_translation = original_translation.downcase + rescue StandardError => e + return e.message + end + + term = I18n.t("resource_term.ontology") + plural_term = I18n.t("resource_term.ontology_plural") + single_term = I18n.t("resource_term.ontology_single") + resource = I18n.t("resource_term.#{$RESOURCE_TERM}") + resources = I18n.t("resource_term.#{$RESOURCE_TERM}_plural") + a_resource = I18n.t("resource_term.#{$RESOURCE_TERM}_single") + + if downcase_translation.include?(term) && resource + replacement = resource.capitalize + replacement = resource if downcase_translation.include?(term) + if downcase_translation.include?(single_term) + term = single_term + replacement = a_resource + end + original_translation.gsub(term, replacement) + elsif downcase_translation.include?(plural_term) && resources + replacement = resources.capitalize + replacement = resources if downcase_translation.include?(plural_term) + original_translation.gsub(plural_term, replacement) + else + I18n.t(*args, **kwargs) + end + end + + def t(*args, **kwargs) + InternationalisationHelper.t(*args, **kwargs) + end + +end diff --git a/app/helpers/multi_languages_helper.rb b/app/helpers/multi_languages_helper.rb new file mode 100644 index 000000000..de65fa4b1 --- /dev/null +++ b/app/helpers/multi_languages_helper.rb @@ -0,0 +1,145 @@ +module MultiLanguagesHelper + + def portal_language_help_text + t('language.portal_language_help_text') + end + + def portal_languages + { + en: { badge: nil, disabled: false }, + fr: { badge: 'beta', disabled: false }, + it: { badge: 'coming', disabled: true }, + de: { badge: 'coming', disabled: true } + } + end + + def portal_language_selector + languages = portal_languages + selected_language = portal_lang + selected_language = content_tag(:span, selected_language.upcase, data: { controller: 'tooltip' }, title: portal_language_help_text) + render DropdownButtonComponent.new do |d| + d.header { selected_language } + d.section(divide: false, selected_index: languages.find_index(selected_language)) do |s| + languages.each do |lang, metadata| + s.item do + text = content_tag(:div, class: 'd-flex align-items-center') do + content_tag(:span, render(LanguageFieldComponent.new(value: lang, auto_label: true)), class: 'mr-1') + beta_badge(metadata[:badge]) + end + link_options = { data: { turbo: false } } + + if metadata[:disabled] + link_options[:class] = 'disabled-link' + link_options[:disabled] = 'disabled' + end + + link_to(text, "/locale/#{lang}", link_options) + end + end + + end + end + end + + def search_language_help_text + content_tag(:div, style: 'width: 300px; text-align: center') do + t('language.search_language_help_text') + end + end + + def search_languages + # top ten spoken languages + portal_languages.keys + %w[zh es hi ar bn pt ru ur id] + end + + def language_hash(concept_label, multiple: false) + if concept_label.is_a?(Array) + return concept_label.first unless multiple + return concept_label + end + + return concept_label.to_h.reject { |key, _| %i[links context].include?(key) } if concept_label.is_a?(OpenStruct) + + concept_label + end + + def sorted_labels(labels) + Array(labels).sort_by { |label| label['prefLabel'].is_a?(String) ? label['prefLabel'] : label['prefLabel'].last } + end + + def select_language_label(concept_label, platform_languages = %i[en fr]) + concept_value = nil + + concept = language_hash(concept_label) + + return ['@none', concept] if concept.is_a?(String) + + concept = concept.to_h + + platform_languages.each do |lang| + if concept[lang] + concept_value = [lang, concept[lang]] + break + end + end + + concept_value || concept.to_a.first + end + + def main_language_label(label) + select_language_label(label)&.last + end + + def selected_language_label(label) + language_hash(label).values.first + end + + def content_language_selector(id: 'content_language', name: 'content_language') + languages, selected = content_languages + select_tag(name, options_for_select(languages, selected || 'all'), class: "form-select", + data: { controller: "language-change", 'language-change-section-value': "classes", action: "change->language-change#dispatchLangChangeEvent" }) unless languages&.empty? + end + + def content_languages(submission = @submission || @submission_latest) + current_lang = request_lang.downcase + submission_lang = submission_languages(submission) + # Transform each language into a select option + submission_lang = submission_lang.map do |lang| + lang = lang.split('/').last.upcase + lang = ISO_639.find(lang.to_s.downcase) + next nil unless lang + [lang.english_name, lang.alpha2] + end.compact + + [submission_lang, current_lang] + end + + def portal_lang + session[:locale] || 'en' + end + + def request_lang + lang = params[:language] || params[:lang] + lang = portal_lang unless lang + lang.upcase + end + + def lang_code(code_in) + code_out = code_in + case code_in + when 'en' + code_out = 'us' + when 'ar' + code_out = 'sa' + when 'hi' + code_out = 'in' + when 'ur' + code_out = 'pk' + when 'zh' + code_out = 'cn' + when 'ja' + code_out = 'jp' + end + code_out + end + +end diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index a1138a9c3..98ad6bdbc 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -2,15 +2,7 @@ module OntologiesHelper - - - - # LANGUAGE_FILTERABLE_SECTIONS = %w[classes properties].freeze - - - - - + LANGUAGE_FILTERABLE_SECTIONS = %w[classes properties].freeze def additional_details return '' if $ADDITIONAL_ONTOLOGY_DETAILS.nil? || $ADDITIONAL_ONTOLOGY_DETAILS[@ontology.acronym].nil? @@ -170,88 +162,24 @@ def change_requests_enabled?(ontology_acronym) Rails.configuration.change_request[:ontologies].include? ontology_acronym.to_sym end - - - - - - - # def current_section - # (params[:p]) ? params[:p] : 'summary' - # end - # - # def ontology_data_sections - # LANGUAGE_FILTERABLE_SECTIONS - # end - # - # def ontology_data_section?(section_title = current_section) - # ontology_data_sections.include?(section_title) - # end - - - - - def edit_sub_languages_button(ontology = @ontology, submission = @submission_latest) - return unless ontology.admin?(session[:user]) - - link = edit_ontology_submission_path(ontology.acronym, submission&.submissionId || '', properties: 'naturalLanguage', container_id: 'application_modal_content') - link_to_modal(nil, link, class: "btn", id:'fair-details-link', - data: { show_modal_title_value: t('ontologies.edit_natural_languages', acronym: ontology.acronym), show_modal_size_value: 'modal-md' }) do - - - render ChipButtonComponent.new(type: 'clickable', class: 'admin-background chip_button_small' ) do - (t('ontologies.edit_available_languages') + content_tag(:i, "", class: "fas fa-lg fa-edit")).html_safe - end - - - - end + def current_section + (params[:p]) ? params[:p] : 'summary' end + def ontology_data_sections + LANGUAGE_FILTERABLE_SECTIONS + end - - - - - - - - + def ontology_data_section?(section_title = current_section) + ontology_data_sections.include?(section_title) + end def language_selector_tag(name) content_language_selector(id: name, name: name) end - - # def language_selector_hidden_tag(section) - # hidden_field_tag "language_selector_hidden_#{section}", '', - # data: { controller: "language-change", 'language-change-section-value': section, action: "change->language-change#dispatchLangChangeEvent" } - # end - - - - - - - - def submission_languages(submission = @submission) - - # binding.pry - Array(submission&.naturalLanguage).map { |natural_language| natural_language.split('/').last }.compact end - - - - - - - - - - - - end diff --git a/app/javascript/controllers/language_change_controller.js b/app/javascript/controllers/language_change_controller.js index 82589059d..2b4f3e9fb 100644 --- a/app/javascript/controllers/language_change_controller.js +++ b/app/javascript/controllers/language_change_controller.js @@ -5,34 +5,12 @@ export default class extends Controller { connect() { - // used for debugging + // can be used for debugging // console.log(this.element.value); jQuery(document).data().bp.lang = this.element.value; - } - dispatchLangChangeEvent() { - - // console.log("lang changed"); - // window.location.reload(); - // - // - // this.element.dispatchEvent(new CustomEvent('lang_changed', { - // bubbles: true, - // cancelable: true, - // detail: { - // data: { - // language: [this.element.value] - // } - // } - // })); - - // initClassTree(); - - - - jQuery(document).data().bp.lang = this.element.value; var url = window.location.href; @@ -42,29 +20,15 @@ export default class extends Controller { } else { url += '?lang=' + this.element.value; } + jQuery.blockUI({ message: '

Switching language...

', showOverlay: true }); window.location.href = url; - - - // var conceptID = getConcept(); - // var decConceptID = decodeURI(conceptID); - // jQuery(document).data().bp.ont_viewer.concept_id = decConceptID; - // nodeClicked(decConceptID, this.element.value); - // getTreeView(this.element.value); - - - - - // var state = History.getState(); - // displayTree(state.data); - } - removeURLParameter(url, parameter) { //prefer to use l.search if you have a location/link object var urlparts = url.split('?'); - if (urlparts.length >= 2) { + if (urlparts.length >= 2) { var prefix = encodeURIComponent(parameter) + '='; var pars = urlparts[1].split(/[&;]/g); @@ -75,7 +39,6 @@ export default class extends Controller { pars.splice(i, 1); } } - return urlparts[0] + (pars.length > 0 ? '?' + pars.join('&') : ''); } return url; diff --git a/app/views/layouts/_ontology_viewer.html.haml b/app/views/layouts/_ontology_viewer.html.haml index c1a0c95d6..3848789c4 100644 --- a/app/views/layouts/_ontology_viewer.html.haml +++ b/app/views/layouts/_ontology_viewer.html.haml @@ -37,6 +37,7 @@ jQuery(document).data().bp.ont_viewer.concept_name_title = (jQuery(document).data().bp.ont_viewer.concept_name == "") ? "" : " - " + jQuery(document).data().bp.ont_viewer.concept_name; jQuery(document).data().bp.ont_viewer.lang = "#{request_lang}"; + jQuery(document).data().bp.ont_viewer.lang_sections = #{ontology_data_sections.to_json.html_safe}; -# Modal dialog for creating a new mapping (must reside in a top-level position in the document to display properly). %div#createNewMappingModal{class: "modal fade", tabindex: "-1", "aria-labelledby": "createNewMappingLabel"} @@ -140,18 +141,8 @@ class: "nav-link", type: 'button', role: "tab", data: {bp_ont_page: "widgets", "bs-toggle": "tab", "bs-target": "#ont_widgets_content"}, aria: {controls: "ont_widgets_content", selected: "false"}) - - - - - %li{class: 'nav-item', role: 'presentation'} + %li{style: "display: #{ontology_data_section? ? 'block' : 'none'}", class: 'nav-item lang-dropdown', role: 'presentation'} = language_selector_tag(:content_language) - = flag_icon(lang_code("en")) - - - - - %div.card-body -# Tab panes for displaying ontology content sections %div.tab-content{id: "ontology_content"} @@ -173,21 +164,6 @@ %div.tab-pane{id: "ont_widgets_content", role: "tabpanel", aria: {labelledby: "ont-widgets-tab"}} - if content_section.eql?("widgets") = yield - - - - - - - - -# %div.tab-pane{id: "ont_languages_content", role: "tabpanel", aria: {labelledby: "ont-languages-tab"}} - -# = language_selector_hidden_tag("Classes") - - - - - - - if Rails.env.appliance? = render partial: "footer_appliance" From 3a27a103072a7fb4a84b4a971e920bed71828da7 Mon Sep 17 00:00:00 2001 From: mdorf Date: Mon, 22 Jul 2024 16:01:55 -0700 Subject: [PATCH 29/61] Gemfile.lock update --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3b3076027..a9646c69f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -404,7 +404,7 @@ GEM time (0.3.0) date timeout (0.4.1) - turbo-rails (2.0.5) + turbo-rails (2.0.6) actionpack (>= 6.0.0) activejob (>= 6.0.0) railties (>= 6.0.0) From d42ae591205596870a19de7cb0be757e49757a2b Mon Sep 17 00:00:00 2001 From: Alex Skrenchuk Date: Mon, 22 Jul 2024 19:58:00 -0700 Subject: [PATCH 30/61] pin strscan to v3.0.1 v3.1.0 causing deployment issues --- Gemfile | 4 ++++ Gemfile.lock | 7 +++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index d04832082..07e36f08d 100644 --- a/Gemfile +++ b/Gemfile @@ -68,6 +68,10 @@ gem 'recaptcha', '~> 5.9.0' gem 'rest-client' gem 'rexml', '~> 3' gem 'stackprof', require: false + +# pinning strscan to v 3.0.1 to deal with deployment issue. Remove line below when issue is fixed +gem 'strscan', '3.0.1' + gem 'terser' gem 'thin' gem 'will_paginate', '~> 3.0' diff --git a/Gemfile.lock b/Gemfile.lock index fdbd874be..cd85b8af7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -163,8 +163,6 @@ GEM faraday-net_http (3.1.0) net-http ffi (1.17.0) - ffi (1.17.0-arm64-darwin) - ffi (1.17.0-x86_64-darwin) flag-icons-rails (3.4.6.1) sass-rails flamegraph (0.9.5) @@ -391,7 +389,7 @@ GEM stackprof (0.2.26) stimulus-rails (1.3.3) railties (>= 6.0.0) - strscan (3.1.0) + strscan (3.0.1) temple (0.10.3) terser (1.2.3) execjs (>= 0.3.0, < 3) @@ -482,6 +480,7 @@ DEPENDENCIES sprockets-rails stackprof stimulus-rails + strscan (= 3.0.1) terser thin turbo-rails @@ -489,4 +488,4 @@ DEPENDENCIES will_paginate (~> 3.0) BUNDLED WITH - 2.5.11 + 2.4.18 From e1ff3356ff6d2234e373a39aac69232edb2b199c Mon Sep 17 00:00:00 2001 From: mdorf Date: Mon, 22 Jul 2024 21:00:55 -0700 Subject: [PATCH 31/61] a bug fix to the language dropdown --- Gemfile | 8 -------- app/assets/javascripts/bp_ontology_viewer.js.erb | 1 - 2 files changed, 9 deletions(-) diff --git a/Gemfile b/Gemfile index d04832082..e31fede4d 100644 --- a/Gemfile +++ b/Gemfile @@ -71,18 +71,10 @@ gem 'stackprof', require: false gem 'terser' gem 'thin' gem 'will_paginate', '~> 3.0' - - - - gem 'net-ftp' gem 'flag-icons-rails', '~> 3.4' gem 'inline_svg' - - - - group :staging, :production do # Application monitoring gem 'newrelic_rpm' diff --git a/app/assets/javascripts/bp_ontology_viewer.js.erb b/app/assets/javascripts/bp_ontology_viewer.js.erb index d9b29450a..5beef4679 100644 --- a/app/assets/javascripts/bp_ontology_viewer.js.erb +++ b/app/assets/javascripts/bp_ontology_viewer.js.erb @@ -306,7 +306,6 @@ jQuery.bioportal.OntologyPage = function(id, location_path, error_string, page_n this.published = false; this.retrieved = false; this.init = init || null; - this.is_data_section = jQuery(document).data().bp.ont_viewer.lang_sections.includes(id); this.retrieve = function(){ jQuery.ajax({ From c7102c8ebe0207011a550f4be97fa30a0ff5b397 Mon Sep 17 00:00:00 2001 From: mdorf Date: Mon, 22 Jul 2024 21:01:47 -0700 Subject: [PATCH 32/61] Gemfile.lock update --- Gemfile.lock | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index cd85b8af7..6db6f9325 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -163,6 +163,8 @@ GEM faraday-net_http (3.1.0) net-http ffi (1.17.0) + ffi (1.17.0-arm64-darwin) + ffi (1.17.0-x86_64-darwin) flag-icons-rails (3.4.6.1) sass-rails flamegraph (0.9.5) @@ -488,4 +490,4 @@ DEPENDENCIES will_paginate (~> 3.0) BUNDLED WITH - 2.4.18 + 2.5.11 From e75d593aae49b985142c6eb19ce91ce16a012d9d Mon Sep 17 00:00:00 2001 From: mdorf Date: Mon, 22 Jul 2024 21:04:22 -0700 Subject: [PATCH 33/61] disable language dropdown for a single language --- app/helpers/multi_languages_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/multi_languages_helper.rb b/app/helpers/multi_languages_helper.rb index de65fa4b1..56630552d 100644 --- a/app/helpers/multi_languages_helper.rb +++ b/app/helpers/multi_languages_helper.rb @@ -96,7 +96,7 @@ def selected_language_label(label) def content_language_selector(id: 'content_language', name: 'content_language') languages, selected = content_languages select_tag(name, options_for_select(languages, selected || 'all'), class: "form-select", - data: { controller: "language-change", 'language-change-section-value': "classes", action: "change->language-change#dispatchLangChangeEvent" }) unless languages&.empty? + data: { controller: "language-change", 'language-change-section-value': "classes", action: "change->language-change#dispatchLangChangeEvent" }) if languages&.length > 1 end def content_languages(submission = @submission || @submission_latest) From c6897f86dad4f7b089eadef9d6bf6f9af780e393 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Thu, 22 Aug 2024 14:15:28 -0700 Subject: [PATCH 34/61] Add include all param where necessary Recent change to the find_by_acronym method in ontologies_api_client result in a lesser set of attributes returned from the triplestore. Also disable cache_refresh_all on updates. --- app/controllers/ontologies_controller.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index d61b1bfa5..4c0847c5f 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -188,7 +188,7 @@ def create def edit # Note: find_by_acronym includes ontology views - @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:id]).first + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:id], include: 'all').first redirect_to_home unless session[:user] && @ontology.administeredBy.include?(session[:user].id) || session[:user].admin? @categories = LinkedData::Client::Models::Category.all @user_select_list = LinkedData::Client::Models::User.all.map {|u| [u.username, u.id]} @@ -254,9 +254,9 @@ def show end # Note: find_by_acronym includes ontology views - @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology], include: 'all').first not_found if @ontology.nil? - + # Handle the case where an ontology is converted to summary only. # See: https://github.com/ncbo/bioportal_web_ui/issues/133. if @ontology.summaryOnly && params[:p].present? @@ -313,7 +313,7 @@ def show end def submit_success - @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:id]).first + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:id], include: 'all').first render 'submit_success' end @@ -345,9 +345,9 @@ def update return end # Note: find_by_acronym includes ontology views - @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology][:acronym] || params[:id]).first + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:id]).first @ontology.update_from_params(ontology_params) - error_response = @ontology.update + error_response = @ontology.update(cache_refresh_all: false) if response_error?(error_response) @categories = LinkedData::Client::Models::Category.all @user_select_list = LinkedData::Client::Models::User.all.map {|u| [u.username, u.id]} From 59ffa48ef6067b3ee55d5fbeefa5319ba86b90c9 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Thu, 22 Aug 2024 14:19:54 -0700 Subject: [PATCH 35/61] Replace find_by_acronym with more performant get Also refactor the update method to prevent an unnecessary fetch of all users and ontologies on successful updates. --- app/controllers/projects_controller.rb | 45 ++++++-------------------- 1 file changed, 10 insertions(+), 35 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 7de8f345f..973fa9f64 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -17,18 +17,11 @@ def index end def show - projects = LinkedData::Client::Models::Project.find_by_acronym(params[:id]) - if projects.blank? - flash.alert = "Project not found: #{params[:id]}" - redirect_to projects_path - return - end - - @project = projects.first + @project = LinkedData::Client::Models::Project.get(params[:id]) @ontologies_used = [] onts_used = @project.ontologyUsed onts_used.each do |ont_used| - ont = LinkedData::Client::Models::Ontology.find(ont_used) + ont = LinkedData::Client::Models::Ontology.get(ont_used, include: 'name,acronym') @ontologies_used << Hash['name', ont.name, 'acronym', ont.acronym] unless ont.nil? end @ontologies_used.sort_by! { |o| o['name'].downcase } @@ -45,13 +38,7 @@ def new end def edit - projects = LinkedData::Client::Models::Project.find_by_acronym(params[:id]) - if projects.blank? - flash.alert = "Project not found: #{params[:id]}" - redirect_to projects_path - return - end - @project = projects.first + @project = LinkedData::Client::Models::Project.get(params[:id]) @user_select_list = LinkedData::Client::Models::User.all.map { |u| [u.username, u.id] } @user_select_list.sort! { |a, b| a[1].downcase <=> b[1].downcase } @usedOntologies = @project.ontologyUsed || [] @@ -84,21 +71,15 @@ def create end def update - projects = LinkedData::Client::Models::Project.find_by_acronym(params[:id]) - if projects.blank? - flash.alert = "Project not found: #{params[:id]}" - redirect_to projects_path - return - end - @project = projects.first + @project = LinkedData::Client::Models::Project.get(params[:id]) @project.update_from_params(project_params) - @user_select_list = LinkedData::Client::Models::User.all.map { |u| [u.username, u.id] } - @user_select_list.sort! { |a, b| a[1].downcase <=> b[1].downcase } - @usedOntologies = @project.ontologyUsed || [] - @ontologies = LinkedData::Client::Models::Ontology.all - error_response = @project.update + error_response = @project.update(cache_refresh_all: false) if response_error?(error_response) @errors = response_errors(error_response) + @user_select_list = LinkedData::Client::Models::User.all.map { |u| [u.username, u.id] } + @user_select_list.sort! { |a, b| a[1].downcase <=> b[1].downcase } + @usedOntologies = @project.ontologyUsed || [] + @ontologies = LinkedData::Client::Models::Ontology.all render :edit else flash[:notice] = 'Project successfully updated' @@ -107,13 +88,7 @@ def update end def destroy - projects = LinkedData::Client::Models::Project.find_by_acronym(params[:id]) - if projects.blank? - flash.alert = "Project not found: #{params[:id]}" - redirect_to projects_path - return - end - @project = projects.first + @project = LinkedData::Client::Models::Project.get(params[:id]) error_response = @project.delete if response_error?(error_response) @errors = response_errors(error_response) From ca3d6dda93070d5069a3648dceb99fc62252f125 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Thu, 22 Aug 2024 14:22:18 -0700 Subject: [PATCH 36/61] Simplify code for fetching an ontology --- app/controllers/submissions_controller.rb | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 042c3e436..9280fdfb6 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -5,15 +5,8 @@ class SubmissionsController < ApplicationController before_action :authorize_and_redirect, only: [:edit, :update, :create, :new] def new - begin - # REVIEW: do we really need this double attempt to locate an ontology? I think find_by_acronym (below) should - # be sufficient. It's not evident that we call the new method with a full URI anymore. - @ontology = LinkedData::Client::Models::Ontology.get(CGI.unescape(params[:ontology_id])) - rescue MultiJson::ParseError - nil - end - - @ontology ||= LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology_id]).first + # NOTE: find_by_acronym includes ontology views + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology_id]).first @submission = @ontology.explore.latest_submission @submission ||= LinkedData::Client::Models::OntologySubmission.new end From c65fc5a38beb6e79390d7670b53d6e1da2038026 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Thu, 22 Aug 2024 14:24:15 -0700 Subject: [PATCH 37/61] Replace find with more performant get Also disable cache_refresh_all on updates, and redirect to the user show page after successful creates. Redirecting to the (slow to load) ontology browse page isn't a good user experience. --- app/controllers/users_controller.rb | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 5d5444304..531b4fd6f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -16,11 +16,7 @@ def index end def show - @user = if session[:user].admin? && params.has_key?(:id) - LinkedData::Client::Models::User.find_by_username(params[:id]).first - else - LinkedData::Client::Models::User.find(session[:user].id) - end + @user = LinkedData::Client::Models::User.get(params[:id], include: 'all') @all_ontologies = LinkedData::Client::Models::Ontology.all(ignore_custom_ontologies: true) @user_ontologies = @user.customOntology @@ -41,8 +37,7 @@ def new end def edit - @user = LinkedData::Client::Models::User.find(params[:id]) - @user ||= LinkedData::Client::Models::User.find_by_username(params[:id]).first + @user = LinkedData::Client::Models::User.get(params[:id], include: 'all') end def create @@ -58,7 +53,7 @@ def create else flash[:notice] = 'Account was successfully created' session[:user] = LinkedData::Client::Models::User.authenticate(@user.username, @user.password) - redirect_to_browse + redirect_to user_path(@user.username) end else render 'new' @@ -66,8 +61,7 @@ def create end def update - @user = LinkedData::Client::Models::User.find(params[:id]) - @user = LinkedData::Client::Models::User.find_by_username(params[:id]).first if @user.nil? + @user = LinkedData::Client::Models::User.get(params[:id], include: 'all') @errors = validate_update(user_params) if @errors.empty? @@ -78,7 +72,7 @@ def update end @user.update_from_params(user_params.merge!(role: user_roles)) - error_response = @user.update + error_response = @user.update(cache_refresh_all: false) if response_error?(error_response) @errors = response_errors(error_response) @@ -99,12 +93,10 @@ def update def destroy response = { errors: String.new(''), success: String.new('') } - @user = LinkedData::Client::Models::User.find(params[:id]) - @user = LinkedData::Client::Models::User.find_by_username(params[:id]).first if @user.nil? + @user = LinkedData::Client::Models::User.get(params[:id]) if session[:user].admin? @user.delete response[:success] << 'User deleted successfully ' - else response[:errors] << 'Not permitted ' end From 4f071d6da52f54429a6c317f6ae0f38ad314c310 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Thu, 22 Aug 2024 14:25:14 -0700 Subject: [PATCH 38/61] Don't make REST calls to get a username from an ID --- app/helpers/application_helper.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4ad4a0de2..95d08d8ff 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -38,9 +38,7 @@ def clean_id(string) end def get_username(user_id) - user = LinkedData::Client::Models::User.find(user_id) - username = user.nil? ? user_id : user.username - username + user_id.split('/').last end def current_user_admin? From b5b19361c0600821c3a41542bc29ee083aa67602 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 4 Sep 2024 11:14:36 -0700 Subject: [PATCH 39/61] fixed correct display of language inside sub-trees --- app/assets/javascripts/bp_class_tree.js.erb | 12 ++-- app/controllers/application_controller.rb | 49 ++++++++++++- app/controllers/concepts_controller.rb | 76 +-------------------- app/controllers/ontologies_controller.rb | 7 -- app/helpers/application_helper.rb | 18 +---- app/helpers/multi_languages_helper.rb | 2 +- app/views/concepts/_child_nodes.html.haml | 5 +- 7 files changed, 62 insertions(+), 107 deletions(-) diff --git a/app/assets/javascripts/bp_class_tree.js.erb b/app/assets/javascripts/bp_class_tree.js.erb index 68ad16600..74c7e9f62 100644 --- a/app/assets/javascripts/bp_class_tree.js.erb +++ b/app/assets/javascripts/bp_class_tree.js.erb @@ -128,12 +128,12 @@ function nodeClicked(node_id, lang = "en") { // Insert notes table insertNotesTable(tabData["notes_table_data"]); - wrapupTabChange(selectedTab); } else { jQuery.blockUI({ message: '

' + '<%= image_tag src="jquery.simple.tree/spinner.gif" %>' + ' Loading Class...

', showOverlay: false }); - jQuery.get('/ajax_concepts/'+jQuery(document).data().bp.ont_viewer.ontology_id+'/?conceptid='+node_id+'&lang=' + lang + '&callback=load', - function(data){ + var ajaxUrl = '/ajax_concepts/'+jQuery(document).data().bp.ont_viewer.ontology_id+'/?conceptid='+node_id+'&lang=' + lang + '&callback=load' + jQuery.get(ajaxUrl, + function(data) { var tabData = data.split("|||"); // the tabs @@ -182,13 +182,15 @@ function placeTreeView(treeHTML) { // Retrieve the tree view using ajax function getTreeView(lang = "en") { + var url = "/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&lang=" + lang + "&conceptid="+encodeURIComponent(jQuery(document).data().bp.ont_viewer.concept_id); jQuery.ajax({ - url: "/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&lang=" + lang + "&conceptid="+encodeURIComponent(jQuery(document).data().bp.ont_viewer.concept_id), + url: url, success: function(data) { placeTreeView(data); }, error: function(data) { - jQuery.get("/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&lang=" + lang + "&conceptid=root", function(data) { + var url = "/ajax/classes/treeview?ontology="+jQuery(document).data().bp.ont_viewer.ontology_id+"&lang=" + lang + "&conceptid=root"; + jQuery.get(url, function(data) { var rootTree = "
Displaying the path to this class has taken too long. You can browse classes below.
" + data; placeTreeView(rootTree); }); diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1625423d2..755d59a02 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -349,9 +349,35 @@ def using_captcha? ENV['USE_RECAPTCHA'].present? && ENV['USE_RECAPTCHA'] == 'true' end + + + + + + + + def get_class(params) lang = request_lang + + + + + + + all_lang = submission_languages + lang = all_lang[0] if all_lang.length === 1 + + + + + + + + + + if @ontology.flat? ignore_concept_param = params[:conceptid].nil? || params[:conceptid].empty? || @@ -381,7 +407,7 @@ def get_class(params) if ignore_concept_param # get the top level nodes for the root # TODO_REV: Support views? Replace old view call: @ontology.top_level_classes(view) - roots = @ontology.explore.roots + roots = @ontology.explore.roots(lang: lang) if roots.nil? || roots.empty? LOG.add :debug, "Missing roots for #{@ontology.acronym}" @@ -412,11 +438,13 @@ def get_class(params) rootNode = @concept.explore.tree(include: "prefLabel,hasChildren,obsolete", lang: lang) if rootNode.nil? || rootNode.empty? - roots = @ontology.explore.roots + roots = @ontology.explore.roots(lang: lang) + if roots.nil? || roots.empty? LOG.add :debug, "Missing roots for #{@ontology.acronym}" not_found end + if roots.any? {|c| c.id == @concept.id} rootNode = roots else @@ -430,6 +458,18 @@ def get_class(params) @concept end + + + + + + + + + + + + def get_metrics_hash metrics_hash = {} # TODO: Metrics do not return for views on the backend, need to enable include_views param there @@ -681,4 +721,9 @@ def request_lang helpers.request_lang end + def submission_languages(submission = nil) + submission = get_ontology_submission_ready(@ontology) unless submission + helpers.submission_languages(submission) + end + end diff --git a/app/controllers/concepts_controller.rb b/app/controllers/concepts_controller.rb index 5eba781eb..25da31451 100644 --- a/app/controllers/concepts_controller.rb +++ b/app/controllers/concepts_controller.rb @@ -29,27 +29,7 @@ def show end end - - - - - - - - - - def show_label - - - - - # binding.pry - - - - - @ontology = LinkedData::Client::Models::Ontology.find(params[:ontology]) @ontology ||= LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first not_found unless @ontology @@ -69,59 +49,16 @@ def show_definition render text: cls.definition end - - - - - - - - - - - - - - - - - def show_tree @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first if @ontology.nil? not_found else - - get_class(params) - - - - - # binding.pry - - - - - render partial: 'ontologies/treeview' end end - - - - - - - - - - - - - - def property_tree @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first not_found if @ontology.nil? @@ -167,10 +104,6 @@ def redirect_new_api private - - - - # Load data for a concept or retrieve a concept's children, depending on the value of the :callback parameter. # Children are retrieved for drawing ontology class trees. def show_ajax_request @@ -185,18 +118,11 @@ def show_ajax_request end end - - - - - - - - def gather_details @mappings = get_concept_mappings(@concept) @notes = @concept.explore.notes @delete_mapping_permission = check_delete_mapping_permission(@mappings) update_tab(@ontology, @concept.id) end + end diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index 69da36a19..d61b1bfa5 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -134,13 +134,6 @@ def index def classes get_class(params) - - - # binding.pry - - - - if ["application/ld+json", "application/json"].include?(request.accept) render plain: @concept.to_jsonld, content_type: request.accept and return end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 5612d667a..9acda65ca 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -72,23 +72,10 @@ def draw_tree(root, id = nil, type = "Menu") end def build_tree(node, string, id) - - - - - # binding.pry - - - - - if node.children.nil? || node.children.length < 1 return string # unchanged end - - - node.children.sort! {|a,b| (a.prefLabel || a.id).downcase <=> (b.prefLabel || b.id).downcase} for child in node.children if child.id.eql?(id) @@ -109,9 +96,10 @@ def build_tree(node, string, id) string << "
  • #{child.prefLabel}
  • " else icons = child.relation_icon(node) - string << "
  • #{child.prefLabel({use_html: true})} #{icons}" + string << "
  • #{child.prefLabel({use_html: true})} #{icons}" + if child.hasChildren && !child.expanded? - string << "" + string << "" elsif child.expanded? string << "
      " build_tree(child, string, id) diff --git a/app/helpers/multi_languages_helper.rb b/app/helpers/multi_languages_helper.rb index 56630552d..54dabd2bd 100644 --- a/app/helpers/multi_languages_helper.rb +++ b/app/helpers/multi_languages_helper.rb @@ -120,7 +120,7 @@ def portal_lang def request_lang lang = params[:language] || params[:lang] lang = portal_lang unless lang - lang.upcase + lang end def lang_code(code_in) diff --git a/app/views/concepts/_child_nodes.html.haml b/app/views/concepts/_child_nodes.html.haml index 79107a3cb..06f887f65 100644 --- a/app/views/concepts/_child_nodes.html.haml +++ b/app/views/concepts/_child_nodes.html.haml @@ -1,8 +1,9 @@ - output ="" - for child in @children - icons = child.relation_icon(@concept) - - output << "
    • #{child.prefLabel} #{icons}" + - output << "
    • #{child.prefLabel} #{icons}" + - if child.hasChildren - - output << "" + - output << "" - output << "
    • " = raw output From 73c641de62e66a337159c567dbc677e9149f8c95 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 4 Sep 2024 11:20:56 -0700 Subject: [PATCH 40/61] Gemfile.lock update --- Gemfile.lock | 84 ++++++++++++++++++++++++++-------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6db6f9325..fd28dcb73 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -85,23 +85,23 @@ GEM tzinfo (~> 2.0) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) - airbrussh (1.5.2) + airbrussh (1.5.3) sshkit (>= 1.6.1, != 1.7.0) ast (2.4.2) - autoprefixer-rails (10.4.16.0) + autoprefixer-rails (10.4.19.0) execjs (~> 2) base64 (0.1.0) bcrypt_pbkdf (1.1.1) bcrypt_pbkdf (1.1.1-arm64-darwin) bcrypt_pbkdf (1.1.1-x86_64-darwin) bigdecimal (3.1.8) - bootsnap (1.18.3) + bootsnap (1.18.4) msgpack (~> 1.2) bootstrap (5.2.3) autoprefixer-rails (>= 9.1.0) popper_js (>= 2.11.6, < 3) sassc-rails (>= 2.0.0) - brakeman (6.1.2) + brakeman (6.2.1) racc builder (3.3.0) capistrano (3.19.1) @@ -109,7 +109,7 @@ GEM i18n rake (>= 10.0.0) sshkit (>= 1.9.0) - capistrano-bundler (2.1.0) + capistrano-bundler (2.1.1) capistrano (~> 3.1) capistrano-locally (0.3.0) capistrano (~> 3.0) @@ -135,7 +135,7 @@ GEM chart-js-rails (0.1.7) railties (> 3.1) coderay (1.1.3) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) crass (1.0.6) cube-ruby (0.0.3) daemons (1.4.1) @@ -152,15 +152,15 @@ GEM eventmachine (1.2.7) excon (0.111.0) execjs (2.9.1) - faraday (2.10.0) - faraday-net_http (>= 2.0, < 3.2) + faraday (2.11.0) + faraday-net_http (>= 2.0, < 3.4) logger faraday-excon (2.1.0) excon (>= 0.27.4) faraday (~> 2.0) faraday-multipart (1.0.4) multipart-post (~> 2) - faraday-net_http (3.1.0) + faraday-net_http (3.3.0) net-http ffi (1.17.0) ffi (1.17.0-arm64-darwin) @@ -184,11 +184,11 @@ GEM nokogiri (>= 1.6.0) ruby_parser (~> 3.5) http-accept (1.7.0) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) i18n (1.14.5) concurrent-ruby (~> 1.0) - inline_svg (1.9.0) + inline_svg (1.10.0) activesupport (>= 3.0) nokogiri (>= 1.6) io-console (0.7.2) @@ -202,14 +202,14 @@ GEM thor (>= 0.14, < 2.0) jquery-ui-rails (7.0.0) railties (>= 3.2.16) - jsbundling-rails (1.3.0) + jsbundling-rails (1.3.1) railties (>= 6.0.0) json (2.7.2) language_server-protocol (3.17.0.3) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - logger (1.6.0) + logger (1.6.1) loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -224,9 +224,9 @@ GEM method_source (1.1.0) mime-types (3.5.2) mime-types-data (~> 3.2015) - mime-types-data (3.2024.0702) + mime-types-data (3.2024.0903) mini_mime (1.1.5) - minitest (5.24.1) + minitest (5.25.1) msgpack (1.7.2) multi_json (1.15.0) multipart-post (2.4.1) @@ -236,7 +236,7 @@ GEM time net-http (0.4.1) uri - net-imap (0.4.14) + net-imap (0.4.16) date net-protocol net-pop (0.1.2) @@ -251,19 +251,21 @@ GEM net-protocol net-ssh (7.2.3) netrc (0.11.0) - newrelic_rpm (9.11.0) + newrelic_rpm (9.13.0) nio4r (2.7.3) - nokogiri (1.16.6-arm64-darwin) + nokogiri (1.16.7-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-darwin) + nokogiri (1.16.7-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-linux) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) - oj (3.16.4) + oj (3.16.5) bigdecimal (>= 3.0) + ostruct (>= 0.2) open_uri_redirections (0.2.1) - parallel (1.25.1) - parser (3.3.4.0) + ostruct (0.6.0) + parallel (1.26.3) + parser (3.3.5.0) ast (~> 2.4.1) racc popper_js (2.11.8) @@ -271,8 +273,8 @@ GEM coderay (~> 1.1) method_source (~> 1.0) psych (3.3.4) - public_suffix (6.0.0) - racc (1.8.0) + public_suffix (6.0.1) + racc (1.8.1) rack (2.2.9) rack-mini-profiler (3.3.1) rack (>= 1.2.0) @@ -327,37 +329,35 @@ GEM http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) - rexml (3.3.2) - strscan - rspec-core (3.13.0) + rexml (3.3.7) + rspec-core (3.13.1) rspec-support (~> 3.13.0) - rspec-expectations (3.13.1) + rspec-expectations (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-mocks (3.13.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (6.1.3) - actionpack (>= 6.1) - activesupport (>= 6.1) - railties (>= 6.1) + rspec-rails (7.0.1) + actionpack (>= 7.0) + activesupport (>= 7.0) + railties (>= 7.0) rspec-core (~> 3.13) rspec-expectations (~> 3.13) rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.1) - rubocop (1.65.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) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.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.31.3) + rubocop-ast (1.32.2) parser (>= 3.3.1.0) ruby-progressbar (1.13.0) ruby_parser (3.21.1) @@ -379,7 +379,7 @@ GEM sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) - sprockets-rails (3.5.1) + sprockets-rails (3.5.2) actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) @@ -389,7 +389,7 @@ GEM net-sftp (>= 2.1.2) net-ssh (>= 2.8.0) stackprof (0.2.26) - stimulus-rails (1.3.3) + stimulus-rails (1.3.4) railties (>= 6.0.0) strscan (3.0.1) temple (0.10.3) @@ -399,7 +399,7 @@ GEM daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) - thor (1.3.1) + thor (1.3.2) tilt (2.4.0) time (0.3.0) date @@ -411,14 +411,14 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.5.0) - uri (0.13.0) + uri (0.13.1) 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.16) + zeitwerk (2.6.18) PLATFORMS arm64-darwin-22 From 7cbdb87e62b76dc7dae278e54b59252623602b64 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 4 Sep 2024 11:47:53 -0700 Subject: [PATCH 41/61] Bump ontologies_api_client to v2.4.0 tag --- Gemfile | 2 +- Gemfile.lock | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index a913445ce..215fecf35 100644 --- a/Gemfile +++ b/Gemfile @@ -57,7 +57,7 @@ gem 'iso-639', '~> 0.3.6' gem 'multi_json' gem 'mysql2', '0.5.5' gem 'oj' -gem 'ontologies_api_client', github: 'ncbo/ontologies_api_ruby_client', tag: 'v2.3.0' +gem 'ontologies_api_client', github: 'ncbo/ontologies_api_ruby_client', tag: 'v2.4.0' gem 'open_uri_redirections' gem 'pry' gem 'psych', '< 4' diff --git a/Gemfile.lock b/Gemfile.lock index 35cd0a8a8..bcd80c448 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GIT remote: https://github.com/ncbo/ontologies_api_ruby_client.git - revision: 027c749f5de3f644b0392197e9482be51738cbe8 - tag: v2.3.0 + revision: f589b13dfbbc133ea67cbae1a8f92b41ea85c14b + tag: v2.4.0 specs: - ontologies_api_client (2.2.5) + ontologies_api_client (2.4.0) activesupport (= 7.0.8) addressable (~> 2.8) excon @@ -13,7 +13,6 @@ GIT lz4-ruby multi_json oj - spawnling (= 2.1.5) GEM remote: https://rubygems.org/ @@ -352,7 +351,6 @@ GEM tilt select2-rails (4.0.13) sexp_processor (4.17.0) - spawnling (2.1.5) sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) From a58a1c845a485e5dfeda4c9b81e1959ec9166659 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 4 Sep 2024 12:14:38 -0700 Subject: [PATCH 42/61] release candidate 1 --- .gitignore | 1 + app/controllers/application_controller.rb | 18 ------------------ 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index e5eb8ea5b..ab0ea98b5 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ node_modules /config/credentials/test.key /config/credentials/staging.key /config/credentials/production.key +/config/credentials/appliance.key /app/assets/builds/* !/app/assets/builds/.keep diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 755d59a02..3fc387b6c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -360,24 +360,6 @@ def using_captcha? def get_class(params) lang = request_lang - - - - - - - all_lang = submission_languages - lang = all_lang[0] if all_lang.length === 1 - - - - - - - - - - if @ontology.flat? ignore_concept_param = params[:conceptid].nil? || params[:conceptid].empty? || From 43a5ce364a9ddb2a4642e37a423d8229ee7472c7 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 4 Sep 2024 14:53:08 -0700 Subject: [PATCH 43/61] bug fix for displaying labels --- app/assets/javascripts/bp_ontology_viewer.js.erb | 2 +- app/javascript/controllers/language_change_controller.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/bp_ontology_viewer.js.erb b/app/assets/javascripts/bp_ontology_viewer.js.erb index 5beef4679..276cfa27d 100644 --- a/app/assets/javascripts/bp_ontology_viewer.js.erb +++ b/app/assets/javascripts/bp_ontology_viewer.js.erb @@ -82,7 +82,7 @@ function displayTree(data) { var metadata_only = jQuery(document).data().bp.ont_viewer.metadata_only; var purl_prefix = jQuery(document).data().bp.ont_viewer.purl_prefix; var concept_name_title = jQuery(document).data().bp.ont_viewer.concept_name_title; - var lang = jQuery(document).data().bp.lang; + var lang = jQuery(document).data().bp.ont_viewer.lang; // Check to see if we're actually loading a new concept or just displaying the one we already loaded previously if (typeof new_concept_id === 'undefined' || new_concept_id == concept_id) { diff --git a/app/javascript/controllers/language_change_controller.js b/app/javascript/controllers/language_change_controller.js index 2b4f3e9fb..b1fc34aa5 100644 --- a/app/javascript/controllers/language_change_controller.js +++ b/app/javascript/controllers/language_change_controller.js @@ -7,11 +7,11 @@ export default class extends Controller { connect() { // can be used for debugging // console.log(this.element.value); - jQuery(document).data().bp.lang = this.element.value; + jQuery(document).data().bp.ont_viewer.lang = this.element.value; } dispatchLangChangeEvent() { - jQuery(document).data().bp.lang = this.element.value; + jQuery(document).data().bp.ont_viewer.lang = this.element.value; var url = window.location.href; url = this.removeURLParameter(url, 'lang'); From ff0cee925a1c9365fab420093585b35bbf0a2237 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 4 Sep 2024 14:56:06 -0700 Subject: [PATCH 44/61] Gemfile.lock update --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index fd28dcb73..21b808b13 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -357,7 +357,7 @@ GEM 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) ruby_parser (3.21.1) From 3802df4c5b806926b0c4e2dc26503130038191bb Mon Sep 17 00:00:00 2001 From: mdorf Date: Mon, 9 Sep 2024 13:18:24 -0700 Subject: [PATCH 45/61] implemented a fix for displaying non-English languages when no English is present --- app/controllers/application_controller.rb | 21 ++------------------ app/controllers/concepts_controller.rb | 12 ++++++----- app/controllers/ontologies_controller.rb | 4 ++-- app/helpers/application_helper.rb | 13 ++++++------ app/helpers/multi_languages_helper.rb | 5 +++-- app/helpers/ontologies_helper.rb | 10 ++++++++++ app/views/concepts/_child_nodes.html.haml | 4 ++-- app/views/layouts/_ontology_viewer.html.haml | 2 +- app/views/ontologies/_metadata.html.haml | 3 +++ app/views/ontologies/_treeview.html.haml | 2 +- 10 files changed, 37 insertions(+), 39 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3fc387b6c..41679e2c0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -349,16 +349,8 @@ def using_captcha? ENV['USE_RECAPTCHA'].present? && ENV['USE_RECAPTCHA'] == 'true' end - - - - - - - - - def get_class(params) - lang = request_lang + def get_class(params, submission) + lang = helpers.request_lang(submission) if @ontology.flat? ignore_concept_param = params[:conceptid].nil? || @@ -699,13 +691,4 @@ def init_trial_license end end - def request_lang - helpers.request_lang - end - - def submission_languages(submission = nil) - submission = get_ontology_submission_ready(@ontology) unless submission - helpers.submission_languages(submission) - end - end diff --git a/app/controllers/concepts_controller.rb b/app/controllers/concepts_controller.rb index 25da31451..c6acd9710 100644 --- a/app/controllers/concepts_controller.rb +++ b/app/controllers/concepts_controller.rb @@ -16,14 +16,15 @@ def show # find_by_acronym includes views by default @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first + @submission = get_ontology_submission_ready(@ontology) @ob_instructions = helpers.ontolobridge_instructions_template(@ontology) if request.xhr? display = params[:callback].eql?('load') ? { full: true } : { display: 'prefLabel' } - display[:language] = request_lang + display[:language] = helpers.request_lang(@submission) @concept = @ontology.explore.single_class(display, params[:id]) not_found if @concept.nil? - show_ajax_request + show_ajax_request(@submission) else render plain: 'Non-AJAX requests are not accepted at this URL', status: :forbidden end @@ -54,7 +55,8 @@ def show_tree if @ontology.nil? not_found else - get_class(params) + @submission = get_ontology_submission_ready(@ontology) + get_class(params, @submission) render partial: 'ontologies/treeview' end end @@ -106,13 +108,13 @@ def redirect_new_api # Load data for a concept or retrieve a concept's children, depending on the value of the :callback parameter. # Children are retrieved for drawing ontology class trees. - def show_ajax_request + def show_ajax_request(submission) case params[:callback] when 'load' gather_details render partial: 'load' when 'children' - @children = @concept.explore.children(pagesize: 750, language: request_lang).collection || [] + @children = @concept.explore.children(pagesize: 750, language: helpers.request_lang(submission)).collection || [] @children.sort! { |x, y| (x.prefLabel || '').downcase <=> (y.prefLabel || '').downcase } render partial: 'child_nodes' end diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index d61b1bfa5..4c80ac1c5 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -132,14 +132,14 @@ def index end def classes - get_class(params) + @submission = get_ontology_submission_ready(@ontology) + get_class(params, @submission) if ["application/ld+json", "application/json"].include?(request.accept) render plain: @concept.to_jsonld, content_type: request.accept and return end @current_purl = @concept.purl if Rails.configuration.settings.purl[:enabled] - @submission = get_ontology_submission_ready(@ontology) unless @concept.id == "bp_fake_root" @notes = @concept.explore.notes diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0f1a6da85..06a8d58d7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -63,15 +63,15 @@ def remove_owl_notation(string) end end - def draw_tree(root, id = nil, type = "Menu") + def draw_tree(root, id = nil, type = "Menu", submission) if id.nil? id = root.children.first.id end # TODO: handle tree view for obsolete classes, e.g. 'http://purl.obolibrary.org/obo/GO_0030400' - raw build_tree(root, "", id) # returns a string, representing nested list items + raw build_tree(root, "", id, submission) # returns a string, representing nested list items end - def build_tree(node, string, id) + def build_tree(node, string, id, submission) if node.children.nil? || node.children.length < 1 return string # unchanged end @@ -96,13 +96,13 @@ def build_tree(node, string, id) string << "
    • #{child.prefLabel}
    • " else icons = child.relation_icon(node) - string << "
    • #{child.prefLabel({use_html: true})} #{icons}" + string << "
    • #{child.prefLabel({use_html: true})} #{icons}" if child.hasChildren && !child.expanded? - string << "" + string << "" elsif child.expanded? string << "
        " - build_tree(child, string, id) + build_tree(child, string, id, submission) string << "
      " end string << "
    • " @@ -366,5 +366,4 @@ def get_link_for_ont_ajax(ont_acronym) end ###END ruby equivalent of JS code in bp_ajax_controller. - end diff --git a/app/helpers/multi_languages_helper.rb b/app/helpers/multi_languages_helper.rb index 54dabd2bd..d12d5dd88 100644 --- a/app/helpers/multi_languages_helper.rb +++ b/app/helpers/multi_languages_helper.rb @@ -100,7 +100,7 @@ def content_language_selector(id: 'content_language', name: 'content_language') end def content_languages(submission = @submission || @submission_latest) - current_lang = request_lang.downcase + current_lang = request_lang(submission).downcase submission_lang = submission_languages(submission) # Transform each language into a select option submission_lang = submission_lang.map do |lang| @@ -117,8 +117,9 @@ def portal_lang session[:locale] || 'en' end - def request_lang + def request_lang(submission = @submission || @submission_latest) lang = params[:language] || params[:lang] + lang = submission_languages(submission)&.first unless lang lang = portal_lang unless lang lang end diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index 98ad6bdbc..e255fdedd 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +require 'iso-639' module OntologiesHelper @@ -182,4 +183,13 @@ def submission_languages(submission = @submission) Array(submission&.naturalLanguage).map { |natural_language| natural_language.split('/').last }.compact end + def abbreviations_to_languages(abbreviations) + # Use iso-639 gem to convert language codes to their English names + languages = abbreviations.map do |abbr| + language = ISO_639.find_by_code(abbr) || ISO_639.find_by_english_name(abbr) + language ? language.english_name : abbr + end + languages.sort + end + end diff --git a/app/views/concepts/_child_nodes.html.haml b/app/views/concepts/_child_nodes.html.haml index 06f887f65..b734cc594 100644 --- a/app/views/concepts/_child_nodes.html.haml +++ b/app/views/concepts/_child_nodes.html.haml @@ -1,9 +1,9 @@ - output ="" - for child in @children - icons = child.relation_icon(@concept) - - output << "
    • #{child.prefLabel} #{icons}" + - output << "
    • #{child.prefLabel} #{icons}" - if child.hasChildren - - output << "" + - output << "" - output << "
    • " = raw output diff --git a/app/views/layouts/_ontology_viewer.html.haml b/app/views/layouts/_ontology_viewer.html.haml index 3848789c4..0370abc0a 100644 --- a/app/views/layouts/_ontology_viewer.html.haml +++ b/app/views/layouts/_ontology_viewer.html.haml @@ -36,7 +36,7 @@ jQuery(document).data().bp.ont_viewer.purl_prefix = "#{(Rails.configuration.settings.purl[:enabled] ? Rails.configuration.settings.purl[:prefix]+"/"+@ontology.acronym : '')}"; jQuery(document).data().bp.ont_viewer.concept_name_title = (jQuery(document).data().bp.ont_viewer.concept_name == "") ? "" : " - " + jQuery(document).data().bp.ont_viewer.concept_name; - jQuery(document).data().bp.ont_viewer.lang = "#{request_lang}"; + jQuery(document).data().bp.ont_viewer.lang = "#{request_lang(sub)}"; jQuery(document).data().bp.ont_viewer.lang_sections = #{ontology_data_sections.to_json.html_safe}; -# Modal dialog for creating a new mapping (must reside in a top-level position in the document to display properly). diff --git a/app/views/ontologies/_metadata.html.haml b/app/views/ontologies/_metadata.html.haml index aa5c44a6d..df5be9e15 100644 --- a/app/views/ontologies/_metadata.html.haml +++ b/app/views/ontologies/_metadata.html.haml @@ -49,6 +49,9 @@ %tr %td Groups %td= groups.map {|g| groups_hash[g].name}.sort.join(", ") + %tr + %td Language + %td= abbreviations_to_languages(@submission_latest.naturalLanguage).join(", ") = raw additional_details -# Submissions pane diff --git a/app/views/ontologies/_treeview.html.haml b/app/views/ontologies/_treeview.html.haml index 3640b5451..20db396a8 100644 --- a/app/views/ontologies/_treeview.html.haml +++ b/app/views/ontologies/_treeview.html.haml @@ -2,4 +2,4 @@ %ul.simpleTree %li.root %ul - = draw_tree(@root, @concept.id) # application_helper::draw_tree + = draw_tree(@root, @concept.id, @submission) # application_helper::draw_tree From 05d6820477531d4925d6c2821685b9afcae016b7 Mon Sep 17 00:00:00 2001 From: mdorf Date: Mon, 9 Sep 2024 13:58:58 -0700 Subject: [PATCH 46/61] fixed the jump_to box to use the correct language --- app/assets/javascripts/bp_visualize.js.erb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/bp_visualize.js.erb b/app/assets/javascripts/bp_visualize.js.erb index 0e4b25542..4e5740ded 100644 --- a/app/assets/javascripts/bp_visualize.js.erb +++ b/app/assets/javascripts/bp_visualize.js.erb @@ -98,7 +98,10 @@ jQuery(document).data().bp.classesTab.search_box_init = function(){ if (jQuery("#search_box").bioportal_autocomplete) { jQuery("#search_box").bioportal_autocomplete("/search/json_search/"+jQuery(document).data().bp.ontology.acronym, { - extraParams: { objecttypes: "class" }, + extraParams: { + objecttypes: "class", + lang: jQuery(document).data().bp.ont_viewer.lang + }, selectFirst: true, lineSeparator: "~!~", matchSubset: 0, From f3e083d253f16582a1d7a4add6595161b87e4d94 Mon Sep 17 00:00:00 2001 From: mdorf Date: Tue, 10 Sep 2024 12:13:17 -0700 Subject: [PATCH 47/61] fixed an issue with site translations --- app/helpers/application_helper.rb | 8 +- app/helpers/internationalisation_helper.rb | 6 +- config/locales/en.yml | 1535 ++------------------ 3 files changed, 96 insertions(+), 1453 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 06a8d58d7..30cfa4855 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -92,11 +92,17 @@ def build_tree(node, string, id, submission) # This fake root will be present at the root of "flat" ontologies, we need to keep the id intact li_id = child.id.eql?("bp_fake_root") ? "bp_fake_root" : short_uuid + # if child.prefLabel && !child.prefLabel.empty? + pref_label = child.prefLabel({use_html: true}) + # else + # pref_label = child.id + # end + if child.id.eql?("bp_fake_root") string << "
    • #{child.prefLabel}
    • " else icons = child.relation_icon(node) - string << "
    • #{child.prefLabel({use_html: true})} #{icons}" + string << "
    • #{pref_label} #{icons}" if child.hasChildren && !child.expanded? string << "" diff --git a/app/helpers/internationalisation_helper.rb b/app/helpers/internationalisation_helper.rb index d8f8ea326..6bda49f76 100644 --- a/app/helpers/internationalisation_helper.rb +++ b/app/helpers/internationalisation_helper.rb @@ -36,8 +36,8 @@ def self.t(*args, **kwargs) end end - def t(*args, **kwargs) - InternationalisationHelper.t(*args, **kwargs) - end + # def t(*args, **kwargs) + # InternationalisationHelper.t(*args, **kwargs) + # end end diff --git a/config/locales/en.yml b/config/locales/en.yml index e406c3c3f..e3187b171 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,1473 +1,110 @@ ---- en: - show_advanced_options: Show advanced options - hide_advanced_options: Hide advanced options - no_result_was_found: No result was found - - activaterecord: + activerecord: errors: models: license: attributes: encrypted_key: - invalid_license_key: is an invalid license key. - no_appliance_id_for_comparison: Could not be validated. Unable to retrieve virtual appliance ID. - appliance_id_mismatch: is an appliance id mismatch. - # Make the resource_term 'ontology' configurable throughout the portal, allowing it to be replaced by alternative terms such as 'semantic_resource', 'vocabulary', 'terminology', or 'semantic_artefact' - resource_term: - ontology: ontology - ontology_plural: ontologies - ontology_single: an ontology - - semantic_resource: semantic resource - semantic_resource_plural: semantic resources - semantic_resource_single: a semantic resource - - vocabulary: vocabulary - vocabulary_plural: vocabularies - vocabulary_single: a vocabulary + invalid_license_key: is an invalid license key + no_appliance_id_for_comparison: couldn't be validated. Unable to retrieve virtual appliance ID. + appliance_id_mismatch: is an appliance ID mismatch - terminology: terminology - terminology_plural: terminologies - terminology_single: a terminology + admin: + licenses: + create: + success: License successfully renewed! - semantic_artefact: semantic artefact - semantic_artefact_plural: semantic artefacts - semantic_artefact_single: a semantic artefact date: formats: - year_month_day_concise: "%Y-%m-%d" # 2017-03-01 - month_day_year: "%b %-d, %Y" # Mar 1, 2017 - monthfull_day_year: "%B %-d, %Y" # March 1, 2017 - language: - translation_not_available: "%{language} translation not available" - portal_language_help_text: Indicate the language in which the interfaces should appear - search_language_help_text: Indicate the language on which to perform the search, restricting text matching exclusively to terms with that language - content_language_help_text_1: Indicate the language on which the content of this resource will be displayed. - content_language_help_text_2: The available languages are specified by the resource admin. - application: - powered_by: Powered by Ontoportal - projects: Projects - loading: loading... - errors_in_fields: Errors in fields %{errors} - add_comment: Add comment - add_new_comment: Add a new comment - reply: Reply - add_proposal: Add proposal - add_new_proposal: Add a new proposal - watching: Watching - beta_badge_text: Beta - beta_badge_tooltip: This feature is experimental and may have issues - ontology_not_found: Ontology %{acronym} not found - concept_not_found: Concept %{concept} not found - error_load: "Error: load failed" - not_found_message: Not Found - try_again: There was an error, please try again - provide_ontology_or_concept: Please provide an ontology id or concept id with an ontology id. - search_for_class: Please search for a class using the Jump To field above - missing_roots_for_ontology: Missing @roots for %{acronym} - missing_roots: Missing roots - missing_class: Missing class %{root_child} - missing_class_ontology: Missing class %{acronym} / %{concept_id} - error_simplify_class: "Failure to simplify class: %{cls}" - no_cache: "No cache or expired cache for ontology: %{id}" - incomplete_simple_ontology: "Incomplete simple ontology: %{id}, %{ont}" - label_xl: - error_valid_label_xl: "Error: You must provide a valid label_xl id" - - admin: - query_not_permitted: Query not permitted - update_info_successfully: Update info successfully retrieved - error_update_info: Problem retrieving update info - %{message} - current_version: "Current version: %{version}" - appliance_id: "Appliance ID: %{id}" - no_record_exists: No record exists for ontology %{acronym} - cache_flush_success: UI cache successfully flushed - cache_flush_error: "Problem flushing the UI cache - %{class}: %{message}" - no_flush_command: "The UI cache does not respond to the 'flush_all' command" - cache_reset_success: UI cache connection successfully reset - cache_reset_error: Problem resetting the UI cache connection - %{message} - no_reset_command: The UI cache does not respond to the 'reset' command - clear_goo_cache_success: Goo cache successfully flushed - clear_goo_cache_error: "Problem flushing the Goo cache - %{class}: %{message}" - clear_http_cache_success: HTTP cache successfully flushed - clear_http_cache_error: "Problem flushing the HTTP cache - %{class}: %{message}" - refresh_report_without_ontologies: Refresh of ontologies report started successfully - refresh_report_with_ontologies: "Refresh of report for ontologies: %{ontologies} started successfully" - submission_deleted_successfully: Submission %{id} for ontology %{ont} was deleted successfully - submission_not_found: Submission %{id} for ontology %{ont} was not found in the system - ontology_not_found: Ontology %{ont} was not found in the system - problem_deleting_submission: "Problem deleting submission %{id} for ontology %{ont} - %{class}: %{message}" - problem_refreshing_report: "Problem refreshing report - %{class}: %{message}" - report_successfully_regenerated: Report successfully regenerated on %{report_date_generated} - ontologies_report_retrieved: Ontologies Report - retrieved %{ontologies} ontologies in %{time}s" - problem_retrieving_ontologies: Problem retrieving ontologies report - %{message} - no_ontologies_parameter_passed: "No ontologies parameter passed. Syntax: ?ontologies=ONT1,ONT2,...,ONTN" - ontology_process_success: "Ontology %{ont} %{success_keyword} successfully," - ontology_not_found_system: "Ontology %{ont} was not found in the system, " - ontology_process_error: "Problem %{error_keyword} ontology %{ont} - %{class}: %{message}," - new_ontologies_created_title: "The following ontologies: %{count} were created in this year" - turbo_confirm: "Are you sure you want to %{name}?" - analytics: - total_ontologies: Total Ontologies - ontologies_count: "%{size} new ontology this year" - ontologies_with_errors: Ontologies with errors - total_visits: Total visits - active_users: Active users - visit_users: "%{visits} users since last month" - ontology_visits: Ontology visits - unique_users_visits: Unique users visits - page_visits: Page visits of this month - main: - cache_management: CACHE MANAGEMENT - clear_cache: Flush UI cache - reset_cache: Reset UI cache connection - clear_goo_cache: Flush GOO cache - clear_http_cache: Flush HTTP cache - version_management: VERSION MANAGEMENT - checking_for_updates: Checking for updates ... - memcache: - title: Memcache stats - parse_log: - title: Parse Log for %{acronym} - log_file: "Log file: %{path}" - index: - title: Administration - administration_console: Administration console - analytics: Analytics - site_administration: Site Administration - ontology_administration: Ontology Administration - licensing: Licensing - users: Users - metadata_administration: Metadata Administration - groups: Groups - categories: Categories - persons_and_organizations: Persons & Organizations - sparql: SPARQL - search: Search & Indexation - report_generated_on: "Report generated on:" - licensed_to: Licensed to - appliance_id: Appliance ID - valid_till: Valid till - days_remaining: Days remaining - renew_license: Renew license - groups: - group_created: Group successfully created in %{time}s - group_error_creation: Problem creating the group - %{message} - group_added_successfully: New Group added successfully - group_updated_successfully: Group successfully updated in %{time}s - problem_of_updating: Problem updating the group - %{message} - group_deleted_successfully: group successfully deleted in in %{time}s - problem_of_deleting: Problem deleting the group - %{message} - synchronization_of_groups: Synchronization of groups started successfully - error_parsing: "Error parsing JSON response - %{class}: %{message}" - problem_synchronizing_groups: "Problem synchronizing groups - %{class}: %{message}" - edit_button: Edit - delete: Delete - info_error_delete: "Can't delete this group because still used" - turbo_confirm: Are you sure? - form: - acronym: Acronym - name: Name - description: Description - created: Created - ontologies: Ontologies - new: - create_new_group: Create new group - create_group: Create group - index: - create_a_new_group: Create a new group - synchronize_groups_slices: Synchronize groups with slices - id: ID - count: COUNT - actions: Actions - no_agents: There are currently no agents. - edit: - edit_group: Edit category - save: Save - categories: - category_created: Category successfully created in %{time}s - category_error_creation: Problem creating the category - %{message} - category_added_successfully: New Category added successfully - category_updated_successfully: Category successfully updated in %{time}s - problem_of_updating: Problem updating the category - %{message} - category_deleted_successfully: Category successfully deleted in %{time}s - problem_of_deleting: Problem deleting the category - %{message} - edit_button: "Edit" - delete: Delete - info_error_delete: Can't delete this category because still used - turbo_confirm: Are you sure? - form: - acronym: Acronym - name: Name - description: Description - created: Created - ontologies: Ontologies - edit: - edit_category: Edit category - save: Save - new: - create_new_category: Create new category - create_category: Create category - index: - create_a_new_category: Create a new category - id: ID - count: COUNT - actions: Actions - no_categories: There are currently no categories. - licenses: - license_notification: - license_contact: > - For more information, email support@ontoportal.org or - visit https://ontoportal.org/licensing. - license_obtain: > - If you are the owner of this OntoPortal installation, you can visit - https://license.ontoportal.org to obtain a license. - license_expired: > - We're sorry, but the license for this OntoPortal installation has expired. If you are the owner of this OntoPortal installation, - please visit https://license.ontoportal.org to renew your license. - license_trial: - one: - This installation of the OntoPortal Appliance is a trial license, which will expire in 1 day. - other: - This installation of the OntoPortal Appliance is a trial license, which will expire in %{count} days. + year_month_day_concise: '%Y-%m-%d' # 2017-03-01 + month_day_year: '%b %-d, %Y' # Mar 1, 2017 + monthfull_day_year: '%B %-d, %Y' # March 1, 2017 - create: - success: License renewed successfully! - label_close: Close - form: - license_from_being_saved: "prohibited this license from being saved:" - paste_your_license: Paste your license key into the text area - submit: Submit - search: - index: - index_data: "INDEX DATA" - collections_management: "COLLECTIONS MANAGEMENT" - name: "Name" - actions: "Actions" - ontology: "Ontology" - agent: "Agent" - ontology_submission: "Ontology submission" - select_model: "Select a model to index" - generate_schema: "Generate schema" - see_schema: "See schema" - see_indexed_data: "See indexed data" - show: - field_name: "Field name" - type: "Type" - multi_valued: "Multi valued" - indexed: "Indexed" - stored: "Stored" - search: - search_query: "Search query" - page: "Page" - page_size: "Page size" - found_documents: "Found %{count} documents" - id: "ID" - properties: "Properties" - show_more: "+ Show more ..." - show_less: "- Show less ..." + layouts: + footer: + copyright_html: Copyright © 2005‑2024, The Board of Trustees of Leland Stanford Junior University. All rights reserved. + grant_html: > + The National Center for Biomedical Ontology was founded as one of the National Centers for Biomedical + Computing, supported by the NHGRI, the NHLBI, and the NIH Common Fund under grant U54-HG004028. + notices: + license_contact: > + For more information, email support@ontoportal.org or + visit https://ontoportal.org/licensing. + license_obtain: > + If you are the owner of this OntoPortal installation, you can visit + https://license.ontoportal.org to obtain a license. + license_expired: > + We're sorry, but the license for this OntoPortal installation has expired. If you are the owner of this OntoPortal installation, + please visit https://license.ontoportal.org to renew your license. + license_trial: + one: + This installation of the OntoPortal Appliance is a trial license, which will expire in 1 day. + other: + This installation of the OntoPortal Appliance is a trial license, which will expire in %{count} days. - annotator: - title: Annotator - description: Get annotations for text with ontology classes - input_hint: Enter or past a text to be annotated ... - options: Options - whole_word_only: Whole word only - match_longest_only: Match longest only - include_mappings: Include mappings - exclude_numbers: Exclude numbers - exclude_synonyms: Exclude synonyms - select_umls_sementic_types: Select UMLS semantic types - select_umls_sementic_groups: Select UMLS semantic groups - include_ancestors: Include ancestors up to level - include_score: Include score - score_threshold: Filter by score threshold - confidence_threshold: Filter by confidence threshold - fastcontext: FastContext - lemmatize: Lemmatize - get_annotation: Get annotations - total_results: Total results - context: Context - certainty: Certainty - class: Class - ontology: Ontology - negation: Negation - temporality: Temporality - experiencer: Experiencer - score: Score - api_documentation: "Annotator API documentation" - insert_sample_text: Insert sample text - sample_text: There has been many recent studies on the use of microbial antagonists to control diseases incited by soilborne and airborne plant pathogenic bacteria - and fungi, in an attempt to replace existing methods of chemical control and avoid extensive use of fungicides, which often lead to resistance in plant pathogens. - In agriculture, plant growth-promoting and biocontrol microorganisms have emerged as safe alternatives to chemical pesticides. Streptomyces spp. and their - metabolites may have great potential as excellent agents for controlling various fungal and bacterial phytopathogens. - concepts: - error_valid_concept: "Error: You must provide a valid concept id" - missing_roots: Missing roots - date_sorted_list_error: The Classes/Concepts didn't define creation or modifications dates with dcterms - list_error: The Collection didn't define any member - close: Close - request_terms_instructions: | - The text below is displayed to the user as the instructions on how to request new terms for your ontology. - You can change this text for %{acronym} ontology by clicking anywhere on the existing text to - bring up the editing dialog. - Click 'Submit Changes' to save or 'Cancel' to discard your changes. - submit_changes: Submit Changes - cancel: Cancel - request_new_term: Request New Term - use_jump_to: Use the "Jump To" to find a class and display details, visualization, notes, and mappings - instances: Instances - notes: Notes - details: Details - visualization: Visualization - mappings: Mappings - new_term_requests: New Term Requests - permanent_link_class: Get a permanent link to this class - request_term: - new_term_instructions: | -

      This ontology integrates with OntoloBridge, allowing community users to suggest additions to the public ontology. Complete the template below to submit a term request directly to the Ontology Manager.

      -
      -

      Term label (required)
      Suggested term name. If a term can be described with more than one synonym, enter only the preferred name here.

      -
      -
      -

      Term Description (required)
      A brief definition, description, or usage of the suggested term. Synonyms of additional terms may be listed in this section.

      -
      -
      -

      Superclass (required)
      The parent term of the suggested term. The parent term must be an existing entry in the current ontology. The superclass can be selected directly from the class tree.

      -
      -
      -

      References (optional)
      Provide evidence that the requested term exists, such as IDs of articles or links to other resources describing the term.

      -
      -
      -

      Justification (optional)
      Provide here any additional information about the requested term.

      -
      home: - bug: Bug - proposition: Proposition - question: Question - ontology_submissions_request: Ontology submissions request - include_name: Please include your name - include_email: Please include your email - include_comment: Please include your comment - fill_text: Please fill in the proper text from the supplied image - notice_feedback: Feedback has been sent - account_title: Account Information - agroportal_figures: "%{site} in figures" - benefit1: Discover new insights and knowledge by exploring other ontologies or smeantic resources in the repository. - benefit2: Contribute to knowledge sharing and semantic interoperability in your domain. - benefit3: Map your ontology to other relevant ones in the domain and collaborate with other users. - benefit4: Get feedback and suggestions from other users who can use and comment on your ontology. - benefit5: Precisely describe your ontology with relevant metadata and get a FAIR score for your ontology. - fair_details: See details - fairness: FAIR Score - get_annotations: Get annotations - get_recommendations: Get recommendations index: - tagline: The home of ontologies and semantic artefacts in agri-food and related domains. - title: Welcome to %{site} + find_ontology_placeholder: Start typing ontology name, then choose from list + query_placeholder: Enter a class, e.g. Melanoma + tagline: the world's most comprehensive repository of biomedical ontologies + title: Welcome to the %{organization} welcome: Welcome to %{site}, - ontology_upload: Do you want to share an ontology? - ontology_upload_benefits: 'By uploading and sharing your ontology to %{site}, you can:' - ontology_upload_button: Submit ontology - discover_ontologies_button: Discover ontologies - ontology_upload_desc: Uploading an ontology or another type of semantic artefact (vocabulary, terminology, thesaurus, ...) is a way of sharing your knowledge with others. - paste_text_prompt: Enter a paragraph of text or some keywords... - recommender_annotator: Recommender and Annotator - support_and_collaborations: Support & Collaborations - ontoportal_instances: Other OntoPortal Instances - see_details: See details - info_tooltip_text: "You are seing the average scores for all the public ontologies in AgroPortal. FAIR scores are computed with the O'FAIRe methodology. More details here: https://github.com/agroportal/fairness" - average: Average - min: Min - max: Max - ontologies: Ontologies - classes: Classes - individuals: Individuals - properties: Properties - projects: Projects - mappings: Mappings - users: Users - twitter_news: News - feedback: - error_on_form: Errors On Form - leave_your_feedback: Leave your feedback - feedback_hi_text: Hi - feedback_info_text: you can use the form below or email us directly at - email: Email - name: Name - proposition_url_page: Error/Proposition page URL - feedback: Feedback - tags: Tags - bug: Bug - proposition: Proposition - question: Question - ontology_submissions_request: Ontology submissions request - optional: (optional) - send: Send - feedback_complete: - title: Feedback sent successfully - message: Thank you for taking the time to share your feedback with us. Our support team will review your message and get back to you as soon as possible with a response. We value your opinion and appreciate your help in improving our services. Thank you for your support! - landscape: - #select_ontologies: Start typing to select ontologies or leave blank to use them all - classes: Number of classes - individuals: Number of individuals - properties: Number of properties - max_depth: Max depth - max_child_count: Max child count - average_child_count: Average child count - classes_with_one_child: Classes with one child - classes_with_more_than_25_children: Classes with more than 25 children - classes_with_no_definition: Classes with no definition - number_of_xioms_triples: Number of axioms (triples) - projects_count: "%{count} projects" - notes_count: "%{count} notes" - reviews_count: "%{count} reviews" - as_contact_count: "%{count} as contact" - as_contributor_count: "%{count} as contributor" - as_creator_count: "%{count} as creator" - as_curator_count: "%{count} as curator" - contributions: "Contributions: %{title}" - published_ontologies: published %{count} ontologies - funded_ontologies: funded %{count} ontologies - endorsed_ontologies: endorsed %{count} ontologies - number_of_ontologies_using_format: Number of ontologies using this format - number_of_ontologies_of_type: Number of ontologies of this ontology type - number_of_ontologies_of_formality_level: Number of ontologies of this formality level - number_of_ontologies_in_catalog: Number of ontologies in this catalog - number_of_ontologies: Number of ontologies - number_of_ontologies_with_class_count_in_range: Number of ontologies with a class count in this range - average: Average - fairness_interface_introduction: This interface shows how an ontology or a group responded successfully to O’FAIRe FAIRness assessment questions - fairness_interface_details: See details for each ontologies on the specific ontology summary pages - fairness_interface_hover_instruction: hover on a principle to see details - average_metrics: Average metrics - category: category - filter_network: Filter network - funding_endorsing_organizations: Organizations funding and endorsing the most ontologies - group: group - groups_and_categories: Groups and Categories - intro: Visualize the panorama of all ontologies on %{site} via metadata aggregation. - more_properties_charts: More properties charts - most_active_ontologies: Most active ontologies - most_active_organizations: Most active organizations - most_active_people: Most active people - most_active_people_as_reviewer: Most active user account - most_mentioned_people: Most mentioned people as contact, creator, contributor or curator - most_mentioned_people_as_reviewer: User account who published notes, reviews, and projects - ontologies_activity_on: Ontology activity - ontologies_by: Ontologies by %{type} - ontologies_contributors: Contributors to ontology development - ontologies_count_by_catalog: Number of ontologies in each semantic artefact catalog - ontologies_formats: Representation language - ontologies_languages: Natural languages - ontologies_licenses: Licenses - ontologies_with_notes_reviews_projects: Ontologies with notes, reviews, and projects - ontology_fairness_evaluator: Ontology FAIRness Evaluator (O’FAIRe) - ontology_formality_levels: Formality levels - ontology_properties_pie_charts: Pie charts retated to object description properties (i.e., meatdata properties used to describe ontology objects). - ontology_relations_network: Ontology relations network - ontology_tools: Most used tools to build ontologies - owl_ontology_author_uris: Properties used to specify object author - owl_ontology_definition_uris: Properties used to specify objects definition - owl_ontology_preflabel_uris: Properties used to specify objects preferred label - owl_ontology_synonym_uris: Properties used to specify object synonyms - properties_usage_proportion: Pie charts related to the most frequent values for certain metadata properties - properties_use: Property usage - relations_between_stored_ontologies: Set of relationships between %{site} ontologies as captured by metadata. Green ontologies are stored in the repository while those in blue are external resources. - size: size - title: "Landscape" - ontologies_properties: Ontologies properties - layout: - header: - account_setting: Account settings - annotator: Annotator - browse: Browse - documentation: Documentation - help: Help - landscape: Landscape - login: Login - logout: Logout - mappings: Mappings - recommender: Recommender - publications: Publications - release_notes: Release Notes - search_prompt: Search in %{portal_name} ... - submit_feedback: Send Feedback - support: Support - cite_us: Cite Us - footer: - products: Products - ontoportal: OntoPortal - release_notes: Release Notes - api: API - tools: Tools - sparql: SPARQL - support: Support - contact_us: Contact Us - wiki: Wiki - documentation: Documentation - agro_documentation: AgroPortal documentation - agreements: Legal - terms: Terms and Conditions - privacy_policy: Privacy Policy - legal_notices: Legal Notices - cite_us: Cite Us - acknowledgments: Acknowledgments - about: About - about_us: About Us - projects: D2KAB - team: Team - notices: - slice: Slice - at: at - visit_the_site: visit the full site - login: - invalid_account_combination: "Invalid account name/password combination" - authentication_failed: "%{provider} authentication failed" - admin_logged_out: Logged out %{old_user}, returned to %{user} - user_logged_out: You have successfully logged out - try_again_notice: ". Please try again." - reset_password_again: ". Please reset your password again." - custom_ontology_set: "The display is now based on your Custom Ontology Set." - welcome: "Welcome " - error_account_name: Please enter an account name - error_password: Please enter a password - enter_email: Enter your username - enter_password: Enter your password - forgot_password: Forgot password? - invalid_login: Errors on the form - no_account: Do not have an account? - password: Password - register: Register - title: Login - username_or_email: Username - login_with_provider: Login with %{provider} - reset_password_message: A password reset email has been sent to your email address, please follow the instructions to reset your password. - back_home_button: Back home - recover_password: Recover password - email: Email - user: User - email_address_associated: Enter the email address associated with your account and we will send an email with instructions to reset your password. - email_placeholder: Enter the email - send_instructions: Send instructions - statistics: - title: "%{portal} Statistics" - lead: (last %{last_years} years) - ontologies: Ontologies - users: Users - projects: Projects - date: Date - ontology_visits: Ontology visits - mappings: - all: All - description: Dive into an overview of the mappings in the bubble view, efficiently locate a specific ontology in the table view or upload your own mappings. - tabs: - bubble_view: Bubbles view - table_view: Table view - upload_mappings: Upload mappings - filter_ontologies: Filter ontologies in the bubble view - filter_bubbles: Filter bubbles - external_mappings: "External Mappings (%{number_with_delimiter})" - interportal_mappings: "Interportal Mappings - %{acronym} (%{number_with_delimiter})" - test_bulk_load: This is the mappings produced to test the bulk load - mapping_created: Mapping created - mapping_updated: Mapping updated - mapping_deleted: "%{map_id} deleted successfully" - mapping_not_found: "Mapping %{id} not found" - error_of_source_and_target: Source and target concepts need to be specified - mapping_issue: "Mapping issue with '%{mapping}' : %{message}" - find_mappings: Find mappings of a class/concept - intro: Find all the mappings of an ontology - loading_mappings: Loading mappings... - no_mappings_available: No mappings available - title: Mappings - upload_mappings: Upload mappings - select_ontologies_list: Select ontologies - bubble_view_legend: - bubble_size: "Bubble size:" - bubble_size_desc: "The global number of mappings with all other ontologies." - color_degree: "Color degree:" - color_degree_desc: "The number of mappings with the selected ontology." - yellow_bubble: "Yellow bubble: " - selected_bubble: "The selected bubble." - less_mappings: "Less mappings" - more_mappings: "More mappings" - count: - ontology: Ontology - mappings: Mappings - no_mappings: There are no mappings to or from this ontology - form: - source_class: Source class - mapping_name: Mapping description (name) - contact_info: Contact info - mapping_source_name: Source name (Mapping set id) - mapping_comment: Comment - mapping_relation: Mapping relation type - save: Save - mapping_table: - mapping_to: Mapping to - relations: Relations - source: Source - type: Type - actions: Actions - no_mappings: There are currently no mappings for this class. - mapping_type_selector: - mapping_type: Mapping type - internal: Internal - interportal: InterPortal - external: External - target_class: Target class - details: Details - ontology_acronym: Ontology (acronym) - class: Class - ontology_acronym_placeholder: Enter the ontology ACRONYM - class_uri_placeholder: Enter the class URI - ontology_uri_placeholder: Enter the ontology URI - show_line: - edit_modal: Edit - delete_button: Delete - turbo_confirm: Are you sure? - edit_mapping: Edit mapping for %{preflabel} - show: - no_mappings_found: No mappings found - bulk_loader: - loader: - example_of_valid_file: See an example of a valid a file - save: Save - loaded_mappings: - mappings_created: "%{size} mappings created successfully" - id: Id - source: Source - target: Target - relation: Relation - properties: Properties - actions: Actions - see_other_properties: See other properties - agents: - not_found_agent: Agent with id %{id} - add_agent: New Agent added successfully - update_agent: Agent successfully updated - agent_usages_updated: Agent usages successfully updated - agent_already_deleted: Agent %{id} already deleted - agent_deleted_successfully: Agent %{id} deleted successfully - ontology_not_valid: "ontology is not valid, here are the errors: " - save: Save - not_used: Not used - see_usages_count: See usages (%{count}) - delete: Delete - delete_error: "Can't delete this %{agent} because still used" - turbo_confirm: Are you sure? - modal_title: "Agent \"%{name}\" usages" - form: - type: Type - person: Person - organization: Organization - name: Name - email: Email - acronym: Acronym - homepage: Homepage - creator: Creator - identifiers: Identifiers - affiliations: Affiliations - index: - create_new_agent: Create New Agent - first_name: First name - usages: Usages - actions: Actions - no_agents: There are currently no agents. - ontology_details: - sections: - classes: Classes - summary: Summary - properties: Properties - instances: Instances - notes: Notes - mappings: Mappings - widgets: Widgets - sparql: Sparql - concepts: Concepts - schemes: Schemes - collections: Collections - concept: - definitions: Definitions - id: ID - in_schemes: In schemes - member_of: Member of - no_preferred_name_for_selected_language: No preferred name for selected language. - obsolete: Obsolete - preferred_name: Preferred name - synonyms: Synonyms - type: Type - metadata: - additional_metadata: Additional Metadata - header: - last_submission_date: Last submission date - projects: - project_not_found: "Project not found: %{id}" - project_successfully_created: Project successfully created - error_unique_acronym: Project with acronym %{acronym} already exists. Please enter a unique acronym. - project_successfully_updated: Project successfully updated - error_delete_project: "Project delete failed: %{errors}" - project_successfully_deleted: Project successfully deleted - contacts: Contacts - create_new_project: Create new project - created: Created - creator: User - delete_admin_only: Delete (admin only) - delete_confirm: Are you sure? - description: Description - description_text: Description text - edit_text: Edit - home_page: Home page - index: - intro: Browse a selection of projects that use %{site} ontologies - institutions: Institutions - ontologies: Ontologies - project_description: Project Description - self: Projects - title: Projects List - view_projects_help: View projects help - - show: - title: Project %{name} - edit_project: Edit Project - description: "Description:" - institution: "Institution:" - contacts: "Contacts:" - home_page: "Home Page:" - ontologies_used: Ontologies Used - no_ontologies_associated: No ontologies are currently associated with this project + help: + welcome: Welcome to the National Center for Biomedical Ontology’s %{site}. %{site} is a web-based application for accessing and sharing biomedical ontologies. + getting_started: > + %{site} allows users to browse, upload, download, search, comment on, and create mappings for ontologies. + browse: > + Users can browse and explore individual ontologies by navigating either a tree structure or an animated graphical view. Users can also view mappings and + ontology metadata, and download ontologies. Additionally, users who are signed in may submit a new ontology to the library. + rest_examples_html: View documentation and examples of the %{site} REST API. + announce_list_html: > + To receive notices of new releases or site outages, please subscribe to the + bioontology-support list. - form: - errors_on_form: Errors On Form - name: "Name: *" - acronym: "Acronym: *" - administrators: "Administrators: *" - home_page: "Homepage: *" - example: "Example:" - description: "Description:*" - select_ontologies: Select Ontologies Used - select_administrators: Select administrators - - edit: - title: Editing Project %{name} - editing_project: Editing project - cancel: Cancel - update_project: Update Project - - new: - title: Add your project - new_project: New project - create_project: Create Project - recommender: - title: Recommender - intro: Get recommendations for the most relevant ontologies from an excerpt of text or a list of keywords - options: Options - input: Input - text: Text - keywords: Keywords - output: Output - ontologies: Ontologies - ontology_sets: Ontology sets - hint: Paste a paragraph of text or some keywords ... - weights_configuration: Weights configuration - coverage: Coverage - acceptance: Acceptance - knowledge_detail: Knowledge detail - specialization: Specialization - ontologies_configuration: Ontologies configuration - max_ont_set: Maximum number of ontologies per set - select_ontologies: Select ontologies - get_recommendations: Get recommendations - edit: Edit - results_title: Recommended ontologies - call_annotator: Call Annotator with the same input - cite: Cite - results_table: - ontology: Ontology - final_score: Final score - coverage_score: Coverage score - acceptance_score: Acceptance score - detail_score: Detail score - specialization_score: Specialization score - annotations: Annotations - register: - account_errors: 'Errors creating your account:' - confirm_password: Confirm password - create_account: Create new account - email: Email - first_name: First name - last_name: Last name - mailing_list: Register to the %{site}'s mailing list - accept_terms_and_conditions: I acknowledge and accept - terms_and_conditions: "%{site}'s terms and conditions." - optional: "(Optional)" - password: Password - title: Register - username: Username - users: - account_successfully_created: Account was successfully created - account_successfully_updated: Account was successfully updated - user_deleted_successfully: User deleted successfully - not_permitted: Not permitted - error_saving_custom_ontologies: Error saving Custom Ontologies, please try again - custom_ontologies_cleared: Custom Ontologies were cleared - custom_ontologies_saved: Custom Ontologies were saved - subscribe_flash_message: "You have successfully %{action} %{to_or_from} our user mailing list: %{list}" - error_subscribe: Something went wrong ... - validate_email_address: Please enter an email address - validate_password: Please enter a password - validate_password_confirmation: Your Password and Password Confirmation do not match - recaptcha_validation: Please fill in the proper text from the supplied image - validate_orcid: Please enter a valid ORCID. - validate_username: please enter a valid username - valid_email_adresse: Please enter a valid email adresse - validate_terms_and_conditions: Accepting the terms and conditions is required, please check the box to proceed. - first_name_required: First name field is required - last_name_required: Last name field is required + annotator: index: - first_name: First name - last_name: Last name - username: Username - email: Email - roles: Roles - ontologies: Ontologies - project: Project - created: Created At - actions: Actions - detail: Detail - delete: Delete - login_as: Login as - error_delete_message: Can't delete this user because still used - turbo_confirm: Are you sure? - edit: - user_to_admin: Are you sure you want to make this user an admin? - admin_privileges: Are you sure you want to revoke admin privileges for this user? - errors_creating_account: "Errors creating your account:" - edit_information: Edit personal information - first_name: First name - last_name: Last name - username: Username - email: Email - orcid_id: ORCID ID - github_id: Github ID - new_password: New password - confirm_password: Confirm password - change_password: Change password - show: - my_account: My account - first_name: "First name:" - last_name: "Last name:" - username: "Username:" - email: "Email:" - orcid_id: "ORCID ID:" - github_id: "GitHub ID:" - mailing_list_subscription: Mailing list subscription - mailing_list_description: Register to the %{portal} announcements mailing list. - api_key_description: Your API Key can be used to access %{portal} Web services - api_documentation: API documentation - custom_semantic_resource: Custom ontology set - please: Please - modify_custom_semantic_resource: | - visit the main site - to modify your Custom Ontology Set. - customize_portal_display: "Customize your %{portal} display: Pick the ontologies that you want to see on %{portal} will hide all other ontologies." - select_semantic_resources: Select ontologies - no_semantic_resources: You haven't picked any ontologies yet - note_feature_logged_in: "Note: this feature works only when you are logged in." - save_custom_semantic_resources: Save custom ontologies - not_subscribed: Not subscribed to any ontology - submitted_semantic_resources: Submitted Ontologies - upload_semantic_resources: Upload Ontologies - projects_created: Projects Created - no_project_created: No project created - unsubscribe: Unsubscribe - subscribe: Subscribe - subscriptions: Subscriptions - no_uploaded_resources: You didn't upload any ontology yet - notes: Notes - search: - no_search_class_provided: No search class provided - search_place_holder: Enter a term, e.g. plant height - advanced_options: - search_language: Search language - ontologies: Ontologies - include_in_search_title: Include in search - include_in_search_values: - property_values: Property values - obolete_classses: Obsolete classes - ontology_views: Ontology views - show_only_title: Show only - show_only_values: - exact_matches: Exact matches - classes_with_definitions: Classes with definitions - show_advanced_options: Show options - hide_advanced_options: Hide options - match_in: Match in - ontologies: ontologies - result_component: - details: Details - visualize: Vizualize - more_from_ontology: more from this ontology - reuses_in: Reuses in - notes: - no_notes: No notes to display - filter_hide_archived: Hide Archived - new_class_proposal: New Class Proposal - new_relationship_proposal: New Relationship Proposal - change_property_value_proposal: Change Property Value Proposal - new_comment_added: New comment added successfully - note_deleted_successfully: Note %{note_id} was deleted successfully - note_not_found: Note %{note_id} was not found in the system - comment: Comment - new_comment: - subject: Subject - comment: Comment - save: Save - new_proposal: - reason_for_change: Reason for change - new_target: New target - old_target: Old target - relationship_type: Relationship type - property_id: Property id - new_value: New value - old_value: Old value - class_id: Class id - label: Label - synonym: Synonym - definition: Definition - parent: Parent - save: save - note_line: - alert_text: "Are you sure you want to delete the note ''%{subject}'' created by %{creator}?
      This action CAN NOT be undone!!!" - delete: Delete - archived: archived - comment: Comment - thread: - submitted_by: submitted by - reply: - about_note_decorator: about %{note_decorator} - comment: Comment - save: save - cancel: cancel - ontolobridge: - problem_of_creating_new_term: "Problem creating a new term %{endpoint}: %{class} - %{message}" - new_term_instructions_saved: New term request instructions for %{acronym} saved - error_saving_new_term_instructions: Unable to save new term instructions for %{acronym} due to a server error - submissions: - filter: - all_formats: All formats - sort_by_name: Sort by name - sort_by_classes: Sort by classes count - sort_by_instances_concepts: Sort by instances/concepts count - sort_by_submitted_date: Sort by submitted date - sort_by_creation_date: Sort by creation date - sort_by_fair_score: Sort by FAIR score - sort_by_popularity: Sort by popularity - sort_by_notes: Sort by notes - sort_by_projects: Sort by projects - no_submissions_for_ontology: "No submissions for ontology: %{ontology}" - submission_updated_successfully: Submission updated successfully - save_button: Save - id: ID - version: Version - actions: Actions - modified: Modified - submitted: Submitted - download: Download - toggle_dropdown: Toggle Dropdown - go_to_api: Go to API - edit_button: Edit - delete_submission_alert: - content_1: "Are you sure you want to delete submission " - content_2: " for ontology " - content_3: "?
      This action CAN NOT be undone!!!" - ontoportal_virtual_appliance: - problem_adding_account: "Problem adding account %{id}: account does not exist" - require_login: You must be logged in to access this section - title: OntoPortal Virtual Appliance Download - intro_paragraph: - main: | - OntoPortal Virtual Appliance distribution contains a pre-installed, - pre-configured version of commonly-used open source NCBO software running - on a Linux operating system. - included_software: "The following software is included on the image:" - ontologies_api: Ontologies API (REST service) - annotator: Annotator - recommender: Recommender - web_user_interface: BioPortal Web User Interface (including ontology visualization, widgets, and Annotator UI) - see_documentation: Please see our - more_information: for more information on working with the Appliance. - download_button_text: Download Latest OntoPortal - archives: - title: Archives - archival_distribution: | - The OntoPortal 2.5 Virtual Appliance is not longer offered for new - users. This archival distribution is available only as a backup for - those who registered for the Appliance before June 2020 - version: version 2.5 - admin_add_users: - title: "Admin: Add Users" - label_account_name: "Account Name:" - add_user_button: Add User - accounts_with_access: "Accounts with access" - export_users_link: "Export Appliance Users As CSV" - bioportal_user: BioPortal User ID - submission_inputs: - edit_ontology_title: Edit ontology - metadata_selector_label: "Filter properties to show" - metadata_selector_placeholder: "Start typing to select properties" - administrators: Administrators - ontology_skos_language_link: Please refer to the documentation for more details. - ontology_skos_language_help: > - SKOS vocabularies submitted to %{portal_name} shall follow a few constraints (e.g., contain a minimum of one skos:ConceptScheme also typed as owl:Ontology) - and top concept assertion. %{link} - ontology_obo_language_link: the OBOinOWL parser. - ontology_obo_language_help: > - OBO ontologies submitted to %{portal_name} will be parsed by the OWL-API which integrates %{link} - The resulting RDF triples will then be loaded in %{portal_name} triple-store. - ontology_owl_language_link: the Protégé - ontology_owl_language_help: > - OWL ontologies submitted to %{portal_name} will be parsed by the OWL-API. An easy way to verify if your ontology will parse is to open it with - %{link} - software which does use the same component. + intro: Get annotations for biomedical text with classes from the ontologies + annotatorplus_html: Check out AnnotatorPlus beta; a new version of the Annotator with added support for negation, and more! - ontology_umls_language_link: by the UMLS2RDF tool. - ontology_umls_language_help: > - UMLS-RRF resources are usually produced %{link} + recommender: + intro: Get recommendations for the most relevant ontologies based on an excerpt from a biomedical text or a list of keywords - groups: Groups - visibility: Visibility - accounts_allowed: Add or remove accounts that are allowed to see this ontology in %{portal_name}. - ontology_view_of_another_ontology: Is this ontology a view of another ontology? - contact: Contact - equivalents: Equivalents - validators: Validators - help_text: Help text - contact_name: "%{name} Name" - contact_email: "%{name} Email" - edit_metadata_instruction: "Edit the metadata of your ontology here. Some of these values are used by %{portal_name} functionalities, including for FAIRness assessment. %{link}" - edit_metadata_instruction_link: "See guidelines and recommendations for metadata here." - license_help: "%{portal_name} requires an URI for the license. If you do not find your choice here, %{link}" - license_help_link: "Please pick up an URI from here." - deprecated_help: "An ontology with status retired shall necessarily be also deprecated, but not the opposite." - known_usage_help: "Consider also declaring %{metadata_knownUsage_help}" - known_usage_help_link: "the projects that are using the ontology" - help_creator: "The following properties take for value an 'agent' in %{portal_name} (either a person or an organization). These agents are shared over all the ontologies and suggested with autocompletion if they already exist. Editing an agent here will change it to all the ontologies that agent is involved in." - version_help: "For more information on how to encode versioning information in an ontology, see %{link}" - version_helper_link: "guidelines and recommendations." - instances: - id: ID - type: Type - label: Label - collections: - error_valid_collection: "Error: You must provide a valid collection id" - no_collections_alert: "%{acronym} does not contain collections (skos:Collection)" - id: ID - preferred_name: Preferred name - members_count: Members count - type: Type - change_requests: - change_request_success_message: Your change request was successfully submitted! View the %{url} on GitHub. - label: Label - type: Type - comment: Comment - close: Close - submit: Submit - check_resolvability: - check_resolvability_message_1: "The URL is resolvable and support the following formats: %{supported_format}" - check_resolvability_message_2: "The URL resolvable but is not content negotiable, support only: %{supported_format}" - check_resolvability_message_3: The URL is not resolvable and not content negotiable (returns %{status}). - uri_placeholder: Type a URI to test its resolvability - show_help: Show help - format_not_specified: Format not specified - how_it_works: - title: How it works - content_1: | - The check resolvability tool allows you to test if a given URL is resolvable. It is based on the HTTP HEAD method. - We check the resolvability of a URL by sending a HEAD request to the URL and checking if the response status code is 200 (OK) and - the returned content type is equal to one of the following %{resolvability_formats}. - content_2: | - We have a timeout set to %{resolvability_timeout} seconds, so if the URL is not resolvable within that time, the check will fail. - And a max redirections set to %{resolvability_max_redirections}, so if the URL is not resolvable within that number of redirections, the check will fail. - resolving_uri: - title: Resolving a URI - content: | - In the context of web semantics, dereferencing refers to the process of resolving and obtaining the actual data associated with a Uniform Resource Identifier (URI). In simpler terms, it involves following a link represented by a URI/IRI to retrieve information or resources linked to that identifier. - In the Semantic Web, URIs are used to uniquely identify resources, and dereferencing allows systems to access and retrieve data related to those resources. When a URI/IRI is dereferenced, it typically leads to the retrieval of RDF (Resource Description Framework) data or other structured information that describes the resource in a machine-readable format. This enables systems to understand and process the meaning of the linked data, facilitating the exchange and integration of information on the web. - content_negotiation: - title: Content negotiation - content: | - Content negotiation in the context of the Semantic Web refers to the mechanism by which two communicating parties, such as a client and a server, agree on the most suitable representation of a resource during the process of dereferencing a URI or IRI. This negotiation aims to ensure effective communication between different systems that may prefer different data formats or languages. - In other words, when a client requests a resource by dereferencing a URI, it indicates its preferences for the format or language of the response data through HTTP headers or other negotiation mechanisms. The server, in turn, examines these preferences and selects the most appropriate representation of the resource based on what is available. - different formats can be agreed upon between a client and server when accessing linked data. Common formats include: + search: + index: + intro: Search for a class in multiple ontologies + search_keywords_placeholder: Enter a class, e.g. Melanoma + categories_placeholder: Start typing to select categories or leave blank to use all + property_definition: Named association between two entities. Examples are "definition" (a relation between a class and some text) and "part-of" (a relation between two classes). + obsolete_definition: > + A class that the authors of the ontology have flagged as being obsolete and which they recommend that people not use. These classes + are often left in ontologies (rather than removing them entirely) so that existing systems that depend on them will continue to function. - RDF/XML: XML-based representation of Resource Description Framework data. - Turtle: Human-readable serialization format for RDF. - JSON-LD: JSON-based format for linked data. - N-Triples and N-Quads: Text formats for expressing RDF triples and quads. - HTML: Markup language for web pages, also used to embed RDF data. - RDFa: Embedding RDF data in HTML or XML using attributes. - SPARQL Query Results XML and JSON: Formats for representing SPARQL query results. + projects: + index: + intro: Browse a selection of projects that use %{site} resources - errors: - error_message: We're sorry but something has gone wrong. - notification_error: We have been notified of this error. - go_home_button: Go home - send_feedback_button: Send a feedback - not_found_page: Page not found - schemes: - error_valid_scheme_id: "Error: You must provide a valid scheme id" - no_main_scheme_alert: no main scheme defined in the URI attribute - no_schemes_alert: "%{acronym} does not contain schemes (skos:ConceptScheme)" - id: ID - preferred_name: Preferred name - type: Type - fair_score: - fairness_unreachable_warning: FAIRness service issue unreachable - go_to_api: Go to API - mod_link: MOD1.4 - see_the_used_properties: see the used properties - metadata_properties: Metadata properties listed in the following are picked from %{mod_link}. - ontology_repository: This ontology repository implements %{mod_link} but does not necessarily encode metadata with the same properties (%{submission_link}). - see_possible_credits: See possible credits - see_metadata_used_properties: See metadata used properties - not_found: not found - "null": "null" - view_fair_scores_definitions: "You are seeing the FAIRness assessment score for this ontology in AgroPortal. FAIR scores are computed with the O'FAIRe methodology. More details here: https://github.com/agroportal/fairness" - ontologies_metadata_curator: - #bulk_edit: Start bulk edit - #use_the_bulk_edit: To use the bulk edit select in the table submissions (the rows) and metadata properties (the columns) for which you want to edit - start_the_bulk_edit: Select in the table submissions (rows) and metadata properties (columns) to start the bulk edit - alert_success_submissions: Submissions were successfully updated - ontologies: Ontologies - get_values: Get values - select_ontologies_and_metadata: select ontologies and the metadata properties - include_all_submissions: Include all submissions - apply_the_change_for_all: Apply the change for all - update_the_current_displayed_content: "will update the current displayed content to all the following submissions:" - save: Save ontologies: - showing_ontologies_size: "Showing %{ontologies_size} of %{analytics_size}" - filters: Filters - no_license: No license - view_license: View license - access_rights_information: Additional license and access rights information - referred_to: It can also be referred to as - private_ontology: Private Ontology - formality_levels: Formality levels - categories: Categories - groups: Groups - ontology_types: Ontology types - natural_languages: Natural languages - showing: Showing - metadata_properties: of %{acronym} metadata properties are filled - home_page: Home Page - ontology_processing_failed: "The ontology processing failed, with the current statuses: %{status}" - ontology_parsing_succeeded: "The ontology parsing succeeded, but some processing steps failed, here are the current statuses: %{status}" - upload_an_ontology: Upload an ontology. Sections such as %{ontology} will be available once done. - new_ontology_is_processing: The ontology is processing. Sections such as %{ontology} will be available once processing is complete. - ontology_is_processing: The ontology is processing. Sections such as %{ontology} will be updated once processing is complete. - contact_support: Contact support - edit_natural_languages: Edit natural languages of %{acronym} - edit_available_languages: Click here to edit available languages - add_new_submission: Add new submission - edit_metadata: Edit metadata - go_to_api: Go to API - projects_using_ontology: Projects using %{acronym} - create_new_project: Create new project - no_projects_using_ontology: No projects using %{acronym} - outside: outside - relation_with_other_ontologies: Relation with other ontologies either in %{inside} or %{outside} - ontology_search_prompt: 'Search an ontology or a term (e.g., plant height)' - views: Views - create_new_view: Create new view - expand_all: Expand All - collapse_all: Collapse All - administrator: "Administrator:" - create_new_view_submission: create new view submission - description: "Description:" - definition: "Definition:" - created_by: "Created By:" - submission: Submission - release_date: Release Date - upload_date: Upload Date - downloads: Downloads - admin_links: Admin Links - edit_link: Edit - no_views: No views available for %{acronym}. - fairness_assessment_questions: O'FAIRe FAIRness assessment questions - see_details: See details - collecting_data: We are still collecting data for %{acronym} - location: Location - allow_users: Allow users to view the metadata of your ontology (Summary) only. - metadata_only: Metadata only (No file) - load_from_url: Load from URL - new_versions_loaded: New versions loaded on a nightly basis. - upload_local_file: Upload local file - ontology_submitted: Ontology submitted successfully! - users_can_see: Users can now see - your_ontology: your ontology - exploring_and_searching: | - in our ontology list but they cannot explore or search it. To enable exploring and searching, - please upload a full version of your ontology. - submitting_ontology_view: Thank you for submitting your ontology view to %{site}. - submitting_ontology: Thank you for submitting your ontology to %{site}. - processing_message: | - We will now put your ontology in the queue to be processed. Please keep in mind that it may take up to several - hours before %{site} users will be able to explore and search your ontology. - notification_message: "When your ontology is ready for viewing, you will receive an email notification and it will be available here:" - contact_support_at: "If you have any questions or problems, please email the %{site} support team at:" - collecting_data_message: We are still collecting data for %{acronym} - submit_new_ontology: Submit new ontology - details: Details - general_information: General information - dates_and_contacts: Dates and contacts - uri: URI - change_notes: Change notes - modification_date: Modification date - date_of_original_creation: Date of original creation - concepts_browsers: - select_scheme: Please select a scheme to display - missing_roots: Missing roots for %{acronym} (skos:topConceptOf) - select_collection: Please select a collection to display - placeholder_jump: Jump to - label_jump: "Jump to:" - filter: Filter - browser: - admin_welcome: Welcome admin,this coloring indicates admin-only features - show_private_ontology: Show private ontology only - show_ontology_views: Show ontology views - show_retired_ontologies: Show retired ontologies - search_placeholder: Start typing to filter ontologies, e.g., AGROVOC... - sections: - classes: Classes - individuals: Individuals - properties: Properties - maximum_depth: Maximum depth - maximum_number_of_children: Maximum number of children - average_number_of_children: Average number of children - classes_with_a_single_child: Classes with a single child - classes_with_more_than_25_children: Classes with more than 25 children - classes_with_no_definition: Classes with no definition - identifiers: Identifiers - identifiers_tooltip: Principal identifiers of the ontology. - dates: Dates - person_and_organization: Persons and organizations - other_links: Other links - info_tooltip_links: Metadata properties that highlight the links enabling access to datasets, downloading ontologies, etc - projects_and_usage_information: Projects and usage information - info_tooltip_projects: Details pertaining to the utilization of the ontology. - methodology_and_provenance: Methodology and provenance - 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. - visits: Visits - views: Views of %{acronym} - create_new_view: Create new view - configuration_metadata: Configuration metadata - info_tooltip_configuration: Regroup all the metadata properties used by the portal to configure the behavior of the resource - label_groups: Groups - download_as_csv: Download as CSV - scroll_down_to_see_more: Scroll down to see more + intro: Browse the library of ontologies - widgets: Widgets are only available for ontologies stored in %{site}. - add_acronym_widgets: Add %{acronym} Web Widgets to your site - widget_block_component_title_1: Jump To - widget_block_component_description_1: Type a class name from %{acronym} and jump to it in %{site} - download: Download the - put_on_server: and put it on your server. - copy_and_paste: Copy the code below and paste it to your HTML page - note: "Note:" - use_quick_jump: If you would like to use Quick Jump across multiple ontologies - enter_list_of_ontologies: You can enter a comma-separated list of ontology ids - set_the_variable: "You can set the variable to 'all' to search all ontologies in %{site}:" - include_definitions: "To include definitions in the Jump To drop-down, add the following variable in Javascript:" - info_pasted_code: In the code that you just pasted, make sure to change the path to the quick_jump.js file to point to the location where you put the file (relative to your HTML file) - example_to_use_code: "For example, if you put the quick_jump.js file in the same directory as your HTML file, this is the code you would use:" - help_visit: For more help visit - widget_block_component_title_2: Autocomplete - widget_block_component_description_2: Fill your form fields with classes from %{acronym} - example_1: Example 1 (start typing the class name to get its full URI) - example_2: Example 2 (get the ID for a class) - example_3: Example 3 (get the preferred name for a class) - include_file: In the header for the page where you want the form field, include the - use_widget: "On your form, for the fields where you want to use the class-selection widget, specify the field's class in the following format:" - for_example: For example, - use_ontology: will use NCI Thesaurus (ontology id is NCIT) and will put the class URI in the field after the user selects the class from the pull-down list. - use_list: "In addition to single ontology ids, you can use a list:" - or: OR - use_all_to_search: "use 'all' to search across all %{site} ontologies:" - autocomplete_widget_accesses: The autocomplete widget accesses ontology content from the latest version of the ontology. - use_following_parameters: "You can use the following parameters to select which value will be placed into the user-visible input field:" - uri: uri - shortid: shortid - name: name - class_uri_description: "Put the complete URI of the class (e.g.," - class_uri_example: 'put the complete URI of the class (e.g., %{link})' - class_shortid_description: put the short id of the class, as used in %{site} (e.g., "Common_Neoplasm"); - class_name_description: put the preferred name of the class (e.g., "Common Neoplasm"); - hidden_elements_description: "In addition to the input element you defined, there are four hidden form elements that are created and then set when a user selects a class from the list. For example, if you create a field with this code:" - hidden_elements_example: "The 'name' attribute is used to create the four following fields (note how the 'a' from the name attribute is appended to the id attribute):" - additional_parameters: Additional parameters are documented on the - more_help_visit: For more help visit - widget_block_component_title_3: Visualization - widget_block_component_description_3: Display a visualization for a given class in %{acronym} - widget_block_component_title_4: Tree Widget - widget_block_component_description_4: Display a class tree with a search field for %{acronym} - can_also_view: You can also view a - detailed_demonstration: detailed demonstration - ncbo_widget_wiki: NCBO Widget Wiki - file: file. - no_class_concept_found: No class/concept found - instances_search_placeholder: Search an Instance in %{acronym} - properties_search_placeholder: Search a property in %{acronym} - schemes_search_placeholder: Search a scheme in %{acronym} - collections_search_placeholder: Search a collection in %{acronym} - - metadata: - fair_score_title: FAIR score - total_score: "Total score : %{score} ( %{normalized_score}%)" - metrics: Metrics - metrics_link_title: Information and evolution diagrams about metrics and other measurements of the ontology. - see_all_metrics: See all the metrics of %{acronym} - show_modal_title_1: All the metrics of %{acronym} - not_calculated_metrics: We have not yet calculated metrics for %{acronym} - show_modal_title_2: "Evolution of the number of %{metric} in %{acronym} per submissions." - submissions: Submissions - submissions_link_title: Set of all the files that have been submitted to %{site}. Each previous metadata record is accessible but only the content of the latest parsed file is accessible via %{site} services - relations_network: Ontology relations network - general_information: General information - initial_created_on: Initial created on - additional_information: For additional information, contact - categories_and_subjects: Categories and subjects - keywords_and_classes: Keywords and classes - languages: Languages - pull_location: Pull location - export_metadata: Export all metadata - abstract: Abstract - description: Description - - htaccess_modal_title: "Rewrite rules for %{acronym} ontology" - instructions_servers: Instructions for %{server} servers - htaccess_redirection_description: "We offer a seamless solution to make your ontology URIs resolvable and content negotiable by allowing URL redirection for your ontology URIs to our Agroportal URIs. To facilitate this process, we've provided you with a set of .htaccess rewrite rules. By following the simple instructions below, you'll be able to implement these rules swiftly and efficiently, ensuring smooth redirection" - - redirection_note: | - This redirection works only for uri with the form of: url/path/resource_id - This will not work for uris in the form of: url/path/to/something#resource_id OR url/path/to/something:resource_id - htaccess_redirection: - instruction_1: "Access .htaccess File:" - instruction_1_content: "Locate the .htaccess file in the root directory of your website, if it doesn't exist create one. This file controls how your web server behaves and is often used for URL rewriting and redirection." - instruction_2: "Copy and Paste Redirect Rules in the .htaccess file:" - instruction_2_content: "Copy the rewrite rules provided in the black rectangle. Open the .htaccess file and paste the copied redirect rules into the file" - instruction_3: "Enable rewrite module (Linux):" - instruction_3_content: "you'll need to ensure that the Apache module called 'rewrite' is enabled by running the command `sudo a2enmod rewrite`" + concepts: + request_term: + new_term_instructions: > +

      This ontology integrates with OntoloBridge, allowing community users to suggest additions to the public ontology. Complete the template below to submit a term request directly to the ontology maintainer.

      +
      +

      Term Label (required)
      Suggested term name. If a term can be described with multiple synonyms, only list the preferred name here.

      +
      +
      +

      Term description (required)
      A brief definition, description, or usage of your suggested term. Additional term synonyms may be listed in this section.

      +
      +
      +

      Superclass (required)
      The parent term of the suggested term. The parent term should be an existing entry of the current ontology. The superclass can be selected directly from Bioportal's Classes tree viewer.

      +
      +
      +

      References (optional)
      Provide evidence for the existence of the requested term such as Pubmed IDs of papers or links to other resources that describe the term.

      +
      +
      +

      Justification (optional)
      Provide any additional information about the requested term here.

      +
      + mappings: + intro: Browse mappings between classes in different ontologies - - nginx_redirection: - instruction_1: "Access Nginx configuration file:" - instruction_1_content: "The location of this file can vary depending on your system, but common locations include /etc/nginx/nginx.conf, /etc/nginx/sites-available/default or /usr/local/etc/nginx/nginx.conf." - instruction_2: "Locate the server block or location block where you want to enable URL rewriting:" - instruction_2_content: "This is typically within the server { ... } block." - instruction_3: "Copy and Paste Redirect Rules :" - instruction_3_content: "Copy the rewrite rules provided in the black rectangle. Inside the appropriate block, add the copied rules to enable URL rewriting" - instruction_4: "Test the configuration for syntax errors:" - instruction_4_content: "Run the following command to test the redirection configuration `sudo nginx -t`" - instruction_5: "Reload Nginx to apply the changes:" - instruction_5_content: "Restart the nginx server to apply the redirection using this command `sudo systemctl reload nginx`" - - components: - check_resolvability: checking resolvability... - error_block: child_data_generator block did not provide all the child arguements - empty_field: The fields %{properties} are empty - save_button: Save - cancel_button: Cancel - go_to_api: Go to API - file_input_message: Drop your file here or, browse files on your device. - all_languages: All languages - select_anguage: Select a language - back: Back - next: Next - finish: Finish - creative_commons_license: Creative Commons License - open_source_license: Open Source License - show_more: "+ Show more ..." - show_less: "- Show less ..." - fair_score: FAIR score - details_details: FAIR details ... - projects: projects - notes: notes - concepts: concepts - instances: instances - classes: classes - submitted: Submitted - by: by - creation_date: Creation date %{date} - view_of_the_ontology: "View of the ontology %{ontology}" - view: View - debug: Debug - loading: Loading - unwatch: UnWatch - watch: Watch - resource: "%{sub_text} this resource" - notified_of_all_updates: Be the first to watch this resource and be notified of all its updates - join_the_count: Join the %{count} users, watching this resource and be notified of all its updates - see_more: See more... - see_less: See less... - tree_view_empty: No result found - copy_original_uri: Copy original URI - copy_portal_uri: Copy %{portal_name} URI - properties: - id: ID - type: Type - preferred_name: Preferred name - definitions: Definitions - parent: Parent - no_properties_alert: "%{acronym} does not contain properties" - visits: - ontology_visits: Ontology visits - name: name - visits: visits - ontologies_selector: - clear_selection: Clear selection - ontologies_advanced_selection: Ontologies advanced selection - search_hint: Filter ontologies ... - show_ontology_view: Show ontology views - hide_retired_ontologies: Hide retired ontologies - tabs_title: - categories: Categories - groups: Groups - format: Format - natural_languages: Natural languages - formality_levels: Formality levels - ontology_types: Ontology types - select_all: Select all - cancel: Cancel - apply: Apply - unselect_all: Unselect all - tools: - search: - title: "Search content (coming)" - description: > - This tool/service enables users to search for any Resource Description Framework (RDF) element within the portal. - Users can search by either the unique identifier (URI) associated with the RDF element or by its label. - It facilitates efficient navigation and retrieval of specific RDF elements, enhancing user experience and productivity within the portal. - converter: - title: "Content converter (coming)" - description: > - The Content Converter tool/service offers the capability to convert any RDF element into various formats such as RDF/XML, Turtle, Ntriples, or JSON. - This functionality allows users to transform RDF data into formats suitable for different purposes or compatible with various systems and applications. - It promotes interoperability and flexibility in handling RDF data within the portal ecosystem. - url_checker: - title: "URI resolvability checker" - description: > - This tool/service verifies the resolvability of Uniform Resource Identifiers (URIs) and their content negotiability. - It checks whether a given URI is accessible and whether the content associated with it can be negotiated based on the client's preferences. - This functionality ensures the reliability and accessibility of linked resources within the RDF ecosystem, aiding in maintaining data integrity and facilitating seamless integration with external resources. - feedback_mail: - name: "Name: %{name}" - email: "Email: %{email}" - location: "Location: %{location}" - tags: Tags - feedback: Feedback \ No newline at end of file From 43555c13c061ba6b0c62ea7a30cc3f4f77e5c9aa Mon Sep 17 00:00:00 2001 From: mdorf Date: Tue, 10 Sep 2024 23:05:16 -0700 Subject: [PATCH 48/61] implemented a fix to the empty nodes display for a specific language --- app/controllers/application_controller.rb | 25 +++++++++------------- app/controllers/concepts_controller.rb | 6 +++++- app/helpers/application_helper.rb | 18 ++++++++++------ app/views/concepts/_details.html.haml | 6 +++++- app/views/concepts/_request_term.html.haml | 6 +----- 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 41679e2c0..06cd2e302 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -243,7 +243,8 @@ def redirect_new_api(class_view = false) not_found unless acronym if class_view @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(acronym).first - concept = get_class(params).first.to_s + @submission = get_ontology_submission_ready(@ontology) + concept = get_class(params, @submission).first.to_s redirect_to "/ontologies/#{acronym}?p=classes#{params_string_for_redirect(params, prefix: "&")}", :status => :moved_permanently else redirect_to "/ontologies/#{acronym}#{params_string_for_redirect(params)}", :status => :moved_permanently @@ -389,7 +390,10 @@ def get_class(params, submission) end @root = LinkedData::Client::Models::Class.new(read_only: true) - @root.children = roots.sort{|x,y| (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase} + @root.children = roots.sort{|x,y| + x.prefLabel = helpers.link_last_part(x.id) if x.prefLabel.to_s.empty? + y.prefLabel = helpers.link_last_part(y.id) if y.prefLabel.to_s.empty? + (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase} # get the initial concept to display root_child = @root.children.first @@ -426,24 +430,15 @@ def get_class(params, submission) end end @root = LinkedData::Client::Models::Class.new(read_only: true) - @root.children = rootNode.sort{|x,y| (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase} + @root.children = rootNode.sort{|x,y| + x.prefLabel = helpers.link_last_part(x.id) if x.prefLabel.to_s.empty? + y.prefLabel = helpers.link_last_part(y.id) if y.prefLabel.to_s.empty? + (x.prefLabel || "").downcase <=> (y.prefLabel || "").downcase} end end @concept end - - - - - - - - - - - - def get_metrics_hash metrics_hash = {} # TODO: Metrics do not return for views on the backend, need to enable include_views param there diff --git a/app/controllers/concepts_controller.rb b/app/controllers/concepts_controller.rb index c6acd9710..b945f04e6 100644 --- a/app/controllers/concepts_controller.rb +++ b/app/controllers/concepts_controller.rb @@ -115,7 +115,11 @@ def show_ajax_request(submission) render partial: 'load' when 'children' @children = @concept.explore.children(pagesize: 750, language: helpers.request_lang(submission)).collection || [] - @children.sort! { |x, y| (x.prefLabel || '').downcase <=> (y.prefLabel || '').downcase } + @children.sort! do |x, y| + x.prefLabel = helpers.link_last_part(x.id) if x.prefLabel.to_s.empty? + y.prefLabel = helpers.link_last_part(y.id) if y.prefLabel.to_s.empty? + (x.prefLabel || '').downcase <=> (y.prefLabel || '').downcase + end render partial: 'child_nodes' end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 30cfa4855..10567ccea 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -92,17 +92,11 @@ def build_tree(node, string, id, submission) # This fake root will be present at the root of "flat" ontologies, we need to keep the id intact li_id = child.id.eql?("bp_fake_root") ? "bp_fake_root" : short_uuid - # if child.prefLabel && !child.prefLabel.empty? - pref_label = child.prefLabel({use_html: true}) - # else - # pref_label = child.id - # end - if child.id.eql?("bp_fake_root") string << "
    • #{child.prefLabel}
    • " else icons = child.relation_icon(node) - string << "
    • #{pref_label} #{icons}" + string << "
    • #{child.prefLabel({use_html: true})} #{icons}" if child.hasChildren && !child.expanded? string << "" @@ -244,6 +238,16 @@ def at_slice? !@subdomain_filter.nil? && !@subdomain_filter[:active].nil? && @subdomain_filter[:active] == true end + def link_last_part(url) + return "" if url.nil? + + if url.include?('#') + url.split('#').last + else + url.split('/').last + end + end + def truncate_with_more(text, options = {}) length ||= options[:length] ||= 30 trailing_text ||= options[:trailing_text] ||= " ... " diff --git a/app/views/concepts/_details.html.haml b/app/views/concepts/_details.html.haml index 4bc965768..cb0ff4449 100644 --- a/app/views/concepts/_details.html.haml +++ b/app/views/concepts/_details.html.haml @@ -25,7 +25,11 @@ %tr %td Preferred Name %td - %p= @concept.prefLabel({:use_html => true}).html_safe + %p + - if @concept.prefLabel({:use_html => false}).to_s.empty? + %div.alert.alert-warning= 'No preferred name provided for selected language' + - else + = @concept.prefLabel({:use_html => true}).html_safe %td %tr %td Synonyms diff --git a/app/views/concepts/_request_term.html.haml b/app/views/concepts/_request_term.html.haml index 7185c38a3..7c3bed4a9 100644 --- a/app/views/concepts/_request_term.html.haml +++ b/app/views/concepts/_request_term.html.haml @@ -36,11 +36,7 @@ var g_prefLabel; jQuery(document).ready(function() { - g_prefLabel = '#{@concept.prefLabel({:use_html => false}).html_safe}'; - - if (!g_prefLabel) { - g_prefLabel = '#{@concept.id}'; - } + g_prefLabel = '#{@concept.prefLabel({:use_html => false}).to_s.empty? ? @concept.id : @concept.prefLabel({:use_html => false}).html_safe}'; var isPopulated = window.localStorage.getItem('request_term_form_populated'); if (isPopulated) { From 297a6ba7bb1806aef06a5415755f606d3bd2c9de Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 11 Sep 2024 13:20:22 -0700 Subject: [PATCH 49/61] fixed languages display on the ontology metadata page --- Gemfile.lock | 13 +++++++------ app/views/ontologies/_metadata.html.haml | 1 + 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a57744812..0ba617a4f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -258,7 +258,7 @@ GEM racc (~> 1.4) nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) - oj (3.16.5) + oj (3.16.6) bigdecimal (>= 3.0) ostruct (>= 0.2) open_uri_redirections (0.2.1) @@ -321,7 +321,7 @@ GEM json redis (4.8.1) regexp_parser (2.9.2) - reline (0.5.9) + reline (0.5.10) io-console (~> 0.5) rest-client (2.1.0) http-accept (>= 1.7.0, < 2.0) @@ -331,7 +331,7 @@ GEM rexml (3.3.7) 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) @@ -373,7 +373,7 @@ GEM sprockets-rails tilt select2-rails (4.0.13) - sexp_processor (4.17.0) + sexp_processor (4.17.2) sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) @@ -381,11 +381,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 stackprof (0.2.26) stimulus-rails (1.3.4) railties (>= 6.0.0) @@ -399,7 +400,7 @@ GEM rack (>= 1, < 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) diff --git a/app/views/ontologies/_metadata.html.haml b/app/views/ontologies/_metadata.html.haml index df5be9e15..b166fda19 100644 --- a/app/views/ontologies/_metadata.html.haml +++ b/app/views/ontologies/_metadata.html.haml @@ -49,6 +49,7 @@ %tr %td Groups %td= groups.map {|g| groups_hash[g].name}.sort.join(", ") + - unless @submission_latest.naturalLanguage.empty? %tr %td Language %td= abbreviations_to_languages(@submission_latest.naturalLanguage).join(", ") From a60be0dc24d35d87a6a67cd8caad26cbea658996 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 11 Sep 2024 10:10:43 -0700 Subject: [PATCH 50/61] Account for https in class IDs Resolves #326 --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d8d9d028b..c57b32dfd 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -357,7 +357,7 @@ def get_link_for_cls_ajax(cls_id, ont_acronym, target=nil) else target = " target='#{target}' " end - if cls_id.start_with? 'http://' + if cls_id.start_with?('http://', 'https://') href_cls = " href='#{bp_class_link(cls_id, ont_acronym)}' " data_cls = " data-cls='#{cls_id}' " data_ont = " data-ont='#{ont_acronym}' " From 2397bdc182f9004194d0e791ce268a85b03fd673 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 11 Sep 2024 10:16:18 -0700 Subject: [PATCH 51/61] Removed unused method --- app/helpers/application_helper.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index c57b32dfd..0cc0d96de 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -136,14 +136,6 @@ def help_icon(link, html_attribs = {}) BLOCK end - def anonymous_user - # - # TODO: Fix and failures from removing 'DataAccess' call here. - # - #user = DataAccess.getUser($ANONYMOUS_USER) - user ||= User.new({"id" => 0}) - end - def render_advanced_picker(custom_ontologies = nil, selected_ontologies = [], align_to_dom_id = nil) selected_ontologies ||= [] init_ontology_picker(custom_ontologies, selected_ontologies) From ccc28341345b80242e46f827b88f9f80accd0164 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Thu, 12 Sep 2024 17:51:39 -0700 Subject: [PATCH 52/61] Remove unused partials --- app/views/search/_concept_preview.html.erb | 17 ------ app/views/search/_concepts.html.erb | 22 ------- app/views/search/_results.html.erb | 69 ---------------------- 3 files changed, 108 deletions(-) delete mode 100644 app/views/search/_concept_preview.html.erb delete mode 100644 app/views/search/_concepts.html.erb delete mode 100644 app/views/search/_results.html.erb diff --git a/app/views/search/_concept_preview.html.erb b/app/views/search/_concept_preview.html.erb deleted file mode 100644 index 5d8cdc1fe..000000000 --- a/app/views/search/_concept_preview.html.erb +++ /dev/null @@ -1,17 +0,0 @@ -
      -Preview of <%=link_to "#{@concept.label_html},#{@concept.id}",uri_url(:ontology=>@ontology,:conceptid=>@concept.id),:target=>"_new"%> - -
      - - -<%@concept.properties.each_pair{|key,value| %> - - - - - -<% } %> - -
      <%="#{remove_owl_notation(key)}:"%><%="#{value}"%>
      - -
      \ No newline at end of file diff --git a/app/views/search/_concepts.html.erb b/app/views/search/_concepts.html.erb deleted file mode 100644 index b0593ccee..000000000 --- a/app/views/search/_concepts.html.erb +++ /dev/null @@ -1,22 +0,0 @@ -<%if @concepts.size > 0%> -
      -
      - -
      - -
      - -
      Comment
      <%=text_area(:mapping, :comment, :size => "30x10")%>
      -
      - -
      - <%= select("mapping", "directionality", { "Bidirectional" => "bidirectional", "Unidirectional" => "unidirectional" })%>

      - <%= submit_tag "Create", :class=>'blueButton'%>    -
      -<%else%> -

      No Results Found

      -<%end%> \ No newline at end of file diff --git a/app/views/search/_results.html.erb b/app/views/search/_results.html.erb deleted file mode 100644 index 314707ad3..000000000 --- a/app/views/search/_results.html.erb +++ /dev/null @@ -1,69 +0,0 @@ - -<%if @results.size > 0%> - - <%if params[:page].to_i > 1 %> - << Previous Page - - <%end%> - Page <%=params[:page] || 1%> of <%=@pages%> - <%if !params[:page].to_i.eql?(@pages.to_i)%> - Next Page >> - - <%end%> - - - - - - - - - - - - - - <%for result in @results%> - <%begin - #catch exceptions and dont draw row - row = " - - - - "%> - <%=row%> - <%rescue - end - %> - <%end%> - - - - - -
      - <%if params[:page].to_i >1 %> << Previous Page <%end%> Page <%=params[:page] || 1%> of <%=@pages%> <%if !params[:page].to_i.eql?(@pages.to_i)%> Next Page >><%end%> -
      Class NameFound InOntology
      #{link_to highlight(result[:contents],@keyword), uri_url(:ontology => result[:ontologyVersionId], :conceptid => result[:conceptIdShort]), :onclick => "YAHOO.tabwait.container.wait.show();"}#{result[:recordType].titleize.gsub("Record Type","")}#{link_to result[:ontologyDisplayLabel], ontology_path(:id=>result[:ontologyVersionId])}
      - <%if params[:page].to_i >1 %> << Previous Page <%end%> Page <%=params[:page] || 1%> of <%=@pages%> <%if !params[:page].to_i.eql?(@pages.to_i)%> Next Page >><%end%> -
      -<%else%> -

      No Results Found

      -<%end%> From 918eff166f77000a08cd5d438141f11b9822cdd2 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Tue, 17 Sep 2024 14:17:31 -0700 Subject: [PATCH 53/61] Fix 163 RuboCop warnings Also removes three unused methods: isOwner?, truncate_with_more, and help_icon. --- app/helpers/application_helper.rb | 203 ++++++++++-------------------- 1 file changed, 66 insertions(+), 137 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0cc0d96de..12ec47cfe 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,40 +1,23 @@ -# Methods added to this helper will be available to all templates in the application. - require 'uri' require 'cgi' require 'digest/sha1' -require 'pry' # used in a rescue module ApplicationHelper - def get_apikey unless session[:user].nil? - return session[:user].apikey + session[:user].apikey else - return LinkedData::Client.settings.apikey - end - end - - def isOwner?(id) - unless session[:user].nil? - if session[:user].admin? - return true - elsif session[:user].id.eql?(id) - return true - else - return false - end + LinkedData::Client.settings.apikey end end def clean(string) - string = string.gsub("\"",'\'') - return string.gsub("\n",'') + string = string.gsub("\"", '\'') + string.gsub("\n", '') end def clean_id(string) - new_string = string.gsub(":","").gsub("-","_").gsub(".","_") - return new_string + string.gsub(':', '').gsub('-', '_').gsub('.', '_') end def get_username(user_id) @@ -47,50 +30,31 @@ def current_user_admin? def remove_owl_notation(string) # TODO_REV: No OWL notation, but should we modify the IRI? - return string - - unless string.nil? - strings = string.split(":") - if strings.size<2 - #return string.titleize - return string - else - #return strings[1].titleize - return strings[1] - end - end + string end - def draw_tree(root, id = nil, type = "Menu", submission) + def draw_tree(root, id = nil, type = 'Menu', submission) if id.nil? id = root.children.first.id end # TODO: handle tree view for obsolete classes, e.g. 'http://purl.obolibrary.org/obo/GO_0030400' - raw build_tree(root, "", id, submission) # returns a string, representing nested list items + raw build_tree(root, '', id, submission) # returns a string, representing nested list items end def build_tree(node, string, id, submission) - if node.children.nil? || node.children.length < 1 - return string # unchanged + if node.children.nil? || node.children.empty? + return string end - node.children.sort! {|a,b| (a.prefLabel || a.id).downcase <=> (b.prefLabel || b.id).downcase} - for child in node.children - if child.id.eql?(id) - active_style="class='active'" - else - active_style = "" - end - if child.expanded? - open = "class='open'" - else - open = "" - end + node.children.sort! { |a, b| (a.prefLabel || a.id).downcase <=> (b.prefLabel || b.id).downcase } + node.children.each do |child| + active_style = child.id.eql?(id) ? "class='active'" : '' + open = child.expanded? ? "class='open'" : '' # This fake root will be present at the root of "flat" ontologies, we need to keep the id intact - li_id = child.id.eql?("bp_fake_root") ? "bp_fake_root" : short_uuid + li_id = child.id.eql?('bp_fake_root') ? 'bp_fake_root' : short_uuid - if child.id.eql?("bp_fake_root") + if child.id.eql?('bp_fake_root') string << "
    • #{child.prefLabel}
    • " else icons = child.relation_icon(node) @@ -99,11 +63,11 @@ def build_tree(node, string, id, submission) if child.hasChildren && !child.expanded? string << "" elsif child.expanded? - string << "
        " + string << '
          ' build_tree(child, string, id, submission) - string << "
        " + string << '
      ' end - string << "" + string << '' end end @@ -111,11 +75,11 @@ def build_tree(node, string, id, submission) end def loading_spinner(padding = false, include_text = true) - loading_text = include_text ? " loading..." : "" + loading_text = include_text ? ' loading...' : '' if padding - raw('
      ' + image_tag("spinners/spinner_000000_16px.gif", style: "vertical-align: text-bottom;") + loading_text + '
      ') + raw('
      ' + image_tag('spinners/spinner_000000_16px.gif', style: 'vertical-align: text-bottom;') + loading_text + '
      ') else - raw(image_tag("spinners/spinner_000000_16px.gif", style: "vertical-align: text-bottom;") + loading_text) + raw(image_tag('spinners/spinner_000000_16px.gif', style: 'vertical-align: text-bottom;') + loading_text) end end @@ -125,22 +89,11 @@ def short_uuid rand(36**8).to_s(36) end - def help_icon(link, html_attribs = {}) - html_attribs["title"] ||= "Help" - attribs = [] - html_attribs.each {|k,v| attribs << "#{k.to_s}='#{v}'"} - return <<-BLOCK - - - - BLOCK - end - def render_advanced_picker(custom_ontologies = nil, selected_ontologies = [], align_to_dom_id = nil) selected_ontologies ||= [] init_ontology_picker(custom_ontologies, selected_ontologies) - render :partial => "shared/ontology_picker_advanced", :locals => { - :custom_ontologies => custom_ontologies, :selected_ontologies => selected_ontologies, :align_to_dom_id => align_to_dom_id + render partial: 'shared/ontology_picker_advanced', locals: { + custom_ontologies: custom_ontologies, selected_ontologies: selected_ontologies, align_to_dom_id: align_to_dom_id } end @@ -159,69 +112,63 @@ def init_ontology_picker_single end def get_ontologies_data(ontologies = nil) - ontologies ||= LinkedData::Client::Models::Ontology.all(include: "acronym,name") + ontologies ||= LinkedData::Client::Models::Ontology.all(include: 'acronym,name') @onts_for_select = [] @onts_acronym_map = {} @onts_uri2acronym_map = {} ontologies.each do |ont| - # TODO: ontologies parameter may be a list of ontology models (not ontology submission models): - # ont.acronym instead of ont.ontology.acronym - # ont.name instead of ont.ontology.name - # ont.id instead of ont.ontology.id - # TODO: annotator passes in 'custom_ontologies' to the ontologies parameter. - next if ( ont.acronym.nil? or ont.acronym.empty? ) + next if ont.acronym.blank? + acronym = ont.acronym name = ont.name - #id = ont.id # ontology URI - abbreviation = acronym.empty? ? "" : "(#{acronym})" + abbreviation = acronym.empty? ? '' : "(#{acronym})" ont_label = "#{name.strip} #{abbreviation}" - #@onts_for_select << [ont_label, id] # using the URI crashes the UI checkbox selection behavior. @onts_for_select << [ont_label, acronym] @onts_acronym_map[ont_label] = acronym - @onts_uri2acronym_map[ont.id] = acronym # required in ontologies_to_acronyms + @onts_uri2acronym_map[ont.id] = acronym end - @onts_for_select.sort! { |a,b| a[0].downcase <=> b[0].downcase } + @onts_for_select.sort! { |a, b| a[0].downcase <=> b[0].downcase } @onts_for_js = @onts_acronym_map.to_json end def categories_for_select - # This method is called in the search index page. get_ontologies_data get_categories_data - return @categories_for_select + @categories_for_select end def get_categories_data @categories_for_select = [] @categories_map = {} - categories = LinkedData::Client::Models::Category.all(include: "name,ontologies") + categories = LinkedData::Client::Models::Category.all(include: 'name,ontologies') categories.each do |c| - @categories_for_select << [ c.name, c.id ] - @categories_map[c.id] = ontologies_to_acronyms(c.ontologies) # c.ontologies is a list of URIs + @categories_for_select << [c.name, c.id] + @categories_map[c.id] = ontologies_to_acronyms(c.ontologies) end - @categories_for_select.sort! { |a,b| a[0].downcase <=> b[0].downcase } + @categories_for_select.sort! { |a, b| a[0].downcase <=> b[0].downcase } @categories_for_js = @categories_map.to_json end def get_groups_data @groups_map = {} @groups_for_select = [] - groups = LinkedData::Client::Models::Group.all(include: "acronym,name,ontologies") + groups = LinkedData::Client::Models::Group.all(include: 'acronym,name,ontologies') groups.each do |g| - next if ( g.acronym.nil? or g.acronym.empty? ) - @groups_for_select << [ g.name + " (#{g.acronym})", g.acronym ] - @groups_map[g.acronym] = ontologies_to_acronyms(g.ontologies) # g.ontologies is a list of URIs + next if g.acronym.blank? + + @groups_for_select << [g.name + " (#{g.acronym})", g.acronym] + @groups_map[g.acronym] = ontologies_to_acronyms(g.ontologies) end - @groups_for_select.sort! { |a,b| a[0].downcase <=> b[0].downcase } + @groups_for_select.sort! { |a, b| a[0].downcase <=> b[0].downcase } @groups_for_js = @groups_map.to_json end def ontologies_to_acronyms(ontologyIDs) acronyms = [] ontologyIDs.each do |id| - acronyms << @onts_uri2acronym_map[id] # hash generated in get_ontologies_data + acronyms << @onts_uri2acronym_map[id] end - return acronyms.compact # remove nil values from any failures to convert ontology URI to acronym + acronyms.compact end def at_slice? @@ -229,7 +176,7 @@ def at_slice? end def link_last_part(url) - return "" if url.nil? + return '' if url.nil? if url.include?('#') url.split('#').last @@ -238,24 +185,13 @@ def link_last_part(url) end end - 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 subscribe_ontology_button(ontology_id, user = nil) user = session[:user] if user.nil? if user.nil? - # subscribe button must redirect to login return sanitize("") end - # Init subscribe button parameters. - sub_text = "Subscribe" + + sub_text = 'Subscribe' params = "data-bp_ontology_id='#{ontology_id}' data-bp_is_subbed='false' data-bp_user_id='#{user.id}'" begin # Try to create an intelligent subscribe button. @@ -264,17 +200,15 @@ def subscribe_ontology_button(ontology_id, user = nil) else 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" + subscribed = subscribed_to_ontology?(ont.acronym, user) + sub_text = subscribed ? 'Unsubscribe' : 'Subscribe' 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 - # TODO: modify/copy CSS for notes_sub_error => subscribe_error - # TODO: modify/copy CSS for subscribe_to_notes => subscribe_to_ontology spinner = '' error = "" - return " #{spinner} #{error}" + " #{spinner} #{error}" end def subscribed_to_ontology?(ontology_acronym, user) @@ -297,10 +231,11 @@ def ontolobridge_instructions_template(ontology) # http://stackoverflow.com/questions/1293573/rails-smart-text-truncation def smart_truncate(s, opts = {}) - opts = {:words => 20}.merge(opts) + opts = { words: 20 }.merge(opts) if opts[:sentences] - return s.split(/\.(\s|$)+/)[0, opts[:sentences]].map{|s| s.strip}.join('. ') + '. ...' + return s.split(/\.(\s|$)+/)[0, opts[:sentences]].map { |s| s.strip }.join('. ') + '. ...' end + a = s.split(/\s/) # or /[ ]+/ to only split on spaces n = opts[:words] a[0...n].join(' ') + (a.size > n ? '...' : '') @@ -311,11 +246,9 @@ def smart_truncate(s, opts = {}) # => '06/27/2010' def xmldatetime_to_date(xml_date_time_str) require 'date' - d = DateTime.xmlschema( xml_date_time_str ).to_date + d = DateTime.xmlschema(xml_date_time_str).to_date # Return conventional US date format: - return sprintf("%02d/%02d/%4d", d.month, d.day, d.year) - # Or return "yyyy/mm/dd" format with: - #return DateTime.xmlschema( xml_date_time_str ).to_date.to_s + sprintf("%02d/%02d/%4d", d.month, d.day, d.year) end def flash_class(level) @@ -329,41 +262,37 @@ def flash_class(level) bootstrap_alert_class[level] end - ###BEGIN ruby equivalent of JS code in bp_ajax_controller. - ###Note: this code is used in concepts/_details partial. + # NOTE: The following 4 methods (bp_ont_link, bp_class_link, get_link_for_cls_ajax, get_link_for_ont_ajax) are + # the Ruby equivalent of JS code in bp_ajax_controller.js and are used in the concepts/_details partial. def bp_ont_link(ont_acronym) - return "/ontologies/#{ont_acronym}" + "/ontologies/#{ont_acronym}" end def bp_class_link(cls_id, ont_acronym) ontology_path(id: ont_acronym, p: 'classes', conceptid: cls_id) end - def get_link_for_cls_ajax(cls_id, ont_acronym, target=nil) - # Note: bp_ajax_controller.ajax_process_cls will try to resolve class labels. + def get_link_for_cls_ajax(cls_id, ont_acronym, target = nil) + # NOTE: bp_ajax_controller.ajax_process_cls will try to resolve class labels. # Uses 'http' as a more generic attempt to resolve class labels than .include? ont_acronym; the # bp_ajax_controller.ajax_process_cls will try to resolve class labels and # otherwise remove the UNIQUE_SPLIT_STR and the ont_acronym. - if target.nil? - target = "" - else - target = " target='#{target}' " - end + target = target.nil? ? '' : " target='#{target}' " + if cls_id.start_with?('http://', 'https://') href_cls = " href='#{bp_class_link(cls_id, ont_acronym)}' " data_cls = " data-cls='#{cls_id}' " data_ont = " data-ont='#{ont_acronym}' " - return "#{cls_id}" + "#{cls_id}" else - return auto_link(cls_id, :all, :target => '_blank') + auto_link(cls_id, :all, target: '_blank') end end + def get_link_for_ont_ajax(ont_acronym) - # ajax call will replace the acronym with an ontology name (triggered by class='ont4ajax') + # Ajax call will replace the acronym with an ontology name (triggered by class='ont4ajax') href_ont = " href='#{bp_ont_link(ont_acronym)}' " data_ont = " data-ont='#{ont_acronym}' " - return "#{ont_acronym}" + "#{ont_acronym}" end - ###END ruby equivalent of JS code in bp_ajax_controller. - end From ea781516563d7398ed0109aaa8f45ddcf18cf5c2 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 18 Sep 2024 15:57:46 -0700 Subject: [PATCH 54/61] fixed #328 - jump to search has not results for ontology views --- Gemfile.lock | 16 ++++++++-------- app/assets/javascripts/bp_visualize.js.erb | 17 +++++++++++++---- app/views/layouts/_ontology_viewer.html.haml | 1 + 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0ba617a4f..4103dea80 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -151,12 +151,13 @@ GEM eventmachine (1.2.7) excon (0.111.0) execjs (2.9.1) - faraday (2.11.0) + faraday (2.12.0) faraday-net_http (>= 2.0, < 3.4) + json logger - faraday-excon (2.1.0) - excon (>= 0.27.4) - faraday (~> 2.0) + faraday-excon (2.2.0) + excon (>= 0.109.0) + faraday (>= 2.11.0, < 3) faraday-multipart (1.0.4) multipart-post (~> 2) faraday-net_http (3.3.0) @@ -185,7 +186,7 @@ GEM http-accept (1.7.0) http-cookie (1.0.7) domain_name (~> 0.5) - i18n (1.14.5) + i18n (1.14.6) concurrent-ruby (~> 1.0) inline_svg (1.10.0) activesupport (>= 3.0) @@ -403,13 +404,12 @@ GEM time (0.4.0) date timeout (0.4.1) - turbo-rails (2.0.6) + turbo-rails (2.0.9) actionpack (>= 6.0.0) - activejob (>= 6.0.0) railties (>= 6.0.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) uri (0.13.1) websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) diff --git a/app/assets/javascripts/bp_visualize.js.erb b/app/assets/javascripts/bp_visualize.js.erb index 4e5740ded..9fdcebe5b 100644 --- a/app/assets/javascripts/bp_visualize.js.erb +++ b/app/assets/javascripts/bp_visualize.js.erb @@ -88,6 +88,10 @@ jQuery(document).trigger("visualize_tab_change", [{tabType: tabId}]); } + function isNotEmpty(arr) { + return Array.isArray(arr) && arr.length > 0; + } + // Only show BioMixer when tab is clicked jQuery(document).live("visualize_tab_change", function(event, data){ if (data.tabType == "visualization") { @@ -97,11 +101,16 @@ jQuery(document).data().bp.classesTab.search_box_init = function(){ if (jQuery("#search_box").bioportal_autocomplete) { + let extraParams = { + objecttypes: 'class' + }; + + if (isNotEmpty(jQuery(document).data().bp.ont_viewer.submission_lang)) { + extraParams["lang"] = jQuery(document).data().bp.ont_viewer.lang; + } + jQuery("#search_box").bioportal_autocomplete("/search/json_search/"+jQuery(document).data().bp.ontology.acronym, { - extraParams: { - objecttypes: "class", - lang: jQuery(document).data().bp.ont_viewer.lang - }, + extraParams: extraParams, selectFirst: true, lineSeparator: "~!~", matchSubset: 0, diff --git a/app/views/layouts/_ontology_viewer.html.haml b/app/views/layouts/_ontology_viewer.html.haml index 0370abc0a..892df38bb 100644 --- a/app/views/layouts/_ontology_viewer.html.haml +++ b/app/views/layouts/_ontology_viewer.html.haml @@ -37,6 +37,7 @@ jQuery(document).data().bp.ont_viewer.concept_name_title = (jQuery(document).data().bp.ont_viewer.concept_name == "") ? "" : " - " + jQuery(document).data().bp.ont_viewer.concept_name; jQuery(document).data().bp.ont_viewer.lang = "#{request_lang(sub)}"; + jQuery(document).data().bp.ont_viewer.submission_lang = #{submission_languages(sub)}; jQuery(document).data().bp.ont_viewer.lang_sections = #{ontology_data_sections.to_json.html_safe}; -# Modal dialog for creating a new mapping (must reside in a top-level position in the document to display properly). From 0b211341ce8279757112aaf66a91e539eef768c9 Mon Sep 17 00:00:00 2001 From: mdorf Date: Wed, 18 Sep 2024 16:07:08 -0700 Subject: [PATCH 55/61] fixed #328 - jump to search has not results for ontology views --- app/views/layouts/_ontology_viewer.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/_ontology_viewer.html.haml b/app/views/layouts/_ontology_viewer.html.haml index 892df38bb..124ecf014 100644 --- a/app/views/layouts/_ontology_viewer.html.haml +++ b/app/views/layouts/_ontology_viewer.html.haml @@ -37,7 +37,7 @@ jQuery(document).data().bp.ont_viewer.concept_name_title = (jQuery(document).data().bp.ont_viewer.concept_name == "") ? "" : " - " + jQuery(document).data().bp.ont_viewer.concept_name; jQuery(document).data().bp.ont_viewer.lang = "#{request_lang(sub)}"; - jQuery(document).data().bp.ont_viewer.submission_lang = #{submission_languages(sub)}; + jQuery(document).data().bp.ont_viewer.submission_lang = #{submission_languages(sub).to_json.html_safe}; jQuery(document).data().bp.ont_viewer.lang_sections = #{ontology_data_sections.to_json.html_safe}; -# Modal dialog for creating a new mapping (must reside in a top-level position in the document to display properly). From 85906141ef57999826657e046060cba705bc22cd Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 18 Sep 2024 15:31:41 -0700 Subject: [PATCH 56/61] Replace deprecated find method with get --- app/controllers/users_controller.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 531b4fd6f..bdae0972c 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -105,8 +105,7 @@ def destroy end def custom_ontologies - @user = LinkedData::Client::Models::User.find(params[:id]) - @user = LinkedData::Client::Models::User.find_by_username(params[:id]).first if @user.nil? + @user = LinkedData::Client::Models::User.get(params[:id]) custom_ontologies = params[:ontology] ? params[:ontology][:ontologyId] : [] custom_ontologies.reject!(&:blank?) @@ -116,7 +115,7 @@ def custom_ontologies if error_response flash[:notice] = 'Error saving Custom Ontologies, please try again' else - updated_user = LinkedData::Client::Models::User.find(@user.id) + updated_user = LinkedData::Client::Models::User.get(@user.id) session[:user].update_from_params(customOntology: updated_user.customOntology) flash[:notice] = if updated_user.customOntology.empty? 'Custom Ontologies were cleared' From 0cc5ba53889e4a2605b199da6c570f11548c89a4 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 18 Sep 2024 15:54:38 -0700 Subject: [PATCH 57/61] Fix red herring error on custom ontology set save Resolves #330 --- app/controllers/users_controller.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index bdae0972c..48934a96b 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -110,18 +110,18 @@ def custom_ontologies custom_ontologies = params[:ontology] ? params[:ontology][:ontologyId] : [] custom_ontologies.reject!(&:blank?) @user.update_from_params(customOntology: custom_ontologies) - error_response = @user.update + response = @user.update - if error_response - flash[:notice] = 'Error saving Custom Ontologies, please try again' - else - updated_user = LinkedData::Client::Models::User.get(@user.id) + if response.success? + updated_user = LinkedData::Client::Models::User.get(@user.id, include: 'customOntology') session[:user].update_from_params(customOntology: updated_user.customOntology) flash[:notice] = if updated_user.customOntology.empty? 'Custom Ontologies were cleared' else 'Custom Ontologies were saved' end + else + flash[:notice] = 'Error saving Custom Ontologies, please try again' end redirect_to user_path(@user.username) end From 5f9791257016db5640dd280426594edf8d6a199f Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 18 Sep 2024 15:55:59 -0700 Subject: [PATCH 58/61] Use proper error flash styling --- app/controllers/users_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 48934a96b..b30f074ed 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -121,7 +121,7 @@ def custom_ontologies 'Custom Ontologies were saved' end else - flash[:notice] = 'Error saving Custom Ontologies, please try again' + flash[:error] = 'Error saving Custom Ontologies, please try again' end redirect_to user_path(@user.username) end From c875b15396a578027606b6bbd0a5490a66174a70 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 18 Sep 2024 16:00:39 -0700 Subject: [PATCH 59/61] Improve flash notice and error text for custom set --- app/controllers/users_controller.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index b30f074ed..cc65fc240 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -116,12 +116,12 @@ def custom_ontologies updated_user = LinkedData::Client::Models::User.get(@user.id, include: 'customOntology') session[:user].update_from_params(customOntology: updated_user.customOntology) flash[:notice] = if updated_user.customOntology.empty? - 'Custom Ontologies were cleared' + 'Custom ontology set successfully cleared' else - 'Custom Ontologies were saved' + 'Custom ontology set successfully saved' end else - flash[:error] = 'Error saving Custom Ontologies, please try again' + flash[:error] = 'Error saving custom ontology set. Please try again.' end redirect_to user_path(@user.username) end From d2f81cd33c20d670848081d6d9213b3a8a6de905 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 18 Sep 2024 16:12:31 -0700 Subject: [PATCH 60/61] Narrow the include params on each get --- app/views/users/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index f4ea24087..cd0150ea4 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -130,7 +130,7 @@ - if @user_ontologies.present? %ul - @user_ontologies.each do |ont| - - ont = LinkedData::Client::Models::Ontology.get(ont) + - ont = LinkedData::Client::Models::Ontology.get(ont, include: 'name,acronym') %li #{ont.name} (#{ont.acronym}) - else From 8a8b346e0192dd4289e1907f265102255942a122 Mon Sep 17 00:00:00 2001 From: Jennifer Vendetti Date: Wed, 18 Sep 2024 16:27:57 -0700 Subject: [PATCH 61/61] Tweak list style for custom ontology sets --- app/views/users/show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index cd0150ea4..28f42ff7c 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -121,14 +121,14 @@ } %h4{class: 'pb-2 mt-5 mb-4 border-bottom'} Custom ontology set - #custom_ontologies.enable-lists + #custom_ontologies - if at_slice? = custom_ontology_set_slice_text - else = custom_ontology_set_intro_text = button_tag('Select ontologies', type: 'button', id: 'edit_custom_ontologies', class: 'btn btn-primary') - if @user_ontologies.present? - %ul + %ul{class: 'list-unstyled mt-3 mb-5'} - @user_ontologies.each do |ont| - ont = LinkedData::Client::Models::Ontology.get(ont, include: 'name,acronym') %li