Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b465b75
Refactor postgresql? method to improve configuration handling
mboremski Nov 19, 2025
d8658c6
Add step to install Subversion in workflow
mboremski Nov 19, 2025
b091722
Merge pull request #1 from mboremski/develop
mboremski Nov 19, 2025
1211351
Merge pull request #39 from mboremski/master
liaham Nov 20, 2025
37d6293
Updates to support Redmine 6 and Rails 7
dcornewell Feb 6, 2026
42e46fe
Fix HTTP 500 error when invalid date is entered in workload filter
claude Mar 21, 2026
fb4564a
Merge pull request #1 from mboremski/claude/fix-workload-issue-41-sQq72
mboremski Mar 21, 2026
ca4be9d
Fix HTTP 500 when 'Use as today' date is after end date
claude Mar 21, 2026
8c321d1
Merge pull request #2 from mboremski/claude/fix-workload-issue-40-sQq72
mboremski Mar 21, 2026
82f1a7e
Merge pull request #42 from dcornewell/rm6_rail7
liaham Mar 21, 2026
9eef5b3
Merge branch 'xmera-circle:develop' into develop
mboremski Aug 26, 2026
62abdab
CI: run tests against Redmine 6.1-stable
mboremski Aug 26, 2026
0c1c1c4
Fix test_helper for Rails 7.2: do not use Rails.root at load time
mboremski Aug 26, 2026
08c7604
CI: upload test.log when a run fails
mboremski Aug 26, 2026
7575e0a
Fix WlUserSelectionTest for Redmine 6 fixture loading
mboremski Aug 26, 2026
86f220d
Declare Redmine 6.1 as the supported version
mboremski Aug 26, 2026
9e80dfc
Remove code that cannot run on Redmine 6
mboremski Aug 26, 2026
ddf3214
CHANGELOG: record the fixes that went into 4.0.0
mboremski Aug 26, 2026
f602deb
Use sprite_icon for the four icon links
mboremski Aug 26, 2026
3a0b051
Rubocop: target Ruby 3.2
mboremski Aug 26, 2026
efa2c50
Load the plugin's assets only on the plugin's own pages
mboremski Aug 26, 2026
e185833
Merge pull request #43 from mboremski/develop
mboremski Aug 26, 2026
45d5574
CHANGELOG: complete the 4.0.0 section and set the release date
mboremski Aug 27, 2026
43f9bf8
Merge pull request #47 from mboremski/fix/changelog-4-0-0
mboremski Aug 27, 2026
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# This configuration is taken from Redmine DMSF plugin
# and adapted to the needs of Redmine Workload plugin.
#
# Targets Redmine 6.1-stable (Rails 7.2). Plugin versions 4.x require
# Redmine 6; for Redmine 5 use the 3.x line.
#
# Copyright © 2023 Liane Hampe <liaham@xmera.de>
# Copyright © 2022-23 Karel Pičman <karel.picman@kontron.com>
Expand Down Expand Up @@ -64,18 +67,20 @@ jobs:
RAILS_ENV: test
NAME: redmine_workload
steps:
- name: Install svn
run: sudo apt-get update && sudo apt-get install -y subversion
- name: Clone Redmine
# Get the latest stable Redmine
run: svn export http://svn.redmine.org/redmine/branches/5.0-stable/ redmine
run: svn export http://svn.redmine.org/redmine/branches/6.1-stable/ redmine
- name: Checkout plugin
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
path: redmine/plugins/${{ env.NAME }}
- name: Install Ruby and gems
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
ruby-version: '3.1'
ruby-version: '3.2'
- name: Setup database
# Create the database
run: |
Expand All @@ -98,6 +103,7 @@ jobs:
bundle exec rake db:migrate
bundle exec rake redmine:plugins:migrate
bundle exec rake redmine:load_default_data
bundle exec rake assets:precompile
env:
REDMINE_LANG: en
working-directory: redmine
Expand All @@ -108,6 +114,14 @@ jobs:
bundle exec rake redmine:plugins:test:units
bundle exec rake redmine:plugins:test:functionals
bundle exec rake redmine:plugins:test:integration
- name: Archive test.log
# Keep the log so failing runs can be diagnosed
if: failure()
uses: actions/upload-artifact@v4
with:
name: "test_${{matrix.engine}}.log"
path: redmine/log/test.log
if-no-files-found: ignore
- name: Cleanup
# Rollback plugin's changes to the database
# Stop the database engine
Expand Down
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ AllCops:
NewCops: enable
DisplayCopNames: true
DisplayStyleGuide: true
TargetRubyVersion: 2.7
TargetRubyVersion: 3.2

