Skip to content

Commit

Permalink
Merge branch 'develop' into issues/show-updated-affected-list
Browse files Browse the repository at this point in the history
  • Loading branch information
MattBudz committed Jun 27, 2024
2 parents 6c0e79d + b027497 commit d1c677d
Show file tree
Hide file tree
Showing 13 changed files with 210 additions and 138 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
[v#.#.#] ([month] [YYYY])
- Attachments: Copy attachments when moving an evidence/note
- Liquid: Make project-level collections available for Liquid syntax
- Upgraded gems: nokogiri, rails, rexml
- Bugs fixes:
- Issues: Update Affected column after a node has been renamed or merged
- Navigation: Restore functionality of native browser back/forward buttons
- Bug tracker items:
- [item]
- New integrations:
Expand Down
17 changes: 12 additions & 5 deletions app/assets/javascripts/shared/behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,18 @@
}

// Update address bar with current tab param
$('[data-bs-toggle~=tab]').on('shown.bs.tab', function (e) {
let currentTab = $(e.target).attr('href').substring(1);
searchParams.set('tab', currentTab);
history.pushState(null, null, `?${searchParams.toString()}`);
});
$(parentElement)
.find('[data-bs-toggle~=tab]')
.on('shown.bs.tab', function (e) {
let currentTab = $(e.target).attr('href').substring(1);
searchParams.set('tab', currentTab);
let urlWithTab = `?${searchParams.toString()}`;
history.pushState(
{ turbolinks: true, url: urlWithTab },
'',
urlWithTab
);
});
}

document.addEventListener('turbolinks:load', function () {
Expand Down
20 changes: 20 additions & 0 deletions app/controllers/concerns/attachments_copier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module AttachmentsCopier
def copy_attachments(record)
record.content.scan(Attachment::SCREENSHOT_REGEX).each do |screenshot_path|
full_screenshot_path, _, _, _, project_id, node_id, filename, _ = screenshot_path

attachment = Attachment.find_by(filename: CGI::unescape(filename), node_id: record.node_id_was)

if attachment
new_attachment = attachment.copy_to(record.node)
new_filename = new_attachment.url_encoded_filename
new_path = full_screenshot_path.gsub(
/nodes\/[0-9]+\/attachments\/.+/,
"nodes/#{new_attachment.node_id}/attachments/#{new_filename}"
)

record.content = record.content.gsub(full_screenshot_path, new_path)
end
end
end
end
2 changes: 1 addition & 1 deletion app/controllers/concerns/liquid_enabled_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ def project_assigns
project = Project.find(params[:project_id])
authorize! :use, project

LiquidAssignsService.new(project: project, text: params[:text]).assigns
LiquidCachedAssigns.new(project: project)
end
end
3 changes: 3 additions & 0 deletions app/controllers/evidence_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class EvidenceController < NestedNodeResourceController
include AttachmentsCopier
include ConflictResolver
include EvidenceHelper
include LiquidEnabledResource
Expand Down Expand Up @@ -55,6 +56,8 @@ def update
@evidence.assign_attributes(evidence_params)
autogenerate_issue if evidence_params[:issue_id].blank?

copy_attachments(@evidence) if @evidence.node_changed?

if @evidence.save
track_updated(@evidence)
check_for_edit_conflicts(@evidence, updated_at_before_save)
Expand Down
7 changes: 6 additions & 1 deletion app/controllers/notes_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This controller exposes the REST operations required to manage the Note
# resource.
class NotesController < NestedNodeResourceController
include AttachmentsCopier
include ConflictResolver
include LiquidEnabledResource
include Mentioned
Expand Down Expand Up @@ -44,7 +45,11 @@ def edit
# Update the attributes of a Note
def update
updated_at_before_save = @note.updated_at.to_i
if @note.update(note_params)

@note.assign_attributes(note_params)
copy_attachments(@note) if @note.node_changed?

if @note.save
track_updated(@note)
check_for_edit_conflicts(@note, updated_at_before_save)
# if the note has just been moved to another node, we must reload
Expand Down
18 changes: 9 additions & 9 deletions app/models/attachment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
**
=end


# ==Description
# This class in an abstraction layer to the <tt>attachments/</tt> folder. It allows
# access to the folder content in a way that mimics the working of ActiveRecord
Expand Down Expand Up @@ -66,6 +65,8 @@ class Attachment < File
AttachmentPwd = Rails.env.test? ? Rails.root.join('tmp', 'attachments') : Rails.root.join('attachments')
FileUtils.mkdir_p(AttachmentPwd) unless File.exists?(AttachmentPwd)

SCREENSHOT_REGEX = /\!((https?:\/\/.+)|((\/pro)?\/projects\/(\d+)\/nodes\/(\d+)\/attachments\/(.+?)(\(.*\))?))\!/i

# -- Class Methods ---------------------------------------------------------

def self.all(*args)
Expand All @@ -81,7 +82,7 @@ def self.count
end

def self.find_by(filename:, node_id:)
find(filename, conditions: { node_id: node_id } )
find(filename, conditions: { node_id: node_id })
rescue StandardError
end

Expand All @@ -91,17 +92,17 @@ def self.find(*args)
dir = Dir.new(pwd)

# makes the find request and stores it to resources
return_value = case args.first
case args.first
when :all, :first, :last
attachments = []
if options[:conditions] && options[:conditions][:node_id]
node_id = options[:conditions][:node_id].to_s
raise "Node with ID=#{node_id} does not exist" unless Node.exists?(node_id)
if (File.exist?( File.join(pwd, node_id)))
if (File.exist?(File.join(pwd, node_id)))
node_dir = Dir.new(pwd.join(node_id)).sort
node_dir.each do |attachment|
next unless (attachment =~ /^(.+)$/) == 0 && !File.directory?(pwd.join(node_id, attachment))
attachments << Attachment.new(:filename => $1, :node_id => node_id.to_i)
attachments << Attachment.new(filename: $1, node_id: node_id.to_i)
end
end
else
Expand All @@ -110,7 +111,7 @@ def self.find(*args)
node_dir = Dir.new(pwd.join(node)).sort
node_dir.each do |attachment|
next unless (attachment =~ /^(.+)$/) == 0 && !File.directory?(pwd.join(node, attachment))
attachments << Attachment.new(:filename => $1, :node_id => node.to_i)
attachments << Attachment.new(filename: $1, node_id: node.to_i)
end
end
attachments.sort_by!(&:filename)
Expand All @@ -129,18 +130,17 @@ def self.find(*args)
# in this routine we find the attachment by file name and node id
filename = args.first
attachments = []
raise "You need to supply a node id in the condition parameter" unless options[:conditions] && options[:conditions][:node_id]
raise 'You need to supply a node id in the condition parameter' unless options[:conditions] && options[:conditions][:node_id]
node_id = options[:conditions][:node_id].to_s
raise "Node with ID=#{node_id} does not exist" unless Node.exists?(node_id)
node_dir = Dir.new(pwd.join(node_id)).sort
node_dir.each do |attachment|
next unless ((attachment =~ /^(.+)$/) == 0 && $1 == filename)
attachments << Attachment.new(:filename => $1, :node_id => node_id.to_i)
attachments << Attachment.new(filename: $1, node_id: node_id.to_i)
end
raise "Could not find Attachment with filename #{filename}" if attachments.empty?
attachments.first
end
return return_value
end

def self.model_name
Expand Down
61 changes: 0 additions & 61 deletions app/services/liquid_assigns_service.rb

This file was deleted.

70 changes: 70 additions & 0 deletions app/services/liquid_cached_assigns.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
class LiquidCachedAssigns < Hash
AVAILABLE_PROJECT_ASSIGNS = %w{ evidences issues nodes notes project tags }.freeze

attr_accessor :assigns, :project

def initialize(project:)
@project = project

@assigns = { 'project' => ProjectDrop.new(project) }
@assigns.merge!(assigns_pro)
end

def [](record_type)
assigns[record_type] ||= cached_drops(record_type)
end

# SEE: https://github.com/Shopify/liquid/blob/77bc56/lib/liquid/context.rb#L211
# Liquid is checking if the variable is present in the assigns hash by
# calling the `key?` method. Since we're lazily loading the keys, the variable
# may not yet be present in the assigns hash.
def key?(key)
AVAILABLE_PROJECT_ASSIGNS.include?(key.to_s) || assigns.key?(key)
end

def merge(hash)
lca = LiquidCachedAssigns.new(project: project)
lca.assigns = @assigns.merge(hash)
lca
end

def merge!(hash)
@assigns.merge!(hash)
self
end

private

def assigns_pro
{}
end

def cached_drops(record_type)
records = project_records(record_type)

return [] if records.empty?

cache_key = ActiveSupport::Cache.expand_cache_key([project.id, records], 'liquid')
drop_class = "#{record_type.singularize.camelize}Drop".constantize

Rails.cache.fetch(cache_key) do
records.map { |record| drop_class.new(record) }
end
end

def project_records(record_type)
return [] unless AVAILABLE_PROJECT_ASSIGNS.include?(record_type)

case record_type
when 'evidences'
project.evidence
when 'nodes'
project.nodes.user_nodes
when 'notes'
# FIXME - ISSUE/NOTE INHERITANCE
project.notes.where.not(node_id: project.issue_library.id)
else
project.send(record_type.to_sym)
end
end
end
13 changes: 12 additions & 1 deletion spec/features/evidence_moving_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,17 @@ def create_node(label, parent = nil)
click_move_evidence
end

let(:current_evidence) { @evidence = create(:evidence, node: @node_5) }
let(:content) { "#[Description]#\nTest Evidence\n" }
let(:current_evidence) { @evidence = create(:evidence, content: content, node: @node_5) }

describe 'moving an evidence to a different node' do
let(:attachment) { create(:attachment, filename: 'name with spaces.png', node: @node_5) }
let(:content) { "#[Description]#\n!/projects/#{current_project.id}/nodes/#{@node_5.id}/attachments/#{attachment.filename}!\n" }

before do
# Ensure this works with duplicated attachment
create(:attachment, filename: 'name with spaces.png', node: @node_1)

within('#modal_move_evidence') do
click_link @node_1.label
click_submit
Expand All @@ -46,6 +53,10 @@ def create_node(label, parent = nil)
it 'should redirect to evidence show path' do
expect(current_path).to eq(project_node_evidence_path(current_project, @node_1, current_evidence))
end

it 'should update the attachment reference to the new node' do
expect(current_evidence.reload.content).to include("nodes/#{@node_1.id}")
end
end

describe 'moving a evidence to a similar node', js: true do
Expand Down
13 changes: 12 additions & 1 deletion spec/features/note_moving_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,17 @@ def create_node(label, parent = nil)
click_move_note
end

let(:current_note) { @note = create(:note, node: @node_5) }
let(:text) { "#[Description]#\nTest Note\n" }
let(:current_note) { @note = create(:note, text: text, node: @node_5) }

describe 'moving a note to a different node' do
let(:attachment) { create(:attachment, filename: 'name with spaces.png', node: @node_5) }
let(:text) { "#[Description]#\n!/projects/#{current_project.id}/nodes/#{@node_5.id}/attachments/#{attachment.filename}!\n" }

before do
# Ensure this works with duplicated attachment
create(:attachment, filename: 'name with spaces.png', node: @node_1)

within('#modal_move_note') do
click_link @node_1.label
click_submit
Expand All @@ -47,6 +54,10 @@ def create_node(label, parent = nil)
it 'should redirect to note show path' do
expect(current_path).to eq(project_node_note_path(current_project, @node_1, current_note))
end

it 'should update the attachment reference to the new node' do
expect(current_note.reload.content).to include("nodes/#{@node_1.id}")
end
end

describe 'moving a note to a similar node' do
Expand Down
Loading

0 comments on commit d1c677d

Please sign in to comment.