elixir-toolkit-theme-plugins 0.1.10 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd0a3fc0a5890ba4e7680359f3fa18d06fe7de42a11813e73cf6db604a6205c6
4
- data.tar.gz: 965789747727984d24d71b4963ccf58422b0a597450056c0a76a25e8d4d04cf7
3
+ metadata.gz: 989e78f7e6995bb095549eb94361934f40c944b45c207f24820d91c518006280
4
+ data.tar.gz: ae7e2b72145732bcdcfc52cabb325eae06c1910a5de7fe2781b958184f93e318
5
5
  SHA512:
6
- metadata.gz: dffa98f82db6ac590161b0a4e38a60ed70495358f9a4064b144c9e50c642f70738d044c1967692c5a2c2ee6eb37f5f89778e2d436fc67fefb28542bbd2ef5673
7
- data.tar.gz: 32b9bbbe4a80b61f70bb2145c910cf50a81a656c5b6053ce989e8cb129898f24cc5d00a2b4db1a800728167285df48df05096c839c50a6e95f2d58b3ee55e921
6
+ metadata.gz: 2869b8b726d00bb4c58e105c97563e61836f122b449decfa13851d8f7a09d1c489b96121f41af6c0a97c6fbc9f34809b2c64ffb0907943fe2f3041e8a82150f9
7
+ data.tar.gz: 07eea18abf4bbe4bb3bcca9303bc4a21c121a1d5d6c98309c8d8df3a3383dc468f9fcb2e1be910b6b289e7dc827e7cd939b67b510bd97c72fd5e8cc663a27fb6
@@ -11,11 +11,13 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
 
13
13
  steps:
14
- - uses: actions/checkout@v3
15
- - name: Set up Ruby 3.1
16
- uses: actions/setup-ruby@v1
14
+ - uses: actions/checkout@v5
15
+ - name: Set up Ruby 3.3
16
+ uses: ruby/setup-ruby@v1.204.0
17
17
  with:
18
- ruby-version: 3.1
18
+ ruby-version: '3.3'
19
+ bundler-cache: true
20
+ cache-version: 0
19
21
 
20
22
  - name: Publish to GPR
21
23
  run: |
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.name = "elixir-toolkit-theme-plugins"
10
10
  spec.version = Jekyll::Ett::VERSION.dup
11
11
  spec.authors = ["bedroesb", "janslifka", "MarekSuchanek"]
12
- spec.email = ["bedro@psb.vib-ugent.be\n"]
12
+ spec.email = ["bert.droesbeke@vib.be\n"]
13
13
 
14
14
  spec.summary = "Plugins to work together with ELIXIR Toolkit theme"
15
15
  spec.homepage = "https://elixir-belgium.github.io/elixir-toolkit-theme-plugins/"
@@ -1,15 +1,25 @@
1
1
  require "jekyll"
2
-
2
+ require "set"
3
3
 
4
4
  module Jekyll
5
5
  class Ett
6
6
  module ToolTableFilter
7
7
  def add_related_pages(data)
8
+ return [] if data.nil?
9
+ return data unless data.is_a?(Array)
10
+
8
11
  load_page_data
12
+ instances_index = Jekyll::Ett::ToolTag.instances_index
13
+
14
+ data.each do |tool|
15
+ next unless tool.is_a?(Hash)
16
+
17
+ if tool["id"] && @related_pages[tool["id"]]
18
+ tool["related_pages"] = @related_pages[tool["id"]].to_a
19
+ end
9
20
 
10
- for tool in data
11
- if tool['id'] && @related_pages[tool['id']]
12
- tool['related_pages'] = @related_pages[tool['id']].to_a
21
+ if tool["id"] && instances_index[tool["id"]]
22
+ tool["instances"] = instances_index[tool["id"]]
13
23
  end
14
24
  end
15
25
 
@@ -21,20 +31,22 @@ module Jekyll
21
31
  def load_page_data
22
32
  @related_pages = {}
23
33
  pages_path = File.join(Dir.pwd, "**", "*.md")
34
+
24
35
  Dir.glob(pages_path).each do |f|
25
36
  file = File.read(f)
26
37
  page_id_matches = file.match(/page_id:\s*(\w+)/)
