From 2d2345c847e8d8e73c6b94fb0a41ebe5101edeb1 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Wed, 20 Mar 2024 17:22:52 +0100 Subject: [PATCH 01/31] add feature to display resouce content in different formats --- app/assets/stylesheets/concepts.scss | 13 +++ app/components/concept_details_component.rb | 3 +- .../concept_details_component.html.haml | 52 ++++++--- app/controllers/ontologies_controller.rb | 20 ++++ .../controllers/content_finder_controller.js | 106 ++++++++++++++++++ app/javascript/controllers/index.js | 3 + app/views/concepts/_details.html.haml | 2 +- .../ontologies/resource_content.html.haml | 5 + config/routes.rb | 1 + 9 files changed, 190 insertions(+), 15 deletions(-) create mode 100644 app/javascript/controllers/content_finder_controller.js create mode 100644 app/views/ontologies/resource_content.html.haml diff --git a/app/assets/stylesheets/concepts.scss b/app/assets/stylesheets/concepts.scss index c9f04c7d0..4012a84a8 100644 --- a/app/assets/stylesheets/concepts.scss +++ b/app/assets/stylesheets/concepts.scss @@ -156,4 +156,17 @@ div.synonym-change-request button { justify-content: center; color: var(--gray-color); margin-top: 10px; +} + +.content-finder-result{ + text-align: left; + max-width: 99%; + padding: 10px; +} + +.concepts-content-format{ + position: relative; + .justify-content-center{ + justify-content: start !important; + } } \ No newline at end of file diff --git a/app/components/concept_details_component.rb b/app/components/concept_details_component.rb index 16635e419..1f9e08a78 100644 --- a/app/components/concept_details_component.rb +++ b/app/components/concept_details_component.rb @@ -9,13 +9,14 @@ class ConceptDetailsComponent < ViewComponent::Base attr_reader :concept_properties - def initialize(id:, acronym:, properties:, top_keys:, bottom_keys:, exclude_keys:) + def initialize(id:, acronym:, concept_id: nil , properties:, top_keys:, bottom_keys:, exclude_keys:) @acronym = acronym @properties = properties @top_keys = top_keys @bottom_keys = bottom_keys @exclude_keys = exclude_keys @id = id + @concept_id=concept_id @concept_properties = concept_properties2hash(@properties) if @properties end diff --git a/app/components/concept_details_component/concept_details_component.html.haml b/app/components/concept_details_component/concept_details_component.html.haml index 4b1d6015c..29aa9d45d 100644 --- a/app/components/concept_details_component/concept_details_component.html.haml +++ b/app/components/concept_details_component/concept_details_component.html.haml @@ -4,22 +4,48 @@ %div.my-3 %div.raw-table - if @bottom_keys.present? - = render DropdownContainerComponent.new(title: 'Raw data', id: "accordion-#{@id}") do - - top_set, leftover_set, bottom_set = filter_properties(@top_keys, @bottom_keys, @exclude_keys, prefix_properties(@concept_properties)) - - leftover_set = convert_dates(leftover_set) - = render TableComponent.new(stripped: true) do |t| + %h2.card_title + %span.mr-2 + Raw Data + .concepts-content-format + = render TabsContainerComponent.new(type: 'outline') do |c| + - c.item(title: "HTML", selected: true) + - c.item_content do + - top_set, leftover_set, bottom_set = filter_properties(@top_keys, @bottom_keys, @exclude_keys, prefix_properties(@concept_properties)) + - leftover_set = convert_dates(leftover_set) + = render TableComponent.new(stripped: true) do |t| - - row_hash_properties(top_set, @acronym).each do |row| - - t.add_row(*row) + - row_hash_properties(top_set, @acronym).each do |row| + - t.add_row(*row) - - row_hash_properties(leftover_set, @acronym).each do |row| - - t.add_row(*row) + - row_hash_properties(leftover_set, @acronym).each do |row| + - t.add_row(*row) - - sections.each do |section| - - t.row do - = section + - sections.each do |section| + - t.row do + = section - - row_hash_properties(bottom_set, @acronym).each do |row| - - t.add_row(*row) + - row_hash_properties(bottom_set, @acronym).each do |row| + - t.add_row(*row) + + - if @concept_id + - uri_parameter= CGI.escape(@concept_id) + - finder_params = "?acronym=#{@acronym}&uri=#{uri_parameter}" + + - c.item(title: "JSON") + - c.item_content do + = render TurboFrameComponent.new(id:'resource_content_frame_json',src: "/content_finder#{finder_params}&output_format=json", loading:"lazy") + + - c.item(title: "XML") + - c.item_content do + = render TurboFrameComponent.new(id:'resource_content_frame_xml',src: "/content_finder#{finder_params}&output_format=xml", loading:"lazy") + + - c.item(title: "NTRIPLES") + - c.item_content do + = render TurboFrameComponent.new(id:'resource_content_frame_ntriples',src: "/content_finder#{finder_params}&output_format=ntriples", loading:"lazy") + + - c.item(title: "TURTLE") + - c.item_content do + = render TurboFrameComponent.new(id:'resource_content_frame_turtle',src: "/content_finder#{finder_params}&output_format=turtle", loading:"lazy") \ No newline at end of file diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index 07f1ad9a3..13d327611 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -219,6 +219,26 @@ def sparql end end + def content_finder + if params[:acronym] && params[:uri] + @acronym = params[:acronym] + ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:acronym]).first + sub = ontology.explore.latest_submission({ include: 'submissionId' }) + params[:output_format] = params[:output_format].presence || 'json' + params[:acronym] = sub.id.gsub(REST_URI, 'http://data.bioontology.org/') + if params[:output_format] == 'html' + params[:output_format] = 'json' + end + @format = params[:output_format] + if params[:output_format] == 'json' + @result = LinkedData::Client::HTTP.post("/dereference_resource", params).to_h.to_json + else + @result = LinkedData::Client::HTTP.post("/dereference_resource", params) + end + end + render 'ontologies/resource_content' + end + # GET /ontologies/ACRONYM # GET /ontologies/1.xml def show diff --git a/app/javascript/controllers/content_finder_controller.js b/app/javascript/controllers/content_finder_controller.js new file mode 100644 index 000000000..2e2d89ce7 --- /dev/null +++ b/app/javascript/controllers/content_finder_controller.js @@ -0,0 +1,106 @@ +import { Controller } from '@hotwired/stimulus' +import hljs from 'highlight.js/lib/core' +import xml from 'highlight.js/lib/languages/xml' +import json from 'highlight.js/lib/languages/json' + +export default class extends Controller { + static targets = ["content"] + static values = { + result: String, + format: String + } + connect() { + switch (this.formatValue) { + case 'json': + hljs.registerLanguage('json', json) + this.showJSON() + break + case 'xml': + hljs.registerLanguage('xml', xml) + this.showXML() + break + case 'ntriples': + hljs.registerLanguage('ntriples', function (hljs) { + var URL_PATTERN = /<[^>]+>/; // Regex pattern for matching URLs in angle brackets + return { + case_insensitive: true, + contains: [ + { + className: 'subject', + begin: /^<[^>]+>/, + }, + { + className: 'predicate', + begin: /<[^>]+>/, + }, + { + className: 'object', + begin: /\s([^\s]+)\s\./, + }, + hljs.COMMENT('^#', '$') + ] + }; + }); + this.showNTriples() + break + case 'turtle': + hljs.registerLanguage('turtle', function (hljs) { + var URL_PATTERN = /(?:<[^>]*>)|(?:https?:\/\/[^\s]+)/; + + return { + case_insensitive: true, + contains: [ + { + className: 'custom-prefixes', + begin: '@prefix', + relevance: 10 + }, + { + className: 'meta', + begin: /@base/, + end: /[\r\n]|$/, + relevance: 10 + }, + { + className: 'variable', + begin: /\?[\w\d]+/ + }, + { + className: 'custom-symbol', + begin: /@?[A-Za-z_][A-Za-z0-9_]*(?= *:)/, + relevance: 10 + }, + { + className: 'custom-concepts', + begin: /:\s*(\w+)/, + relevance: 10 + }, + { + className: 'string', + begin: URL_PATTERN + } + ] + }; + }); + //hljs.registerLanguage('turtle', hljsDefineTurtle) + this.showTURTLE() + break + } + } + + showJSON() { + this.contentTarget.innerHTML = hljs.highlight(JSON.stringify(JSON.parse(this.resultValue), null, " "), { language: 'json' }).value + } + + showXML() { + this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'xml' }).value + } + + showNTriples() { + this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'ntriples' }).value + } + + showTURTLE() { + this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'turtle' }).value + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 14bf869e4..123c844e9 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -102,3 +102,6 @@ application.register('form-url', FormUrlController) import OntologiesSelector from "./ontologies_selector_controller" application.register("ontologies-selector", OntologiesSelector) + +import ContentFinderController from "./content_finder_controller" +application.register("content-finder", ContentFinderController) \ No newline at end of file diff --git a/app/views/concepts/_details.html.haml b/app/views/concepts/_details.html.haml index 506a70092..5a5f6c67e 100644 --- a/app/views/concepts/_details.html.haml +++ b/app/views/concepts/_details.html.haml @@ -3,7 +3,7 @@ - label_xl_set = %w[skos-xl#prefLabel skos-xl#altLabel skos-xl#hiddenLabel] - = render ConceptDetailsComponent.new(id:'concept-details', acronym: @ontology.acronym, + = render ConceptDetailsComponent.new(id:'concept-details', acronym: @ontology.acronym, concept_id: @concept.id, properties: @concept.properties, top_keys: %w[description comment], bottom_keys: %w[disjoint subclass is_a has_part], diff --git a/app/views/ontologies/resource_content.html.haml b/app/views/ontologies/resource_content.html.haml new file mode 100644 index 000000000..23f10e0ca --- /dev/null +++ b/app/views/ontologies/resource_content.html.haml @@ -0,0 +1,5 @@ += render TurboFrameComponent.new(id:"resource_content_frame_#{@format}") do + - if @result + .content-finder-result{data: {controller: 'content-finder', 'content-finder-result-value': @result, 'content-finder-format-value': @format }} + %pre.mb-0 + %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'content-finder-target': 'content'}} \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index cf2c1cb3f..94c5fa662 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -122,6 +122,7 @@ # Ontologies get '/ontologies/view/edit/:id' => 'ontologies#edit_view', :constraints => { id: /[^\/?]+/ } get '/ontologies/view/new/:id' => 'ontologies#new_view' + get '/content_finder' => 'ontologies#content_finder' get '/ontologies/virtual/:ontology' => 'ontologies#virtual', :as => :ontology_virtual get '/ontologies/success/:id' => 'ontologies#submit_success' From 74660925e6991fce60e693b8fd88fc67e19e6993 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Wed, 20 Mar 2024 17:24:40 +0100 Subject: [PATCH 02/31] Add download and copy buttons to the raw data section --- app/assets/stylesheets/concepts.scss | 6 ++++++ .../concept_details_component.html.haml | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/app/assets/stylesheets/concepts.scss b/app/assets/stylesheets/concepts.scss index 4012a84a8..08ced3de0 100644 --- a/app/assets/stylesheets/concepts.scss +++ b/app/assets/stylesheets/concepts.scss @@ -169,4 +169,10 @@ div.synonym-change-request button { .justify-content-center{ justify-content: start !important; } + .concpets-content-format-actions{ + display: flex; + position: absolute; + right: 2%; + top: 5px; + } } \ No newline at end of file diff --git a/app/components/concept_details_component/concept_details_component.html.haml b/app/components/concept_details_component/concept_details_component.html.haml index 29aa9d45d..8dae341b8 100644 --- a/app/components/concept_details_component/concept_details_component.html.haml +++ b/app/components/concept_details_component/concept_details_component.html.haml @@ -8,6 +8,19 @@ %span.mr-2 Raw Data .concepts-content-format + .concpets-content-format-actions + .download-btn + = render ChipButtonComponent.new(type: 'clickable', 'data-action': "click->metadata-downloader#download") do + = inline_svg("summary/download.svg", width: '15px', height: '15px') + .clipboard.d-flex.align-items-center{data:{controller: 'clipboard'}} + %div{data: {'clipboard-target': 'content'}, class: @show_content ? '' : 'd-none'} + = @message || content + .ml-2 + .copy{data: {controller:'tooltip', action: 'click->clipboard#copy', 'clipboard-target': 'copy'}, title: 'Copy'} + = inline_svg_tag('icons/copy.svg', width: '20', height: '21px', fill: 'none') + .d-none.check{data: {controller:'tooltip', 'clipboard-target': 'check'}, title: 'Copied!'} + = inline_svg_tag('check.svg', width: '18px', height: '18px') + = render TabsContainerComponent.new(type: 'outline') do |c| - c.item(title: "HTML", selected: true) - c.item_content do From f1326d7b906939bc6c4d966f16d7786ca1a4b289 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Thu, 21 Mar 2024 21:30:26 +0100 Subject: [PATCH 03/31] Add css to highlight the content of turtle and ntriples --- app/assets/stylesheets/concepts.scss | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/app/assets/stylesheets/concepts.scss b/app/assets/stylesheets/concepts.scss index 08ced3de0..618cd8691 100644 --- a/app/assets/stylesheets/concepts.scss +++ b/app/assets/stylesheets/concepts.scss @@ -175,4 +175,38 @@ div.synonym-change-request button { right: 2%; top: 5px; } +} + + +.hljs-custom-prefixes{ + color: grey; +} +.hljs-custom-symbol{ + color: red; +} + +.hljs-custom-concepts{ + color: #770088; +} + + +.content-finder-logo svg { + width: 100px; + height: 100px; +} +.content-finder-logo svg path{ + fill: var(--primary-color) +} + +.hljs-subject{ + color: green; +} + +.hljs-predicate{ + color: #770088; +} + +.parse_button{ + display: flex; + justify-content: center; } \ No newline at end of file From 1836108da7f8517b0ae09af4a97170d41cbe91bb Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Thu, 21 Mar 2024 21:31:46 +0100 Subject: [PATCH 04/31] Add the copy functionality to the resource raw data for formts: json, xml, ntriples and turtle --- .../clipboard_component_controller.js | 15 +++++++++++ .../concept_details_component.html.haml | 25 +++++-------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/app/components/clipboard_component/clipboard_component_controller.js b/app/components/clipboard_component/clipboard_component_controller.js index e28b5bdb0..7ba41f31a 100644 --- a/app/components/clipboard_component/clipboard_component_controller.js +++ b/app/components/clipboard_component/clipboard_component_controller.js @@ -14,6 +14,21 @@ export default class extends Controller { this.#copied() }) } + + copy_concept_content () { + const activeTab = document.querySelector('.concepts-content-format .tab-content .tab-pane.active'); + if(activeTab.id != 'html_content'){ + let resource_content = activeTab.querySelector('.content-finder-result').getAttribute('data-content-finder-result-value') + navigator.clipboard.writeText(resource_content).then(() => { + this.#copied(); + }) + }else{ + navigator.clipboard.writeText("").then(() => { + this.#copied() + }) + } + + } #copied () { if (this.timeout) { diff --git a/app/components/concept_details_component/concept_details_component.html.haml b/app/components/concept_details_component/concept_details_component.html.haml index 8dae341b8..d3dfa8610 100644 --- a/app/components/concept_details_component/concept_details_component.html.haml +++ b/app/components/concept_details_component/concept_details_component.html.haml @@ -13,10 +13,8 @@ = render ChipButtonComponent.new(type: 'clickable', 'data-action': "click->metadata-downloader#download") do = inline_svg("summary/download.svg", width: '15px', height: '15px') .clipboard.d-flex.align-items-center{data:{controller: 'clipboard'}} - %div{data: {'clipboard-target': 'content'}, class: @show_content ? '' : 'd-none'} - = @message || content .ml-2 - .copy{data: {controller:'tooltip', action: 'click->clipboard#copy', 'clipboard-target': 'copy'}, title: 'Copy'} + .copy{data: {controller:'tooltip', action: 'click->clipboard#copy_concept_content', 'clipboard-target': 'copy'}, title: 'Copy'} = inline_svg_tag('icons/copy.svg', width: '20', height: '21px', fill: 'none') .d-none.check{data: {controller:'tooltip', 'clipboard-target': 'check'}, title: 'Copied!'} = inline_svg_tag('check.svg', width: '18px', height: '18px') @@ -46,19 +44,8 @@ - if @concept_id - uri_parameter= CGI.escape(@concept_id) - finder_params = "?acronym=#{@acronym}&uri=#{uri_parameter}" - - - c.item(title: "JSON") - - c.item_content do - = render TurboFrameComponent.new(id:'resource_content_frame_json',src: "/content_finder#{finder_params}&output_format=json", loading:"lazy") - - - c.item(title: "XML") - - c.item_content do - = render TurboFrameComponent.new(id:'resource_content_frame_xml',src: "/content_finder#{finder_params}&output_format=xml", loading:"lazy") - - - c.item(title: "NTRIPLES") - - c.item_content do - = render TurboFrameComponent.new(id:'resource_content_frame_ntriples',src: "/content_finder#{finder_params}&output_format=ntriples", loading:"lazy") - - - c.item(title: "TURTLE") - - c.item_content do - = render TurboFrameComponent.new(id:'resource_content_frame_turtle',src: "/content_finder#{finder_params}&output_format=turtle", loading:"lazy") \ No newline at end of file + + - ['json', 'xml', 'ntriples', 'turtle'].each do |format| + - c.item(title: format) + - c.item_content do + = render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") \ No newline at end of file From 145702edfc05c1532277fffc407f050e99edfc8d Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Tue, 26 Mar 2024 22:47:27 +0100 Subject: [PATCH 05/31] Remove the clipboard component and change the HTML title to Raw Data --- .../concept_details_component.html.haml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/app/components/concept_details_component/concept_details_component.html.haml b/app/components/concept_details_component/concept_details_component.html.haml index d3dfa8610..c2ad32538 100644 --- a/app/components/concept_details_component/concept_details_component.html.haml +++ b/app/components/concept_details_component/concept_details_component.html.haml @@ -3,24 +3,11 @@ = header %div.my-3 %div.raw-table - - if @bottom_keys.present? - %h2.card_title - %span.mr-2 - Raw Data + - if @bottom_keys.present? .concepts-content-format - .concpets-content-format-actions - .download-btn - = render ChipButtonComponent.new(type: 'clickable', 'data-action': "click->metadata-downloader#download") do - = inline_svg("summary/download.svg", width: '15px', height: '15px') - .clipboard.d-flex.align-items-center{data:{controller: 'clipboard'}} - .ml-2 - .copy{data: {controller:'tooltip', action: 'click->clipboard#copy_concept_content', 'clipboard-target': 'copy'}, title: 'Copy'} - = inline_svg_tag('icons/copy.svg', width: '20', height: '21px', fill: 'none') - .d-none.check{data: {controller:'tooltip', 'clipboard-target': 'check'}, title: 'Copied!'} - = inline_svg_tag('check.svg', width: '18px', height: '18px') = render TabsContainerComponent.new(type: 'outline') do |c| - - c.item(title: "HTML", selected: true) + - c.item(title: "Raw Data", selected: true) - c.item_content do - top_set, leftover_set, bottom_set = filter_properties(@top_keys, @bottom_keys, @exclude_keys, prefix_properties(@concept_properties)) - leftover_set = convert_dates(leftover_set) From 55029d8f5f628c63d0a39dd6cb9724775f84ef1f Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Tue, 26 Mar 2024 22:47:54 +0100 Subject: [PATCH 06/31] Add clipboard component to the resource-content --- app/views/ontologies/resource_content.html.haml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/views/ontologies/resource_content.html.haml b/app/views/ontologies/resource_content.html.haml index 23f10e0ca..1158daac3 100644 --- a/app/views/ontologies/resource_content.html.haml +++ b/app/views/ontologies/resource_content.html.haml @@ -1,5 +1,7 @@ = render TurboFrameComponent.new(id:"resource_content_frame_#{@format}") do - if @result + .clipboard-component-resource-content{style: 'position: absolute; right: 2%; top: 5px;'} + = render ClipboardComponent.new(message: @result.to_s, show_content: false) .content-finder-result{data: {controller: 'content-finder', 'content-finder-result-value': @result, 'content-finder-format-value': @format }} %pre.mb-0 %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'content-finder-target': 'content'}} \ No newline at end of file From e65b1658479ed92c30bc1ace685966502b7bc36e Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Tue, 26 Mar 2024 22:52:26 +0100 Subject: [PATCH 07/31] Use Net::HTTP instead of Linked::Client::HTTP to include accept header --- app/controllers/ontologies_controller.rb | 33 ++++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index 13d327611..505f47a7e 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -221,19 +221,30 @@ def sparql def content_finder if params[:acronym] && params[:uri] - @acronym = params[:acronym] - ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:acronym]).first - sub = ontology.explore.latest_submission({ include: 'submissionId' }) - params[:output_format] = params[:output_format].presence || 'json' - params[:acronym] = sub.id.gsub(REST_URI, 'http://data.bioontology.org/') - if params[:output_format] == 'html' - params[:output_format] = 'json' - end @format = params[:output_format] - if params[:output_format] == 'json' - @result = LinkedData::Client::HTTP.post("/dereference_resource", params).to_h.to_json + + url = URI.parse("#{REST_URI}ontologies/#{params[:acronym]}/resolve/#{CGI.escape(params[:uri])}") + http = Net::HTTP.new(url.host, url.port) + http.use_ssl = true if url.scheme == 'https' + request = Net::HTTP::Get.new(url) + request.body = "apikey=#{API_KEY}" + + case params[:output_format] + when 'json' + request['Accept'] = "application/json" + when 'xml' + request['Accept'] = "application/xml" + when 'ntriples' + request['Accept'] = "application/n-triples" + when 'turtle' + request['Accept'] = "text/turtle" + end + + response = http.request(request) + if response.code == '200' + @result = response.body else - @result = LinkedData::Client::HTTP.post("/dereference_resource", params) + raise "Request failed with status code: #{response.code}" end end render 'ontologies/resource_content' From 4d55dec10ab558bd539966ef596b650a51920f6d Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Tue, 26 Mar 2024 22:53:36 +0100 Subject: [PATCH 08/31] Add the content formats for instances and Properties --- .../instances/_instance_details.html.haml | 36 ++++++++++++------- app/views/properties/_show.html.haml | 12 +++++++ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/app/views/instances/_instance_details.html.haml b/app/views/instances/_instance_details.html.haml index 901dc1761..18e57014d 100644 --- a/app/views/instances/_instance_details.html.haml +++ b/app/views/instances/_instance_details.html.haml @@ -2,18 +2,30 @@ %div - ontology_acronym = params[:ontology_id] - filter_properties = ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"] - %table.table - %tr - %td - = link_to 'type', "#" - %td{style:"word-break: break-all"} - - type = @instance.types.reject{|x| x['NamedIndividual']} - #{type.map {|cls| link_to_class(ontology_acronym,cls)}.join(', ').html_safe} - - properties = @instance[:properties].to_h.select{|k,v| !filter_properties.include? k.to_s} - - properties.each do |prop| - - if !prop[1].nil? + = render TabsContainerComponent.new(type: 'outline') do |c| + - c.item(title: "Raw Data", selected: true) + - c.item_content do + %table.table %tr %td - = link_to_property(prop[0] , ontology_acronym) + = link_to 'type', "#" %td{style:"word-break: break-all"} - = prop[1].map { |value| instance_property_value(value , ontology_acronym) }.join(', ').html_safe \ No newline at end of file + - type = @instance.types.reject{|x| x['NamedIndividual']} + #{type.map {|cls| link_to_class(ontology_acronym,cls)}.join(', ').html_safe} + - properties = @instance[:properties].to_h.select{|k,v| !filter_properties.include? k.to_s} + - properties.each do |prop| + - if !prop[1].nil? + %tr + %td + = link_to_property(prop[0] , ontology_acronym) + %td{style:"word-break: break-all"} + = prop[1].map { |value| instance_property_value(value , ontology_acronym) }.join(', ').html_safe + + + - if params["id"] + - uri_parameter= CGI.escape(params["id"]) + - finder_params = "?acronym=#{ontology_acronym}&uri=#{uri_parameter}" + - ['json', 'xml', 'ntriples', 'turtle'].each do |format| + - c.item(title: format) + - c.item_content do + = render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") diff --git a/app/views/properties/_show.html.haml b/app/views/properties/_show.html.haml index 5544f93be..7df198a3e 100644 --- a/app/views/properties/_show.html.haml +++ b/app/views/properties/_show.html.haml @@ -13,3 +13,15 @@ - t.add_row({th: t('properties.preferred_name')}, {td: display_in_multiple_languages(c.concept_properties[:label][:values])}) if c.concept_properties[:label][:values].present? - t.add_row({th: t('properties.definitions')}, {td: display_in_multiple_languages(c.concept_properties[:definition][:values])}) if c.concept_properties[:definition][:values].present? - t.add_row({th: t('properties.parent')}, {td: display_in_multiple_languages(c.concept_properties[:parents][:values])}) if c.concept_properties[:parents][:values].present? + + .property-content-format + - property = OpenStruct.new(LinkedData::Client::Models::Property.properties_to_hash(@property).first) + + = render TabsContainerComponent.new(type: 'outline') do |c| + - uri_parameter= CGI.escape(property.id) + - finder_params = "?acronym=#{@acronym}&uri=#{uri_parameter}" + %h4= finder_params + - ['json', 'xml', 'ntriples', 'turtle'].each do |format| + - c.item(title: format, selected: (format == 'json')) + - c.item_content do + = render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") \ No newline at end of file From a6a867a85d6dd088172c18fda50df4b44aa54898 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Wed, 27 Mar 2024 17:18:32 +0100 Subject: [PATCH 09/31] Remove copy_concept_content method in clipboard component --- .../clipboard_component_controller.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/components/clipboard_component/clipboard_component_controller.js b/app/components/clipboard_component/clipboard_component_controller.js index 7ba41f31a..dda376687 100644 --- a/app/components/clipboard_component/clipboard_component_controller.js +++ b/app/components/clipboard_component/clipboard_component_controller.js @@ -15,21 +15,6 @@ export default class extends Controller { }) } - copy_concept_content () { - const activeTab = document.querySelector('.concepts-content-format .tab-content .tab-pane.active'); - if(activeTab.id != 'html_content'){ - let resource_content = activeTab.querySelector('.content-finder-result').getAttribute('data-content-finder-result-value') - navigator.clipboard.writeText(resource_content).then(() => { - this.#copied(); - }) - }else{ - navigator.clipboard.writeText("").then(() => { - this.#copied() - }) - } - - } - #copied () { if (this.timeout) { clearTimeout(this.timeout) From 001d1cc2c4f990658c5822818f14fa4696e3ee18 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Wed, 27 Mar 2024 17:21:01 +0100 Subject: [PATCH 10/31] Use the highlight-js in the metadata controller and remove content_finder_controller --- .../controllers/content_finder_controller.js | 106 ---------------- .../metadata_downloader_controller.js | 115 +++++++++++++++--- .../ontologies/resource_content.html.haml | 6 +- 3 files changed, 104 insertions(+), 123 deletions(-) delete mode 100644 app/javascript/controllers/content_finder_controller.js diff --git a/app/javascript/controllers/content_finder_controller.js b/app/javascript/controllers/content_finder_controller.js deleted file mode 100644 index 2e2d89ce7..000000000 --- a/app/javascript/controllers/content_finder_controller.js +++ /dev/null @@ -1,106 +0,0 @@ -import { Controller } from '@hotwired/stimulus' -import hljs from 'highlight.js/lib/core' -import xml from 'highlight.js/lib/languages/xml' -import json from 'highlight.js/lib/languages/json' - -export default class extends Controller { - static targets = ["content"] - static values = { - result: String, - format: String - } - connect() { - switch (this.formatValue) { - case 'json': - hljs.registerLanguage('json', json) - this.showJSON() - break - case 'xml': - hljs.registerLanguage('xml', xml) - this.showXML() - break - case 'ntriples': - hljs.registerLanguage('ntriples', function (hljs) { - var URL_PATTERN = /<[^>]+>/; // Regex pattern for matching URLs in angle brackets - return { - case_insensitive: true, - contains: [ - { - className: 'subject', - begin: /^<[^>]+>/, - }, - { - className: 'predicate', - begin: /<[^>]+>/, - }, - { - className: 'object', - begin: /\s([^\s]+)\s\./, - }, - hljs.COMMENT('^#', '$') - ] - }; - }); - this.showNTriples() - break - case 'turtle': - hljs.registerLanguage('turtle', function (hljs) { - var URL_PATTERN = /(?:<[^>]*>)|(?:https?:\/\/[^\s]+)/; - - return { - case_insensitive: true, - contains: [ - { - className: 'custom-prefixes', - begin: '@prefix', - relevance: 10 - }, - { - className: 'meta', - begin: /@base/, - end: /[\r\n]|$/, - relevance: 10 - }, - { - className: 'variable', - begin: /\?[\w\d]+/ - }, - { - className: 'custom-symbol', - begin: /@?[A-Za-z_][A-Za-z0-9_]*(?= *:)/, - relevance: 10 - }, - { - className: 'custom-concepts', - begin: /:\s*(\w+)/, - relevance: 10 - }, - { - className: 'string', - begin: URL_PATTERN - } - ] - }; - }); - //hljs.registerLanguage('turtle', hljsDefineTurtle) - this.showTURTLE() - break - } - } - - showJSON() { - this.contentTarget.innerHTML = hljs.highlight(JSON.stringify(JSON.parse(this.resultValue), null, " "), { language: 'json' }).value - } - - showXML() { - this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'xml' }).value - } - - showNTriples() { - this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'ntriples' }).value - } - - showTURTLE() { - this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'turtle' }).value - } -} diff --git a/app/javascript/controllers/metadata_downloader_controller.js b/app/javascript/controllers/metadata_downloader_controller.js index c1f139e51..cb1619cf2 100644 --- a/app/javascript/controllers/metadata_downloader_controller.js +++ b/app/javascript/controllers/metadata_downloader_controller.js @@ -12,11 +12,14 @@ export default class extends Controller { metadata: Object, context: Object, namespaces: Object, + result: String, format: { type: String, default: 'xml' } } connect () { - this.formatedData = this.#formatData() + if(this.formatedData){ + this.formatedData = this.#formatData() + } switch (this.formatValue) { case 'xml': hljs.registerLanguage('xml', xml) @@ -30,6 +33,71 @@ export default class extends Controller { hljs.registerLanguage('xml', xml) this.showNTriples() break + case 'ntriples': + hljs.registerLanguage('ntriples', function (hljs) { + var URL_PATTERN = /<[^>]+>/; // Regex pattern for matching URLs in angle brackets + return { + case_insensitive: true, + contains: [ + { + className: 'subject', + begin: /^<[^>]+>/, + }, + { + className: 'predicate', + begin: /<[^>]+>/, + }, + { + className: 'object', + begin: /\s([^\s]+)\s\./, + }, + hljs.COMMENT('^#', '$') + ] + }; + }); + this.showNTriples() + break + case 'turtle': + hljs.registerLanguage('turtle', function (hljs) { + var URL_PATTERN = /(?:<[^>]*>)|(?:https?:\/\/[^\s]+)/; + + return { + case_insensitive: true, + contains: [ + { + className: 'custom-prefixes', + begin: '@prefix', + relevance: 10 + }, + { + className: 'meta', + begin: /@base/, + end: /[\r\n]|$/, + relevance: 10 + }, + { + className: 'variable', + begin: /\?[\w\d]+/ + }, + { + className: 'custom-symbol', + begin: /@?[A-Za-z_][A-Za-z0-9_]*(?= *:)/, + relevance: 10 + }, + { + className: 'custom-concepts', + begin: /:\s*(\w+)/, + relevance: 10 + }, + { + className: 'string', + begin: URL_PATTERN + } + ] + }; + }); + this.showTURTLE() + break } } @@ -50,28 +118,45 @@ export default class extends Controller { } showNTriples () { - this.#toggleLoader() - this.#toNTriples(this.formatedData).then((nquads) => { - this.contentTarget.innerHTML = hljs.highlight(nquads, { language: 'xml' }).value + if(this.resultValue){ + this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'ntriples' }).value + }else{ this.#toggleLoader() - }) + this.#toNTriples(this.formatedData).then((nquads) => { + this.contentTarget.innerHTML = hljs.highlight(nquads, { language: 'xml' }).value + this.#toggleLoader() + }) + } } showXML () { - this.#toggleLoader() - this.contentTarget.innerHTML = hljs.highlight( - this.#toXML(this.formatedData, this.contextValue), - { language: 'xml' } - ).value - this.#toggleLoader() + if(this.resultValue){ + this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'xml' }).value + }else{ + this.#toggleLoader() + this.contentTarget.innerHTML = hljs.highlight( + this.#toXML(this.formatedData, this.contextValue), + { language: 'xml' } + ).value + this.#toggleLoader() + } } showJSONLD () { - this.#toggleLoader() - this.#toJSONLD().then((jsonld) => { - this.contentTarget.innerHTML = hljs.highlight(JSON.stringify(jsonld, null, ' '), { language: 'json' }).value + if(this.resultValue){ + this.contentTarget.innerHTML = hljs.highlight(JSON.stringify(JSON.parse(this.resultValue), null, " "), { language: 'json' }).value + }else{ this.#toggleLoader() - }) + this.#toJSONLD().then((jsonld) => { + this.contentTarget.innerHTML = hljs.highlight(JSON.stringify(jsonld, null, ' '), { language: 'json' }).value + this.#toggleLoader() + }) + } + } + + + showTURTLE() { + this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'turtle' }).value } #toggleLoader () { diff --git a/app/views/ontologies/resource_content.html.haml b/app/views/ontologies/resource_content.html.haml index 1158daac3..a62956542 100644 --- a/app/views/ontologies/resource_content.html.haml +++ b/app/views/ontologies/resource_content.html.haml @@ -2,6 +2,8 @@ - if @result .clipboard-component-resource-content{style: 'position: absolute; right: 2%; top: 5px;'} = render ClipboardComponent.new(message: @result.to_s, show_content: false) - .content-finder-result{data: {controller: 'content-finder', 'content-finder-result-value': @result, 'content-finder-format-value': @format }} + .content-finder-result{data: {controller: 'metadata-downloader', 'metadata-downloader-result-value': @result, 'metadata-downloader-format-value': @format }} %pre.mb-0 - %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'content-finder-target': 'content'}} \ No newline at end of file + %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'metadata-downloader-target': 'content'}} + + %div.d-none{data: {'metadata-downloader-target': 'loader'}} \ No newline at end of file From 2b7da510326c77706b0261653c778d5603ee5990 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Wed, 27 Mar 2024 17:22:00 +0100 Subject: [PATCH 11/31] Remove code duplication for content formats - add content_formats method in application helper and use it for concepts, properties and instances --- .../concept_details_component.html.haml | 11 ++--------- app/helpers/application_helper.rb | 13 +++++++++++++ app/views/instances/_instance_details.html.haml | 7 +------ app/views/properties/_show.html.haml | 8 +------- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/app/components/concept_details_component/concept_details_component.html.haml b/app/components/concept_details_component/concept_details_component.html.haml index c2ad32538..0f40704b2 100644 --- a/app/components/concept_details_component/concept_details_component.html.haml +++ b/app/components/concept_details_component/concept_details_component.html.haml @@ -27,12 +27,5 @@ - row_hash_properties(bottom_set, @acronym).each do |row| - t.add_row(*row) - - - if @concept_id - - uri_parameter= CGI.escape(@concept_id) - - finder_params = "?acronym=#{@acronym}&uri=#{uri_parameter}" - - - ['json', 'xml', 'ntriples', 'turtle'].each do |format| - - c.item(title: format) - - c.item_content do - = render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") \ No newline at end of file + + = content_formats(concept_id: @concept_id, acronym: @acronym, c: c) \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1e78d1389..a8fbe41cf 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -630,4 +630,17 @@ def cancel_button_component(class_name: nil, id: , value:, data: nil) end end end + + def content_formats(concept_id: nil, acronym: nil, c: nil, selected_format: nil) + if concept_id && acronym + finder_params = "?acronym=#{acronym}&uri=#{CGI.escape(concept_id)}" + ['json', 'xml', 'ntriples', 'turtle'].each do |format| + c.item(title: format, selected: (format.eql?(selected_format))) + c.item_content do + render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") + end + end + end + end + end diff --git a/app/views/instances/_instance_details.html.haml b/app/views/instances/_instance_details.html.haml index 18e57014d..9d8375872 100644 --- a/app/views/instances/_instance_details.html.haml +++ b/app/views/instances/_instance_details.html.haml @@ -23,9 +23,4 @@ - if params["id"] - - uri_parameter= CGI.escape(params["id"]) - - finder_params = "?acronym=#{ontology_acronym}&uri=#{uri_parameter}" - - ['json', 'xml', 'ntriples', 'turtle'].each do |format| - - c.item(title: format) - - c.item_content do - = render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") + = content_formats(concept_id: params["id"], acronym: ontology_acronym, c: c) \ No newline at end of file diff --git a/app/views/properties/_show.html.haml b/app/views/properties/_show.html.haml index 7df198a3e..b3bfd36f2 100644 --- a/app/views/properties/_show.html.haml +++ b/app/views/properties/_show.html.haml @@ -18,10 +18,4 @@ - property = OpenStruct.new(LinkedData::Client::Models::Property.properties_to_hash(@property).first) = render TabsContainerComponent.new(type: 'outline') do |c| - - uri_parameter= CGI.escape(property.id) - - finder_params = "?acronym=#{@acronym}&uri=#{uri_parameter}" - %h4= finder_params - - ['json', 'xml', 'ntriples', 'turtle'].each do |format| - - c.item(title: format, selected: (format == 'json')) - - c.item_content do - = render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") \ No newline at end of file + = content_formats(concept_id: property.id, acronym: @acronym, c: c, selected_format: 'json') \ No newline at end of file From 4d9c8f3cf25a0121fabd7d4cdbcdd901f5a7fb26 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Fri, 29 Mar 2024 15:09:42 +0100 Subject: [PATCH 12/31] Remove content finder controller from index.js --- app/javascript/controllers/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 123c844e9..b66014109 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -103,5 +103,5 @@ application.register('form-url', FormUrlController) import OntologiesSelector from "./ontologies_selector_controller" application.register("ontologies-selector", OntologiesSelector) -import ContentFinderController from "./content_finder_controller" -application.register("content-finder", ContentFinderController) \ No newline at end of file +//import ContentFinderController from "./content_finder_controller" +//application.register("content-finder", ContentFinderController) \ No newline at end of file From fca5cd4404b8b3b849be32245fa8f9cb85bb6ab2 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Tue, 2 Apr 2024 18:10:16 +0200 Subject: [PATCH 13/31] Small fix: remove a condition in metadata_downloader --- app/javascript/controllers/metadata_downloader_controller.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/javascript/controllers/metadata_downloader_controller.js b/app/javascript/controllers/metadata_downloader_controller.js index cb1619cf2..a4aac3358 100644 --- a/app/javascript/controllers/metadata_downloader_controller.js +++ b/app/javascript/controllers/metadata_downloader_controller.js @@ -17,9 +17,7 @@ export default class extends Controller { } connect () { - if(this.formatedData){ - this.formatedData = this.#formatData() - } + this.formatedData = this.#formatData() switch (this.formatValue) { case 'xml': hljs.registerLanguage('xml', xml) From 5c7d09d9d09f3a972196bf5e32a298e1fba09130 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Tue, 2 Apr 2024 18:11:07 +0200 Subject: [PATCH 14/31] Add content_formats method to ontologies_helper and remove it from application_helper --- app/components/concept_details_component.rb | 1 + app/helpers/application_helper.rb | 13 ------------- app/helpers/ontologies_helper.rb | 13 +++++++++++++ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/components/concept_details_component.rb b/app/components/concept_details_component.rb index 1f9e08a78..9ef4156b5 100644 --- a/app/components/concept_details_component.rb +++ b/app/components/concept_details_component.rb @@ -2,6 +2,7 @@ class ConceptDetailsComponent < ViewComponent::Base include ApplicationHelper + include OntologiesHelper include MultiLanguagesHelper renders_one :header, TableComponent diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a8fbe41cf..1e78d1389 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -630,17 +630,4 @@ def cancel_button_component(class_name: nil, id: , value:, data: nil) end end end - - def content_formats(concept_id: nil, acronym: nil, c: nil, selected_format: nil) - if concept_id && acronym - finder_params = "?acronym=#{acronym}&uri=#{CGI.escape(concept_id)}" - ['json', 'xml', 'ntriples', 'turtle'].each do |format| - c.item(title: format, selected: (format.eql?(selected_format))) - c.item_content do - render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") - end - end - end - end - end diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index c233552fc..7c1ec7587 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -690,4 +690,17 @@ def submission_languages(submission = @submission) def id_to_acronym(id) id.split('/').last end + + def content_formats(concept_id: nil, acronym: nil, c: nil, selected_format: nil) + if concept_id && acronym + finder_params = "?acronym=#{acronym}&uri=#{CGI.escape(concept_id)}" + ['json', 'xml', 'ntriples', 'turtle'].each do |format| + c.item(title: format, selected: (format.eql?(selected_format))) + c.item_content do + render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") + end + end + end + end + end From 53f77cacfa7ae14ccc3d778f8cd93e27d4390606 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Tue, 2 Apr 2024 18:11:32 +0200 Subject: [PATCH 15/31] Use Faraday instead of Net::HTTP --- app/controllers/ontologies_controller.rb | 48 ++++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index 505f47a7e..37b35f43e 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -221,31 +221,31 @@ def sparql def content_finder if params[:acronym] && params[:uri] - @format = params[:output_format] - - url = URI.parse("#{REST_URI}ontologies/#{params[:acronym]}/resolve/#{CGI.escape(params[:uri])}") - http = Net::HTTP.new(url.host, url.port) - http.use_ssl = true if url.scheme == 'https' - request = Net::HTTP::Get.new(url) - request.body = "apikey=#{API_KEY}" - - case params[:output_format] - when 'json' - request['Accept'] = "application/json" - when 'xml' - request['Accept'] = "application/xml" - when 'ntriples' - request['Accept'] = "application/n-triples" - when 'turtle' - request['Accept'] = "text/turtle" - end + @acronym = params[:acronym] + @format = params[:output_format] + case params[:output_format] + when 'json' + accept_header = "application/json" + when 'xml' + accept_header = "application/xml" + when 'ntriples' + accept_header = "application/n-triples" + when 'turtle' + accept_header = "text/turtle" + end - response = http.request(request) - if response.code == '200' - @result = response.body - else - raise "Request failed with status code: #{response.code}" - end + url = URI.parse("#{rest_url}/ontologies/#{params[:acronym].strip}/resolve/#{helpers.escape(params[:uri].strip)}") + conn = Faraday.new(url: url) do |faraday| + faraday.headers['Accept'] = accept_header + faraday.adapter Faraday.default_adapter + faraday.headers['Authorization'] = "apikey token=#{API_KEY}" + end + + response = conn.get + @result="" + if response.success? + @result = response.body.force_encoding(Encoding::UTF_8) + end end render 'ontologies/resource_content' end From c8073915ef78c9c689af4108a1dd2745f83d1fb6 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Thu, 4 Apr 2024 18:15:08 +0200 Subject: [PATCH 16/31] Add helpers for content finder controller --- app/controllers/ontologies_controller.rb | 20 ++++---------------- app/helpers/ontologies_helper.rb | 24 +++++++++++++++--------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index 37b35f43e..974f9ca1a 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -220,29 +220,17 @@ def sparql end def content_finder - if params[:acronym] && params[:uri] - @acronym = params[:acronym] + if params[:acronym] && params[:id] @format = params[:output_format] - case params[:output_format] - when 'json' - accept_header = "application/json" - when 'xml' - accept_header = "application/xml" - when 'ntriples' - accept_header = "application/n-triples" - when 'turtle' - accept_header = "text/turtle" - end - - url = URI.parse("#{rest_url}/ontologies/#{params[:acronym].strip}/resolve/#{helpers.escape(params[:uri].strip)}") + @result="" + url = content_finder_url(params[:acronym], params[:id]) + accept_header = content_finder_accept_header(@format) conn = Faraday.new(url: url) do |faraday| faraday.headers['Accept'] = accept_header faraday.adapter Faraday.default_adapter faraday.headers['Authorization'] = "apikey token=#{API_KEY}" end - response = conn.get - @result="" if response.success? @result = response.body.force_encoding(Encoding::UTF_8) end diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index 7c1ec7587..f50f46064 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -691,15 +691,21 @@ def id_to_acronym(id) id.split('/').last end - def content_formats(concept_id: nil, acronym: nil, c: nil, selected_format: nil) - if concept_id && acronym - finder_params = "?acronym=#{acronym}&uri=#{CGI.escape(concept_id)}" - ['json', 'xml', 'ntriples', 'turtle'].each do |format| - c.item(title: format, selected: (format.eql?(selected_format))) - c.item_content do - render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/content_finder#{finder_params}&output_format=#{format}", loading: "lazy") - end - end + + def content_finder_url(acronym, uri) + URI.parse("#{rest_url}/ontologies/#{acronym.strip}/resolve/#{helpers.escape(uri.strip)}") + end + + def content_finder_accept_header(output_format) + case output_format + when 'json' + accept_header = "application/json" + when 'xml' + accept_header = "application/xml" + when 'ntriples' + accept_header = "application/n-triples" + when 'turtle' + accept_header = "text/turtle" end end From feee39121b1c6db327dfaea1536f09d74c3c2955 Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Thu, 4 Apr 2024 18:16:21 +0200 Subject: [PATCH 17/31] change content finder route to /ontologies/:acronym/:id/serialize/:output_format --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 94c5fa662..16d77425b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -122,7 +122,7 @@ # Ontologies get '/ontologies/view/edit/:id' => 'ontologies#edit_view', :constraints => { id: /[^\/?]+/ } get '/ontologies/view/new/:id' => 'ontologies#new_view' - get '/content_finder' => 'ontologies#content_finder' + get '/ontologies/:acronym/:id/serialize/:output_format' => 'ontologies#content_finder', :id => /.+/ get '/ontologies/virtual/:ontology' => 'ontologies#virtual', :as => :ontology_virtual get '/ontologies/success/:id' => 'ontologies#submit_success' From f3e0b5c1aca5d98237363ce4931aa8b54d07eb6e Mon Sep 17 00:00:00 2001 From: imadbourouche Date: Thu, 4 Apr 2024 18:17:07 +0200 Subject: [PATCH 18/31] Add concept details component to schemes and collections and fix code --- .../concept_details_component.html.haml | 41 ++++++++++--------- .../metadata_downloader_controller.js | 3 +- app/views/collections/_collection.html.haml | 2 +- app/views/properties/_show.html.haml | 10 +---- app/views/schemes/_scheme.html.haml | 2 +- 5 files changed, 26 insertions(+), 32 deletions(-) diff --git a/app/components/concept_details_component/concept_details_component.html.haml b/app/components/concept_details_component/concept_details_component.html.haml index 0f40704b2..6bda59040 100644 --- a/app/components/concept_details_component/concept_details_component.html.haml +++ b/app/components/concept_details_component/concept_details_component.html.haml @@ -3,29 +3,30 @@ = header %div.my-3 %div.raw-table - - if @bottom_keys.present? - .concepts-content-format + = render TabsContainerComponent.new(type: 'outline') do |c| + - if @bottom_keys.present? + - c.item(title: "Raw Data", selected: true) + - c.item_content do + - top_set, leftover_set, bottom_set = filter_properties(@top_keys, @bottom_keys, @exclude_keys, prefix_properties(@concept_properties)) + - leftover_set = convert_dates(leftover_set) + = render TableComponent.new(stripped: true) do |t| - = render TabsContainerComponent.new(type: 'outline') do |c| - - c.item(title: "Raw Data", selected: true) - - c.item_content do - - top_set, leftover_set, bottom_set = filter_properties(@top_keys, @bottom_keys, @exclude_keys, prefix_properties(@concept_properties)) - - leftover_set = convert_dates(leftover_set) - = render TableComponent.new(stripped: true) do |t| - - - row_hash_properties(top_set, @acronym).each do |row| - - t.add_row(*row) + - row_hash_properties(top_set, @acronym).each do |row| + - t.add_row(*row) - - row_hash_properties(leftover_set, @acronym).each do |row| - - t.add_row(*row) + - row_hash_properties(leftover_set, @acronym).each do |row| + - t.add_row(*row) - - sections.each do |section| - - t.row do - = section + - sections.each do |section| + - t.row do + = section - - row_hash_properties(bottom_set, @acronym).each do |row| - - t.add_row(*row) - - = content_formats(concept_id: @concept_id, acronym: @acronym, c: c) \ No newline at end of file + - row_hash_properties(bottom_set, @acronym).each do |row| + - t.add_row(*row) + - if @concept_id + - ['json', 'xml', 'ntriples', 'turtle'].each do |format| + - c.item(title: format, selected: (format=="json" && @bottom_keys.empty?)) + - c.item_content do + = render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/ontologies/#{@acronym}/#{CGI.escape(@concept_id)}/serialize/#{format}", loading: "lazy") \ No newline at end of file diff --git a/app/javascript/controllers/metadata_downloader_controller.js b/app/javascript/controllers/metadata_downloader_controller.js index a4aac3358..a2af7ed05 100644 --- a/app/javascript/controllers/metadata_downloader_controller.js +++ b/app/javascript/controllers/metadata_downloader_controller.js @@ -33,7 +33,6 @@ export default class extends Controller { break case 'ntriples': hljs.registerLanguage('ntriples', function (hljs) { - var URL_PATTERN = /<[^>]+>/; // Regex pattern for matching URLs in angle brackets return { case_insensitive: true, contains: [ @@ -57,7 +56,7 @@ export default class extends Controller { break case 'turtle': hljs.registerLanguage('turtle', function (hljs) { - var URL_PATTERN = /(?:<[^>]*>)|(?:https?:\/\/[^\s]+)/; + let URL_PATTERN = /(?:<[^>]*>)|(?:https?:\/\/[^\s]+)/; return { case_insensitive: true, diff --git a/app/views/collections/_collection.html.haml b/app/views/collections/_collection.html.haml index 99ea94cfa..9ab1f547a 100644 --- a/app/views/collections/_collection.html.haml +++ b/app/views/collections/_collection.html.haml @@ -1,5 +1,5 @@ = turbo_frame_tag 'collection' do - = render ConceptDetailsComponent.new(id:'collection-label', acronym: @ontology.acronym, + = render ConceptDetailsComponent.new(id:'collection-label', acronym: @ontology.acronym, concept_id: collection.id, properties: collection.properties, top_keys: %w[created modified comment note], bottom_keys: [], diff --git a/app/views/properties/_show.html.haml b/app/views/properties/_show.html.haml index b3bfd36f2..a1a5297f0 100644 --- a/app/views/properties/_show.html.haml +++ b/app/views/properties/_show.html.haml @@ -3,7 +3,7 @@ - if @property.errors = render Display::AlertComponent.new(type:'info', message: @property.errors.join) - else - = render ConceptDetailsComponent.new(id:'property-details', acronym: @acronym, + = render ConceptDetailsComponent.new(id:'property-details', acronym: @acronym, concept_id: @property.id, properties: OpenStruct.new(LinkedData::Client::Models::Property.properties_to_hash(@property).first), top_keys: [], bottom_keys: [], @@ -12,10 +12,4 @@ - t.add_row({th: t('properties.id')}, {td: link_to_with_actions(c.concept_properties[:id][:values])}) if c.concept_properties[:id][:values].present? - t.add_row({th: t('properties.preferred_name')}, {td: display_in_multiple_languages(c.concept_properties[:label][:values])}) if c.concept_properties[:label][:values].present? - t.add_row({th: t('properties.definitions')}, {td: display_in_multiple_languages(c.concept_properties[:definition][:values])}) if c.concept_properties[:definition][:values].present? - - t.add_row({th: t('properties.parent')}, {td: display_in_multiple_languages(c.concept_properties[:parents][:values])}) if c.concept_properties[:parents][:values].present? - - .property-content-format - - property = OpenStruct.new(LinkedData::Client::Models::Property.properties_to_hash(@property).first) - - = render TabsContainerComponent.new(type: 'outline') do |c| - = content_formats(concept_id: property.id, acronym: @acronym, c: c, selected_format: 'json') \ No newline at end of file + - t.add_row({th: t('properties.parent')}, {td: display_in_multiple_languages(c.concept_properties[:parents][:values])}) if c.concept_properties[:parents][:values].present? \ No newline at end of file diff --git a/app/views/schemes/_scheme.html.haml b/app/views/schemes/_scheme.html.haml index d2670af16..664cda427 100644 --- a/app/views/schemes/_scheme.html.haml +++ b/app/views/schemes/_scheme.html.haml @@ -1,6 +1,6 @@ = turbo_frame_tag 'scheme' do - if @scheme && !@scheme.empty? - = render ConceptDetailsComponent.new(id:'scheme-label', acronym: @ontology.acronym, + = render ConceptDetailsComponent.new(id:'scheme-label', acronym: @ontology.acronym, concept_id: @scheme.id, properties: scheme.properties, top_keys: %w[description comment], bottom_keys: %w[disjoint subclass is_a has_part], From 5a57cda03e39240033833035d6e4b637fd9e0dbb Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 15:24:26 +0200 Subject: [PATCH 19/31] rename the metadata downloader controller and remove the result value --- app/javascript/controllers/index.js | 4 ++-- ...oller.js => rdf_highlighter_controller.js} | 17 ++++++++-------- app/views/metadata_export/index.html.haml | 20 +++++++++---------- 3 files changed, 20 insertions(+), 21 deletions(-) rename app/javascript/controllers/{metadata_downloader_controller.js => rdf_highlighter_controller.js} (93%) diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 14bf869e4..9960f87bc 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -46,8 +46,8 @@ application.register("language-change", LanguageChangeController) import LoadChartController from "./load_chart_controller" application.register("load-chart", LoadChartController) -import MetadataDownloaderController from "./metadata_downloader_controller" -application.register("metadata-downloader", MetadataDownloaderController) +import RDFHighlighter from "./rdf_highlighter_controller" +application.register("rdf-highlighter", RDFHighlighter) import OntologyRelationsNetworkController from "./ontology_relations_network_controller" application.register("ontology-relations-network", OntologyRelationsNetworkController) diff --git a/app/javascript/controllers/metadata_downloader_controller.js b/app/javascript/controllers/rdf_highlighter_controller.js similarity index 93% rename from app/javascript/controllers/metadata_downloader_controller.js rename to app/javascript/controllers/rdf_highlighter_controller.js index a2af7ed05..ae801c8cb 100644 --- a/app/javascript/controllers/metadata_downloader_controller.js +++ b/app/javascript/controllers/rdf_highlighter_controller.js @@ -4,7 +4,7 @@ import hljs from 'highlight.js/lib/core' import xml from 'highlight.js/lib/languages/xml' import json from 'highlight.js/lib/languages/json' -// Connects to data-controller="metadata-downloader" +// Connects to data-controller="rdf-highlighter" export default class extends Controller { static targets = ['content', 'loader'] @@ -12,7 +12,6 @@ export default class extends Controller { metadata: Object, context: Object, namespaces: Object, - result: String, format: { type: String, default: 'xml' } } @@ -115,8 +114,8 @@ export default class extends Controller { } showNTriples () { - if(this.resultValue){ - this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'ntriples' }).value + if(!this.hasMetadataValue){ + this.contentTarget.innerHTML = hljs.highlight(this.contentTarget.textContent, { language: 'ntriples' }).value }else{ this.#toggleLoader() this.#toNTriples(this.formatedData).then((nquads) => { @@ -127,8 +126,8 @@ export default class extends Controller { } showXML () { - if(this.resultValue){ - this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'xml' }).value + if(!this.hasMetadataValue){ + this.contentTarget.innerHTML = hljs.highlight(this.contentTarget.textContent, { language: 'xml' }).value }else{ this.#toggleLoader() this.contentTarget.innerHTML = hljs.highlight( @@ -140,8 +139,8 @@ export default class extends Controller { } showJSONLD () { - if(this.resultValue){ - this.contentTarget.innerHTML = hljs.highlight(JSON.stringify(JSON.parse(this.resultValue), null, " "), { language: 'json' }).value + if(!this.hasMetadataValue){ + this.contentTarget.innerHTML = hljs.highlight(JSON.stringify(JSON.parse(this.contentTarget.textContent), null, " "), { language: 'json' }).value }else{ this.#toggleLoader() this.#toJSONLD().then((jsonld) => { @@ -153,7 +152,7 @@ export default class extends Controller { showTURTLE() { - this.contentTarget.innerHTML = hljs.highlight(this.resultValue, { language: 'turtle' }).value + this.contentTarget.innerHTML = hljs.highlight(this.contentTarget.textContent, { language: 'turtle' }).value } #toggleLoader () { diff --git a/app/views/metadata_export/index.html.haml b/app/views/metadata_export/index.html.haml index 5f5d95245..726ac235a 100644 --- a/app/views/metadata_export/index.html.haml +++ b/app/views/metadata_export/index.html.haml @@ -6,22 +6,22 @@ - t.item(id: 'triples', title: 'N3') - %w[csv xml json triples].each do |format| - t.item_content do - %div.metadata-exporter{data: {controller: 'metadata-downloader', - 'metadata-downloader-metadata-value': @ontology_metadata.to_json, - 'metadata-downloader-context-value': @submission_latest['@context'].to_json, - 'metadata-downloader-namespaces-value': resolve_namespaces.to_json, - 'metadata-downloader-format-value': format}} + %div.metadata-exporter{data: {controller: 'rdf-highlighter', + 'rdf-highlighter-metadata-value': @ontology_metadata.to_json, + 'rdf-highlighter-context-value': @submission_latest['@context'].to_json, + 'rdf-highlighter-namespaces-value': resolve_namespaces.to_json, + 'rdf-highlighter-format-value': format}} %div.download-btn - = render ChipButtonComponent.new(type: 'clickable', 'data-action': "click->metadata-downloader#download") do + = render ChipButtonComponent.new(type: 'clickable', 'data-action': "click->rdf-highlighter#download") do = inline_svg("summary/download.svg", width: '15px', height: '15px') - if format.eql?('csv') - %div{data: {'metadata-downloader-target': 'content'}} + %div{data: {'rdf-highlighter-target': 'content'}} = render partial: 'ontologies/sections/additional_metadata' - %div.d-none{data: {'metadata-downloader-target': 'loader'}} + %div.d-none{data: {'rdf-highlighter-target': 'loader'}} = render LoaderComponent.new - else %div.p-3.my-2.card %pre - %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'metadata-downloader-target': 'content'}} - %div.d-none{data: {'metadata-downloader-target': 'loader'}} + %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'rdf-highlighter-target': 'content'}} + %div.d-none{data: {'rdf-highlighter-target': 'loader'}} = render LoaderComponent.new \ No newline at end of file From 850d9496f45e327025effdb91a9ea3582a1911ef Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 15:25:20 +0200 Subject: [PATCH 20/31] update the concept formats tab UI to use icons and open in modal --- app/assets/images/icons/json-ld-file.svg | 19 +++++++++ app/assets/images/icons/ntriples-file.svg | 19 +++++++++ app/assets/images/icons/rdf-xml-file.svg | 19 +++++++++ app/assets/images/icons/turtle-file.svg | 19 +++++++++ app/assets/stylesheets/concepts.scss | 6 +-- app/assets/stylesheets/content_finder.scss | 6 --- .../concept_details_component.html.haml | 39 ++++++++++--------- app/controllers/ontologies_controller.rb | 5 ++- .../ontologies/resource_content.html.haml | 19 ++++----- 9 files changed, 110 insertions(+), 41 deletions(-) create mode 100644 app/assets/images/icons/json-ld-file.svg create mode 100644 app/assets/images/icons/ntriples-file.svg create mode 100644 app/assets/images/icons/rdf-xml-file.svg create mode 100644 app/assets/images/icons/turtle-file.svg diff --git a/app/assets/images/icons/json-ld-file.svg b/app/assets/images/icons/json-ld-file.svg new file mode 100644 index 000000000..91d7f2a3a --- /dev/null +++ b/app/assets/images/icons/json-ld-file.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + JSON-LD + + diff --git a/app/assets/images/icons/ntriples-file.svg b/app/assets/images/icons/ntriples-file.svg new file mode 100644 index 000000000..d4c1128f1 --- /dev/null +++ b/app/assets/images/icons/ntriples-file.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + N-TRIPLE + + diff --git a/app/assets/images/icons/rdf-xml-file.svg b/app/assets/images/icons/rdf-xml-file.svg new file mode 100644 index 000000000..e48df17e3 --- /dev/null +++ b/app/assets/images/icons/rdf-xml-file.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + RDF/XML + + diff --git a/app/assets/images/icons/turtle-file.svg b/app/assets/images/icons/turtle-file.svg new file mode 100644 index 000000000..02fb92131 --- /dev/null +++ b/app/assets/images/icons/turtle-file.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + TURTLE + + diff --git a/app/assets/stylesheets/concepts.scss b/app/assets/stylesheets/concepts.scss index 618cd8691..2d59e48c6 100644 --- a/app/assets/stylesheets/concepts.scss +++ b/app/assets/stylesheets/concepts.scss @@ -158,11 +158,7 @@ div.synonym-change-request button { margin-top: 10px; } -.content-finder-result{ - text-align: left; - max-width: 99%; - padding: 10px; -} + .concepts-content-format{ position: relative; diff --git a/app/assets/stylesheets/content_finder.scss b/app/assets/stylesheets/content_finder.scss index 67cff5fa3..a64d8be51 100644 --- a/app/assets/stylesheets/content_finder.scss +++ b/app/assets/stylesheets/content_finder.scss @@ -13,20 +13,14 @@ .content-finder-container .inputs > div{ margin:10px 0; text-align: center; - width: 400px; } .content-finder-result{ - margin-top: 20px; - margin-bottom: 30px; text-align: left; max-width: 95%; margin-left: auto; margin-right: auto; - padding: 20px; background-color: #fff; - border-radius: 10px; - border: 1px solid #a1a1a1; } .content-finder-html-result{ diff --git a/app/components/concept_details_component/concept_details_component.html.haml b/app/components/concept_details_component/concept_details_component.html.haml index 6bda59040..821d9cc7d 100644 --- a/app/components/concept_details_component/concept_details_component.html.haml +++ b/app/components/concept_details_component/concept_details_component.html.haml @@ -3,30 +3,31 @@ = header %div.my-3 %div.raw-table - = render TabsContainerComponent.new(type: 'outline') do |c| - - if @bottom_keys.present? - - c.item(title: "Raw Data", selected: true) - - c.item_content do + = render DropdownContainerComponent.new(title: 'Raw data', id: "accordion-#{@id}") do + = render TableComponent.new(stripped: true) do |t| + - if @bottom_keys.present? - top_set, leftover_set, bottom_set = filter_properties(@top_keys, @bottom_keys, @exclude_keys, prefix_properties(@concept_properties)) - leftover_set = convert_dates(leftover_set) - = render TableComponent.new(stripped: true) do |t| + - row_hash_properties(top_set, @acronym).each do |row| + - t.add_row(*row) - - row_hash_properties(top_set, @acronym).each do |row| - - t.add_row(*row) + - row_hash_properties(leftover_set, @acronym).each do |row| + - t.add_row(*row) - - row_hash_properties(leftover_set, @acronym).each do |row| - - t.add_row(*row) + - sections.each do |section| + - t.row do + = section - - sections.each do |section| - - t.row do - = section + - row_hash_properties(bottom_set, @acronym).each do |row| + - t.add_row(*row) - - row_hash_properties(bottom_set, @acronym).each do |row| - - t.add_row(*row) - - if @concept_id - - ['json', 'xml', 'ntriples', 'turtle'].each do |format| - - c.item(title: format, selected: (format=="json" && @bottom_keys.empty?)) - - c.item_content do - = render TurboFrameComponent.new(id: "resource_content_frame_#{format}", src: "/ontologies/#{@acronym}/#{CGI.escape(@concept_id)}/serialize/#{format}", loading: "lazy") \ No newline at end of file + - if @concept_id + - t.row do |r| + - r.td(colspan: 2) do + %div.d-flex.justify-content-center.p-2 + - [["json","json-ld-file"], ["xml","rdf-xml-file"] , ["ntriples","ntriples-file"], ["turtle","turtle-file"]].each do |format, icon| + %div.mx-4{data: {controller: "tooltip"}, title: "Export in #{format.upcase}"} + = link_to_modal(nil, "/ontologies/#{@acronym}/#{CGI.escape(@concept_id)}/serialize/#{format}", data: {show_modal_title_value: @concept_id, show_modal_size_value: 'modal-xl'}) do + = inline_svg("icons/#{icon}.svg", width: '50px', height: '50px') \ No newline at end of file diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index 974f9ca1a..1b5451adb 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -222,7 +222,8 @@ def sparql def content_finder if params[:acronym] && params[:id] @format = params[:output_format] - @result="" + @result= "" + url = content_finder_url(params[:acronym], params[:id]) accept_header = content_finder_accept_header(@format) conn = Faraday.new(url: url) do |faraday| @@ -235,7 +236,7 @@ def content_finder @result = response.body.force_encoding(Encoding::UTF_8) end end - render 'ontologies/resource_content' + render 'ontologies/resource_content', layout: nil end # GET /ontologies/ACRONYM diff --git a/app/views/ontologies/resource_content.html.haml b/app/views/ontologies/resource_content.html.haml index a62956542..915dd1c98 100644 --- a/app/views/ontologies/resource_content.html.haml +++ b/app/views/ontologies/resource_content.html.haml @@ -1,9 +1,10 @@ -= render TurboFrameComponent.new(id:"resource_content_frame_#{@format}") do - - if @result - .clipboard-component-resource-content{style: 'position: absolute; right: 2%; top: 5px;'} - = render ClipboardComponent.new(message: @result.to_s, show_content: false) - .content-finder-result{data: {controller: 'metadata-downloader', 'metadata-downloader-result-value': @result, 'metadata-downloader-format-value': @format }} - %pre.mb-0 - %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'metadata-downloader-target': 'content'}} - - %div.d-none{data: {'metadata-downloader-target': 'loader'}} \ No newline at end of file += render_in_modal do + - if @result + .clipboard-component-resource-content{style: 'position: absolute; right: 2%; top: 5px;'} + = render ClipboardComponent.new(message: @result.to_s, show_content: false) + .content-finder-result{data: {controller: 'rdf-highlighter', 'rdf-highlighter-format-value': @format }} + %pre.mb-0 + %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'rdf-highlighter-target': 'content'}} + = @result + + %div.d-none{data: {'rdf-highlighter-target': 'loader'}} \ No newline at end of file From 55e91af0a5ae1930b8de57f2feff34164ab6ba52 Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 15:26:02 +0200 Subject: [PATCH 21/31] update the content finder page code to re-use concept format code --- app/views/content_finder/index.html.haml | 38 +++++++++++++----------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/app/views/content_finder/index.html.haml b/app/views/content_finder/index.html.haml index 32ca82370..f4d77bcad 100644 --- a/app/views/content_finder/index.html.haml +++ b/app/views/content_finder/index.html.haml @@ -1,25 +1,29 @@ - @title = "Content Finder" = form_tag('/content_finder', method: :get, 'data-turbo': true, novalidate: true) do - .content-finder-container - %div - .inputs - #graph_url - = ontologies_selector(id: 'search-ontologies', name: 'acronym', selected: @acronym || '', placeholder: "Graph URL", multiple: false) - #uri - = render Input::UrlComponent.new(name: "uri", placeholder: "URI", value: params[:uri]) - #output_format - = render Input::SelectComponent.new(name: "output_format", value: ['html', 'json', 'xml', 'ntriples', 'turtle'], selected: params[:output_format], multiple: false) - #submit_button - = render Buttons::RegularButtonComponent.new(id:'regular-button', value: "Fetch", variant: "primary", size: "slim", type:"submit") + .content-finder-container + .inputs.w-50 + #graph_url + = ontologies_selector(id: 'search-ontologies', name: 'acronym', selected: @acronym || '', placeholder: "Graph URL", multiple: false) + #uri + = render Input::UrlComponent.new(name: "uri", placeholder: "URI", value: params[:uri]) + #output_format + = render Input::SelectComponent.new(name: "output_format", value: %w[html json xml ntriples turtle], selected: params[:output_format], multiple: false) + #submit_button + = render Buttons::RegularButtonComponent.new(id:'regular-button', value: "Fetch", variant: "primary", size: "slim", type:"submit") - if @result && !@result.empty? - .content-finder-result{data: {controller: 'content-finder', 'content-finder-format-value': @format }} - %pre.mb-0 - %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'content-finder-target': 'content'}} - = @result + %div.p-2.card.content-finder-result + .clipboard-component-resource-content{style: 'position: absolute; right: 2%; top: 5px;'} + = render ClipboardComponent.new(message: @result.to_s, show_content: false) + %div.p-2{data: {controller: 'rdf-highlighter', 'rdf-highlighter-format-value': @format }} + %pre.mb-0 + %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'rdf-highlighter-target': 'content'}} + = @result + + %div.d-none{data: {'rdf-highlighter-target': 'loader'}} - if params[:output_format] == "html" - .content-finder-html-result.bg-white.p-2.card - = render TurboFrameComponent.new(id:"concept_details" , src: "/ajax/class_details?&ontology=#{@acronym}&conceptid=#{CGI.escape(params[:uri])}&styled=false", loading: "lazy") + .content-finder-html-result.bg-white.p-2.card + = render TurboFrameComponent.new(id:"concept_details" , src: "/ajax/class_details?&ontology=#{@acronym}&conceptid=#{CGI.escape(params[:uri])}&styled=false", loading: "lazy") \ No newline at end of file From c0747e0f371fbbc54494c3f10ef9f9ec9f2adbf6 Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 16:31:09 +0200 Subject: [PATCH 22/31] update the content formats icons to use the primary color of the portal --- app/assets/images/icons/json-ld-file.svg | 2 +- app/assets/images/icons/ntriples-file.svg | 2 +- app/assets/images/icons/rdf-xml-file.svg | 2 +- app/assets/images/icons/turtle-file.svg | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/images/icons/json-ld-file.svg b/app/assets/images/icons/json-ld-file.svg index 91d7f2a3a..870b32514 100644 --- a/app/assets/images/icons/json-ld-file.svg +++ b/app/assets/images/icons/json-ld-file.svg @@ -1,7 +1,7 @@ - + diff --git a/app/assets/images/icons/ntriples-file.svg b/app/assets/images/icons/ntriples-file.svg index d4c1128f1..99628c2a1 100644 --- a/app/assets/images/icons/ntriples-file.svg +++ b/app/assets/images/icons/ntriples-file.svg @@ -1,7 +1,7 @@ - + diff --git a/app/assets/images/icons/rdf-xml-file.svg b/app/assets/images/icons/rdf-xml-file.svg index e48df17e3..570097b55 100644 --- a/app/assets/images/icons/rdf-xml-file.svg +++ b/app/assets/images/icons/rdf-xml-file.svg @@ -1,7 +1,7 @@ - + diff --git a/app/assets/images/icons/turtle-file.svg b/app/assets/images/icons/turtle-file.svg index 02fb92131..11eaaa257 100644 --- a/app/assets/images/icons/turtle-file.svg +++ b/app/assets/images/icons/turtle-file.svg @@ -1,7 +1,7 @@ - + From a376685751689775bc391b64abc1670351b7bab0 Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 16:32:54 +0200 Subject: [PATCH 23/31] extract commun code in content finder and concept formats in a concern --- .../concerns/ontology_content_serializer.rb | 43 +++++++++++++++++++ app/controllers/content_finder_controller.rb | 38 ++++------------ app/controllers/ontologies_controller.rb | 41 +++++++----------- app/helpers/ontologies_helper.rb | 17 -------- 4 files changed, 66 insertions(+), 73 deletions(-) create mode 100644 app/controllers/concerns/ontology_content_serializer.rb diff --git a/app/controllers/concerns/ontology_content_serializer.rb b/app/controllers/concerns/ontology_content_serializer.rb new file mode 100644 index 000000000..33ba605c7 --- /dev/null +++ b/app/controllers/concerns/ontology_content_serializer.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module OntologyContentSerializer + extend ActiveSupport::Concern + + def serialize_content(ontology_acronym:, concept_id:, format:) + if ontology_acronym && concept_id + @format = format + @result = "" + @acronym = ontology_acronym + url = content_finder_url(ontology_acronym, concept_id) + accept_header = content_finder_accept_header(@format) + conn = Faraday.new(url: url) do |faraday| + faraday.headers['Accept'] = accept_header + faraday.adapter Faraday.default_adapter + faraday.headers['Authorization'] = "apikey token=#{get_apikey}" + end + response = conn.get + if response.success? + @result = response.body.force_encoding(Encoding::UTF_8) + end + end + @result + end + + def content_finder_url(acronym, uri) + URI.parse("#{rest_url}/ontologies/#{acronym.strip}/resolve/#{helpers.escape(uri.strip)}") + end + + def content_finder_accept_header(output_format) + case output_format + when 'json' + "application/json" + when 'xml' + "application/xml" + when 'ntriples' + "application/n-triples" + when 'turtle' + "text/turtle" + end + end + +end diff --git a/app/controllers/content_finder_controller.rb b/app/controllers/content_finder_controller.rb index b8ed8e66b..5f360f696 100644 --- a/app/controllers/content_finder_controller.rb +++ b/app/controllers/content_finder_controller.rb @@ -1,34 +1,12 @@ require 'faraday' -class ContentFinderController < ApplicationController - def index - if params[:acronym] && params[:uri] - @acronym = params[:acronym] - @format = params[:output_format] - case params[:output_format] - when 'json' - accept_header = "application/json" - when 'xml' - accept_header = "application/xml" - when 'ntriples' - accept_header = "application/n-triples" - when 'turtle' - accept_header = "text/turtle" - end +class ContentFinderController < ApplicationController + include OntologyContentSerializer - url = URI.parse("#{rest_url}/ontologies/#{params[:acronym].strip}/resolve/#{helpers.escape(params[:uri].strip)}") - conn = Faraday.new(url: url) do |faraday| - faraday.headers['Accept'] = accept_header - faraday.adapter Faraday.default_adapter - faraday.headers['Authorization'] = "apikey token=#{API_KEY}" - end - - response = conn.get - @result="" - if response.success? - @result = response.body.force_encoding(Encoding::UTF_8) - end - end - render 'content_finder/index', layout: 'tool' - end + def index + @result = serialize_content(ontology_acronym: params[:acronym], + concept_id: params[:uri], + format: params[:output_format]) + render 'content_finder/index', layout: 'tool' + end end \ No newline at end of file diff --git a/app/controllers/ontologies_controller.rb b/app/controllers/ontologies_controller.rb index 1b5451adb..3ad0d7b14 100644 --- a/app/controllers/ontologies_controller.rb +++ b/app/controllers/ontologies_controller.rb @@ -12,6 +12,7 @@ class OntologiesController < ApplicationController include TurboHelper include SparqlHelper include SubmissionFilter + include OntologyContentSerializer require 'multi_json' require 'cgi' @@ -52,7 +53,7 @@ def ontologies_filter count_streams = [ replace('ontologies_filter_count_request') do - helpers.content_tag(:p, class: "browse-desc-text", style: "margin-bottom: 12px !important;") { t("ontologies.showing_ontologies_size", ontologies_size: @ontologies.size, analytics_size: @analytics.keys.size)} + helpers.content_tag(:p, class: "browse-desc-text", style: "margin-bottom: 12px !important;") { t("ontologies.showing_ontologies_size", ontologies_size: @ontologies.size, analytics_size: @analytics.keys.size) } end ] + update_filters_counts @@ -219,24 +220,12 @@ def sparql end end - def content_finder - if params[:acronym] && params[:id] - @format = params[:output_format] - @result= "" + def content_serializer + @result = serialize_content(ontology_acronym: params[:acronym], + concept_id: params[:id], + format: params[:output_format]) - url = content_finder_url(params[:acronym], params[:id]) - accept_header = content_finder_accept_header(@format) - conn = Faraday.new(url: url) do |faraday| - faraday.headers['Accept'] = accept_header - faraday.adapter Faraday.default_adapter - faraday.headers['Authorization'] = "apikey token=#{API_KEY}" - end - response = conn.get - if response.success? - @result = response.body.force_encoding(Encoding::UTF_8) - end - end - render 'ontologies/resource_content', layout: nil + render 'ontologies/content_serializer', layout: nil end # GET /ontologies/ACRONYM @@ -463,16 +452,16 @@ def ontologies_selector @groups = LinkedData::Client::Models::Group.all(display_links: false, display_context: false) @filters = ontology_filters_init(@categories, @groups) @select_id = params[:id] - render 'ontologies/ontologies_selector/ontologies_selector' , layout: false + render 'ontologies/ontologies_selector/ontologies_selector', layout: false end def ontologies_selector_results @ontologies = LinkedData::Client::Models::Ontology.all(include_views: params[:showOntologyViews]) @total_ontologies_number = @ontologies.length @input = params[:input] || '' - @ontologies = @ontologies.select { |ontology| ontology.name.downcase.include?(@input.downcase) || ontology.acronym.downcase.include?(@input.downcase)} + @ontologies = @ontologies.select { |ontology| ontology.name.downcase.include?(@input.downcase) || ontology.acronym.downcase.include?(@input.downcase) } - if params[:groups] + if params[:groups] @ontologies = @ontologies.select do |ontology| (ontology.group & params[:groups]).any? end @@ -485,9 +474,9 @@ def ontologies_selector_results end if params[:formats] || params[:naturalLanguage] || params[:formalityLevel] || params[:isOfType] || params[:showRetiredOntologies] - submissions = LinkedData::Client::Models::OntologySubmission.all({also_include_views: 'true'}) + submissions = LinkedData::Client::Models::OntologySubmission.all({ also_include_views: 'true' }) if params[:formats] - submissions = submissions.select { |submission| params[:formats].include?(submission.hasOntologyLanguage)} + submissions = submissions.select { |submission| params[:formats].include?(submission.hasOntologyLanguage) } end if params[:naturalLanguage] submissions = submissions.select do |submission| @@ -495,13 +484,13 @@ def ontologies_selector_results end end if params[:formalityLevel] - submissions = submissions.select { |submission| params[:formalityLevel].include?(submission.hasFormalityLevel)} + submissions = submissions.select { |submission| params[:formalityLevel].include?(submission.hasFormalityLevel) } end if params[:isOfType] - submissions = submissions.select { |submission| params[:isOfType].include?(submission.isOfType)} + submissions = submissions.select { |submission| params[:isOfType].include?(submission.isOfType) } end if params[:showRetiredOntologies] - submissions = submissions.reject { |submission| submission.status.eql?('retired')} + submissions = submissions.reject { |submission| submission.status.eql?('retired') } end @ontologies = @ontologies.select do |ontology| submissions.any? { |submission| submission.ontology.id == ontology.id } diff --git a/app/helpers/ontologies_helper.rb b/app/helpers/ontologies_helper.rb index f50f46064..52b193a0e 100644 --- a/app/helpers/ontologies_helper.rb +++ b/app/helpers/ontologies_helper.rb @@ -691,22 +691,5 @@ def id_to_acronym(id) id.split('/').last end - - def content_finder_url(acronym, uri) - URI.parse("#{rest_url}/ontologies/#{acronym.strip}/resolve/#{helpers.escape(uri.strip)}") - end - - def content_finder_accept_header(output_format) - case output_format - when 'json' - accept_header = "application/json" - when 'xml' - accept_header = "application/xml" - when 'ntriples' - accept_header = "application/n-triples" - when 'turtle' - accept_header = "text/turtle" - end - end end From 52c88b8edb91616d28038a6db8f92d583e6541ba Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 16:39:46 +0200 Subject: [PATCH 24/31] extract rdf highlighter component from concept format and content finder --- app/components/display/rdf_highlighter_component.rb | 9 +++++++++ .../rdf_highlighter_component.html.haml | 6 ++++++ .../rdf_highlighter_component_controller.js} | 1 - app/helpers/components_helper.rb | 4 ++++ app/javascript/component_controllers/index.js | 3 +++ app/javascript/controllers/index.js | 3 --- app/views/content_finder/index.html.haml | 11 ++--------- app/views/ontologies/resource_content.html.haml | 10 ---------- 8 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 app/components/display/rdf_highlighter_component.rb create mode 100644 app/components/display/rdf_highlighter_component/rdf_highlighter_component.html.haml rename app/{javascript/controllers/rdf_highlighter_controller.js => components/display/rdf_highlighter_component/rdf_highlighter_component_controller.js} (99%) delete mode 100644 app/views/ontologies/resource_content.html.haml diff --git a/app/components/display/rdf_highlighter_component.rb b/app/components/display/rdf_highlighter_component.rb new file mode 100644 index 000000000..9e96dafaf --- /dev/null +++ b/app/components/display/rdf_highlighter_component.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Display::RdfHighlighterComponent < ViewComponent::Base + + def initialize(format: , text: ) + @format = format + @content = text + end +end diff --git a/app/components/display/rdf_highlighter_component/rdf_highlighter_component.html.haml b/app/components/display/rdf_highlighter_component/rdf_highlighter_component.html.haml new file mode 100644 index 000000000..ee81966e9 --- /dev/null +++ b/app/components/display/rdf_highlighter_component/rdf_highlighter_component.html.haml @@ -0,0 +1,6 @@ +.clipboard-component-resource-content{style: 'position: absolute; right: 2%; top: 5px;'} + = render ClipboardComponent.new(message: @content, show_content: false) +.content-finder-result{data: {controller: 'rdf-highlighter', 'rdf-highlighter-format-value': @format }} + %pre.mb-0 + %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'rdf-highlighter-target': 'content'}} + #{@content} \ No newline at end of file diff --git a/app/javascript/controllers/rdf_highlighter_controller.js b/app/components/display/rdf_highlighter_component/rdf_highlighter_component_controller.js similarity index 99% rename from app/javascript/controllers/rdf_highlighter_controller.js rename to app/components/display/rdf_highlighter_component/rdf_highlighter_component_controller.js index ae801c8cb..d045944f4 100644 --- a/app/javascript/controllers/rdf_highlighter_controller.js +++ b/app/components/display/rdf_highlighter_component/rdf_highlighter_component_controller.js @@ -150,7 +150,6 @@ export default class extends Controller { } } - showTURTLE() { this.contentTarget.innerHTML = hljs.highlight(this.contentTarget.textContent, { language: 'turtle' }).value } diff --git a/app/helpers/components_helper.rb b/app/helpers/components_helper.rb index 1bcfe5e64..c62a54609 100644 --- a/app/helpers/components_helper.rb +++ b/app/helpers/components_helper.rb @@ -1,5 +1,9 @@ module ComponentsHelper + def rdf_highlighter_container(format, content) + render Display::RdfHighlighterComponent.new(format: format, text: content) + end + def check_resolvability_container(url) turbo_frame_tag("#{escape(url)}_container", src: "/check_url_resolvability?url=#{escape(url)}", loading: "lazy", class: 'd-inline-block') do content_tag(:div, class: 'p-1', data: { controller: 'tooltip' }, title: t('components.check_resolvability')) do diff --git a/app/javascript/component_controllers/index.js b/app/javascript/component_controllers/index.js index ed08af5dc..eaf1728f0 100644 --- a/app/javascript/component_controllers/index.js +++ b/app/javascript/component_controllers/index.js @@ -24,7 +24,10 @@ import Reveal_component_controller from '../../components/layout/reveal_componen import Table_component_controller from '../../components/table_component/table_component_controller' import clipboard_component_controller from '../../components/clipboard_component/clipboard_component_controller' import range_slider_component_controller from '../../components/input/range_slider_component/range_slider_component_controller' +import RDFHighlighter from '../../components/display/rdf_highlighter_component/rdf_highlighter_component_controller' + +application.register("rdf-highlighter", RDFHighlighter) application.register('turbo-modal', TurboModalController) application.register('file-input', FileInputLoaderController) application.register('radio-chip', RadioChipController) diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 9960f87bc..b93b16d5e 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -46,9 +46,6 @@ application.register("language-change", LanguageChangeController) import LoadChartController from "./load_chart_controller" application.register("load-chart", LoadChartController) -import RDFHighlighter from "./rdf_highlighter_controller" -application.register("rdf-highlighter", RDFHighlighter) - import OntologyRelationsNetworkController from "./ontology_relations_network_controller" application.register("ontology-relations-network", OntologyRelationsNetworkController) diff --git a/app/views/content_finder/index.html.haml b/app/views/content_finder/index.html.haml index f4d77bcad..43098809a 100644 --- a/app/views/content_finder/index.html.haml +++ b/app/views/content_finder/index.html.haml @@ -13,17 +13,10 @@ - if @result && !@result.empty? %div.p-2.card.content-finder-result - .clipboard-component-resource-content{style: 'position: absolute; right: 2%; top: 5px;'} - = render ClipboardComponent.new(message: @result.to_s, show_content: false) - %div.p-2{data: {controller: 'rdf-highlighter', 'rdf-highlighter-format-value': @format }} - %pre.mb-0 - %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'rdf-highlighter-target': 'content'}} - = @result - - %div.d-none{data: {'rdf-highlighter-target': 'loader'}} + = rdf_highlighter_container(@format, @result) - if params[:output_format] == "html" .content-finder-html-result.bg-white.p-2.card - = render TurboFrameComponent.new(id:"concept_details" , src: "/ajax/class_details?&ontology=#{@acronym}&conceptid=#{CGI.escape(params[:uri])}&styled=false", loading: "lazy") + = render TurboFrameComponent.new(id: "concept_details" , src: "/ajax/class_details?&ontology=#{@acronym}&conceptid=#{CGI.escape(params[:uri])}&styled=false") \ No newline at end of file diff --git a/app/views/ontologies/resource_content.html.haml b/app/views/ontologies/resource_content.html.haml deleted file mode 100644 index 915dd1c98..000000000 --- a/app/views/ontologies/resource_content.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -= render_in_modal do - - if @result - .clipboard-component-resource-content{style: 'position: absolute; right: 2%; top: 5px;'} - = render ClipboardComponent.new(message: @result.to_s, show_content: false) - .content-finder-result{data: {controller: 'rdf-highlighter', 'rdf-highlighter-format-value': @format }} - %pre.mb-0 - %code.d-block{style: 'text-wrap: pretty; word-break: break-all', data: {'rdf-highlighter-target': 'content'}} - = @result - - %div.d-none{data: {'rdf-highlighter-target': 'loader'}} \ No newline at end of file From 4ff797d6cbfcfb1a7e4f94af6cb9af63a8f1b5cc Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 16:40:22 +0200 Subject: [PATCH 25/31] rename the concept formats file and action to content_serlizer --- app/views/ontologies/content_serializer.html.haml | 2 ++ config/routes.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 app/views/ontologies/content_serializer.html.haml diff --git a/app/views/ontologies/content_serializer.html.haml b/app/views/ontologies/content_serializer.html.haml new file mode 100644 index 000000000..beda31695 --- /dev/null +++ b/app/views/ontologies/content_serializer.html.haml @@ -0,0 +1,2 @@ += render_in_modal do + = rdf_highlighter_container(@format, @result) if @result diff --git a/config/routes.rb b/config/routes.rb index 16d77425b..af25d72e3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -122,7 +122,7 @@ # Ontologies get '/ontologies/view/edit/:id' => 'ontologies#edit_view', :constraints => { id: /[^\/?]+/ } get '/ontologies/view/new/:id' => 'ontologies#new_view' - get '/ontologies/:acronym/:id/serialize/:output_format' => 'ontologies#content_finder', :id => /.+/ + get '/ontologies/:acronym/:id/serialize/:output_format' => 'ontologies#content_serializer', :id => /.+/ get '/ontologies/virtual/:ontology' => 'ontologies#virtual', :as => :ontology_virtual get '/ontologies/success/:id' => 'ontologies#submit_success' From a1dd8cbd72b1704cecb29a70955833f179c46c03 Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 16:40:51 +0200 Subject: [PATCH 26/31] in content finder don't call the backend serializer if format is html --- app/controllers/concerns/ontology_content_serializer.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/controllers/concerns/ontology_content_serializer.rb b/app/controllers/concerns/ontology_content_serializer.rb index 33ba605c7..bc1675946 100644 --- a/app/controllers/concerns/ontology_content_serializer.rb +++ b/app/controllers/concerns/ontology_content_serializer.rb @@ -8,6 +8,9 @@ def serialize_content(ontology_acronym:, concept_id:, format:) @format = format @result = "" @acronym = ontology_acronym + + return @result if format.eql?('html') + url = content_finder_url(ontology_acronym, concept_id) accept_header = content_finder_accept_header(@format) conn = Faraday.new(url: url) do |faraday| From 8e84daeef7377e70f876a26c85812ac5731801b6 Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 16:44:08 +0200 Subject: [PATCH 27/31] fix the content finder container width --- app/assets/stylesheets/content_finder.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/content_finder.scss b/app/assets/stylesheets/content_finder.scss index a64d8be51..c657858c6 100644 --- a/app/assets/stylesheets/content_finder.scss +++ b/app/assets/stylesheets/content_finder.scss @@ -17,7 +17,7 @@ .content-finder-result{ text-align: left; - max-width: 95%; + width: 95%; margin-left: auto; margin-right: auto; background-color: #fff; From 61023e1bdb9e20c99725b0870162e60ff09ca47c Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 16:47:54 +0200 Subject: [PATCH 28/31] clean unused css code for the content finder --- app/assets/stylesheets/concepts.scss | 49 ---------------------- app/assets/stylesheets/content_finder.scss | 20 --------- 2 files changed, 69 deletions(-) diff --git a/app/assets/stylesheets/concepts.scss b/app/assets/stylesheets/concepts.scss index 2d59e48c6..99f01169d 100644 --- a/app/assets/stylesheets/concepts.scss +++ b/app/assets/stylesheets/concepts.scss @@ -157,52 +157,3 @@ div.synonym-change-request button { color: var(--gray-color); margin-top: 10px; } - - - -.concepts-content-format{ - position: relative; - .justify-content-center{ - justify-content: start !important; - } - .concpets-content-format-actions{ - display: flex; - position: absolute; - right: 2%; - top: 5px; - } -} - - -.hljs-custom-prefixes{ - color: grey; -} -.hljs-custom-symbol{ - color: red; -} - -.hljs-custom-concepts{ - color: #770088; -} - - -.content-finder-logo svg { - width: 100px; - height: 100px; -} -.content-finder-logo svg path{ - fill: var(--primary-color) -} - -.hljs-subject{ - color: green; -} - -.hljs-predicate{ - color: #770088; -} - -.parse_button{ - display: flex; - justify-content: center; -} \ No newline at end of file diff --git a/app/assets/stylesheets/content_finder.scss b/app/assets/stylesheets/content_finder.scss index c657858c6..4259b5720 100644 --- a/app/assets/stylesheets/content_finder.scss +++ b/app/assets/stylesheets/content_finder.scss @@ -4,12 +4,6 @@ margin-bottom: 6px; } -.content-finder-logo{ - display: flex; - justify-content: center; - margin: 20px 0; -} - .content-finder-container .inputs > div{ margin:10px 0; text-align: center; @@ -39,24 +33,10 @@ color: #770088; } - -.content-finder-logo svg { - width: 100px; - height: 100px; -} -.content-finder-logo svg path{ - fill: var(--primary-color) -} - .hljs-subject{ color: green; } .hljs-predicate{ color: #770088; -} - -.parse_button{ - display: flex; - justify-content: center; } \ No newline at end of file From 7bad7d4f002ef37ac96df7b01a2bc24217b3cf73 Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 17:08:38 +0200 Subject: [PATCH 29/31] extract link_to_format_modal function in the concept details component --- app/components/concept_details_component.rb | 6 ++++++ .../concept_details_component.html.haml | 3 +-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/components/concept_details_component.rb b/app/components/concept_details_component.rb index 9ef4156b5..41d9def7a 100644 --- a/app/components/concept_details_component.rb +++ b/app/components/concept_details_component.rb @@ -79,6 +79,12 @@ def filter_properties(top_keys, bottom_keys, exclude_keys, concept_properties) private + def link_to_format_modal(format, icon) + link_to_modal(nil, "/ontologies/#{@acronym}/#{escape(@concept_id)}/serialize/#{format}", data: {show_modal_title_value: @concept_id, show_modal_size_value: 'modal-xl'}) do + inline_svg("icons/#{icon}.svg", width: '50px', height: '50px') + end + end + def concept_properties2hash(properties) # NOTE: example properties # diff --git a/app/components/concept_details_component/concept_details_component.html.haml b/app/components/concept_details_component/concept_details_component.html.haml index 821d9cc7d..e42cfa818 100644 --- a/app/components/concept_details_component/concept_details_component.html.haml +++ b/app/components/concept_details_component/concept_details_component.html.haml @@ -29,5 +29,4 @@ %div.d-flex.justify-content-center.p-2 - [["json","json-ld-file"], ["xml","rdf-xml-file"] , ["ntriples","ntriples-file"], ["turtle","turtle-file"]].each do |format, icon| %div.mx-4{data: {controller: "tooltip"}, title: "Export in #{format.upcase}"} - = link_to_modal(nil, "/ontologies/#{@acronym}/#{CGI.escape(@concept_id)}/serialize/#{format}", data: {show_modal_title_value: @concept_id, show_modal_size_value: 'modal-xl'}) do - = inline_svg("icons/#{icon}.svg", width: '50px', height: '50px') \ No newline at end of file + = link_to_format_modal(format, icon) \ No newline at end of file From dd98eb3ebdafa518c52c3d33f9b0fea01f09f607 Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 17:09:15 +0200 Subject: [PATCH 30/31] abstract the usage of a highlighter from the dependency highlight.js --- .../rdf_highlighter_component_controller.js | 102 ++++-------------- app/javascript/mixins/useHighLight.js | 88 +++++++++++++++ 2 files changed, 107 insertions(+), 83 deletions(-) create mode 100644 app/javascript/mixins/useHighLight.js diff --git a/app/components/display/rdf_highlighter_component/rdf_highlighter_component_controller.js b/app/components/display/rdf_highlighter_component/rdf_highlighter_component_controller.js index d045944f4..073736970 100644 --- a/app/components/display/rdf_highlighter_component/rdf_highlighter_component_controller.js +++ b/app/components/display/rdf_highlighter_component/rdf_highlighter_component_controller.js @@ -1,8 +1,6 @@ import { Controller } from '@hotwired/stimulus' import * as jsonld from 'jsonld' -import hljs from 'highlight.js/lib/core' -import xml from 'highlight.js/lib/languages/xml' -import json from 'highlight.js/lib/languages/json' +import { useHighLighter } from '../../../javascript/mixins/useHighLight' // Connects to data-controller="rdf-highlighter" export default class extends Controller { @@ -17,81 +15,22 @@ export default class extends Controller { connect () { this.formatedData = this.#formatData() + this.highlighter = useHighLighter(this.formatValue) + switch (this.formatValue) { case 'xml': - hljs.registerLanguage('xml', xml) this.showXML() break case 'json': - hljs.registerLanguage('json', json) this.showJSONLD() break case 'triples': - hljs.registerLanguage('xml', xml) this.showNTriples() break case 'ntriples': - hljs.registerLanguage('ntriples', function (hljs) { - return { - case_insensitive: true, - contains: [ - { - className: 'subject', - begin: /^<[^>]+>/, - }, - { - className: 'predicate', - begin: /<[^>]+>/, - }, - { - className: 'object', - begin: /\s([^\s]+)\s\./, - }, - hljs.COMMENT('^#', '$') - ] - }; - }); this.showNTriples() break case 'turtle': - hljs.registerLanguage('turtle', function (hljs) { - let URL_PATTERN = /(?:<[^>]*>)|(?:https?:\/\/[^\s]+)/; - - return { - case_insensitive: true, - contains: [ - { - className: 'custom-prefixes', - begin: '@prefix', - relevance: 10 - }, - { - className: 'meta', - begin: /@base/, - end: /[\r\n]|$/, - relevance: 10 - }, - { - className: 'variable', - begin: /\?[\w\d]+/ - }, - { - className: 'custom-symbol', - begin: /@?[A-Za-z_][A-Za-z0-9_]*(?= *:)/, - relevance: 10 - }, - { - className: 'custom-concepts', - begin: /:\s*(\w+)/, - relevance: 10 - }, - { - className: 'string', - begin: URL_PATTERN - } - ] - }; - }); this.showTURTLE() break } @@ -114,44 +53,42 @@ export default class extends Controller { } showNTriples () { - if(!this.hasMetadataValue){ - this.contentTarget.innerHTML = hljs.highlight(this.contentTarget.textContent, { language: 'ntriples' }).value - }else{ + if (!this.hasMetadataValue) { + this.contentTarget.innerHTML = this.highlighter.highlight(this.contentTarget.textContent, 'ntriples') + } else { this.#toggleLoader() this.#toNTriples(this.formatedData).then((nquads) => { - this.contentTarget.innerHTML = hljs.highlight(nquads, { language: 'xml' }).value + this.contentTarget.innerHTML = this.highlighter.highlight(nquads, 'ntriples') this.#toggleLoader() }) } } showXML () { - if(!this.hasMetadataValue){ - this.contentTarget.innerHTML = hljs.highlight(this.contentTarget.textContent, { language: 'xml' }).value - }else{ + if (!this.hasMetadataValue) { + this.contentTarget.innerHTML = this.highlighter.highlight(this.contentTarget.textContent, 'xml') + } else { this.#toggleLoader() - this.contentTarget.innerHTML = hljs.highlight( - this.#toXML(this.formatedData, this.contextValue), - { language: 'xml' } - ).value + const xml = this.#toXML(this.formatedData, this.contextValue) + this.contentTarget.innerHTML = this.highlighter.highlight(xml, 'xml') this.#toggleLoader() } } showJSONLD () { - if(!this.hasMetadataValue){ - this.contentTarget.innerHTML = hljs.highlight(JSON.stringify(JSON.parse(this.contentTarget.textContent), null, " "), { language: 'json' }).value - }else{ + if (!this.hasMetadataValue) { + this.contentTarget.innerHTML = this.highlighter.highlight(JSON.stringify(JSON.parse(this.contentTarget.textContent), null, ' '), 'json') + } else { this.#toggleLoader() this.#toJSONLD().then((jsonld) => { - this.contentTarget.innerHTML = hljs.highlight(JSON.stringify(jsonld, null, ' '), { language: 'json' }).value + this.contentTarget.innerHTML = this.highlighter.highlight(JSON.stringify(jsonld, null, ' '), 'json') this.#toggleLoader() }) } } - showTURTLE() { - this.contentTarget.innerHTML = hljs.highlight(this.contentTarget.textContent, { language: 'turtle' }).value + showTURTLE () { + this.contentTarget.innerHTML = this.highlighter.highlight(this.contentTarget.textContent, 'turtle') } #toggleLoader () { @@ -233,7 +170,7 @@ export default class extends Controller { const data = this.formatedData const resolveNamespace = this.namespacesValue let namespaces = {} - let xmlString = "" + let xmlString = '' delete data['@id'] delete data['@type'] @@ -241,7 +178,6 @@ export default class extends Controller { for (let prop in data) { const attr_uri = prop - // Replace the full URI by namespace:attr for (const ns in resolveNamespace) { if (prop.startsWith(resolveNamespace[ns])) { diff --git a/app/javascript/mixins/useHighLight.js b/app/javascript/mixins/useHighLight.js new file mode 100644 index 000000000..7b34116f7 --- /dev/null +++ b/app/javascript/mixins/useHighLight.js @@ -0,0 +1,88 @@ +import hljs from 'highlight.js/lib/core' +import xml from 'highlight.js/lib/languages/xml' +import json from 'highlight.js/lib/languages/json' + +class HighLighter { + constructor (highlighter, format) { + switch (format) { + case 'xml': + highlighter.registerLanguage('xml', xml) + break + case 'json': + highlighter.registerLanguage('json', json) + break + case 'triples': + case 'ntriples': + highlighter.registerLanguage('ntriples', function (hljs) { + return { + case_insensitive: true, + contains: [ + { + className: 'subject', + begin: /^<[^>]+>/, + }, + { + className: 'predicate', + begin: /<[^>]+>/, + }, + { + className: 'object', + begin: /\s([^\s]+)\s\./, + }, + hljs.COMMENT('^#', '$') + ] + } + }) + break + case 'turtle': + highlighter.registerLanguage('turtle', function (hljs) { + let URL_PATTERN = /(?:<[^>]*>)|(?:https?:\/\/[^\s]+)/ + + return { + case_insensitive: true, + contains: [ + { + className: 'custom-prefixes', + begin: '@prefix', + relevance: 10 + }, + { + className: 'meta', + begin: /@base/, + end: /[\r\n]|$/, + relevance: 10 + }, + { + className: 'variable', + begin: /\?[\w\d]+/ + }, + { + className: 'custom-symbol', + begin: /@?[A-Za-z_][A-Za-z0-9_]*(?= *:)/, + relevance: 10 + }, + { + className: 'custom-concepts', + begin: /:\s*(\w+)/, + relevance: 10 + }, + { + className: 'string', + begin: URL_PATTERN + } + ] + } + }) + break + } + this.highlighter = highlighter + } + + highlight (text, format) { + return this.highlighter.highlight(text, { language: format }).value + } +} + +export function useHighLighter (format) { + return new HighLighter(hljs, format) +} \ No newline at end of file From 136bf1d3afb1e12a66575520d0cae214b3683f2a Mon Sep 17 00:00:00 2001 From: Syphax bouazzouni Date: Sun, 7 Apr 2024 17:25:29 +0200 Subject: [PATCH 31/31] update instance details page to use the concept details component --- app/components/concept_details_component.rb | 2 +- app/controllers/instances_controller.rb | 4 +- .../instances/_instance_details.html.haml | 38 ++++++++----------- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/app/components/concept_details_component.rb b/app/components/concept_details_component.rb index 41d9def7a..c51b11260 100644 --- a/app/components/concept_details_component.rb +++ b/app/components/concept_details_component.rb @@ -10,7 +10,7 @@ class ConceptDetailsComponent < ViewComponent::Base attr_reader :concept_properties - def initialize(id:, acronym:, concept_id: nil , properties:, top_keys:, bottom_keys:, exclude_keys:) + def initialize(id:, acronym:, concept_id: nil , properties: nil, top_keys: [], bottom_keys: [], exclude_keys: []) @acronym = acronym @properties = properties @top_keys = top_keys diff --git a/app/controllers/instances_controller.rb b/app/controllers/instances_controller.rb index 8b170f5e2..26e848b25 100644 --- a/app/controllers/instances_controller.rb +++ b/app/controllers/instances_controller.rb @@ -13,15 +13,15 @@ def index_by_class end def show + get_ontology(params) @instance = get_instance_details_json(params[:ontology_id], params[:id] || params[:instance_id], {include: 'all'}) - render partial: 'instances/instance_details', layout: nil end private def get_ontology(params) - @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology]).first + @ontology = LinkedData::Client::Models::Ontology.find_by_acronym(params[:ontology] || params[:acronym] || params[:ontology_id]).first ontology_not_found(params[:ontology]) if @ontology.nil? end # json render + adding next and prev pages links diff --git a/app/views/instances/_instance_details.html.haml b/app/views/instances/_instance_details.html.haml index 9d8375872..21110aba3 100644 --- a/app/views/instances/_instance_details.html.haml +++ b/app/views/instances/_instance_details.html.haml @@ -1,26 +1,20 @@ = render_in_modal do %div - - ontology_acronym = params[:ontology_id] + - ontology_acronym = params[:ontology_id] || @ontology.acronym - filter_properties = ["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"] - = render TabsContainerComponent.new(type: 'outline') do |c| - - c.item(title: "Raw Data", selected: true) - - c.item_content do - %table.table - %tr - %td - = link_to 'type', "#" - %td{style:"word-break: break-all"} - - type = @instance.types.reject{|x| x['NamedIndividual']} - #{type.map {|cls| link_to_class(ontology_acronym,cls)}.join(', ').html_safe} - - properties = @instance[:properties].to_h.select{|k,v| !filter_properties.include? k.to_s} - - properties.each do |prop| - - if !prop[1].nil? - %tr - %td - = link_to_property(prop[0] , ontology_acronym) - %td{style:"word-break: break-all"} - = prop[1].map { |value| instance_property_value(value , ontology_acronym) }.join(', ').html_safe - - - if params["id"] - = content_formats(concept_id: params["id"], acronym: ontology_acronym, c: c) \ No newline at end of file + = render ConceptDetailsComponent.new(id:'instance-details', acronym: ontology_acronym, concept_id: @instance["@id"]) do |c| + - c.header(stripped: true) do |t| + - t.add_row({th: t("collections.id")}, {td: link_to_with_actions(@instance["@id"]) }) + + - label = @instance['label'] || @instance['prefLabel'] + - unless label.blank? + - t.add_row({th: 'label' }, {td: label.join(',').html_safe}) + + - t.add_row({th: 'type' }) do |r| + - r.td do + = @instance.types.reject{|x| x['NamedIndividual']}.map {|cls| link_to_class(ontology_acronym,cls)}.join(', ').html_safe + - properties = @instance[:properties].to_h.select{|k,v| !filter_properties.include? k.to_s} + - properties.each do |prop| + - if !prop[1].nil? + - t.add_row({th: link_to_property(prop[0], ontology_acronym)}, {td: prop[1].map { |value| instance_property_value(value , ontology_acronym) }.join(', ').html_safe}) \ No newline at end of file