From 571c3882f3ff45c93ce1fe67bf7c62996ca6e2f2 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Thu, 30 May 2024 17:42:40 +0800 Subject: [PATCH 01/19] Add project_records method to liquid assigns service --- app/services/liquid_assigns_service.rb | 25 +++++++++++++++++++- spec/services/liquid_assigns_service_spec.rb | 13 ++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/app/services/liquid_assigns_service.rb b/app/services/liquid_assigns_service.rb index 79f8d48ba..454708076 100644 --- a/app/services/liquid_assigns_service.rb +++ b/app/services/liquid_assigns_service.rb @@ -6,7 +6,7 @@ def initialize(project) end def assigns - result = { 'project' => ProjectDrop.new(project) } + result = project_drops result.merge!(assigns_pro) if defined?(Dradis::Pro) result end @@ -15,4 +15,27 @@ def assigns def assigns_pro end + + def project_drops + { + 'evidence' => project_records(type: :evidence), + 'issues' => project_records(type: :issue), + 'nodes' => project_records(type: :node), + 'notes' => project_records(type: :note), + 'project' => ProjectDrop.new(project), + 'tags' => project_records(type: :tag) + } + end + + def project_records(type:) + records = project.send(type.to_s.pluralize) + records = records.user_nodes if type == :node + + cache_key = "liquid-project-#{type.to_s.pluralize}#{records.maximum(:updated_at)}/#{records.pluck(:id).join('-')}" + drop_class = "#{type.to_s.capitalize}Drop".constantize + + Rails.cache.fetch(cache_key) do + records.map { |record| drop_class.new(record) } + end + end end diff --git a/spec/services/liquid_assigns_service_spec.rb b/spec/services/liquid_assigns_service_spec.rb index 581bb8a52..a26da85ef 100644 --- a/spec/services/liquid_assigns_service_spec.rb +++ b/spec/services/liquid_assigns_service_spec.rb @@ -5,8 +5,21 @@ let(:liquid_assigns) { described_class.new(project).assigns } + before do + node = create(:node, project: project) + issue = create(:issue, node: project.issue_library) + create(:evidence, issue: issue, node: node) + create(:note, node: node) + create(:tag) + end + it 'builds a hash of liquid assigns' do expect(liquid_assigns['project'].name).to eq(project.name) + expect(liquid_assigns['issues'].map(&:title)).to eq(project.issues.map(&:title)) + expect(liquid_assigns['evidence'].map(&:title)).to eq(project.evidence.map(&:title)) + expect(liquid_assigns['nodes'].map(&:label)).to eq(project.nodes.user_nodes.map(&:label)) + expect(liquid_assigns['notes'].map(&:title)).to eq(project.notes.map(&:title)) + expect(liquid_assigns['tags'].map(&:display_name)).to eq(project.tags.map(&:display_name)) end context 'with pro records', skip: !defined?(Dradis::Pro) do From 418d85e3948086f8bff0d6238c3264c5e7082ee6 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Thu, 30 May 2024 18:10:46 +0800 Subject: [PATCH 02/19] Update changelog --- CHANGELOG | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e3e1a5825..c4c00b69f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,5 @@ [v#.#.#] ([month] [YYYY]) - - [entity]: - - [future tense verb] [feature] + - Liquid: Make project-level drops available for Liquid syntax - Upgraded gems: nokogiri, rexml - Bugs fixes: - [entity]: From ea2baa0c9793a2b588f167786d2dd9ad55704e81 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Thu, 30 May 2024 18:27:52 +0800 Subject: [PATCH 03/19] Update spec for parity --- spec/services/liquid_assigns_service_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/services/liquid_assigns_service_spec.rb b/spec/services/liquid_assigns_service_spec.rb index a26da85ef..3ff178fc3 100644 --- a/spec/services/liquid_assigns_service_spec.rb +++ b/spec/services/liquid_assigns_service_spec.rb @@ -29,11 +29,14 @@ report_content = project.content_library report_content.properties = { 'dradis.project' => project.name } report_content.save + + create(:content_block, project: project) end it 'builds a hash with Dradis::Pro assigns' do expect(liquid_assigns['document_properties'].available_properties).to eq({ 'dradis.project' => project.name }) expect(liquid_assigns['team'].name).to eq(project.team.name) + expect(liquid_assigns['content_blocks'].map(&:content)).to eq(project.content_blocks.map(&:content)) end end end From c148482534e7629070fa79d64b75f1a2756eefd3 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Thu, 30 May 2024 18:33:08 +0800 Subject: [PATCH 04/19] Use camelize --- app/services/liquid_assigns_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/liquid_assigns_service.rb b/app/services/liquid_assigns_service.rb index 454708076..42818c93d 100644 --- a/app/services/liquid_assigns_service.rb +++ b/app/services/liquid_assigns_service.rb @@ -32,7 +32,7 @@ def project_records(type:) records = records.user_nodes if type == :node cache_key = "liquid-project-#{type.to_s.pluralize}#{records.maximum(:updated_at)}/#{records.pluck(:id).join('-')}" - drop_class = "#{type.to_s.capitalize}Drop".constantize + drop_class = "#{type.to_s.camelize}Drop".constantize Rails.cache.fetch(cache_key) do records.map { |record| drop_class.new(record) } From 8929784ce948a6a712a0d35cb685bae046fc41e6 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Fri, 31 May 2024 16:31:28 +0800 Subject: [PATCH 05/19] Simplify cache key --- app/services/liquid_assigns_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/liquid_assigns_service.rb b/app/services/liquid_assigns_service.rb index 42818c93d..f0cb4a343 100644 --- a/app/services/liquid_assigns_service.rb +++ b/app/services/liquid_assigns_service.rb @@ -31,7 +31,7 @@ def project_records(type:) records = project.send(type.to_s.pluralize) records = records.user_nodes if type == :node - cache_key = "liquid-project-#{type.to_s.pluralize}#{records.maximum(:updated_at)}/#{records.pluck(:id).join('-')}" + cache_key = "liquid-project-#{project.id}-#{type.to_s.pluralize}:#{records.maximum(:updated_at).to_i}-#{records.count}" drop_class = "#{type.to_s.camelize}Drop".constantize Rails.cache.fetch(cache_key) do From 825eb27c3cf4c936c46e5eedb076f9085416f5d1 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Mon, 3 Jun 2024 16:53:40 +0800 Subject: [PATCH 06/19] Apply requested changes --- app/services/liquid_assigns_service.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/services/liquid_assigns_service.rb b/app/services/liquid_assigns_service.rb index f0cb4a343..ceed78075 100644 --- a/app/services/liquid_assigns_service.rb +++ b/app/services/liquid_assigns_service.rb @@ -6,7 +6,7 @@ def initialize(project) end def assigns - result = project_drops + result = project_assigns result.merge!(assigns_pro) if defined?(Dradis::Pro) result end @@ -16,23 +16,23 @@ def assigns def assigns_pro end - def project_drops + def project_assigns { - 'evidence' => project_records(type: :evidence), - 'issues' => project_records(type: :issue), - 'nodes' => project_records(type: :node), - 'notes' => project_records(type: :note), + 'evidences' => cached_drops(project.evidence), + 'issues' => cached_drops(project.issues), + 'nodes' => cached_drops(project.nodes.user_nodes), + 'notes' => cached_drops(project.notes), 'project' => ProjectDrop.new(project), - 'tags' => project_records(type: :tag) + 'tags' => cached_drops(project.tags) } end - def project_records(type:) - records = project.send(type.to_s.pluralize) - records = records.user_nodes if type == :node + def cached_drops(records) + return [] if records.empty? - cache_key = "liquid-project-#{project.id}-#{type.to_s.pluralize}:#{records.maximum(:updated_at).to_i}-#{records.count}" - drop_class = "#{type.to_s.camelize}Drop".constantize + type = records.first.class.to_s.underscore + cache_key = "liquid-project-#{project.id}-#{type.pluralize}:#{records.maximum(:updated_at).to_i}-#{records.count}" + drop_class = "#{type.camelize}Drop".constantize Rails.cache.fetch(cache_key) do records.map { |record| drop_class.new(record) } From ce03ecb209184efefcb0b5f6533a32ce5aff46ec Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Mon, 3 Jun 2024 17:06:06 +0800 Subject: [PATCH 07/19] Update assigns spec --- spec/services/liquid_assigns_service_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/liquid_assigns_service_spec.rb b/spec/services/liquid_assigns_service_spec.rb index 3ff178fc3..418cdc0c3 100644 --- a/spec/services/liquid_assigns_service_spec.rb +++ b/spec/services/liquid_assigns_service_spec.rb @@ -16,7 +16,7 @@ it 'builds a hash of liquid assigns' do expect(liquid_assigns['project'].name).to eq(project.name) expect(liquid_assigns['issues'].map(&:title)).to eq(project.issues.map(&:title)) - expect(liquid_assigns['evidence'].map(&:title)).to eq(project.evidence.map(&:title)) + expect(liquid_assigns['evidences'].map(&:title)).to eq(project.evidence.map(&:title)) expect(liquid_assigns['nodes'].map(&:label)).to eq(project.nodes.user_nodes.map(&:label)) expect(liquid_assigns['notes'].map(&:title)).to eq(project.notes.map(&:title)) expect(liquid_assigns['tags'].map(&:display_name)).to eq(project.tags.map(&:display_name)) From 305cab0150ac95663b69c053781604dfe32263d9 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Tue, 4 Jun 2024 15:31:18 +0800 Subject: [PATCH 08/19] Parse liquid asynchronously in tylium --- app/assets/javascripts/tylium.js | 1 + .../javascripts/tylium/modules/liquid_async.js | 18 ++++++++++++++++++ .../concerns/liquid_enabled_resource.rb | 2 ++ app/views/evidence/show.html.erb | 5 ++--- app/views/issues/show.html.erb | 4 ++-- app/views/notes/show.html.erb | 4 ++-- app/views/qa/issues/show.html.erb | 4 ++-- 7 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 app/assets/javascripts/tylium/modules/liquid_async.js diff --git a/app/assets/javascripts/tylium.js b/app/assets/javascripts/tylium.js index d05374de6..a2f393ba6 100644 --- a/app/assets/javascripts/tylium.js +++ b/app/assets/javascripts/tylium.js @@ -51,6 +51,7 @@ //= require tylium/modules/export //= require tylium/modules/fileupload //= require tylium/modules/issues +//= require tylium/modules/liquid_async //= require tylium/modules/nodes //= require tylium/modules/search //= require tylium/modules/sidebar diff --git a/app/assets/javascripts/tylium/modules/liquid_async.js b/app/assets/javascripts/tylium/modules/liquid_async.js new file mode 100644 index 000000000..aee7cf23b --- /dev/null +++ b/app/assets/javascripts/tylium/modules/liquid_async.js @@ -0,0 +1,18 @@ +document.addEventListener('turbolinks:load', function () { + $('[data-behavior~=liquid-async]').each(function() { + var that = this, + data = { text: $(that).attr('data-content') }; + + $.ajax($(that).attr('data-path'), { + method: 'POST', + headers: { + "Accept": "text/html", + "Content-Type": "application/json" + }, + data: JSON.stringify(data) + }). + done(function(html){ + $(that).html(html); + }); + }); +}); diff --git a/app/controllers/concerns/liquid_enabled_resource.rb b/app/controllers/concerns/liquid_enabled_resource.rb index a8f8216e3..e23863ce9 100644 --- a/app/controllers/concerns/liquid_enabled_resource.rb +++ b/app/controllers/concerns/liquid_enabled_resource.rb @@ -15,6 +15,8 @@ def liquid_resource_assigns end def preview + # Artificial load time for testing + sleep 3 @text = params[:text] render 'markup/preview', layout: false end diff --git a/app/views/evidence/show.html.erb b/app/views/evidence/show.html.erb index 15721d50c..b1576fd03 100644 --- a/app/views/evidence/show.html.erb +++ b/app/views/evidence/show.html.erb @@ -32,9 +32,8 @@ Evidence for this instance <%= render partial: 'actions' %> - -
- <%= markup(@evidence.content, liquid: true) %> +
+ <%= markup(@evidence.content) %>
Author: <%= @evidence.author || 'n/a' %> diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb index fedc5f79b..6ff2ec3d3 100644 --- a/app/views/issues/show.html.erb +++ b/app/views/issues/show.html.erb @@ -35,8 +35,8 @@ <%= @issue.title %> <%= render partial: 'actions' %> -
- <%= markup(@issue.text, liquid: true) %> +
+ <%= markup(@issue.text) %>
Author: <%= @issue.author || 'n/a' %> diff --git a/app/views/notes/show.html.erb b/app/views/notes/show.html.erb index 95c269968..2dcdf7714 100644 --- a/app/views/notes/show.html.erb +++ b/app/views/notes/show.html.erb @@ -23,8 +23,8 @@ <%= @note.title %> <%= render partial: 'actions' %> -
- <%= markup(@note.text, liquid: true) %> +
+ <%= markup(@note.text) %>
Author: <%= @note.author || 'n/a' %> diff --git a/app/views/qa/issues/show.html.erb b/app/views/qa/issues/show.html.erb index 7e1b2fb37..d7b32177c 100644 --- a/app/views/qa/issues/show.html.erb +++ b/app/views/qa/issues/show.html.erb @@ -29,8 +29,8 @@ -
- <%= markup(@issue.text, liquid: true) %> +
+ <%= markup(@issue.text) %>
Author: <%= @issue.author || 'n/a' %> From aeddb6b51813eb83480864a8c7be521438e61ebc Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Tue, 4 Jun 2024 16:01:13 +0800 Subject: [PATCH 09/19] Fix path for notes --- app/views/notes/show.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/notes/show.html.erb b/app/views/notes/show.html.erb index 2dcdf7714..6772b83a2 100644 --- a/app/views/notes/show.html.erb +++ b/app/views/notes/show.html.erb @@ -23,7 +23,7 @@ <%= @note.title %> <%= render partial: 'actions' %> -
+
<%= markup(@note.text) %>
From b0c2a9b6770e35d69966f19d8df91af14ef2913f Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Tue, 4 Jun 2024 18:54:35 +0800 Subject: [PATCH 10/19] Use fetch --- app/assets/javascripts/tylium/modules/liquid_async.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/tylium/modules/liquid_async.js b/app/assets/javascripts/tylium/modules/liquid_async.js index aee7cf23b..d1cc4984a 100644 --- a/app/assets/javascripts/tylium/modules/liquid_async.js +++ b/app/assets/javascripts/tylium/modules/liquid_async.js @@ -3,15 +3,17 @@ document.addEventListener('turbolinks:load', function () { var that = this, data = { text: $(that).attr('data-content') }; - $.ajax($(that).attr('data-path'), { + fetch($(that).attr('data-path'), { method: 'POST', headers: { "Accept": "text/html", - "Content-Type": "application/json" + "Content-Type": "application/json", + "X-CSRF-Token": $('meta[name="csrf-token"]').attr('content') }, - data: JSON.stringify(data) + body: JSON.stringify(data) }). - done(function(html){ + then(response => response.text()). + then(function(html) { $(that).html(html); }); }); From 365e714a606045180698922616c3b49a30dc3a40 Mon Sep 17 00:00:00 2001 From: Matt Budz Date: Tue, 4 Jun 2024 16:41:06 +0200 Subject: [PATCH 11/19] add `liquid_loading` partial --- app/assets/stylesheets/tylium/modules.scss | 1 + app/helpers/application_helper.rb | 6 +++--- app/views/shared/_liquid_loading.html.erb | 8 ++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 app/views/shared/_liquid_loading.html.erb diff --git a/app/assets/stylesheets/tylium/modules.scss b/app/assets/stylesheets/tylium/modules.scss index ca1fd5cbf..7553cf419 100644 --- a/app/assets/stylesheets/tylium/modules.scss +++ b/app/assets/stylesheets/tylium/modules.scss @@ -149,6 +149,7 @@ } .header-underline { + align-items: center; border-bottom: 1px solid $borderColor; display: flex; margin-bottom: 0.5em; diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index d0d4e237c..e4ba04e65 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,5 +1,5 @@ module ApplicationHelper # :nodoc: - def markup(text, options={}) + def markup(text, options = {}) return unless text.present? context = {} @@ -38,8 +38,8 @@ def render_view_hooks(partial, locals: {}, feature: :addon) ;nil end - def spinner_tag(spinner_class: 'text-primary') - content_tag :div, class: 'd-flex align-items-center justify-content-center spinner-container' do + def spinner_tag(spinner_class: 'text-primary', align: 'center', inline: false) + content_tag :div, class: "#{inline ? 'd-inline-flex' : 'd-flex' } align-items-center justify-content-#{align} spinner-container" do content_tag :div, nil, class: "spinner-border #{spinner_class}" end end diff --git a/app/views/shared/_liquid_loading.html.erb b/app/views/shared/_liquid_loading.html.erb new file mode 100644 index 000000000..fa5c8d635 --- /dev/null +++ b/app/views/shared/_liquid_loading.html.erb @@ -0,0 +1,8 @@ +
+ - + <%= spinner_tag spinner_class: 'spinner-border-sm text-primary', align: 'start', inline: true %> + Loading liquid dynamic content +
From 340196802e945db42005492a93b414b2a70fd3c9 Mon Sep 17 00:00:00 2001 From: Matt Budz Date: Tue, 4 Jun 2024 16:46:13 +0200 Subject: [PATCH 12/19] use partial in views --- app/views/evidence/show.html.erb | 1 + app/views/issues/show.html.erb | 1 + app/views/notes/show.html.erb | 1 + app/views/qa/issues/show.html.erb | 1 + 4 files changed, 4 insertions(+) diff --git a/app/views/evidence/show.html.erb b/app/views/evidence/show.html.erb index b1576fd03..9dbfddc86 100644 --- a/app/views/evidence/show.html.erb +++ b/app/views/evidence/show.html.erb @@ -30,6 +30,7 @@

Evidence for this instance + <%= render partial: 'shared/liquid_loading' %> <%= render partial: 'actions' %>

diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb index 6ff2ec3d3..1ebbe1fc5 100644 --- a/app/views/issues/show.html.erb +++ b/app/views/issues/show.html.erb @@ -33,6 +33,7 @@

<%= @issue.title %> + <%= render partial: 'shared/liquid_loading' %> <%= render partial: 'actions' %>

diff --git a/app/views/notes/show.html.erb b/app/views/notes/show.html.erb index 6772b83a2..db589456c 100644 --- a/app/views/notes/show.html.erb +++ b/app/views/notes/show.html.erb @@ -21,6 +21,7 @@

<%= @note.title %> + <%= render partial: 'shared/liquid_loading' %> <%= render partial: 'actions' %>

diff --git a/app/views/qa/issues/show.html.erb b/app/views/qa/issues/show.html.erb index d7b32177c..f834f9262 100644 --- a/app/views/qa/issues/show.html.erb +++ b/app/views/qa/issues/show.html.erb @@ -21,6 +21,7 @@

<%= @issue.title %> + <%= render partial: 'shared/liquid_loading' %> <%= link_to edit_project_qa_issue_path(current_project, @issue) do %> From 499c2ac94a4e34ffa5b032ffe3b1832ce2c95595 Mon Sep 17 00:00:00 2001 From: Matt Budz Date: Tue, 4 Jun 2024 16:51:15 +0200 Subject: [PATCH 13/19] hide spinner once liquid is rendered --- .../tylium/modules/liquid_async.js | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/tylium/modules/liquid_async.js b/app/assets/javascripts/tylium/modules/liquid_async.js index d1cc4984a..c3551f3a0 100644 --- a/app/assets/javascripts/tylium/modules/liquid_async.js +++ b/app/assets/javascripts/tylium/modules/liquid_async.js @@ -1,20 +1,22 @@ document.addEventListener('turbolinks:load', function () { - $('[data-behavior~=liquid-async]').each(function() { - var that = this, - data = { text: $(that).attr('data-content') }; + $('[data-behavior~=liquid-async]').each(function () { + const that = this, + data = { text: $(that).attr('data-content') }, + $spinner = $(that).prev().find('[data-behavior~=liquid-spinner'); fetch($(that).attr('data-path'), { method: 'POST', headers: { - "Accept": "text/html", - "Content-Type": "application/json", - "X-CSRF-Token": $('meta[name="csrf-token"]').attr('content') + Accept: 'text/html', + 'Content-Type': 'application/json', + 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'), }, - body: JSON.stringify(data) - }). - then(response => response.text()). - then(function(html) { - $(that).html(html); - }); + body: JSON.stringify(data), + }) + .then((response) => response.text()) + .then(function (html) { + $(that).html(html); + $spinner.addClass('d-none'); + }); }); }); From f18b19004b75f0a62261df4800ddb3ee1ba1efcb Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Wed, 5 Jun 2024 15:18:38 +0800 Subject: [PATCH 14/19] Fix specs and remove artificial sleep --- app/controllers/concerns/liquid_enabled_resource.rb | 2 -- spec/features/evidence_spec.rb | 2 +- spec/features/issues_spec.rb | 2 +- spec/features/note_pages_spec.rb | 2 +- spec/support/liquid_shared_examples.rb | 3 ++- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/controllers/concerns/liquid_enabled_resource.rb b/app/controllers/concerns/liquid_enabled_resource.rb index e23863ce9..a8f8216e3 100644 --- a/app/controllers/concerns/liquid_enabled_resource.rb +++ b/app/controllers/concerns/liquid_enabled_resource.rb @@ -15,8 +15,6 @@ def liquid_resource_assigns end def preview - # Artificial load time for testing - sleep 3 @text = params[:text] render 'markup/preview', layout: false end diff --git a/spec/features/evidence_spec.rb b/spec/features/evidence_spec.rb index de5866929..7246b19cc 100644 --- a/spec/features/evidence_spec.rb +++ b/spec/features/evidence_spec.rb @@ -80,7 +80,7 @@ let(:model) { @evidence } include_examples 'nodes pages breadcrumbs', :show, Evidence - describe 'when including liquid content' do + describe 'when including liquid content', js: true do let(:record) { create(:evidence, :with_liquid, issue: @issue, node: @node) } include_examples 'liquid dynamic content', 'evidence', true end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 178702dd8..a2642cb10 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -412,7 +412,7 @@ end end - describe 'when including liquid content' do + describe 'when including liquid content', js: true do let(:record) { create(:issue, :with_liquid, node: issuelib) } include_examples 'liquid dynamic content', 'issue', false end diff --git a/spec/features/note_pages_spec.rb b/spec/features/note_pages_spec.rb index c71b53881..bb9a71d28 100644 --- a/spec/features/note_pages_spec.rb +++ b/spec/features/note_pages_spec.rb @@ -77,7 +77,7 @@ let(:model) { @note } include_examples 'nodes pages breadcrumbs', :show, Note - describe 'when including liquid content' do + describe 'when including liquid content', js: true do let(:record) { create(:note, :with_liquid, node: @node) } include_examples 'liquid dynamic content', 'note', true end diff --git a/spec/support/liquid_shared_examples.rb b/spec/support/liquid_shared_examples.rb index 713c1e731..7216e3eb2 100644 --- a/spec/support/liquid_shared_examples.rb +++ b/spec/support/liquid_shared_examples.rb @@ -10,13 +10,14 @@ end it 'dynamically renders item properties' do + expect(page).to have_no_css('span.text-nowrap', text: 'Loading liquid dynamic content', wait: 10) + expect(find('.note-text-inner')).to have_content("Liquid: #{record.title}") expect(find('.note-text-inner')).not_to have_content("Liquid: {{#{item_type}.title}}") end end shared_examples 'liquid preview' do |item_type, node_association| - before do @path = if node_association polymorphic_path([:edit, current_project, record.node, record]) From 89c96ebe96587615a248691bcb31022d3bf99a43 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Wed, 5 Jun 2024 15:56:55 +0800 Subject: [PATCH 15/19] Fix QA issues spec --- spec/support/qa_shared_examples.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/support/qa_shared_examples.rb b/spec/support/qa_shared_examples.rb index de078dba6..ff7ad5395 100644 --- a/spec/support/qa_shared_examples.rb +++ b/spec/support/qa_shared_examples.rb @@ -78,7 +78,9 @@ visit polymorphic_path([current_project, :qa, record]) end - it 'parses liquid content' do + it 'parses liquid content', js: true do + expect(page).to have_no_css('span.text-nowrap', text: 'Loading liquid dynamic content', wait: 10) + expect(find('.note-text-inner')).to have_content("Liquid: #{record.title}") expect(find('.note-text-inner')).not_to have_content("Liquid: {{#{item_type.to_s}.title}}") end From a87d0bfdc091d08c6dd122484049c9af2d227bd3 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Wed, 5 Jun 2024 19:08:57 +0800 Subject: [PATCH 16/19] Move js: true to the shared example --- spec/features/evidence_spec.rb | 2 +- spec/features/issues_spec.rb | 2 +- spec/features/note_pages_spec.rb | 2 +- spec/support/liquid_shared_examples.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/features/evidence_spec.rb b/spec/features/evidence_spec.rb index 7246b19cc..de5866929 100644 --- a/spec/features/evidence_spec.rb +++ b/spec/features/evidence_spec.rb @@ -80,7 +80,7 @@ let(:model) { @evidence } include_examples 'nodes pages breadcrumbs', :show, Evidence - describe 'when including liquid content', js: true do + describe 'when including liquid content' do let(:record) { create(:evidence, :with_liquid, issue: @issue, node: @node) } include_examples 'liquid dynamic content', 'evidence', true end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index a2642cb10..178702dd8 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -412,7 +412,7 @@ end end - describe 'when including liquid content', js: true do + describe 'when including liquid content' do let(:record) { create(:issue, :with_liquid, node: issuelib) } include_examples 'liquid dynamic content', 'issue', false end diff --git a/spec/features/note_pages_spec.rb b/spec/features/note_pages_spec.rb index bb9a71d28..c71b53881 100644 --- a/spec/features/note_pages_spec.rb +++ b/spec/features/note_pages_spec.rb @@ -77,7 +77,7 @@ let(:model) { @note } include_examples 'nodes pages breadcrumbs', :show, Note - describe 'when including liquid content', js: true do + describe 'when including liquid content' do let(:record) { create(:note, :with_liquid, node: @node) } include_examples 'liquid dynamic content', 'note', true end diff --git a/spec/support/liquid_shared_examples.rb b/spec/support/liquid_shared_examples.rb index 7216e3eb2..d49f39beb 100644 --- a/spec/support/liquid_shared_examples.rb +++ b/spec/support/liquid_shared_examples.rb @@ -9,7 +9,7 @@ visit @path end - it 'dynamically renders item properties' do + it 'dynamically renders item properties', js: true do expect(page).to have_no_css('span.text-nowrap', text: 'Loading liquid dynamic content', wait: 10) expect(find('.note-text-inner')).to have_content("Liquid: #{record.title}") From 79a26dc3d0163d1c76903d2d5ae03aed5b0de563 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Thu, 6 Jun 2024 16:46:34 +0800 Subject: [PATCH 17/19] Use Liquid::VariableLookup to determine which liquid assign we need --- .../concerns/liquid_enabled_resource.rb | 2 +- app/services/liquid_assigns_service.rb | 43 ++++++++++++++----- spec/services/liquid_assigns_service_spec.rb | 35 +++++++++++---- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/app/controllers/concerns/liquid_enabled_resource.rb b/app/controllers/concerns/liquid_enabled_resource.rb index a8f8216e3..1dac5829e 100644 --- a/app/controllers/concerns/liquid_enabled_resource.rb +++ b/app/controllers/concerns/liquid_enabled_resource.rb @@ -35,6 +35,6 @@ def project_assigns project = Project.find(params[:project_id]) authorize! :use, project - LiquidAssignsService.new(project).assigns + LiquidAssignsService.new(project: project, text: params[:text]).assigns end end diff --git a/app/services/liquid_assigns_service.rb b/app/services/liquid_assigns_service.rb index ceed78075..88cf164af 100644 --- a/app/services/liquid_assigns_service.rb +++ b/app/services/liquid_assigns_service.rb @@ -1,8 +1,11 @@ class LiquidAssignsService - attr_accessor :project + AVAILABLE_PROJECT_ASSIGNS = %w{ evidences issues nodes notes tags }.freeze - def initialize(project) + attr_accessor :project, :text + + def initialize(project:, text: nil) @project = project + @text = text end def assigns @@ -16,15 +19,13 @@ def assigns def assigns_pro end - def project_assigns - { - 'evidences' => cached_drops(project.evidence), - 'issues' => cached_drops(project.issues), - 'nodes' => cached_drops(project.nodes.user_nodes), - 'notes' => cached_drops(project.notes), - 'project' => ProjectDrop.new(project), - 'tags' => cached_drops(project.tags) - } + # This method uses Liquid::VariableLookup to find all liquid variables from + # a given text. We use the list to know which project assign we need. + def assigns_from_content + return AVAILABLE_PROJECT_ASSIGNS if text.nil? + + variable_lookup = Liquid::VariableLookup.parse(text) + return (variable_lookup.lookups & AVAILABLE_PROJECT_ASSIGNS) end def cached_drops(records) @@ -38,4 +39,24 @@ def cached_drops(records) records.map { |record| drop_class.new(record) } end end + + def project_assigns + project_assigns = { 'project' => ProjectDrop.new(project) } + + assigns_from_content.each do |var| + records = + case var + when 'evidences' + project.evidence + when 'nodes' + project.nodes.user_nodes + else + project.send(var.to_sym) + end + + project_assigns.merge!(var => cached_drops(records)) + end + + project_assigns + end end diff --git a/spec/services/liquid_assigns_service_spec.rb b/spec/services/liquid_assigns_service_spec.rb index 418cdc0c3..5e1fd0796 100644 --- a/spec/services/liquid_assigns_service_spec.rb +++ b/spec/services/liquid_assigns_service_spec.rb @@ -3,8 +3,6 @@ RSpec.describe LiquidAssignsService do let!(:project) { create(:project) } - let(:liquid_assigns) { described_class.new(project).assigns } - before do node = create(:node, project: project) issue = create(:issue, node: project.issue_library) @@ -13,16 +11,35 @@ create(:tag) end - it 'builds a hash of liquid assigns' do - expect(liquid_assigns['project'].name).to eq(project.name) - expect(liquid_assigns['issues'].map(&:title)).to eq(project.issues.map(&:title)) - expect(liquid_assigns['evidences'].map(&:title)).to eq(project.evidence.map(&:title)) - expect(liquid_assigns['nodes'].map(&:label)).to eq(project.nodes.user_nodes.map(&:label)) - expect(liquid_assigns['notes'].map(&:title)).to eq(project.notes.map(&:title)) - expect(liquid_assigns['tags'].map(&:display_name)).to eq(project.tags.map(&:display_name)) + describe '#project_assigns' do + context 'with the :text argument' do + LiquidAssignsService::AVAILABLE_PROJECT_ASSIGNS.each do |assign| + it "adds #{assign} to the project_assigns if present in the text" do + text = "#[Description]#\n {% for #{assign.singularize} in #{assign} %}{% endfor %}\n" + liquid_assigns = described_class.new(project: project, text: text).assigns + + expect(liquid_assigns.keys).to include(assign) + end + end + end + + context 'without the :text argument' do + let(:liquid_assigns) { described_class.new(project: project).assigns } + + it 'builds a hash of liquid assigns' do + expect(liquid_assigns['project'].name).to eq(project.name) + expect(liquid_assigns['issues'].map(&:title)).to eq(project.issues.map(&:title)) + expect(liquid_assigns['evidences'].map(&:title)).to eq(project.evidence.map(&:title)) + expect(liquid_assigns['nodes'].map(&:label)).to eq(project.nodes.user_nodes.map(&:label)) + expect(liquid_assigns['notes'].map(&:title)).to eq(project.notes.map(&:title)) + expect(liquid_assigns['tags'].map(&:display_name)).to eq(project.tags.map(&:display_name)) + end + end end context 'with pro records', skip: !defined?(Dradis::Pro) do + let(:liquid_assigns) { described_class.new(project: project).assigns } + let!(:project) { create(:project, :with_team) } before do From 3cfd946c9112226d066da9f62d49f2efeda2553f Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Mon, 10 Jun 2024 15:58:41 +0800 Subject: [PATCH 18/19] Apply requested changes --- CHANGELOG | 2 +- app/services/liquid_assigns_service.rb | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 34dfcb4f4..4968516d7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,5 @@ [v#.#.#] ([month] [YYYY]) - - Liquid: Make project-level drops available for Liquid syntax + - Liquid: Make project-level collections available for Liquid syntax - Upgraded gems: nokogiri, rails, rexml - Bugs fixes: - [entity]: diff --git a/app/services/liquid_assigns_service.rb b/app/services/liquid_assigns_service.rb index 88cf164af..b781c77b6 100644 --- a/app/services/liquid_assigns_service.rb +++ b/app/services/liquid_assigns_service.rb @@ -28,12 +28,11 @@ def assigns_from_content return (variable_lookup.lookups & AVAILABLE_PROJECT_ASSIGNS) end - def cached_drops(records) + def cached_drops(records, record_type) return [] if records.empty? - type = records.first.class.to_s.underscore - cache_key = "liquid-project-#{project.id}-#{type.pluralize}:#{records.maximum(:updated_at).to_i}-#{records.count}" - drop_class = "#{type.camelize}Drop".constantize + cache_key = "liquid-project-#{project.id}-#{record_type.pluralize}:#{records.maximum(:updated_at).to_i}-#{records.count}" + drop_class = "#{record_type.camelize}Drop".constantize Rails.cache.fetch(cache_key) do records.map { |record| drop_class.new(record) } @@ -43,18 +42,18 @@ def cached_drops(records) def project_assigns project_assigns = { 'project' => ProjectDrop.new(project) } - assigns_from_content.each do |var| + assigns_from_content.each do |record_type| records = - case var + case record_type when 'evidences' project.evidence when 'nodes' project.nodes.user_nodes else - project.send(var.to_sym) + project.send(record_type.to_sym) end - project_assigns.merge!(var => cached_drops(records)) + project_assigns.merge!(record_type => cached_drops(records, record_type.singularize)) end project_assigns From 1c77ace083ff42952e6984416c41aceec6476476 Mon Sep 17 00:00:00 2001 From: Aaron Manaloto Date: Thu, 13 Jun 2024 15:38:54 +0800 Subject: [PATCH 19/19] Add textile_content partial --- app/views/evidence/show.html.erb | 4 +--- app/views/issues/show.html.erb | 4 +--- app/views/notes/show.html.erb | 4 +--- app/views/qa/issues/show.html.erb | 4 +--- app/views/shared/_textile_content.html.erb | 5 +++++ 5 files changed, 9 insertions(+), 12 deletions(-) create mode 100644 app/views/shared/_textile_content.html.erb diff --git a/app/views/evidence/show.html.erb b/app/views/evidence/show.html.erb index 9dbfddc86..f971355f3 100644 --- a/app/views/evidence/show.html.erb +++ b/app/views/evidence/show.html.erb @@ -33,9 +33,7 @@ <%= render partial: 'shared/liquid_loading' %> <%= render partial: 'actions' %>

-
- <%= markup(@evidence.content) %> -
+ <%= render partial: 'shared/textile_content', locals: { record: @evidence, preview_path: preview_project_node_evidence_path(current_project, @evidence.node, @evidence) } %>
Author: <%= @evidence.author || 'n/a' %>
diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb index 1ebbe1fc5..23f9ac46d 100644 --- a/app/views/issues/show.html.erb +++ b/app/views/issues/show.html.erb @@ -36,9 +36,7 @@ <%= render partial: 'shared/liquid_loading' %> <%= render partial: 'actions' %> -
- <%= markup(@issue.text) %> -
+ <%= render partial: 'shared/textile_content', locals: { record: @issue, preview_path: preview_project_issue_path(current_project, @issue) } %>
Author: <%= @issue.author || 'n/a' %>
diff --git a/app/views/notes/show.html.erb b/app/views/notes/show.html.erb index db589456c..4a65d4564 100644 --- a/app/views/notes/show.html.erb +++ b/app/views/notes/show.html.erb @@ -24,9 +24,7 @@ <%= render partial: 'shared/liquid_loading' %> <%= render partial: 'actions' %> -
- <%= markup(@note.text) %> -
+ <%= render partial: 'shared/textile_content', locals: { record: @note, preview_path: preview_project_node_note_path(current_project, @note.node, @note) } %>
Author: <%= @note.author || 'n/a' %>
diff --git a/app/views/qa/issues/show.html.erb b/app/views/qa/issues/show.html.erb index f834f9262..96123a8a4 100644 --- a/app/views/qa/issues/show.html.erb +++ b/app/views/qa/issues/show.html.erb @@ -30,9 +30,7 @@ -
- <%= markup(@issue.text) %> -
+ <%= render partial: 'shared/textile_content', locals: { record: @issue, preview_path: preview_project_issue_path(current_project, @issue) } %>
Author: <%= @issue.author || 'n/a' %>
diff --git a/app/views/shared/_textile_content.html.erb b/app/views/shared/_textile_content.html.erb new file mode 100644 index 000000000..63cc85ee5 --- /dev/null +++ b/app/views/shared/_textile_content.html.erb @@ -0,0 +1,5 @@ +<% content = record.is_a?(Note) ? record.text : record.content %> + +<%= content_tag :div, class: 'content-textile', data: { behavior: 'content-textile liquid-async', path: preview_path, content: content } do %> + <%= markup(content) %> +<% end %>