)]
+ def get_descendants(item:, include_self: true)
+ if include_self
+ Success(item.self_and_descendants)
+ else
+ Success(item.descendants)
+ end
+ end
+
# Move an item/node to a new parent item/node
# @param item [CustomField::Hierarchy::Item] the parent of the node
# @param new_parent [CustomField::Hierarchy::Item] the new parent of the node
@@ -97,18 +110,37 @@ def move_item(item:, new_parent:)
# Reorder the item along its siblings.
# @param item [CustomField::Hierarchy::Item] the parent of the node
- # @param new_sort_order [Integer] the new parent of the node
- # @return [Success(CustomField::Hierarchy::Item)]
+ # @param new_sort_order [Integer] the new position of the node
+ # @return [Success]
def reorder_item(item:, new_sort_order:)
- old_item = item.siblings.where(sort_order: new_sort_order).first
- Success(old_item.prepend_sibling(item))
+ return Success() if item.siblings.empty?
+
+ new_sort_order = [0, new_sort_order.to_i].max
+
+ return Success() if item.sort_order == new_sort_order
+
+ update_item_order(item:, new_sort_order:)
+
+ Success()
end
- def soft_delete_item(item)
+ def soft_delete_item(item:)
# Soft delete the item and children
raise NotImplementedError
end
+ def hashed_subtree(item:, depth:)
+ if depth >= 0
+ Success(item.hash_tree(limit_depth: depth + 1))
+ else
+ Success(item.hash_tree)
+ end
+ end
+
+ def descendant_of?(item:, parent:)
+ item.descendant_of?(parent) ? Success() : Failure()
+ end
+
private
def create_root_item(custom_field)
@@ -118,8 +150,11 @@ def create_root_item(custom_field)
Success(item)
end
- def create_child_item(validation:)
- item = validation[:parent].children.create(label: validation[:label], short: validation[:short])
+ def create_child_item(validation:, sort_order: nil)
+ attributes = validation.to_h
+ attributes[:sort_order] = sort_order - 1 if sort_order
+
+ item = validation[:parent].children.create(**attributes)
return Failure(item.errors) if item.new_record?
Success(item)
@@ -132,6 +167,16 @@ def update_item_attributes(item:, attributes:)
Failure(item.errors)
end
end
+
+ def update_item_order(item:, new_sort_order:)
+ target_item = item.siblings.find_by(sort_order: new_sort_order)
+ if target_item.present?
+ target_item.prepend_sibling(item)
+ else
+ target_item = item.siblings.last
+ target_item.append_sibling(item)
+ end
+ end
end
end
end
diff --git a/app/services/journals/update_service.rb b/app/services/journals/update_service.rb
index 229838dd8df2..db3ba619ede4 100644
--- a/app/services/journals/update_service.rb
+++ b/app/services/journals/update_service.rb
@@ -27,5 +27,15 @@
#++
module Journals
- class UpdateService < ::BaseServices::Update; end
+ class UpdateService < ::BaseServices::Update
+ protected
+
+ def after_perform(call)
+ OpenProject::Notifications.send(OpenProject::Events::JOURNAL_UPDATED,
+ journal: call.result,
+ send_notification: Journal::NotificationConfiguration.active?)
+
+ call
+ end
+ end
end
diff --git a/app/views/account/_password_login_form.html.erb b/app/views/account/_password_login_form.html.erb
index 04b2e0d9d507..7ae03893f00d 100644
--- a/app/views/account/_password_login_form.html.erb
+++ b/app/views/account/_password_login_form.html.erb
@@ -27,7 +27,12 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
-<%= styled_form_tag({action: "login"}, autocomplete: 'off', class: '-wide-labels user-login--form') do %>
+<%= styled_form_tag(
+ {action: "login"},
+ autocomplete: 'off',
+ class: '-wide-labels user-login--form',
+ data: { turbo: false } # allow redirects without turbo
+ ) do %>
<%= back_url_hidden_field_tag %>