Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -68,16 +71,16 @@ jobs:
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 @@ -100,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 @@ -110,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
22 changes: 21 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

* Updated for Redmine 6.0.x / Rails 7.2 compatibility
* 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

### 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

### 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

Expand Down
16 changes: 14 additions & 2 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-v4.0.0-red) ![Redmine Version](https://img.shields.io/badge/v6.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
17 changes: 12 additions & 5 deletions app/controllers/workloads_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,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 @@ -54,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 @@ -108,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: 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 %>
6 changes: 4 additions & 2 deletions app/views/workloads/_trigger.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
# Parameters:
# trigger_for: set as "data-for"-attribute
#
# &#x25b6; is a right-pointing filled triangle.
# The icon is rendered once as 'angle-right'. The opened state is expressed by
# the 'opened' css class, which rotates it by 90 degrees. Do not swap the icon
# in JavaScript; the class is the single source of truth for the state.
%>

<span class="trigger closed" data-for="<%= trigger_for %>">&#x25b6;</span>
<span class="trigger closed" data-for="<%= trigger_for %>"><%= sprite_icon('angle-right') %></span>
6 changes: 1 addition & 5 deletions assets/javascripts/slides.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

$(document).ready(function() {
$('.trigger').click(function() {
var OPENED = '&#x25bc;'
var CLOSED = '&#x25b6;'
$(this).toggleClass('closed opened');

identifier = $(this).attr('data-for');
Expand Down Expand Up @@ -44,7 +42,6 @@ $(document).ready(function() {
$(this).show(); // but keep its 'children' closed if any
$(this).siblings('.invisible-issues-summary.' + identifierClasses).show();
});
$(this).html(OPENED);
}
else {
lowerHierarchieLevelClasses = bottomUpHierarchieChain.get(currentHierarchieLevel);
Expand All @@ -54,11 +51,10 @@ $(document).ready(function() {
$(css).hide();
$(css).siblings('.invisible-issues-summary.' + identifierClasses).hide();
currentHierarchieLevel = $(css).find('span.trigger.opened');
currentHierarchieLevel.html(CLOSED);
currentHierarchieLevel.removeClass('opened').addClass('closed');
currentHierarchieLevel.siblings('dl').hide();
})
$(this).siblings().hide();
$(this).html(CLOSED);
}
});
});
11 changes: 10 additions & 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 Expand Up @@ -146,6 +146,15 @@ legend {
margin-right: 4px;
}

.controller-workloads .data .trigger svg {
vertical-align: middle;
transition: transform 0.15s ease-in-out;
}

.controller-workloads .data .trigger.opened svg {
transform: rotate(90deg);
}

.controller-workloads table dt.mt-5,
.controller-workloads table dd.mt-5 {
margin-top: 5px;
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
35 changes: 21 additions & 14 deletions init.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@
'all their assigned issus on time.'
version '4.0.0'
url 'https://github.com/xmera-circle/redmine_workload'

if RedmineWorkload.postgresql? && Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.1.0')
msg = "#{name} requires at least Ruby 3.1.0 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,17 +45,28 @@
permission :edit_user_data, wl_user_datas: :update
end

# Rails 6+ handles autoloading differently with Zeitwerk
if Gem::Version.new(Rails.version) < Gem::Version.new('6.0')
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
14 changes: 0 additions & 14 deletions lib/redmine_workload.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +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?
db_config = if ActiveRecord::Base.configurations.respond_to?(:configs_for)
ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first
else
ActiveRecord::Base.configurations[Rails.env]
end
adapter = db_config.respond_to?(:adapter) ? db_config.adapter : db_config['adapter']
adapter == 'postgresql'
end
end
9 changes: 0 additions & 9 deletions lib/redmine_workload/extensions/user_patch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,3 @@ def main_group_id
end
end
end

# Rails 6+ uses after_plugins_loaded hook instead
if Gem::Version.new(Rails.version) < Gem::Version.new('6.0')
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 Gem::Version.new(Rails.version) >= Gem::Version.new('6.0')

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