Skip to content

Commit

Permalink
Release OpenProject 15.2.1
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverguenther committed Feb 5, 2025
2 parents 711a70e + ffbd09e commit ef0508f
Show file tree
Hide file tree
Showing 316 changed files with 2,176 additions and 2,027 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ def initialize(tabs: nil)
end

def breadcrumb_items
[{ href: admin_index_path, text: t("label_administration") },
I18n.t("menus.breadcrumb.nested_element", section_header: t(:"attribute_help_texts.label_plural"),
title: I18n.t(currently_selected_tab[:label].to_s)).html_safe]
[
{ href: admin_index_path, text: t("label_administration") },
helpers.nested_breadcrumb_element(t(:"attribute_help_texts.label_plural"),
I18n.t(currently_selected_tab[:label].to_s))
]
end

def currently_selected_tab
Expand Down
8 changes: 5 additions & 3 deletions app/components/custom_fields/index_page_header_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ def initialize(tabs: nil)
end

def breadcrumb_items
[{ href: admin_index_path, text: t("label_administration") },
I18n.t("menus.breadcrumb.nested_element", section_header: t(:label_custom_field_plural),
title: I18n.t(currently_selected_tab[:label].to_s)).html_safe]
[
{ href: admin_index_path, text: t("label_administration") },
helpers.nested_breadcrumb_element(t(:label_custom_field_plural),
I18n.t(currently_selected_tab[:label].to_s))
]
end

def currently_selected_tab
Expand Down
2 changes: 1 addition & 1 deletion app/components/members/index_page_header_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def current_breadcrumb_element

if @query && query_name
if menu_header.present?
I18n.t("menus.breadcrumb.nested_element", section_header: menu_header, title: query_name).html_safe
helpers.nested_breadcrumb_element(menu_header, query_name)
else
query_name
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def breadcrumb_items

def current_breadcrumb_element
if current_section && current_section.header.present?
I18n.t("menus.breadcrumb.nested_element", section_header: current_section.header, title: page_title).html_safe
helpers.nested_breadcrumb_element(current_section.header, page_title)
else
page_title
end
Expand Down
2 changes: 1 addition & 1 deletion app/components/projects/index_page_header_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def current_breadcrumb_element
return page_title if query.name.blank?

if current_section && current_section.header.present?
I18n.t("menus.breadcrumb.nested_element", section_header: current_section.header, title: query.name).html_safe
helpers.nested_breadcrumb_element(current_section.header, query.name)
else
page_title
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
@import "helpers"

.work-packages-activities-tab-journals-new-component
&--input-trigger-column
width: 100%
overflow: hidden
button
min-width: 0
background: var(--bgColor-default)
cursor: text
.Button-content
span
display: block
@include text-shortener
&--ck-editor-column
width: calc(100% - 40px)
// specific ck editor adjustments
Expand Down
8 changes: 8 additions & 0 deletions app/helpers/breadcrumb_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ def full_breadcrumbs
end
end

def nested_breadcrumb_element(section_header, title)
output = "".html_safe
output << "#{section_header}: "
output << content_tag(:strong, title)

output
end

def breadcrumb_paths(*args)
if args.empty?
@breadcrumb_paths ||= [default_breadcrumb]
Expand Down
7 changes: 5 additions & 2 deletions app/models/custom_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,11 @@ def possible_user_values_options(obj)
.in_visible_project_or_me(User.current)
end

scope
.select(User::USER_FORMATS_STRUCTURE[Setting.user_format].map(&:to_s), "id", "type")
user_format_columns = User::USER_FORMATS_STRUCTURE[Setting.user_format].map(&:to_s)
# Always include lastname if not already included, as Groups always need a lastname (alias for name)
user_format_columns << "lastname" unless user_format_columns.include?("lastname")

scope.select(*user_format_columns, "id", "type")
end
end

Expand Down
6 changes: 4 additions & 2 deletions app/models/work_packages/scopes/directly_related.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ module DirectlyRelated
def directly_related(work_package, ignored_relation: nil)
relations_without_ignored = ignored_relation ? Relation.where.not(id: ignored_relation.id) : Relation.all

where(id: relations_without_ignored.where(from_id: work_package).select(:to_id))
.or(where(id: relations_without_ignored.where(to_id: work_package).select(:from_id)))
from_id_relations = relations_without_ignored.where(from_id: work_package).select(:to_id)
to_id_relations = relations_without_ignored.where(to_id: work_package).select(:from_id)

