diff --git a/app/assets/images/arrow-right.svg b/app/assets/images/arrow-right.svg new file mode 100644 index 000000000..07eb88ef1 --- /dev/null +++ b/app/assets/images/arrow-right.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/assets/images/json.svg b/app/assets/images/json.svg new file mode 100644 index 000000000..e7f7e8898 --- /dev/null +++ b/app/assets/images/json.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/assets/images/white-check.svg b/app/assets/images/white-check.svg new file mode 100644 index 000000000..6b657da4f --- /dev/null +++ b/app/assets/images/white-check.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/assets/stylesheets/application.css.scss.erb b/app/assets/stylesheets/application.css.scss.erb index 1b70724a0..8bd7ba555 100644 --- a/app/assets/stylesheets/application.css.scss.erb +++ b/app/assets/stylesheets/application.css.scss.erb @@ -49,7 +49,7 @@ @import "login"; @import "components/index"; @import "account"; - +@import "upload_ontology"; /* Bootstrap and Font Awesome */ @import "bootstrap"; @import "bootstrap_overrides"; diff --git a/app/assets/stylesheets/components/chips.scss b/app/assets/stylesheets/components/chips.scss index 63811c9d1..f627f9242 100644 --- a/app/assets/stylesheets/components/chips.scss +++ b/app/assets/stylesheets/components/chips.scss @@ -1,4 +1,4 @@ -.chips-container div{ +.chips-container > div{ margin-right: 10px; } @@ -11,6 +11,7 @@ display: none; } + .chips-container div label{ cursor: pointer; } diff --git a/app/assets/stylesheets/components/primary_button.scss b/app/assets/stylesheets/components/primary_button.scss new file mode 100644 index 000000000..5e534fecd --- /dev/null +++ b/app/assets/stylesheets/components/primary_button.scss @@ -0,0 +1,101 @@ +.button-container{ + width: 100%; +} +.primary-button { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + font-size: 16px; + color: white; + height: 60px; + background-color: var(--primary-color); + border: none; + border-radius: 9px; + transition: background-color ease 0.3s; +} +.primary-button:hover { + background-color: var(--hover-color); + cursor: pointer; +} + + +.animation-container{ + width: 100%; + height: 60px; + font-size: 16px; + background-color: var(--hover-color); + border: none; + border-radius: 9px; + justify-content: center; + align-items: center; + display: none; + +} +.lds-ellipsis { + display: inline-block; + position: relative; + margin-top: 50px; + width: 80px; + height: 80px; + transform: scale(0.7); +} + +.lds-ellipsis div { + position: absolute; + width: 13px; + height: 13px; + border-radius: 50%; + background: white; + animation-timing-function: cubic-bezier(0, 1, 1, 0); +} + +.lds-ellipsis div:nth-child(1) { + left: 8px; + animation: lds-ellipsis1 0.6s infinite; +} + +.lds-ellipsis div:nth-child(2) { + left: 8px; + animation: lds-ellipsis2 0.6s infinite; +} + +.lds-ellipsis div:nth-child(3) { + left: 32px; + animation: lds-ellipsis2 0.6s infinite; +} + +.lds-ellipsis div:nth-child(4) { + left: 56px; + animation: lds-ellipsis3 0.6s infinite; +} + +@keyframes lds-ellipsis1 { + 0% { + transform: scale(0); + } + + 100% { + transform: scale(1); + } +} + +@keyframes lds-ellipsis3 { + 0% { + transform: scale(1); + } + + 100% { + transform: scale(0); + } +} + +@keyframes lds-ellipsis2 { + 0% { + transform: translate(0, 0); + } + + 100% { + transform: translate(24px, 0); + } +} diff --git a/app/assets/stylesheets/upload_ontology.scss b/app/assets/stylesheets/upload_ontology.scss new file mode 100644 index 000000000..d5026556c --- /dev/null +++ b/app/assets/stylesheets/upload_ontology.scss @@ -0,0 +1,105 @@ +.upload-ontology-container { + display: flex; + justify-content: center; + padding: 40px 0; +} + +.upload-ontology-card { + width: 589px; + border-radius: 14px; + box-shadow: rgba(0, 0, 0, 0.05) 0px 20px 50px; + padding: 20px 40px; + +} + +.upload-ontology-center { + display: flex; + justify-content: center; + flex-direction: column; +} + +.Upload-ontology-title { + font-size: 18px; + display: flex; + font-weight: bold; + flex-direction: column; + align-items: center; +} + +.Upload-ontology-title hr { + border: 1px solid var(--primary-color); + width: 93px; + margin: 0; +} + +.upload-ontology-progress { + display: flex; + align-items: center; + margin-top: 20px; + margin-bottom: 50px; +} + + +.upload-ontology-chips-container{ + display: flex; + flex-wrap: wrap; +} + + +.hide { + display: none; +} + +.show { + display: block; +} + +.upload-ontology-desc { + font-size: 12px; + color: #777777; + margin-bottom: 23px; +} + +.upload-ontology-desc a { + text-decoration: none; + color: var(--primary-color); +} +.upload-ontology-desc a svg{ + transform: scale(1.2); +} + + +.upload-ontology-contact .add-another-contact div { + font-size: 11px; + color: #DADADA; + margin-left: 10px; + +} + +.upload-ontology-field-container .location-choice{ + display: flex; + align-items: center; + margin-bottom: 3px; +} + +.upload-ontology-field-container .location-choice .title{ + font-size: 13px; + color: black; + margin-left: 13px; + margin-bottom: 0; + cursor: pointer; +} + +.upload-ontology-field-container > div{ + font-size: 12px; + color: #666666; +} + + +.upload-ontology-input-field-container{ + margin-bottom: 10px; +} + +.upload-ontology-input-field-container .switch-filter p{ + font-size: 12px !important; +} diff --git a/app/components/buttons/primary_button_component/primary_button_component.html.haml b/app/components/buttons/primary_button_component/primary_button_component.html.haml new file mode 100644 index 000000000..154eb2abe --- /dev/null +++ b/app/components/buttons/primary_button_component/primary_button_component.html.haml @@ -0,0 +1,23 @@ +- if @type == "submit" + .button-container{onclick: "displayAnimation()", id: "primary-button"} + %input.primary-button{:name => @name, :type => "submit", :value => @value, oncklick: @onclick}/ +- else + .button-container + .primary-button{:name => @name, onclick: "displayAnimation()", id: "primary-button", oncklick: @onclick} + = @value + +.animation-container#loading-animation + .lds-ellipsis + %div + %div + %div + %div + +:javascript + const button = document.getElementById("primary-button") + const loading = document.getElementById("loading-animation") + function displayAnimation(){ + console.log("working") + button.style.display = 'none'; + loading.style.display = 'flex'; + } \ No newline at end of file diff --git a/app/components/chips_component/chips_component.html.haml b/app/components/chips_component/chips_component.html.haml index 3e260e431..0718283b0 100644 --- a/app/components/chips_component/chips_component.html.haml +++ b/app/components/chips_component/chips_component.html.haml @@ -5,4 +5,6 @@ %span %svg.chips-check-icon{:fill => "none", :height => "8", :viewbox => "0 0 10 8", :width => "10", :xmlns => "http://www.w3.org/2000/svg"} %path{:d => "M9.76764 0.232287C9.45824 -0.0775267 8.95582 -0.0773313 8.646 0.232287L3.59787 5.28062L1.35419 3.03696C1.04438 2.72714 0.542174 2.72714 0.23236 3.03696C-0.0774534 3.34677 -0.0774534 3.84897 0.23236 4.15879L3.03684 6.96326C3.19165 7.11807 3.39464 7.19567 3.59765 7.19567C3.80067 7.19567 4.00386 7.11827 4.15867 6.96326L9.76764 1.3541C10.0775 1.0445 10.0775 0.542081 9.76764 0.232287Z"} - = @value + %div + = @label + = count diff --git a/app/components/nested_form_inputs_component/nested_form_inputs_component.html.haml b/app/components/nested_form_inputs_component/nested_form_inputs_component.html.haml index 986284231..73e0eaf1e 100644 --- a/app/components/nested_form_inputs_component/nested_form_inputs_component.html.haml +++ b/app/components/nested_form_inputs_component/nested_form_inputs_component.html.haml @@ -16,6 +16,7 @@ %i.fas.fa-minus.fa-lg %div{'data-nested-form-target': "target"} - %div.float-right - %button.btn.btn-success{data: {action:"nested-form#add"}} - %i.fas.fa-plus.fa-lg \ No newline at end of file + %div.add-another-object{data: {action:"click->nested-form#add"}} + = inline_svg 'icons/plus.svg' + %div + Add another #{@object_name} diff --git a/app/components/select_input_component.rb b/app/components/select_input_component.rb index 872f7f41e..082ca4d73 100644 --- a/app/components/select_input_component.rb +++ b/app/components/select_input_component.rb @@ -10,12 +10,28 @@ def initialize(id:, name:, values:, selected:, multiple: false) @selected = selected @multiple = multiple end + + def call + select_input_tag(@name, @values, @selected, multiple: @multiple, open_to_add_values: @open_to_add_values) + end + + private + + def select_input_tag(id, values, selected, options = {}) + multiple = options[:multiple] || false + open_to_add_values = options[:open_to_add_values] || false + + select_html_options = { + id: "select_#{id}", + autocomplete: "off", + multiple: multiple, + data: { + controller: "select-input", + 'select-input-multiple-value': multiple, + 'select-input-open-add-value': open_to_add_values + } + } - def options_values - if @selected.nil? || @selected.empty? - @selected = 0 - @values.unshift('') - end - options_for_select(@values, @selected) + select_tag(id, options_for_select(values, selected), select_html_options) end end diff --git a/app/controllers/concerns/ontology_updater.rb b/app/controllers/concerns/ontology_updater.rb new file mode 100644 index 000000000..c45020ce9 --- /dev/null +++ b/app/controllers/concerns/ontology_updater.rb @@ -0,0 +1,92 @@ +module OntologyUpdater + extend ActiveSupport::Concern + include SubmissionUpdater + def ontology_from_params + ontology = LinkedData::Client::Models::Ontology.new(values: ontology_params) + ontology.viewOf = nil unless ontology.isView + ontology + end + + def save_ontology + + @ontology = save_new_ontology + + if response_error?(@ontology) + show_new_errors(@ontology) + return + end + + + @submission = save_new_submission(params[:submission], @ontology) + + if response_error?(@submission) + @ontology.delete + show_new_errors(@submission) + else + redirect_to "/ontologies/success/#{@ontology.acronym}" + end + end + + def add_ontology_submission(acronym) + @ontology = update_existent_ontology(acronym) + + if @ontology.nil? || response_error?(@ontology) + show_new_errors(@ontology) + return + end + + @submission = @ontology.explore.latest_submission({ display: 'all' }) + submission_params = submission_params(params[:submission]) + submission_params = submission_params(ActionController::Parameters.new(@submission.to_hash.delete_if { |k, v| v.nil? || v.respond_to?(:empty?) && v.empty? })).merge(submission_params) if @submission + submission_params.delete 'submissionId' + @submission = save_new_submission(ActionController::Parameters.new(submission_params), @ontology) + + if response_error?(@submission) + show_new_errors(@submission) + else + redirect_to "/ontologies/success/#{@ontology.acronym}" + end + end + + def save_new_ontology + ontology = ontology_from_params + ontology.save + end + + def update_existent_ontology(acronym) + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(acronym).first + return nil if @ontology.nil? + + @ontology.update_from_params(ontology_params) + end + + def save_new_submission(submission_hash, ontology) + new_submission_params = submission_hash + new_submission_params[:ontology] = ontology.acronym + save_submission(new_submission_params) + end + + def ontology_params + p = params.require(:ontology).permit(:name, :acronym, { administeredBy: [] }, :viewingRestriction, { acl: [] }, + { hasDomain: [] }, :viewOf,:isView, :subscribe_notifications, { group: [] }) + + p[:administeredBy].reject!(&:blank?) if p[:administeredBy] + # p[:acl].reject!(&:blank?) + p[:hasDomain].reject!(&:blank?) if p[:hasDomain] + p[:group].reject!(&:blank?) if p[:group] + p.to_h + end + + def show_new_errors(object) + # TODO optimize + @ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym', include_views: true, display_links: false, display_context: false) + @categories = LinkedData::Client::Models::Category.all + @groups = LinkedData::Client::Models::Group.all(display_links: false, display_context: false) + @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 } + @errors = response_errors(object) + @ontology = ontology_from_params + @submission = submission_from_params(params[:submission]) + render 'ontologies/new' + end +end diff --git a/app/controllers/concerns/submission_updater.rb b/app/controllers/concerns/submission_updater.rb index 3b1218fc3..70ac9e31e 100644 --- a/app/controllers/concerns/submission_updater.rb +++ b/app/controllers/concerns/submission_updater.rb @@ -1,11 +1,15 @@ module SubmissionUpdater extend ActiveSupport::Concern - def save_submission(new_submission_hash) + def submission_from_params(new_submission_hash) convert_values_to_types(new_submission_hash) + LinkedData::Client::Models::OntologySubmission.new(values: submission_params(new_submission_hash)) + end + def save_submission(new_submission_hash) + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(new_submission_hash[:ontology]).first - @submission = LinkedData::Client::Models::OntologySubmission.new(values: submission_params(new_submission_hash)) + @submission = submission_from_params(new_submission_hash) update_ontology_summary_only @submission.save(cache_refresh_all: false) @@ -33,12 +37,12 @@ def update_ontology_summary_only def convert_values_to_types(new_submission_hash) unless new_submission_hash[:contact].nil? - new_submission_hash[:contact] = new_submission_hash[:contact].values + new_submission_hash[:contact] = new_submission_hash[:contact].values unless new_submission_hash[:contact].is_a?(Array) new_submission_hash[:contact].delete_if { |c| c[:name].empty? || c[:email].empty? } end # Convert metadata that needs to be integer to int - @metadata.map do |hash| + submission_metadata.map do |hash| if hash["enforce"].include?("integer") if !new_submission_hash[hash["attribute"]].nil? && !new_submission_hash[hash["attribute"]].eql?("") new_submission_hash[hash["attribute"].to_s.to_sym] = Integer(new_submission_hash[hash["attribute"].to_s.to_sym]) @@ -80,7 +84,7 @@ def submission_params(params) :publication ] - @metadata.each do |m| + submission_metadata.each do |m| m_attr = m["attribute"].to_sym diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index 07fdfe451..c4ec5bb7a 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -7,6 +7,7 @@ class OntologiesController < ApplicationController include SchemesHelper include CollectionsHelper include MappingStatistics + include OntologyUpdater require 'multi_json' require 'cgi' @@ -190,26 +191,9 @@ def properties end def create - if params[:commit].eql? 'Cancel' - redirect_to ontologies_path and return - end - @ontology = LinkedData::Client::Models::Ontology.new(values: ontology_params) - @ontology_saved = @ontology.save - if response_error?(@ontology_saved) - @categories = LinkedData::Client::Models::Category.all - @groups = LinkedData::Client::Models::Group.all(display_links: false, display_context: false) - @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 } - @errors = response_errors(@ontology_saved) - render 'new' - else - if @ontology_saved.summaryOnly - redirect_to "/ontologies/success/#{@ontology.acronym}" - else - redirect_to new_ontology_submission_path(@ontology.acronym) - end - end + # redirect_to ontologies_path and return if params[:commit].eql? 'Cancel' + save_ontology end def edit @@ -234,8 +218,8 @@ def mappings def new @ontology = LinkedData::Client::Models::Ontology.new - @ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym', include_views: true, -display_links: false, display_context: false) + @submission = LinkedData::Client::Models::OntologySubmission.new + @ontologies = LinkedData::Client::Models::Ontology.all(include: 'acronym', include_views: true, display_links: false, display_context: false) @categories = LinkedData::Client::Models::Category.all @groups = LinkedData::Client::Models::Group.all @user_select_list = LinkedData::Client::Models::User.all.map {|u| [u.username, u.id]} @@ -454,29 +438,23 @@ def widgets end end + def show_licenses - private + @metadata = submission_metadata + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:id]).first + @licenses= ["hasLicense","morePermissions","copyrightHolder"] + @submission_latest = @ontology.explore.latest_submission(include: @licenses.join(",")) + render partial: 'ontologies/sections/licenses' + end + def ajax_ontologies - def ontology_params - p = params.require(:ontology).permit(:name, :acronym, { administeredBy:[] }, :viewingRestriction, { acl:[] }, - { hasDomain:[] }, :isView, :viewOf, :subscribe_notifications, {group:[]}) - p[:administeredBy].reject!(&:blank?) - p[:acl].reject!(&:blank?) - p[:hasDomain].reject!(&:blank?) - p[:group].reject!(&:blank?) - p.to_h + render json: LinkedData::Client::Models::Ontology.all(include_views: true, + display: 'acronym,name', display_links: false, display_context: false) end - def determine_layout - case action_name - when 'index' - 'angular' - else - super - end - end + private def get_views(ontology) views = ontology.explore.views || [] views.select!{ |view| view.access?(session[:user]) } diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 639eee192..5acd60198 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -1,5 +1,5 @@ class SubmissionsController < ApplicationController - include SubmissionsHelper, SubmissionUpdater + include SubmissionsHelper, SubmissionUpdater, OntologyUpdater layout :determine_layout before_action :authorize_and_redirect, :only => [:edit, :update, :create, :new] before_action :submission_metadata, only: [:create, :edit, :new, :update, :index] @@ -28,27 +28,17 @@ def new @submission = @ontology.explore.latest_submission @submission ||= LinkedData::Client::Models::OntologySubmission.new @submission.id = nil + @categories = LinkedData::Client::Models::Category.all + @groups = LinkedData::Client::Models::Group.all + @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} + @is_update_ontology = true end # Called when form to "Add submission" is submitted def create - # Make the contacts an array - _, submission_params = params[:submission].each.first - @required_only = !params['required-only'].nil? - @filters_disabled = true - @submission_saved = save_submission(submission_params) - if response_error?(@submission_saved) - @errors = response_errors(@submission_saved) # see application_controller::response_errors - if @errors && @errors[:uploadFilePath] - @errors = ["Please specify the location of your ontology"] - elsif @errors && @errors[:contact] - @errors = ["Please enter a contact"] - end - - render "new" - else - redirect_to "/ontologies/success/#{@ontology.acronym}" - end + @is_update_ontology = true + add_ontology_submission(params[:ontology][:acronym] || params[:id]) end # Called when form to "Edit submission" is submitted diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 98bc05f94..90c01bb7f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -335,14 +335,14 @@ def get_groups_data def metadata_for_select get_metadata return @metadata_for_select - end + end def get_metadata @metadata_for_select = [] submission_metadata.each do |data| @metadata_for_select << data["attribute"] end - end + end def ontologies_to_acronyms(ontologyIDs) @@ -394,7 +394,7 @@ def add_proposal_button(parent_id, parent_type) class: "add_proposal btn btn-primary", data: { show_modal_title_value: "Add a new proposal"} end end - + def subscribe_button(ontology_id) if session[:user].nil? return link_to 'Subscribe to notes emails', "/login?redirect=#{request.url}", {style:'font-size: .9em;', class:'link_button'} @@ -607,4 +607,49 @@ def skos? submission = @submission || @submission_latest submission&.hasOntologyLanguage === 'SKOS' end + + def current_page?(path) + request.path.eql?(path) + end + + def request_lang + lang = params[:language] || params[:lang] + lang = 'EN' unless lang + lang.upcase + end + + def bp_config_json + # For config settings, see + # config/bioportal_config.rb + # config/initializers/ontologies_api_client.rb + config = { + org: $ORG, + org_url: $ORG_URL, + site: $SITE, + org_site: $ORG_SITE, + ui_url: $UI_URL, + apikey: LinkedData::Client.settings.apikey, + userapikey: get_apikey, + rest_url: LinkedData::Client.settings.rest_url, + proxy_url: $PROXY_URL, + biomixer_url: $BIOMIXER_URL, + annotator_url: $ANNOTATOR_URL, + ncbo_annotator_url: $NCBO_ANNOTATOR_URL, + ncbo_apikey: $NCBO_API_KEY, + interportal_hash: $INTERPORTAL_HASH, + resolve_namespace: RESOLVE_NAMESPACE + } + config[:ncbo_slice] = @subdomain_filter[:acronym] if (@subdomain_filter[:active] && !@subdomain_filter[:acronym].empty?) + config.to_json + end + def portal_name + $SITE + end + def navitems + items = [["/ontologies", "Browse"],["/mappings", "Mappings"],["/recommender", "Recommender"],["/annotator", "Annotator"], ["/landscape", "Landscape"]] + end + + def attribute_enforced_values(attr) + submission_metadata.select {|x| x['@id'][attr]}.first['enforcedValues'] + end end diff --git a/app/helpers/inputs_helper.rb b/app/helpers/inputs_helper.rb new file mode 100644 index 000000000..ff0f14294 --- /dev/null +++ b/app/helpers/inputs_helper.rb @@ -0,0 +1,45 @@ +module InputsHelper + + def text_input(label: nil, name: , value:, disabled: false) + render Input::TextInputComponent.new(label: input_label(label, name) , name: name, value: value, error_message: input_error_message(name), disabled: disabled) + end + + def select_input(label: nil, name: , values:, selected: nil, multiple: false) + render Input::SelectComponent.new(label: input_label(label, name), name: name, value: values, selected: selected, multiple: multiple) + end + + def check_input(id:, name: , label: '', value:, checked: false) + render ChipsComponent.new(name: name, id: id, label: label, value: value, checked: checked) + end + + def switch_input(id: , name:, label: ,checked: false) + render SwitchInputComponent.new(id: id, name: name, label: label, checked: checked) + end + + def url_input(label: nil, name: , value:) + render Input::UrlComponent.new(label: input_label(label, name), name: name, value: value, error_message: input_error_message(name) ) + end + + def text_area_input(label: nil, name: , value:) + render Input::TextAreaComponent.new(label: input_label(label, name), name: name, value: value, error_message: input_error_message(name)) + end + + def date_input(label: nil, name:, value:) + render Input::DateComponent.new(label: input_label(label, name) ,name: name, value: value || Date.today, error_message: input_error_message(name)) + end + + private + + def method_name(name) + match = /.*\[(.*?)\]/.match(name) + match.nil? ? name : match[1] + end + + def input_label(label, name) + label || method_name(name).humanize + end + + def input_error_message(name) + attribute_error(method_name(name)) + end +end \ No newline at end of file diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index 6e43ba819..6b9b62f3c 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -416,5 +416,85 @@ def sections_to_show end sections end + + + def language_selector_tag(name) + languages = languages_options + + if languages.empty? + content_tag(:div ,data: {'ontology-viewer-tabs-target': 'languageSelector'}, style: "visibility: #{ontology_data_section? ? 'visible' : 'hidden'} ; margin-bottom: -1px;") do + render EditSubmissionAttributeButtonComponent.new(acronym: @ontology.acronym, submission_id: @submission_latest.submissionId, attribute: :naturalLanguage) do + concat "Enable multilingual display " + concat content_tag(:i , "", class: "fas fa-lg fa-question-circle") + end + end + else + select_tag name, languages_options, class: '', disabled: !ontology_data_section?, style: "visibility: #{ontology_data_section? ? 'visible' : 'hidden'}; border: none; outline: none;", data: {'ontology-viewer-tabs-target': 'languageSelector'} + end + 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 languages_options(submission = @submission || @submission_latest) + current_lang = request_lang + 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, lang, { selected: lang.eql?(current_lang) }] + end + options_for_select(submission_lang) + end + + def dispaly_complex_text(definitions) + html = "" + definitions.each do |definition| + if definition.is_a?(String) + html += '

' + definition + '

' + elsif definition.respond_to?(:uri) && definition.uri + html += render LinkFieldComponent.new(value: definition.uri) + end + end + return html.html_safe + end + + + def count_subscriptions(ontology_id) + users = LinkedData::Client::Models::User.all(include: 'subscription', display_context: false, display_links: false ) + users.select{ |u| u.subscription.find{ |s| s.ontology.eql?(ontology_id)} }.count + end + + def ontology_edit_button + return unless @ontology.admin?(session[:user]) + render RoundedButtonComponent.new(link: edit_ontology_path(@ontology.acronym), icon: 'edit.svg', size: 'medium') + end + + def submission_json_button + render RoundedButtonComponent.new(link: "#{(@submission_latest || @ontology).id}?display=all", target: '_blank', size: 'medium') + end + + def attribute_error(attr) + return '' unless @errors && @errors[attr.to_sym] + errors = @errors[attr.to_sym] + + errors.values.join(', ') + end + + def error_message + if !@errors[:error].nil? && @errors[:error].is_a?(String) + @errors[:error] + else + "Errors in fields #{@errors.keys.join(', ')}" + end + + end + private + + def submission_languages(submission = @submission) + submission&.naturalLanguage.map { |natural_language| natural_language["iso639"] && natural_language.split('/').last }.compact + end end diff --git a/app/helpers/submissions_helper.rb b/app/helpers/submissions_helper.rb index a9dc3d8b9..b57f0ceab 100644 --- a/app/helpers/submissions_helper.rb +++ b/app/helpers/submissions_helper.rb @@ -333,12 +333,7 @@ def generate_integer_input(attr) def generate_date_input(attr) field_id = [:submission, attr["attribute"].to_s, @ontology.acronym].join('_') date_value = @submission.send(attr["attribute"]).presence - data_flat_picker = { controller: "flatpickr", flatpickr_date_format: "Y-m-d", flatpickr_alt_input: "true", flatpickr_alt_format: "F j, Y" } - content_tag(:div, class: 'input-group') do - [ - date_field(object_name, attr["attribute"].to_s.to_sym, value: date_value, id: field_id, data: data_flat_picker, class: "not-disabled") - ].join.html_safe - end + render Input::DateComponent.new(label: (attr["label"] || attr["attribute"]).to_s ,name: object_name, value: date_value || Date.today, id: field_id) end def generate_textarea_input(attr) @@ -350,27 +345,35 @@ def generate_select_input(attr, name, select_values, metadata_values, multiple: render SelectInputComponent.new(id: id, name: name, values: select_values , selected: metadata_values , multiple: multiple) end - def generate_list_field_input(attr, name, values, field_func) + def generate_list_field_input(attr, name, label, values, &block) render NestedFormInputsComponent.new do |c| - c.template do - method(field_func).call("#{name}[NEW_RECORD]", '', :id => attr["attribute"].to_s + "_" + @ontology.acronym, class: "metadataInput form-control my-1") + c.header do + content_tag(:div, label) + end + c.template do + block.call('', "#{name}[NEW_RECORD]", attr["attribute"].to_s + "_" + @ontology.acronym) end values.each_with_index do |metadata_val, i| c.row do - method(field_func).call("#{name}[#{i}]", metadata_val, :id => "submission_#{attr["attribute"].to_s}" + "_" + @ontology.acronym, class: "metadataInput my-1 form-control") + block.call(metadata_val, "#{name}[#{i}]" ,"submission_#{attr["attribute"].to_s}" + "_" + @ontology.acronym) end end end end - def generate_url_input(attr, name, values) - generate_list_field_input(attr, name, values, :url_field_tag) + def generate_url_input(attr, name, values, label:"") + generate_list_field_input(attr, name, label, values) do |value, row_name, id| + render Input::UrlComponent.new(label: "", name: row_name, value: value, id: id) + end end - def generate_list_text_input(attr, name, values) - generate_list_field_input(attr, name, values, :text_field_tag) + def generate_list_text_input(attr, name, values, label:"") + generate_list_field_input(attr, name, label, values) do |value, row_name, id| + render Input::TextInputComponent.new(label: "", name: row_name, value: value, id: id) + end end + def generate_boolean_input(attr, name) value = attribute_values(attr) value = value.to_s unless value.nil? @@ -399,7 +402,7 @@ def generate_attribute_input(attr_label, options = {}) input_html = ''.html_safe # Get the attribute hash corresponding to the given attribute - attr = @metadata.select { |attr_hash| attr_hash["attribute"].to_s.eql?(attr_label) }.first + attr = submission_metadata.select { |attr_hash| attr_hash["attribute"].to_s.eql?(attr_label) }.first object_name, name = attribute_input_name(attr["attribute"]) diff --git a/app/javascript/controllers/tom_select_controller.js b/app/javascript/controllers/tom_select_controller.js new file mode 100644 index 000000000..4f157b2aa --- /dev/null +++ b/app/javascript/controllers/tom_select_controller.js @@ -0,0 +1,16 @@ +import { Controller } from "@hotwired/stimulus" +import TomSelect from "tom-select" +// Connects to data-controller="tom-select" +export default class extends Controller { + + + + connect() { + + new TomSelect(this.element, { + + + //plugins: ['remove_button'] + }); + } +} diff --git a/app/views/layouts/_topnav.html.haml b/app/views/layouts/_topnav.html.haml index 8c3e73e76..729e105c9 100644 --- a/app/views/layouts/_topnav.html.haml +++ b/app/views/layouts/_topnav.html.haml @@ -54,3 +54,25 @@ = link_to("Help", "#{$WIKI_HELP_PAGE}", target: "_blank", class: "dropdown-item") = link_to("Release Notes", "#{$RELEASE_NOTES}", target: "_blank", class: "dropdown-item") = link_to("Publications", $PUBLICATIONS_URL, target: "_blank", class: "dropdown-item") + + = select_tag('language', options_for_select([['EN','en'],['FR','fr']]), id: 'language-select', class: 'nav-language', + data: { controller: "platform-language", action: "change->platform-language#handleLangChanged" }) + + = render DropdownButtonComponent.new do |d| + - d.header do + = link_to("#", id: "supportMenuDropdownLink", class: "nav-link top-nav-nav-link supportMenuDropdownLink", role: "button") do + Support + - d.with_section(divide: false) do |s| + - s.item do + = link_to(t(:submit_feedback), feedback_path(location: encode_param(request.url)), id: "submitFeedbackMenuItem", class: "pop_window") + - d.with_section do |s| + - s.header do + Documentation + - s.item do + = link_to(t(:_help), "#{$WIKI_HELP_PAGE}", target: "_blank") + - s.item do + = link_to(t(:_release_notes), "#{$RELEASE_NOTES}", target: "_blank") + - s.item do + = link_to(t(:_publications), $PUBLICATIONS_URL, target: "_blank") + + diff --git a/app/views/layouts/component_preview.html.erb b/app/views/layouts/component_preview.html.erb new file mode 100644 index 000000000..429f63010 --- /dev/null +++ b/app/views/layouts/component_preview.html.erb @@ -0,0 +1,60 @@ + + + Component Preview + + <%= stylesheet_link_tag "application" %> + <%= javascript_include_tag "vendor" %> + + + + <%= modal_frame_container %> +
+ <%= yield %> + <%= javascript_include_tag "application" %> +
+ + + + \ No newline at end of file diff --git a/app/views/login/index.html.haml b/app/views/login/index.html.haml index 16d6b495a..35eb0b227 100644 --- a/app/views/login/index.html.haml +++ b/app/views/login/index.html.haml @@ -14,7 +14,8 @@ = password_field 'user','password', :autocomplete => "off", class: "login-input password-input", placeholder: "Enter your password" %a.login-forgot-password{:href => "/lost_pass"} %p Forgot password? - %input.login-button{"data-disable-with" => "Login", :name => "commit", :type => "submit", :value => "Login"}/ + .login-button-container + = render Buttons::RegularButtonComponent.new(id: 'login-button', value: "Login", type:'submit') %p.dont-have-account Don't have an account? %a.text-decoration-none{:href => new_user_path} Register diff --git a/app/views/ontologies/_form.html.haml b/app/views/ontologies/_form.html.haml index fe945e9f6..012a3056a 100644 --- a/app/views/ontologies/_form.html.haml +++ b/app/views/ontologies/_form.html.haml @@ -1,220 +1,130 @@ -- button_text ||= "Create ontology" -- title_text ||= "Submit New Ontology" +.upload-ontology-container + %div{style: 'width: 589px'} + - unless @errors.nil? + = render Display::AlertComponent.new(message: error_message, type: 'danger', closable: false) + .upload-ontology-card + .upload-ontology-center + .Upload-ontology-title + %div + = @is_update_ontology ? "Upload new update" : "Upload ontology" + %hr + .upload-ontology-progress + = render Layout::ProgressPagesComponent.new(pages_title: ['Details', 'General metadata', 'Dates contacts']) do |c| + - c.page do + .upload-ontology-input-field-container + = text_input(name: 'ontology[name]', value: @ontology.name) + .upload-ontology-input-field-container + = text_input(name: 'ontology[acronym]', value: @ontology.acronym, disabled: @is_update_ontology) + = hidden_field_tag 'ontology[acronym]', @ontology.acronym if @is_update_ontology + + .upload-ontology-input-field-container#visibilityContainer + = select_input(label: "Visibility", name: "ontology[viewingRestriction]", values: ["public","private"], selected: @ontology.viewingRestriction ) + .upload-ontology-input-field-container#visibility-group{style: 'display: none'} + = select_input(label: "Add or remove accounts that are allowed to view classes in this ontology using the account name", name: "ontology[acl]", values: @user_select_list, selected: @ontology.acl, multiple: true) + + .upload-ontology-input-field-container + = select_input(label: "Administrator", name: "ontology[administeredBy]", values: @user_select_list, selected: @ontology.administeredBy || session[:user].id, multiple: true) + .upload-ontology-input-field-container + = render Input::InputFieldComponent.new(name: '', label:'Categories') do + %div.upload-ontology-chips-container + - @categories.each do |category| + = check_input(name: "ontology[hasDomain][]", id: category[:acronym] , label: category[:acronym], value: category[:id], checked: @ontology.hasDomain&.any?{|x| x.eql?(category[:id])}) + .upload-ontology-field-container + = render Input::InputFieldComponent.new(name: '', label:'Groups') do + %div.upload-ontology-chips-container + - @groups.each do |group| + = check_input(name: "ontology[group][]", id: group[:acronym] , label: group[:acronym], value: group[:id], checked: @ontology.group&.any?{|x| x.eql?(group[:id])}) + + .upload-ontology-input-field-container.mt-2 + %span.d-flex + = switch_input(id: 'ontology_isView', name: 'ontology[isView]', label: 'Is a view of another ontology?', checked: @ontology.view?) + %div#ontology_viewOf{style: "display: #{ !@ontology.view? ? 'none' : 'block'}"} + = render partial: "shared/ontology_picker_single", locals: {placeholder: "", field_name: "viewOf", selected: @ontology.viewOf} + + - c.page do + .upload-ontology-desc + %div + To understand the ontologies metadata: + %a{:href => "#seethewiki"} + see the Wiki + %svg{:fill => "none", :height => "8", :viewbox => "0 0 8 8", :width => "8", :xmlns => "http://www.w3.org/2000/svg"} + %path{:d => "M5.77776 8H1.33333C0.977156 8 0.642334 7.8613 0.390512 7.60946C0.138689 7.35762 0 7.02278 0 6.66666V2.22222C0 1.86607 0.138704 1.53124 0.390527 1.27942C0.64235 1.0276 0.977172 0.888894 1.33334 0.888894H3.11111C3.35659 0.888894 3.55556 1.08787 3.55556 1.33334C3.55556 1.57881 3.35659 1.77779 3.11111 1.77779H1.33333C1.2146 1.77779 1.10301 1.82402 1.01907 1.90795C0.935144 1.99188 0.888894 2.1035 0.888894 2.22222V6.66666C0.888894 6.78538 0.935129 6.89698 1.01907 6.98094C1.10301 7.06486 1.2146 7.11111 1.33333 7.11111H5.77775C5.89647 7.11111 6.00807 7.06487 6.09202 6.98091C6.17595 6.89698 6.22218 6.78537 6.22218 6.66664V4.88889C6.22218 4.64341 6.42117 4.44445 6.66664 4.44445C6.91212 4.44445 7.11111 4.64343 7.11111 4.88889V6.66666C7.11111 7.02281 6.9724 7.35762 6.72056 7.60947C6.46872 7.8613 6.13389 8 5.77776 8ZM3.11111 5.33332C2.99736 5.33332 2.88362 5.28994 2.79685 5.20315C2.62329 5.02959 2.62329 4.74816 2.79685 4.5746L6.48254 0.888894H4.88889C4.64341 0.888894 4.44445 0.68992 4.44445 0.444447C4.44445 0.198974 4.64341 0 4.88889 0H7.55555C7.61702 0 7.67556 0.0124825 7.72882 0.0350409C7.77851 0.0560624 7.82518 0.0865233 7.86602 0.126439L7.86605 0.12647C7.86634 0.126765 7.86664 0.127045 7.86692 0.12734C7.86699 0.127417 7.8671 0.127511 7.86718 0.127588C7.8674 0.127805 7.86765 0.128038 7.86786 0.128271C7.86802 0.128427 7.86816 0.128566 7.86831 0.128721C7.86848 0.128892 7.86867 0.129079 7.86881 0.129218C7.86912 0.129529 7.86946 0.129855 7.86977 0.130181C7.87008 0.130491 7.87042 0.130833 7.87074 0.131143C7.87091 0.131299 7.87109 0.131501 7.87122 0.13164C7.87139 0.131796 7.87151 0.131935 7.87167 0.132091C7.87191 0.132323 7.87213 0.132541 7.87235 0.132789C7.87243 0.132851 7.87254 0.13296 7.8726 0.133038C7.87289 0.133333 7.87319 0.133628 7.87347 0.133923L7.8735 0.133954C7.9134 0.174817 7.94388 0.221486 7.96488 0.271167C7.98744 0.32442 7.99994 0.382951 7.99994 0.444431V3.1111C7.99994 3.35657 7.80095 3.55555 7.55548 3.55555C7.31 3.55555 7.11104 3.35657 7.11104 3.1111V1.51744L3.4253 5.20317C3.33859 5.28995 3.22485 5.33332 3.11111 5.33332Z", :fill => "#31B404"} + .upload-ontology-input-field-container + = url_input(label: 'URL', name: "submission[URI]", value: @submission.URI) + .upload-ontology-input-field-container + = text_area_input(name: "submission[description]", value: @submission.description) + + - if @is_update_ontology + .upload-ontology-input-field-container + = generate_list_text_input("notes", "submission[notes]", Array(@submission.notes), label: "Change notes") + .upload-ontology-field-container + = select_input(label: "Format", name: "submission[hasOntologyLanguage]", values: ["OBO", "OWL", "SKOS", "UMLS"], selected: @submission.hasOntologyLanguage) + .upload-ontology-desc.hide + %div + SKOS vocabularies submitted to BioPortal must contain a minimum of one concept scheme and top concept assertion. Please + refer to the NCBO wiki for a more + %a{:href => "#seethewiki"} + detailed explanation + %svg{:fill => "none", :height => "8", :viewbox => "0 0 8 8", :width => "8", :xmlns => "http://www.w3.org/2000/svg"} + %path{:d => "M5.77776 8H1.33333C0.977156 8 0.642334 7.8613 0.390512 7.60946C0.138689 7.35762 0 7.02278 0 6.66666V2.22222C0 1.86607 0.138704 1.53124 0.390527 1.27942C0.64235 1.0276 0.977172 0.888894 1.33334 0.888894H3.11111C3.35659 0.888894 3.55556 1.08787 3.55556 1.33334C3.55556 1.57881 3.35659 1.77779 3.11111 1.77779H1.33333C1.2146 1.77779 1.10301 1.82402 1.01907 1.90795C0.935144 1.99188 0.888894 2.1035 0.888894 2.22222V6.66666C0.888894 6.78538 0.935129 6.89698 1.01907 6.98094C1.10301 7.06486 1.2146 7.11111 1.33333 7.11111H5.77775C5.89647 7.11111 6.00807 7.06487 6.09202 6.98091C6.17595 6.89698 6.22218 6.78537 6.22218 6.66664V4.88889C6.22218 4.64341 6.42117 4.44445 6.66664 4.44445C6.91212 4.44445 7.11111 4.64343 7.11111 4.88889V6.66666C7.11111 7.02281 6.9724 7.35762 6.72056 7.60947C6.46872 7.8613 6.13389 8 5.77776 8ZM3.11111 5.33332C2.99736 5.33332 2.88362 5.28994 2.79685 5.20315C2.62329 5.02959 2.62329 4.74816 2.79685 4.5746L6.48254 0.888894H4.88889C4.64341 0.888894 4.44445 0.68992 4.44445 0.444447C4.44445 0.198974 4.64341 0 4.88889 0H7.55555C7.61702 0 7.67556 0.0124825 7.72882 0.0350409C7.77851 0.0560624 7.82518 0.0865233 7.86602 0.126439L7.86605 0.12647C7.86634 0.126765 7.86664 0.127045 7.86692 0.12734C7.86699 0.127417 7.8671 0.127511 7.86718 0.127588C7.8674 0.127805 7.86765 0.128038 7.86786 0.128271C7.86802 0.128427 7.86816 0.128566 7.86831 0.128721C7.86848 0.128892 7.86867 0.129079 7.86881 0.129218C7.86912 0.129529 7.86946 0.129855 7.86977 0.130181C7.87008 0.130491 7.87042 0.130833 7.87074 0.131143C7.87091 0.131299 7.87109 0.131501 7.87122 0.13164C7.87139 0.131796 7.87151 0.131935 7.87167 0.132091C7.87191 0.132323 7.87213 0.132541 7.87235 0.132789C7.87243 0.132851 7.87254 0.13296 7.8726 0.133038C7.87289 0.133333 7.87319 0.133628 7.87347 0.133923L7.8735 0.133954C7.9134 0.174817 7.94388 0.221486 7.96488 0.271167C7.98744 0.32442 7.99994 0.382951 7.99994 0.444431V3.1111C7.99994 3.35657 7.80095 3.55555 7.55548 3.55555C7.31 3.55555 7.11104 3.35657 7.11104 3.1111V1.51744L3.4253 5.20317C3.33859 5.28995 3.22485 5.33332 3.11111 5.33332Z", :fill => "#31B404"} + with examples. + .upload-ontology-field-container.mt-3 + = select_input(name: "submission[status]", values: ["alpha", "beta", "production", "retired"], selected: @submission.status) + .upload-ontology-field-container + .mt-3.mb-2 Location + = render partial: 'ontologies/submission_location_form' + + - c.page do + .upload-ontology-input-field-container + - if @is_update_ontology + = date_input(label: 'Modification date (dd/mm/yy)', name: 'submission[modificationDate]', value: @submission.modificationDate) + - else + = date_input(label: 'Date of original creation (dd/mm/yy)', name: 'submission[released]', value: @submission.released) + .upload-ontology-contact + = render Input::InputFieldComponent.new(name:'', error_message: attribute_error(:contact)) do + = render NestedFormInputsComponent.new(object_name: "Contact") do |c| + - c.header do + - content_tag(:div, 'Contact name', class: 'w-50') + content_tag(:div, 'Contact email', class: 'w-50') + - c.template do + = content_tag(:div, class: "d-flex my-1") do + .w-50.mr-2 + = render Input::TextInputComponent.new(label: "", name: "submission[contact][NEW_RECORD][name]") + .w-50 + = render Input::TextInputComponent.new(label: "", name: "submission[contact][NEW_RECORD][email]") + - Array(@submission.contact).each_with_index do |contact, i| + - c.row do + = content_tag(:div, class: "d-flex my-1") do + .w-50.mr-2 + = render Input::TextInputComponent.new(label: "", name: "submission[contact][#{i}][name]", value: contact["name"]) + .w-50 + = render Input::TextInputComponent.new(label: "", name: "submission[contact][#{i}][email]", value: contact["email"]) :javascript - function hideAllRestrictions() { - jQuery(".viewing_restriction_disabled").attr("disabled", true); - jQuery("div.viewing_restriction_types").addClass("hidden"); - } - - function showRestrictionPrivate() { - jQuery("#ontology_acl").removeAttr("disabled"); - jQuery("#viewingRestrictionsPrivate").removeClass("hidden"); - } - - function showRestrictionLicensed() { - jQuery("#ontology_licenseInformation").removeAttr("disabled"); - jQuery("#viewingRestrictionsLicensed").removeClass("hidden"); - } - jQuery(document).data().bp.acronyms = #{raw LinkedData::Client::Models::Ontology.all.map {|o| o.acronym}.to_json}; + function showPrivateAclSelect() { + const visibilityGroupDiv = document.getElementById('visibility-group'); + // Get the selected value from the select element + const selectElement = document.getElementById('select_ontology[viewingRestriction]'); + const selectedValue = selectElement.value; - jQuery(document).ready(function(){ - // Wire up options for restriction how an ontology is viewed - jQuery("#ontology_viewingRestriction").change(function(){ - var select = jQuery(this); - if (select.val() == "private") { - hideAllRestrictions() - showRestrictionPrivate(); - } else if (select.val() == "licensed") { - hideAllRestrictions(); - showRestrictionLicensed(); - } else if (select.val() == "public") { - hideAllRestrictions(); - } - }); - // Make sure you can see the account select if the select list has private selected - if (jQuery("#ontology_viewingRestriction").val() == "private") { - showRestrictionPrivate(); - } else if (jQuery("#ontology_viewingRestriction").val() == "licensed") { - showRestrictionLicensed(); + // Check if the selected value is "private" + if (selectedValue === 'private') { + // If it's "private", show the visibility-group div + visibilityGroupDiv.style.display = 'block'; + } else { + // If it's not "private", hide the visibility-group div + visibilityGroupDiv.style.display = 'none'; } + } + const parentDiv = document.getElementById('visibilityContainer'); + parentDiv.addEventListener('change', () => { showPrivateAclSelect() }); + - jQuery("#ontology_isView").live("click", function(){ - console.log(jQuery("#ontology_isView").is(":checked")) + jQuery("#ontology_isView").live("click", function(){ if (jQuery("#ontology_isView").is(":checked")) { - jQuery("#ontology_viewOf").removeAttr('disabled').trigger("liszt:updated"); + jQuery("#ontology_viewOf").removeAttr('disabled').show(); } else { - jQuery("#ontology_viewOf").attr('disabled', true).trigger("liszt:updated"); + jQuery("#ontology_viewOf").attr('disabled', true).hide(); } - }); - - // Wire up chosen selectors - jQuery("#ontology_administeredBy").chosen(); - jQuery("#ontology_acl").chosen(); - jQuery("#ontology_hasDomain").chosen(); - jQuery("#ontology_group").chosen(); - - // Make acronym upcase as you type - jQuery("#ontology_acronym").on('input', function(e) { - var input = $(this); - var start = input[0].selectionStart; - $(this).val(function (_, val) { - return val.toUpperCase(); - }); - input[0].selectionStart = input[0].selectionEnd = start; - }); - - // Check acronym as you type - jQuery("#ontology_acronym").on('input', function(e) { - var $this = $(this); - var errors = []; - var errorHTML = ""; - - if ($this.val().match("^[^a-z^A-Z]{1}")) { - errors.push("Acronym must start with a letter"); - } - - if ($this.val().match("[^-_0-9a-zA-Z]")) { - errors.push("Acronym must only contain the folowing characters: -, _, letters, and numbers"); - } - - if ($this.val().match(".{17,}")) { - errors.push("Acronym must be sixteen characters or less"); - } - - if (jQuery(document).data().bp.acronyms.indexOf($this.val()) !== -1) { - errors.push("Acronym already in use"); - } - - if (errors.length > 0) { - errorHTML = "
  • " + errors.join("
  • ") + "
  • "; - } - - jQuery("#acronym_errors").html(errorHTML); - }); - - jQuery("#ontologyForm").validate({ - errorClass: "ontologyFormError", - errorElement: "div", - rules: { - "ontology[name]": "required", - "ontology[acronym]": "required", - }, - messages: { - "ontology[name]": "Please enter a name", - "ontology[acronym]": "Please enter an acronym", - }, - }); - }); - -:css - div.ontologyFormError { - color: red; - padding-top: 3px; - } - -- unless @errors.nil? - .enable-lists{:style => "color:red;"} - %strong Errors On Form - %ul - - if @errors[:error].instance_of? OpenStruct - - errors = @errors[:error].to_h - - errors.delete :links - - errors.delete :context - - errors.to_h.each do |errors_field, error| - - next if error.nil? - - %li - - if error.instance_of? OpenStruct - - error_hash = error.to_h - - error_hash.delete :links - - error_hash.delete :context - - error_hash.each do |error_type, e| - = "#{error_type} : #{e}" - - else - = errors_field - - else - -# A generic fallback - = @errors.to_json - -%div.p-5 - %div - %h1 - #{title_text} - - %div.p-5.card - %small.asterik.mb-2.text-right - * fields are required - %div.form-row - %div.form-group.col-md-6 - = f.label :name, "Name" - %span.asterik * - = f.text_field :name, value: @ontology.name, class:"form-control" - %div.form-group.col-md-3 - = f.label :acronym, "Acronym" - %span.asterik * - - acronym_enabled = @ontology.acronym.nil? || ! @errors.nil? - = f.text_field(:acronym, value: @ontology.acronym, :disabled => ! acronym_enabled, data: { acronyms: acronyms(@ontologies) }, class:"form-control") - %ul#acronym_errors.enable-lists{style: "color: red; padding: 3px;"} - %div.form-group.col-md-3 - %div - - viewing_help = "Public ontologies; will be accessible to everyone via UI and API. Download can be desactivated on demand.
    Private ontologies; are only accessible via UI and API to logged users listed explicitly." - = f.label :viewingRestriction do - = render partial: "shared/ui-component/label_with_help", locals:{help_text: viewing_help, id:"viewing_tooltip" ,label: "Viewing Restriction"} - - view_restiction_options = [["Public", "public"], ["Private", "private"]] - - selected = @ontology.private? ? "private" : "" - - selected = @ontology.licensed? ? "licensed" : selected - - display_private = @ontology.private? ? "" : "hidden" - - display_licensed = @ontology.licensed? ? "" : "hidden" - = f.select :viewingRestriction, view_restiction_options, { :selected => selected }, class:"form-control" - - %div.form-row - #viewingRestrictionsPrivate.form-group.col.viewing_restriction_types{class: display_private} - = f.label :acl do - Add or remove accounts that are allowed to view classes in this ontology using the account name - = f.select(:acl, @user_select_list, {include_blank: true, selected: @ontology.acl}, {multiple: true, :"data-placeholder" => "Select users who have access", class:"form-control"}) - %div.form-row - %div#viewingRestrictionsLicensed.form-group.col.viewing_restriction_types{class: display_licensed} - = f.label :licenseInformation do - %b License Text: - The text below explains what licensing information you want to collect before allowing access. We will display this text and record the user's response when the user attempts to access your ontology. - - disabled = @ontology.licensed? ? {} : {:disabled => "true"} - = f.text_area :licenseInformation, { :rows => 5, :class => "viewing_restriction_disabled form-control", :style => "width: 90%;" }.merge(disabled) - - %div.form-row.form-group - %div.col-md-1 - = f.label :administeredBy, "Administrators", class:" col-form-label" - %span.asterik * - %div.col-md-11 - = f.select(:administeredBy, @user_select_list ,{selected: @ontology.administeredBy || session[:user].id}, {multiple: true, :"data-placeholder" => "Select administrators", class:"form-control"}) - %div.form-group.row - %div.col-md-1 - = f.label :hasDomain, "Categories", class:"col-form-label" - %div.col-md-11 - - cat_select = @categories.sort{|a,b| a.name <=> b.name}.map{|c| [c.name, c.id]} - = f.hidden_field(:hasDomain, {value: "", id: "ontology_hasDomain_empty_select_hack", name: "ontology[hasDomain][]"}) - = f.select(:hasDomain, cat_select, {selected: @ontology.hasDomain}, {multiple: true, :"data-placeholder" => "Select category (domain)", class:"form-control"}) - %div.form-group.row - %div.col-md-1 - = f.label :group, "Groups", class:"col-form-label" - %div.col-md-11 - - group_select = @groups.sort{|a,b| a.name <=> b.name}.map{|c| [c.name, c.id]} - = f.hidden_field(:group, {value: "", id: "ontology_group_empty_select_hack", name: "ontology[group][]"}) - = f.select(:group, group_select, {selected: @ontology.group}, {multiple: true, :"data-placeholder" => "Select group", class:"form-control"}) - %div.form-row - %div.from-group.col-md-6 - = f.label :isView, "This ontology is a view of:" - = f.check_box :isView, checked: @ontology.view? - %div#viewOf_picker.row.form-group - - single_picker_locals = {:picker_id => "ontology_viewOf", placeholder: "Select an ontology to create a view on", field_name: "viewOf", disabled: !@ontology.view?, selected: @ontology.viewOf} - = render :partial => "shared/ontology_picker_single", :locals => single_picker_locals - %div.form-row - %div.from-group.col-md-6 - = f.label :subscribe_notifications, "Subscribe to email notifications for new notes" - = f.check_box :subscribe_notifications - - %div.d-flex.justify-content-center - = submit_tag "Cancel", formnovalidate: "formnovalidate", class: "btn btn-secondary mx-1 col-2" - = submit_tag button_text, class: "btn btn-primary mx-1 col-2" + }); \ No newline at end of file diff --git a/app/views/ontologies/_submission_location_form.html.haml b/app/views/ontologies/_submission_location_form.html.haml new file mode 100644 index 000000000..4fbdc2b24 --- /dev/null +++ b/app/views/ontologies/_submission_location_form.html.haml @@ -0,0 +1,49 @@ +.location-choice + %input{type: "radio", name: "submission[isRemote]", value: "3", id: "metadata_only", onchange: "displayMetadataOnlyForm()"} + %label.title{for: "metadata_only"} + Metadata only (No file) +.upload-ontology-desc.mb-2 + Allow users to view and search your ontology metadata, but not its classes and properties. +#metadata-only-form.d-none +.location-choice + %input{type: "radio", name: "submission[isRemote]", value: "1", id: "load_from_url", onchange: "displayUrlForm()"} + %label.title{for: "load_from_url"} + Load from URL +.upload-ontology-desc.mb-1 + New versions loaded on a nightly basis. +#url-form.d-none + = render Input::UrlComponent.new(label: "", name: "submission[pullLocation]") +.location-choice.mb-3.mt-3 + %input{type: "radio", name: "submission[isRemote]", value: "0", id: "upload_local_file", checked: true, onchange: "displayLocalFileForm()"} + %label.title{for: "upload_local_file"} + Upload local file +#local-file-form + = render Input::FileInputComponent.new(name: "submission[filePath]") + + +:javascript + const MetadataOnlyForm = document.getElementById("metadata-only-form"); + const UrlForm = document.getElementById("url-form"); + const LocalFileForm = document.getElementById("local-file-form"); + + const displayForm = (formElement) => { + [MetadataOnlyForm, UrlForm, LocalFileForm].forEach((form) => { + if (form === formElement) { + form.classList.remove("d-none"); + } else { + form.classList.add("d-none"); + } + }); + }; + + const displayMetadataOnlyForm = () => { + displayForm(MetadataOnlyForm); + }; + + const displayUrlForm = () => { + displayForm(UrlForm); + }; + + const displayLocalFileForm = () => { + displayForm(LocalFileForm); + }; diff --git a/app/views/ontologies/edit.html.haml b/app/views/ontologies/edit.html.haml index edaa0d654..ef1fd0f78 100644 --- a/app/views/ontologies/edit.html.haml +++ b/app/views/ontologies/edit.html.haml @@ -1,5 +1,5 @@ - @title = "Edit Ontology Information" %div{:style => "margin:10px;"} - = form_for :ontology, url: ontology_path(@ontology.acronym), html: {method: :put, id: "ontologyForm"} do |f| + = form_for :ontology, url: ontology_path(@ontology.acronym), html: {method: :put, id: "ontologyForm", multipart: true} do |f| = render partial: "form", locals: {f: f, button_text: "Save ontology", title_text: "Edit Ontology Information"} diff --git a/app/views/ontologies/new.html.haml b/app/views/ontologies/new.html.haml index a8bdab8e0..29a66b45e 100644 --- a/app/views/ontologies/new.html.haml +++ b/app/views/ontologies/new.html.haml @@ -1,5 +1,5 @@ - @title = "Submit New Ontology" %div{:style => "margin:10px;"} - = form_for :ontology, url: {action: "create"}, html: {id: "ontologyForm"} do |f| - = render partial: "form", locals: {f: f} + = form_for :ontology, url: {action: "create"}, html: {id: "ontologyForm", multipart: true} do |f| + = render partial: "ontologies/form", locals: {f: f} diff --git a/app/views/shared/_ontology_picker_single.html.erb b/app/views/shared/_ontology_picker_single.html.erb index dbcb73125..b6132ac10 100644 --- a/app/views/shared/_ontology_picker_single.html.erb +++ b/app/views/shared/_ontology_picker_single.html.erb @@ -16,6 +16,6 @@
    - <%= select object_name, field_name, @onts_for_select, { :include_blank => true, :selected => selected }, :id => picker_id, :class => "ontology_picker_single form-control", "data-placeholder".to_sym => placeholder, disabled: disabled %> + <%= render Input::SelectComponent.new(label: placeholder, name: "#{object_name}[#{field_name}]", value: @onts_for_select, selected: selected) %>
    diff --git a/app/views/submissions/new.html.haml b/app/views/submissions/new.html.haml index 21d83e669..82e77242e 100644 --- a/app/views/submissions/new.html.haml +++ b/app/views/submissions/new.html.haml @@ -1,9 +1,2 @@ -- @title = "Add new ontology submission" - -%div.container.py-4.py-md-5 - %h3.text-center.mb-4 - Add new submission - - if !(@submission.ontology.nil? || (@submission.ontology.is_a? String)) - %small.text-muted for #{@submission.ontology.acronym} - = form_for :submission, url: {action: "create"}, html: {id: "ontology_submission_form", multipart: true} do |f| - = render partial: "form", locals: {f: f, button_text: "Add submission"} += form_for :submission, url: {action: "create"}, html: {id: "ontology_submission_form", multipart: true} do |f| + = render partial: "ontologies/form", locals: {f: f, button_text: "Add submission"} \ No newline at end of file