Exclude:
- '**/vendor/**/*'
Expand Down
41 changes: 40 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,45 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 4.0.0 - 2026-08-27

### Changed

* Updated for Redmine 6.1.x / Rails 7.2 compatibility
* Fixed Rails version comparisons to use Gem::Version
* Updated database adapter detection for Rails 7.2
* Updated ActiveRecord migrations to version 7.2
* Removed deprecated `unloadable` from controllers and models
* Fixed Ruby version comparison for PostgreSQL requirement
* Declared `requires_redmine version_or_higher: '6.1'`
* CI now runs against Redmine 6.1-stable on Ruby 3.2 instead of 5.0-stable
* Icons are rendered with `sprite_icon` instead of the legacy icon font
classes, so they follow the active theme
* The plugin's javascript and stylesheet are no longer injected into every
Redmine page, only into the plugin's own views
* Rubocop targets Ruby 3.2, the lowest version Redmine 6.1 allows

### Fixed

* HTTP 500 when an invalid date such as `2026-01-32` was entered in a workload
filter; invalid input now falls back to the default value (#41)
* HTTP 500 when the 'Use as today' date was set beyond the last day of the
displayed time span; the date is capped and a flash warning is shown (#40)
* `test/test_helper.rb` no longer uses `Rails.root` at load time. Rails 7.2 runs
plugin tests in a separate process that requires the test files before the
environment is loaded, where `Rails.root` does not exist
* `WlUserSelectionTest` accounts for Redmine 6's `fixtures :all`, which makes
`users(:users_008)` a member of two groups
* An unscoped `legend` rule in the plugin's stylesheet restyled the legend of
every fieldset in the whole Redmine installation; it is now scoped to the
plugin's own views

### Removed

* Dead `Rails.version < '6'` branches in `init.rb` and `user_patch.rb`
* Ruby version guard for PostgreSQL and `RedmineWorkload.postgresql?`,
unreachable since Redmine 6.1 requires Ruby >= 3.2

## 3.0.2 - 2023-07-24

### Deletes
Expand Down Expand Up @@ -108,4 +147,4 @@ not at the bottom of the page

---

**NOTE** Changes prior and equal to version 1.1.0 are not reported.
**NOTE** Changes prior and equal to version 1.1.0 are not reported.
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Workload Plugin for Redmine

![Redmine Workload Version](https://img.shields.io/badge/Redmine_Plugin-v3.0.2-red) ![Redmine Version](https://img.shields.io/badge/Redmine-v5.0.z-blue) ![Language Support](https://img.shields.io/badge/Languages-en,_de,_fr,_es,_it-green) ![Version Stage](https://img.shields.io/badge/Stage-release-important) ![ci](https://github.com/xmera-circle/redmine_workload/actions/workflows/5-0-stable.yml/badge.svg)
![Redmine Workload Version](https://img.shields.io/badge/Redmine_Plugin-v4.0.0-red) ![Redmine Version](https://img.shields.io/badge/Redmine-v6.1.z-blue) ![Language Support](https://img.shields.io/badge/Languages-en,_de,_fr,_es,_it-green) ![Version Stage](https://img.shields.io/badge/Stage-release-important) ![ci](https://github.com/xmera-circle/redmine_workload/actions/workflows/6-1-stable.yml/badge.svg)

A complete rewrite of the original workload-plugin from Rafael Calleja.
The plugin calculates how much work each user would have to do per day in order to hit the deadlines for all his issues.
Expand Down Expand Up @@ -33,7 +33,19 @@ The encoding options are the same as on the page itself and might depend on your

Workday settings are fixed now (see [#27](https://github.com/xmera-circle/redmine_workload/issues/27)) but lead to restrictions for PostgreSQL user.

:warning: **With PostgreSQL installed you need to run Ruby 3.1.z!**
:warning: **PostgreSQL requires Ruby 3.1 or newer.** Redmine 6.1 requires Ruby 3.2
or newer anyway, so this is no longer a separate constraint for the 4.x line.

## New Features in Version 4.0.0

### support of Redmine 6

Version 4.0.0 supports Redmine 6.1.z and is **not** backward compatible: the
database migrations are on `ActiveRecord::Migration[7.2]`, which Redmine 5
(Rails 6.1) cannot load. Installations on Redmine 5 stay on the 3.x line.

Redmine 6.0.z ships the same Rails 7.2 and is likely to work, but it is not
covered by the test workflow and therefore not claimed as supported.

## New Features in Version 2.2.0

Expand Down Expand Up @@ -140,4 +152,4 @@ Here you can export the values that are shown in the browser to use it in other
|Hours of overdue issues|*hours*|Aggregated hours of issues that are behind schedule.|
|Number of unplanned issues|*number*|Number of issues that are unplanned.|
|Hours of unplanned issues|*hours*|Aggregated hours of issues that are unplanned.|
|..date..|*datum* and *hours*|Column is named from the belonging datum. Lists per line the hours per day.|
|..date..|*datum* and *hours*|Column is named from the belonging datum. Lists per line the hours per day.|
19 changes: 12 additions & 7 deletions app/controllers/workloads_controller.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# frozen_string_literal: true

class WorkloadsController < ApplicationController
unloadable

helper :gantt
helper :issues
helper :projects
Expand Down Expand Up @@ -32,6 +30,12 @@ def index
# Make sure that last_day is at most 12 months after first_day to prevent
# long running times
@last_day = [(@first_day >> 12) - 1, @last_day].min

# Make sure that today is not after last_day to prevent a crash in the
# workload calculation (today would be outside the time span)
@today_capped = @today > @last_day
@today = [@today, @last_day].min

@time_span_to_display = @first_day..@last_day

if @date_check
Expand All @@ -56,6 +60,7 @@ def index
respond_to do |format|
format.html do
flash.now[:error] = l(:error_date_setting) unless @date_check
flash.now[:warning] = l(:warning_today_capped_to_last_day) if @today_capped
render action: :index
end

Expand Down Expand Up @@ -110,10 +115,10 @@ def assignee_ids
end

def sanitizeDateParameter(parameter, default)
if parameter.respond_to?(:to_date)
parameter.to_date
else
default
end
return default unless parameter.respond_to?(:to_date)

parameter.to_date
rescue Date::Error
default
end
end
2 changes: 0 additions & 2 deletions app/models/wl_national_holiday.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# frozen_string_literal: true

class WlNationalHoliday < ActiveRecord::Base
unloadable

validates :start, date: true
validates :end, date: true
validates :start, :end, :reason, presence: true
Expand Down
2 changes: 0 additions & 2 deletions app/models/wl_user_vacation.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# frozen_string_literal: true

class WlUserVacation < ActiveRecord::Base
unloadable

belongs_to :user, inverse_of: :wl_user_vacations, optional: true

validates :date_from, date: true
Expand Down
2 changes: 1 addition & 1 deletion app/views/settings/_workload_settings.erb
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,6 @@
<%= "checked" if settings['workload_of_parent_issues'] != '' %>
>
<em class='info'><%= l(:info_include_parent_tasks) %></em>
<em class='info icon icon-warning'><%= l(:warning_include_parent_tasks) %></em>
<em class='info icon icon-warning'><%= sprite_icon('warning', l(:warning_include_parent_tasks)) %></em>
</p>
</fieldset>
2 changes: 1 addition & 1 deletion app/views/wl_national_holiday/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<h2><%= l(:workload_holiday_title)%></h2>

<%= link_to l(:label_new), {controller: "wl_national_holiday", action: "new"}, class: "icon icon-add" if @is_allowed%>
<%= link_to sprite_icon('add', l(:label_new)), {controller: "wl_national_holiday", action: "new"}, class: "icon icon-add" if @is_allowed%>

<p id="year-nav" >
<%= link_to "<<", :controller => 'wl_national_holiday', :action => "index", :year => @this_year-1 %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/wl_user_vacations/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<h2><%= l(:workload_user_vacation_site_title) %> » <%= User.current.name %></h2>

<%= link_to l(:label_new), new_wl_user_vacation_path, :class => 'icon icon-add' if @is_allowed %>
<%= link_to sprite_icon('add', l(:label_new)), new_wl_user_vacation_path, :class => 'icon icon-add' if @is_allowed %>

<% unless @wl_user_vacations.empty?%>
<%= render(partial: "show_list", locals: {wl_user_vacations: @wl_user_vacations, is_allowed: @is_allowed}) %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/workloads/_filters.erb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@
<%= label_tag :workload_groups, l(:workload_show_filter_group) %>
<%= select_tag :workload_groups, group_options_for_select(@groups&.allowed_to_display, @groups&.selected), :name => 'workload[groups][]', :multiple => true, :onchange => "this.form.workload_users.selectedIndex=-1;" %>
</div>
<%= link_to_function l(:button_apply), 'jQuery("#filter_form").submit()', :class => 'apply icon icon-checked' %>
<%= link_to_function sprite_icon('checked', l(:button_apply)), 'jQuery("#filter_form").submit()', :class => 'apply icon icon-checked' %>
</fieldset>
<% end %>
2 changes: 1 addition & 1 deletion assets/stylesheets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
display: inline-block;
}

legend {
.controller-workloads legend {
color: var(--anthracite);
}

Expand Down
1 change: 1 addition & 0 deletions config/locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ de:
workload_unscheduled_issues_num: 'Anzahl ungeplanter Tickets:'
workload_unscheduled_issues_hours: 'Stunden aus ungeplanten Tickets:'
error_date_setting: 'Überprüfen Sie Ihre Eingabe! Das Enddatum (bis) liegt vor dem Startdatum (von).'
warning_today_capped_to_last_day: 'Das "Als Heute verwenden"-Datum lag nach dem Enddatum und wurde auf das Enddatum gesetzt.'
error_encoding_setting: 'Zeichenkodierung nicht erlaubt. Gültige Werte: %{value}.'
label_workload_calculation: Workloadberechnung
label_include_parent_tasks: Hauptaufgaben einbeziehen
Expand Down
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ en:
workload_unscheduled_issues_num: 'Number of unscheduled issues:'
workload_unscheduled_issues_hours: 'Hours of unscheduled issues:'
error_date_setting: 'Please check your data! The end date is before the start date.'
warning_today_capped_to_last_day: 'The "Use as today" date was after the end date and has been set to the end date.'
error_encoding_setting: 'Character encoding not allowed. Valid values: %{value}.'
label_workload_calculation: Workload calculation
label_include_parent_tasks: Include parent tasks
Expand Down
2 changes: 1 addition & 1 deletion db/migrate/001_create_wl_user_vacations.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

class CreateWlUserVacations < ActiveRecord::Migration[5.2]
class CreateWlUserVacations < ActiveRecord::Migration[7.2]
def change
create_table :wl_user_vacations do |t|
t.belongs_to :user, index: true, null: false
Expand Down
2 changes: 1 addition & 1 deletion db/migrate/002_create_wl_user_data.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

class CreateWlUserData < ActiveRecord::Migration[5.2]
class CreateWlUserData < ActiveRecord::Migration[7.2]
def change
create_table :wl_user_datas do |t|
t.belongs_to :user, index: true, null: false
Expand Down
2 changes: 1 addition & 1 deletion db/migrate/003_create_wl_national_holidays.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

class CreateWlNationalHolidays < ActiveRecord::Migration[5.2]
class CreateWlNationalHolidays < ActiveRecord::Migration[7.2]
def change
create_table :wl_national_holidays do |t|
t.date :start, null: false
Expand Down
2 changes: 1 addition & 1 deletion db/migrate/004_add_main_group_to_wl_user_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Adds main_group column to store the users group which should be considered when
# calculating group workloads.
#
class AddMainGroupToWlUserData < ActiveRecord::Migration[5.2]
class AddMainGroupToWlUserData < ActiveRecord::Migration[7.2]
def change
add_column :wl_user_datas, :main_group, :integer
add_index :wl_user_datas, :main_group
Expand Down
36 changes: 22 additions & 14 deletions init.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,9 @@
description 'This is a plugin for Redmine, originally developed by Rafael Calleja. It ' \
'displays the estimated number of hours users and groups have to work to finish ' \
'all their assigned issus on time.'
version '3.0.2'
version '4.0.0'
url 'https://github.com/xmera-circle/redmine_workload'

if RedmineWorkload.postgresql? && RUBY_VERSION <= '3.1'
msg = "#{name} requires at least Ruby 3.1.z when using postgresql database."
raise Redmine::PluginRequirementError, msg
end
requires_redmine version_or_higher: '6.1'

menu :top_menu,
:WorkLoad,
Expand Down Expand Up @@ -49,16 +45,28 @@
permission :edit_user_data, wl_user_datas: :update
end

if Rails.version < '6'
plugin = Redmine::Plugin.find(:redmine_workload)
Rails.application.configure do
config.autoload_paths << "#{plugin.directory}/app/presenters"
end
end

class RedmineToolbarHookListener < Redmine::Hook::ViewListener
def view_layouts_base_html_head(_context)
# Controllers whose views need the plugin's assets.
WORKLOAD_CONTROLLERS = %w[
workloads
wl_user_datas
wl_user_vacations
wl_national_holiday
].freeze

def view_layouts_base_html_head(context = {})
return '' unless workload_page?(context)

javascript_include_tag('slides', plugin: :redmine_workload) +
stylesheet_link_tag('style', plugin: :redmine_workload)
end

private

def workload_page?(context)
controller = context[:controller]
return false unless controller

WORKLOAD_CONTROLLERS.include?(controller.params[:controller].to_s)
end
end
8 changes: 0 additions & 8 deletions lib/redmine_workload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,3 @@
require File.expand_path('redmine_workload/wl_issue_state', __dir__)
require File.expand_path('redmine_workload/wl_user_data_finder', __dir__)
require File.expand_path('redmine_workload/wl_user_data_defaults', __dir__)

# Simple Rails related methods
module RedmineWorkload
# Check whether Redmine is running postgresql database
def self.postgresql?
ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'postgresql'
end
end
8 changes: 0 additions & 8 deletions lib/redmine_workload/extensions/user_patch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,3 @@ def main_group_id
end
end
end

if Rails.version < '6'
Rails.configuration.to_prepare do
unless User.included_modules.include?(RedmineWorkload::Extensions::UserPatch)
User.prepend RedmineWorkload::Extensions::UserPatch
end
end
end
2 changes: 0 additions & 2 deletions lib/redmine_workload/hooks/after_plugins_loaded_hook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ module RedmineWorkload
module Hooks
class AfterPluginsLoadedHook < Redmine::Hook::Listener
def after_plugins_loaded(_context = {})
return unless Rails.version > '6'

patch = RedmineWorkload::Extensions::UserPatch
klass = User
klass.prepend patch unless klass.included_modules.include?(patch)
Expand Down
Loading
Loading