where(arel_table[:id].in(Arel::Nodes::UnionAll.new(from_id_relations.arel, to_id_relations.arel)))
end
end
end
Expand Down
44 changes: 35 additions & 9 deletions app/models/work_packages/scopes/involving_user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,45 @@ module InvolvingUser
# for, or watcher, via a group or themself
#
# @param user User the user involved in work packages.
def involving_user(user)
WorkPackage.left_joins(:watchers)
.where(watchers: { user: })
.or(WorkPackage.where(assigned_to: user))
.or(WorkPackage.where(assigned_to: group_having(user)))
.or(WorkPackage.where(responsible: user))
.or(WorkPackage.where(responsible: group_having(user)))
def involving_user(user) # rubocop:disable Metrics/AbcSize
# Using CTEs and UNION for performance reasons
# The alternative would be the simpler:
# ```
# WorkPackage
# .left_joins(:watchers)
# .where(watchers: { user: })
# .or(WorkPackage.where(assigned_to: user))
# .or(WorkPackage.where(assigned_to: group_having(user)))
# .or(WorkPackage.where(responsible: user))
# .or(WorkPackage.where(responsible: group_having(user)))
# ```
# That however prevents indices from being used because of the or clauses on different columns.
watched = WorkPackage
.left_joins(:watchers)
.where(watchers: { user: })
.select(:id)
assigned = WorkPackage
.where("assigned_to_id IN (SELECT * FROM user_and_groups)")
.select(:id)
responsible = WorkPackage
.where("responsible_id IN (SELECT * FROM user_and_groups)")
.select(:id)

watched_and_assigned_and_responsible = Arel::Nodes::Union.new(watched.arel,
Arel::Nodes::Union.new(assigned.arel,
responsible.arel))

WorkPackage
.with(user_and_groups: user_and_groups_union(user))
.where(arel_table[:id].in(watched_and_assigned_and_responsible))
end

private

def group_having(user)
GroupUser.select(:group_id).where(user_id: user.id)
def user_and_groups_union(user)
in_group = GroupUser.select("group_id").where(["user_id = ?", user.id]).arel # rubocop:disable Rails/WhereEquals

Arel.sql("#{in_group.to_sql} UNION SELECT #{user.id}")
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions app/models/work_packages/scopes/relatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,11 @@ def not_having_transitive_relation(work_package, relation_type, ignored_relation
if relation_type == Relation::TYPE_RELATES
# Bypassing the recursive query in this case as only children and parent needs to be excluded.
# Using this more complicated statement since
# where.not(parent:id: work_package.id)
# where.not(parent_id: work_package.id)
# will lead to
# "parent_id != 123" which excludes
# work packages having parent_id NULL.
where.not(id: where(id: work_package.parent_id).or(where(parent_id: work_package.id)).select(:id))
where.not(id: unscoped.where(id: work_package.parent_id).or(unscoped.where(parent_id: work_package.id)).select(:id))
else
sql = <<~SQL.squish
WITH
Expand Down
15 changes: 10 additions & 5 deletions app/views/admin/settings/project_custom_fields/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,19 @@ See COPYRIGHT and LICENSE files for more details.
))
%>

<%= error_messages_for 'custom_field' %>
<%= error_messages_for "custom_field" %>

<% content_controller "admin--custom-fields",
dynamic: true,
"admin--custom-fields-format-config-value": OpenProject::CustomFieldFormatDependent.stimulus_config
%>

<%= labelled_tabular_form_for @custom_field, as: :custom_field,
url: admin_settings_project_custom_field_path(@custom_field),
html: {method: :put, id: 'custom_field_form'} do |f| %>
<%= render partial: 'custom_fields/form', locals: { f: f, custom_field: @custom_field } %>
html: {method: :put, id: "custom_field_form"} do |f| %>
<%= render partial: "custom_fields/form", locals: { f: f, custom_field: @custom_field } %>
<% if @custom_field.new_record? %>
<%= hidden_field_tag 'type', @custom_field.type %>
<%= hidden_field_tag "type", @custom_field.type %>
<% end %>
<%= styled_button_tag t(:button_save), class: '-highlight -with-icon icon-checkmark' %>
<%= styled_button_tag t(:button_save), class: "-highlight -with-icon icon-checkmark" %>
<% end %>
12 changes: 6 additions & 6 deletions app/views/admin/settings/project_custom_fields/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,19 @@ See COPYRIGHT and LICENSE files for more details.

<%= render(Settings::ProjectCustomFields::NewFormHeaderComponent.new) %>

<%= error_messages_for 'custom_field' %>
<%= error_messages_for "custom_field" %>

<% content_controller "admin--custom-fields",
dynamic: true,
'admin--custom-fields-format-config-value': OpenProject::CustomFieldFormatDependent.stimulus_config
"admin--custom-fields-format-config-value": OpenProject::CustomFieldFormatDependent.stimulus_config
%>

