Skip to content

Commit

Permalink
Release OpenProject 12.3.2
Browse files Browse the repository at this point in the history
  • Loading branch information
machisuji committed Oct 26, 2022
2 parents 08a94e2 + 782e700 commit 60c74d9
Show file tree
Hide file tree
Showing 13 changed files with 165 additions and 43 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Expand Up @@ -129,7 +129,7 @@ gem 'rack-protection', '~> 2.2.0'
gem 'rack-attack', '~> 6.6.0'

# CSP headers
gem 'secure_headers', '~> 6.4.0'
gem 'secure_headers', '~> 6.5.0'

# Browser detection for incompatibility checks
gem 'browser', '~> 5.3.0'
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Expand Up @@ -904,7 +904,7 @@ GEM
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
secure_headers (6.4.0)
secure_headers (6.5.0)
selenium-webdriver (4.5.0)
childprocess (>= 0.5, < 5.0)
rexml (~> 3.2, >= 3.2.5)
Expand Down Expand Up @@ -1154,7 +1154,7 @@ DEPENDENCIES
rubytree (~> 2.0.0)
sanitize (~> 6.0.0)
sassc-rails
secure_headers (~> 6.4.0)
secure_headers (~> 6.5.0)
selenium-webdriver (~> 4.0)
semantic (~> 1.6.1)
sentry-delayed_job (~> 5.4.0)
Expand Down
19 changes: 12 additions & 7 deletions app/workers/journals/completed_job.rb
Expand Up @@ -34,7 +34,7 @@ def schedule(journal, send_mails)
return unless supported?(journal)

set(wait_until: delivery_time)
.perform_later(journal.id, send_mails)
.perform_later(journal.id, journal.updated_at, send_mails)
end

def aggregated_event(journal)
Expand All @@ -61,12 +61,17 @@ def supported?(journal)
end
end

def perform(journal_id, send_mails)
journal = Journal.find_by(id: journal_id)

# If the WP has been deleted the journal will have been deleted, too.
# Or the journal might have been replaced
return if journal.nil?
def perform(journal_id, journal_updated_at, send_mails)
# If the WP has been deleted, the journal will have been deleted, too.
# The journal might also have been updated in the meantime. This happens if
# the journable is updated a second time by the same user within the aggregation time.
# If aggregation happened, then the job scheduled when the journal was updated the second time
# will take care of notifying later.
# If another user were to update the journable even within aggregation time,
# the journal would not be altered. It is thus safe to consider the journal
# final.
journal = Journal.find_by(id: journal_id, updated_at: journal_updated_at)
return unless journal

notify_journal_complete(journal, send_mails)
end
Expand Down
4 changes: 3 additions & 1 deletion config/initializers/postgresql_timestamp.rb
@@ -1,2 +1,4 @@
# Use timestampz to create new timestamp columns, so that we get WITH TIME ZONE support
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
end
15 changes: 15 additions & 0 deletions db/migrate/20221017073431_remove_orphaned_tokens.rb
@@ -1,10 +1,25 @@
require_relative "./migration_utils/column"

class RemoveOrphanedTokens < ActiveRecord::Migration[7.0]
def up
Token::Base.where.not(user_id: User.select(:id)).delete_all

# Make sure we have bigint columns on both sides so the foreign key can be added.
# It could be that they are of type numeric if the data was migrated from MySQL once.
change_column_type! :users, :id, :bigint
change_column_type! :tokens, :user_id, :bigint

add_foreign_key :tokens, :users

User.reset_column_information
Token::Base.reset_column_information
end

def down
# Nothing to do
end

def change_column_type!(table, column, type)
Migration::MigrationUtils::Column.new(connection, table, column).change_type! type
end
end
64 changes: 64 additions & 0 deletions db/migrate/migration_utils/column.rb
@@ -0,0 +1,64 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2022 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 Migration
module MigrationUtils
class Column
attr_reader :connection, :table, :name, :ar_column

def initialize(connection, table, name)
@connection = connection
@table = table
@name = name
@ar_column = connection.columns(table.to_s).find { |col| col.name == name.to_s }

raise ArgumentError, "Column not found: #{field}" if ar_column.nil?
end

def change_type!(type)
return if self.type == type.to_s

connection.change_column table, name, type

return if id_seq_name.nil?

connection.execute <<~SQL.squish
ALTER SEQUENCE #{id_seq_name} AS #{type};
SQL
end

