Skip to content

Develop #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 51 commits into from
Jan 4, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
ce18362
added check for project (in case issue is deleted)
nutso Dec 31, 2013
88c7e79
ide files
nutso Jan 1, 2014
8075802
typo
nutso Jan 1, 2014
c161c0f
catching error when issue is deleted even though recurrence still exists
nutso Jan 1, 2014
32812ad
backed out unnecessary error handling -- what i thought was happening…
nutso Jan 1, 2014
be90410
checking for nil issue
nutso Jan 1, 2014
c0e8d76
updated notes with known issues
nutso Jan 1, 2014
aff42c6
removing dependence on localized strings for day/week/month/year sepa…
nutso Jan 1, 2014
4f44004
error message typo
nutso Jan 1, 2014
8e5b8f7
error message typo
nutso Jan 1, 2014
7f40e2a
migrating the data in the database to match the non-localized values …
nutso Jan 1, 2014
c9b6c52
logging
nutso Jan 1, 2014
48890e0
saving changes
nutso Jan 1, 2014
906a503
rolling back migration
nutso Jan 1, 2014
547aae5
checking for nil
nutso Jan 1, 2014
c6f093b
updated error messages to more clearly show where they were generated
nutso Jan 1, 2014
bdd37fd
returning an empty string so at least there are no type errors
nutso Jan 1, 2014
dc3850d
removed a space so definitely calling the right method
nutso Jan 1, 2014
f8d052a
skipping validation on save
nutso Jan 1, 2014
8b11556
removed a space so definitely calling the right method
nutso Jan 1, 2014
19c8b87
referencing generic conversion instead of object based
nutso Jan 1, 2014
2b06ffc
always returning a date value
nutso Jan 1, 2014
c3d4c16
standardized project column display
nutso Jan 1, 2014
523bd0e
localized more strings, updated en to refer to issues instead of tasks
nutso Jan 2, 2014
8446fbd
(#21) localized menu captions
nutso Jan 2, 2014
111f6cd
localized messages
nutso Jan 2, 2014
f3580d9
error messages and TODOs
nutso Jan 2, 2014
9637b7b
updated known issues and resolved for next version
nutso Jan 2, 2014
32c2fff
l function not available at init
nutso Jan 2, 2014
dbfd406
using t instead of l
nutso Jan 2, 2014
1ae47a9
saying irreversible instead of erroring out
nutso Jan 2, 2014
cafa50d
error message contained wrong variable name
nutso Jan 2, 2014
e0c3453
added marker for error
nutso Jan 2, 2014
643a55c
debugging
nutso Jan 2, 2014
7c8270a
calling redmine's l method #21
nutso Jan 2, 2014
3e98a5c
using nonlocalized symbols #21
nutso Jan 2, 2014
cb363d7
checking for errors
nutso Jan 2, 2014
f7927b7
back to raising the exception
nutso Jan 2, 2014
dce1d23
default recurrence pattern
nutso Jan 2, 2014
344f819
better display of error value
nutso Jan 2, 2014
5c1ca48
added section to note known issues with develop branch
nutso Jan 2, 2014
d6bc4ae
better organization for sections
nutso Jan 2, 2014
b00ed9b
centralized interval name to interval conversion
nutso Jan 3, 2014
2e148c5
setting interval from localized name before save
nutso Jan 4, 2014
58eb04d
debug statement and method renamed
nutso Jan 4, 2014
acbc7ef
typo
nutso Jan 4, 2014
5b9f3e7
debug statements
nutso Jan 4, 2014
decd5b7
setting interval before validation instead of before save
nutso Jan 4, 2014
e07028f
workaround to set interval despite update_attributes
nutso Jan 4, 2014
6faf37c
recurrence interval saves
nutso Jan 4, 2014
f109a65
Version 1.2.6
nutso Jan 4, 2014
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Eclipse project file
# Eclipse project and settings files
.project
.settings/*
# OS generated files
.DS_Store
.DS_Store?
Expand Down
40 changes: 39 additions & 1 deletion ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
# Recurring Tasks Redmine Plugin -- Release Notes

## Known Issues

### Master (Stable)

* (Confirmed) Deleting an issue neither generates a warning nor deletes the recurrence
* (Reported) Incompatibility with Redmine Stable 2.4 (#20) -- possibly line feed issue
* (Confirmed) No ability to view historic recurrences

### Develop

*

## Resolved in Develop (Next Version)

*

## Version 1.2.6

* After deleting an issue that still has a recurrence, recurrence views generate errors
* Menu captions not localized (#21)
* Changing the interval_day, interval_week, interval_month, or interval_year strings in the locale file, or changing locales, after adding recurrences generates an error

## Version 1.2.5

* resolved nil reference for fixed schedule recurrences with no due date (#16)
* includes german translation contributed by @skolarianer

## Version 1.2.0

* more intuitive management within the issues themselves
* add link to add recurrence when viewing an issue (#7)
* display existing recurrence if application when viewing an issue (#6)

## Version 1.1.0

* Project-specific recurring tasks view (#11)
* Better permissions (managed under issues) (#12)

## Version 1.0.2

* Show next scheduled recurrence when displaying a recurring task (#9)
Expand All @@ -9,7 +47,7 @@
## Version 1.0.1

* Added missing translations
* Fixed missing routes
* Fixed missing routes (#1)

## Version 1.0.0

Expand Down
3 changes: 2 additions & 1 deletion app/controllers/recurring_tasks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def edit
def update
logger.info "Updating recurring task #{params[:id]}"

params[:recurring_task][:interval_unit] = RecurringTask.get_interval_from_localized_name(params[:recurring_task][:interval_localized_name])
if @recurring_task.update_attributes(params[:recurring_task])
flash[:notice] = l(:recurring_task_saved)
redirect_to :action => :show
Expand Down Expand Up @@ -83,6 +84,6 @@ def find_recurring_task
end

def set_interval_units
@interval_units = RecurringTask::INTERVAL_UNITS
@interval_units = RecurringTask::INTERVAL_UNITS_LOCALIZED
end
end
83 changes: 72 additions & 11 deletions app/models/recurring_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,77 @@ class RecurringTask < ActiveRecord::Base
belongs_to :issue, :foreign_key => 'current_issue_id'
has_one :project, through: :issue

# these are the flags used in the database to denote the interval
# the actual text displayed to the user is controlled in the language file
INTERVAL_DAY = 'd'
INTERVAL_WEEK = 'w'
INTERVAL_MONTH = 'm'
INTERVAL_YEAR = 'y'

# must come before validations otherwise unitialized
INTERVAL_UNITS = [l(:interval_day), l(:interval_week), l(:interval_month), l(:interval_year)]
INTERVAL_UNITS_LOCALIZED = [l(:interval_day), l(:interval_week), l(:interval_month), l(:interval_year)]

validates :interval_unit, presence: true, inclusion: { in: RecurringTask::INTERVAL_UNITS, message: "#{l(:error_invalid_interval)} %{value}" }
validates :interval_localized_name, presence: true, inclusion: { in: RecurringTask::INTERVAL_UNITS_LOCALIZED, message: "#{l(:error_invalid_interval)} '%{value}' (Validation)" }
validates :interval_number, presence: true, numericality: {only_integer: true, greater_than: 0}
# cannot validate presence of issue if want to use other features
# validates :issue, presence: true
# validates :issue, presence: true # cannot validate presence of issue if want to use other features
# validates :fixed_schedule # requiring presence requires true

validates_associated :issue # just in case we build in functionality to add an issue at the same time, verify the issue is ok

# text for the interval name
def interval_localized_name
case interval_unit
when INTERVAL_DAY
l(:interval_day)
when INTERVAL_WEEK
l(:interval_week)
when INTERVAL_MONTH
l(:interval_month)
when INTERVAL_YEAR
l(:interval_year)
else
logger.error "#{l(:error_invalid_interval)} #{interval_unit} (interval_localized_name)"
""
end
end

# interval database name for the localized text
def interval_localized_name=(value)
@interval_localized_name = value
interval_unit= RecurringTask.get_interval_from_localized_name(value)
end

# used for migration #2
def self.get_interval_from_localized_name(value)
case value
when l(:interval_day)
INTERVAL_DAY
when l(:interval_week)
INTERVAL_WEEK
when l(:interval_month)
INTERVAL_MONTH
when l(:interval_year)
INTERVAL_YEAR
else
logger.error "#{l(:error_invalid_interval)} #{value} (interval_localized_name=)"
""
end
end

# time interval value of the recurrence pattern
def recurrence_pattern
case interval_unit
when l(:interval_day)
when INTERVAL_DAY
interval_number.days
when l(:interval_week)
when INTERVAL_WEEK
interval_number.weeks
when l(:interval_month)
when INTERVAL_MONTH
interval_number.months
when l(:interval_year)
when INTERVAL_YEAR
interval_number.years
else
logger.error "Unsupported interval unit: #{interval_unit}"
logger.error "#{l(:error_invalid_interval)} #{interval_unit} (recurrence_pattern)"
1.years
end
end

Expand All @@ -43,7 +90,12 @@ def self.all_for_project project

# next due date for the task, if there is one (relative tasks won't have a next schedule until the current issue is closed)
def next_scheduled_recurrence
previous_date_for_recurrence + recurrence_pattern unless previous_date_for_recurrence.nil?
if previous_date_for_recurrence.nil?
logger.error "Previous date for recurrence was nil for recurrence #{id}"
Date.today
else
previous_date_for_recurrence + recurrence_pattern
end
end

# whether a recurrence needs to be added
Expand Down Expand Up @@ -94,6 +146,15 @@ def self.add_recurrences!
# for a fixed schedule, this is the due date
# for a relative schedule, this is the date closed
def previous_date_for_recurrence
if fixed_schedule and !issue.due_date.nil? then issue.due_date elsif issue.closed_on.nil? then issue.updated_on else issue.closed_on end
if issue.nil?
logger.error "Issue is nil for recurrence #{id}."
Date.today
elsif fixed_schedule and !issue.due_date.nil?
issue.due_date
elsif issue.closed_on.nil?
issue.updated_on
else
issue.closed_on
end
end
end
2 changes: 1 addition & 1 deletion app/views/recurring_tasks/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<p><%= f.hidden_field :id %></p>
<p><%= label(:recurring_task, :current_issue_id, l(:field_issue)) %><%= collection_select('recurring_task', 'current_issue_id', @recurrable_issues, :id, :subj_date) %></p>
<p><%= f.number_field :interval_number %></p>
<p><%= label(:recurring_task, :interval_unit, l(:field_interval_unit)) %><%= select 'recurring_task', 'interval_unit', @interval_units %></p>
<p><%= label(:recurring_task, :interval_localized_name, l(:field_interval_unit)) %><%= select 'recurring_task', 'interval_localized_name', @interval_units %></p>
<p><%= f.check_box :fixed_schedule %> </p>
<p><%= f.submit %></p>
<% end %>
Expand Down
23 changes: 14 additions & 9 deletions app/views/recurring_tasks/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<table class="list">
<thead>
<tr>
<% if !@project %><th><%= l(:field_project)%></th><% end %>
<% if @project.nil? %><th><%= l(:field_project)%></th><% end %>
<th><%= l(:label_current_issue)%></th>
<th><%= l(:label_recurrence_pattern)%></th>
<th><%= l(:field_fixed_schedule)%></th>
Expand All @@ -18,14 +18,19 @@
</thead>
<tbody>
<% @recurring_tasks.each do |rt| %>
<tr class="<%= cycle('odd', 'even') %>">
<% if !@project %><td><%= link_to(rt.project, project_path(rt.project)) %></td><% end %>
<td><%= rt.issue.nil? ? l(:label_recurring_task_issue_empty) : link_to(rt.issue.subj_date, {:action => 'show', :id => rt.id, :project_id => rt.project.id}) %></td>
<td><%= pluralize(rt.interval_number, rt.interval_unit) %></td>
<td><%= rt.fixed_schedule %></td>
<td><%= format_date(rt.next_scheduled_recurrence) %></td>
<td><%= edit_button rt %></td>
</tr>
<tr class="<%= cycle('odd', 'even') %>">
<% begin %>
<% logger.info rt.id %>
<% if @project.nil? %><td><%= rt.project.nil? ? l(:label_recurring_task_project_empty) : link_to(rt.project, project_path(rt.project)) %></td><% end %>
<td><%= rt.issue.nil? ? l(:label_recurring_task_issue_empty) : link_to(rt.issue.subj_date, {:action => 'show', :id => rt.id, :project_id => rt.project.id}) %></td>
<td><%= pluralize(rt.interval_number, rt.interval_localized_name) %></td>
<td><%= rt.fixed_schedule %></td>
<td><%= format_date(rt.next_scheduled_recurrence) %></td>
<td><%= edit_button rt %></td>
<% rescue => e %>
<td><%= "ERROR: #{e}" %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
Expand Down
2 changes: 1 addition & 1 deletion app/views/recurring_tasks/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<h2><%= @recurring_task.issue.nil? ? l(:label_recurring_task_issue_empty) : link_to(@recurring_task.issue.subj_date, {:controller => 'issues', :action => 'show', :id => @recurring_task.issue.id}) %></h2>

<p><%= l(:label_recurrence_pattern) %> <%= pluralize(@recurring_task.interval_number, @recurring_task.interval_unit) %></p>
<p><%= l(:label_recurrence_pattern) %> <%= pluralize(@recurring_task.interval_number, @recurring_task.interval_localized_name) %></p>
<p><%= @recurring_task.fixed_schedule ? l(:label_recurs_fixed) : l(:label_recurs_dependent) %></p>

<p><%= l(:label_next_scheduled_run) %>: <%= format_date(@recurring_task.next_scheduled_recurrence) %></p>
Expand Down
2 changes: 2 additions & 0 deletions config/locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ de:
label_no_recurring_tasks: "Keine wiederkehrenden Themen gefunden."
label_next_scheduled_run: "Nächste Ausführung"
label_no_recurrence: "Keine Wiederholung."
label_no_project: "Kein Projekt."


label_recurs_fixed: "nach einem festen Zeitplan"
label_recurs_dependent: "nach Abschluss der vorherigen Durchführung"
Expand Down
12 changes: 7 additions & 5 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
# English strings go here for Rails i18n
en:
label_recurring_tasks: "Recurring Tasks"
label_recurring_tasks: "Recurring Issues"
label_current_issue: "Current Issue for Recurrence"
label_recurrence_pattern: "Recurs every"
label_add_recurring_task: "Add Recurrence"
label_no_recurring_tasks: "No recurring tasks found on the system."
label_no_recurring_tasks: "No recurring issues found on the system."
label_next_scheduled_run: "Next scheduled run"
label_no_recurrence: "No recurrence."
label_no_project: "No project."

label_recurs_fixed: "on a fixed schedule"
label_recurs_dependent: "after previous task completion"
label_recurs_dependent: "after previous issue completion"

label_recurring_task_issue_empty: "N/A"
label_recurring_task_project_empty: "N/A"

label_belongs_to_project: "Belongs to project"
label_assigned_to: "Assigned to"

error_invalid_interval: "Interval provided is invalid."
error_recurring_task_not_found: "Could not find recurring task. "
error_recurring_task_could_not_remove: "Could not remove recurrence from task. "
error_recurring_task_not_found: "Could not find recurring issue. "
error_recurring_task_could_not_remove: "Could not remove recurrence from issue. "

field_interval_number: "Interval number"
field_interval_unit: "Interval Unit(s)"
Expand Down
29 changes: 29 additions & 0 deletions db/migrate/002_standardize_recurrence_units_nonlocalized.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Previous to this migration, the value for recurring_tasks.interval_units
# was pulled from the localization file.
# This made the values more user-friendly when looking at the database directly
# however caused issues when the text changed, or locale changed.
# Here we are switching to just flags to denote the type of unit instead of text.
class StandardizeRecurrenceUnitsNonlocalized < ActiveRecord::Migration
def up
RecurringTask.all.each do |rt|
begin
logger.info "Migrating task ##{rt.id} from #{rt.interval_unit}"
rt.interval_unit = RecurringTask.get_interval_from_localized_name(rt.interval_unit)
rt.save!(:validate => false)
rescue => e
msg = "Migration for recurrence FAILED. ##{rt.id} from #{rt.interval_unit} FAILED. You will need to update this manually. Error: #{e}" # TODO localize
logger.error msg
say msg # also display to user
end
end
end

# There is no rolling back - this is a change to the DATA in the database,
# not to the structure itself.
# There is no guarantee that the current localized translation was the value
# previously in the database.
def down
# say "ActiveRecord::IrreversibleMigration"
raise ActiveRecord::IrreversibleMigration
end
end
6 changes: 3 additions & 3 deletions init.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
author_url 'https://github.com/nutso/'
url 'https://github.com/nutso/redmine-plugin-recurring-tasks'
description 'Allows you to set a task to recur on a regular schedule, or when marked complete, regenerate a new task due in the future. Plugin is based -- very loosely -- on the periodic tasks plugin published by Tanguy de Courson'
version '1.2.5'
version '1.2.6'

Redmine::MenuManager.map :top_menu do |menu|
menu.push :recurring_tasks, { :controller => 'recurring_tasks', :action => 'index' }, :caption => 'Recurring Issues', :if => Proc.new { User.current.admin? } # TODO localize string
menu.push :recurring_tasks, { :controller => 'recurring_tasks', :action => 'index' }, :caption => :label_recurring_tasks, :if => Proc.new { User.current.admin? }
end

# Permissions map to issue permissions (#12)
Expand All @@ -26,7 +26,7 @@
end

# project-specific recurring tasks view (#11)
menu :project_menu, :recurring_tasks, { :controller => 'recurring_tasks', :action => 'index' }, :caption => 'Recurring Issues', :after => :new_issue, :param => :project_id # TODO localize string
menu :project_menu, :recurring_tasks, { :controller => 'recurring_tasks', :action => 'index' }, :caption => :label_recurring_tasks, :after => :new_issue, :param => :project_id

# Send patches to models and controllers
Rails.configuration.to_prepare do
Expand Down