<%= labelled_tabular_form_for @custom_field, as: :custom_field,
url: admin_settings_project_custom_fields_path,
html: { id: 'custom_field_form' } do |f| %>
<%= render partial: 'custom_fields/form', locals: { f: f, custom_field: @custom_field } %>
html: { id: "custom_field_form" } do |f| %>
<%= render partial: "custom_fields/form", locals: { f: f, custom_field: @custom_field } %>
<% if @custom_field.new_record? %>
<%= hidden_field_tag 'type', @custom_field.type %>
<%= hidden_field_tag "type", @custom_field.type %>
<% end %>
<%= styled_button_tag t(:button_save), class: '-highlight -with-icon icon-checkmark' %>
<%= styled_button_tag t(:button_save), class: "-highlight -with-icon icon-checkmark" %>
<% end %>
12 changes: 8 additions & 4 deletions app/views/enumerations/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@ See COPYRIGHT and LICENSE files for more details.
<%=
render(Primer::OpenProject::PageHeader.new) do |header|
header.with_title { h @enumeration.name }
header.with_breadcrumbs([{ href: admin_index_path, text: t(:label_administration) },
{ href: enumerations_path, text: t(:label_enumerations) },
I18n.t("menus.breadcrumb.nested_element", section_header: t(@enumeration.option_name), title: h(@enumeration.name)).html_safe],
selected_item_font_weight: :normal)
header.with_breadcrumbs(
[
{ href: admin_index_path, text: t(:label_administration) },
{ href: enumerations_path, text: t(:label_enumerations) },
nested_breadcrumb_element(t(@enumeration.option_name), @enumeration.name)
],
selected_item_font_weight: :normal
)
end
%>

Expand Down
11 changes: 7 additions & 4 deletions app/views/enumerations/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,13 @@ See COPYRIGHT and LICENSE files for more details.
<%=
render(Primer::OpenProject::PageHeader.new) do |header|
header.with_title { t(:label_enumeration_new) }
header.with_breadcrumbs([{ href: admin_index_path, text: t(:label_administration) },
{ href: enumerations_path, text: t(:label_enumerations) },
I18n.t("menus.breadcrumb.nested_element", section_header: t(@enumeration.option_name), title: t(:label_enumeration_new)).html_safe],
selected_item_font_weight: :normal)
header.with_breadcrumbs(
[{ href: admin_index_path, text: t(:label_administration) },
{ href: enumerations_path, text: t(:label_enumerations) },
nested_breadcrumb_element(t(@enumeration.option_name), t(:label_enumeration_new))
],
selected_item_font_weight: :normal
)
end
%>

Expand Down
27 changes: 1 addition & 26 deletions app/workers/application_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,8 @@
class ApplicationJob < ActiveJob::Base
include ::JobStatus::ApplicationJobWithStatus
include SharedJobSetup
include JobPriority

##
# Return a priority number on the given payload
def self.priority_number(prio = :default)
case prio
when :high
0
when :notification
5
when :above_normal
7
when :below_normal
13
when :low
20
else
10
end
end

def self.queue_with_priority(value = :default)
if value.is_a?(Symbol)
super(priority_number(value))
else
super
end
end

def job_scheduled_at
GoodJob::Job.where(id: job_id).pick(:scheduled_at)
Expand Down
67 changes: 67 additions & 0 deletions app/workers/job_priority.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 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.
#++

module JobPriority
extend ActiveSupport::Concern

included do
# Default to queue with priority 10
queue_with_priority :default
end

class_methods do
##
# Return a priority number on the given payload
def priority_number(prio = :default)
case prio
when :high
0
when :notification
5
when :above_normal
7
when :below_normal
13
when :low
20
else
10
end
end

def queue_with_priority(value = :default)
if value.is_a?(Symbol)
super(priority_number(value))
else
super
end
end
end
end
3 changes: 3 additions & 0 deletions app/workers/mails/mailer_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
#
# It also adds retry logic to the job.
class Mails::MailerJob < ActionMailer::MailDeliveryJob
include JobPriority
queue_with_priority :high

include SharedJobSetup

# Retry mailing jobs 14 times with polynomial backoff (retries for ~ 1.5 days).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ def query

def alertable_work_packages
work_packages = WorkPackage
.with_status_open
.involving_user(user)
.involving_user(user)
.with_status_open

# `work_packages.to_sql` was producing SQL with weird select clauses that could
# not be used in a CTE, while doing `work_packages.pluck(:something)` was producing
Expand Down
Loading

0 comments on commit ef0508f

Please sign in to comment.