27
-
28
- if page_id_matches
29
- page_id = page_id_matches[1]
30
- file.scan(/\{%\s*tool\s*"([^"]+)"\s*%}/).flatten.each do |m|
31
- @related_pages[m] = Set[] unless @related_pages[m]
32
- @related_pages[m].add(page_id)
33
- end
38
+ next unless page_id_matches
39
+
40
+ page_id = page_id_matches[1]
41
+
42
+ file.scan(/\{%\s*tool\s*"([^"]+)"\s*%}/).flatten.each do |m|
43
+ @related_pages[m] ||= Set.new
44
+ @related_pages[m].add(page_id)
34
45
  end
35
46
  end
36
47
  end
37
48
  end
38
49
  end
50
+
39
51
  Liquid::Template.register_filter(Jekyll::Ett::ToolTableFilter)
40
52
  end
@@ -1,75 +1,188 @@
1
1
  require "jekyll"
2
-
2
+ require "yaml"
3
3
 
4
4
  module Jekyll
5
5
  class Ett
6
6
  class ToolTag < Liquid::Tag
7
+ @@instances_by_tool = nil
8
+ TOOLS_PATH = File.join(Dir.pwd, "_data", "tool_and_resource_list.yml")
9
+
7
10
  def initialize(tagName, content, tokens)
8
11
  super
9
12
  @content = content
10
13
  load_tools
14
+ load_instances
11
15
  end
12
16
 
13
17
  def load_tools
14
- tools_path = File.join(Dir.pwd, "_data", "tool_and_resource_list.yml")
15
- @tools = YAML.load(File.read(tools_path))
18
+ @tools = YAML.load(File.read(TOOLS_PATH))
19
+ end
20
+
21
+ # --- class-level accessor used by both tag and filters ---
22
+ def self.instances_index
23
+ @@instances_by_tool ||= build_instances_index
24
+ end
25
+
26
+ def self.build_instances_index
27
+ instances_by_tool = Hash.new { |h, k| h[k] = [] }
28
+
29
+ tools = YAML.load(File.read(TOOLS_PATH))
30
+
31
+ # 1) instances from national_resources in page front matter
32
+ pages_path = File.join(Dir.pwd, "**", "*.md")
33
+ files = Dir.glob(pages_path)
34
+
35
+ files.each do |f|
36
+ raw = File.read(f)
37
+ fm = extract_front_matter(raw)
38
+ next unless fm.is_a?(Hash)
39
+
40
+ country_code = (fm["country_code"] || "").to_s
41
+ country_name = (fm["title"] || "").to_s
42
+ resources = fm["national_resources"] || []
43
+ next unless resources.is_a?(Array)
44
+
45
+ resources.each do |res|
46
+ next unless res.is_a?(Hash)
47
+ inst_of = res["instance_of"]
48
+ next if inst_of.nil? || inst_of == "NA"
49
+
50
+ instances_by_tool[inst_of] << {
51
+ "name" => (res["name"] || res["id"] || "Instance"),
52
+ "url" => res["url"],
53
+ "id" => res["id"],
54
+ "country_code" => country_code,
55
+ "country_name" => country_name,
56
+ "page_path" => f
57
+ }
58
+ end
59
+ end
60
+
61
+ # 2) instances from tool_and_resource_list.yml itself
62
+ (tools || []).each do |tool|
63
+ parent_id = tool["instance_of"]
64
+ next if parent_id.nil? || parent_id == "NA"
65
+
66
+ instances_by_tool[parent_id] << {
67
+ "name" => (tool["name"] || tool["id"] || "Instance"),
68
+ "url" => tool["url"],
69
+ "id" => tool["id"],
70
+ "country_code" => "",
71
+ "country_name" => "",
72
+ "page_path" => nil
73
+ }
74
+ end
75
+
76
+ instances_by_tool
77
+ end
78
+
79
+ # shared front-matter parser (class-level)
80
+ def self.extract_front_matter(file_string)
81
+ if file_string =~ /\A---\s*\n(.*?)\n---\s*\n/m
82
+ yaml = Regexp.last_match(1)
83
+ YAML.safe_load(yaml, permitted_classes: [Date, Time], aliases: true)
84
+ else
85
+ nil
86
+ end
87
+ rescue
88
+ nil
89
+ end
90
+
91
+ # instance-level wrapper uses the shared index
92
+ def load_instances
93
+ @instances_by_tool = self.class.instances_index
16
94
  end
17
95
 
