2023-02-02 11:23:20 +01:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
2011-05-29 13:11:52 -07:00
|
|
|
#-- copyright
|
2020-01-15 11:31:26 +01:00
|
|
|
# OpenProject is an open source project management software.
|
2024-07-30 13:42:36 +02:00
|
|
|
# Copyright (C) the OpenProject GmbH
|
2011-05-30 20:52:25 +02:00
|
|
|
#
|
2011-05-29 13:11:52 -07:00
|
|
|
# This program is free software; you can redistribute it and/or
|
2013-06-05 16:27:56 +02:00
|
|
|
# modify it under the terms of the GNU General Public License version 3.
|
2011-05-30 20:52:25 +02:00
|
|
|
#
|
2013-09-16 17:59:31 +02:00
|
|
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
2021-01-13 17:47:45 +01:00
|
|
|
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
2013-09-16 17:59:31 +02:00
|
|
|
# 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.
|
|
|
|
|
#
|
2021-09-02 21:49:06 +02:00
|
|
|
# See COPYRIGHT and LICENSE files for more details.
|
2011-05-29 13:11:52 -07:00
|
|
|
#++
|
|
|
|
|
|
2011-05-13 09:55:55 -07:00
|
|
|
class JournalsController < ApplicationController
|
2024-06-10 17:19:55 +02:00
|
|
|
before_action :load_and_authorize_in_optional_project, only: [:index]
|
2024-06-07 16:55:40 +02:00
|
|
|
before_action :find_journal,
|
|
|
|
|
:ensure_permitted,
|
2025-08-26 14:43:32 +02:00
|
|
|
:ensure_valid_for_diffing,
|
2024-06-07 16:55:40 +02:00
|
|
|
only: [:diff]
|
2024-06-11 10:20:38 +02:00
|
|
|
authorization_checked! :diff
|
2024-06-07 16:55:40 +02:00
|
|
|
|
2011-05-20 14:49:18 -07:00
|
|
|
accept_key_auth :index
|
|
|
|
|
menu_item :issues
|
2011-05-30 20:52:25 +02:00
|
|
|
|
2011-05-20 14:49:18 -07:00
|
|
|
include QueriesHelper
|
|
|
|
|
include SortHelper
|
|
|
|
|
|
|
|
|
|
def index
|
2023-05-31 11:50:48 +02:00
|
|
|
@query = retrieve_query(@project)
|
2011-05-20 14:49:18 -07:00
|
|
|
sort_init "id", "desc"
|
2017-02-01 16:45:36 +01:00
|
|
|
sort_update(@query.sortable_key_by_column_name)
|
2011-05-30 20:52:25 +02:00
|
|
|
|
2011-05-20 14:49:18 -07:00
|
|
|
if @query.valid?
|
2014-11-03 21:10:20 +01:00
|
|
|
@journals = @query.work_package_journals(order: "#{Journal.table_name}.created_at DESC",
|
|
|
|
|
limit: 25)
|
2011-05-20 14:49:18 -07:00
|
|
|
end
|
2013-09-02 11:54:32 +02:00
|
|
|
|
2012-08-14 15:29:19 +02:00
|
|
|
respond_to do |format|
|
2013-09-02 11:54:32 +02:00
|
|
|
format.atom do
|
2014-11-03 21:10:20 +01:00
|
|
|
render layout: false,
|
|
|
|
|
content_type: "application/atom+xml",
|
2020-09-16 11:26:15 +02:00
|
|
|
locals: { title: journals_index_title,
|
2014-11-03 21:26:12 +01:00
|
|
|
journals: @journals }
|
2013-09-02 11:54:32 +02:00
|
|
|
end
|
2012-08-14 15:29:19 +02:00
|
|
|
end
|
2011-05-20 14:49:18 -07:00
|
|
|
end
|
2011-05-13 09:55:55 -07:00
|
|
|
|
2011-12-28 14:16:38 -08:00
|
|
|
def diff
|
2026-02-25 11:21:42 +01:00
|
|
|
from, to = diff_values
|
|
|
|
|
unless from || to
|
2023-02-06 09:32:26 +01:00
|
|
|
return render_400 message: I18n.t(:error_journal_attribute_not_present, attribute: field_param)
|
2018-09-05 07:50:45 +02:00
|
|
|
end
|
|
|
|
|
|
2023-02-21 18:06:37 +01:00
|
|
|
@activity_page = params["activity_page"]
|
2018-09-05 07:50:45 +02:00
|
|
|
@diff = Redmine::Helpers::Diff.new(to, from)
|
2023-02-06 09:32:26 +01:00
|
|
|
|
2018-09-05 07:50:45 +02:00
|
|
|
respond_to do |format|
|
|
|
|
|
format.html
|
|
|
|
|
format.js do
|
|
|
|
|
render partial: "diff", locals: { diff: @diff }
|
2011-12-28 14:16:38 -08:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2012-12-10 17:37:42 +01:00
|
|
|
|
2011-05-20 14:49:18 -07:00
|
|
|
private
|
2011-05-30 20:52:25 +02:00
|
|
|
|
2011-05-13 09:55:55 -07:00
|
|
|
def find_journal
|
|
|
|
|
@journal = Journal.find(params[:id])
|
2023-02-06 09:32:26 +01:00
|
|
|
@journable = @journal.journable
|
|
|
|
|
@project = @journable.project
|
2011-05-20 14:49:18 -07:00
|
|
|
end
|
|
|
|
|
|
2023-02-02 11:23:20 +01:00
|
|
|
def ensure_permitted
|
2026-05-18 11:18:02 +02:00
|
|
|
deny_access unless @journal.visible?(User.current)
|
2023-02-02 11:23:20 +01:00
|
|
|
end
|
|
|
|
|
|
2026-02-25 11:21:42 +01:00
|
|
|
def diff_values
|
|
|
|
|
if @journal.details[field_param] in [from, to]
|
|
|
|
|
[from, to]
|
|
|
|
|
elsif @journal.cause_type == "import"
|
|
|
|
|
imported_cause_diff_values
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def imported_cause_diff_values
|
|
|
|
|
entries = @journal.cause_import_history
|
|
|
|
|
return unless entries.is_a?(Array)
|
|
|
|
|
|
|
|
|
|
item = entries.flat_map { |e| e["items"] || [] }
|
|
|
|
|
.find { |i| i["field"]&.parameterize&.underscore == field_param }
|
|
|
|
|
return unless item
|
|
|
|
|
|
|
|
|
|
[item["fromString"], item["toString"]]
|
|
|
|
|
end
|
|
|
|
|
|
2023-02-06 09:32:26 +01:00
|
|
|
def field_param
|
|
|
|
|
@field_param ||= params[:field].parameterize.underscore
|
2011-12-28 14:16:38 -08:00
|
|
|
end
|
2013-06-21 14:51:42 +02:00
|
|
|
|
2025-08-26 14:43:32 +02:00
|
|
|
def ensure_valid_for_diffing
|
2024-07-23 18:46:05 +02:00
|
|
|
case field_param
|
|
|
|
|
when "description",
|
|
|
|
|
"status_explanation",
|
|
|
|
|
/\Aagenda_items_\d+_notes\z/
|
2025-08-26 14:43:32 +02:00
|
|
|
# no additional checks
|
2026-01-29 15:43:21 +01:00
|
|
|
when /\Acustom_fields_(?<cf_id>\d+)\z/
|
|
|
|
|
ensure_custom_value_valid_for_diffing(Regexp.last_match(:cf_id))
|
|
|
|
|
when /\Acustom_comment_(?<cf_id>\d+)\z/
|
|
|
|
|
ensure_custom_comment_valid_for_diffing(Regexp.last_match(:cf_id))
|
2025-08-26 14:43:32 +02:00
|
|
|
else
|
|
|
|
|
render_404
|
2024-07-23 18:46:05 +02:00
|
|
|
end
|
2015-02-05 10:59:18 +01:00
|
|
|
end
|
2020-09-16 11:26:15 +02:00
|
|
|
|
2026-01-29 15:43:21 +01:00
|
|
|
def ensure_custom_value_valid_for_diffing(cf_id)
|
2026-05-18 13:30:54 +02:00
|
|
|
custom_field = CustomField.find_by(id: cf_id)
|
2026-05-18 20:42:36 +02:00
|
|
|
# Even if the custom field is deleted
|
|
|
|
|
# we will need to return 403 if the custom field class allows admin-only fields
|
2026-05-18 13:30:54 +02:00
|
|
|
return render_403 if deleted_cf_forbidden?(custom_field)
|
2026-01-29 15:43:21 +01:00
|
|
|
|
2026-05-18 20:42:36 +02:00
|
|
|
# If no admin-only field, we can allow deleted custom fields to be rendered now
|
|
|
|
|
return if custom_field.nil?
|
2026-05-18 13:30:54 +02:00
|
|
|
return render_403 unless custom_field.visible?(User.current, project: @journable.project)
|
2026-05-18 20:42:36 +02:00
|
|
|
|
2026-05-18 13:30:54 +02:00
|
|
|
render_404 if custom_field.field_format != "text"
|
2026-01-29 15:43:21 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def ensure_custom_comment_valid_for_diffing(cf_id)
|
2026-05-18 13:30:54 +02:00
|
|
|
custom_field = CustomField.find_by(id: cf_id)
|
|
|
|
|
return render_403 if deleted_cf_forbidden?(custom_field)
|
|
|
|
|
return if custom_field.nil?
|
2026-01-29 15:43:21 +01:00
|
|
|
|
2026-05-18 13:30:54 +02:00
|
|
|
render_403 unless custom_field.visible?(User.current, project: @journable.project)
|
2026-01-29 15:43:21 +01:00
|
|
|
end
|
|
|
|
|
|
2026-05-18 13:30:54 +02:00
|
|
|
# When a CF is deleted we can no longer call visible? on it. For journables that support
|
|
|
|
|
# admin-only CFs (e.g. Project), block non-admins because the deleted CF could have been admin-only.
|
|
|
|
|
def deleted_cf_forbidden?(custom_field)
|
|
|
|
|
custom_field.nil? &&
|
|
|
|
|
@journable.admin_only_custom_fields_allowed? &&
|
|
|
|
|
!User.current.admin?
|
2026-01-29 15:43:21 +01:00
|
|
|
end
|
|
|
|
|
|
2020-09-16 11:26:15 +02:00
|
|
|
def journals_index_title
|
2023-02-06 09:32:26 +01:00
|
|
|
subject = @project ? @project.name : Setting.app_title
|
|
|
|
|
query_name = @query.new_record? ? I18n.t(:label_changes_details) : @query.name
|
|
|
|
|
"#{subject}: #{query_name}"
|
2020-09-16 11:26:15 +02:00
|
|
|
end
|
2011-05-13 09:55:55 -07:00
|
|
|
end
|