def id_seq_name
@id_seq_name ||= connection.serial_sequence table, name
end

def type
ar_column.sql_type.to_s
end
end
end
end
29 changes: 29 additions & 0 deletions docs/release-notes/12-3-2/README.md
@@ -0,0 +1,29 @@
---
title: OpenProject 12.3.2
sidebar_navigation:
title: 12.3.2
release_version: 12.3.2
release_date: 2022-10-26
---

# OpenProject 12.3.2

Release date: 2022-10-26

We released [OpenProject 12.3.2](https://community.openproject.com/versions/1608).
The release contains several bug fixes and we recommend updating to the newest version.

<!--more-->
#### Bug fixes and changes

- Fixed: Multiples Identicals Webhooks are sent for each WP change applied, not considering the Aggregated WorkPackage Journal \[[#44158](https://community.openproject.com/wp/44158)\]
- Fixed: Moving a week-days-only WP on Gantt chart and falling its end-date to a non-working date is not possible \[[#44501](https://community.openproject.com/wp/44501)\]
- Fixed: Migration to 12.3.1 fails with Key columns "user_id" and "id" are of incompatible types: numeric and bigint. \[[#44634](https://community.openproject.com/wp/44634)\]
- Fixed: rake assets:precompile fails with NameError: uninitialized constant ActiveRecord::ConnectionAdapters::PostgreSQLAdapter \[[#44635](https://community.openproject.com/wp/44635)\]

#### Contributions
A big thanks to community members for reporting bugs and helping us identifying and providing fixes.

Special thanks for reporting and finding bugs go to

Nico Aymet, Johannes Zellner
7 changes: 7 additions & 0 deletions docs/release-notes/README.md
Expand Up @@ -14,6 +14,13 @@ Stay up to date and get an overview of the new features included in the releases
<!--- New release notes are generated below. Do not remove comment. -->
<!--- RELEASE MARKER -->

## 12.3.2

Release date: 2022-10-26

[Release Notes](12-3-2/)


## 12.3.1

Release date: 2022-10-24
Expand Down
Expand Up @@ -61,6 +61,8 @@ export class TimelineCellRenderer {

public ganttChartRowHeight:number;

public mouseDirection:MouseDirection;

public fieldRenderer:DisplayFieldRenderer = new DisplayFieldRenderer(this.injector, 'timeline');

protected mouseDownCursorType:string;
Expand Down Expand Up @@ -174,6 +176,7 @@ export class TimelineCellRenderer {
if (renderInfo.viewParams.activeSelectionMode) {
renderInfo.viewParams.activeSelectionMode(renderInfo.workPackage);
ev.preventDefault();
this.mouseDirection = 'both';
return 'both'; // irrelevant
}

Expand All @@ -184,17 +187,20 @@ export class TimelineCellRenderer {
if (jQuery(ev.target!).hasClass(classNameLeftHandle)) {
// only left
direction = 'left';
this.mouseDirection = 'left';
this.mouseDownCursorType = 'col-resize';
if (projection.startDate === null) {
projection.startDate = projection.dueDate;
}
} else if (jQuery(ev.target!).hasClass(classNameRightHandle) || dateForCreate) {
// only right
direction = 'right';
this.mouseDirection = 'right';
this.mouseDownCursorType = 'col-resize';
} else {
// both
direction = 'both';
this.mouseDirection = 'both';
this.mouseDownCursorType = 'ew-resize';
}

Expand All @@ -207,6 +213,7 @@ export class TimelineCellRenderer {
projection.startDate = dateForCreate;
projection.dueDate = moment(dateForCreate).add(duration, 'days').format('YYYY-MM-DD');
direction = 'dragright';
this.mouseDirection = 'dragright';
}

this.updateLabels(true, labels, renderInfo.change);
Expand Down Expand Up @@ -452,15 +459,17 @@ export class TimelineCellRenderer {
}
}

cursorOrDatesAreNonWorking(evOrDates:MouseEvent|Moment[], renderInfo:RenderInfo):boolean {
cursorOrDatesAreNonWorking(evOrDates:MouseEvent|Moment[], renderInfo:RenderInfo, direction?:MouseDirection|null):boolean {
if (renderInfo.workPackage.ignoreNonWorkingDays) {
return false;
}

const dates = (evOrDates instanceof MouseEvent)
? [this.cursorDateAndDayOffset(evOrDates, renderInfo)[0]]
: evOrDates;

if (!renderInfo.workPackage.ignoreNonWorkingDays && direction === 'both' && this.weekdayService.isNonWorkingDay(dates[dates.length - 1].toDate())) {
return false;
}
return dates.some((date) => this.weekdayService.isNonWorkingDay(date.toDate()));
}

Expand Down Expand Up @@ -518,7 +527,7 @@ export class TimelineCellRenderer {
// Check for non-working days and display a not-allowed cursor
// when the startDate, dueDate are non-working days
const { startDate, dueDate } = renderInfo.change.projectedResource;
const invalidDates = this.cursorOrDatesAreNonWorking([moment(startDate), moment(dueDate)], renderInfo);
const invalidDates = this.cursorOrDatesAreNonWorking([moment(startDate), moment(dueDate)], renderInfo, this.mouseDirection);

if (invalidDates) {
this.workPackageTimeline.forceCursor('not-allowed');
Expand Down
Expand Up @@ -225,7 +225,7 @@ export function registerWorkPackageMouseHandler(this:void,

// Cancel changes if the startDate or dueDate are not allowed
const { startDate, dueDate } = change.projectedResource;
const invalidDates = renderer.cursorOrDatesAreNonWorking([moment(startDate), moment(dueDate)], renderInfo);
const invalidDates = renderer.cursorOrDatesAreNonWorking([moment(startDate), moment(dueDate)], renderInfo, direction);

if (cancelled || change.isEmpty() || invalidDates) {
cancelChange();
Expand Down
2 changes: 1 addition & 1 deletion lib/open_project/version.rb
Expand Up @@ -33,7 +33,7 @@ module OpenProject
module VERSION # :nodoc:
MAJOR = 12
MINOR = 3
PATCH = 1
PATCH = 2

class << self
# Used by semver to define the special version (if any).
Expand Down
8 changes: 4 additions & 4 deletions spec/models/work_package/openproject_notifications_spec.rb
Expand Up @@ -61,23 +61,23 @@
end

it "are triggered" do
expect(journal_ids).to include(work_package.journals.last.id)
expect(journal_ids).to match [work_package.journals.last.id]
end
end

describe 'when after update' do
before do
work_package

perform_enqueued_jobs
journal_ids.clear

work_package.update subject: 'the wind of change'
work_package.update(subject: 'the wind of change')

perform_enqueued_jobs
end

it "are triggered" do
expect(journal_ids).to include(work_package.journals.last.id)
expect(journal_ids).to match [work_package.journals.last.id]
end
end
end
Expand Down
37 changes: 14 additions & 23 deletions spec/workers/journals/completed_job_spec.rb
Expand Up @@ -32,20 +32,7 @@
let(:send_mail) { true }

let(:journal) do
build_stubbed(:journal, journable:).tap do |j|
allow(Journal)
.to receive(:find)
.with(j.id.to_s)
.and_return(j)
allow(Journal)
.to receive(:find_by)
.with(id: j.id)
.and_return(j)
allow(Journal)
.to receive(:exists?)
.with(id: j.id)
.and_return(true)
end
build_stubbed(:journal, journable:)
end

describe '.schedule' do
Expand All @@ -63,6 +50,7 @@
.to have_enqueued_job(described_class)
.at(Setting.journal_aggregation_time_minutes.to_i.minutes.from_now)
.with(journal.id,
journal.updated_at,
send_mail)
end
end
Expand Down Expand Up @@ -94,7 +82,16 @@
end

describe '#perform' do
subject { described_class.new.perform(journal.id, send_mail) }
subject { described_class.new.perform(journal.id, journal.updated_at, send_mail) }

let(:find_by_journal) { journal }

before do
allow(Journal)
.to receive(:find_by)
.with(id: journal.id, updated_at: journal.updated_at)
.and_return(find_by_journal)
end

shared_examples_for 'sends a notification' do |event|
it 'sends a notification' do
Expand Down Expand Up @@ -132,15 +129,9 @@
OpenProject::Events::AGGREGATED_NEWS_JOURNAL_READY
end

context 'with a non non-existant journal' do
context 'with a non non-existent journal (either because the journable was deleted or the journal updated)' do
let(:journable) { build_stubbed(:work_package) }

before do
allow(Journal)
.to receive(:find_by)
.with(id: journal.id)
.and_return(nil)
end
let(:find_by_journal) { nil }

it 'sends no notification' do
allow(OpenProject::Notifications)
Expand Down

0 comments on commit 60c74d9

Please sign in to comment.