18
96
  def render(context)
19
- tool = find_tool(context[@content.strip])
20
- tags = create_tags(tool)
97
+ site = context.registers[:site]
98
+ tool_id_from_liquid = context[@content.strip]
99
+ tool = find_tool(tool_id_from_liquid)
100
+ tags = create_tags(tool, site)
21
101
  %Q{<a
22
102
  tabindex="0"
23
103
  class="tool"
24
- aria-description="#{tool["description"]}"
104
+ aria-description="#{html_escape(tool["description"])}"
25
105
  data-bs-toggle="popover"
26
106
  data-bs-placement="bottom"
27
107
  data-bs-trigger="focus"
28
- data-bs-content="<h5>#{tool["name"]}</h5><div class='mb-2'>#{tool["description"]}</div>#{tags}"
108
+ data-bs-content="<h5>#{html_escape(tool["name"])}</h5><div class='mb-2'>#{html_escape(tool["description"])}</div><div class='d-flex flex-wrap gap-1'>#{tags}</div>"
29
109
  data-bs-template="<div class='popover popover-tool' role='tooltip'><div class='popover-arrow'></div><h3 class='popover-header'></h3><div class='popover-body'></div></div>"
30
110
  data-bs-html="true"
31
- ><i class="fa-solid fa-wrench fa-sm me-2"></i>#{ tool["name"] }</a>}
111
+ ><i class="fa-solid fa-wrench fa-sm me-2"></i>#{ html_escape(tool["name"]) }</a>}
32
112
  end
33
113
 
34
114
  def find_tool(tool_id)
35
115
  tool = @tools.find { |t| t["id"] == tool_id.strip }
36
116
  return tool if tool
37
-
38
117
  raise Exception.new "Undefined tool ID: #{tool_id}"
39
118
  end
40
119
 
41
- def create_tags(tool)
120
+ def create_tags(tool, site)
42
121
  tags = ""
43
122
  tags << create_tag("#{tool["url"]}", "fa-link", "Website")
44
123
  if tool["registry"]
45
124
  registry = tool["registry"]
125
+ tags << create_tag("https://bio.tools/#{registry["biotools"]}", "fa-info", "Tool info") if registry["biotools"] && registry["biotools"] != "NA"
126
+ tags << create_tag("https://fairsharing.org/FAIRsharing.#{registry["fairsharing"]}", "fa-database", "Standards/Databases") if registry["fairsharing"] && registry["fairsharing"] != "NA"
127
+ tags << create_tag("https://fairsharing.org/#{registry["fairsharing-coll"]}", "fa-database", "Standards/Databases") if registry["fairsharing-coll"] && registry["fairsharing-coll"] != "NA"
128
+ tags << create_tag("https://tess.elixir-europe.org/search?q=#{registry["tess"]}", "fa-graduation-cap", "Training") if registry["tess"] && registry["tess"] != "NA"
129
+ tags << create_tag("https://europepmc.org/article/MED/#{registry["europmc"]}", "fa-book", "Publication") if registry["europmc"] && registry["europmc"] != "NA"
130
+ end
46
131
 
47
- if registry["biotools"] and registry["biotools"] != "NA"
48
- tags << create_tag("https://bio.tools/#{registry["biotools"]}", "fa-info", "Tool info")
49
- end
132
+ instances = @instances_by_tool[tool["id"]]
133
+ tags << instances_dropdown(instances, site, tool["id"]) if instances && !instances.empty?
134
+ tags
135
+ end
50
136
 
51
- if registry["fairsharing"] and registry["fairsharing"] != "NA"
52
- tags << create_tag("https://fairsharing.org/FAIRsharing.#{registry["fairsharing"]}", "fa-database", "Standards/Databases")
53
- end
137
+ def create_tag(url, icon, label)
138
+ "<a href='#{html_attr(url)}' target='_blank' rel='noopener'><span class='badge bg-dark text-white hover-primary'><i class='fa-solid #{icon} me-2'></i>#{html_escape(label)}</span></a>"
139
+ end
54
140
 
55
- if registry["fairsharing-coll"] and registry["fairsharing-coll"] != "NA"
56
- tags << create_tag("https://fairsharing.org/#{registry["fairsharing-coll"]}", "fa-database", "Standards/Databases")
57
- end
141
+ def instances_dropdown(instances, site, tool_id)
142
+ dd_id = "instances-dd-#{tool_id}-#{object_id}"
58
143
 
59
- if registry["tess"] and registry["tess"] != "NA"
60
- tags << create_tag("https://tess.elixir-europe.org/search?q=#{registry["tess"]}", "fa-graduation-cap", "Training")
61
- end
144
+ items = instances.map do |inst|
145
+ href = inst["url"] || resolve_page_url(inst["page_path"], site) || "#"
146
+ flag = inst["country_code"].to_s.strip.empty? ? "" : "<span class='flag-icon ms-2 shadow-sm flag-icon-#{inst["country_code"].downcase}'></span>"
147
+ name = html_escape(inst["name"])
148
+ url = html_attr(href)
149
+ "<li><a class='dropdown-item' href='#{url}' target='_blank' rel='noopener'>#{name} #{flag}</a></li>"
150
+ end.join
62
151
 
63
- if registry["europmc"] and registry["europmc"] != "NA"
64
- tags << create_tag("https://europepmc.org/article/MED/#{registry["europmc"]}", "fa-book", "Publication")
65
- end
152
+ %Q{
153
+ <div class='dropdown d-inline-block'>
154
+ <button class='btn btn-badge btn-outline-primary dropdown-toggle'
155
+ type='button'
156
+ id='#{dd_id}'
157
+ data-bs-toggle='dropdown'
158
+ aria-expanded='false'>
159
+ <i class='fa-solid fa-globe me-2'></i>Instances
160
+ </button>
161
+ <ul class='dropdown-menu' aria-labelledby='#{dd_id}'>
162
+ #{items}
163
+ </ul>
164
+ </div>
165
+ }
166
+ end
66
167
 
67
- end
68
- tags
168
+ # Resolve a site-relative URL from an absolute file path stored in page_path
169
+ def resolve_page_url(abs_path, site)
170
+ return nil unless abs_path && site
171
+ rel = abs_path.sub(%r{\A#{Regexp.escape(Dir.pwd)}/?}, "")
172
+ doc = (site.pages + site.collections.values.flat_map(&:docs)).find { |p| p.path == rel }
173
+ doc&.url
174
+ rescue
175
+ nil
69
176
  end
70
177
 
71
- def create_tag(url, icon, label)
72
- "<a href='#{url}' target='_blank' rel='noopener' class='mt-2 me-2'><span class='badge bg-dark text-white hover-primary'><i class='fa-solid #{icon} me-2'></i>#{label}</span></a>"
178
+ # --- escaping helpers ---
179
+
180
+ def html_escape(s)
181
+ (s || "").to_s.gsub("&","&amp;").gsub("<","&lt;").gsub(">","&gt;").gsub('"',"&quot;")
182
+ end
183
+
184
+ def html_attr(s)
185
+ html_escape(s).gsub("'","&#39;")
73
186
  end
74
187
  end
75
188
  end
@@ -1,5 +1,5 @@
1
1
  module Jekyll
2
2
  class Ett
3
- VERSION = '0.1.10'
3
+ VERSION = '1.1.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elixir-toolkit-theme-plugins
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bedroesb
8
8
  - janslifka
9
9
  - MarekSuchanek
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-03-20 00:00:00.000000000 Z
13
+ date: 2025-12-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: jekyll
@@ -46,9 +46,9 @@ dependencies:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
48
  version: '12.0'
49
- description:
49
+ description:
50
50
  email:
51
- - 'bedro@psb.vib-ugent.be
51
+ - 'bert.droesbeke@vib.be
52
52
 
53
53
  '
54
54
  executables: []
@@ -69,7 +69,7 @@ homepage: https://elixir-belgium.github.io/elixir-toolkit-theme-plugins/
69
69
  licenses:
70
70
  - MIT
71
71
  metadata: {}
72
- post_install_message:
72
+ post_install_message:
73
73
  rdoc_options: []
74
74
  require_paths:
75
75
  - lib
@@ -84,8 +84,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  - !ruby/object:Gem::Version
85
85
  version: '0'
86
86
  requirements: []
87
- rubygems_version: 3.3.26
88
- signing_key:
87
+ rubygems_version: 3.5.22
88
+ signing_key:
89
89
  specification_version: 4
90
90
  summary: Plugins to work together with ELIXIR Toolkit theme
91
91
  test_files: []