mirror of
https://github.com/TracksApp/tracks.git
synced 2026-01-16 14:15:28 +01:00
Merge branch 'master' into new-gui
Conflicts: Gemfile.lock
This commit is contained in:
commit
fdd4c267b3
39 changed files with 2824 additions and 1022 deletions
53
app/controllers/recurring_todos/form_helper.rb
Normal file
53
app/controllers/recurring_todos/form_helper.rb
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
module RecurringTodos
|
||||
|
||||
class FormHelper
|
||||
|
||||
def initialize(recurring_todo)
|
||||
@recurring_todo = recurring_todo
|
||||
|
||||
@method_map = {
|
||||
# delegate daily_xxx to daily_pattern.xxx
|
||||
"daily" => {prefix: "", method: daily_pattern},
|
||||
"weekly" => {prefix: "", method: weekly_pattern},
|
||||
"monthly" => {prefix: "", method: monthly_pattern},
|
||||
"yearly" => {prefix: "", method: yearly_pattern},
|
||||
# delegate on_xxx to weekly_pattern.on_xxx
|
||||
"on" => {prefix: "on_", method: weekly_pattern}
|
||||
}
|
||||
end
|
||||
|
||||
def create_pattern(pattern_class)
|
||||
pattern = pattern_class.new(@recurring_todo.user)
|
||||
pattern.build_from_recurring_todo(@recurring_todo)
|
||||
pattern
|
||||
end
|
||||
|
||||
def daily_pattern
|
||||
@daily_pattern ||= create_pattern(DailyRepeatPattern)
|
||||
end
|
||||
|
||||
def weekly_pattern
|
||||
@weekly_pattern ||= create_pattern(WeeklyRepeatPattern)
|
||||
end
|
||||
|
||||
def monthly_pattern
|
||||
@monthly_pattern ||= create_pattern(MonthlyRepeatPattern)
|
||||
end
|
||||
|
||||
def yearly_pattern
|
||||
@yearly_pattern ||= create_pattern(YearlyRepeatPattern)
|
||||
end
|
||||
|
||||
def method_missing(method, *args)
|
||||
# delegate daily_xxx to daily_pattern, weekly_xxx to weekly_pattern, etc.
|
||||
if method.to_s =~ /^([^_]+)_(.+)$/
|
||||
return @method_map[$1][:method].send(@method_map[$1][:prefix]+$2, *args) unless @method_map[$1].nil?
|
||||
end
|
||||
|
||||
# no match, let @recurring_todo handle it, or fail
|
||||
@recurring_todo.send(method, *args)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -37,68 +37,18 @@ class RecurringTodosController < ApplicationController
|
|||
end
|
||||
|
||||
def edit
|
||||
@form_helper = RecurringTodos::FormHelper.new(@recurring_todo)
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
# TODO: write tests for updating
|
||||
@recurring_todo.tag_with(params[:edit_recurring_todo_tag_list]) if params[:edit_recurring_todo_tag_list]
|
||||
@original_item_context_id = @recurring_todo.context_id
|
||||
@original_item_project_id = @recurring_todo.project_id
|
||||
updater = RecurringTodos::RecurringTodosBuilder.new(current_user, update_recurring_todo_params)
|
||||
@saved = updater.update(@recurring_todo)
|
||||
|
||||
# we needed to rename the recurring_period selector in the edit form because
|
||||
# the form for a new recurring todo and the edit form are on the same page.
|
||||
# Same goes for start_from and end_date
|
||||
params['recurring_todo']['recurring_period']=params['recurring_edit_todo']['recurring_period']
|
||||
params['recurring_todo']['end_date']=parse_date_per_user_prefs(params['recurring_todo_edit_end_date'])
|
||||
params['recurring_todo']['start_from']=parse_date_per_user_prefs(params['recurring_todo_edit_start_from'])
|
||||
|
||||
# update project
|
||||
if params['recurring_todo']['project_id'].blank? && !params['project_name'].nil?
|
||||
if params['project_name'] == 'None'
|
||||
project = Project.null_object
|
||||
else
|
||||
project = current_user.projects.where(:name => params['project_name'].strip)
|
||||
unless project
|
||||
project = current_user.projects.build
|
||||
project.name = params['project_name'].strip
|
||||
project.save
|
||||
@new_project_created = true
|
||||
end
|
||||
end
|
||||
params["recurring_todo"]["project_id"] = project.id
|
||||
end
|
||||
|
||||
# update context
|
||||
if params['recurring_todo']['context_id'].blank? && params['context_name'].present?
|
||||
context = current_user.contexts.where(:name => params['context_name'].strip).first
|
||||
unless context
|
||||
context = current_user.contexts.build
|
||||
context.name = params['context_name'].strip
|
||||
context.save
|
||||
@new_context_created = true
|
||||
end
|
||||
params["recurring_todo"]["context_id"] = context.id
|
||||
end
|
||||
|
||||
# make sure that we set weekly_return_xxx to empty (space) when they are
|
||||
# not checked (and thus not present in params["recurring_todo"])
|
||||
%w{monday tuesday wednesday thursday friday saturday sunday}.each do |day|
|
||||
params["recurring_todo"]["weekly_return_"+day]=' ' if params["recurring_todo"]["weekly_return_"+day].nil?
|
||||
end
|
||||
|
||||
selector_attributes = {
|
||||
'recurring_period' => recurring_todo_params['recurring_period'],
|
||||
'daily_selector' => recurring_todo_params['daily_selector'],
|
||||
'monthly_selector' => recurring_todo_params['monthly_selector'],
|
||||
'yearly_selector' => recurring_todo_params['yearly_selector']
|
||||
}
|
||||
|
||||
@recurring_todo.assign_attributes(:recurring_period => recurring_todo_params[:recurring_period])
|
||||
@recurring_todo.assign_attributes(selector_attributes)
|
||||
@saved = @recurring_todo.update_attributes recurring_todo_params
|
||||
@recurring_todo.reload
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
|
|
@ -106,42 +56,17 @@ class RecurringTodosController < ApplicationController
|
|||
end
|
||||
|
||||
def create
|
||||
p = RecurringTodoCreateParamsHelper.new(params, recurring_todo_params)
|
||||
p.attributes['end_date']=parse_date_per_user_prefs(p.attributes['end_date'])
|
||||
p.attributes['start_from']=parse_date_per_user_prefs(p.attributes['start_from'])
|
||||
|
||||
# make sure we set :recurring_period first, since other setters depend on it being set
|
||||
# TODO: move logic into model
|
||||
@recurring_todo = current_user.recurring_todos.build(:recurring_period => params[:recurring_period])
|
||||
@recurring_todo.assign_attributes(p.selector_attributes)
|
||||
@recurring_todo.update_attributes(p.attributes)
|
||||
|
||||
if p.project_specified_by_name?
|
||||
project = current_user.projects.where(:name => p.project_name).first_or_create
|
||||
@new_project_created = project.new_record_before_save?
|
||||
@recurring_todo.project_id = project.id
|
||||
end
|
||||
|
||||
if p.context_specified_by_name?
|
||||
context = current_user.contexts.where(:name => p.context_name).first_or_create
|
||||
@new_context_created = context.new_record_before_save?
|
||||
@recurring_todo.context_id = context.id
|
||||
end
|
||||
|
||||
@saved = @recurring_todo.save
|
||||
if @saved && p.tag_list.present?
|
||||
@recurring_todo.tag_with(p.tag_list)
|
||||
@recurring_todo.tags.reload
|
||||
end
|
||||
builder = RecurringTodos::RecurringTodosBuilder.new(current_user, all_recurring_todo_params)
|
||||
@saved = builder.save
|
||||
|
||||
if @saved
|
||||
@status_message = t('todos.recurring_action_saved')
|
||||
@todo_saved = TodoFromRecurringTodo.new(current_user, @recurring_todo).create.nil? == false
|
||||
if @todo_saved
|
||||
@status_message += " / " + t('todos.new_related_todo_created_short')
|
||||
else
|
||||
@status_message += " / " + t('todos.new_related_todo_not_created_short')
|
||||
end
|
||||
@recurring_todo = builder.saved_recurring_todo
|
||||
todo_saved = TodoFromRecurringTodo.new(current_user, @recurring_todo).create.nil? == false
|
||||
|
||||
@status_message =
|
||||
t('todos.recurring_action_saved') + " / " +
|
||||
t("todos.new_related_todo_#{todo_saved ? "" : "not_"}created_short")
|
||||
|
||||
@down_count = current_user.recurring_todos.active.count
|
||||
@new_recurring_todo = RecurringTodo.new
|
||||
else
|
||||
|
|
@ -154,13 +79,10 @@ class RecurringTodosController < ApplicationController
|
|||
end
|
||||
|
||||
def destroy
|
||||
@number_of_todos = @recurring_todo.todos.count
|
||||
|
||||
# remove all references to this recurring todo
|
||||
@todos = @recurring_todo.todos
|
||||
@number_of_todos = @todos.size
|
||||
@todos.each do |t|
|
||||
t.recurring_todo_id = nil
|
||||
t.save
|
||||
end
|
||||
@recurring_todo.clear_todos_association
|
||||
|
||||
# delete the recurring todo
|
||||
@saved = @recurring_todo.destroy
|
||||
|
|
@ -174,11 +96,10 @@ class RecurringTodosController < ApplicationController
|
|||
format.html do
|
||||
if @saved
|
||||
notify :notice, t('todos.recurring_deleted_success')
|
||||
redirect_to :action => 'index'
|
||||
else
|
||||
notify :error, t('todos.error_deleting_recurring', :description => @recurring_todo.description)
|
||||
redirect_to :action => 'index'
|
||||
notify :error, t('todos.error_deleting_recurring', :description => @recurring_todo.description)
|
||||
end
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
|
||||
format.js do
|
||||
|
|
@ -217,58 +138,6 @@ class RecurringTodosController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
class RecurringTodoCreateParamsHelper
|
||||
|
||||
def initialize(params, recurring_todo_params)
|
||||
@params = params['request'] || params
|
||||
@attributes = recurring_todo_params
|
||||
|
||||
# make sure all selectors (recurring_period, recurrence_selector,
|
||||
# daily_selector, monthly_selector and yearly_selector) are first in hash
|
||||
# so that they are processed first by the model
|
||||
@selector_attributes = {
|
||||
'recurring_period' => @attributes['recurring_period'],
|
||||
'daily_selector' => @attributes['daily_selector'],
|
||||
'monthly_selector' => @attributes['monthly_selector'],
|
||||
'yearly_selector' => @attributes['yearly_selector']
|
||||
}
|
||||
end
|
||||
|
||||
def attributes
|
||||
@attributes
|
||||
end
|
||||
|
||||
def selector_attributes
|
||||
return @selector_attributes
|
||||
end
|
||||
|
||||
def project_name
|
||||
@params['project_name'].strip unless @params['project_name'].nil?
|
||||
end
|
||||
|
||||
def context_name
|
||||
@params['context_name'].strip unless @params['context_name'].nil?
|
||||
end
|
||||
|
||||
def tag_list
|
||||
@params['tag_list']
|
||||
end
|
||||
|
||||
def project_specified_by_name?
|
||||
return false if @attributes['project_id'].present?
|
||||
return false if project_name.blank?
|
||||
return false if project_name == 'None'
|
||||
true
|
||||
end
|
||||
|
||||
def context_specified_by_name?
|
||||
return false if @attributes['context_id'].present?
|
||||
return false if context_name.blank?
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def recurring_todo_params
|
||||
|
|
@ -278,7 +147,7 @@ class RecurringTodosController < ApplicationController
|
|||
:ends_on, :end_date, :number_of_occurences, :occurences_count, :target,
|
||||
:show_from_delta, :recurring_period, :recurrence_selector, :every_other1,
|
||||
:every_other2, :every_other3, :every_day, :only_work_days, :every_count,
|
||||
:weekday, :show_always,
|
||||
:weekday, :show_always, :context_name, :project_name, :tag_list,
|
||||
# form attributes
|
||||
:recurring_period, :daily_selector, :monthly_selector, :yearly_selector,
|
||||
:recurring_target, :daily_every_x_days, :monthly_day_of_week,
|
||||
|
|
@ -293,17 +162,50 @@ class RecurringTodosController < ApplicationController
|
|||
)
|
||||
end
|
||||
|
||||
def all_recurring_todo_params
|
||||
# move context_name, project_name and tag_list into :recurring_todo hash for easier processing
|
||||
{
|
||||
context_name: :context_name,
|
||||
project_name: :project_name,
|
||||
tag_list: :tag_list
|
||||
}.each do |target,source|
|
||||
move_into_recurring_todo_param(params, target, source)
|
||||
end
|
||||
recurring_todo_params
|
||||
end
|
||||
|
||||
def update_recurring_todo_params
|
||||
# we needed to rename the recurring_period selector in the edit form because
|
||||
# the form for a new recurring todo and the edit form are on the same page.
|
||||
# Same goes for start_from and end_date
|
||||
params['recurring_todo']['recurring_period'] = params['recurring_edit_todo']['recurring_period']
|
||||
|
||||
{
|
||||
context_name: :context_name,
|
||||
project_name: :project_name,
|
||||
tag_list: :edit_recurring_todo_tag_list,
|
||||
end_date: :recurring_todo_edit_end_date,
|
||||
start_from: :recurring_todo_edit_start_from
|
||||
}.each do |target,source|
|
||||
move_into_recurring_todo_param(params, target, source)
|
||||
end
|
||||
|
||||
# make sure that we set weekly_return_xxx to empty (space) when they are
|
||||
# not checked (and thus not present in params["recurring_todo"])
|
||||
%w{monday tuesday wednesday thursday friday saturday sunday}.each do |day|
|
||||
params["recurring_todo"]["weekly_return_#{day}"]=' ' if params["recurring_todo"]["weekly_return_#{day}"].nil?
|
||||
end
|
||||
|
||||
recurring_todo_params
|
||||
end
|
||||
|
||||
def move_into_recurring_todo_param(params, target, source)
|
||||
params[:recurring_todo][target] = params[source] unless params[source].blank?
|
||||
end
|
||||
|
||||
def init
|
||||
@days_of_week = []
|
||||
0.upto 6 do |i|
|
||||
@days_of_week << [t('date.day_names')[i], i]
|
||||
end
|
||||
|
||||
@months_of_year = []
|
||||
1.upto 12 do |i|
|
||||
@months_of_year << [t('date.month_names')[i], i]
|
||||
end
|
||||
|
||||
@days_of_week = (0..6).map{|i| [t('date.day_names')[i], i] }
|
||||
@months_of_year = (1..12).map{|i| [t('date.month_names')[i], i] }
|
||||
@xth_day = [[t('common.first'),1],[t('common.second'),2],[t('common.third'),3],[t('common.fourth'),4],[t('common.last'),5]]
|
||||
@projects = current_user.projects.includes(:default_context)
|
||||
@contexts = current_user.contexts
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def age_in_days
|
||||
@age_in_days ||= ((Time.now.utc - created_at).to_i / 1.day) + 1
|
||||
@age_in_days ||= (Time.current.to_date - created_at.to_date).to_i + 1
|
||||
end
|
||||
|
||||
def self.import(filename, params, user)
|
||||
|
|
|
|||
|
|
@ -29,86 +29,23 @@ class RecurringTodo < ActiveRecord::Base
|
|||
validates_length_of :description, :maximum => 100
|
||||
validates_length_of :notes, :maximum => 60000, :allow_nil => true
|
||||
|
||||
validate :period_specific_validations
|
||||
validate :starts_and_ends_on_validations
|
||||
validate :set_recurrence_on_validations
|
||||
validate :period_validation
|
||||
validate :pattern_specific_validations
|
||||
|
||||
def period_specific_validations
|
||||
if %W[daily weekly monthly yearly].include?(recurring_period)
|
||||
self.send("validate_#{recurring_period}")
|
||||
def pattern_specific_validations
|
||||
if pattern
|
||||
pattern.validate
|
||||
else
|
||||
errors.add(:recurring_period, "is an unknown recurrence pattern: '#{recurring_period}'")
|
||||
errors[:recurring_todo] << "Invalid recurrence period '#{recurring_period}'"
|
||||
end
|
||||
end
|
||||
|
||||
def validate_daily
|
||||
if (!only_work_days) && daily_every_x_days.blank?
|
||||
errors[:base] << "Every other nth day may not be empty for recurrence setting"
|
||||
end
|
||||
def valid_period?
|
||||
%W[daily weekly monthly yearly].include?(recurring_period)
|
||||
end
|
||||
|
||||
def validate_weekly
|
||||
if weekly_every_x_week.blank?
|
||||
errors[:base] << "Every other nth week may not be empty for recurrence setting"
|
||||
end
|
||||
something_set = false
|
||||
%w{sunday monday tuesday wednesday thursday friday saturday}.each { |day| something_set ||= self.send("on_#{day}") }
|
||||
errors[:base] << "You must specify at least one day on which the todo recurs" unless something_set
|
||||
end
|
||||
|
||||
def validate_monthly
|
||||
case recurrence_selector
|
||||
when 0 # 'monthly_every_x_day'
|
||||
errors[:base] << "The day of the month may not be empty for recurrence setting" if monthly_every_x_day.blank?
|
||||
errors[:base] << "Every other nth month may not be empty for recurrence setting" if monthly_every_x_month.blank?
|
||||
when 1 # 'monthly_every_xth_day'
|
||||
errors[:base] <<"Every other nth month may not be empty for recurrence setting" if monthly_every_x_month2.blank?
|
||||
errors[:base] <<"The nth day of the month may not be empty for recurrence setting" if monthly_every_xth_day.blank?
|
||||
errors[:base] <<"The day of the month may not be empty for recurrence setting" if monthly_day_of_week.blank?
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence selector '#{recurrence_selector}'"
|
||||
end
|
||||
end
|
||||
|
||||
def validate_yearly
|
||||
case recurrence_selector
|
||||
when 0 # 'yearly_every_x_day'
|
||||
errors[:base] << "The month of the year may not be empty for recurrence setting" if yearly_month_of_year.blank?
|
||||
errors[:base] << "The day of the month may not be empty for recurrence setting" if yearly_every_x_day.blank?
|
||||
when 1 # 'yearly_every_xth_day'
|
||||
errors[:base] << "The month of the year may not be empty for recurrence setting" if yearly_month_of_year2.blank?
|
||||
errors[:base] << "The nth day of the month may not be empty for recurrence setting" if yearly_every_xth_day.blank?
|
||||
errors[:base] << "The day of the week may not be empty for recurrence setting" if yearly_day_of_week.blank?
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence selector '#{recurrence_selector}'"
|
||||
end
|
||||
end
|
||||
|
||||
def starts_and_ends_on_validations
|
||||
errors[:base] << "The start date needs to be filled in" if start_from.blank?
|
||||
case ends_on
|
||||
when 'ends_on_number_of_times'
|
||||
errors[:base] << "The number of recurrences needs to be filled in for 'Ends on'" if number_of_occurences.blank?
|
||||
when "ends_on_end_date"
|
||||
errors[:base] << "The end date needs to be filled in for 'Ends on'" if end_date.blank?
|
||||
else
|
||||
errors[:base] << "The end of the recurrence is not selected" unless ends_on == "no_end_date"
|
||||
end
|
||||
end
|
||||
|
||||
def set_recurrence_on_validations
|
||||
# show always or x days before due date. x not null
|
||||
case target
|
||||
when 'show_from_date'
|
||||
# no validations
|
||||
when 'due_date'
|
||||
errors[:base] << "Please select when to show the action" if show_always.nil?
|
||||
unless show_always
|
||||
errors[:base] << "Please fill in the number of days to show the todo before the due date" if show_from_delta.blank?
|
||||
end
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target selector '#{recurrence_target}'"
|
||||
end
|
||||
def period_validation
|
||||
errors.add(:recurring_period, "is an unknown recurrence pattern: '#{recurring_period}'") unless valid_period?
|
||||
end
|
||||
|
||||
# the following recurrence patterns can be stored:
|
||||
|
|
@ -133,262 +70,20 @@ class RecurringTodo < ActiveRecord::Base
|
|||
# choosing between both options is done on recurrence_selector where 0 is
|
||||
# for first type and 1 for second type
|
||||
|
||||
# DAILY
|
||||
|
||||
def daily_selector=(selector)
|
||||
case selector
|
||||
when 'daily_every_x_day'
|
||||
only_work_days = false
|
||||
when 'daily_every_work_day'
|
||||
only_work_days = true
|
||||
else
|
||||
raise Exception.new, "unknown daily recurrence pattern: '#{selector}'"
|
||||
end
|
||||
end
|
||||
|
||||
def daily_every_x_days=(x)
|
||||
self.every_other1 = x if recurring_period=='daily'
|
||||
end
|
||||
|
||||
def daily_every_x_days
|
||||
every_other1
|
||||
end
|
||||
|
||||
# WEEKLY
|
||||
|
||||
def weekly_every_x_week=(x)
|
||||
self.every_other1 = x if recurring_period=='weekly'
|
||||
end
|
||||
|
||||
def weekly_every_x_week
|
||||
self.every_other1
|
||||
end
|
||||
|
||||
def switch_week_day(day, position)
|
||||
self.every_day = ' ' if self.every_day.nil?
|
||||
self.every_day = every_day[0, position] + day + every_day[position+1, every_day.length]
|
||||
end
|
||||
|
||||
{ monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 0 }.each do |day, number|
|
||||
define_method("weekly_return_#{day}=") do |selector|
|
||||
switch_week_day(selector, number) if recurring_period=='weekly'
|
||||
end
|
||||
|
||||
define_method("on_#{day}") do
|
||||
on_xday number
|
||||
end
|
||||
end
|
||||
|
||||
def on_xday(n)
|
||||
every_day && every_day[n, 1] != ' '
|
||||
end
|
||||
|
||||
# MONTHLY
|
||||
|
||||
def monthly_selector=(selector)
|
||||
self.recurrence_selector = ( (selector=='monthly_every_x_day') ? 0 : 1) if recurring_period=='monthly'
|
||||
end
|
||||
|
||||
def monthly_every_x_day=(x)
|
||||
self.every_other1 = x if recurring_period=='monthly'
|
||||
end
|
||||
|
||||
def monthly_every_x_day
|
||||
self.every_other1
|
||||
end
|
||||
|
||||
def is_monthly_every_x_day
|
||||
recurring_period == 'monthly' && self.recurrence_selector == 0
|
||||
end
|
||||
|
||||
def is_monthly_every_xth_day
|
||||
recurring_period == 'monthly' && self.recurrence_selector == 1
|
||||
end
|
||||
|
||||
def monthly_every_x_month=(x)
|
||||
self.every_other2 = x if recurring_period=='monthly' && recurrence_selector == 0
|
||||
end
|
||||
|
||||
def monthly_every_x_month
|
||||
# in case monthly pattern is every day x, return every_other2 otherwise
|
||||
# return a default value
|
||||
self.recurrence_selector == 0 ? self.every_other2 : 1
|
||||
end
|
||||
|
||||
def monthly_every_x_month2=(x)
|
||||
self.every_other2 = x if recurring_period=='monthly' && recurrence_selector == 1
|
||||
end
|
||||
|
||||
def monthly_every_x_month2
|
||||
# in case monthly pattern is every xth day, return every_other2 otherwise
|
||||
# return a default value
|
||||
self.recurrence_selector == 1 ? self.every_other2 : 1
|
||||
end
|
||||
|
||||
def monthly_every_xth_day=(x)
|
||||
self.every_other3 = x if recurring_period=='monthly'
|
||||
end
|
||||
|
||||
def monthly_every_xth_day(default=nil)
|
||||
self.every_other3 || default
|
||||
end
|
||||
|
||||
def monthly_day_of_week=(dow)
|
||||
self.every_count = dow if recurring_period=='monthly'
|
||||
end
|
||||
|
||||
def monthly_day_of_week
|
||||
self.every_count
|
||||
end
|
||||
|
||||
# YEARLY
|
||||
|
||||
def yearly_selector=(selector)
|
||||
self.recurrence_selector = ( (selector=='yearly_every_x_day') ? 0 : 1) if recurring_period=='yearly'
|
||||
end
|
||||
|
||||
def yearly_month_of_year=(moy)
|
||||
self.every_other2 = moy if self.recurring_period=='yearly' && self.recurrence_selector == 0
|
||||
end
|
||||
|
||||
def yearly_month_of_year
|
||||
# if recurrence pattern is every x day in a month, return month otherwise
|
||||
# return a default value
|
||||
self.recurrence_selector == 0 ? self.every_other2 : Time.zone.now.month
|
||||
end
|
||||
|
||||
def yearly_month_of_year2=(moy)
|
||||
self.every_other2 = moy if self.recurring_period=='yearly' && self.recurrence_selector == 1
|
||||
end
|
||||
|
||||
def yearly_month_of_year2
|
||||
# if recurrence pattern is every xth day in a month, return month otherwise
|
||||
# return a default value
|
||||
self.recurrence_selector == 1 ? self.every_other2 : Time.zone.now.month
|
||||
end
|
||||
|
||||
def yearly_every_x_day=(x)
|
||||
self.every_other1 = x if recurring_period=='yearly'
|
||||
end
|
||||
|
||||
def yearly_every_x_day
|
||||
self.every_other1
|
||||
end
|
||||
|
||||
def yearly_every_xth_day=(x)
|
||||
self.every_other3 = x if recurring_period=='yearly'
|
||||
end
|
||||
|
||||
def yearly_every_xth_day
|
||||
self.every_other3
|
||||
end
|
||||
|
||||
def yearly_day_of_week=(dow)
|
||||
self.every_count=dow if recurring_period=='yearly'
|
||||
end
|
||||
|
||||
def yearly_day_of_week
|
||||
self.every_count
|
||||
end
|
||||
|
||||
# target
|
||||
|
||||
def recurring_target=(t)
|
||||
self.target = t
|
||||
end
|
||||
|
||||
def recurring_target_as_text
|
||||
case self.target
|
||||
when 'due_date'
|
||||
I18n.t("todos.recurrence.pattern.due")
|
||||
when 'show_from_date'
|
||||
I18n.t("todos.recurrence.pattern.show")
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||
end
|
||||
end
|
||||
|
||||
def recurring_show_days_before=(days)
|
||||
self.show_from_delta=days
|
||||
end
|
||||
|
||||
def recurring_show_always=(value)
|
||||
self.show_always=value
|
||||
end
|
||||
|
||||
def daily_recurrence_pattern
|
||||
if only_work_days
|
||||
I18n.t("todos.recurrence.pattern.on_work_days")
|
||||
elsif every_other1 > 1
|
||||
I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.days_midsentence.other")
|
||||
else
|
||||
I18n.t("todos.recurrence.pattern.every_day")
|
||||
end
|
||||
end
|
||||
|
||||
def weekly_recurrence_pattern
|
||||
if every_other1 > 1
|
||||
I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.weeks")
|
||||
else
|
||||
I18n.t('todos.recurrence.pattern.weekly')
|
||||
end
|
||||
end
|
||||
|
||||
def monthly_recurrence_pattern
|
||||
return "invalid repeat pattern" if every_other2.nil?
|
||||
if self.recurrence_selector == 0
|
||||
on_day = " #{I18n.t('todos.recurrence.pattern.on_day_n', :n => self.every_other1)}"
|
||||
if self.every_other2>1
|
||||
I18n.t("todos.recurrence.pattern.every_n", :n => self.every_other2) + " " + I18n.t('common.months') + on_day
|
||||
else
|
||||
I18n.t("todos.recurrence.pattern.every_month") + on_day
|
||||
end
|
||||
else
|
||||
n_months = if self.every_other2 > 1
|
||||
"#{self.every_other2} #{I18n.t('common.months')}"
|
||||
else
|
||||
I18n.t('common.month')
|
||||
end
|
||||
I18n.t('todos.recurrence.pattern.every_xth_day_of_every_n_months',
|
||||
:x => self.xth, :day => self.day_of_week, :n_months => n_months)
|
||||
end
|
||||
end
|
||||
|
||||
def yearly_recurrence_pattern
|
||||
if self.recurrence_selector == 0
|
||||
I18n.t("todos.recurrence.pattern.every_year_on",
|
||||
:date => I18n.l(DateTime.new(Time.zone.now.year, self.every_other2, self.every_other1), :format => :month_day))
|
||||
else
|
||||
I18n.t("todos.recurrence.pattern.every_year_on",
|
||||
:date => I18n.t("todos.recurrence.pattern.the_xth_day_of_month", :x => self.xth, :day => self.day_of_week, :month => self.month_of_year))
|
||||
def pattern
|
||||
if valid_period?
|
||||
@pattern = eval("RecurringTodos::#{recurring_period.capitalize}RepeatPattern.new(user)")
|
||||
@pattern.build_from_recurring_todo(self)
|
||||
end
|
||||
@pattern
|
||||
end
|
||||
|
||||
def recurrence_pattern
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
case recurring_period
|
||||
when 'daily' then daily_recurrence_pattern
|
||||
when 'weekly' then weekly_recurrence_pattern
|
||||
when 'monthly' then monthly_recurrence_pattern
|
||||
when 'yearly' then yearly_recurrence_pattern
|
||||
else
|
||||
'unknown recurrence pattern: period unknown'
|
||||
end
|
||||
pattern.recurrence_pattern
|
||||
end
|
||||
|
||||
def xth
|
||||
xth_day = [
|
||||
I18n.t('todos.recurrence.pattern.first'),I18n.t('todos.recurrence.pattern.second'),I18n.t('todos.recurrence.pattern.third'),
|
||||
I18n.t('todos.recurrence.pattern.fourth'),I18n.t('todos.recurrence.pattern.last')]
|
||||
self.every_other3.nil? ? '??' : xth_day[self.every_other3-1]
|
||||
end
|
||||
|
||||
def day_of_week
|
||||
self.every_count.nil? ? '??' : I18n.t('todos.recurrence.pattern.day_names')[self.every_count]
|
||||
end
|
||||
|
||||
def month_of_year
|
||||
self.every_other2.nil? ? '??' : I18n.t('todos.recurrence.pattern.month_names')[self.every_other2]
|
||||
def recurring_target_as_text
|
||||
pattern.recurring_target_as_text
|
||||
end
|
||||
|
||||
def starred?
|
||||
|
|
@ -396,214 +91,11 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def get_due_date(previous)
|
||||
case self.target
|
||||
when 'due_date'
|
||||
get_next_date(previous)
|
||||
when 'show_from_date'
|
||||
# so leave due date empty
|
||||
nil
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||
end
|
||||
pattern.get_due_date(previous)
|
||||
end
|
||||
|
||||
def get_show_from_date(previous)
|
||||
case self.target
|
||||
when 'due_date'
|
||||
# so set show from date relative to due date unless show_always is true or show_from_delta is nil
|
||||
(self.show_always? || self.show_from_delta.nil?) ? nil : get_due_date(previous) - self.show_from_delta.days
|
||||
when 'show_from_date'
|
||||
# Leave due date empty
|
||||
get_next_date(previous)
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||
end
|
||||
end
|
||||
|
||||
def get_next_date(previous)
|
||||
case self.recurring_period
|
||||
when 'daily' then get_daily_date(previous)
|
||||
when 'weekly' then get_weekly_date(previous)
|
||||
when 'monthly' then get_monthly_date(previous)
|
||||
when 'yearly' then get_yearly_date(previous)
|
||||
else
|
||||
raise Exception.new, "unknown recurrence pattern: '#{self.recurring_period}'"
|
||||
end
|
||||
end
|
||||
|
||||
def get_daily_date(previous)
|
||||
# previous is the due date of the previous todo or it is the completed_at
|
||||
# date when the completed_at date is after due_date (i.e. you did not make
|
||||
# the due date in time)
|
||||
#
|
||||
# assumes self.recurring_period == 'daily'
|
||||
|
||||
start = determine_start(previous, 1.day)
|
||||
|
||||
if self.only_work_days
|
||||
return start + 2.day if start.wday() == 6 # saturday
|
||||
return start + 1.day if start.wday() == 0 # sunday
|
||||
return start
|
||||
else # every nth day; n = every_other1
|
||||
# if there was no previous todo, do not add n: the first todo starts on
|
||||
# today or on start_from
|
||||
return previous == nil ? start : start+every_other1.day-1.day
|
||||
end
|
||||
end
|
||||
|
||||
def get_weekly_date(previous)
|
||||
# determine start
|
||||
if previous == nil
|
||||
start = self.start_from.nil? ? Time.zone.now : self.start_from
|
||||
else
|
||||
start = previous + 1.day
|
||||
if start.wday() == 0
|
||||
# we went to a new week , go to the nth next week and find first match
|
||||
# that week. Note that we already went into the next week, so -1
|
||||
start += (self.every_other1-1).week
|
||||
end
|
||||
unless self.start_from.nil?
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
# start_from as start to search for next date
|
||||
start = self.start_from if self.start_from > previous
|
||||
end
|
||||
end
|
||||
|
||||
day = find_first_day_in_this_week(start)
|
||||
return day unless day == -1
|
||||
|
||||
# we did not find anything this week, so check the nth next, starting from
|
||||
# sunday
|
||||
start = start + self.every_other1.week - (start.wday()).days
|
||||
|
||||
start = find_first_day_in_this_week(start)
|
||||
return start unless start == -1
|
||||
|
||||
raise Exception.new, "unable to find next weekly date (#{self.every_day})"
|
||||
end
|
||||
|
||||
def get_monthly_date(previous)
|
||||
start = determine_start(previous)
|
||||
day = self.every_other1
|
||||
n = self.every_other2
|
||||
|
||||
case self.recurrence_selector
|
||||
when 0 # specific day of the month
|
||||
if (previous && start.mday >= day) || (previous.nil? && start.mday > day)
|
||||
# there is no next day n in this month, search in next month
|
||||
#
|
||||
# start += n.months
|
||||
#
|
||||
# The above seems to not work. Fiddle with timezone. Looks like we hit a
|
||||
# bug in rails here where 2008-12-01 +0100 plus 1.month becomes
|
||||
# 2008-12-31 +0100. For now, just calculate in UTC and convert back to
|
||||
# local timezone.
|
||||
#
|
||||
# TODO: recheck if future rails versions have this problem too
|
||||
start = Time.utc(start.year, start.month, start.day)+n.months
|
||||
start = Time.zone.local(start.year, start.month, start.day)
|
||||
|
||||
# go back to day
|
||||
end
|
||||
Time.zone.local(start.year, start.month, day)
|
||||
|
||||
when 1 # relative weekday of a month
|
||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, start.month, start.year)
|
||||
if the_next.nil? || the_next <= start
|
||||
# the nth day is already passed in this month, go to next month and try
|
||||
# again
|
||||
|
||||
# fiddle with timezone. Looks like we hit a bug in rails here where
|
||||
# 2008-12-01 +0100 plus 1.month becomes 2008-12-31 +0100. For now, just
|
||||
# calculate in UTC and convert back to local timezone.
|
||||
# TODO: recheck if future rails versions have this problem too
|
||||
the_next = Time.utc(the_next.year, the_next.month, the_next.day)+n.months
|
||||
the_next = Time.zone.local(the_next.year, the_next.month, the_next.day)
|
||||
|
||||
# TODO: if there is still no match, start will be set to nil. if we ever
|
||||
# support 5th day of the month, we need to handle this case
|
||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, the_next.month, the_next.year)
|
||||
end
|
||||
the_next
|
||||
else
|
||||
raise Exception.new, "unknown monthly recurrence selection (#{self.recurrence_selector})"
|
||||
end
|
||||
end
|
||||
|
||||
def get_xth_day_of_month(x, weekday, month, year)
|
||||
if x == 5
|
||||
# last -> count backwards. use UTC to avoid strange timezone oddities
|
||||
# where last_day -= 1.day seems to shift tz+0100 to tz+0000
|
||||
last_day = Time.utc(year, month, Time.days_in_month(month))
|
||||
while last_day.wday != weekday
|
||||
last_day -= 1.day
|
||||
end
|
||||
# convert back to local timezone
|
||||
Time.zone.local(last_day.year, last_day.month, last_day.day)
|
||||
else
|
||||
# 1-4th -> count upwards last -> count backwards. use UTC to avoid strange
|
||||
# timezone oddities where last_day -= 1.day seems to shift tz+0100 to
|
||||
# tz+0000
|
||||
start = Time.utc(year,month,1)
|
||||
n = x
|
||||
while n > 0
|
||||
while start.wday() != weekday
|
||||
start+= 1.day
|
||||
end
|
||||
n -= 1
|
||||
start += 1.day unless n==0
|
||||
end
|
||||
# convert back to local timezone
|
||||
Time.zone.local(start.year, start.month, start.day)
|
||||
end
|
||||
end
|
||||
|
||||
def get_yearly_date(previous)
|
||||
start = determine_start(previous)
|
||||
day = self.every_other1
|
||||
month = self.every_other2
|
||||
|
||||
case self.recurrence_selector
|
||||
when 0 # specific day of a specific month
|
||||
if start.month > month || (start.month == month && start.day >= day)
|
||||
# if there is no next month n and day m in this year, search in next
|
||||
# year
|
||||
start = Time.zone.local(start.year+1, month, 1)
|
||||
else
|
||||
# if there is a next month n, stay in this year
|
||||
start = Time.zone.local(start.year, month, 1)
|
||||
end
|
||||
Time.zone.local(start.year, month, day)
|
||||
|
||||
when 1 # relative weekday of a specific month
|
||||
# if there is no next month n in this year, search in next year
|
||||
the_next = start.month > month ? Time.zone.local(start.year+1, month, 1) : start
|
||||
|
||||
# get the xth day of the month
|
||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, month, the_next.year)
|
||||
|
||||
# if the_next is before previous, we went back into the past, so try next
|
||||
# year
|
||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, month, start.year+1) if the_next <= start
|
||||
|
||||
the_next
|
||||
else
|
||||
raise Exception.new, "unknown monthly recurrence selection (#{self.recurrence_selector})"
|
||||
end
|
||||
end
|
||||
|
||||
def continues_recurring?(previous)
|
||||
return self.occurences_count < self.number_of_occurences unless self.number_of_occurences.nil?
|
||||
return true if self.end_date.nil? || self.ends_on == 'no_end_date'
|
||||
|
||||
case self.target
|
||||
when 'due_date'
|
||||
get_due_date(previous) <= self.end_date
|
||||
when 'show_from_date'
|
||||
get_show_from_date(previous) <= self.end_date
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||
end
|
||||
pattern.get_show_from_date(previous)
|
||||
end
|
||||
|
||||
def done?(end_date)
|
||||
|
|
@ -627,7 +119,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
def remove_from_project!
|
||||
self.project = nil
|
||||
self.save
|
||||
end
|
||||
end
|
||||
|
||||
def clear_todos_association
|
||||
unless todos.nil?
|
||||
|
|
@ -643,29 +135,8 @@ class RecurringTodo < ActiveRecord::Base
|
|||
self.save
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Determine start date to calculate next date for recurring todo
|
||||
# offset needs to be 1.day for daily patterns
|
||||
def determine_start(previous, offset=0.day)
|
||||
start = self.start_from || NullTime.new
|
||||
now = Time.zone.now
|
||||
if previous
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
# start_from as start to search for next date
|
||||
start > previous ? start : previous + offset
|
||||
else
|
||||
# skip to present
|
||||
start > now ? start : now
|
||||
end
|
||||
def continues_recurring?(previous)
|
||||
pattern.continues_recurring?(previous)
|
||||
end
|
||||
|
||||
def find_first_day_in_this_week(start)
|
||||
# check if there are any days left this week for the next todo
|
||||
start.wday().upto 6 do |i|
|
||||
return start + (i-start.wday()).days unless self.every_day[i,1] == ' '
|
||||
end
|
||||
-1
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
152
app/models/recurring_todos/abstract_recurring_todos_builder.rb
Normal file
152
app/models/recurring_todos/abstract_recurring_todos_builder.rb
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
module RecurringTodos
|
||||
|
||||
class AbstractRecurringTodosBuilder
|
||||
|
||||
attr_reader :mapped_attributes, :pattern
|
||||
|
||||
def initialize(user, attributes, pattern_class)
|
||||
@user = user
|
||||
@saved = false
|
||||
|
||||
@attributes = attributes
|
||||
@selector = get_selector(selector_key)
|
||||
@filterred_attributes = filter_attributes(@attributes)
|
||||
@mapped_attributes = map_attributes(@filterred_attributes)
|
||||
|
||||
@pattern = pattern_class.new(user)
|
||||
@pattern.attributes = @mapped_attributes
|
||||
|
||||
end
|
||||
|
||||
# build does not add tags. For tags, the recurring todos needs to be saved
|
||||
def build
|
||||
@recurring_todo = @pattern.build_recurring_todo(@mapped_attributes)
|
||||
end
|
||||
|
||||
def update(recurring_todo)
|
||||
@recurring_todo = @pattern.update_recurring_todo(recurring_todo, @mapped_attributes)
|
||||
save_recurring_todo
|
||||
end
|
||||
|
||||
def save
|
||||
build
|
||||
save_recurring_todo
|
||||
end
|
||||
|
||||
def save_project
|
||||
save_collection(:project, :project_id)
|
||||
end
|
||||
|
||||
def save_context
|
||||
save_collection(:context, :context_id)
|
||||
end
|
||||
|
||||
def saved_recurring_todo
|
||||
raise(Exception.new, @recurring_todo.valid? ? "Recurring todo was not saved yet" : "Recurring todos was not saved because of validation errors") unless @saved
|
||||
|
||||
@recurring_todo
|
||||
end
|
||||
|
||||
def attributes
|
||||
@pattern.attributes
|
||||
end
|
||||
|
||||
def errors
|
||||
@recurring_todo.try(:errors)
|
||||
end
|
||||
|
||||
def attributes_to_filter
|
||||
raise Exception.new, "attributes_to_filter should be overridden"
|
||||
end
|
||||
|
||||
def filter_attributes(attributes)
|
||||
# get pattern independend attributes
|
||||
filterred_attributes = filter_generic_attributes(attributes)
|
||||
# append pattern specific attributes
|
||||
attributes_to_filter.each{|key| filterred_attributes[key]= attributes[key] if attributes.key?(key)}
|
||||
|
||||
filterred_attributes
|
||||
end
|
||||
|
||||
def filter_generic_attributes(attributes)
|
||||
return Tracks::AttributeHandler.new(@user, {
|
||||
recurring_period: attributes[:recurring_period],
|
||||
description: attributes[:description],
|
||||
notes: attributes[:notes],
|
||||
tag_list: tag_list_or_empty_string(attributes),
|
||||
start_from: attributes[:start_from],
|
||||
end_date: attributes[:end_date],
|
||||
ends_on: attributes[:ends_on],
|
||||
target: attributes[:target],
|
||||
project: attributes[:project],
|
||||
context: attributes[:context],
|
||||
project_id: attributes[:project_id],
|
||||
context_id: attributes[:context_id],
|
||||
target: attributes[:recurring_target],
|
||||
show_from_delta: attributes[:recurring_show_days_before],
|
||||
show_always: attributes[:recurring_show_always]
|
||||
})
|
||||
end
|
||||
|
||||
def map_attributes
|
||||
# should be overwritten by subclasses to map attributes to activerecord model attributes
|
||||
@filterred_attributes
|
||||
end
|
||||
|
||||
# helper method to be used in mapped_attributes in subclasses
|
||||
# changes name of key from source_key to key
|
||||
def map(mapping, key, source_key)
|
||||
mapping[key] = mapping[source_key]
|
||||
mapping.except(source_key)
|
||||
end
|
||||
|
||||
# should return period specific selector like yearly_selector or daily_selector
|
||||
def selector_key
|
||||
raise Exception.new, "selector_key should be overridden in subclass of AbstractRecurringTodosBuilder"
|
||||
end
|
||||
|
||||
def get_selector(key)
|
||||
return nil if key.nil?
|
||||
|
||||
raise Exception.new, "recurrence selector pattern (#{key}) not given" unless @attributes.selector_key_present?(key)
|
||||
selector = @attributes[key]
|
||||
|
||||
raise Exception.new, "unknown recurrence selector pattern: '#{selector}'" unless valid_selector?(selector)
|
||||
|
||||
@attributes = @attributes.except(key)
|
||||
return selector
|
||||
end
|
||||
|
||||
def valid_selector?(selector)
|
||||
raise Exception.new, "valid_selector? should be overridden in subclass of AbstractRecurringTodosBuilder"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def save_recurring_todo
|
||||
@saved = @recurring_todo.save
|
||||
save_tags if @saved
|
||||
return @saved
|
||||
end
|
||||
|
||||
def save_tags
|
||||
@recurring_todo.tag_with(@filterred_attributes[:tag_list]) if @filterred_attributes[:tag_list].present?
|
||||
@recurring_todo.reload
|
||||
end
|
||||
|
||||
def save_collection(collection, collection_id)
|
||||
# save object (project or context) and add its id to @mapped_attributes and remove the object from the attributes
|
||||
object = @mapped_attributes[collection]
|
||||
object.save
|
||||
@mapped_attributes[collection_id] = object.id
|
||||
@mapped_attributes.except(collection)
|
||||
end
|
||||
|
||||
def tag_list_or_empty_string(attributes)
|
||||
# avoid nil
|
||||
attributes[:tag_list].blank? ? "" : attributes[:tag_list].strip
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
226
app/models/recurring_todos/abstract_repeat_pattern.rb
Normal file
226
app/models/recurring_todos/abstract_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
module RecurringTodos
|
||||
|
||||
class AbstractRepeatPattern
|
||||
|
||||
attr_accessor :attributes
|
||||
|
||||
def initialize(user)
|
||||
@user = user
|
||||
end
|
||||
|
||||
def start_from
|
||||
get :start_from
|
||||
end
|
||||
|
||||
def end_date
|
||||
get :end_date
|
||||
end
|
||||
|
||||
def ends_on
|
||||
get :ends_on
|
||||
end
|
||||
|
||||
def target
|
||||
get :target
|
||||
end
|
||||
|
||||
def show_always?
|
||||
get :show_always
|
||||
end
|
||||
|
||||
def show_from_delta
|
||||
get :show_from_delta
|
||||
end
|
||||
|
||||
def recurring_target_as_text
|
||||
target == 'due_date' ? I18n.t("todos.recurrence.pattern.due") : I18n.t("todos.recurrence.pattern.show")
|
||||
end
|
||||
|
||||
def recurrence_pattern
|
||||
raise "Should not call AbstractRepeatPattern.recurrence_pattern directly. Overwrite in subclass"
|
||||
end
|
||||
|
||||
def xth(x)
|
||||
xth_day = [
|
||||
I18n.t('todos.recurrence.pattern.first'),I18n.t('todos.recurrence.pattern.second'),I18n.t('todos.recurrence.pattern.third'),
|
||||
I18n.t('todos.recurrence.pattern.fourth'),I18n.t('todos.recurrence.pattern.last')]
|
||||
x.nil? ? '??' : xth_day[x-1]
|
||||
end
|
||||
|
||||
def day_of_week_as_text(day)
|
||||
day.nil? ? '??' : I18n.t('todos.recurrence.pattern.day_names')[day]
|
||||
end
|
||||
|
||||
def month_of_year_as_text(month)
|
||||
month.nil? ? '??' : I18n.t('todos.recurrence.pattern.month_names')[month]
|
||||
end
|
||||
|
||||
def build_recurring_todo(attribute_handler)
|
||||
@recurring_todo = @user.recurring_todos.build(attribute_handler.safe_attributes)
|
||||
end
|
||||
|
||||
def update_recurring_todo(recurring_todo, attribute_handler)
|
||||
recurring_todo.assign_attributes(attribute_handler.safe_attributes)
|
||||
recurring_todo
|
||||
end
|
||||
|
||||
def build_from_recurring_todo(recurring_todo)
|
||||
@recurring_todo = recurring_todo
|
||||
@attributes = Tracks::AttributeHandler.new(@user, recurring_todo.attributes)
|
||||
end
|
||||
|
||||
def valid?
|
||||
@recurring_todo.valid?
|
||||
end
|
||||
|
||||
def validate_not_blank(object, msg)
|
||||
errors[:base] << msg if object.blank?
|
||||
end
|
||||
|
||||
def validate_not_nil(object, msg)
|
||||
errors[:base] << msg if object.nil?
|
||||
end
|
||||
|
||||
def validate
|
||||
starts_and_ends_on_validations
|
||||
set_recurrence_on_validations
|
||||
end
|
||||
|
||||
def starts_and_ends_on_validations
|
||||
validate_not_blank(start_from, "The start date needs to be filled in")
|
||||
case ends_on
|
||||
when 'ends_on_number_of_times'
|
||||
validate_not_blank(number_of_occurences, "The number of recurrences needs to be filled in for 'Ends on'")
|
||||
when "ends_on_end_date"
|
||||
validate_not_blank(end_date, "The end date needs to be filled in for 'Ends on'")
|
||||
else
|
||||
errors[:base] << "The end of the recurrence is not selected" unless ends_on == "no_end_date"
|
||||
end
|
||||
end
|
||||
|
||||
def set_recurrence_on_validations
|
||||
# show always or x days before due date. x not null
|
||||
case target
|
||||
when 'show_from_date'
|
||||
# no validations
|
||||
when 'due_date'
|
||||
validate_not_nil(show_always?, "Please select when to show the action")
|
||||
validate_not_blank(show_from_delta, "Please fill in the number of days to show the todo before the due date") unless show_always?
|
||||
else
|
||||
errors[:base] << "Unexpected value of recurrence target selector '#{target}'"
|
||||
end
|
||||
end
|
||||
|
||||
def errors
|
||||
@recurring_todo.errors
|
||||
end
|
||||
|
||||
def get(attribute)
|
||||
@attributes[attribute]
|
||||
end
|
||||
|
||||
# gets the next due date. returns nil if recurrence_target is not 'due_date'
|
||||
def get_due_date(previous)
|
||||
case target
|
||||
when 'due_date'
|
||||
get_next_date(previous)
|
||||
when 'show_from_date'
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_show_from_date(previous)
|
||||
case target
|
||||
when 'due_date'
|
||||
# so set show from date relative to due date unless show_always is true or show_from_delta is nil
|
||||
return nil unless put_in_tickler?
|
||||
get_due_date(previous) - show_from_delta.days
|
||||
when 'show_from_date'
|
||||
# Leave due date empty
|
||||
get_next_date(previous)
|
||||
end
|
||||
end
|
||||
|
||||
# checks if the next todos should be put in the tickler for recurrence_target == 'due_date'
|
||||
def put_in_tickler?
|
||||
!( show_always? || show_from_delta.nil?)
|
||||
end
|
||||
|
||||
def get_next_date(previous)
|
||||
raise "Should not call AbstractRepeatPattern.get_next_date directly. Overwrite in subclass"
|
||||
end
|
||||
|
||||
def continues_recurring?(previous)
|
||||
return @recurring_todo.occurences_count < @recurring_todo.number_of_occurences unless @recurring_todo.number_of_occurences.nil?
|
||||
return true if self.end_date.nil? || self.ends_on == 'no_end_date'
|
||||
|
||||
case self.target
|
||||
when 'due_date'
|
||||
get_due_date(previous) <= self.end_date
|
||||
when 'show_from_date'
|
||||
get_show_from_date(previous) <= self.end_date
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Determine start date to calculate next date for recurring todo which
|
||||
# takes start_from and previous into account.
|
||||
# offset needs to be 1.day for daily patterns or the start will be the
|
||||
# same day as the previous
|
||||
def determine_start(previous, offset=0.day)
|
||||
start = self.start_from || NullTime.new
|
||||
now = Time.zone.now
|
||||
if previous
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
# start_from as start to search for next date
|
||||
start > previous ? start : previous + offset
|
||||
else
|
||||
# skip to present
|
||||
start > now ? start : now
|
||||
end
|
||||
end
|
||||
|
||||
# Example: get 3rd (x) wednesday (weekday) of december (month) 2014 (year)
|
||||
# 5th means last, so it will return the 4th if there is no 5th
|
||||
def get_xth_day_of_month(x, weekday, month, year)
|
||||
raise "Weekday should be between 0 and 6 with 0=sunday. You supplied #{weekday}" unless (0..6).include?(weekday)
|
||||
raise "x should be 1-4 for first-fourth or 5 for last. You supplied #{x}" unless (0..5).include?(x)
|
||||
|
||||
if x == 5
|
||||
return find_last_day_x_of_month(weekday, month, year)
|
||||
else
|
||||
return find_xth_day_of_month(x, weekday, month, year)
|
||||
end
|
||||
end
|
||||
|
||||
def find_last_day_x_of_month(weekday, month, year)
|
||||
# count backwards. use UTC to avoid strange timezone oddities
|
||||
# where last_day -= 1.day seems to shift tz+0100 to tz+0000
|
||||
last_day = Time.utc(year, month, Time.days_in_month(month))
|
||||
while last_day.wday != weekday
|
||||
last_day -= 1.day
|
||||
end
|
||||
# convert back to local timezone
|
||||
Time.zone.local(last_day.year, last_day.month, last_day.day)
|
||||
end
|
||||
|
||||
def find_xth_day_of_month(x, weekday, month, year)
|
||||
# 1-4th -> count upwards last -> count backwards. use UTC to avoid strange
|
||||
# timezone oddities where last_day -= 1.day seems to shift tz+0100 to
|
||||
# tz+0000
|
||||
start = Time.utc(year,month,1)
|
||||
n = x
|
||||
while n > 0
|
||||
while start.wday() != weekday
|
||||
start+= 1.day
|
||||
end
|
||||
n -= 1
|
||||
start+= 1.day unless n==0
|
||||
end
|
||||
# convert back to local timezone
|
||||
Time.zone.local(start.year, start.month, start.day)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
35
app/models/recurring_todos/daily_recurring_todos_builder.rb
Normal file
35
app/models/recurring_todos/daily_recurring_todos_builder.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
module RecurringTodos
|
||||
|
||||
class DailyRecurringTodosBuilder < AbstractRecurringTodosBuilder
|
||||
attr_reader :recurring_todo, :pattern
|
||||
|
||||
def initialize(user, attributes)
|
||||
super(user, attributes, DailyRepeatPattern)
|
||||
end
|
||||
|
||||
def attributes_to_filter
|
||||
%w{daily_selector daily_every_x_days}
|
||||
end
|
||||
|
||||
def map_attributes(mapping)
|
||||
mapping.set(:only_work_days, only_work_days?(@selector))
|
||||
mapping.set(:every_other1, mapping.get(:daily_every_x_days))
|
||||
mapping.except(:daily_every_x_days)
|
||||
end
|
||||
|
||||
def only_work_days?(daily_selector)
|
||||
{ 'daily_every_x_day' => false,
|
||||
'daily_every_work_day' => true}[daily_selector]
|
||||
end
|
||||
|
||||
def selector_key
|
||||
:daily_selector
|
||||
end
|
||||
|
||||
def valid_selector?(selector)
|
||||
%w{daily_every_x_day daily_every_work_day}.include?(selector)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
52
app/models/recurring_todos/daily_repeat_pattern.rb
Normal file
52
app/models/recurring_todos/daily_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
module RecurringTodos
|
||||
|
||||
class DailyRepeatPattern < AbstractRepeatPattern
|
||||
|
||||
def initialize(user)
|
||||
super user
|
||||
end
|
||||
|
||||
def every_x_days
|
||||
get :every_other1
|
||||
end
|
||||
|
||||
def only_work_days?
|
||||
get :only_work_days
|
||||
end
|
||||
|
||||
def recurrence_pattern
|
||||
if only_work_days?
|
||||
I18n.t("todos.recurrence.pattern.on_work_days")
|
||||
elsif every_x_days > 1
|
||||
I18n.t("todos.recurrence.pattern.every_n_days", :n => every_x_days)
|
||||
else
|
||||
I18n.t("todos.recurrence.pattern.every_day")
|
||||
end
|
||||
end
|
||||
|
||||
def validate
|
||||
super
|
||||
errors[:base] << "Every other nth day may not be empty for this daily recurrence setting" if (!only_work_days?) && every_x_days.blank?
|
||||
end
|
||||
|
||||
def get_next_date(previous)
|
||||
# previous is the due date of the previous todo or it is the completed_at
|
||||
# date when the completed_at date is after due_date (i.e. you did not make
|
||||
# the due date in time)
|
||||
|
||||
start = determine_start(previous, 1.day)
|
||||
|
||||
if only_work_days?
|
||||
# jump over weekend if necessary
|
||||
return start + 2.day if start.wday() == 6 # saturday
|
||||
return start + 1.day if start.wday() == 0 # sunday
|
||||
return start
|
||||
else
|
||||
# if there was no previous todo, do not add n: the first todo starts on
|
||||
# today or on start_from
|
||||
return previous == nil ? start : start+every_x_days.day-1.day
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
module RecurringTodos
|
||||
|
||||
class MonthlyRecurringTodosBuilder < AbstractRecurringTodosBuilder
|
||||
|
||||
def initialize(user, attributes)
|
||||
super(user, attributes, MonthlyRepeatPattern)
|
||||
end
|
||||
|
||||
def attributes_to_filter
|
||||
%w{
|
||||
monthly_selector monthly_every_x_day monthly_every_x_month
|
||||
monthly_every_x_month2 monthly_every_xth_day monthly_day_of_week
|
||||
}
|
||||
end
|
||||
|
||||
def map_attributes(mapping)
|
||||
mapping = map(mapping, :every_other1, 'monthly_every_x_day')
|
||||
mapping = map(mapping, :every_other3, 'monthly_every_xth_day')
|
||||
mapping = map(mapping, :every_count, 'monthly_day_of_week')
|
||||
|
||||
mapping.set(:recurrence_selector, get_recurrence_selector)
|
||||
|
||||
mapping.set(:every_other2, mapping.get(get_every_other2))
|
||||
mapping.except('monthly_every_x_month').except('monthly_every_x_month2')
|
||||
end
|
||||
|
||||
def get_recurrence_selector
|
||||
@selector=='monthly_every_x_day' ? 0 : 1
|
||||
end
|
||||
|
||||
def get_every_other2
|
||||
get_recurrence_selector == 0 ? 'monthly_every_x_month' : 'monthly_every_x_month2'
|
||||
end
|
||||
|
||||
def selector_key
|
||||
:monthly_selector
|
||||
end
|
||||
|
||||
def valid_selector?(selector)
|
||||
%w{monthly_every_x_day monthly_every_xth_day}.include?(selector)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
142
app/models/recurring_todos/monthly_repeat_pattern.rb
Normal file
142
app/models/recurring_todos/monthly_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
module RecurringTodos
|
||||
|
||||
class MonthlyRepeatPattern < AbstractRepeatPattern
|
||||
|
||||
def initialize(user)
|
||||
super user
|
||||
end
|
||||
|
||||
def recurrence_selector
|
||||
get :recurrence_selector
|
||||
end
|
||||
|
||||
def every_x_day?
|
||||
get(:recurrence_selector) == 0
|
||||
end
|
||||
|
||||
def every_x_day
|
||||
get(:every_other1)
|
||||
end
|
||||
|
||||
def every_xth_day?
|
||||
get(:recurrence_selector) == 1
|
||||
end
|
||||
|
||||
def every_x_month
|
||||
# in case monthly pattern is every day x, return every_other2 otherwise
|
||||
# return a default value
|
||||
get(:recurrence_selector) == 0 ? get(:every_other2) : 1
|
||||
end
|
||||
|
||||
def every_x_month2
|
||||
# in case monthly pattern is every xth day, return every_other2 otherwise
|
||||
# return a default value
|
||||
get(:recurrence_selector) == 1 ? get(:every_other2) : 1
|
||||
end
|
||||
|
||||
def every_xth_day(default=nil)
|
||||
get(:every_other3) || default
|
||||
end
|
||||
|
||||
def day_of_week
|
||||
get :every_count
|
||||
end
|
||||
|
||||
def recurrence_pattern
|
||||
if recurrence_selector == 0
|
||||
recurrence_pattern_for_specific_day
|
||||
else
|
||||
recurrence_pattern_for_relative_day_in_month
|
||||
end
|
||||
end
|
||||
|
||||
def validate
|
||||
super
|
||||
|
||||
case recurrence_selector
|
||||
when 0 # 'monthly_every_x_day'
|
||||
validate_not_blank(every_x_month, "Every other nth month may not be empty for recurrence setting")
|
||||
when 1 # 'monthly_every_xth_day'
|
||||
validate_not_blank(every_x_month2, "Every other nth month may not be empty for recurrence setting")
|
||||
validate_not_blank(day_of_week, "The day of the month may not be empty for recurrence setting")
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence selector '#{recurrence_selector}'"
|
||||
end
|
||||
end
|
||||
|
||||
def get_next_date(previous)
|
||||
start = determine_start(previous)
|
||||
n = get(:every_other2)
|
||||
|
||||
case recurrence_selector
|
||||
when 0 # specific day of the month
|
||||
return find_specific_day_of_month(previous, start, n)
|
||||
when 1 # relative weekday of a month
|
||||
return find_relative_day_of_month(start, n)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_specific_day_of_month(previous, start, n)
|
||||
if (previous && start.mday >= every_x_day) || (previous.nil? && start.mday > every_x_day)
|
||||
# there is no next day n in this month, search in next month
|
||||
#
|
||||
# start += n.months
|
||||
#
|
||||
# The above seems to not work. Fiddle with timezone. Looks like we hit a
|
||||
# bug in rails here where 2008-12-01 +0100 plus 1.month becomes
|
||||
# 2008-12-31 +0100. For now, just calculate in UTC and convert back to
|
||||
# local timezone.
|
||||
#
|
||||
# TODO: recheck if future rails versions have this problem too
|
||||
start = Time.utc(start.year, start.month, start.day)+n.months
|
||||
end
|
||||
Time.zone.local(start.year, start.month, every_x_day)
|
||||
end
|
||||
|
||||
def find_relative_day_of_month(start, n)
|
||||
the_next = get_xth_day_of_month(every_xth_day, day_of_week, start.month, start.year)
|
||||
if the_next.nil? || the_next <= start
|
||||
# the nth day is already passed in this month, go to next month and try
|
||||
# again
|
||||
|
||||
# fiddle with timezone. Looks like we hit a bug in rails here where
|
||||
# 2008-12-01 +0100 plus 1.month becomes 2008-12-31 +0100. For now, just
|
||||
# calculate in UTC and convert back to local timezone.
|
||||
# TODO: recheck if future rails versions have this problem too
|
||||
the_next = Time.utc(the_next.year, the_next.month, the_next.day)+n.months
|
||||
the_next = Time.zone.local(the_next.year, the_next.month, the_next.day)
|
||||
|
||||
# TODO: if there is still no match, start will be set to nil. if we ever
|
||||
# support 5th day of the month, we need to handle this case
|
||||
the_next = get_xth_day_of_month(every_xth_day, day_of_week, the_next.month, the_next.year)
|
||||
end
|
||||
the_next
|
||||
end
|
||||
|
||||
def recurrence_pattern_for_specific_day
|
||||
on_day = " #{I18n.t('todos.recurrence.pattern.on_day_n', :n => every_x_day)}"
|
||||
if every_xth_day(0) > 1
|
||||
I18n.t("todos.recurrence.pattern.every_n_months", :n => every_xth_day) + on_day
|
||||
else
|
||||
I18n.t("todos.recurrence.pattern.every_month") + on_day
|
||||
end
|
||||
end
|
||||
|
||||
def recurrence_pattern_for_relative_day_in_month
|
||||
n_months = if every_x_month2 > 1
|
||||
"#{every_x_month2} #{I18n.t('common.months')}"
|
||||
else
|
||||
I18n.t('common.month')
|
||||
end
|
||||
I18n.t('todos.recurrence.pattern.every_xth_day_of_every_n_months',
|
||||
x: xth(every_xth_day),
|
||||
day: day_of_week_as_text(day_of_week),
|
||||
n_months: n_months)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
78
app/models/recurring_todos/recurring_todos_builder.rb
Normal file
78
app/models/recurring_todos/recurring_todos_builder.rb
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
module RecurringTodos
|
||||
|
||||
class RecurringTodosBuilder
|
||||
|
||||
attr_reader :builder, :project, :context, :tag_list, :user
|
||||
|
||||
def initialize (user, attributes)
|
||||
@user = user
|
||||
@attributes = Tracks::AttributeHandler.new(@user, attributes)
|
||||
|
||||
parse_dates
|
||||
parse_project
|
||||
parse_context
|
||||
|
||||
@builder = create_builder(@attributes[:recurring_period])
|
||||
end
|
||||
|
||||
def create_builder(selector)
|
||||
raise "Unknown recurrence selector in :recurring_period (#{selector})" unless valid_selector? selector
|
||||
eval("RecurringTodos::#{selector.capitalize}RecurringTodosBuilder.new(@user, @attributes)")
|
||||
end
|
||||
|
||||
def build
|
||||
@builder.build
|
||||
end
|
||||
|
||||
def update(recurring_todo)
|
||||
@builder.update(recurring_todo)
|
||||
end
|
||||
|
||||
def save
|
||||
@builder.save_project if @new_project_created
|
||||
@builder.save_context if @new_context_created
|
||||
|
||||
return @builder.save
|
||||
end
|
||||
|
||||
def saved_recurring_todo
|
||||
@builder.saved_recurring_todo
|
||||
end
|
||||
|
||||
def recurring_todo
|
||||
@builder.recurring_todo
|
||||
end
|
||||
|
||||
def attributes
|
||||
@builder.attributes
|
||||
end
|
||||
|
||||
def pattern
|
||||
@builder.pattern
|
||||
end
|
||||
|
||||
def errors
|
||||
@builder.errors
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_selector?(selector)
|
||||
%w{daily weekly monthly yearly}.include?(selector)
|
||||
end
|
||||
|
||||
def parse_dates
|
||||
%w{end_date start_from}.each {|date| @attributes.parse_date date }
|
||||
end
|
||||
|
||||
def parse_project
|
||||
@project, @new_project_created = @attributes.parse_collection(:project, @user.projects)
|
||||
end
|
||||
|
||||
def parse_context
|
||||
@context, @new_context_created = @attributes.parse_collection(:context, @user.contexts)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
42
app/models/recurring_todos/weekly_recurring_todos_builder.rb
Normal file
42
app/models/recurring_todos/weekly_recurring_todos_builder.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
module RecurringTodos
|
||||
|
||||
class WeeklyRecurringTodosBuilder < AbstractRecurringTodosBuilder
|
||||
|
||||
def initialize(user, attributes)
|
||||
super(user, attributes, WeeklyRepeatPattern)
|
||||
end
|
||||
|
||||
def attributes_to_filter
|
||||
%w{weekly_selector weekly_every_x_week} + %w{monday tuesday wednesday thursday friday saturday sunday}.map{|day| "weekly_return_#{day}" }
|
||||
end
|
||||
|
||||
def map_attributes(mapping)
|
||||
mapping = map(mapping, :every_other1, 'weekly_every_x_week')
|
||||
|
||||
{ monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 0 }
|
||||
.each do |day, index|
|
||||
mapping = map_day(mapping, :every_day, "weekly_return_#{day}", index)
|
||||
end
|
||||
|
||||
mapping
|
||||
end
|
||||
|
||||
def map_day(mapping, key, source_key, index)
|
||||
mapping.set_if_nil(key, ' ') # avoid nil
|
||||
mapping.set_if_nil(source_key, ' ') # avoid nil
|
||||
|
||||
mapping.set(key, mapping.get(key)[0, index] + mapping.get(source_key) + mapping.get(key)[index+1, mapping.get(key).length])
|
||||
mapping.except(source_key)
|
||||
end
|
||||
|
||||
def selector_key
|
||||
nil
|
||||
end
|
||||
|
||||
def valid_selector?(key)
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
86
app/models/recurring_todos/weekly_repeat_pattern.rb
Normal file
86
app/models/recurring_todos/weekly_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
module RecurringTodos
|
||||
|
||||
class WeeklyRepeatPattern < AbstractRepeatPattern
|
||||
|
||||
def initialize(user)
|
||||
super user
|
||||
end
|
||||
|
||||
def every_x_week
|
||||
get :every_other1
|
||||
end
|
||||
|
||||
{ monday: 1, tuesday: 2, wednesday: 3, thursday: 4, friday: 5, saturday: 6, sunday: 0 }.each do |day, number|
|
||||
define_method("on_#{day}") do
|
||||
on_xday number
|
||||
end
|
||||
end
|
||||
|
||||
def on_xday(n)
|
||||
get(:every_day) && get(:every_day)[n, 1] != ' '
|
||||
end
|
||||
|
||||
def recurrence_pattern
|
||||
if every_x_week > 1
|
||||
I18n.t("todos.recurrence.pattern.every_n", :n => every_x_week) + " " + I18n.t("common.weeks")
|
||||
else
|
||||
I18n.t('todos.recurrence.pattern.weekly')
|
||||
end
|
||||
end
|
||||
|
||||
def validate
|
||||
super
|
||||
validate_not_blank(every_x_week, "Every other nth week may not be empty for weekly recurrence setting")
|
||||
something_set = %w{sunday monday tuesday wednesday thursday friday saturday}.inject(false) { |set, day| set || self.send("on_#{day}") }
|
||||
errors[:base] << "You must specify at least one day on which the todo recurs" unless something_set
|
||||
end
|
||||
|
||||
def get_next_date(previous)
|
||||
start = determine_start_date(previous)
|
||||
|
||||
day = find_first_day_in_this_week(start)
|
||||
return day unless day == -1
|
||||
|
||||
# we did not find anything this week, so check the nth next, starting from
|
||||
# sunday
|
||||
start = start + self.every_x_week.week - (start.wday()).days
|
||||
|
||||
start = find_first_day_in_this_week(start)
|
||||
return start unless start == -1
|
||||
|
||||
raise Exception.new, "unable to find next weekly date (#{self.every_day})"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def determine_start_date(previous)
|
||||
if previous.nil?
|
||||
return self.start_from || Time.zone.now
|
||||
else
|
||||
start = previous + 1.day
|
||||
if start.wday() == 0
|
||||
# we went to a new week, go to the nth next week and find first match
|
||||
# that week. Note that we already went into the next week, so -1
|
||||
start += (every_x_week-1).week
|
||||
end
|
||||
unless self.start_from.nil?
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
# start_from as start to search for next date
|
||||
start = self.start_from if self.start_from > previous
|
||||
end
|
||||
return start
|
||||
end
|
||||
end
|
||||
|
||||
def find_first_day_in_this_week(start)
|
||||
# check if there are any days left this week for the next todo
|
||||
start.wday().upto 6 do |i|
|
||||
return start + (i-start.wday()).days if on_xday(i)
|
||||
end
|
||||
-1
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
44
app/models/recurring_todos/yearly_recurring_todos_builder.rb
Normal file
44
app/models/recurring_todos/yearly_recurring_todos_builder.rb
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
module RecurringTodos
|
||||
|
||||
class YearlyRecurringTodosBuilder < AbstractRecurringTodosBuilder
|
||||
|
||||
def initialize(user, attributes)
|
||||
super(user, attributes, YearlyRepeatPattern)
|
||||
end
|
||||
|
||||
def attributes_to_filter
|
||||
%w{ yearly_selector yearly_month_of_year yearly_month_of_year2
|
||||
yearly_every_x_day yearly_every_xth_day yearly_day_of_week
|
||||
}
|
||||
end
|
||||
|
||||
def map_attributes(mapping)
|
||||
mapping.set(:recurrence_selector, get_recurrence_selector)
|
||||
mapping.set(:every_other2, mapping.get(get_every_other2))
|
||||
|
||||
mapping = map(mapping, :every_other1, 'yearly_every_x_day')
|
||||
mapping = map(mapping, :every_other3, 'yearly_every_xth_day')
|
||||
mapping = map(mapping, :every_count, 'yearly_day_of_week')
|
||||
|
||||
mapping.except(:yearly_month_of_year).except(:yearly_month_of_year2)
|
||||
end
|
||||
|
||||
def selector_key
|
||||
:yearly_selector
|
||||
end
|
||||
|
||||
def valid_selector?(selector)
|
||||
%w{yearly_every_x_day yearly_every_xth_day}.include?(selector.to_s)
|
||||
end
|
||||
|
||||
def get_recurrence_selector
|
||||
@selector=='yearly_every_x_day' ? 0 : 1
|
||||
end
|
||||
|
||||
def get_every_other2
|
||||
{ 0 => :yearly_month_of_year, 1 => :yearly_month_of_year2 }[get_recurrence_selector]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
109
app/models/recurring_todos/yearly_repeat_pattern.rb
Normal file
109
app/models/recurring_todos/yearly_repeat_pattern.rb
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
module RecurringTodos
|
||||
|
||||
class YearlyRepeatPattern < AbstractRepeatPattern
|
||||
|
||||
def initialize(user)
|
||||
super user
|
||||
end
|
||||
|
||||
def recurrence_selector
|
||||
get :recurrence_selector
|
||||
end
|
||||
|
||||
def month_of_year
|
||||
get :every_other2
|
||||
end
|
||||
|
||||
def every_x_day
|
||||
get :every_other1
|
||||
end
|
||||
|
||||
def every_xth_day
|
||||
get :every_other3
|
||||
end
|
||||
|
||||
def day_of_week
|
||||
get :every_count
|
||||
end
|
||||
|
||||
def month_of_year2
|
||||
# if recurrence pattern is every xth day in a month, return month otherwise
|
||||
# return a default value
|
||||
get(:recurrence_selector) == 1 ? get(:every_other2) : Time.zone.now.month
|
||||
end
|
||||
|
||||
def recurrence_pattern
|
||||
if self.recurrence_selector == 0
|
||||
I18n.t("todos.recurrence.pattern.every_year_on", :date => date_as_month_day)
|
||||
else
|
||||
I18n.t("todos.recurrence.pattern.every_year_on",
|
||||
:date => I18n.t("todos.recurrence.pattern.the_xth_day_of_month",
|
||||
:x => xth(every_xth_day),
|
||||
:day => day_of_week_as_text(day_of_week),
|
||||
:month => month_of_year_as_text(month_of_year)
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
def validate
|
||||
super
|
||||
case recurrence_selector
|
||||
when 0 # 'yearly_every_x_day'
|
||||
validate_not_blank(month_of_year, "The month of the year may not be empty for recurrence setting")
|
||||
validate_not_blank(every_x_day, "The day of the month may not be empty for recurrence setting")
|
||||
when 1 # 'yearly_every_xth_day'
|
||||
validate_not_blank(month_of_year2, "The month of the year may not be empty for recurrence setting")
|
||||
validate_not_blank(every_xth_day, "The nth day of the month may not be empty for recurrence setting")
|
||||
validate_not_blank(day_of_week, "The day of the week may not be empty for recurrence setting")
|
||||
else
|
||||
raise "unexpected value of recurrence selector '#{recurrence_selector}'"
|
||||
end
|
||||
end
|
||||
|
||||
def get_next_date(previous)
|
||||
start = determine_start(previous)
|
||||
month = get(:every_other2)
|
||||
|
||||
case recurrence_selector
|
||||
when 0 # specific day of a specific month
|
||||
return get_specific_day_of_month(start, month)
|
||||
when 1 # relative weekday of a specific month
|
||||
return get_relative_weekday_of_month(start, month)
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def date_as_month_day
|
||||
I18n.l(DateTime.new(Time.zone.now.year, month_of_year, every_x_day), :format => :month_day)
|
||||
end
|
||||
|
||||
def get_specific_day_of_month(start, month)
|
||||
if start.month > month || (start.month == month && start.day >= every_x_day)
|
||||
# if there is no next month n and day m in this year, search in next
|
||||
# year
|
||||
start = Time.zone.local(start.year+1, month, 1)
|
||||
else
|
||||
# if there is a next month n, stay in this year
|
||||
start = Time.zone.local(start.year, month, 1)
|
||||
end
|
||||
Time.zone.local(start.year, month, every_x_day)
|
||||
end
|
||||
|
||||
def get_relative_weekday_of_month(start, month)
|
||||
# if there is no next month n in this year, search in next year
|
||||
the_next = start.month > month ? Time.zone.local(start.year+1, month, 1) : start
|
||||
|
||||
# get the xth day of the month
|
||||
the_next = get_xth_day_of_month(self.every_xth_day, day_of_week, month, the_next.year)
|
||||
|
||||
# if the_next is before previous, we went back into the past, so try next
|
||||
# year
|
||||
the_next = get_xth_day_of_month(self.every_xth_day, day_of_week, month, start.year+1) if the_next <= start
|
||||
|
||||
the_next
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -5,91 +5,91 @@
|
|||
<div id="recurring_todo_form_container">
|
||||
<div id="recurring_todo">
|
||||
<label for="recurring_todo_description"><%= Todo.human_attribute_name('description') %></label><%=
|
||||
text_field_tag( "recurring_todo[description]", @recurring_todo.description, "size" => 30, "maxlength" => 100, :id => "edit_recurring_todo_description") -%>
|
||||
text_field_tag( "recurring_todo[description]", @form_helper.description, "size" => 30, "maxlength" => 100, :id => "edit_recurring_todo_description") -%>
|
||||
|
||||
<label for="recurring_todo_notes"><%= Todo.human_attribute_name('notes') %></label><%=
|
||||
text_area_tag( "recurring_todo[notes]", @recurring_todo.notes, {:cols => 29, :rows => 6}) -%>
|
||||
text_area_tag( "recurring_todo[notes]", @form_helper.notes, {:cols => 29, :rows => 6}) -%>
|
||||
|
||||
<label for="edit_recurring_todo_project_name"><%= Todo.human_attribute_name('project') %></label>
|
||||
<input id="edit_recurring_todo_project_name" name="project_name" autocomplete="off" size="30" type="text" value="<%= @recurring_todo.project.nil? ? 'None' : @recurring_todo.project.name.gsub(/"/,""") %>" />
|
||||
<input id="edit_recurring_todo_project_name" name="project_name" autocomplete="off" size="30" type="text" value="<%= @form_helper.project.nil? ? 'None' : @form_helper.project.name.gsub(/"/,""") %>" />
|
||||
<div class="page_name_auto_complete" id="edit_project_list" style="display:none"></div>
|
||||
|
||||
<label for="edit_recurring_todo_context_name"><%= Todo.human_attribute_name('context') %></label>
|
||||
<input id="edit_recurring_todo_context_name" name="context_name" autocomplete="off" size="30" type="text" value="<%= @recurring_todo.context.name %>" />
|
||||
<input id="edit_recurring_todo_context_name" name="context_name" autocomplete="off" size="30" type="text" value="<%= @form_helper.context.name %>" />
|
||||
<div class="page_name_auto_complete" id="edit_context_list" style="display:none"></div>
|
||||
|
||||
<label for="edit_recurring_todo_tag_list"><%= "#{Todo.human_attribute_name('tags')} #{t('shared.separate_tags_with_commas')}"%></label>
|
||||
<%= text_field_tag "edit_recurring_todo_tag_list", @recurring_todo.tag_list, :size => 30 -%>
|
||||
<%= text_field_tag "edit_recurring_todo_tag_list", @form_helper.tag_list, :size => 30 -%>
|
||||
</div>
|
||||
</div>
|
||||
<div id="recurring_edit_period_id">
|
||||
<div id="recurring_edit_period">
|
||||
<label><%= t('todos.recurrence_period') %></label><br/>
|
||||
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'daily', @recurring_todo.recurring_period == 'daily')%> <%= t('todos.recurrence.daily') %><br/>
|
||||
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'weekly', @recurring_todo.recurring_period == 'weekly')%> <%= t('todos.recurrence.weekly') %><br/>
|
||||
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'monthly', @recurring_todo.recurring_period == 'monthly')%> <%= t('todos.recurrence.monthly') %><br/>
|
||||
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'yearly', @recurring_todo.recurring_period == 'yearly')%> <%= t('todos.recurrence.yearly') %><br/>
|
||||
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'daily', @form_helper.recurring_period == 'daily')%> <%= t('todos.recurrence.daily') %><br/>
|
||||
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'weekly', @form_helper.recurring_period == 'weekly')%> <%= t('todos.recurrence.weekly') %><br/>
|
||||
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'monthly', @form_helper.recurring_period == 'monthly')%> <%= t('todos.recurrence.monthly') %><br/>
|
||||
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'yearly', @form_helper.recurring_period == 'yearly')%> <%= t('todos.recurrence.yearly') %><br/>
|
||||
</div>
|
||||
<div id="recurring_timespan">
|
||||
<br/>
|
||||
<label for="recurring_todo[start_from]"><%= t('todos.recurrence.starts_on') %>: </label><%=
|
||||
text_field_tag("recurring_todo_edit_start_from", format_date(@recurring_todo.start_from), "size" => 12, "class" => "Date", "autocomplete" => "off") %><br/>
|
||||
text_field_tag("recurring_todo_edit_start_from", format_date(@form_helper.start_from), "size" => 12, "class" => "Date", "autocomplete" => "off") %><br/>
|
||||
<br/>
|
||||
<label for="recurring_todo[ends_on]"><%= t('todos.recurrence.ends_on') %>:</label><br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', @recurring_todo.ends_on == 'no_end_date')%> <%= t('todos.recurrence.no_end_date') %><br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', @recurring_todo.ends_on == 'ends_on_number_of_times')%>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', @form_helper.ends_on == 'no_end_date')%> <%= t('todos.recurrence.no_end_date') %><br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', @form_helper.ends_on == 'ends_on_number_of_times')%>
|
||||
<%= raw t('todos.recurrence.ends_on_number_times', :number => text_field( :recurring_todo, :number_of_occurences, "size" => 3)) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', @recurring_todo.ends_on == 'ends_on_end_date')%>
|
||||
<%= raw t('todos.recurrence.ends_on_date', :date => text_field_tag('recurring_todo_edit_end_date', format_date(@recurring_todo.end_date), "size" => 12, "class" => "Date", "autocomplete" => "off")) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', @form_helper.ends_on == 'ends_on_end_date')%>
|
||||
<%= raw t('todos.recurrence.ends_on_date', :date => text_field_tag('recurring_todo_edit_end_date', format_date(@form_helper.end_date), "size" => 12, "class" => "Date", "autocomplete" => "off")) %><br/>
|
||||
</div></div>
|
||||
<div id="recurring_edit_daily" style="display:<%= @recurring_todo.recurring_period == 'daily' ? 'block' : 'none' %> ">
|
||||
<div id="recurring_edit_daily" style="display:<%= @form_helper.recurring_period == 'daily' ? 'block' : 'none' %> ">
|
||||
<label><%= t('todos.recurrence.daily_options') %></label><br/>
|
||||
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', !@recurring_todo.only_work_days)%>
|
||||
<%= raw t('todos.recurrence.daily_every_number_day', :number=> text_field_tag( 'recurring_todo[daily_every_x_days]', @recurring_todo.daily_every_x_days, {"size" => 3})) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day', @recurring_todo.only_work_days)%> <%= t('todos.recurrence.every_work_day') %><br/>
|
||||
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', !@form_helper.only_work_days)%>
|
||||
<%= raw t('todos.recurrence.daily_every_number_day', :number=> text_field_tag( 'recurring_todo[daily_every_x_days]', @form_helper.daily_every_x_days, {"size" => 3})) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day', @form_helper.only_work_days)%> <%= t('todos.recurrence.every_work_day') %><br/>
|
||||
</div>
|
||||
<div id="recurring_edit_weekly" style="display:<%= @recurring_todo.recurring_period == 'weekly' ? 'block' : 'none' %>">
|
||||
<div id="recurring_edit_weekly" style="display:<%= @form_helper.recurring_period == 'weekly' ? 'block' : 'none' %>">
|
||||
<label><%= t('todos.recurrence.weekly_options') %></label><br/>
|
||||
<%= raw t('todos.recurrence.weekly_every_number_week', :number => text_field_tag('recurring_todo[weekly_every_x_week]', @recurring_todo.weekly_every_x_week, {"size" => 3})) %><br/>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_monday]', 'm', @recurring_todo.on_monday ) %> <%= t('date.day_names')[1] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_tuesday]', 't', @recurring_todo.on_tuesday) %> <%= t('date.day_names')[2] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_wednesday]', 'w', @recurring_todo.on_wednesday) %> <%= t('date.day_names')[3] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_thursday]', 't', @recurring_todo.on_thursday) %> <%= t('date.day_names')[4] %><br/>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_friday]', 'f', @recurring_todo.on_friday) %> <%= t('date.day_names')[5] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_saturday]', 's', @recurring_todo.on_saturday) %> <%= t('date.day_names')[6] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_sunday]', 's', @recurring_todo.on_sunday) %> <%= t('date.day_names')[0] %><br/>
|
||||
<%= raw t('todos.recurrence.weekly_every_number_week', :number => text_field_tag('recurring_todo[weekly_every_x_week]', @form_helper.weekly_every_x_week, {"size" => 3})) %><br/>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_monday]', 'm', @form_helper.on_monday ) %> <%= t('date.day_names')[1] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_tuesday]', 't', @form_helper.on_tuesday) %> <%= t('date.day_names')[2] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_wednesday]', 'w', @form_helper.on_wednesday) %> <%= t('date.day_names')[3] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_thursday]', 't', @form_helper.on_thursday) %> <%= t('date.day_names')[4] %><br/>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_friday]', 'f', @form_helper.on_friday) %> <%= t('date.day_names')[5] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_saturday]', 's', @form_helper.on_saturday) %> <%= t('date.day_names')[6] %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_sunday]', 's', @form_helper.on_sunday) %> <%= t('date.day_names')[0] %><br/>
|
||||
</div>
|
||||
<div id="recurring_edit_monthly" style="display:<%= @recurring_todo.recurring_period == 'monthly' ? 'block' : 'none' %>">
|
||||
<div id="recurring_edit_monthly" style="display:<%= @form_helper.recurring_period == 'monthly' ? 'block' : 'none' %>">
|
||||
<label><%= t('todos.recurrence.monthly_options') %></label><br/>
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', @recurring_todo.is_monthly_every_x_day || @recurring_todo.recurring_period == 'weekly')%>
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', @form_helper.monthly_every_x_day? || @form_helper.recurring_period == 'weekly')%>
|
||||
<%= raw t('todos.recurrence.day_x_on_every_x_month',
|
||||
:day => text_field_tag('recurring_todo[monthly_every_x_day]', @recurring_todo.monthly_every_x_day, {"size" => 3}),
|
||||
:month => text_field_tag('recurring_todo[monthly_every_x_month]', @recurring_todo.monthly_every_x_month, {"size" => 3})) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day', @recurring_todo.is_monthly_every_xth_day)%>
|
||||
:day => text_field_tag('recurring_todo[monthly_every_x_day]', @form_helper.monthly_every_x_day, {"size" => 3}),
|
||||
:month => text_field_tag('recurring_todo[monthly_every_x_month]', @form_helper.monthly_every_x_month, {"size" => 3})) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day', @form_helper.monthly_every_xth_day?)%>
|
||||
<%= raw t('todos.recurrence.monthly_every_xth_day',
|
||||
:day => select_tag('recurring_todo[monthly_every_xth_day]', options_for_select(@xth_day, @xth_day[@recurring_todo.monthly_every_xth_day(1)-1][1])),
|
||||
:day_of_week => select_tag('recurring_todo[monthly_day_of_week]' , options_for_select(@days_of_week, @recurring_todo.monthly_day_of_week)),
|
||||
:month => text_field_tag('recurring_todo[monthly_every_x_month2]', @recurring_todo.monthly_every_x_month2, {"size" => 3})) %><br/>
|
||||
:day => select_tag('recurring_todo[monthly_every_xth_day]', options_for_select(@xth_day, @xth_day[@form_helper.monthly_every_xth_day(1)-1][1])),
|
||||
:day_of_week => select_tag('recurring_todo[monthly_day_of_week]' , options_for_select(@days_of_week, @form_helper.monthly_day_of_week)),
|
||||
:month => text_field_tag('recurring_todo[monthly_every_x_month2]', @form_helper.monthly_every_x_month2, {"size" => 3})) %><br/>
|
||||
</div>
|
||||
<div id="recurring_edit_yearly" style="display:<%= @recurring_todo.recurring_period == 'yearly' ? 'block' : 'none' %>">
|
||||
<div id="recurring_edit_yearly" style="display:<%= @form_helper.recurring_period == 'yearly' ? 'block' : 'none' %>">
|
||||
<label><%= t('todos.recurrence.yearly_options') %></label><br/>
|
||||
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', @recurring_todo.recurrence_selector == 0)%>
|
||||
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', @form_helper.recurrence_selector == 0)%>
|
||||
<%= raw t('todos.recurrence.yearly_every_x_day',
|
||||
:month => select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year)),
|
||||
:day => text_field_tag('recurring_todo[yearly_every_x_day]', @recurring_todo.yearly_every_x_day, "size" => 3)) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day', @recurring_todo.recurrence_selector == 1)%>
|
||||
:month => select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, @form_helper.yearly_month_of_year)),
|
||||
:day => text_field_tag('recurring_todo[yearly_every_x_day]', @form_helper.yearly_every_x_day, "size" => 3)) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day', @form_helper.recurrence_selector == 1)%>
|
||||
<%= raw t('todos.recurrence.yearly_every_xth_day',
|
||||
:day => select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day, @recurring_todo.yearly_every_xth_day)),
|
||||
:day_of_week => select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, @recurring_todo.yearly_day_of_week)),
|
||||
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year2))) %><br/>
|
||||
:day => select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day, @form_helper.yearly_every_xth_day)),
|
||||
:day_of_week => select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, @form_helper.yearly_day_of_week)),
|
||||
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, @form_helper.yearly_month_of_year2))) %><br/>
|
||||
</div>
|
||||
<div id="recurring_target">
|
||||
<label><%= t('todos.recurrence.recurrence_on.options') %></label><br/>
|
||||
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @recurring_todo.target == 'due_date')%> <%= t('todos.recurrence.recurrence_on.due_date') %>. <%= t('todos.recurrence.recurrence_on.show_options') %>:
|
||||
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @recurring_todo.show_always?)%> <%= t('todos.recurrence.recurrence_on.show_always') %>
|
||||
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', !@recurring_todo.show_always?)%>
|
||||
<%= raw t('todos.recurrence.recurrence_on.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', @recurring_todo.show_from_delta, {"size" => 3})) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @recurring_todo.target == 'show_from_date')%> <%= t('todos.recurrence.recurrence_on.from_tickler') %><br/>
|
||||
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @form_helper.target == 'due_date')%> <%= t('todos.recurrence.recurrence_on.due_date') %>. <%= t('todos.recurrence.recurrence_on.show_options') %>:
|
||||
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @form_helper.show_always?)%> <%= t('todos.recurrence.recurrence_on.show_always') %>
|
||||
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', !@form_helper.show_always?)%>
|
||||
<%= raw t('todos.recurrence.recurrence_on.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', @form_helper.show_from_delta, {"size" => 3})) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @form_helper.target == 'show_from_date')%> <%= t('todos.recurrence.recurrence_on.from_tickler') %><br/>
|
||||
<br/>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue