mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-27 20:38:48 +01:00
Merge branch 'master' of git://github.com/TracksApp/tracks into upstream
This commit is contained in:
commit
87b25f77d2
50 changed files with 3411 additions and 1978 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -22,3 +22,4 @@ public/stylesheets/tracks-cached.css
|
|||
.idea
|
||||
.rvmrc
|
||||
.yardoc
|
||||
tags
|
||||
|
|
|
|||
11
Gemfile
11
Gemfile
|
|
@ -15,6 +15,7 @@ gem "rubyjedi-actionwebservice", :require => "actionwebservice"
|
|||
gem "rubycas-client", "~>2.2.1"
|
||||
gem "ruby-openid", :require => "openid"
|
||||
gem "sqlite3"
|
||||
gem "mysql"
|
||||
gem 'bcrypt-ruby', '~> 2.1.4'
|
||||
gem 'htmlentities', '~> 4.3.0'
|
||||
gem "mail"
|
||||
|
|
@ -25,10 +26,6 @@ else
|
|||
gem "soap4r", "~>1.5.8"
|
||||
end
|
||||
|
||||
gem "webrat", ">=0.7.0", :groups => [:cucumber, :test]
|
||||
gem "database_cleaner", ">=0.5.0", :groups => [:cucumber, :selenium]
|
||||
gem "cucumber-rails", "~>0.3.0", :groups => :cucumber
|
||||
|
||||
group :development do
|
||||
if RUBY_VERSION.to_f >= 1.9
|
||||
gem "ruby-debug19"
|
||||
|
|
@ -49,8 +46,8 @@ group :test do
|
|||
gem "rspec-rails", "~>1.3.3"
|
||||
gem "thoughtbot-factory_girl"
|
||||
gem 'memory_test_fix', '~>0.1.3'
|
||||
end
|
||||
|
||||
group :selenium do
|
||||
gem "selenium-client"
|
||||
gem "webrat", ">=0.7.0"
|
||||
gem "database_cleaner", ">=0.5.0"
|
||||
gem "cucumber-rails", "~>0.3.0"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ GEM
|
|||
daemons (>= 1.0.3)
|
||||
fastthread (>= 1.0.1)
|
||||
gem_plugin (>= 0.2.3)
|
||||
mysql (2.8.1)
|
||||
nokogiri (1.4.7)
|
||||
polyglot (0.3.2)
|
||||
rack (1.1.0)
|
||||
|
|
@ -129,6 +130,7 @@ DEPENDENCIES
|
|||
mail
|
||||
memory_test_fix (~> 0.1.3)
|
||||
mongrel
|
||||
mysql
|
||||
rack (= 1.1.0)
|
||||
rails (~> 2.3.12)
|
||||
rake (~> 0.8.7)
|
||||
|
|
|
|||
27
README
27
README
|
|
@ -19,13 +19,30 @@
|
|||
* Copyright: (cc) 2004-2011 rousette.org.uk.
|
||||
* License: GNU GPL
|
||||
|
||||
All the documentation for Tracks can be found within the /doc directory and at http://bsag.github.com/tracks/
|
||||
The latter includes full instructions for both new installations and upgrades from older installations of Tracks.
|
||||
The instructions might appear long and intimidatingly complex, but that is mostly because of the number of different platforms supported, and the different configurations which can be used (e.g. running Tracks on your local computer or on a remote server). If you choose the appropriate section for your situation (installation vs. upgrade), and use the easiest (recommended) method, you should find the instructions easy to follow. If you encounter problems, try searching the wiki, forum or mailing list (URLs above), and ask a question if you cannot find a solution to your problem.
|
||||
All the documentation for Tracks can be found within the /doc directory and at
|
||||
http://bsag.github.com/tracks/
|
||||
|
||||
The latter includes full instructions for both new installations and upgrades
|
||||
from older installations of Tracks.
|
||||
|
||||
The instructions might appear long and intimidatingly complex, but that is
|
||||
mostly because of the number of different platforms supported, and the
|
||||
different configurations which can be used (e.g. running Tracks on your local
|
||||
computer or on a remote server). If you choose the appropriate section for your
|
||||
situation (installation vs. upgrade), and use the easiest (recommended) method,
|
||||
you should find the instructions easy to follow. If you encounter problems, try
|
||||
searching the wiki, forum or mailing list (URLs above), and ask a question if
|
||||
you cannot find a solution to your problem.
|
||||
|
||||
The wiki has a lot of user contributed installation HOWTOs for various webhosts, specific OS's and more.
|
||||
|
||||
If you are thinking about contributing towards the development of Tracks, please read /doc/README_DEVELOPERS for general information, or /doc/tracks_api_wrapper.rb for information on Tracks' API. Also you can find some information on development on the wiki.
|
||||
If you are thinking about contributing towards the development of Tracks,
|
||||
please read /doc/README_DEVELOPERS for general information, or
|
||||
/doc/tracks_api_wrapper.rb for information on Tracks' API. Also you can find
|
||||
some information on development on the wiki.
|
||||
|
||||
While fully usable for everyday use, Tracks is still a work in progress. Make sure that you take sensible precautions and back up all your data frequently, taking particular care when you are upgrading.
|
||||
While fully usable for everyday use, Tracks is still a work in progress. Make
|
||||
sure that you take sensible precautions and back up all your data frequently,
|
||||
taking particular care when you are upgrading.
|
||||
|
||||
Enjoy being productive!
|
||||
|
|
|
|||
|
|
@ -177,9 +177,9 @@ class ApplicationController < ActionController::Base
|
|||
def create_todo_from_recurring_todo(rt, date=nil)
|
||||
# create todo and initialize with data from recurring_todo rt
|
||||
todo = current_user.todos.build( { :description => rt.description, :notes => rt.notes, :project_id => rt.project_id, :context_id => rt.context_id})
|
||||
todo.recurring_todo_id = rt.id
|
||||
|
||||
# set dates
|
||||
todo.recurring_todo_id = rt.id
|
||||
todo.due = rt.get_due_date(date)
|
||||
|
||||
show_from_date = rt.get_show_from_date(date)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ class RecurringTodosController < ApplicationController
|
|||
@page_title = t('todos.recurring_actions_title')
|
||||
@source_view = params['_source_view'] || 'recurring_todo'
|
||||
find_and_inactivate
|
||||
@recurring_todos = current_user.recurring_todos.active
|
||||
@completed_recurring_todos = current_user.recurring_todos.completed.find(:all, :limit => 10)
|
||||
@recurring_todos = current_user.recurring_todos.active.find(:all, :include => [:tags, :taggings])
|
||||
@completed_recurring_todos = current_user.recurring_todos.completed.find(:all, :limit => 10, :include => [:tags, :taggings])
|
||||
|
||||
@no_recurring_todos = @recurring_todos.size == 0
|
||||
@no_completed_recurring_todos = @completed_recurring_todos.size == 0
|
||||
|
|
@ -21,15 +21,15 @@ class RecurringTodosController < ApplicationController
|
|||
|
||||
def new
|
||||
end
|
||||
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
|
||||
def done
|
||||
@page_title = t('todos.completed_recurring_actions_title')
|
||||
@source_view = params['_source_view'] || 'recurring_todo'
|
||||
items_per_page = 20
|
||||
page = params[:page] || 1
|
||||
page = params[:page] || 1
|
||||
@completed_recurring_todos = current_user.recurring_todos.completed.paginate :page => params[:page], :per_page => items_per_page
|
||||
@total = @count = current_user.recurring_todos.completed.count
|
||||
@range_low = (page.to_i-1) * items_per_page + 1
|
||||
|
|
@ -54,7 +54,7 @@ class RecurringTodosController < ApplicationController
|
|||
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'
|
||||
|
|
@ -70,7 +70,7 @@ class RecurringTodosController < ApplicationController
|
|||
end
|
||||
params["recurring_todo"]["project_id"] = project.id
|
||||
end
|
||||
|
||||
|
||||
# update context
|
||||
if params['recurring_todo']['context_id'].blank? && !params['context_name'].blank?
|
||||
context = current_user.contexts.find_by_name(params['context_name'].strip)
|
||||
|
|
@ -88,14 +88,14 @@ class RecurringTodosController < ApplicationController
|
|||
%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
|
||||
|
||||
|
||||
@saved = @recurring_todo.update_attributes params["recurring_todo"]
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def create
|
||||
p = RecurringTodoCreateParamsHelper.new(params)
|
||||
p.attributes['end_date']=parse_date_per_user_prefs(p.attributes['end_date'])
|
||||
|
|
@ -109,7 +109,7 @@ class RecurringTodosController < ApplicationController
|
|||
@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.find_or_create_by_name(p.context_name)
|
||||
@new_context_created = context.new_record_before_save?
|
||||
|
|
@ -134,13 +134,13 @@ class RecurringTodosController < ApplicationController
|
|||
@new_recurring_todo = RecurringTodo.new
|
||||
else
|
||||
@status_message = t('todos.error_saving_recurring')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def destroy
|
||||
# remove all references to this recurring todo
|
||||
@todos = @recurring_todo.todos
|
||||
|
|
@ -149,16 +149,16 @@ class RecurringTodosController < ApplicationController
|
|||
t.recurring_todo_id = nil
|
||||
t.save
|
||||
end
|
||||
|
||||
|
||||
# delete the recurring todo
|
||||
@saved = @recurring_todo.destroy
|
||||
|
||||
# count remaining recurring todos
|
||||
@active_remaining = current_user.recurring_todos.active.count
|
||||
@completed_remaining = current_user.recurring_todos.completed.count
|
||||
|
||||
|
||||
respond_to do |format|
|
||||
|
||||
|
||||
format.html do
|
||||
if @saved
|
||||
notify :notice, t('todos.recurring_deleted_success'), 2.0
|
||||
|
|
@ -168,10 +168,10 @@ class RecurringTodosController < ApplicationController
|
|||
redirect_to :action => 'index'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
format.js do
|
||||
render
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -184,19 +184,19 @@ class RecurringTodosController < ApplicationController
|
|||
|
||||
if @recurring_todo.active?
|
||||
@completed_remaining = current_user.recurring_todos.completed.count
|
||||
|
||||
|
||||
# from completed back to active -> check if there is an active todo
|
||||
@active_todos = @recurring_todo.todos.active.count
|
||||
# create todo if there is no active todo belonging to the activated
|
||||
# recurring_todo
|
||||
@new_recurring_todo = create_todo_from_recurring_todo(@recurring_todo) if @active_todos == 0
|
||||
end
|
||||
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def toggle_star
|
||||
@recurring_todo.toggle_star!
|
||||
@saved = @recurring_todo.save!
|
||||
|
|
@ -204,13 +204,13 @@ class RecurringTodosController < ApplicationController
|
|||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class RecurringTodoCreateParamsHelper
|
||||
|
||||
def initialize(params)
|
||||
@params = params['request'] || params
|
||||
@attributes = params['request'] && params['request']['recurring_todo'] || params['recurring_todo']
|
||||
|
||||
|
||||
# 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
|
||||
|
|
@ -221,47 +221,47 @@ class RecurringTodosController < ApplicationController
|
|||
'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 unless @attributes['project_id'].blank?
|
||||
return false if project_name.blank?
|
||||
return false if project_name == 'None'
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
def context_specified_by_name?
|
||||
return false unless @attributes['context_id'].blank?
|
||||
return false if context_name.blank?
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
||||
def init
|
||||
@days_of_week = []
|
||||
0.upto 6 do |i|
|
||||
0.upto 6 do |i|
|
||||
@days_of_week << [t('date.day_names')[i], i]
|
||||
end
|
||||
|
||||
|
|
@ -274,15 +274,18 @@ class RecurringTodosController < ApplicationController
|
|||
@projects = current_user.projects.find(:all, :include => [:default_context])
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
end
|
||||
|
||||
|
||||
def get_recurring_todo_from_param
|
||||
@recurring_todo = current_user.recurring_todos.find(params[:id])
|
||||
end
|
||||
|
||||
def find_and_inactivate
|
||||
# find active recurring todos without active todos and inactivate them
|
||||
recurring_todos = current_user.recurring_todos.active
|
||||
recurring_todos.each { |rt| rt.toggle_completion! if rt.todos.not_completed.count == 0}
|
||||
|
||||
current_user.recurring_todos.active.all(
|
||||
:select => "recurring_todos.id, recurring_todos.state",
|
||||
:joins => "LEFT JOIN todos fai_todos ON (recurring_todos.id = fai_todos.recurring_todo_id) AND (NOT fai_todos.state='completed')",
|
||||
:conditions => "fai_todos.id IS NULL").each { |rt| current_user.recurring_todos.find(rt.id).toggle_completion! }
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
if @todo.errors.empty?
|
||||
@todo.starred= (params[:new_todo_starred]||"").include? "true"
|
||||
@todo.starred= (params[:new_todo_starred]||"").include? "true" if params[:new_todo_starred]
|
||||
|
||||
@todo.add_predecessor_list(predecessor_list)
|
||||
|
||||
|
|
@ -361,6 +361,49 @@ class TodosController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def mobile_done
|
||||
# copied from toggle_check, left out other formats as they shouldn't come here
|
||||
# ultimately would like to just use toggle_check
|
||||
@todo = current_user.todos.find(params['id'])
|
||||
@source_view = params['_source_view'] || 'todo'
|
||||
@original_item_due = @todo.due
|
||||
@original_item_was_deferred = @todo.deferred?
|
||||
@original_item_was_pending = @todo.pending?
|
||||
@original_item_was_hidden = @todo.hidden?
|
||||
@original_item_context_id = @todo.context_id
|
||||
@original_item_project_id = @todo.project_id
|
||||
@todo_was_completed_from_deferred_or_blocked_state = @original_item_was_deferred || @original_item_was_pending
|
||||
@saved = @todo.toggle_completion!
|
||||
|
||||
@todo_was_blocked_from_completed_state = @todo.pending? # since we toggled_completion the previous state was completed
|
||||
|
||||
# check if this todo has a related recurring_todo. If so, create next todo
|
||||
@new_recurring_todo = check_for_next_todo(@todo) if @saved
|
||||
|
||||
@predecessors = @todo.uncompleted_predecessors
|
||||
if @saved
|
||||
if @todo.completed?
|
||||
@pending_to_activate = @todo.activate_pending_todos
|
||||
else
|
||||
@active_to_block = @todo.block_successors
|
||||
end
|
||||
end
|
||||
|
||||
if @saved
|
||||
if cookies[:mobile_url]
|
||||
old_path = cookies[:mobile_url]
|
||||
cookies[:mobile_url] = {:value => nil, :secure => SITE_CONFIG['secure_cookies']}
|
||||
notify(:notice, t("todos.action_marked_complete", :description => @todo.description, :completed => @todo.completed? ? 'complete' : 'incomplete'))
|
||||
redirect_to old_path
|
||||
else
|
||||
notify(:notice, t("todos.action_marked_complete", :description => @todo.description, :completed => @todo.completed? ? 'complete' : 'incomplete'))
|
||||
redirect_to todos_path(:format => 'm')
|
||||
end
|
||||
else
|
||||
render :action => "edit", :format => :m
|
||||
end
|
||||
end
|
||||
|
||||
def toggle_star
|
||||
@todo = current_user.todos.find(params['id'])
|
||||
@todo.toggle_star!
|
||||
|
|
@ -1103,7 +1146,7 @@ class TodosController < ApplicationController
|
|||
if @todo_was_completed_from_deferred_or_blocked_state
|
||||
@remaining_in_context = @remaining_deferred_or_pending_count
|
||||
else
|
||||
@remaining_in_context = current_user.projects.find(project_id).todos.active.count
|
||||
@remaining_in_context = current_user.projects.find(project_id).todos.active_or_hidden.count
|
||||
end
|
||||
|
||||
@target_context_count = current_user.projects.find(project_id).todos.active.count
|
||||
|
|
@ -1176,6 +1219,10 @@ class TodosController < ApplicationController
|
|||
lambda do
|
||||
@page_title = t('todos.mobile_todos_page_title')
|
||||
@home = true
|
||||
|
||||
max_completed = current_user.prefs.show_number_completed
|
||||
@done = current_user.todos.completed.find(:all, :limit => max_completed, :include => Todo::DEFAULT_INCLUDES) unless max_completed == 0
|
||||
|
||||
cookies[:mobile_url]= { :value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
|
||||
determine_down_count
|
||||
|
||||
|
|
@ -1516,6 +1563,15 @@ class TodosController < ApplicationController
|
|||
@params = params['request'] || params
|
||||
@prefs = prefs
|
||||
@attributes = params['request'] && params['request']['todo'] || params['todo']
|
||||
|
||||
if @attributes && @attributes[:tags]
|
||||
# for single tags, @attributed[:tags] returns a hash. For multiple tags,
|
||||
# it with return an array of hashes. Make sure it is always an array of hashes
|
||||
@attributes[:tags][:tag] = [@attributes[:tags][:tag]] unless @attributes[:tags][:tag].class == Array
|
||||
# the REST api may use <tags> which will collide with tags association, so rename tags to add_tags
|
||||
@attributes[:add_tags] = @attributes[:tags]
|
||||
@attributes.delete :tags
|
||||
end
|
||||
end
|
||||
|
||||
def attributes
|
||||
|
|
|
|||
|
|
@ -89,6 +89,12 @@ module TodosHelper
|
|||
:title => todo.pending? ? t('todos.blocked_by', :predecessors => todo.uncompleted_predecessors.map(&:description).join(', ')) : "", :readonly => todo.pending?)
|
||||
end
|
||||
|
||||
def remote_mobile_checkbox(todo=@todo)
|
||||
form_tag mobile_done_todo_path(@todo, :format => 'm'), :method => :put, :class => "mobile-done", :name => "mobile_complete_#{@todo.id}" do
|
||||
check_box_tag('_source_view', 'todo', @todo && @todo.completed?, "onClick" => "document.mobile_complete_#{@todo.id}.submit()")
|
||||
end
|
||||
end
|
||||
|
||||
def date_span(todo=@todo)
|
||||
if todo.completed?
|
||||
content_tag(:span, {:class => :grey}) { format_date( todo.completed_at ) }
|
||||
|
|
|
|||
|
|
@ -120,8 +120,8 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def stalled?
|
||||
# stalled is active/hidden project with no active todos
|
||||
return false if self.completed?
|
||||
# Stalled projects are active projects with no active next actions
|
||||
return false if self.completed? || self.hidden?
|
||||
return self.todos.deferred_or_blocked.empty? && self.todos.not_deferred_or_blocked.empty?
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
class RecurringTodo < ActiveRecord::Base
|
||||
|
||||
|
||||
belongs_to :context
|
||||
belongs_to :project
|
||||
belongs_to :user
|
||||
|
|
@ -10,11 +10,11 @@ class RecurringTodo < ActiveRecord::Base
|
|||
named_scope :completed, :conditions => { :state => 'completed'}
|
||||
|
||||
attr_protected :user
|
||||
|
||||
|
||||
include AASM
|
||||
aasm_column :state
|
||||
aasm_initial_state :active
|
||||
|
||||
|
||||
aasm_state :active, :enter => Proc.new { |t| t.occurences_count = 0 }
|
||||
aasm_state :completed, :enter => Proc.new { |t| t.completed_at = Time.zone.now }, :exit => Proc.new { |t| t.completed_at = nil }
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
aasm_event :activate do
|
||||
transitions :to => :active, :from => [:completed]
|
||||
end
|
||||
|
||||
|
||||
validates_presence_of :description
|
||||
validates_presence_of :recurring_period
|
||||
validates_presence_of :target
|
||||
|
|
@ -33,12 +33,12 @@ class RecurringTodo < ActiveRecord::Base
|
|||
validates_presence_of :context
|
||||
|
||||
validates_length_of :description, :maximum => 100
|
||||
validates_length_of :notes, :maximum => 60000, :allow_nil => true
|
||||
validates_length_of :notes, :maximum => 60000, :allow_nil => true
|
||||
|
||||
validate :period_specific_validations
|
||||
validate :starts_and_ends_on_validations
|
||||
validate :set_recurrence_on_validations
|
||||
|
||||
|
||||
def period_specific_validations
|
||||
if %W[daily weekly monthly yearly].include?(recurring_period)
|
||||
self.send("validate_#{recurring_period}")
|
||||
|
|
@ -118,7 +118,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
raise Exception.new, "unexpected value of recurrence target selector '#{self.recurrence_target}'"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# the following recurrence patterns can be stored:
|
||||
#
|
||||
# daily todos - recurrence_period = 'daily'
|
||||
|
|
@ -140,9 +140,9 @@ class RecurringTodo < ActiveRecord::Base
|
|||
# x is stored in every_other3, y is stored in every_count, z is stored in every_other2
|
||||
# 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'
|
||||
|
|
@ -153,34 +153,34 @@ class RecurringTodo < ActiveRecord::Base
|
|||
raise Exception.new, "unknown daily recurrence pattern: '#{selector}'"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def daily_every_x_days=(x)
|
||||
if recurring_period=='daily'
|
||||
self.every_other1 = x
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def daily_every_x_days
|
||||
return self.every_other1
|
||||
end
|
||||
|
||||
|
||||
# WEEKLY
|
||||
|
||||
|
||||
def weekly_every_x_week=(x)
|
||||
self.every_other1 = x if recurring_period=='weekly'
|
||||
end
|
||||
|
||||
|
||||
def weekly_every_x_week
|
||||
return self.every_other1
|
||||
end
|
||||
|
||||
|
||||
def switch_week_day (day, position)
|
||||
if self.every_day.nil?
|
||||
self.every_day=' '
|
||||
end
|
||||
self.every_day = self.every_day[0,position] + day + self.every_day[position+1,self.every_day.length]
|
||||
end
|
||||
|
||||
|
||||
def weekly_return_monday=(selector)
|
||||
switch_week_day(selector,1) if recurring_period=='weekly'
|
||||
end
|
||||
|
|
@ -188,15 +188,15 @@ class RecurringTodo < ActiveRecord::Base
|
|||
def weekly_return_tuesday=(selector)
|
||||
switch_week_day(selector,2) if recurring_period=='weekly'
|
||||
end
|
||||
|
||||
|
||||
def weekly_return_wednesday=(selector)
|
||||
switch_week_day(selector,3) if recurring_period=='weekly'
|
||||
end
|
||||
|
||||
|
||||
def weekly_return_thursday=(selector)
|
||||
switch_week_day(selector,4) if recurring_period=='weekly'
|
||||
end
|
||||
|
||||
|
||||
def weekly_return_friday=(selector)
|
||||
switch_week_day(selector,5) if recurring_period=='weekly'
|
||||
end
|
||||
|
|
@ -204,11 +204,11 @@ class RecurringTodo < ActiveRecord::Base
|
|||
def weekly_return_saturday=(selector)
|
||||
switch_week_day(selector,6) if recurring_period=='weekly'
|
||||
end
|
||||
|
||||
|
||||
def weekly_return_sunday=(selector)
|
||||
switch_week_day(selector,0) if recurring_period=='weekly'
|
||||
end
|
||||
|
||||
|
||||
def on_xday(n)
|
||||
unless self.every_day.nil?
|
||||
return self.every_day[n,1] == ' ' ? false : true
|
||||
|
|
@ -216,51 +216,51 @@ class RecurringTodo < ActiveRecord::Base
|
|||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def on_monday
|
||||
return on_xday(1)
|
||||
end
|
||||
|
||||
|
||||
def on_tuesday
|
||||
return on_xday(2)
|
||||
end
|
||||
|
||||
|
||||
def on_wednesday
|
||||
return on_xday(3)
|
||||
end
|
||||
|
||||
|
||||
def on_thursday
|
||||
return on_xday(4)
|
||||
end
|
||||
|
||||
|
||||
def on_friday
|
||||
return on_xday(5)
|
||||
end
|
||||
|
||||
|
||||
def on_saturday
|
||||
return on_xday(6)
|
||||
end
|
||||
|
||||
|
||||
def on_sunday
|
||||
return on_xday(0)
|
||||
end
|
||||
|
||||
# MONTHLY
|
||||
|
||||
|
||||
def monthly_selector=(selector)
|
||||
if recurring_period=='monthly'
|
||||
self.recurrence_selector= (selector=='monthly_every_x_day')? 0 : 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def monthly_every_x_day=(x)
|
||||
self.every_other1 = x if recurring_period=='monthly'
|
||||
end
|
||||
|
||||
|
||||
def monthly_every_x_day
|
||||
return self.every_other1
|
||||
end
|
||||
|
||||
|
||||
def is_monthly_every_x_day
|
||||
return self.recurrence_selector == 0 if recurring_period == 'monthly'
|
||||
return false
|
||||
|
|
@ -270,11 +270,11 @@ class RecurringTodo < ActiveRecord::Base
|
|||
return self.recurrence_selector == 1 if recurring_period == 'monthly'
|
||||
return false
|
||||
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
|
||||
|
|
@ -298,36 +298,36 @@ class RecurringTodo < ActiveRecord::Base
|
|||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def monthly_every_xth_day=(x)
|
||||
self.every_other3 = x if recurring_period=='monthly'
|
||||
end
|
||||
|
||||
|
||||
def monthly_every_xth_day(default=nil)
|
||||
return self.every_other3 unless self.every_other3.nil?
|
||||
return default
|
||||
end
|
||||
|
||||
|
||||
def monthly_day_of_week=(dow)
|
||||
self.every_count = dow if recurring_period=='monthly'
|
||||
end
|
||||
|
||||
|
||||
def monthly_day_of_week
|
||||
return self.every_count
|
||||
end
|
||||
|
||||
|
||||
# YEARLY
|
||||
|
||||
|
||||
def yearly_selector=(selector)
|
||||
if recurring_period=='yearly'
|
||||
self.recurrence_selector = (selector=='yearly_every_x_day') ? 0 : 1
|
||||
end
|
||||
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
|
||||
|
|
@ -341,7 +341,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
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
|
||||
|
|
@ -351,33 +351,33 @@ class RecurringTodo < ActiveRecord::Base
|
|||
return Time.zone.now.month
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def yearly_every_x_day=(x)
|
||||
self.every_other1 = x if recurring_period=='yearly'
|
||||
end
|
||||
|
||||
|
||||
def yearly_every_x_day
|
||||
return self.every_other1
|
||||
end
|
||||
|
||||
|
||||
def yearly_every_xth_day=(x)
|
||||
self.every_other3 = x if recurring_period=='yearly'
|
||||
end
|
||||
|
||||
|
||||
def yearly_every_xth_day
|
||||
return self.every_other3
|
||||
end
|
||||
|
||||
|
||||
def yearly_day_of_week=(dow)
|
||||
self.every_count=dow if recurring_period=='yearly'
|
||||
end
|
||||
|
||||
|
||||
def yearly_day_of_week
|
||||
return self.every_count
|
||||
end
|
||||
|
||||
|
||||
# target
|
||||
|
||||
|
||||
def recurring_target=(t)
|
||||
self.target = t
|
||||
end
|
||||
|
|
@ -392,11 +392,11 @@ class RecurringTodo < ActiveRecord::Base
|
|||
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
|
||||
|
|
@ -435,41 +435,45 @@ class RecurringTodo < ActiveRecord::Base
|
|||
else
|
||||
n_months = I18n.t('common.month')
|
||||
end
|
||||
return I18n.t('todos.recurrence.pattern.every_xth_day_of_every_n_months',
|
||||
return 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
|
||||
when 'yearly'
|
||||
if self.recurrence_selector == 0
|
||||
return I18n.t("todos.recurrence.pattern.every_year_on",
|
||||
return 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
|
||||
return I18n.t("todos.recurrence.pattern.every_year_on",
|
||||
return 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))
|
||||
end
|
||||
else
|
||||
return 'unknown recurrence pattern: period unknown'
|
||||
end
|
||||
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')]
|
||||
return self.every_other3.nil? ? '??' : xth_day[self.every_other3-1]
|
||||
end
|
||||
|
||||
|
||||
def day_of_week
|
||||
return (self.every_count.nil? ? '??' : I18n.t('todos.recurrence.pattern.day_names')[self.every_count])
|
||||
end
|
||||
|
||||
|
||||
def month_of_year
|
||||
return self.every_other2.nil? ? '??' : I18n.t('todos.recurrence.pattern.month_names')[self.every_other2]
|
||||
end
|
||||
|
||||
|
||||
def starred?
|
||||
tags.any? {|tag| tag.name == Todo::STARRED_TAG_NAME }
|
||||
return has_tag?(Todo::STARRED_TAG_NAME)
|
||||
end
|
||||
|
||||
|
||||
def has_tag?(tag_name)
|
||||
return self.tags.any? {|tag| tag.name == tag_name}
|
||||
end
|
||||
|
||||
def get_due_date(previous)
|
||||
case self.target
|
||||
when 'due_date'
|
||||
|
|
@ -481,7 +485,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def get_show_from_date(previous)
|
||||
case self.target
|
||||
when 'due_date'
|
||||
|
|
@ -498,7 +502,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def get_next_date(previous)
|
||||
case self.recurring_period
|
||||
when 'daily'
|
||||
|
|
@ -513,14 +517,14 @@ class RecurringTodo < ActiveRecord::Base
|
|||
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
|
||||
|
|
@ -533,7 +537,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
return previous == nil ? start : start+every_other1.day-1.day
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def get_weekly_date(previous)
|
||||
# determine start
|
||||
if previous == nil
|
||||
|
|
@ -551,12 +555,12 @@ class RecurringTodo < ActiveRecord::Base
|
|||
start = self.start_from if self.start_from > previous
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
# we did not find anything this week, so check the nth next, starting from
|
||||
# sunday
|
||||
start = start + self.every_other1.week - (start.wday()).days
|
||||
|
|
@ -568,9 +572,9 @@ class RecurringTodo < ActiveRecord::Base
|
|||
|
||||
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
|
||||
|
|
@ -594,7 +598,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
# go back to day
|
||||
end
|
||||
return 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
|
||||
|
|
@ -618,7 +622,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
def get_xth_day_of_month(x, weekday, month, year)
|
||||
if x == 5
|
||||
# last -> count backwards. use UTC to avoid strange timezone oddities
|
||||
|
|
@ -646,12 +650,12 @@ class RecurringTodo < ActiveRecord::Base
|
|||
return 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)
|
||||
|
|
@ -663,25 +667,25 @@ class RecurringTodo < ActiveRecord::Base
|
|||
start = Time.zone.local(start.year, month, 1)
|
||||
end
|
||||
return 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
|
||||
|
||||
|
||||
return the_next
|
||||
else
|
||||
raise Exception.new, "unknown monthly recurrence selection (#{self.recurrence_selector})"
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
def has_next_todo(previous)
|
||||
unless self.number_of_occurences.nil?
|
||||
return self.occurences_count < self.number_of_occurences
|
||||
|
|
@ -700,11 +704,11 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def toggle_completion!
|
||||
return completed? ? activate! : complete!
|
||||
end
|
||||
|
||||
|
||||
def toggle_star!
|
||||
if starred?
|
||||
_remove_tags Todo::STARRED_TAG_NAME
|
||||
|
|
@ -715,7 +719,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
starred?
|
||||
end
|
||||
|
||||
|
||||
def remove_from_project!
|
||||
self.project = nil
|
||||
self.save
|
||||
|
|
@ -729,17 +733,18 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def inc_occurences
|
||||
self.occurences_count += 1
|
||||
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)
|
||||
# offset needs to be 1.day for daily patterns
|
||||
|
||||
|
||||
if previous.nil?
|
||||
start = self.start_from.nil? ? Time.zone.now : self.start_from
|
||||
# skip to present
|
||||
|
|
@ -747,13 +752,12 @@ class RecurringTodo < ActiveRecord::Base
|
|||
else
|
||||
start = previous + offset
|
||||
|
||||
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
|
||||
# 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 && self.start_from > previous )
|
||||
end
|
||||
|
||||
return start
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -47,13 +47,6 @@ class Todo < ActiveRecord::Base
|
|||
STARRED_TAG_NAME = "starred"
|
||||
DEFAULT_INCLUDES = [ :project, :context, :tags, :taggings, :pending_successors, :uncompleted_predecessors, :recurring_todo ]
|
||||
|
||||
# regular expressions for dependencies. TODO: are these still used?
|
||||
RE_TODO = /[^']+/
|
||||
RE_CONTEXT = /[^']+/
|
||||
RE_PROJECT = /[^']+/
|
||||
RE_PARTS = /'(#{RE_TODO})'\s<'(#{RE_CONTEXT})';\s'(#{RE_PROJECT})'>/ # results in array
|
||||
RE_SPEC = /'#{RE_TODO}'\s<'#{RE_CONTEXT}';\s'#{RE_PROJECT}'>/ # results in string
|
||||
|
||||
# state machine
|
||||
include AASM
|
||||
aasm_column :state
|
||||
|
|
@ -113,12 +106,11 @@ class Todo < ActiveRecord::Base
|
|||
|
||||
def no_uncompleted_predecessors_or_deferral?
|
||||
no_deferral = show_from.blank? or Time.zone.now > show_from
|
||||
no_uncompleted_predecessors = uncompleted_predecessors.all(true).empty?
|
||||
return (no_deferral && no_uncompleted_predecessors)
|
||||
return (no_deferral && no_uncompleted_predecessors?)
|
||||
end
|
||||
|
||||
def no_uncompleted_predecessors?
|
||||
return uncompleted_predecessors.all(true).empty?
|
||||
return !uncompleted_predecessors?
|
||||
end
|
||||
|
||||
def uncompleted_predecessors?
|
||||
|
|
@ -197,8 +189,8 @@ class Todo < ActiveRecord::Base
|
|||
return !pending_successors.empty?
|
||||
end
|
||||
|
||||
def has_tag?(tag)
|
||||
return self.tags.select{|t| t.name==tag }.size > 0
|
||||
def has_tag?(tag_name)
|
||||
return self.tags.any? {|tag| tag.name == tag_name}
|
||||
end
|
||||
|
||||
def hidden?
|
||||
|
|
@ -239,12 +231,6 @@ class Todo < ActiveRecord::Base
|
|||
defer! if active? && !date.blank? && date > user.date
|
||||
end
|
||||
|
||||
alias_method :original_project, :project
|
||||
|
||||
def project
|
||||
original_project.nil? ? Project.null_object : original_project
|
||||
end
|
||||
|
||||
def self.feed_options(user)
|
||||
{
|
||||
:title => 'Tracks Actions',
|
||||
|
|
@ -253,7 +239,7 @@ class Todo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def starred?
|
||||
tags.any? {|tag| tag.name == STARRED_TAG_NAME}
|
||||
return has_tag?(STARRED_TAG_NAME)
|
||||
end
|
||||
|
||||
def toggle_star!
|
||||
|
|
@ -276,18 +262,18 @@ class Todo < ActiveRecord::Base
|
|||
def add_predecessor_list(predecessor_list)
|
||||
return unless predecessor_list.kind_of? String
|
||||
|
||||
@predecessor_array=[]
|
||||
|
||||
predecessor_ids_array = predecessor_list.split(",")
|
||||
predecessor_ids_array.each do |todo_id|
|
||||
@predecessor_array=predecessor_list.split(",").inject([]) do |list, todo_id|
|
||||
predecessor = self.user.todos.find_by_id( todo_id.to_i ) unless todo_id.blank?
|
||||
@predecessor_array << predecessor unless predecessor.nil?
|
||||
list << predecessor unless predecessor.nil?
|
||||
list
|
||||
end
|
||||
|
||||
return @predecessor_array
|
||||
end
|
||||
|
||||
def add_predecessor(t)
|
||||
return if t.nil?
|
||||
|
||||
@predecessor_array = predecessors
|
||||
@predecessor_array << t
|
||||
end
|
||||
|
|
@ -310,8 +296,53 @@ class Todo < ActiveRecord::Base
|
|||
self[:notes] = value
|
||||
end
|
||||
|
||||
# Rich Todo API
|
||||
# XML API fixups
|
||||
def predecessor_dependencies=(params)
|
||||
value = params[:predecessor]
|
||||
return if value.nil?
|
||||
|
||||
# for multiple dependencies, value will be an array of id's, but for a single dependency,
|
||||
# value will be a string. In that case convert to array
|
||||
value = [value] unless value.class == Array
|
||||
|
||||
value.each { |ele| add_predecessor(self.user.todos.find_by_id(ele.to_i)) unless ele.blank? }
|
||||
end
|
||||
|
||||
alias_method :original_context=, :context=
|
||||
def context=(value)
|
||||
if value.is_a? Context
|
||||
self.original_context=(value)
|
||||
else
|
||||
c = Context.find_by_name(value[:name])
|
||||
c = Context.create(value) if c.nil?
|
||||
self.original_context=(c)
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :original_project, :project
|
||||
def project
|
||||
original_project.nil? ? Project.null_object : original_project
|
||||
end
|
||||
|
||||
alias_method :original_project=, :project=
|
||||
def project=(value)
|
||||
if value.is_a? Project
|
||||
self.original_project=(value)
|
||||
elsif !(value.nil? || value.is_a?(NullProject))
|
||||
p = Project.find_by_name(value[:name])
|
||||
p = Project.create(value) if p.nil?
|
||||
self.original_project=(p)
|
||||
else
|
||||
self.original_project=value
|
||||
end
|
||||
end
|
||||
|
||||
# used by the REST API. <tags> will also work, this is renamed to add_tags in TodosController::TodoCreateParamsHelper::initialize
|
||||
def add_tags=(params)
|
||||
tag_with params[:tag].inject([]) { |list, value| list << value[:name] } unless params[:tag].nil?
|
||||
end
|
||||
|
||||
# Rich Todo API
|
||||
def self.from_rich_message(user, default_context_id, description, notes)
|
||||
fields = description.match(/([^>@]*)@?([^>]*)>?(.*)/)
|
||||
description = fields[1].strip
|
||||
|
|
|
|||
|
|
@ -7,10 +7,8 @@ if not @not_done.empty?
|
|||
%>
|
||||
<h2><%=@context.name%></h2>
|
||||
<ul class="c">
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="c">
|
||||
<%= render :partial => "todos/mobile_todo",
|
||||
:collection => @not_done,
|
||||
:locals => { :parent_container_type => "context" }-%>
|
||||
</table>
|
||||
</ul>
|
||||
<% end -%>
|
||||
|
|
|
|||
|
|
@ -7,13 +7,12 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="initial-scale = 1.0" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<%= stylesheet_link_tag "mobile", :media => 'handheld,all' %>
|
||||
<title><%= @page_title %></title>
|
||||
</head><body>
|
||||
<% if !(@new_mobile || @edit_mobile)
|
||||
if current_user && !current_user.prefs.nil? -%>
|
||||
<h1><span class="count" id="badge_count"><%= @down_count %></span> <%=
|
||||
<% if current_user && !current_user.prefs.nil? -%>
|
||||
<div id="topbar"><h1><% if @down_count -%><span class="count" id="badge_count"><%= @down_count %></span><% end -%> <%=
|
||||
l(Date.today, :format => current_user.prefs.title_date_format) -%></h1>
|
||||
<div class="nav">
|
||||
<%= (link_to(t('layouts.mobile_navigation.new_action'), new_todo_path(new_todo_params))+" | ") unless @new_mobile -%>
|
||||
|
|
@ -21,10 +20,9 @@
|
|||
<%= (link_to(t('layouts.mobile_navigation.contexts'), contexts_path(:format => 'm'))+" | ") -%>
|
||||
<%= (link_to(t('layouts.mobile_navigation.projects'), projects_path(:format => 'm'))+" | ") -%>
|
||||
<%= (link_to(t('layouts.mobile_navigation.starred'), {:action => "tag", :controller => "todos", :id => "starred.m"})) -%>
|
||||
<% end
|
||||
end -%><%= render_flash -%>
|
||||
</div>
|
||||
<%= yield -%>
|
||||
<% end -%>
|
||||
</div></div>
|
||||
<div id="content"><%= render_flash -%><%= yield -%></div>
|
||||
<hr/><% if current_user && !current_user.prefs.nil? -%>
|
||||
<div class="nav">
|
||||
<%= (link_to(t('layouts.mobile_navigation.logout'), logout_path(:format => 'm')) +" | ") -%>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,17 @@
|
|||
replace_project_with_edit_form();
|
||||
function html_for_edit_form() {
|
||||
return "<%= escape_javascript(render(:partial => 'project_form', :object => @project)) %>";
|
||||
}
|
||||
|
||||
function show_edit_form() {
|
||||
$('div#<%=dom_id(@project, 'edit')%>').html(html_for_edit_form());
|
||||
$('div#<%=dom_id(@project, 'edit')%>').fadeIn(500);
|
||||
$('div#project_name').editable('disable');
|
||||
}
|
||||
|
||||
function set_focus() {
|
||||
$('input.project-name').focus();
|
||||
}
|
||||
|
||||
|
||||
function replace_project_with_edit_form() {
|
||||
$('div#<%=dom_id(@project)%>').fadeOut(250, function() {
|
||||
|
|
@ -8,15 +21,5 @@ function replace_project_with_edit_form() {
|
|||
});
|
||||
}
|
||||
|
||||
function show_edit_form() {
|
||||
$('div#<%=dom_id(@project, 'edit')%>').html(html_for_edit_form());
|
||||
$('div#<%=dom_id(@project, 'edit')%>').fadeIn(500);
|
||||
}
|
||||
|
||||
function set_focus() {
|
||||
$('input.project-name').focus();
|
||||
}
|
||||
|
||||
function html_for_edit_form() {
|
||||
return "<%= escape_javascript(render(:partial => 'project_form', :object => @project)) %>"
|
||||
}
|
||||
replace_project_with_edit_form();
|
||||
|
|
|
|||
|
|
@ -4,24 +4,34 @@
|
|||
<% unless @project.description.blank? -%>
|
||||
<div class="project_description"><%= sanitize(@project.description) %></div>
|
||||
<% end -%>
|
||||
<%= render :partial => "todos/mobile_todo", :collection => @not_done, :locals => { :parent_container_type => "project" }%>
|
||||
<ul class="c">
|
||||
<%= render :partial => "todos/mobile_todo",
|
||||
:collection => @not_done,
|
||||
:locals => { :parent_container_type => "project" }%>
|
||||
</ul>
|
||||
<h2><%= t('projects.deferred_actions')%></h2>
|
||||
<% if @deferred.empty? -%>
|
||||
<%= t('projects.deferred_actions_empty') %>
|
||||
<% else -%>
|
||||
<%= render :partial => "todos/mobile_todo", :collection => @deferred, :locals => { :parent_container_type => "project" }%>
|
||||
<% end
|
||||
<ul class="c">
|
||||
<%= render :partial => "todos/mobile_todo",
|
||||
:collection => @deferred,
|
||||
:locals => { :parent_container_type => "project" }%>
|
||||
</ul><% end
|
||||
-%>
|
||||
<h2><%= t('projects.completed_actions')%></h2>
|
||||
<% if @done.empty? -%>
|
||||
<%= t('projects.completed_actions_empty') %>
|
||||
<% else -%>
|
||||
<%= render :partial => "todos/mobile_todo", :collection => @done, :locals => { :parent_container_type => "project" }%>
|
||||
<% end %>
|
||||
<ul class="c">
|
||||
<%= render :partial => "todos/mobile_todo",
|
||||
:collection => @done,
|
||||
:locals => { :parent_container_type => "project" }%>
|
||||
</ul><% end %>
|
||||
<h2><%= t('projects.notes') %></h2>
|
||||
<% if @project.notes.empty? -%>
|
||||
<%= t('projects.notes_empty') %>
|
||||
<% else -%><%= render :partial => "notes/mobile_notes_summary", :collection => @project.notes %>
|
||||
<% end -%>
|
||||
<h2><%= t('projects.settings') %></h2>
|
||||
<%= t('projects.state', :state => project.aasm_current_state.to_s) %>. <%= @project_default_context %>
|
||||
<%= t('projects.state', :state => project.aasm_current_state.to_s) %>. <%= @project_default_context %>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<label>Edit Recurring Todo</label><br/>
|
||||
<div class="recurring_container">
|
||||
<% #form_remote_tag(
|
||||
#url => recurring_todo_path(@recurring_todo), :method => :put,
|
||||
|
|
@ -99,17 +98,5 @@
|
|||
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @recurring_todo.target == 'show_from_date', {:tabindex => next_tab_index})%> <%= t('todos.recurrence.from_tickler') %><br/>
|
||||
<br/>
|
||||
</div>
|
||||
<div class="recurring_submit_box">
|
||||
<div class="widgets">
|
||||
<button type="submit" class="positive" id="recurring_todo_edit_action_submit" tabindex="<%=next_tab_index%>">
|
||||
<%=image_tag("accept.png", :alt => "") %>
|
||||
<%= t('common.update') %>
|
||||
</button>
|
||||
<button type="button" class="positive" id="recurring_todo_edit_action_cancel" tabindex="<%=next_tab_index%>">
|
||||
<%=image_tag("cancel.png", :alt => "") %>
|
||||
<%= t('common.cancel') %>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<% @recurring_todo = recurring_todo -%>
|
||||
<div id="<%= dom_id(@recurring_todo)%>" class="recurring_todo item-container">
|
||||
<%= recurring_todo_remote_delete_icon %> <%= recurring_todo_remote_edit_icon -%>
|
||||
|
||||
<%= recurring_todo_remote_star_icon %> <%= recurring_todo_remote_toggle_checkbox -%>
|
||||
<%= recurring_todo_remote_delete_icon %>
|
||||
<%= recurring_todo_remote_edit_icon -%>
|
||||
<%= recurring_todo_remote_star_icon %>
|
||||
<%= recurring_todo_remote_toggle_checkbox -%>
|
||||
<div class="rec_description">
|
||||
<span class="todo.descr"><%= sanitize(recurring_todo.description) %></span> <%= recurring_todo_tag_list %>
|
||||
<span class='recurrence_pattern'>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<%- reset_tab_index %>
|
||||
<div class="recurring_container">
|
||||
<%
|
||||
<%
|
||||
form_for(@new_recurring_todo, :html=> { :id=>'recurring-todo-form-new-action', :name=>'recurring_todo', :class => 'inline-form' }) do
|
||||
-%>
|
||||
<div id="error_status"><%= error_messages_for("item", :object_name => 'action') %></div>
|
||||
|
||||
|
||||
<div id="recurring_todo_form_container">
|
||||
<div id="recurring_todo">
|
||||
<label for="recurring_todo_description"><%= Todo.human_attribute_name('description') %></label><%=
|
||||
|
|
@ -14,12 +14,12 @@
|
|||
<label for="recurring_todo_project_name"><%= Todo.human_attribute_name('project') %></label>
|
||||
<input id="recurring_todo_project_name" name="project_name" autocomplete="off" tabindex="<%=next_tab_index%>" size="30" type="text" value="" />
|
||||
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
|
||||
|
||||
|
||||
<label for="recurring_todo_context_name"><%= Todo.human_attribute_name('context') %></label>
|
||||
<input id="recurring_todo_context_name" name="context_name" autocomplete="off" tabindex="<%=next_tab_index%>" size="30" type="text" value="<%= current_user.contexts.first.name unless current_user.contexts.first.nil?%>" />
|
||||
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
|
||||
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
|
||||
<label for="tag_list"><%= "#{Todo.human_attribute_name('tags')} #{t('shared.separate_tags_with_commas')}"%></label>
|
||||
<%= text_field_tag "tag_list", nil, :size => 30, :tabindex => next_tab_index -%>
|
||||
<%= text_field_tag "tag_list", nil, :size => 30, :tabindex => next_tab_index -%>
|
||||
</div>
|
||||
</div>
|
||||
<div id="recurring_period_id">
|
||||
|
|
@ -88,17 +88,6 @@
|
|||
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.from_tickler') %><br/>
|
||||
<br/>
|
||||
</div>
|
||||
<div class="recurring_submit_box">
|
||||
<div class="widgets">
|
||||
<button type="submit" class="positive" id="recurring_todo_new_action_submit" tabindex="<%=next_tab_index%>">
|
||||
<%=image_tag("accept.png", :alt => "") %>
|
||||
<%= t('common.create') %>
|
||||
</button>
|
||||
<button type="button" class="positive" id="recurring_todo_new_action_cancel" tabindex="<%=next_tab_index%>">
|
||||
<%=image_tag("cancel.png", :alt => "") %>
|
||||
<%= t('common.cancel') %>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<% if @saved -%>
|
||||
TracksPages.page_notify('notice', "<%=@status_message%>", 5);
|
||||
RecurringTodosPage.toggle_overlay();
|
||||
add_recurring_todo_to_active_container();
|
||||
replace_form_with_empty_form();
|
||||
$( "#new-recurring-todo" ).dialog( "close" );
|
||||
TracksPages.set_page_badge(<%= @down_count %>);
|
||||
<% else -%>
|
||||
TracksPages.show_errors(html_for_error_messages());
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
$('#new-recurring-todo').hide();
|
||||
$('#edit-recurring-todo').html(html_for_edit_form());
|
||||
$('#edit-recurring-todo').show();
|
||||
RecurringTodosPage.toggle_overlay();
|
||||
enable_rich_interaction();
|
||||
$('#edit-recurring-todo').dialog( "open" );
|
||||
|
||||
function html_for_edit_form() {
|
||||
return "<%= escape_javascript(render(:partial => 'edit_form')) %>"
|
||||
return "<%= escape_javascript(render(:partial => 'edit_form')) %>";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<%= render :partial => @recurring_todos %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container" id="completed_recurring_todos_container">
|
||||
<div class=add_note_link><%= link_to "Show all", done_recurring_todos_path%></div>
|
||||
<h2><%= t('common.last') %> <%= t('todos.completed_recurring') %></h2>
|
||||
|
|
@ -26,12 +26,9 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="overlay">
|
||||
<div id="new-recurring-todo" class="new-form">
|
||||
<label><%= t('todos.add_new_recurring') %></label><br/>
|
||||
<%= render :partial => "recurring_todo_form" %>
|
||||
</div>
|
||||
<div id="edit-recurring-todo" class="edit-form" style="display:none">
|
||||
<div class='placeholder'>This should not be visible</div>
|
||||
</div>
|
||||
<div id="new-recurring-todo" class="new-form" title="<%= t('todos.add_new_recurring') %>">
|
||||
<%= render :partial => "recurring_todo_form" %>
|
||||
</div>
|
||||
<div id="edit-recurring-todo" class="edit-form" style="display:none" title="<%= t('todos.edit_recurring_todo') %>">
|
||||
<div class='placeholder'>This should not be visible</div>
|
||||
</div>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<%- if @saved -%>
|
||||
RecurringTodosPage.toggle_overlay();
|
||||
$( "#edit-recurring-todo" ).dialog( "close" );
|
||||
TracksPages.page_notify('notice', text_for_status_message(), 5);
|
||||
replace_old_recurring_todo_with_updated();
|
||||
<%- else -%>
|
||||
|
|
|
|||
|
|
@ -4,37 +4,34 @@
|
|||
<%= error_messages_for("todo") %>
|
||||
</span>
|
||||
<% this_year = current_user.time.to_date.strftime("%Y").to_i -%>
|
||||
<% if parent_container_type == 'show_mobile' -%>
|
||||
<p><label for="todo_done"><%= t('todos.done') %></label> <%= check_box_tag("done", 1, @todo && @todo.completed?, "tabindex" => 1, "onClick" => "document.mobileEdit.submit()") %></p>
|
||||
<% end -%>
|
||||
<h2><label for="todo_description"><%= t('common.description') %></label></h2>
|
||||
<%= text_field( "todo", "description", "tabindex" => 2, "maxlength" => 100, "size" => 50) %>
|
||||
<h2><label for="todo_notes"><%= t('common.notes') %></label></h2>
|
||||
<%= text_area( "todo", "notes", "cols" => 40, "rows" => 3, "tabindex" => 3) %>
|
||||
<%= text_field( "todo", "description", "tabindex" => 1, "maxlength" => 100, "size" => 50) %>
|
||||
<h2><label for="tag_list"><%= t('todos.tags') %></label></h2>
|
||||
<%= text_field_tag "tag_list", @tag_list_text, :size => 50, :tabindex => 2 %>
|
||||
<h2><label for="todo_context_id"><%= t('common.context') %></label></h2>
|
||||
<%= unless @mobile_from_context
|
||||
collection_select( "todo", "context_id", @contexts, "id", "name", {}, {"tabindex" => 4} )
|
||||
collection_select( "todo", "context_id", @contexts, "id", "name", {}, {"tabindex" => 3} )
|
||||
else
|
||||
select_tag("todo[context_id]", options_from_collection_for_select(
|
||||
@contexts, "id", "name", @mobile_from_context.id),
|
||||
{"id" => :todo_context_id, :tabindex => 4} )
|
||||
{"id" => :todo_context_id, :tabindex => 3} )
|
||||
end %>
|
||||
<h2><label for="todo_project_id"><%= t('common.project') %></label></h2>
|
||||
<%= unless @mobile_from_project
|
||||
collection_select( "todo", "project_id", @projects, "id", "name",
|
||||
{:include_blank => t('todos.no_project')}, {"tabindex" => 5} )
|
||||
{:include_blank => t('todos.no_project')}, {"tabindex" => 4} )
|
||||
else
|
||||
# manually add blank option since :include_blank does not work
|
||||
# with options_from_collection_for_select
|
||||
select_tag("todo[project_id]", "<option value=\"\"></option>"+options_from_collection_for_select(
|
||||
@projects, "id", "name", @mobile_from_project.id),
|
||||
{"id" => :todo_project_id, :tabindex => 5} )
|
||||
{"id" => :todo_project_id, :tabindex => 4} )
|
||||
end %>
|
||||
<h2><label for="tag_list"><%= t('todos.tags') %></label></h2>
|
||||
<%= text_field_tag "tag_list", @tag_list_text, :size => 50, :tabindex => 6 %>
|
||||
<h2><label for="todo_notes"><%= t('common.notes') %></label></h2>
|
||||
<%= text_area( "todo", "notes", "cols" => 40, "rows" => 3, "tabindex" => 5) %>
|
||||
<h2><label for="todo_due"><%= t('todos.due') %></label></h2>
|
||||
<%= date_select("todo", "due", {:order => [:day, :month, :year],
|
||||
:start_year => this_year, :include_blank => '--'}, :tabindex => 7) %>
|
||||
:start_year => this_year, :include_blank => '--'}, :tabindex => 6) %>
|
||||
<h2><label for="todo_show_from"><%= t('todos.show_from') %></label></h2>
|
||||
<%= date_select("todo", "show_from", {:order => [:day, :month, :year],
|
||||
:start_year => this_year, :include_blank => true}, :tabindex => 8) %>
|
||||
:start_year => this_year, :include_blank => true}, :tabindex => 7) %>
|
||||
|
|
|
|||
|
|
@ -2,4 +2,12 @@
|
|||
<p><%= t('todos.no_incomplete_actions') %></p>
|
||||
<% else -%>
|
||||
<%= render :partial => "contexts/mobile_context", :collection => @contexts_to_show -%>
|
||||
<% end -%>
|
||||
<% end -%>
|
||||
<% unless @done.nil? -%>
|
||||
<div id="completed_container">
|
||||
<h2><%= t('todos.completed_actions') %></h2>
|
||||
<ul class="c">
|
||||
<%= render :partial => "todos/mobile_todo", :collection => @done %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,13 @@
|
|||
<% @todo = mobile_todo
|
||||
if mobile_todo.starred?
|
||||
bullet = "<span class=\"star\">"+image_tag("menustar_small.gif")+"</span>"
|
||||
li_class = " class=\"star\""
|
||||
else
|
||||
bullet = "<span class=\"r\">» </span>"
|
||||
li_class = ""
|
||||
end -%>
|
||||
<li id="<%= dom_id(mobile_todo) %>" <%= li_class %>><%= bullet %><%
|
||||
if mobile_todo.completed?
|
||||
-%><span class="m_t_d">
|
||||
<% else
|
||||
-%><span class="m_t">
|
||||
<% end -%>
|
||||
<% @todo = mobile_todo -%>
|
||||
<li id="<%= dom_id(mobile_todo) %>" >
|
||||
<% remote_mobile_checkbox(mobile_todo) %>
|
||||
<%= date_span -%> <%= link_to mobile_todo.description, todo_path(mobile_todo, :format => 'm') -%>
|
||||
<% unless mobile_todo.notes.blank? %>
|
||||
<%= link_to(image_tag("mobile_notes.png", :border => "0"), mobile_todo_show_notes_path(mobile_todo, :format => 'm')) -%>
|
||||
<% end %>
|
||||
<% if mobile_todo.starred? %>
|
||||
<%= image_tag("menustar_small.gif", :border => "0") -%>
|
||||
<% end %>
|
||||
<% if parent_container_type == 'context' or parent_container_type == 'tag' -%>
|
||||
<%= "<span class=\"prj\"> (" +
|
||||
link_to(mobile_todo.project.name, project_path(mobile_todo.project, :format => 'm')) +
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
<%= link_to @todo.description, todo_path(@todo, :format => 'm') -%>
|
||||
<h2><%= t('todos.notes') %></h2>
|
||||
<%= format_note(@todo.notes) %>
|
||||
<%= link_to t('common.back'), @return_path %>
|
||||
<%= link_to t('common.back'), @return_path %>
|
||||
|
|
|
|||
|
|
@ -8,18 +8,18 @@
|
|||
<%= render :partial => "contexts/mobile_context", :collection => @contexts_to_show -%>
|
||||
<h2><%= t('todos.deferred_actions_with', :tag_name=> @tag_title) %></h2>
|
||||
<% unless (@deferred.nil? or @deferred.size == 0) -%>
|
||||
<table cellpadding="0" cellspacing="0" border="0">
|
||||
<ul class="c">
|
||||
<%= render :partial => "todos/mobile_todo", :collection => @deferred, :locals => { :parent_container_type => "tag" } -%>
|
||||
</table>
|
||||
</ul>
|
||||
<% else -%>
|
||||
<%= t('todos.no_deferred_actions_with', :tag_name => @tag_title) %>
|
||||
<% end -%>
|
||||
<h2><%= t('todos.completed_actions_with', :tag_name => @tag_title) %></h2>
|
||||
<% unless (@done.nil? or @done.size == 0) -%>
|
||||
<table cellpadding="0" cellspacing="0" border="0">
|
||||
<ul class="c">
|
||||
<%= render :partial => "todos/mobile_todo", :collection => @done, :locals => { :parent_container_type => "tag" } %>
|
||||
</table>
|
||||
</ul>
|
||||
<% else -%>
|
||||
<%= t('todos.no_completed_actions_with', :tag_name => @tag_title) %>
|
||||
<% end -%>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
945
config/locales/cz.yml
Executable file
945
config/locales/cz.yml
Executable file
|
|
@ -0,0 +1,945 @@
|
|||
---
|
||||
cz:
|
||||
layouts:
|
||||
toggle_notes: Zobrazit/skrýt poznámky
|
||||
toggle_contexts: "Přepnout sbalené kontexty"
|
||||
toggle_contexts_title: "Zobrazí/skryje sbalené kontexty"
|
||||
next_actions_rss_feed: RSS feed aktuálních úkolů
|
||||
toggle_notes_title: Zobrazí/skryje všechny poznámky
|
||||
mobile_navigation:
|
||||
new_action: 0-Nový úkol
|
||||
logout: Odhlásit
|
||||
feeds: Feedy
|
||||
starred: 4-Hvězdičky
|
||||
projects: 3-Projekty
|
||||
tickler: Tickler
|
||||
contexts: 2-Kontexty
|
||||
home: 1-Domů
|
||||
navigation:
|
||||
manage_users_title: Přidat nebo smazat uživatele
|
||||
recurring_todos: Opakující se úkoly
|
||||
api_docs: REST API Dokumenty
|
||||
feeds: Feedy
|
||||
starred: S hvězdou
|
||||
notes_title: Zobrazit všechny poznámky
|
||||
review_title: Provést revizi
|
||||
stats: Statistiky
|
||||
tickler_title: Tickler
|
||||
manage_users: Správa uživatelů
|
||||
export_title: Import a export dat
|
||||
preferences: Nastavení
|
||||
integrations_: Integrovat Tracks
|
||||
feeds_title: Seznam dostupných feedů
|
||||
calendar_title: Kalendář datovaných úkolů
|
||||
completed_tasks: Hotové
|
||||
stats_title: Zobrazí statistiky úkolů
|
||||
tickler: Tickler
|
||||
home_title: Domů
|
||||
starred_title: Zobrazí úkoly s hvězdičkou
|
||||
recurring_todos_title: Správa opakovaných úkolů
|
||||
view: Ukázat
|
||||
organize: Správa
|
||||
completed_tasks_title: Hotové úkoly
|
||||
home: Domů
|
||||
export: Export
|
||||
contexts_title: Kontexty
|
||||
calendar: Kalendář
|
||||
projects_title: Projekty
|
||||
search: Hledat
|
||||
preferences_title: Zobrazí možnosti nastavení
|
||||
integrations:
|
||||
opensearch_description: Prohledat Tracks
|
||||
applescript_next_action_prompt: "Popis úkolu:"
|
||||
gmail_description: Gadget pro Tracks do Gmailu
|
||||
applescript_success_after_id: vytvořen
|
||||
applescript_success_before_id: Nový úkol s ID
|
||||
number:
|
||||
format:
|
||||
separator: .
|
||||
precision: 3
|
||||
delimiter: ","
|
||||
human:
|
||||
format:
|
||||
precision: 1
|
||||
delimiter: ""
|
||||
storage_units:
|
||||
format: "%n %u"
|
||||
units:
|
||||
kb: KB
|
||||
tb: TB
|
||||
gb: GB
|
||||
byte:
|
||||
one: Byte
|
||||
other: Bytů
|
||||
mb: MB
|
||||
percentage:
|
||||
format:
|
||||
delimiter: ""
|
||||
currency:
|
||||
format:
|
||||
format: "%u%n"
|
||||
unit: $
|
||||
separator: .
|
||||
precision: 2
|
||||
delimiter: ","
|
||||
precision:
|
||||
format:
|
||||
delimiter: ""
|
||||
common:
|
||||
back: Zpět
|
||||
third: Třetí
|
||||
recurring_todos: Opakované úkoly
|
||||
actions: Úkoly
|
||||
add: Přidat
|
||||
previous: Předchozí
|
||||
logout: Odhlásit
|
||||
go_back: Zpět
|
||||
optional: volitelné
|
||||
week: týden
|
||||
cancel: Zrušit
|
||||
none: Žádný
|
||||
second: Druhý
|
||||
month: měsíc
|
||||
server_error: Nastala chyba na serveru.
|
||||
forum: Fórum
|
||||
notes: Poznámky
|
||||
review: Revize
|
||||
last: Poslední
|
||||
projects: Projekty
|
||||
action: Úkol
|
||||
project: Projekt
|
||||
ok: Ok
|
||||
contribute: Přispět
|
||||
website: Webová stránka
|
||||
first: První
|
||||
numbered_step: Krok %{number}
|
||||
sort:
|
||||
by_task_count_title: Řadit podle počtu úkolů
|
||||
by_task_count_title_confirm: Určitě chcete řadit tyto projekty podle počtu úkolů? Stávající pořadí bude ztraceno.
|
||||
alphabetically: Abecedně
|
||||
alphabetically_confirm: Určitě chcete řadit tyto projekty abecedně? Stávající pořadí bude ztraceno.
|
||||
alphabetically_title: Seřadit projekty abecedně
|
||||
sort: Řadit
|
||||
by_task_count: Podle počtu úkolů
|
||||
fourth: Čtvrtý
|
||||
create: Vytvořit
|
||||
months: měsíce
|
||||
contexts: Kontexty
|
||||
errors_with_fields: "Nastaly potíže s následujícími políčky:"
|
||||
next: Další
|
||||
todo: úkol
|
||||
context: Kontext
|
||||
drag_handle: CHYŤ MĚ
|
||||
description: Popis
|
||||
bugs: Chyby
|
||||
update: Uložit
|
||||
forth: Čtvrtý
|
||||
weeks: týdny
|
||||
wiki: Wiki
|
||||
email: Email
|
||||
search: Hledat
|
||||
ajaxError: Chyba čtení ze serveru
|
||||
data:
|
||||
import_successful: Import byl úspěšný.
|
||||
import_errors: Při importu došlo k chybám
|
||||
models:
|
||||
project:
|
||||
feed_title: Projekty
|
||||
feed_description: Všechny projekty uživatele %{username}
|
||||
todo:
|
||||
error_date_must_be_future: datum musí být v budoucnosti
|
||||
user:
|
||||
error_context_not_associated: Kontext %{context} nepatří uživateli %{user}.
|
||||
error_project_not_associated: Projekt %{project} nepatří uživateli %{user}.
|
||||
preference:
|
||||
due_on: Plánováno na %{date}
|
||||
due_in: Plánováno za %{days} dní
|
||||
due_styles:
|
||||
- Plánováno za ___ dní
|
||||
- Plánováno na _______
|
||||
activerecord:
|
||||
attributes:
|
||||
project:
|
||||
name: Název
|
||||
default_tags: Výchozí štítky
|
||||
default_context_name: Výchozí kontext
|
||||
description: Popis
|
||||
todo:
|
||||
predecessors: Závisí na
|
||||
show_from: Zobrazovat od
|
||||
notes: Poznámky
|
||||
project: Projekt
|
||||
context: Kontext
|
||||
description: Popis
|
||||
due: Plánováno na
|
||||
user:
|
||||
last_name: Jméno
|
||||
first_name: Příjmení
|
||||
preference:
|
||||
show_hidden_projects_in_sidebar: Zobrazovat skryté projekty v sidebaru
|
||||
date_format: Formát data
|
||||
show_hidden_contexts_in_sidebar: Zobrazovat skryté kontexty v sidebaru
|
||||
mobile_todos_per_page: Úkolů na stránku (mobilní zobrazení)
|
||||
verbose_action_descriptors: Ukecané popisovače úkolů
|
||||
staleness_starts: Jako prošlé označit projekty starší než
|
||||
review_period: Interval revize projektů
|
||||
sms_context: Výchozí emailový kontext
|
||||
title_date_format: Formát data nadpisu
|
||||
show_number_completed: Počet hotových úkolů k zobrazení
|
||||
refresh: Interval obnovení stránky (v minutách)
|
||||
week_starts: Začátek týdne
|
||||
last_name: Příjmení
|
||||
locale: Lokále
|
||||
due_style: Zobrazení datovaných úkolů
|
||||
time_zone: Časové pásmo
|
||||
show_project_on_todo_done: Po splnění úkolu přejít na projekt
|
||||
sms_email: SMS email
|
||||
first_name: Jméno
|
||||
show_completed_projects_in_sidebar: Zobrazovat hotové projekty v sidebaru
|
||||
errors:
|
||||
messages:
|
||||
greater_than_or_equal_to: musí být větší nebo rovno %{count}
|
||||
record_invalid: "Problém s daty: %{errors}"
|
||||
confirmation: se neshoduje s ověřením
|
||||
less_than_or_equal_to: musí být menší nebo rovno %{count}
|
||||
blank: nesmí být prázdné
|
||||
invalid: nesmí obsahovat čárku (',')
|
||||
exclusion: je rezervované
|
||||
odd: must be odd
|
||||
even: must be even
|
||||
too_short: je příliš krátké (minimum je %{count} znaků)
|
||||
empty: nesmí být prázdné
|
||||
wrong_length: nemá správnou délku (má mít %{count} znaků)
|
||||
less_than: musí být menší než %{count}
|
||||
greater_than: musí být větší než %{count}
|
||||
equal_to: se musí rovnat %{count}
|
||||
accepted: musí být akceptováno
|
||||
too_long: je příliš dlouhé (maximum je %{count} znaků)
|
||||
taken: už bylo zabráno
|
||||
inclusion: není na seznamu
|
||||
not_a_number: není číslo
|
||||
models:
|
||||
project:
|
||||
attributes:
|
||||
name:
|
||||
blank: projekt musí mít název
|
||||
too_long: název projektu musí být kratší než 256 znaků
|
||||
taken: už existuje
|
||||
full_messages:
|
||||
format: "%{attribute} %{message}"
|
||||
template:
|
||||
body: "Nastaly potíže s následujícími políčky:"
|
||||
header:
|
||||
one: jedna chyba brání uložení tohoto objektu %{model}
|
||||
other: "%{count} chyb brání uložení tohoto objektu %{model}"
|
||||
stats:
|
||||
tag_cloud_title: Mrak štítků pro všechny úkly
|
||||
tag_cloud_description: Tento mrak zahrnuje štítky všech úkolů (hotových, nehotových, viditelných i skrytých)
|
||||
tag_cloud_90days_title: Značky úkolů z posledních 90-ti dní
|
||||
actions: Úkoly
|
||||
totals_active_project_count: Znich %{count} je aktivních projeků
|
||||
actions_last_year_legend:
|
||||
number_of_actions: Počet úklolů
|
||||
months_ago: měsíců zpět
|
||||
totals_first_action: Od vašeho prvního úkolu %{date}
|
||||
actions_avg_completion_time: Pro všechny vaše hotové úkoly je průměrný čas dokončení %{count} dní.
|
||||
top10_longrunning: 10 nejdéle běžících projektů
|
||||
actions_dow_30days_title: Dny v týdnu (posleních 30 dní)
|
||||
legend:
|
||||
actions: Úkoly
|
||||
number_of_days: Před kolika dny
|
||||
number_of_actions: Počet úkolů
|
||||
day_of_week: Den v týdnu
|
||||
percentage: Podíl
|
||||
running_time: Čas k dokončení úkolu (týdny)
|
||||
months_ago: měsíců zpět
|
||||
current_running_time_of_incomplete_visible_actions: Aktuální čas běhu nedokončených viditelných úkolů
|
||||
totals_deferred_actions: z nichž %{count} jsou odložené úkoly v Ticlkeru
|
||||
running_time_legend:
|
||||
actions: Úkoly
|
||||
percentage: Podíl
|
||||
weeks: Čas běhu úkolu (týdny). Klepněte na sloupec pro další info
|
||||
totals_action_count: máte celkem %{count} úkolů
|
||||
totals_incomplete_actions: Máte %{count} nehotových úkolů
|
||||
totals_unique_tags: Z těchto štítků je %{count} unikátních.
|
||||
actions_avg_completed_30days: a dokončeno průměrně %{count} úkolů za den.
|
||||
top5_contexts: Top 5 kontextů
|
||||
actions_lastyear_title: Úkoly za posledních 12 měsíců
|
||||
totals_actions_completed: "%{count} z nich je hotových."
|
||||
totals_context_count: Máte %{count} kontextů.
|
||||
totals_visible_context_count: Z nich je %{count} viditelných kontextů
|
||||
totals_blocked_actions: "%{count} je závislých na dokončení jiných akcí."
|
||||
projects: Projekty
|
||||
action_completion_time_title: Čas dokončení (všechny hotové úkoly)
|
||||
actions_last_year: Úkoly v posledním roce
|
||||
actions_min_max_completion_days: Maximum/minimum dní na dokončení je %{min}/%{max}.
|
||||
tags: Štítky
|
||||
actions_min_completion_time: Minimální čas k dokončení je %{time}.
|
||||
no_tags_available: žádné štítky nejsou definovány
|
||||
actions_day_of_week_title: Den v týdnu (všechny úkoly)
|
||||
totals_project_count: Máte %{count} projektů.
|
||||
running_time_all: Aktuální čas běhu všech nehotových úkolů
|
||||
actions_30days_title: Úkoly za posledních 30 dní
|
||||
time_of_day: Denní doba (všechny úkoly)
|
||||
totals_hidden_project_count: "%{count} je skrytých"
|
||||
tod30: Denní doba (posledních 30 dní)
|
||||
tag_cloud_90days_description: Tento mrak zahrnuje štítky úkolů, které byly vytvořeny nebo dokončeny v posledních 90-ti dnech.
|
||||
more_stats_will_appear: Další statistiky se zobrazí až přibyde více úkolů.
|
||||
top5_visible_contexts_with_incomplete_actions: Top 5 viditelných kontextů s nehotovými úkoly
|
||||
actions_further: " a dále"
|
||||
totals_tag_count: Na akcích je umístěno %{count} štítků.
|
||||
top10_projects_30days: Top 10 projektů za posledních 30 dní
|
||||
spread_of_running_actions_for_visible_contexts: Distribuce běžících úkolů do viditelných kontextů
|
||||
actions_selected_from_week: "Úkoly vybrané z týdne "
|
||||
spread_of_actions_for_all_context: Distribuce všech úkolů do kontextů
|
||||
click_to_show_actions_from_week: Klepněte %{link} pro zobrazení úkolů z týdne %{week} a dalších.
|
||||
other_actions_label: (ostatní)
|
||||
top10_projects: Top 10 projektů
|
||||
totals_completed_project_count: a %{count} je hotových projektů.
|
||||
actions_avg_created: Za posledních 12 měsíců bylo vytvořeno průměrně %{count} úkolů
|
||||
click_to_return: Klepněte %{link} pro návrat ke statistikám.
|
||||
actions_avg_completed: a uzavřeno průměrně %{count} úkolů za měsíc.
|
||||
totals: Celkem
|
||||
time_of_day_legend:
|
||||
number_of_actions: Počet úkolů
|
||||
time_of_day: Denní doba
|
||||
contexts: Kontexty
|
||||
click_to_return_link: zde
|
||||
totals_hidden_context_count: a %{count} skrytých kontextů.
|
||||
labels:
|
||||
month_avg_completed: "%{months} měsíční průměr hotových"
|
||||
completed: Completed
|
||||
month_avg_created: "%{months} měsíční průměr vytvořených"
|
||||
avg_created: průměrně vytvořeno
|
||||
avg_completed: průměrně uzavřeno
|
||||
created: Vytvořeno
|
||||
running_time_all_legend:
|
||||
actions: Úkoly
|
||||
percentage: Podíl
|
||||
running_time: Čas běhu úkolů (týdny). Klepněte na sloupec pro další info
|
||||
click_to_update_actions: Klepněte na sloupec v grafu pro zobrazení detailů níže.
|
||||
no_actions_selected: Nejsou vybrány žádné úkoly.
|
||||
actions_actions_avg_created_30days: Za posledních 30 dní bylo vytvořeno průměrně %{count} úkolů
|
||||
tod30_legend:
|
||||
number_of_actions: Počet úkolů
|
||||
time_of_day: Denní doba
|
||||
action_selection_title: TRACKS::výběr úkolů
|
||||
todos:
|
||||
show_from: Zobrazovat od
|
||||
error_starring_recurring: Nebylo možno ohvězdičkovat opakovaný úkol \'%{description}\'
|
||||
recurring_action_deleted: Úkol byl smazán. Protože jde o opakovaný úkol, byl vložen nový úkol
|
||||
completed_actions: Hotové úkoly
|
||||
completed_recurring: Hotové opakované úkoly
|
||||
added_new_next_action: Přidán nový úkol
|
||||
completed_rest_of_previous_month: Uzavřeno ve zbytku minulého měsíce
|
||||
blocked_by: Čeká na %{predecessors}
|
||||
star_action: Oznařit hvězdičkou
|
||||
completed_recurrence_completed: Bylo smazáno poslední opakování opakovaného úkolu. Opakování dokončeno
|
||||
defer_date_after_due_date: Datum zobrazení je až po plánovaném datu úkolu. Upravte datum úkolu před dalším pokusem o odpložení zobrazení.
|
||||
unable_to_add_dependency: Nepodařilo se přidat závislost
|
||||
done: Hotovo?
|
||||
star_action_with_description: ohvězdičkovat úkol '%{description}'
|
||||
tagged_with: označeno štítkem ‘%{tag_name}’
|
||||
completed: Hotovo
|
||||
no_deferred_actions_with: Žádné odložené úkoly se štítkem '%{tag_name}'
|
||||
edit_action_with_description: Upravit úkol '%{description}'
|
||||
no_hidden_actions: Žádné skryté úkoly
|
||||
action_due_on: (úkol plánován na %{date})
|
||||
remove_dependency: Odstranit závislost (nesmaže úkol)
|
||||
archived_tasks_title: TRACKS::Archiv hotových úkolů
|
||||
list_incomplete_next_actions: Zabrazí nehotové úkoly
|
||||
tags: Štítky (oddělené čárkami)
|
||||
action_deleted_success: Úkol byl úspěšně smazán
|
||||
new_related_todo_created: Byl vytvořen nový úkol patřící do tohoto opakovaného úkolu
|
||||
context_changed: Kontext byl změněn na %{name}
|
||||
add_another_dependency: Přidat další závislost
|
||||
mobile_todos_page_title: Všechny úkoly
|
||||
delete_recurring_action_title: Smazat opakovaný úkol
|
||||
removed_predecessor: Byl odstraněn %{successor} jako závislost pro %{predecessor}.
|
||||
recurring_actions_title: TRACKS::Opakované úkoly
|
||||
next_action_needed: Je potřeba zadat aspoň jeden úkol
|
||||
action_saved: Úkol uložen
|
||||
scheduled_overdue: Naplánováno zobrazení před %{days} dny
|
||||
action_deleted_error: Nepodařilo se smazat úkol
|
||||
edit_action: Upravit úkol
|
||||
added_new_context: Přidán nový kontext
|
||||
next_actions_description: "Filtr:"
|
||||
list_incomplete_next_actions_with_limit: Zobrazuje posledních %{count} nedokončených úkolů
|
||||
set_to_pending: "%{task} nastaven jako čekající"
|
||||
added_new_project: Přidán nový projekt
|
||||
next_actions_title_additions:
|
||||
completed: hotové úkoly
|
||||
due_today: dnes
|
||||
due_within_a_week: během týdne
|
||||
older_completed_items: ""
|
||||
append_in_this_project: v tomto projektu
|
||||
error_deleting_item: Nepodařilo se smazat položku %{description}
|
||||
task_list_title: TRACKS::Úkoly
|
||||
no_actions_due_this_week: Žádné úkoly plánovány na tento týden
|
||||
no_deferred_pending_actions: Žádné odložené ani čekající úkoly
|
||||
no_recurring_todos: Žádné opakované úkoly
|
||||
error_completing_todo: Nepodařilo se ukončit / aktivovat opakovaný úkol %{description}
|
||||
recurring_pattern_removed: Vzor opakování byl odstraněn z %{count}
|
||||
convert_to_project: Vytvořit projekt
|
||||
delete_recurring_action_confirm: Opravdu chcete smazat opakovaný úkol '%{description}'?
|
||||
completed_last_day: Ukončené v posledních 24 hodinách
|
||||
completed_more_than_x_days_ago: ""
|
||||
show_in_days: Zobrazit za %{days} dní
|
||||
no_project: --Žádný projekt--
|
||||
error_saving_recurring: Nepodařilo se uložit opakovaný úkol \'%{description}\'
|
||||
new_related_todo_created_short: vytvořen nový úkol
|
||||
all_completed: Všechny hotové úkoly
|
||||
feed_title_in_context: v kontextu '%{context}'
|
||||
older_than_days: ""
|
||||
completed_tagged_page_title: TRACKS::Hotové úkoly se štítkem '%{tag_name}'
|
||||
edit: Upravit
|
||||
pending: Čekající
|
||||
completed_actions_with: Hotové úkoly se štítkem '%{tag_name}'
|
||||
deleted_success: Úkol byl úspěšně smazán.
|
||||
completed_tasks_title: TRACKS::Hotové úkoly
|
||||
feed_title_in_project: v projektu '%{project}'
|
||||
clear_due_date: Smazat plánované datum úkolu
|
||||
hidden_actions: Skryté úkoly
|
||||
error_removing_dependency: Nepodařilo se odstranit závislost
|
||||
was_due_on_date: bylo plánováno na %{date}
|
||||
show_on_date: Ukázat %{date}
|
||||
recurrence_period: Interval opakování
|
||||
deferred_actions_with: Odložené úkoly se štítkem '%{tag_name}'
|
||||
recurring_deleted_success: Opakovaný úkol byl úspěšně smazán.
|
||||
confirm_delete: Opravdu chcete smazat úkol '%{description}'?
|
||||
deferred_tasks_title: TRACKS::Tickler
|
||||
next_actions_title: Tracks - Úkoly
|
||||
next_action_description: Popis úkolu
|
||||
no_completed_actions_with: Žádné hotové úkoly se štítkem '%{tag_name}'
|
||||
clear_show_from_date: Odstranit datum zobrazení
|
||||
calendar_page_title: TRACKS::Kalendář
|
||||
unresolved_dependency: Hodnota v poli 'závisí na' neodpovídá žádnému existujícímu úkolu. Hodnota bude ignorována. Pokračovat?
|
||||
in_hidden_state: (skrytý)
|
||||
show_today: Zobrazit Dnes
|
||||
no_actions_found_title: Nenalezeny žádné úkoly
|
||||
next_actions_due_date:
|
||||
overdue_by: Prošlé %{days} den
|
||||
due_today: Dnes
|
||||
due_in_x_days: Za %{days} dní
|
||||
overdue_by_plural: Prošlé %{days} dní
|
||||
due_tomorrow: Zítra
|
||||
completed_last_x_days: Uzavřené za posledních %{count} dní
|
||||
no_actions_with: Žádné úkoly se štítkem '%{tag_name}'
|
||||
defer_x_days:
|
||||
one: Ukázat zítra
|
||||
other: Ukázat za %{count} dní
|
||||
added_new_next_action_singular: Přidán nový úkol
|
||||
no_completed_actions: Žádné hotové úkoly.
|
||||
feeds:
|
||||
completed: "Hotové: %{date}"
|
||||
due: "Plánováno na: %{date}"
|
||||
deferred_pending_actions: Odložené/čekající úkoly
|
||||
has_x_pending:
|
||||
one: Jeden čekající úkol
|
||||
other: %{count} čekajících úkolů
|
||||
delete_action: Smazat úkol
|
||||
error_deleting_recurring: Nepodařilo se smazat opakovaný úkol \'%{description}\'
|
||||
recurring_todos: Opakované úkoly
|
||||
delete: Smazat
|
||||
cannot_add_dependency_to_completed_todo: Nelze přidat úkol jako závislost k hotovému úkolu!
|
||||
drag_action_title: Přetáhnout na jiný úkol pro vytvoření závislosti
|
||||
no_last_completed_actions: Žádné hotové úkoly
|
||||
depends_on: Závisí na
|
||||
tickler_items_due:
|
||||
one: Jeden úkol v Tickleru je plánován dnes - obnovte stránku pro zobrazení.
|
||||
other: "%{count} položek v Tickleru je plánováno na dnes tickler items are now due - obnovte stránku pro zobrazení."
|
||||
action_marked_complete: Úkol <strong>'%{description}'</strong> byl označen jako <strong>%{completed}</strong>
|
||||
completed_today: Dokončené dnes
|
||||
added_new_next_action_plural: Úkoly byly přidány
|
||||
new_related_todo_not_created_short: úkol nebyl vytvořen
|
||||
completed_rest_of_week: Dokončené ve zbytku týdne
|
||||
error_starring: Nepodařilo se označit úkol hvězdičkou '%{description}'
|
||||
calendar:
|
||||
get_in_ical_format: Kalendář ve formátu iCal
|
||||
due_next_week: Plánované příští týden
|
||||
no_actions_due_next_week: Na příští týden nejsou plánované žádné úkoly
|
||||
due_this_week: Plánované ve zbytku týdne
|
||||
due_today: Plánované dnes
|
||||
no_actions_due_today: Na dnešek nejsou plánované žádné úkoly
|
||||
due_next_month_and_later: Plánováno na %{month} a dále
|
||||
no_actions_due_after_this_month: Na příští měsíc a dále nejsou plánované žádné úkoly
|
||||
due_this_month: Plánováno na %{month}
|
||||
no_actions_due_this_month: Na zbytek tohoto měsíce nejsou žádné úkoly
|
||||
show_tomorrow: Zobrazit zítra
|
||||
tagged_page_title: TRACKS::Se štítkem '%{tag_name}'
|
||||
action_deferred: Úkol '%{description}' byl oldožen
|
||||
recurrence:
|
||||
ends_on_number_times: Končí po %{number} opakováních
|
||||
ends_on_date: Končí %{date}
|
||||
every_work_day: Každý pracovní den
|
||||
recurrence_on_due_date: datum na které je úkol plánován
|
||||
weekly_options: Nastavení pro týdenní opakované úkoly
|
||||
weekly: Týdně
|
||||
monthly_options: Nastavení pro měsíční opakované úkoly
|
||||
starts_on: Začíná
|
||||
daily_options: Nastavení pro denní opakované úkoly
|
||||
monthly: Měsíčně
|
||||
pattern:
|
||||
month_names:
|
||||
-
|
||||
- Leden
|
||||
- Únor
|
||||
- Březen
|
||||
- Duben
|
||||
- Květen
|
||||
- Červen
|
||||
- Červenec
|
||||
- Srpen
|
||||
- Září
|
||||
- Říjen
|
||||
- Listopad
|
||||
- Prosinec
|
||||
third: třetí
|
||||
every_n: každé %{n}
|
||||
on_day_n: %{n}. den
|
||||
second: druhý
|
||||
every_xth_day_of_every_n_months: každý %{x} %{day} každých %{n_months}
|
||||
from: od
|
||||
weekly: každý týden
|
||||
last: poslední
|
||||
every_day: každý den
|
||||
the_xth_day_of_month: %{x} %{day} měsíce %{month}
|
||||
times: (%{number} opakování)
|
||||
on_work_days: v pracovní dny
|
||||
first: první
|
||||
every_year_on: každý rok %{date}
|
||||
day_names:
|
||||
- neděle
|
||||
- pondělí
|
||||
- úterý
|
||||
- středa
|
||||
- čtvrtek
|
||||
- pátek
|
||||
- sobota
|
||||
show: ukázat
|
||||
fourth: čtvrtý
|
||||
due: plánováno na
|
||||
until: do
|
||||
every_month: každý měsíc
|
||||
show_option_always: stále
|
||||
daily: Denně
|
||||
yearly_every_x_day: Každý %{month} %{day}
|
||||
recurrence_on_options: Nastavit opakování na
|
||||
daily_every_number_day: Každých %{number} dní
|
||||
show_options: Úkázat úkol
|
||||
weekly_every_number_week: Každých %{number} týdnů
|
||||
ends_on: Končí
|
||||
show_days_before: "%{days} dní před plánovaným datem"
|
||||
from_tickler: datum kdy úkol vypadne z Tickleru (není nastaveno plánované datum)
|
||||
no_end_date: Nikdy
|
||||
day_x_on_every_x_month: %{day}. den každý %{month}. měsíc
|
||||
yearly_options: Nastavení pro roční opakované úkoly
|
||||
yearly_every_xth_day: %{day} %{day_of_week} měsíce %{month}
|
||||
monthly_every_xth_day: %{day} %{day_of_week} každý %{month}. měsíc
|
||||
yearly: Ročně
|
||||
no_completed_recurring: Žádné hotové opakované úkoly
|
||||
added_dependency: Přidáno %{dependency} jako závislost.
|
||||
no_deferred_actions: Žádné odložené úkoly.
|
||||
all_completed_tagged_page_title: TRACKS::Hotové úkoly se štítkem %{tag_name}
|
||||
completed_rest_of_month: Ukončené ve zbytku tohoto měsíce
|
||||
recurrence_completed: Poslední opakování úkolu bylo označeno jako hotové. Opakovaný úkol je dokončený
|
||||
error_toggle_complete: Nepodařilo se označit úkol jako hotový
|
||||
no_actions_found: Žádné běžící úkoly.
|
||||
in_pending_state: ve stavu čekající
|
||||
due: Plánováno na
|
||||
action_marked_complete_error: Úkol <strong>'%{description}'</strong> NEBYL označen jako <strong>%{completed} kvůli chybě na serveru.</strong>
|
||||
depends_on_separate_with_commas: Závisí na (odděleno čárkami)
|
||||
action_saved_to_tickler: Úkol byl uložen do Tickleru
|
||||
recurring_action_saved: Opakovaný úkol byl uložen
|
||||
completed_in_archive:
|
||||
one: V archivu je hotový úkol.
|
||||
other: V archivu je %{count} hotových úkolů.
|
||||
to_tickler: do Tickleru
|
||||
next_actions_description_additions:
|
||||
completed: v posledních %{count} dnech
|
||||
due_date: plánovano na %{due_date} nebo dříve
|
||||
overdue: Spožděné úkoly
|
||||
add_new_recurring: Vytvořit opakovaný úkol
|
||||
edit_recurring_todo: Upravit opakovaný úkol
|
||||
no_incomplete_actions: Žádné nehotové úkoly
|
||||
notes:
|
||||
delete_confirmation: Opravdu chcete smazat poznámku '%{id}'?
|
||||
delete_item_title: Smazat položku
|
||||
delete_note_title: Smazat poznámku '%{id}'
|
||||
note_link_title: Zobrazit poznámku %{id}
|
||||
show_note_title: Zobrazit poznámku
|
||||
deleted_note: Smazat poznámku '%{id}'
|
||||
edit_item_title: Upravit položku
|
||||
note_location_link: "V:"
|
||||
no_notes_available: "Žádné poznámky: přidejte poznámky ze stránek jednotlivých projektů."
|
||||
note_header: Poznámka %{id}
|
||||
delete_note_confirm: Opravdu chcete smazat poznámku '%{id}'?
|
||||
states:
|
||||
hidden_plural: Skryté
|
||||
completed: Hotový
|
||||
completed_plural: Hotové
|
||||
visible_plural: Viditelné
|
||||
visible: Viditelný
|
||||
active_plural: Aktivní
|
||||
hidden: Skrytý
|
||||
active: Aktivní
|
||||
review_plural: Nerevidované
|
||||
review: Nerevidovaný
|
||||
stalled_plural: Opuštěné
|
||||
stalled: Opuštěný
|
||||
blocked_plural: Blokované
|
||||
blocked: Blokovaný
|
||||
current_plural: Aktuální
|
||||
current: Aktuální
|
||||
projects:
|
||||
was_marked_hidden: byl označen jako skrytý
|
||||
edit_project_title: Upravit projekt
|
||||
default_tags_removed_notice: Výchozí štítky byly odstraněny
|
||||
default_context_set: Výchozí kontext %{default_context} byl nastaven
|
||||
no_actions_in_project: Žádné aktivní úkoly
|
||||
deferred_actions: Odložené úkoly projektu
|
||||
all_completed_tasks_title: TRACKS::Hotové úkoly projektu '%{project_name}'
|
||||
hide_form: Skrýt formulář
|
||||
page_title: "TRACKS::Projekt: %{project}"
|
||||
show_form_title: Nový projekt
|
||||
list_completed_projects: TRACKS::Hotové projekty
|
||||
to_new_project_page: přejít k novému projektu
|
||||
no_notes_attached: Žádné poznámky
|
||||
deferred_actions_empty: Žádné odložené úkoly
|
||||
this_project: Tento projekt
|
||||
project_state: Projekt je %{state}.
|
||||
todos_append: v tomto projektu
|
||||
no_last_completed_projects: Žádné hotové projekty
|
||||
notes: Poznámky
|
||||
no_last_completed_recurring_todos: Žádné hotové opakované úkoly
|
||||
notes_empty: Žádné poznámky
|
||||
no_projects: Žádné projekty
|
||||
hide_form_title: Schovat formulář založení projektu
|
||||
with_no_default_context: bez výchozího kontextu
|
||||
delete_project: Smazat projekt
|
||||
completed_actions_empty: V tomto projektu nejsou žádné hotové úkoly
|
||||
show_form: Nový projekt
|
||||
actions_in_project_title: Úkoly v tomto projetku
|
||||
delete_project_confirmation: Opravdu chcete smazat projekt '%{name}'?
|
||||
with_default_context: s výchozím kontextem '%{context_name}'
|
||||
set_default_tags_notice: Nastavit výchozí šítky úkolů v tomto projektu %{default_tags}
|
||||
is_active: je aktivní
|
||||
settings: Nastavení
|
||||
completed_projects: Hotové projetky
|
||||
with_default_tags: a s '%{tags}' jako výchozí štítky
|
||||
list_projects: TRACKS::Projekty
|
||||
list_reviews: TRACKS::Revize
|
||||
project_saved_status: Projekt byl uložen
|
||||
add_project: Přidat projekt
|
||||
add_note: Nová poznámka
|
||||
completed_tasks_title: TRACKS::Hotové úkoly projektu '%{project_name}'
|
||||
delete_project_title: Smaže projekt
|
||||
hidden_projects: Skryté projekty
|
||||
add_note_submit: Nová poznámka
|
||||
was_marked_complete: byl označen jako hotový
|
||||
completed_actions: Hotové úkoly tohoto projektu
|
||||
default_context_removed: Výchozí kontext byl odstraněn
|
||||
default_context: Výchozí kontext pro tento projekt je %{context}
|
||||
status_project_name_changed: Jméno projektu bylo změněno
|
||||
active_projects: Aktivní projekty
|
||||
no_default_context: Tento projekt nemá výchozí kontext
|
||||
with_no_default_tags: a nemá žádné výchozí značky
|
||||
edit_project_settings: Upravit vlastnosti projektu
|
||||
state: Tento projekt je %{state}
|
||||
time:
|
||||
am: am
|
||||
formats:
|
||||
default: "%a, %d %b %Y %H:%M:%S %z"
|
||||
time: ""
|
||||
short: "%d %b %H:%M"
|
||||
month_day: "%B %d"
|
||||
long: "%B %d, %Y %H:%M"
|
||||
pm: pm
|
||||
preferences:
|
||||
open_id_url: Vaše OpenID URL je
|
||||
staleness_starts_after: Zastarání nastává po %{days} dnech
|
||||
change_identity_url: Změna URL identity
|
||||
change_password: Změna hesla
|
||||
password_changed: Heslo bylo změněno. Prosím znovu se přihlašte.
|
||||
updated: Nastavení bylo uloženo
|
||||
page_title: TRACKS::Nastavení
|
||||
title: Vaše nastavení
|
||||
token_description: Pešek (feedy a použití API)
|
||||
is_false: "ne"
|
||||
show_number_completed: Zobrazit %{number} hotových položek
|
||||
page_title_edit: TRACKS::Editace nastavení
|
||||
is_true: "ano"
|
||||
edit_preferences: Editace nastavení
|
||||
sms_context_none: žádný
|
||||
generate_new_token: Generovat nového peška
|
||||
token_header: Váš pešek
|
||||
authentication_header: Vaše autentizace
|
||||
current_authentication_type: Používáte autentizaci %{auth_type}
|
||||
change_authentication_type: Změna typu autentizace
|
||||
tabs:
|
||||
authentication: Autentizace
|
||||
tracks_behavior: Chování Tracks
|
||||
profile: Profil
|
||||
date_and_time: Datum a čas
|
||||
generate_new_token_confirm: Opravdu? Nový pešek nahradí ten původní a způsobí nefunkčnost ve všech aplikacích, kde jej používáte.
|
||||
errors:
|
||||
user_unauthorized: "401 Neautorizováno: Jen administrátoři smí používat tuto funkci."
|
||||
date:
|
||||
month_names:
|
||||
-
|
||||
- Leden
|
||||
- Únor
|
||||
- Březen
|
||||
- Duben
|
||||
- Květen
|
||||
- Červen
|
||||
- Červenec
|
||||
- Srpen
|
||||
- Září
|
||||
- Říjen
|
||||
- Listopad
|
||||
- Prosinec
|
||||
abbr_day_names:
|
||||
- Ne
|
||||
- Po
|
||||
- Út
|
||||
- St
|
||||
- Čt
|
||||
- Pá
|
||||
- So
|
||||
order:
|
||||
- :rok
|
||||
- :měsíc
|
||||
- :den
|
||||
formats:
|
||||
only_day: ""
|
||||
default: "%Y-%m-%d"
|
||||
short: "%b %d"
|
||||
month_day: ""
|
||||
long: "%B %d, %Y"
|
||||
longer: "%A %B %d, %Y"
|
||||
day_names:
|
||||
- Neděle
|
||||
- Ponělí
|
||||
- Úterý
|
||||
- Středa
|
||||
- Čtvrtek
|
||||
- Pátek
|
||||
- Sobota
|
||||
abbr_month_names:
|
||||
-
|
||||
- Led
|
||||
- Úno
|
||||
- Bře
|
||||
- Dub
|
||||
- Kvě
|
||||
- Čer
|
||||
- Čec
|
||||
- Srp
|
||||
- Zář
|
||||
- Říj
|
||||
- Lis
|
||||
- Pro
|
||||
support:
|
||||
array:
|
||||
words_connector: ", "
|
||||
last_word_connector: ", a "
|
||||
two_words_connector: " a "
|
||||
select:
|
||||
prompt: Prosím vyberte
|
||||
footer:
|
||||
send_feedback: Poslat zpětnou vazbu na %{version}
|
||||
shared:
|
||||
multiple_next_actions: Úkoly (jeden na každém řádku)
|
||||
hide_form: Schovat formulář
|
||||
toggle_single: Přidat úkol
|
||||
add_action: Přidat úkol
|
||||
add_actions: Přidat úkoly
|
||||
tags_for_all_actions: Značky (oddělené čárkami)
|
||||
toggle_single_title: Přidat jeden nový úkol
|
||||
project_for_all_actions: Projekt (pro všechny)
|
||||
context_for_all_actions: Kontext (pro všechny)
|
||||
toggle_multi: Přidat více úkolů
|
||||
separate_tags_with_commas: oddělené čárkami
|
||||
toggle_multi_title: Přepnout formulář zakládání jedoho/více úkolů
|
||||
hide_action_form_title: Skrýt formulář pro založení nového úkolu
|
||||
make_actions_dependent: Akce budou vzájemně závislé
|
||||
users:
|
||||
successfully_deleted_user: Uživatel %{username} byl úspěšně smazán
|
||||
failed_to_delete_user: Nepodařilo se smazat uživatele %{username}
|
||||
total_contexts: Kontextů celkem
|
||||
first_user_heading: "Vítejte v TRACKS. Nejdříve je nutné vytvořit administrátorský účet:"
|
||||
openid_url_verified: Identitní url %{url} bylo úspěšně ověřeno a nastavena autentizace pomocí OpenID.
|
||||
auth_type_update_error: "Nepodařilo se změnit typ autentizace: %{error_messages}"
|
||||
destroy_successful: Uživatel %{login} byl úspěšně zničen
|
||||
new_token_generated: Nový pešek byl úspěšně vygenerován
|
||||
total_projects: Projektů celkem
|
||||
signup_successful: Registrace uživatele %{username} byla úspěšná.
|
||||
change_password_submit: Změnit heslo
|
||||
no_signups_title: TRACKS::Registrace není povolena
|
||||
user_created: Uživatel byl vytvořen.
|
||||
manage_users: Správa uživatelů
|
||||
account_signup: Registrace uživatele
|
||||
password_updated: Heslo bylo změněno.
|
||||
desired_login: Uživatelské jméno
|
||||
signup: Registrace
|
||||
confirm_password: Potvrzení hesla
|
||||
new_user_heading: "Registrace nového uživatele:"
|
||||
auth_type_updated: Typ autentizace byl změněn.
|
||||
total_actions: Úkolů celkem
|
||||
change_password_title: TRACKS::Změna hesla
|
||||
change_auth_type_title: TRACKS::Změna zůsobu autentizace
|
||||
change_password_prompt: Pro změnu hesla zadejte nové hestlo do polí níže a stiskněte 'Změna hesla'.
|
||||
password_confirmation_label: Potvrzení hesla
|
||||
destroy_error: Nepodařilo se smazat uživatele %{login}
|
||||
choose_password: Zvolte heslo
|
||||
register_with_cas: S vaším uživatelským jménem z CASu
|
||||
label_auth_type: Způsob autentizace
|
||||
new_password_label: Nové heslo
|
||||
you_have_to_reset_your_password: "Je nutné změnit heslo"
|
||||
new_user_title: TRACKS::Přihlášení jako administrátor
|
||||
destroy_user: Zničit uživatele
|
||||
total_users_count: Máte celkem %{count} uživatelů
|
||||
destroy_confirmation: "Varování: tato akce smaže uživatele '%{login}', všechny jeho úkoly, kontexty, projekty i poznámky. Opravdu chcete pokračovat?"
|
||||
signup_new_user: Registrace nového uživatele
|
||||
openid_ok_pref_failed: Vaše identitní URL %{url} bylo úspěšně ověřeno, ale došlo k problému při ukládání nastavení.
|
||||
identity_url: URL identity
|
||||
auth_change_submit: Změnit způsob přihlašování
|
||||
change_authentication_type: Změna způsobu přihlašování
|
||||
total_notes: Poznámek celkem
|
||||
select_authentication_type: Vyberte nový způsob autentizace a stiskněte 'Změnit způsob přihlašování'.
|
||||
sidebar:
|
||||
list_name_active_contexts: Aktivní kontexty
|
||||
list_name_active_projects: Aktivní projekty
|
||||
list_empty: Nic
|
||||
list_name_completed_projects: Hotové projekty
|
||||
list_name_hidden_projects: Skryté projekty
|
||||
list_name_hidden_contexts: Skryté kontexty
|
||||
feedlist:
|
||||
choose_context: Vyberte kontext jehož feed si přejete
|
||||
actions_due_today: Akce plánované na dnes
|
||||
rss_feed: RSS Feed
|
||||
ical_feed: iCal feed
|
||||
all_contexts: Všechny kontexty
|
||||
legend: "Legenda:"
|
||||
all_projects: Všechny projekty
|
||||
choose_project: Vyberte projekt jehož feed si přejete
|
||||
select_feed_for_project: Vyberte feed pro tento projekt
|
||||
active_projects_wo_next: Aktivni projekty bez úkolů
|
||||
project_needed: Musíte nejdříve založit aspoň jeden projekt
|
||||
active_starred_actions: Všechny aktivní úkoly s hvězdičkou
|
||||
select_feed_for_context: Select the feed for this context
|
||||
projects_and_actions: Aktivní projekty a jejich úkoly
|
||||
context_needed: Musíte nejdříve založit aspoň jeden kontext
|
||||
actions_due_next_week: Úkoly plánované na příštích sedm dní
|
||||
notice_incomplete_only: "Poznámka: všechny feedy obsahují jen nehotové úkoly."
|
||||
all_actions: Všechny úkoly
|
||||
actions_completed_last_week: Úkoly dokončené v posledních sedmi dnech
|
||||
context_centric_actions: Feedy s aktivními úkoly podle kontextů
|
||||
plain_text_feed: Prostý text
|
||||
last_fixed_number: Posledních %{number} úkolů
|
||||
project_centric: Feedy s aktivními úkoly podle projektu
|
||||
contexts:
|
||||
delete_context_title: Smazat kontext
|
||||
all_completed_tasks_title: TRACKS::Hotové úkoly v kontextu '%{context_name}'
|
||||
hide_form: Schovat formulář
|
||||
show_form_title: Nový kontext
|
||||
delete_context_confirmation: Opravdu chcete smazat kontext '%{name}'? Dojde ke smazání všech (opakovaných) úkolů z daného kontextu!
|
||||
delete_context: Smazat kontext
|
||||
edit_context: Upravit kontext
|
||||
hide_form_title: Schovat formulář
|
||||
context_hide: Schovat z úvodní stránky?
|
||||
hidden_contexts: Schovat kontexty
|
||||
no_contexts_active: Žádné aktivní kontexty
|
||||
show_form: Nový kontext
|
||||
visible_contexts: Viditelné kontexty
|
||||
save_status_message: Kontext uložen
|
||||
add_context: Vytvořit kontext
|
||||
context_name: Náev kontextu
|
||||
update_status_message: Název kontextu byl změněn
|
||||
completed_tasks_title: TRACKS::Hotové úkoly v kontextu '%{context_name}'
|
||||
new_context_post: "' bude také vytvořen. Opravdu?"
|
||||
status_active: Kontext je aktivní
|
||||
no_actions: Žádné aktivní úkoly v tomto kontextu
|
||||
last_completed_in_context: v tomto kontextu (posledních %{number})
|
||||
context_deleted: Kontext byl odstraněn '%{name}'
|
||||
no_contexts_hidden: Žádné skryté kontexty
|
||||
new_context_pre: Nový kontext '
|
||||
status_hidden: kontext je skrytý
|
||||
login:
|
||||
login_cas: přejít na CAS
|
||||
sign_in: Přihlásit se
|
||||
openid_identity_url_not_found: Je nám líto, neexistuje uživatel s touto identitou (%{identity_url})
|
||||
user_no_expiry: Neodhlšovat
|
||||
cas_no_user_found: Nazdar, %{username}! Nemáte účet na Tracks.
|
||||
cas_login: Přihlášení přes CAS
|
||||
successful_with_session_info: "Přihlášení bylo úspěšné:"
|
||||
please_login: Pro pokračování se prosím přihlšte do Tracks
|
||||
cas_logged_in_greeting: Zdravíčko, %{username}! Byl jste autorizován.
|
||||
cas_username_not_found: Bohužel neexistuje uživatel v CASu se jménem (%{username})
|
||||
mobile_use_openid: "\xE2\x80\xA6nebo přihlášení s OpenID"
|
||||
cas_create_account: Pro vytvoření účtu v CASu prosím pokračujte sem %{signup_link}
|
||||
account_login: Přihlášení uživatele
|
||||
cas_signup_link: Vyžádat účet
|
||||
session_will_not_expire: sezení bylo nastaveno jako trvalé.
|
||||
successful: Přihlášení úspěšné. Vítejte zpět!
|
||||
option_separator: nebo
|
||||
session_time_out: Sezení vypršelo. Prosím %{link}
|
||||
session_will_expire: sezen vyprší za %{hours} hodin neaktivity.
|
||||
login_standard: vraťte se ke standardnímu přihlášení
|
||||
logged_out: You have been logged out of Tracks.
|
||||
login_with_openid: přihlašte se se svým OpenID
|
||||
unsuccessful: Přihlášení bylo úspěšné.
|
||||
log_in_again: přihlašte se znovu.
|
||||
datetime:
|
||||
prompts:
|
||||
minute: Minuta
|
||||
second: Sekunda
|
||||
month: Měsíc
|
||||
hour: Hodina
|
||||
day: Den
|
||||
year: Rok
|
||||
distance_in_words:
|
||||
less_than_x_minutes:
|
||||
one: méně než minuta
|
||||
other: méně než %{count} minut
|
||||
zero: méně než minuta
|
||||
x_days:
|
||||
one: 1 den
|
||||
other: "%{count} dní"
|
||||
almost_x_years:
|
||||
one: almost 1 rok
|
||||
other: skoro %{count} let
|
||||
x_seconds:
|
||||
one: 1 sekunda
|
||||
other: "%{count} sekund"
|
||||
about_x_hours:
|
||||
one: about 1 hodina
|
||||
other: přibližně %{count} hodin
|
||||
less_than_x_seconds:
|
||||
one: méně než 1 sekunda
|
||||
other: mén než %{count} sekund
|
||||
zero: méně než 1 sekunda
|
||||
x_months:
|
||||
one: 1 měsíc
|
||||
other: "%{count} měsíců"
|
||||
x_minutes:
|
||||
one: 1 minuta
|
||||
other: "%{count} minut"
|
||||
about_x_years:
|
||||
one: about 1 rok
|
||||
other: přibližně %{count} let
|
||||
about_x_months:
|
||||
one: about 1 měsíc
|
||||
other: přibližně %{count} měsíců
|
||||
over_x_years:
|
||||
one: přes rok
|
||||
other: přes %{count} let
|
||||
half_a_minute: půl minuty
|
||||
search:
|
||||
contexts_matching_query: Nalezené kontexty
|
||||
tags_matching_query: Nalezené štítky
|
||||
notes_matching_query: Nalezené poznámky
|
||||
no_results: Na váš dotaz nebylo nic nalezeno.
|
||||
todos_matching_query: Nalezené úkoly
|
||||
projects_matching_query: Nalezené projekty
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,14 +7,14 @@ en:
|
|||
next_actions_rss_feed: RSS feed of next actions
|
||||
toggle_notes_title: Toggle all notes
|
||||
mobile_navigation:
|
||||
new_action: 0-New action
|
||||
new_action: New
|
||||
logout: Logout
|
||||
feeds: Feeds
|
||||
starred: 4-Starred
|
||||
projects: 3-Projects
|
||||
starred: Starred
|
||||
projects: Projects
|
||||
tickler: Tickler
|
||||
contexts: 2-Contexts
|
||||
home: 1-Home
|
||||
contexts: Contexts
|
||||
home: Home
|
||||
navigation:
|
||||
manage_users_title: Add or delete users
|
||||
recurring_todos: Repeating todos
|
||||
|
|
@ -560,6 +560,7 @@ en:
|
|||
due_date: with a due date %{due_date} or earlier
|
||||
overdue: Overdue
|
||||
add_new_recurring: Add a new recurring action
|
||||
edit_recurring_todo: Edit repeating action
|
||||
no_incomplete_actions: There are no incomplete actions
|
||||
notes:
|
||||
delete_confirmation: Are you sure that you want to delete the note '%{id}'?
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -25,7 +25,7 @@ ActionController::Routing::Routes.draw do |map|
|
|||
map.resources :notes
|
||||
|
||||
map.resources :todos,
|
||||
:member => {:toggle_check => :put, :toggle_star => :put, :defer => :put},
|
||||
:member => {:toggle_check => :put, :toggle_star => :put, :defer => :put, :mobile_done => :put},
|
||||
:collection => {:check_deferred => :post, :filter_to_context => :post, :filter_to_project => :post, :done => :get, :all_done => :get
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,32 +15,32 @@
|
|||
|
||||
== Version 2.1devel
|
||||
|
||||
NOTE: to use this version you need to migrate your database. Not migrating will
|
||||
NOTE: To use this version you need to migrate your database. Not migrating will
|
||||
cause new actions not to appear!
|
||||
|
||||
This version of tracks has moved to a new place on github. Also the wiki moved
|
||||
to github, see the changed URLs above.
|
||||
This version of tracks has moved to a new place on GitHub. Also the wiki moved
|
||||
to GitHub, see the changed URLs above.
|
||||
|
||||
New and changed features:
|
||||
1. redesign of the completed todos: a new overview page. Also all context and
|
||||
1. Redesign of the completed todos: a new overview page. Also all context and
|
||||
project pages have a link to their completed actions
|
||||
2. New locales (es and fr) and updated locales (de, nl)
|
||||
3. You can star an action right from the form to add a new action
|
||||
4. redesign of preferences page
|
||||
4. Redesign of preferences page.
|
||||
5. You can now mark an action complete from the tickler
|
||||
6. project names can now contain comma (',') in it name
|
||||
6. Project names can now contain comma (',') in it name
|
||||
7. There are two example ruby scripts in /doc to use the REST API to add a todo
|
||||
or a project template with todos from the command line
|
||||
|
||||
Under the hood:
|
||||
1. Upgraded rails to 2.3.12, jquery to 1.6.2 and jquery-ui to 1.8.14
|
||||
2. fixed several issues with the REST API
|
||||
3. upgraded the act_as_statemachine plugin. This change requires a
|
||||
migration, see note above!
|
||||
4. migated to bundler for gem dependencies
|
||||
2. Fixed several issues with the REST API
|
||||
3. Upgraded the act_as_statemachine plugin. This change requires a
|
||||
migration. See note above!
|
||||
4. Migated to bundler for gem dependencies
|
||||
|
||||
See https://github.com/tracksapp/tracks/compare/v2.0...master for all
|
||||
detailled changes
|
||||
detailed changes
|
||||
|
||||
== Version 2.0
|
||||
|
||||
|
|
@ -50,23 +50,23 @@ New features:
|
|||
to a new todo in that project if no tags are supplied
|
||||
3. Tracks now includes support of dependencies. Making an action dependent on
|
||||
another action will hide it until the dependency is completed
|
||||
4. you can now drag an action from one context to another
|
||||
4. You can now drag an action from one context to another
|
||||
5. Support for entering multiple actions in one form
|
||||
6. You can now promote an action to a project
|
||||
7. It is easier to view notes on the mobile interface and other interface fixes
|
||||
8. The project description supports markup
|
||||
9. support for Mail.app (message://) and OneNote (onenote://) links in notes
|
||||
10.The email receiver is now able to receive email from several email adresses.
|
||||
In site.yml this could be set to the previous behavior (receive from one
|
||||
address per user)
|
||||
11.You can enable open signup (like in tracks.tra.in)
|
||||
12.Cleanup of context page
|
||||
13.Support for CAS for login
|
||||
14.Support for adding Tracks as a GMail Widget with instructions on the
|
||||
Integrations page
|
||||
15.Tracks now support internationalization. First translations are German and
|
||||
Dutch. See http://www.getontracks.org/wiki/Translating-Tracks it you like to
|
||||
help translating Tracks to other languages
|
||||
9. Support for Mail.app (message://) and OneNote (onenote://) links in notes
|
||||
10. The email receiver is now able to receive email from several email adresses.
|
||||
In site.yml this could be set to the previous behavior (receive from one
|
||||
address per user)
|
||||
11. You can enable open signup (like in tracks.tra.in)
|
||||
12. Cleanup of context page
|
||||
13. Support for CAS for login
|
||||
14. Support for adding Tracks as a GMail Widget with instructions on the
|
||||
Integrations page
|
||||
15. Tracks now supports internationalization. First translations are German and
|
||||
Dutch. See http://www.getontracks.org/wiki/Translating-Tracks if you'd like to
|
||||
help translate Tracks to other languages
|
||||
|
||||
Under the hood
|
||||
1. All js is migrated to jQuery and most ui-widgets are migrated to jQuery-UI
|
||||
|
|
@ -93,9 +93,9 @@ Under the hood:
|
|||
5. Bugfixes, including fixing OpenID
|
||||
|
||||
== Version 1.6
|
||||
1. upgrade to rails 2.0.2
|
||||
2. new mobile interface (with some iPhone compatibility fixes)
|
||||
3. new search functionality to search on todos, projects, contexts and notes
|
||||
1. Upgrade to rails 2.0.2
|
||||
2. New mobile interface (with some iPhone compatibility fixes)
|
||||
3. New search functionality to search on todos, projects, contexts and notes
|
||||
4. Bugfixes
|
||||
|
||||
== Version 1.5
|
||||
|
|
@ -173,11 +173,6 @@ Under the hood:
|
|||
|
||||
== Version 1.03
|
||||
|
||||
13. All the adding, updating, deleting and marking actions done is performed using Ajax, so happens without needing to refresh the page.
|
||||
14. There's a new setting in settings.yml ('staleness_starts') which defines the number of days before which actions get marked as stale. Let's say you set it to 7 days. Actions created between 7 and 14 days ago get marked pale yellow, those created between 14 and 28 days ago (staleness_starts x 2) get marked darker yellow, and those created more than 28 days ago (staleness_starts x 3) are fluorescent yellow! This is only applied to items without a due date, so you can add items to be done some time in the future without getting yellow splashed all over the place (thanks to Nicholas for the patch to restrict to items with no due date).
|
||||
15. Contexts and projects can now be sorted in any order you like. Arrow buttons on the <tt>/contexts</tt> and <tt>/projects</tt> pages let you move an item to the top, up, down or to the bottom. For contexts, this affects the order in which they sort on the home page. Position is also used to sort the listings of active/completed/hidden contexts and projects in the 'sidebar', and in the dropdown lists on forms.
|
||||
16. You can mark projects as completed (by editing the project on <tt>/projects</tt>). In the 'sidebar' active and completed projects are shown separately, but you can still view the completed project.
|
||||
17. New images (from eclipse.org) for the edit, delete, notes and up, down, top and bottom buttons. I've made a greyscale version for the default, then the coloured version gets loaded when the mouse is hovering over the button.
|
||||
1. Added back border="0" to images which I had mistakenly taken out. This should fix the ugly red border around images that appears in Firefox (thanks, Adam Hughes).
|
||||
2. Removed the section in <tt>config/environment.rb</tt> which requires Redcloth. This was causing errors because Rails now requires Redcloth itself. This means that you now need to have Redcloth installed as a gem (gem install redcloth) (thanks, Jim).
|
||||
3. SQL dumps are now available for each of the available database formats (MySQL, PostgreSQL and SQLite), as well as a separate file containing some example contents, which should work for all of the formats. These are in the <tt>tracks/db</tt> directory (thanks, Jim)
|
||||
|
|
@ -190,6 +185,11 @@ Under the hood:
|
|||
10. <b>Patch by lolindrath</b>: Sorting by date is now much smarter on the home page. Actions are sorted by ascending due date then ascending creation date, but non-due dated items sort to the bottom. This means that the most urgent items float to the top of each context list.
|
||||
11. You can now uncheck actions from the completed actions list, so that they dynamically appear back in the uncompleted actions area.
|
||||
12. A tiny improvement: the toggling of the individual notes now uses Element.toggle from prototype.js, so it doesn't have to be an onLoad property of the body tag. This means that you don't get the notes flashing before they are hidden when you load or reload a page.
|
||||
13. All the adding, updating, deleting and marking actions done is performed using Ajax, so happens without needing to refresh the page.
|
||||
14. There's a new setting in settings.yml ('staleness_starts') which defines the number of days before which actions get marked as stale. Let's say you set it to 7 days. Actions created between 7 and 14 days ago get marked pale yellow, those created between 14 and 28 days ago (staleness_starts x 2) get marked darker yellow, and those created more than 28 days ago (staleness_starts x 3) are fluorescent yellow! This is only applied to items without a due date, so you can add items to be done some time in the future without getting yellow splashed all over the place (thanks to Nicholas for the patch to restrict to items with no due date).
|
||||
15. Contexts and projects can now be sorted in any order you like. Arrow buttons on the <tt>/contexts</tt> and <tt>/projects</tt> pages let you move an item to the top, up, down or to the bottom. For contexts, this affects the order in which they sort on the home page. Position is also used to sort the listings of active/completed/hidden contexts and projects in the 'sidebar', and in the dropdown lists on forms.
|
||||
16. You can mark projects as completed (by editing the project on <tt>/projects</tt>). In the 'sidebar' active and completed projects are shown separately, but you can still view the completed project.
|
||||
17. New images (from eclipse.org) for the edit, delete, notes and up, down, top and bottom buttons. I've made a greyscale version for the default, then the coloured version gets loaded when the mouse is hovering over the button.
|
||||
|
||||
== Version 1.02
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
Tracks is using
|
||||
* github to host the git repository.
|
||||
* Assembla to manage bugs and enhancement request.
|
||||
* the mailing list to discuss features and development
|
||||
* the forum to discuss with users
|
||||
* the mailing list to discuss features and development and interact with users
|
||||
|
||||
See README for links to the respective sites
|
||||
|
||||
|
|
@ -12,35 +11,39 @@ Also see the Development pages on the wiki for details on installing, upgrading,
|
|||
|
||||
2. Dependencies
|
||||
|
||||
The dependencies are maintained by Tracks. For development we try not to vendor them
|
||||
|
||||
Install them using
|
||||
|
||||
rake gems:install RAILS_ENV=development
|
||||
rake gems:install RAILS_ENV=test
|
||||
rake gems:install RAILS_ENV=selenium
|
||||
The dependencies for Tracks are maintained using bundler. Before starting your
|
||||
tracks instance, you'll need to run 'bundle install' to fetch all the
|
||||
dependencies
|
||||
|
||||
3. Wiki
|
||||
|
||||
There are some pointers for setting up your Tracks copy for testing at https://github.com/TracksApp/tracks/wiki/Testing/
|
||||
There are some pointers for setting up your Tracks copy for testing at
|
||||
https://github.com/TracksApp/tracks/wiki/Testing/
|
||||
|
||||
4. SQLITE3 FOR TESTING
|
||||
|
||||
By default, tests are configured to run using sqlite3 in memory mode to increase speed. You will need the sqlite3-ruby gem for this.
|
||||
By default, tests are configured to run using sqlite3 in memory mode to
|
||||
increase speed. You will need the sqlite3-ruby gem for this.
|
||||
|
||||
To avoid showing the migrations as tests are run, add the following to your database.yml below 'database: ":memory:"':
|
||||
To avoid showing the migrations as tests are run, add the following to your
|
||||
database.yml below 'database: ":memory:"':
|
||||
|
||||
verbosity: quiet
|
||||
|
||||
If you want to run tests using another database, that's fine, too. Just change your database.yml accordingly.
|
||||
If you want to run tests using another database, that's fine, too. Just change
|
||||
your database.yml accordingly.
|
||||
|
||||
5. SELENIUM TESTS (Selenium on Rails)
|
||||
5. Test::Unit tests
|
||||
|
||||
This testing style is deprecated and tests are being moved over to Selenium via Cucumber.
|
||||
To run the Test::Unit tests run
|
||||
|
||||
See the wiki for more information to run the tests that are not yet migrated: https://github.com/TracksApp/tracks/wiki/Testing
|
||||
rake test
|
||||
|
||||
6. RSPEC tests
|
||||
Running the above command will run through the unit, functional, and
|
||||
integration tests for Tracks. Use 'rake -T' to see a list of all rake tasks to
|
||||
determine how to run each section of tests separately.
|
||||
|
||||
6. RSpec tests
|
||||
|
||||
To run the RSpec tests run
|
||||
|
||||
|
|
@ -56,4 +59,7 @@ and for those using javascript/ajax use
|
|||
|
||||
rake cucumber:selenium
|
||||
|
||||
See the wiki for more information on testing: https://github.com/TracksApp/tracks/wiki/Testing
|
||||
In order to run the selenium tests, you'll need to have Firefox 3.x installed.
|
||||
Newer versions won't work.
|
||||
|
||||
See the wiki for more information on testing: https://github.com/TracksApp/tracks/wiki/Testing
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Feature: Add new next action from mobile page
|
|||
|
||||
Scenario Outline: The new action form is prefilled with context and project
|
||||
Given I am on the <page>
|
||||
When I follow "0-New action"
|
||||
When I follow "New"
|
||||
Then the selected project should be "<project>"
|
||||
And the selected context should be "<context>"
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ Feature: Add new next action from mobile page
|
|||
Scenario: I can add a new todo using the mobile interface
|
||||
Given I am on the home page
|
||||
Then the badge should show 0
|
||||
When I follow "0-New action"
|
||||
When I follow "New"
|
||||
And I fill in "Description" with "test me"
|
||||
And I press "Create"
|
||||
Then I should see "test me"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ Feature: Edit a next action from the mobile view
|
|||
| context | description |
|
||||
| @mobile | test action |
|
||||
|
||||
@selenium
|
||||
Scenario: I can edit an action on the mobile page
|
||||
When I am on the home page
|
||||
Then the badge should show 1
|
||||
|
|
@ -27,10 +28,8 @@ Feature: Edit a next action from the mobile view
|
|||
Then I should see "changed action"
|
||||
And I should not see "test action"
|
||||
When I follow "changed action"
|
||||
And I press "Edit this action"
|
||||
And I check "done"
|
||||
And I press "Update"
|
||||
Then I should not see "changed action"
|
||||
And I press "Mark complete"
|
||||
Then I should see "changed action" in the completed container
|
||||
|
||||
Scenario: Navigate from home page
|
||||
move this to separate features when other scenarios are created for these features
|
||||
|
|
|
|||
|
|
@ -33,6 +33,15 @@ Feature: Edit a project
|
|||
Then I should see that a project named "release tracks 1.8" is not present
|
||||
And I should see that a project named "release tracks 2.0" is present
|
||||
|
||||
@selenium
|
||||
Scenario: I cannot edit the project name in two places at once
|
||||
Given I have a project "release tracks 1.8" with 1 todos
|
||||
When I go to the "release tracks 1.8" project
|
||||
And I click to edit the project name in place
|
||||
Then I should be able to change the project name in place
|
||||
When I edit the project settings
|
||||
Then I should not be able to change the project name in place
|
||||
|
||||
# Ticket #1041
|
||||
@selenium
|
||||
Scenario: I can change the name of the project using the Edit Project Settings form
|
||||
|
|
|
|||
|
|
@ -196,6 +196,39 @@ When /^I edit the project name in place to be "([^"]*)"$/ do |new_project_name|
|
|||
click_button "Ok"
|
||||
end
|
||||
|
||||
When /^I click to edit the project name in place$/ do
|
||||
selenium.click "css=div#project_name"
|
||||
end
|
||||
|
||||
Then /^I should be able to change the project name in place$/ do
|
||||
#Note that this is not changing the project name
|
||||
selenium.wait_for_element "css=div#project_name>form>input"
|
||||
selenium.click "css=div#project_name > form > button[type=cancel]"
|
||||
end
|
||||
|
||||
When /^I edit the project settings$/ do
|
||||
@project.should_not be_nil
|
||||
|
||||
click_link "link_edit_project_#{@project.id}"
|
||||
selenium.wait_for_element("xpath=//div[@id='edit_project_#{@project.id}']/form//button[@id='submit_project_#{@project.id}']")
|
||||
|
||||
end
|
||||
|
||||
Then /^I should not be able to change the project name in place$/ do
|
||||
When "I click to edit the project name in place"
|
||||
found = selenium.element? "xpath=//div[@id='project_name']/form/input"
|
||||
!found
|
||||
end
|
||||
|
||||
When /^I close the project settings$/ do
|
||||
@project.should_not be_nil
|
||||
click_link "Cancel"
|
||||
wait_for :wait_for => :effects , :javascript_framework => 'jquery' do
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
When /^I edit the project state of "([^"]*)" to "([^"]*)"$/ do |project_name, state_name|
|
||||
project = @current_user.projects.find_by_name(project_name)
|
||||
project.should_not be_nil
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
Given /^I have a repeat pattern called "([^"]*)"$/ do |pattern_name|
|
||||
context = @current_user.contexts.first
|
||||
|
||||
@recurring_todo = @current_user.recurring_todos.create!(
|
||||
:description => pattern_name,
|
||||
:context_id => context.id,
|
||||
|
|
@ -44,10 +45,10 @@ When /^I edit the name of the pattern "([^\"]*)" to "([^\"]*)"$/ do |pattern_nam
|
|||
wait_for_ajax
|
||||
|
||||
fill_in "edit_recurring_todo_description", :with => new_name
|
||||
selenium.click "recurring_todo_edit_action_submit"
|
||||
selenium.click "recurring_todo_edit_update_button"
|
||||
|
||||
wait_for do
|
||||
!selenium.is_visible("overlay")
|
||||
!selenium.is_visible("edit-recurring-todo")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ class ActiveRecord::Base #:nodoc:
|
|||
|
||||
# These extensions make models taggable. This file is automatically generated and required by your app if you run the tagging generator included with has_many_polymorphs.
|
||||
module TaggingExtensions
|
||||
|
||||
|
||||
# Add tags to <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
#
|
||||
# We need to avoid name conflicts with the built-in ActiveRecord association methods, thus the underscores.
|
||||
|
|
@ -21,8 +21,8 @@ class ActiveRecord::Base #:nodoc:
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Removes tags from <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
|
||||
# Removes tags from <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
def _remove_tags outgoing
|
||||
taggable?(true)
|
||||
outgoing = tag_cast_to_string(outgoing)
|
||||
|
|
@ -35,20 +35,20 @@ class ActiveRecord::Base #:nodoc:
|
|||
def tag_list
|
||||
# Redefined later to avoid an RDoc parse error.
|
||||
end
|
||||
|
||||
|
||||
# Replace the existing tags on <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
def tag_with list
|
||||
def tag_with list
|
||||
#:stopdoc:
|
||||
taggable?(true)
|
||||
list = tag_cast_to_string(list)
|
||||
|
||||
|
||||
# Transactions may not be ideal for you here; be aware.
|
||||
Tag.transaction do
|
||||
Tag.transaction do
|
||||
current = tags.map(&:name)
|
||||
_add_tags(list - current)
|
||||
_remove_tags(current - list)
|
||||
end
|
||||
|
||||
|
||||
self
|
||||
#:startdoc:
|
||||
end
|
||||
|
|
@ -64,10 +64,10 @@ class ActiveRecord::Base #:nodoc:
|
|||
|
||||
def tag_list=(value)
|
||||
tag_with(value)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
private
|
||||
|
||||
def tag_cast_to_string obj #:nodoc:
|
||||
case obj
|
||||
when Array
|
||||
|
|
@ -88,9 +88,9 @@ class ActiveRecord::Base #:nodoc:
|
|||
else
|
||||
raise "Invalid object of class #{obj.class} as tagging method parameter"
|
||||
end.flatten.compact.map(&:downcase).uniq
|
||||
end
|
||||
|
||||
# Check if a model is in the :taggables target list. The alternative to this check is to explicitly include a TaggingMethods module (which you would create) in each target model.
|
||||
end
|
||||
|
||||
# Check if a model is in the :taggables target list. The alternative to this check is to explicitly include a TaggingMethods module (which you would create) in each target model.
|
||||
def taggable?(should_raise = false) #:nodoc:
|
||||
unless flag = respond_to?(:tags)
|
||||
raise "#{self.class} is not a taggable model" if should_raise
|
||||
|
|
@ -99,69 +99,69 @@ class ActiveRecord::Base #:nodoc:
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
module TaggingFinders
|
||||
# Find all the objects tagged with the supplied list of tags
|
||||
#
|
||||
#
|
||||
# Usage : Model.tagged_with("ruby")
|
||||
# Model.tagged_with("hello", "world")
|
||||
# Model.tagged_with("hello", "world", :limit => 10)
|
||||
#
|
||||
# XXX This query strategy is not performant, and needs to be rewritten as an inverted join or a series of unions
|
||||
#
|
||||
#
|
||||
def tagged_with(*tag_list)
|
||||
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
||||
tag_list = parse_tags(tag_list)
|
||||
|
||||
|
||||
scope = scope(:find)
|
||||
options[:select] ||= "#{table_name}.*"
|
||||
options[:from] ||= "#{table_name}, tags, taggings"
|
||||
|
||||
|
||||
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
||||
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
||||
|
||||
add_joins!(sql, options[:joins], scope)
|
||||
|
||||
|
||||
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
||||
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
||||
sql << "AND taggings.tag_id = tags.id "
|
||||
|
||||
|
||||
tag_list_condition = tag_list.map {|name| "'#{name}'"}.join(", ")
|
||||
|
||||
|
||||
sql << "AND (tags.name IN (#{sanitize_sql(tag_list_condition)})) "
|
||||
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
||||
|
||||
columns = column_names.map do |column|
|
||||
|
||||
columns = column_names.map do |column|
|
||||
"#{table_name}.#{column}"
|
||||
end.join(", ")
|
||||
|
||||
|
||||
sql << "GROUP BY #{columns} "
|
||||
sql << "HAVING COUNT(taggings.tag_id) = #{tag_list.size}"
|
||||
|
||||
|
||||
add_order!(sql, options[:order], scope)
|
||||
add_limit!(sql, options, scope)
|
||||
add_lock!(sql, options, scope)
|
||||
|
||||
|
||||
find_by_sql(sql)
|
||||
end
|
||||
|
||||
|
||||
def self.tagged_with_any(*tag_list)
|
||||
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
||||
tag_list = parse_tags(tag_list)
|
||||
|
||||
|
||||
scope = scope(:find)
|
||||
options[:select] ||= "#{table_name}.*"
|
||||
options[:from] ||= "#{table_name}, meta_tags, taggings"
|
||||
|
||||
|
||||
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
||||
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
||||
|
||||
add_joins!(sql, options, scope)
|
||||
|
||||
|
||||
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
||||
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
||||
sql << "AND taggings.meta_tag_id = meta_tags.id "
|
||||
|
||||
|
||||
sql << "AND ("
|
||||
or_options = []
|
||||
tag_list.each do |name|
|
||||
|
|
@ -169,30 +169,30 @@ class ActiveRecord::Base #:nodoc:
|
|||
end
|
||||
or_options_joined = or_options.join(" OR ")
|
||||
sql << "#{or_options_joined}) "
|
||||
|
||||
|
||||
|
||||
|
||||
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
||||
|
||||
|
||||
columns = column_names.map do |column|
|
||||
"#{table_name}.#{column}"
|
||||
end.join(", ")
|
||||
|
||||
|
||||
sql << "GROUP BY #{columns} "
|
||||
|
||||
|
||||
add_order!(sql, options[:order], scope)
|
||||
add_limit!(sql, options, scope)
|
||||
add_lock!(sql, options, scope)
|
||||
|
||||
|
||||
find_by_sql(sql)
|
||||
end
|
||||
|
||||
|
||||
def parse_tags(tags)
|
||||
return [] if tags.blank?
|
||||
tags = Array(tags).first
|
||||
tags = tags.respond_to?(:flatten) ? tags.flatten : tags.split(Tag::DELIMITER)
|
||||
tags.map { |tag| tag.strip.squeeze(" ") }.flatten.compact.map(&:downcase).uniq
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
include TaggingExtensions
|
||||
|
|
|
|||
|
|
@ -737,7 +737,8 @@ var ProjectListPage = {
|
|||
$('div#project_name').editable(ProjectListPage.save_project_name, {
|
||||
style: 'padding: 0px; width=100%;',
|
||||
submit: i18n['common.ok'],
|
||||
cancel: i18n['common.cancel']
|
||||
cancel: i18n['common.cancel'],
|
||||
onblur: 'cancel'
|
||||
});
|
||||
|
||||
/* alphabetize project list */
|
||||
|
|
@ -775,12 +776,14 @@ var ProjectListPage = {
|
|||
|
||||
/* submit project form after edit */
|
||||
$("form.edit-project-form button.positive").live('click', function (ev) {
|
||||
$('div#project_name').editable('enable');
|
||||
submit_with_ajax_and_block_element('form.edit-project-form', $(this));
|
||||
return false;
|
||||
});
|
||||
|
||||
/* cancel edit project form */
|
||||
$('form.edit-project-form a.negative').live('click', function(){
|
||||
$('div#project_name').editable('enable');
|
||||
$(this).parents('.edit-form').fadeOut(200, function () {
|
||||
$(this).parents('.list').find('.project').fadeIn(500);
|
||||
$(this).parents('.container').find('.item-show').fadeIn(500);
|
||||
|
|
@ -991,64 +994,89 @@ var RecurringTodosPage = {
|
|||
$('#recurring_edit_'+this).hide();
|
||||
});
|
||||
},
|
||||
reset_radio: function () {
|
||||
$('input:radio[name="recurring_todo[recurring_period]"]')[0].checked = true;
|
||||
},
|
||||
toggle_overlay: function () {
|
||||
var overlay_element = document.getElementById("overlay");
|
||||
overlay_element.style.visibility = (overlay_element.style.visibility == "visible") ? "hidden" : "visible";
|
||||
},
|
||||
setup_behavior: function() {
|
||||
/* cancel button on new recurring todo form */
|
||||
$("#recurring_todo_new_action_cancel").live('click', function(){
|
||||
$('#recurring-todo-form-new-action input:text:first').focus();
|
||||
RecurringTodosPage.hide_all_recurring();
|
||||
$('#recurring_daily').show();
|
||||
RecurringTodosPage.toggle_overlay();
|
||||
});
|
||||
/* cancel button on edit recurring todo form */
|
||||
$("#recurring_todo_edit_action_cancel").live('click', function(){
|
||||
$('#recurring-todo-form-edit-action input:text:first').focus();
|
||||
RecurringTodosPage.hide_all_recurring();
|
||||
$('#recurring_daily').show();
|
||||
RecurringTodosPage.toggle_overlay();
|
||||
});
|
||||
/* change recurring period radio input on edit form */
|
||||
$("#recurring_edit_period input").live('click', function(){
|
||||
RecurringTodosPage.hide_all_edit_recurring();
|
||||
$('#recurring_edit_'+this.id.split('_')[5]).show();
|
||||
});
|
||||
/* change recurring period radio input on new form */
|
||||
$("#recurring_period input").live('click', function(){
|
||||
RecurringTodosPage.hide_all_recurring();
|
||||
$('#recurring_'+this.id.split('_')[4]).show();
|
||||
});
|
||||
/* add new recurring todo plus-button in sidebar */
|
||||
$("#add-new-recurring-todo").live('click', function(){
|
||||
$('#new-recurring-todo').show();
|
||||
$('#edit-recurring-todo').hide();
|
||||
RecurringTodosPage.toggle_overlay();
|
||||
});
|
||||
/* submit form when editing a recurring todo */
|
||||
$("#recurring_todo_edit_action_submit").live('click', function (ev) {
|
||||
submit_with_ajax_and_block_element('form#recurring-todo-form-edit-action', $(this));
|
||||
return false;
|
||||
});
|
||||
/* submit form for new recurring todo */
|
||||
$("#recurring_todo_new_action_submit").live('click', function (ev) {
|
||||
submit_with_ajax_and_block_element('form.#recurring-todo-form-new-action', $(this));
|
||||
return false;
|
||||
});
|
||||
/* set behavior for edit recurring todo */
|
||||
$(".item-container a.edit_icon").live('click', function (ev){
|
||||
get_with_ajax_and_block_element(this.href, $(this).parents(".item-container"));
|
||||
return false;
|
||||
});
|
||||
/* delete button to delete a todo from the list */
|
||||
$('.item-container a.delete_icon').live('click', function(evt){
|
||||
var confirm_message = $(this).attr("x_confirm_message")
|
||||
if(confirm(confirm_message)){
|
||||
delete_with_ajax_and_block_element(this.href, $(this).parents('.project'));
|
||||
/* add new recurring todo plus-button in sidebar */
|
||||
$("#add-new-recurring-todo").live('click', function(){
|
||||
$( "#new-recurring-todo" ).dialog( "open" );
|
||||
});
|
||||
|
||||
/* setup dialog for new repeating action */
|
||||
$( "#new-recurring-todo" ).dialog({
|
||||
autoOpen: false,
|
||||
height: 690,
|
||||
width: 750,
|
||||
modal: true,
|
||||
buttons: {
|
||||
"Create": function() { submit_with_ajax_and_block_element('form.#recurring-todo-form-new-action', $(this).parents(".ui-dialog")); },
|
||||
Cancel: function() { $( this ).dialog( "close" ); }
|
||||
},
|
||||
show: "fade",
|
||||
hide: "fade",
|
||||
close: function() {
|
||||
$('#recurring-todo-form-new-action input:text:first').focus();
|
||||
RecurringTodosPage.hide_all_recurring();
|
||||
RecurringTodosPage.reset_radio();
|
||||
$('#recurring_daily').show();
|
||||
}
|
||||
});
|
||||
|
||||
/* change recurring period radio input on new form */
|
||||
$("#recurring_period input").live('click', function(){
|
||||
RecurringTodosPage.hide_all_recurring();
|
||||
$('#recurring_'+this.id.split('_')[4]).show();
|
||||
});
|
||||
|
||||
/* setup dialog for new repeating action */
|
||||
$( "#edit-recurring-todo" ).dialog({
|
||||
autoOpen: false,
|
||||
height: 690,
|
||||
width: 750,
|
||||
modal: true,
|
||||
buttons: {
|
||||
"Update": {
|
||||
text: "Update",
|
||||
id: 'recurring_todo_edit_update_button',
|
||||
click: function() { submit_with_ajax_and_block_element('form#recurring-todo-form-edit-action', $(this).parents(".ui-dialog")); }
|
||||
},
|
||||
Cancel: function() { $( this ).dialog( "close" ); }
|
||||
},
|
||||
show: "fade",
|
||||
hide: "fade",
|
||||
close: function() {
|
||||
$('#recurring-todo-form-edit-action input:text:first').focus();
|
||||
RecurringTodosPage.hide_all_recurring();
|
||||
RecurringTodosPage.reset_radio();
|
||||
$('#recurring_daily').show();
|
||||
}
|
||||
});
|
||||
|
||||
/* change recurring period radio input on edit form */
|
||||
$("#recurring_edit_period input").live('click', function(){
|
||||
RecurringTodosPage.hide_all_edit_recurring();
|
||||
$('#recurring_edit_'+this.id.split('_')[5]).show();
|
||||
});
|
||||
|
||||
/* set behavior for edit recurring todo */
|
||||
$(".item-container a.edit_icon").live('click', function (ev){
|
||||
get_with_ajax_and_block_element(this.href, $(this).parents(".item-container"));
|
||||
return false;
|
||||
});
|
||||
|
||||
/* delete button to delete a todo from the list */
|
||||
$('.item-container a.delete_icon').live('click', function(evt){
|
||||
var confirm_message = $(this).attr("x_confirm_message")
|
||||
if(confirm(confirm_message)){
|
||||
delete_with_ajax_and_block_element(this.href, $(this).parents('.project'));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1290,7 +1318,7 @@ function enable_rich_interaction(){
|
|||
$(document).ready(function() {
|
||||
|
||||
// fix for IE8. Without this checkboxes don't work AJAXy. See #1152
|
||||
if($.browser.msie && ($.browser.version.substring(0, 2) == "8.")) {
|
||||
if($.browser.msie && ( ($.browser.version.substring(0, 2) == "8.") || ($.browser.version.substring(0, 2) == "7.") ) ) {
|
||||
$('body').bind('change', function() {
|
||||
return true;
|
||||
});
|
||||
|
|
|
|||
23
public/javascripts/i18n/jquery.ui.datepicker-cz.js
vendored
Normal file
23
public/javascripts/i18n/jquery.ui.datepicker-cz.js
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* Czech initialisation for the jQuery UI date picker plugin. */
|
||||
/* Written by Pavel Župa (pavel.zupa@gmail.com). */
|
||||
jQuery(function($){
|
||||
$.datepicker.regional['cz'] = {
|
||||
closeText: 'zavřít',
|
||||
prevText: 'předchozí',
|
||||
nextText: 'další',
|
||||
currentText: 'dnes',
|
||||
monthNames: ['Leden','Únor','Březen','Duben','Květen','Červen',
|
||||
'Červenec','Srpen','Září','Říjen','Listopad','Prosinec'],
|
||||
monthNamesShort: ['Led','Úno','Bře','Dub','Kvě','Čer',
|
||||
'Čec','Srp','Zář','Říj','Lis','Pro'],
|
||||
dayNames: ['Neděle','Pondělí','Úterý','Středa','Čtvrtek','Pátek','Sobota'],
|
||||
dayNamesShort: ['Ne','Po','Út','St','Čt','Pá','So'],
|
||||
dayNamesMin: ['Ne','Po','Út','St','Čt','Pá','So'],
|
||||
weekHeader: 'č.',
|
||||
dateFormat: 'dd.mm.yy',
|
||||
firstDay: 1,
|
||||
isRTL: false,
|
||||
showMonthAfterYear: false,
|
||||
yearSuffix: ''};
|
||||
$.datepicker.setDefaults($.datepicker.regional['cz']);
|
||||
});
|
||||
|
|
@ -3,36 +3,41 @@ body {
|
|||
font-size: smaller;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
font-size: XX-small;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a, a:link, a:active, a:visited {
|
||||
color: #CC3334;
|
||||
padding-left: 1px;
|
||||
padding-right: 1px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
background-color: #CC3334;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
text-decoration: underline;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.m_t_d a {
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.m_t_d a:hover {
|
||||
text-decoration: underline;
|
||||
color: #0000FF;
|
||||
}
|
||||
|
||||
.m_t_d .red, .m_t_d .amber, .m_t_d .orange, .m_t_d .green{
|
||||
background-color: #999999;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #f00;
|
||||
color: #fff;
|
||||
font-size: small;
|
||||
margin-top:.3em;
|
||||
margin-bottom:.3em;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
padding-left:8px;
|
||||
margin-top:0;
|
||||
margin-bottom:0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
|
|
@ -43,6 +48,17 @@ h2 {
|
|||
border-top: 1px solid #777777;
|
||||
}
|
||||
|
||||
h2 a, h2 a:link, h2 a:active, h2 a:visited {
|
||||
color: #666666;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h2 a:hover {
|
||||
background-color: transparent;
|
||||
color: #CC3334;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h4.alert {
|
||||
border: 1px solid #666666;
|
||||
text-align: center;
|
||||
|
|
@ -79,6 +95,15 @@ span.r {
|
|||
span.prj, span.ctx{
|
||||
font-size: X-small;
|
||||
}
|
||||
|
||||
#ctx, #pjr {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
#ctx a, #pjr a {
|
||||
padding: 0.1em 0;
|
||||
}
|
||||
|
||||
/* Draw attention to some text
|
||||
Same format as traffic lights */
|
||||
.red {
|
||||
|
|
@ -118,22 +143,22 @@ span.prj, span.ctx{
|
|||
|
||||
.count {
|
||||
color: #fff;
|
||||
background: #000;
|
||||
font-size: medium;
|
||||
background: #f00;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
.errors {
|
||||
background: #FFC2C2;
|
||||
}
|
||||
|
||||
ul.c li.star {
|
||||
list-style-type: circle;
|
||||
ul.c li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ul.c {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding-left: 1.1em;
|
||||
padding-left: 0.1em;
|
||||
}
|
||||
|
||||
ul.c li {
|
||||
|
|
@ -149,8 +174,44 @@ span.r {
|
|||
display:none;
|
||||
}
|
||||
|
||||
#topbar {
|
||||
background-color: #000000;
|
||||
clear: both;
|
||||
color: #EEEEEE;
|
||||
height: 45px;
|
||||
left: 0;
|
||||
margin-bottom: 5px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 501;
|
||||
}
|
||||
|
||||
.nav {
|
||||
font-size: x-small;
|
||||
color: #fff;
|
||||
background: #000;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
#topbar .nav {
|
||||
padding-left:8px;
|
||||
margin-bottom:0.3em;
|
||||
}
|
||||
|
||||
.nav a, .nav a:link, .nav a:active, .nav a:visited {
|
||||
color: #fff;
|
||||
padding-top: 1.0em;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.nav a:focus, .nav a:hover, .nav a:active {
|
||||
background: transparent;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.nav li:hover, .nav a:focus, .nav a:hover, .nav a:active {
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
#database_auth_form table td {
|
||||
|
|
@ -160,3 +221,10 @@ span.r {
|
|||
table.c {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.mobile-done {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
input#todo_description, input#tag_list, textarea#todo_notes, select#todo_project_id, select#todo_context_id {
|
||||
width: 95%;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,26 +203,6 @@ a.show_successors:hover, a.link_to_successors:hover {background-image: url(../im
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
#overlay {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 102;
|
||||
text-align: center;
|
||||
background-image:url("../images/trans70.png");
|
||||
}
|
||||
|
||||
#overlay #new-recurring-todo, #overlay #edit-recurring-todo {
|
||||
width:750px;
|
||||
background-color: #fff;
|
||||
border:1px solid #000;
|
||||
padding: 15px;
|
||||
margin: 70px auto;
|
||||
}
|
||||
|
||||
.recurring_container {
|
||||
padding: 0px 5px 0px 5px;
|
||||
border: 1px solid #999;
|
||||
|
|
@ -231,14 +211,6 @@ a.show_successors:hover, a.link_to_successors:hover {background-image: url(../im
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
.recurring_submit_box {
|
||||
height: 25px;
|
||||
padding: 5px 0;
|
||||
text-align: center;
|
||||
clear: both;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Navigation links at the top */
|
||||
|
||||
#navcontainer {
|
||||
|
|
@ -278,8 +250,8 @@ a.show_successors:hover, a.link_to_successors:hover {background-image: url(../im
|
|||
#navlist a:hover { color: #CCC; }
|
||||
|
||||
#develop-notify-bar {
|
||||
line-height:0.5;
|
||||
background-image: url(/images/construction.gif);
|
||||
line-height:0.5;
|
||||
background-image: url(/images/construction.gif);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
|
|
@ -321,6 +293,9 @@ a.show_successors:hover, a.link_to_successors:hover {background-image: url(../im
|
|||
border: 1px solid #999;
|
||||
margin: 0px 0px 15px 0px;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
|
||||
.completed {
|
||||
|
|
@ -373,7 +348,7 @@ div#input_box {
|
|||
|
||||
#input_box ul#predecessor_ul {
|
||||
list-style-type: none;
|
||||
color: black;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.show_from_input, .due_input, .project_input, .context_input,
|
||||
|
|
@ -424,7 +399,7 @@ a.delete_dependency_button:hover {background-color: white;}
|
|||
|
||||
div.item-container {
|
||||
padding: 2px 0px;
|
||||
line-height:20px;
|
||||
line-height: 20px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
|
@ -445,6 +420,7 @@ a.icon {
|
|||
|
||||
input.item-checkbox {
|
||||
float: left;
|
||||
margin-top: 2px;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
vertical-align: middle;
|
||||
|
|
@ -753,6 +729,10 @@ The colour of the background gets progressively yellower with age */
|
|||
font-size: 12pt;
|
||||
margin: 10px 10px 0px 0px;
|
||||
height:26px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
|
||||
}
|
||||
|
||||
ul {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ begin
|
|||
t.sequence(:description) { |n| "testtodo#{n}" }
|
||||
t.association :context
|
||||
end
|
||||
|
||||
rescue FactoryGirl::DuplicateDefinitionError
|
||||
# No problem, apparently this file was included already.
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
|||
|
||||
class RecurringTodosControllerTest < ActionController::TestCase
|
||||
fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings, :recurring_todos
|
||||
|
||||
|
||||
def setup
|
||||
@controller = RecurringTodosController.new
|
||||
@request, @response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
||||
|
|
@ -12,30 +12,30 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
get :index
|
||||
assert_redirected_to :controller => 'login', :action => 'login'
|
||||
end
|
||||
|
||||
|
||||
def test_destroy_recurring_todo
|
||||
login_as(:admin_user)
|
||||
xhr :post, :destroy, :id => 1, :_source_view => 'todo'
|
||||
begin
|
||||
begin
|
||||
rc = RecurringTodo.find(1)
|
||||
rescue
|
||||
rc = nil
|
||||
rc = nil
|
||||
end
|
||||
assert_nil rc
|
||||
end
|
||||
|
||||
|
||||
def test_new_recurring_todo
|
||||
login_as(:admin_user)
|
||||
orig_rt_count = RecurringTodo.count
|
||||
orig_todo_count = Todo.count
|
||||
put :create,
|
||||
"context_name"=>"library",
|
||||
"project_name"=>"Build a working time machine",
|
||||
"recurring_todo" =>
|
||||
put :create,
|
||||
"context_name"=>"library",
|
||||
"project_name"=>"Build a working time machine",
|
||||
"recurring_todo" =>
|
||||
{
|
||||
"daily_every_x_days"=>"1",
|
||||
"daily_selector"=>"daily_every_x_day",
|
||||
"description"=>"new recurring pattern",
|
||||
"daily_every_x_days"=>"1",
|
||||
"daily_selector"=>"daily_every_x_day",
|
||||
"description"=>"new recurring pattern",
|
||||
"end_date" => "31/08/2010",
|
||||
"ends_on" => "ends_on_end_date",
|
||||
"monthly_day_of_week" => "1",
|
||||
|
|
@ -59,15 +59,15 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
"yearly_month_of_year2"=>"8",
|
||||
"yearly_month_of_year"=>"6",
|
||||
"yearly_selector"=>"yearly_every_x_day"
|
||||
},
|
||||
},
|
||||
"tag_list"=>"one, two, three, four"
|
||||
|
||||
|
||||
# check new recurring todo added
|
||||
assert_equal orig_rt_count+1, RecurringTodo.count
|
||||
assert_equal orig_rt_count+1, RecurringTodo.count
|
||||
# check new todo added
|
||||
assert_equal orig_todo_count+1, Todo.count
|
||||
end
|
||||
|
||||
|
||||
def test_recurring_todo_toggle_check
|
||||
# the test fixtures did add recurring_todos but not the corresponding todos,
|
||||
# so we check complete and uncheck to force creation of a todo from the
|
||||
|
|
@ -78,22 +78,22 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
xhr :post, :toggle_check, :id=>1, :_source_view=>""
|
||||
recurring_todo_1 = RecurringTodo.find(1)
|
||||
assert recurring_todo_1.completed?
|
||||
|
||||
|
||||
# remove remaining todo
|
||||
todo = Todo.find_by_recurring_todo_id(1)
|
||||
todo.recurring_todo_id = 2
|
||||
todo.save
|
||||
|
||||
|
||||
todo_count = Todo.count
|
||||
|
||||
|
||||
# mark as active
|
||||
xhr :post, :toggle_check, :id=>1, :_source_view=>""
|
||||
xhr :post, :toggle_check, :id=>1, :_source_view=>""
|
||||
recurring_todo_1.reload
|
||||
assert recurring_todo_1.active?
|
||||
|
||||
|
||||
# by making active, a new todo should be created from the pattern
|
||||
assert_equal todo_count+1, Todo.count
|
||||
|
||||
|
||||
# find the new todo and check its description
|
||||
new_todo = Todo.find_by_recurring_todo_id 1
|
||||
assert_equal "Call Bill Gates every day", new_todo.description
|
||||
|
|
@ -101,9 +101,9 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
|
||||
def test_creating_recurring_todo_with_show_from_in_past
|
||||
login_as(:admin_user)
|
||||
|
||||
|
||||
@yearly = RecurringTodo.find(5) # yearly on june 8th
|
||||
|
||||
|
||||
# change due date in four days from now and show from 10 days before, i.e. 6
|
||||
# days ago
|
||||
target_date = Time.now.utc + 4.days
|
||||
|
|
@ -114,37 +114,37 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
# @yearly.errors.each {|obj, error| puts error}
|
||||
# end
|
||||
assert @yearly.save
|
||||
|
||||
|
||||
# toggle twice to force generation of new todo
|
||||
xhr :post, :toggle_check, :id=>5, :_source_view=>""
|
||||
xhr :post, :toggle_check, :id=>5, :_source_view=>""
|
||||
|
||||
new_todo = Todo.find_by_recurring_todo_id 5
|
||||
|
||||
|
||||
# due date should be the target_date
|
||||
assert_equal users(:admin_user).at_midnight(Date.new(target_date.year, target_date.month, target_date.day)), new_todo.due
|
||||
|
||||
|
||||
# show_from should be nil since now+4.days-10.days is in the past
|
||||
assert_equal nil, new_todo.show_from
|
||||
end
|
||||
|
||||
|
||||
def test_last_sunday_of_march
|
||||
# this test is a duplicate of the unit test. Only this test covers the
|
||||
# codepath in the controllers
|
||||
|
||||
|
||||
login_as(:admin_user)
|
||||
|
||||
orig_rt_count = RecurringTodo.count
|
||||
orig_todo_count = Todo.count
|
||||
|
||||
put :create,
|
||||
"context_name"=>"library",
|
||||
"project_name"=>"Build a working time machine",
|
||||
"recurring_todo" =>
|
||||
put :create,
|
||||
"context_name"=>"library",
|
||||
"project_name"=>"Build a working time machine",
|
||||
"recurring_todo" =>
|
||||
{
|
||||
"daily_every_x_days"=>"1",
|
||||
"daily_selector"=>"daily_every_x_day",
|
||||
"description"=>"new recurring pattern",
|
||||
"daily_every_x_days"=>"1",
|
||||
"daily_selector"=>"daily_every_x_day",
|
||||
"description"=>"new recurring pattern",
|
||||
"end_date" => "",
|
||||
"ends_on" => "no_end_date",
|
||||
"monthly_day_of_week" => "1",
|
||||
|
|
@ -168,36 +168,36 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
"yearly_month_of_year2"=>"3",
|
||||
"yearly_month_of_year"=>"10",
|
||||
"yearly_selector"=>"yearly_every_xth_day"
|
||||
},
|
||||
},
|
||||
"tag_list"=>"one, two, three, four"
|
||||
|
||||
|
||||
# check new recurring todo added
|
||||
assert_equal orig_rt_count+1, RecurringTodo.count
|
||||
assert_equal orig_rt_count+1, RecurringTodo.count
|
||||
# check new todo added
|
||||
assert_equal orig_todo_count+1, Todo.count
|
||||
|
||||
# find the newly created todo
|
||||
new_todo = Todo.find_by_description("new recurring pattern")
|
||||
assert !new_todo.nil?
|
||||
|
||||
|
||||
# the date should be 31 march 2013
|
||||
assert_equal Time.zone.local(2013,3,31), new_todo.due
|
||||
end
|
||||
|
||||
|
||||
def test_recurring_todo_with_due_date_and_show_always
|
||||
login_as(:admin_user)
|
||||
|
||||
orig_rt_count = RecurringTodo.count
|
||||
orig_todo_count = Todo.count
|
||||
|
||||
put :create,
|
||||
"context_name"=>"library",
|
||||
"project_name"=>"Build a working time machine",
|
||||
"recurring_todo" =>
|
||||
put :create,
|
||||
"context_name"=>"library",
|
||||
"project_name"=>"Build a working time machine",
|
||||
"recurring_todo" =>
|
||||
{
|
||||
"daily_every_x_days"=>"1",
|
||||
"daily_selector"=>"daily_every_x_day",
|
||||
"description"=>"new recurring pattern",
|
||||
"daily_every_x_days"=>"1",
|
||||
"daily_selector"=>"daily_every_x_day",
|
||||
"description"=>"new recurring pattern",
|
||||
"end_date" => "",
|
||||
"ends_on" => "no_end_date",
|
||||
"monthly_day_of_week" => "1",
|
||||
|
|
@ -221,20 +221,50 @@ class RecurringTodosControllerTest < ActionController::TestCase
|
|||
"yearly_month_of_year2"=>"3",
|
||||
"yearly_month_of_year"=>"10",
|
||||
"yearly_selector"=>"yearly_every_xth_day"
|
||||
},
|
||||
},
|
||||
"tag_list"=>"one, two, three, four"
|
||||
|
||||
|
||||
# check new recurring todo added
|
||||
assert_equal orig_rt_count+1, RecurringTodo.count
|
||||
assert_equal orig_rt_count+1, RecurringTodo.count
|
||||
# check new todo added
|
||||
assert_equal orig_todo_count+1, Todo.count
|
||||
|
||||
# find the newly created recurring todo
|
||||
recurring_todo = RecurringTodo.find_by_description("new recurring pattern")
|
||||
assert !recurring_todo.nil?
|
||||
|
||||
|
||||
assert_equal "due_date", recurring_todo.target
|
||||
assert_equal true, recurring_todo.show_always?
|
||||
end
|
||||
|
||||
def test_find_and_inactivate
|
||||
login_as(:admin_user)
|
||||
|
||||
rt = RecurringTodo.find(recurring_todos(:call_bill_gates_every_day).id)
|
||||
todo = Todo.find_by_recurring_todo_id(rt.id)
|
||||
|
||||
assert_not_nil todo
|
||||
assert_equal "active", todo.state, "todo should be active"
|
||||
assert_equal "active", rt.state, "repeat pattern should be active"
|
||||
|
||||
get :index # will call find_and_inactivate
|
||||
|
||||
rt.reload
|
||||
assert_equal "active", rt.state, "repeat pattern should still be active"
|
||||
|
||||
# disconnect todo from pattern thus leaving the pattern without
|
||||
# any active todos, but in active state
|
||||
todo.reload
|
||||
todo.recurring_todo_id=nil
|
||||
todo.save!
|
||||
|
||||
todo.reload
|
||||
rt.reload
|
||||
assert_equal "active", rt.state, "repeat pattern should still be active and not changed"
|
||||
|
||||
get :index
|
||||
rt.reload
|
||||
assert_equal "completed", rt.state, "repeat pattern should be completed"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -530,7 +530,9 @@ class TodosControllerTest < ActionController::TestCase
|
|||
# check that the new_todo is in the tickler to show next month
|
||||
assert !new_todo.show_from.nil?
|
||||
|
||||
next_month = today + 1.month
|
||||
# do not use today here. It somehow gets messed up with the timezone calculation.
|
||||
next_month = (Time.zone.now + 1.month).at_midnight
|
||||
|
||||
assert_equal next_month.utc.to_date.to_s(:db), new_todo.show_from.utc.to_date.to_s(:db)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ class TodoXmlApiTest < ActionController::IntegrationTest
|
|||
|
||||
def setup
|
||||
assert_test_environment_ok
|
||||
@user = users(:other_user)
|
||||
@password = 'sesame'
|
||||
@user = users(:admin_user)
|
||||
@password = 'abracadabra'
|
||||
end
|
||||
|
||||
def test_get_tickler_succeeds
|
||||
|
|
@ -25,24 +25,24 @@ class TodoXmlApiTest < ActionController::IntegrationTest
|
|||
assert_response 401
|
||||
end
|
||||
|
||||
def test_get_tickler_returns_all_deferred_todos
|
||||
number = @user.todos.deferred.count
|
||||
def test_get_tickler_returns_all_deferred_and_pending_todos
|
||||
number = @user.todos.deferred.count + @user.todos.pending.count
|
||||
authenticated_get_xml "/tickler", @user.login, @password, {}
|
||||
assert_tag :tag => "todos", :children => { :count => number, :only => { :tag => "todo" } }
|
||||
assert_tag :tag => "todos", :children => { :count => number }
|
||||
end
|
||||
|
||||
def test_get_tickler_omits_user_id
|
||||
authenticated_get_xml "/tickler", @user.login, @password, {}
|
||||
assert_no_tag :tag => "user_id"
|
||||
end
|
||||
|
||||
def test_create_todo_via_xml_show_from
|
||||
|
||||
def test_create_todo_with_show_from
|
||||
old_count = @user.todos.count
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>Call Warren Buffet to find out how much he makes per day</description>
|
||||
<project_id>#{projects(:attendrailsconf).id}</project_id>
|
||||
<context_id>#{contexts(:office_otheruser).id}</context_id>
|
||||
<context_id>#{contexts(:office).id}</context_id>
|
||||
<project_id>#{projects(:timemachine).id}</project_id>
|
||||
<show-from type=\"datetime\">#{1.week.from_now.xmlschema}</show-from>
|
||||
</todo>"
|
||||
|
||||
|
|
@ -50,22 +50,169 @@ class TodoXmlApiTest < ActionController::IntegrationTest
|
|||
assert_equal @user.todos.count, old_count + 1
|
||||
end
|
||||
|
||||
def test_post_create_todo_with_multiple_dependencies
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>this will succeed 2.0</description>
|
||||
<context_id>#{contexts(:office).id}</context_id>
|
||||
<project_id>#{projects(:timemachine).id}</project_id>
|
||||
<predecessor_dependencies>
|
||||
<predecessor>5</predecessor>
|
||||
<predecessor>6</predecessor>
|
||||
</predecessor_dependencies>
|
||||
</todo>"
|
||||
|
||||
assert_response :success
|
||||
todo = @user.todos.find_by_description("this will succeed 2.0")
|
||||
assert_not_nil todo
|
||||
assert !todo.uncompleted_predecessors.empty?
|
||||
end
|
||||
|
||||
def test_post_create_todo_with_single_dependency
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>this will succeed 2.1</description>
|
||||
<context_id>#{contexts(:office).id}</context_id>
|
||||
<project_id>#{projects(:timemachine).id}</project_id>
|
||||
<predecessor_dependencies>
|
||||
<predecessor>6</predecessor>
|
||||
</predecessor_dependencies>
|
||||
</todo>"
|
||||
|
||||
assert_response :success
|
||||
todo = @user.todos.find_by_description("this will succeed 2.1")
|
||||
assert_not_nil todo
|
||||
assert !todo.uncompleted_predecessors.empty?
|
||||
end
|
||||
|
||||
def test_post_create_todo_with_multiple_tags
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>this will succeed 3</description>
|
||||
<context_id>#{contexts(:office).id}</context_id>
|
||||
<project_id>#{projects(:timemachine).id}</project_id>
|
||||
<tags>
|
||||
<tag><name>starred</name></tag>
|
||||
<tag><name>starred1</name></tag>
|
||||
<tag><name>starred2</name></tag>
|
||||
</tags>
|
||||
</todo>"
|
||||
|
||||
assert_response :success
|
||||
todo = @user.todos.find_by_description("this will succeed 3")
|
||||
assert_not_nil todo
|
||||
assert_equal "starred, starred1, starred2", todo.tag_list
|
||||
assert todo.starred?
|
||||
end
|
||||
|
||||
def test_post_create_todo_with_single_tag
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>this will succeed 3.1</description>
|
||||
<context_id>#{contexts(:office).id}</context_id>
|
||||
<project_id>#{projects(:timemachine).id}</project_id>
|
||||
<tags>
|
||||
<tag><name>tracks</name></tag>
|
||||
</tags>
|
||||
</todo>"
|
||||
|
||||
assert_response :success
|
||||
todo = @user.todos.find_by_description("this will succeed 3.1")
|
||||
assert_not_nil todo
|
||||
assert_equal "tracks", todo.tag_list
|
||||
end
|
||||
|
||||
def test_post_create_todo_with_new_context
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>this will succeed 4</description>
|
||||
<project_id>#{projects(:timemachine).id}</project_id>
|
||||
<context>
|
||||
<name>@SomeNewContext</name>
|
||||
</context>
|
||||
</todo>"
|
||||
|
||||
assert_response :success
|
||||
todo = @user.todos.find_by_description("this will succeed 4")
|
||||
assert_not_nil todo
|
||||
assert_not_nil todo.context
|
||||
assert_equal todo.context.name, "@SomeNewContext"
|
||||
end
|
||||
|
||||
def test_post_create_todo_with_name_of_existing_context
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>this will succeed 4</description>
|
||||
<project_id>#{projects(:timemachine).id}</project_id>
|
||||
<context>
|
||||
<name>#{contexts(:office).name}</name>
|
||||
</context>
|
||||
</todo>"
|
||||
|
||||
assert_response :success
|
||||
todo = @user.todos.find_by_description("this will succeed 4")
|
||||
assert_not_nil todo
|
||||
assert_not_nil todo.context
|
||||
assert_equal contexts(:office).name, todo.context.name
|
||||
end
|
||||
|
||||
|
||||
def test_post_create_todo_with_new_project
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>this will succeed 5</description>
|
||||
<context_id>#{contexts(:office).id}</context_id>
|
||||
<project>
|
||||
<name>Make even more money</name>
|
||||
</project>
|
||||
</todo>"
|
||||
|
||||
assert_response :success
|
||||
todo = @user.todos.find_by_description("this will succeed 5")
|
||||
assert_not_nil todo
|
||||
assert_not_nil todo.project
|
||||
assert_equal todo.project.name, "Make even more money"
|
||||
end
|
||||
|
||||
def test_post_create_todo_with_name_of_existing_project
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>this will succeed 5</description>
|
||||
<context_id>#{contexts(:office).id}</context_id>
|
||||
<project>
|
||||
<name>#{projects(:timemachine).name}</name>
|
||||
</project>
|
||||
</todo>"
|
||||
|
||||
assert_response :success
|
||||
todo = @user.todos.find_by_description("this will succeed 5")
|
||||
assert_not_nil todo
|
||||
assert_not_nil todo.project
|
||||
assert_equal projects(:timemachine).name, todo.project.name
|
||||
assert 1, @user.projects.all(:conditions => ["projects.name = ?", projects(:timemachine).name]).count # no duplication of project
|
||||
end
|
||||
|
||||
def test_post_create_todo_with_wrong_project_and_context_id
|
||||
authenticated_post_xml_to_todo_create "<todo><description>this will fail</description><context_id type='integer'>-16</context_id><project_id type='integer'>-11</project_id></todo>"
|
||||
authenticated_post_xml_to_todo_create "
|
||||
<todo>
|
||||
<description>this will fail</description>
|
||||
<context_id type='integer'>-16</context_id>
|
||||
<project_id type='integer'>-11</project_id>
|
||||
</todo>"
|
||||
assert_response 422
|
||||
assert_xml_select 'errors' do
|
||||
assert_select 'error', 2
|
||||
end
|
||||
end
|
||||
|
||||
def test_fails_with_401_if_not_authorized_user
|
||||
|
||||
def test_fails_with_401_if_not_authorized_user
|
||||
authenticated_post_xml_to_todo_create '', 'nobody', 'nohow'
|
||||
assert_response 401
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authenticated_post_xml_to_todo_create(postdata = @@valid_postdata, user = users(:other_user).login, password = 'sesame')
|
||||
def authenticated_post_xml_to_todo_create(postdata = @@valid_postdata, user = @user.login, password = @password)
|
||||
authenticated_post_xml "/todos", user, password, postdata
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue