Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Journal the addition and removal of File Links on Work Packages #13157

Merged
merged 12 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/models/journal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class Journal < ApplicationRecord
register_journal_formatter :wiki_diff, OpenProject::JournalFormatter::WikiDiff
register_journal_formatter :time_entry_named_association, OpenProject::JournalFormatter::TimeEntryNamedAssociation
register_journal_formatter :cause, OpenProject::JournalFormatter::Cause
register_journal_formatter :file_link, OpenProject::JournalFormatter::FileLink

# Attributes related to the cause are stored in a JSONB column so we can easily add new relations and related
# attributes without a heavy database migration. Fields will be prefixed with `cause_` but are stored in the JSONB
Expand All @@ -74,6 +75,7 @@ class Journal < ApplicationRecord

has_many :attachable_journals, class_name: 'Journal::AttachableJournal', dependent: :delete_all
has_many :customizable_journals, class_name: 'Journal::CustomizableJournal', dependent: :delete_all
has_many :storable_journals, class_name: 'Journal::StorableJournal', dependent: :delete_all

has_many :notifications, dependent: :destroy

Expand Down Expand Up @@ -149,6 +151,10 @@ def has_cause?

private

def has_file_links?
journable.respond_to?(:file_links)
end

def predecessor
@predecessor ||= if initial?
nil
Expand Down
35 changes: 35 additions & 0 deletions app/models/journal/storable_journal.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class Journal::StorableJournal < Journal::AssociatedJournal
self.table_name = 'storages_file_links_journals'

belongs_to :file_link, class_name: 'Storages::FileLink'
end
1 change: 1 addition & 0 deletions app/models/work_package/journalized.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def self.event_url
register_journal_formatted_fields(:custom_field, /custom_fields_\d+/)
register_journal_formatted_fields(:ignore_non_working_days, 'ignore_non_working_days')
register_journal_formatted_fields(:cause, 'cause')
register_journal_formatted_fields(:file_link, /file_links_?\d+/)

# Joined
register_journal_formatted_fields :named_association, :parent_id, :project_id,
Expand Down
66 changes: 66 additions & 0 deletions app/services/journals/create_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ def create_journal_sql(predecessor, notes, cause)
cleanup_predecessor_customizable AS (
#{cleanup_predecessor_customizable(predecessor)}
),
cleanup_predecessor_storable AS (
#{cleanup_predecessor_storable(predecessor)}
),
max_journals AS (
#{select_max_journal_sql(predecessor)}
), changes AS (
Expand All @@ -172,6 +175,8 @@ def create_journal_sql(predecessor, notes, cause)
#{insert_attachable_sql}
), insert_customizable AS (
#{insert_customizable_sql}
), insert_storable AS (
#{insert_storable_sql}
)

SELECT * from inserted_journal
Expand Down Expand Up @@ -199,6 +204,13 @@ def cleanup_predecessor_customizable(predecessor)
:id)
end

def cleanup_predecessor_storable(predecessor)
cleanup_predecessor(predecessor,
'storages_file_links_journals',
:journal_id,
:id)
end

def cleanup_predecessor(predecessor, table_name, column, referenced_id)
return "SELECT 1" unless predecessor

Expand Down Expand Up @@ -371,6 +383,30 @@ def insert_customizable_sql
journable_class_name:)
end

def insert_storable_sql
storable_sql = <<~SQL
INSERT INTO
storages_file_links_journals (
journal_id,
file_link_id,
link_name
)
SELECT
#{id_from_inserted_journal_sql},
file_links.id,
file_links.origin_name
FROM file_links
WHERE
#{only_if_created_sql}
AND file_links.container_id = :journable_id
AND file_links.container_type = :journable_class_name
SQL

sanitize(storable_sql,
journable_id: journable.id,
journable_class_name:)
end

