From cc65f89eda35c89fafdd167674b4aef9b65038fb Mon Sep 17 00:00:00 2001 From: Syphax Bouazzouni Date: Tue, 5 Sep 2023 06:57:20 +0200 Subject: [PATCH] Feature: Implement the new upload ontology form (#292) * use inline svg in file input loader component * add hint to text input component * add error state to input field component * add helper text to input field component * add notes to input field component to explain how to use it * combine text area input component with input field component * groupe inputs in one folder in the lookbook * fix style issues in input field component * refactor input field component code to remove repetition * remove the duplicated alert component preview * make the input field component generic using a the content slot * implement form date component * implement form text area component * implement form text input component * duplicate the select component to be in the "form" namespace * fix select input component inversed open_to_add_values argument usage * move and rename file input loader component to be in the form module * update select component to use InputFieldComponent to have a label * remove unsed text in NestedFormInputComponentPreview * Fix bug of select list view of ontology (#289) * Add Chosen.js functionality to fix bug of select views of ontolgy * Add Chosen.js functionality for list views ontologies * fix bug of update ontology where there is no view * Refactor code * Delete ponse = @ontology.update * fix nested form component inputs focus color to use primary variable * add upload ontology from style file * extract from upload ontology from the location partial * update upload ontology form design * make the upload ontology form button style apply also submit button * add id argument to chips components to permit having multiple chips with same name * fix label not showing for the from select component * put the correct input names in the new ontology form * replace the finish div in the new ontology form with a submit tag * use for the administrated_by input with the form select component * implement the backend create action for the new upload ontology form * fix text area component not showing the value given as argument * add the default values for the inputs in the new ontology form * extract submission_from_params method from the save_submission concern * handle new ontology upload errors in the create action * add default empty submission in the new action * add type parametre to text_input_component so we can specify type as email, number .. * make nested form component start by default by one form initialized * add URI field to the upload ontology process * add search input component * add ontology search input component re-using search-input component * re-use ontology search input component in the topnav * re-use the ontology-search-input in the home * update search-input component to make it more generic * rename view components "form" namespace to "input" * create the display namespace for the data display components * create the layout namespace for view component that structure content * use the table component in the ontology mappings tab * add table component (style, preview and code) * make dropdown content. with no default padding * update concept details component to use the Table and dropdown component * add concept details component preview * update collection, concept, label_xl and scheme views with the new concept details component * migrate notes table to use Table component * install tom select * update select component with tom select * use not centred layout for the select input previews * remove the select id in the select input component template * remove is_selected_value variable in the select input component * fix select component id * add pill button component * add loader component small option and previews * update ontology subscribe component design and add preview * move rounded button preview to the button namespace * extract modal helpers into a helper file * use the new modal helpers instead of writing the view component or frame * add option to modal to show in connect alone * add modal component style * add modal component preview * move the modal component preview into the layout namespace * Update the design of the switch component * add switch input component preview * update search input component to handle custom link turbo targets * update search input component to have form-control style by default * remove no used home search controller replaced with search input * fix. ontology search input component ajax_url argument to add ?acronym= * fix home page search input style * migrate search input style from home.scss to search_input.scss * add margin top and bottom for the nested form component * update nested form component to have an empty state to send to the back * handle contact nested form empty state * migrate the fair score js code to stimulus to make it work with turbo * remove the old fair_score.js code * fix home page responsiveness issues * fix footer responsiveness * fix nav bar responsiveness and add admin to nav items * fix home annotator/recommender section title * fix home responsiveness for small screens * add link text component * use link text component to implement popup link component * use link text component to implement external link component * use link text component to implement internal link component * use internal and external links component in the label link component * add previews to link text components * change internal link icon in link text component tmp tmp tmp tmp tmp tmp tmp tmp tmp tmp * refactor select input component template to use the rails helper * use stimulus values in select input component controller * Add email, password, url components to lookbook * use url component in upload ontology process * fix location subform selection via text on upload ontology * change date of creation by modification date field, and add comment of changes field when upload new version of an ontology * fix select input component to use the name * use the correct name for the visibility and allowed to view inputs * make ontology edit and new form use multi part * fix URI input name and value to replace ontology by submission * add disable state to input field component * make acronym field disabled when uploading a new version of an ontology * migrate the ontology picker single partial to select component * Add the checked property to the chips component * update date component with flatpicker * add disabled state to input field component * update submission helpers with the new components * refactor update ontology code to remove undesired ifs * add text field and text area components to lookbook * add select component to lookbook * add date field to the lookbook * add "inline_svg" gem to use svg in files * make the components preview container include JS cod if needed * add language field component * add link field component * add date_time field component * add license field component * add text area field component * add summary section component preview * add dropdown component * add field container component to show a label with it's value vertically * add nested form input component preview * add a pop-up with list of ontologies in group and category admin table * Revert "add a pop-up with list of ontologies in group and category admin table" This reverts commit c4b391a9d3e5272703051922bcb9495ac35297c8. * add a select to update ontologies in the pop-up edit of group in admin table * add a select to update ontologies in the pop-up edit of category in admin table * Refactor code * refactor code and fix bug * remove old undesired text area input from lookbook * add Upload file input component to lookbook * combine all input components in one globale component * remove style params from input field component * recover text area field component (after deleting it by mistake in a previous commit * add hint to text input component * add error state to input field component * add helper text to input field component * add notes to input field component to explain how to use it * combine text area input component with input field component * groupe inputs in one folder in the lookbook * make the input field component generic using a the content slot * implement form date component * implement form text area component * implement form text input component * duplicate the select component to be in the "form" namespace * move and rename file input loader component to be in the form module * update select component to use InputFieldComponent to have a label * remove unsed text in NestedFormInputComponentPreview * fix nested form component inputs focus color to use primary variable * add upload ontology from style file * extract from upload ontology from the location partial * update upload ontology form design * make the upload ontology form button style apply also submit button * add id argument to chips components to permit having multiple chips with same name * fix label not showing for the from select component * put the correct input names in the new ontology form * replace the finish div in the new ontology form with a submit tag * use for the administrated_by input with the form select component * implement the backend create action for the new upload ontology form * fix text area component not showing the value given as argument * add the default values for the inputs in the new ontology form * extract submission_from_params method from the save_submission concern * handle new ontology upload errors in the create action * add default empty submission in the new action * add type parametre to text_input_component so we can specify type as email, number .. * make nested form component start by default by one form initialized * add URI field to the upload ontology process * rename view components "form" namespace to "input" * create the display namespace for the data display components * create the layout namespace for view component that structure content * add modal component preview * move the modal component preview into the layout namespace * change internal link icon in link text component tmp tmp tmp tmp tmp tmp tmp tmp tmp tmp * add text field and text area components to lookbook * add date field to the lookbook * fix label not showing for the from select component * fix text area component not showing the value given as argument * add type parametre to text_input_component so we can specify type as email, number .. * refactor select input component template to use the rails helper * use stimulus values in select input component controller * Add email, password, url components to lookbook * use url component in upload ontology process * fix location subform selection via text on upload ontology * fix select input component to use the name * use the correct name for the visibility and allowed to view inputs * make ontology edit and new form use multi part * fix URI input name and value to replace ontology by submission * migrate the ontology picker single partial to select component * Add the checked property to the chips component * Merge remote-tracking branch 'origin/feature/update-upload-ontology' into feature/update-upload-ontology * change date of creation by modification date field, and add comment of changes field when upload new version of an ontology * add disable state to input field component * update submission helpers with the new components * refactor update ontology code to remove undesired ifs * add id parameter to input field component * replace finish button in upload ontology by the button component * Add size parameter to button component * Add color parameter to button component * Add state parameter to the button component * make finish button animated in upload ontology process * add progress pages component * put again the type argument for button component * fix progress pages item not center if test is long * migrate upload ontology form to use progress pages component * make progress pages container take full width by default * update login page button component usage to have an ID * put again the removed JS code for visibility and is a view * fix chips style padding removed from merging with the browse branch * update date input in the ontology form to use the metadata helper * fix chips style in upload ontology form * remove old new submission form from the bottom of the new ontology page * Fix select multiple items, delete icon style in select component * fix upload ontology style to have less margin bottom * extract ontology updater and saver concern form ontologies controller code * update submission create action tu update also the ontology * use AlertComponent to display ontology form error messages * create input helpers to use inputs component more easily and use them in ontology form * remove no more used style code in upload ontology style file * fix ontology form date and contact inputs not showing error message * set update ontology to true in the create action of submission controller * fix alignement of chips and button components --------- Co-authored-by: Syphax Bouazzouni Co-authored-by: SirineMhedhbi <31127782+SirineMhedhbi@users.noreply.github.com> Co-authored-by: Sirine Mhedhbi --- app/assets/images/arrow-right.svg | 3 + app/assets/images/json.svg | 3 + app/assets/images/white-check.svg | 3 + .../stylesheets/application.css.scss.erb | 2 +- app/assets/stylesheets/components/chips.scss | 3 +- .../components/primary_button.scss | 101 ++++++ app/assets/stylesheets/upload_ontology.scss | 105 ++++++ .../primary_button_component.html.haml | 23 ++ .../chips_component/chips_component.html.haml | 4 +- .../nested_form_inputs_component.html.haml | 7 +- app/components/select_input_component.rb | 28 +- app/controllers/concerns/ontology_updater.rb | 92 +++++ .../concerns/submission_updater.rb | 14 +- app/controllers/ontologies_controller.rb | 54 +-- app/controllers/submissions_controller.rb | 26 +- app/helpers/application_helper.rb | 51 ++- app/helpers/inputs_helper.rb | 45 +++ app/helpers/ontologies_helper.rb | 80 +++++ app/helpers/submissions_helper.rb | 33 +- .../controllers/tom_select_controller.js | 16 + app/views/layouts/_topnav.html.haml | 22 ++ app/views/layouts/component_preview.html.erb | 60 ++++ app/views/login/index.html.haml | 3 +- app/views/ontologies/_form.html.haml | 330 +++++++----------- .../_submission_location_form.html.haml | 49 +++ app/views/ontologies/edit.html.haml | 2 +- app/views/ontologies/new.html.haml | 4 +- .../shared/_ontology_picker_single.html.erb | 2 +- app/views/submissions/new.html.haml | 11 +- 29 files changed, 861 insertions(+), 315 deletions(-) create mode 100644 app/assets/images/arrow-right.svg create mode 100644 app/assets/images/json.svg create mode 100644 app/assets/images/white-check.svg create mode 100644 app/assets/stylesheets/components/primary_button.scss create mode 100644 app/assets/stylesheets/upload_ontology.scss create mode 100644 app/components/buttons/primary_button_component/primary_button_component.html.haml create mode 100644 app/controllers/concerns/ontology_updater.rb create mode 100644 app/helpers/inputs_helper.rb create mode 100644 app/javascript/controllers/tom_select_controller.js create mode 100644 app/views/layouts/component_preview.html.erb create mode 100644 app/views/ontologies/_submission_location_form.html.haml 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