def select_max_journal_sql(predecessor)
sql = <<~SQL
SELECT
Expand Down Expand Up @@ -408,6 +444,10 @@ def select_changed_sql
(#{attachable_changes_sql}) attachable_changes
ON
attachable_changes.journable_id = data_changes.journable_id
FULL JOIN
(#{storable_changes_sql}) storable_changes
ON
storable_changes.journable_id = data_changes.journable_id
SQL
end

Expand Down Expand Up @@ -465,6 +505,32 @@ def customizable_changes_sql
journable_id: journable.id)
end

def storable_changes_sql
storables_changes_sql = <<~SQL
SELECT
max_journals.journable_id
FROM
max_journals
LEFT OUTER JOIN
storages_file_links_journals
ON
storages_file_links_journals.journal_id = max_journals.id
FULL JOIN
(SELECT *
FROM file_links
WHERE file_links.container_id = :journable_id AND file_links.container_type = :container_type) file_links
ON
file_links.id = storages_file_links_journals.file_link_id
WHERE
(file_links.id IS NULL AND storages_file_links_journals.file_link_id IS NOT NULL)
OR (storages_file_links_journals.file_link_id IS NULL AND file_links.id IS NOT NULL)
SQL

sanitize(storables_changes_sql,
journable_id: journable.id,
container_type: journable_class_name)
end

def data_changes_sql
data_changes_sql = <<~SQL
SELECT
Expand Down
42 changes: 42 additions & 0 deletions db/migrate/20230713144232_create_storages_file_link_journals.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class CreateStoragesFileLinkJournals < ActiveRecord::Migration[7.0]
def change
# rubocop:disable Rails/CreateTableWithTimestamps
mereghost marked this conversation as resolved.
Show resolved Hide resolved
create_table :storages_file_links_journals do |t|
t.belongs_to :journal, null: false, foreign_key: true
t.belongs_to :file_link, null: false

t.string :link_name, null: false
end
# rubocop:enable Rails/CreateTableWithTimestamps
end
end
1 change: 1 addition & 0 deletions lib/api/v3/activities/activities_by_work_package_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class ActivitiesByWorkPackageAPI < ::API::OpenProjectAPI
journals = @work_package.journals.includes(:data,
:customizable_journals,
:attachable_journals,
:storable_journals,
:bcf_comment)

Activities::ActivityCollectionRepresenter.new(journals,
Expand Down
2 changes: 1 addition & 1 deletion lib/api/v3/activities/activity_eager_loading_wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def predecessor_journals(journals)
) AS journals
SQL
)
.includes(:attachable_journals, :customizable_journals)
.includes(:attachable_journals, :customizable_journals, :storable_journals)
end
end
end
Expand Down
58 changes: 58 additions & 0 deletions lib/open_project/journal_formatter/file_link.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class OpenProject::JournalFormatter::FileLink < JournalFormatter::Base
include OpenProject::ObjectLinking

def render(key, values, options = { html: true })
id = key.to_s.sub('file_links_', '')
label, old_value, value = format_details(id, values)

if options[:html]
label, old_value, value = *format_html_details(label, old_value, value)
value = format_html_file_link_detail(id, value)
end

render_binary_detail_text(label, value, old_value)
end

private

# Based this off the Attachment formatter. Not sure if it is the best approach
def label(_key) = Storages::FileLink.model_name.human
mereghost marked this conversation as resolved.
Show resolved Hide resolved

def format_html_file_link_detail(key, value)
if value.present? && file_link = ::Storages::FileLink.find_by(id: key.to_i)
link_to_file_link(file_link, only_path: false)
elsif value.present?
value
end
end
end
24 changes: 21 additions & 3 deletions lib/open_project/object_linking.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ def link_to_attachment(attachment, options = {})
options
end

def link_to_file_link(file_link, options = {})
text = options.delete(:text) || file_link.origin_name

link_to text,
url_to_file_link(file_link, only_path: options.delete(:only_path) { true }),
options
end

# Generates a link to a SCM revision
# Options:
# * :text - Link text (default to the formatted revision)
Expand Down Expand Up @@ -138,14 +146,24 @@ def project_link_name(project, show_icon)
end

def url_to_attachment(attachment, only_path: true)
# Including the module breaks the application in strange and mysterious ways
v3_paths = API::V3::Utilities::PathHelper::ApiV3Path

if only_path
v3_paths.attachment_content(attachment.id)
else
v3_paths.url_for(:attachment_content, attachment.id)
end
end

def url_to_file_link(file_link, only_path: true)
if only_path
v3_paths.file_link_open(file_link.id)
else
v3_paths.url_for(:file_link_open, file_link.id)
end
end

def v3_paths
# Including the module breaks the application in strange and mysterious ways
API::V3::Utilities::PathHelper::ApiV3Path
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ def attachments_addable?(user = User.current)
(persisted? && allowed_to_on_attachment?(user, self.class.attachable_options[:add_on_persisted_permission]))
end

def attachable?
true
end

private

def allowed_to_on_attachment?(user, permissions)
Expand Down Expand Up @@ -243,6 +247,10 @@ def claimed_attachments_already_claimed?
end
end
end

def attachable?
false
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ def self.included(base)
base.extend HumanAttributeName
end

def customizable?
true
end

def available_custom_fields
self.class.available_custom_fields(self)
end
Expand Down Expand Up @@ -409,6 +413,10 @@ def available_custom_fields(_model)
end
end
end

def customizable?
false
end
end
end
end
Loading
Loading