Merge branch 'master' of https://github.com/TracksApp/tracks into upstream

This commit is contained in:
Hans de Graaff 2011-10-23 06:59:47 +02:00
commit 92c7700639
2839 changed files with 10605 additions and 363513 deletions

2
.gitignore vendored
View file

@ -2,6 +2,7 @@
*.tmproj
.dotest
/.emacs-project
/.redcar
config/database.yml
config/site.yml
config/deploy.rb
@ -20,3 +21,4 @@ public/javascripts/tracks-cached.js
public/stylesheets/tracks-cached.css
.idea
.rvmrc
.yardoc

3
.yardopts Normal file
View file

@ -0,0 +1,3 @@
--title "Tracks Documentation"
--charset utf-8
--markup="textile"

56
Gemfile Normal file
View file

@ -0,0 +1,56 @@
source :gemcutter
source "http://gems.github.com/"
gem "rake", "~>0.8.7"
gem "rails", "~>2.3.12"
gem "highline", "~>1.5.0"
gem "RedCloth", "4.2.8"
gem "sanitize", "~>1.2.1"
gem "rack", "1.1.0"
gem "will_paginate", "~> 2.3.15"
gem "has_many_polymorphs", "~> 2.13"
gem "acts_as_list", "~>0.1.4"
gem "aasm", "~>2.2.0"
gem "rubyjedi-actionwebservice", :require => "actionwebservice"
gem "rubycas-client", "~>2.2.1"
gem "ruby-openid", :require => "openid"
gem "sqlite3"
gem 'bcrypt-ruby', '~> 2.1.4'
gem 'htmlentities', '~> 4.3.0'
gem "mail"
if RUBY_VERSION.to_f >= 1.9
gem "soap4r-ruby1.9"
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"
gem "mongrel", "1.2.0.pre2"
else
gem "ruby-debug"
gem "mongrel"
end
gem "yard"
end
group :test do
gem "test-unit", "1.2.3"
gem "flexmock"
gem "ZenTest", ">=4.0.0"
gem "hpricot"
gem "hoe"
gem "rspec-rails", "~>1.3.3"
gem "thoughtbot-factory_girl"
gem 'memory_test_fix', '~>0.1.3'
end
group :selenium do
gem "selenium-client"
end

148
Gemfile.lock Normal file
View file

@ -0,0 +1,148 @@
GEM
remote: http://rubygems.org/
remote: http://gems.github.com/
specs:
RedCloth (4.2.8)
ZenTest (4.6.1)
aasm (2.2.0)
actionmailer (2.3.14)
actionpack (= 2.3.14)
actionpack (2.3.14)
activesupport (= 2.3.14)
rack (~> 1.1.0)
activerecord (2.3.14)
activesupport (= 2.3.14)
activeresource (2.3.14)
activesupport (= 2.3.14)
activesupport (2.3.14)
acts_as_list (0.1.4)
bcrypt-ruby (2.1.4)
builder (3.0.0)
cgi_multipart_eof_fix (2.5.0)
columnize (0.3.4)
cucumber (1.0.2)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
gherkin (~> 2.4.5)
json (>= 1.4.6)
term-ansicolor (>= 1.0.5)
cucumber-rails (0.3.2)
cucumber (>= 0.8.0)
daemons (1.1.4)
database_cleaner (0.6.7)
diff-lcs (1.1.2)
fastthread (1.0.7)
flexmock (0.9.0)
gem_plugin (0.2.3)
gherkin (2.4.11)
json (>= 1.4.6)
has_many_polymorphs (2.13)
activerecord
highline (1.5.2)
hoe (2.12.0)
rake (~> 0.8)
hpricot (0.8.4)
htmlentities (4.3.0)
httpclient (2.2.1)
i18n (0.6.0)
json (1.5.3)
linecache (0.46)
rbx-require-relative (> 0.0.4)
mail (2.3.0)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
memory_test_fix (0.1.3)
mime-types (1.16)
mongrel (1.1.5)
cgi_multipart_eof_fix (>= 2.4)
daemons (>= 1.0.3)
fastthread (>= 1.0.1)
gem_plugin (>= 0.2.3)
nokogiri (1.4.7)
polyglot (0.3.2)
rack (1.1.0)
rack-test (0.6.1)
rack (>= 1.0)
rails (2.3.14)
actionmailer (= 2.3.14)
actionpack (= 2.3.14)
activerecord (= 2.3.14)
activeresource (= 2.3.14)
activesupport (= 2.3.14)
rake (>= 0.8.3)
rake (0.8.7)
rbx-require-relative (0.0.5)
rspec (1.3.2)
rspec-rails (1.3.4)
rack (>= 1.0.0)
rspec (~> 1.3.1)
ruby-debug (0.10.4)
columnize (>= 0.1)
ruby-debug-base (~> 0.10.4.0)
ruby-debug-base (0.10.4)
linecache (>= 0.3)
ruby-openid (2.1.8)
rubycas-client (2.2.1)
activesupport
rubyjedi-actionwebservice (2.3.5.20100714122544)
actionpack (~> 2.3.0)
activerecord (~> 2.3.0)
activesupport (~> 2.3.0)
sanitize (1.2.1)
nokogiri (~> 1.4.1)
selenium-client (1.2.18)
soap4r (1.5.8)
httpclient (>= 2.1.1)
sqlite3 (1.3.4)
term-ansicolor (1.0.6)
test-unit (1.2.3)
hoe (>= 1.5.1)
thoughtbot-factory_girl (1.2.2)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
webrat (0.7.3)
nokogiri (>= 1.2.0)
rack (>= 1.0)
rack-test (>= 0.5.3)
will_paginate (2.3.16)
yard (0.7.3)
PLATFORMS
ruby
DEPENDENCIES
RedCloth (= 4.2.8)
ZenTest (>= 4.0.0)
aasm (~> 2.2.0)
acts_as_list (~> 0.1.4)
bcrypt-ruby (~> 2.1.4)
cucumber-rails (~> 0.3.0)
database_cleaner (>= 0.5.0)
flexmock
has_many_polymorphs (~> 2.13)
highline (~> 1.5.0)
hoe
hpricot
htmlentities (~> 4.3.0)
mail
memory_test_fix (~> 0.1.3)
mongrel
rack (= 1.1.0)
rails (~> 2.3.12)
rake (~> 0.8.7)
rspec-rails (~> 1.3.3)
ruby-debug
ruby-openid
rubycas-client (~> 2.2.1)
rubyjedi-actionwebservice
sanitize (~> 1.2.1)
selenium-client
soap4r (~> 1.5.8)
sqlite3
test-unit (= 1.2.3)
thoughtbot-factory_girl
webrat (>= 0.7.0)
will_paginate (~> 2.3.15)
yard

23
README
View file

@ -1,24 +1,31 @@
# Tracks: a GTD(TM) web application, built with Ruby on Rails
**IMPORTANT: Tracks is moving to a GitHub Organization to make it easier to continue administering the project. Development will soon cease at bsag/tracks and move to TracksApp/tracks. If you are currently pulling from bsag/tracks, please pull from TracksApp instead.**
`git clone git://github.com/TracksApp/tracks.git`
**The new home for Tracks is https://github.com/TracksApp/tracks**
* Project homepage: http://getontracks.org/
* Manual: http://bsag.github.com/tracks/
* Source at GitHub: http://github.com/bsag/tracks/
* Manual: http://TracksApp.github.com/tracks/
* Source at GitHub: http://github.com/TracksApp/tracks/
* Assembla space (for bug reports and feature requests): http://www.assembla.com/spaces/tracks-tickets/tickets
* Wiki (community contributed information): http://getontracks.org/wiki/
* Wiki (community contributed information): https://github.com/TracksApp/tracks/wiki
* Forum: http://getontracks.org/forums/
* Mailing list: http://lists.rousette.org.uk/mailman/listinfo/tracks-discuss
* Original developer: bsag (http://www.rousette.org.uk/)
* Contributors: http://getontracks.org/wiki/Contributors
* Version: 2.0devel
* Copyright: (cc) 2004-2010 rousette.org.uk.
* Contributors: https://github.com/TracksApp/tracks/wiki/Contributors
* Version: 2.1devel
* 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.
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.
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.
Enjoy being productive!
Enjoy being productive!

View file

@ -13,4 +13,4 @@ begin
require 'test/rails/rake_tasks'
rescue LoadError => e
#It's ok if you don't have ZenTest installed if you're not a developer
end
end

View file

@ -15,8 +15,8 @@ class TodoApi < ActionWebService::API::Base
:expects => [{:username => :string}, {:token => :string}],
:returns => [[Context]]
api_method :list_projects,
:expects => [{:username => :string}, {:token => :string}],
:returns => [[Project]]
api_method :list_projects,
:expects => [{:username => :string}, {:token => :string}],
:returns => [[Project]]
end

View file

@ -15,7 +15,8 @@ class ApplicationController < ActionController::Base
layout proc{ |controller| controller.mobile? ? "mobile" : "standard" }
exempt_from_layout /\.js\.erb$/
before_filter :check_for_deprecated_password_hash
before_filter :set_session_expiration
before_filter :set_time_zone
before_filter :set_zindex_counter
@ -23,12 +24,12 @@ class ApplicationController < ActionController::Base
prepend_before_filter :login_required
prepend_before_filter :enable_mobile_content_negotiation
after_filter :set_charset
# By default, sets the charset to UTF-8 if it isn't already set
def set_charset
headers["Content-Type"] ||= "text/html; charset=UTF-8"
headers["Content-Type"] ||= "text/html; charset=UTF-8"
end
def set_locale
locale = params[:locale] # specifying a locale in the request takes precedence
locale = locale || prefs.locale unless current_user.nil? # otherwise, the locale of the currently logged in user takes over
@ -36,7 +37,7 @@ class ApplicationController < ActionController::Base
I18n.locale = locale.nil? ? I18n.default_locale : (I18n::available_locales.include?(locale.to_sym) ? locale : I18n.default_locale)
logger.debug("Selected '#{I18n.locale}' as locale")
end
def set_session_expiration
# http://wiki.rubyonrails.com/rails/show/HowtoChangeSessionOptions
unless session == nil
@ -58,11 +59,20 @@ class ApplicationController < ActionController::Base
end
end
end
# Redirects to change_password_user_path if the current user uses a
# deprecated password hashing algorithm.
def check_for_deprecated_password_hash
if current_user and current_user.uses_deprecated_password?
notify :warning, t('users.you_have_to_reset_your_password')
redirect_to change_password_user_path current_user
end
end
def render_failure message, status = 404
render :text => message, :status => status
end
# def rescue_action(exception)
# log_error(exception) if logger
# respond_to do |format|
@ -74,21 +84,24 @@ class ApplicationController < ActionController::Base
# format.xml { render :text => 'An error occurred on the server.' + $! }
# end
# end
# Returns a count of next actions in the given context or project The result
# is count and a string descriptor, correctly pluralised if there are no
# actions or multiple actions
#
def count_undone_todos_phrase(todos_parent, string="actions")
count = count_undone_todos(todos_parent)
if count == 1
word = string.singularize
deferred_count = count_deferred_todos(todos_parent)
if count == 0 && deferred_count > 0
word = deferred_count == 1 ? string.singularize : string.pluralize
word = "deferred&nbsp;" + word
deferred_count.to_s + "&nbsp;" + word
else
word = string.pluralize
word = count == 1 ? string.singularize : string.pluralize
count.to_s + "&nbsp;" + word
end
return count.to_s + "&nbsp;" + word
end
def count_undone_todos(todos_parent)
if todos_parent.nil?
count = 0
@ -100,6 +113,14 @@ class ApplicationController < ActionController::Base
count || 0
end
def count_deferred_todos(todos_parent)
if todos_parent.nil?
count = 0
else
count = todos_parent.todos.deferred.count
end
end
# Convert a date object to the format specified in the user's preferences in
# config/settings.yml
#
@ -156,11 +177,11 @@ 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})
# set dates
todo.recurring_todo_id = rt.id
todo.due = rt.get_due_date(date)
show_from_date = rt.get_show_from_date(date)
if show_from_date.nil?
todo.show_from=nil
@ -168,20 +189,20 @@ class ApplicationController < ActionController::Base
# make sure that show_from is not in the past
todo.show_from = show_from_date < Time.zone.now ? nil : show_from_date
end
saved = todo.save
if saved
todo.tag_with(rt.tag_list)
todo.tags.reload
todo.tags.reload
end
# increate number of occurences created from recurring todo
rt.inc_occurences
# mark recurring todo complete if there are no next actions left
checkdate = todo.due.nil? ? todo.show_from : todo.due
rt.toggle_completion! unless rt.has_next_todo(checkdate)
return saved ? todo : nil
end
@ -192,21 +213,21 @@ class ApplicationController < ActionController::Base
end
protected
def admin_login_required
unless User.find_by_id_and_is_admin(session['user_id'], true)
render :text => t('errors.user_unauthorized'), :status => 401
return false
end
end
def redirect_back_or_home
respond_to do |format|
format.html { redirect_back_or_default home_url }
format.m { redirect_back_or_default mobile_url }
end
end
def boolean_param(param_name)
return false if param_name.blank?
s = params[param_name]
@ -214,11 +235,11 @@ class ApplicationController < ActionController::Base
return true if s == true || s =~ /^true$/i
raise ArgumentError.new("invalid value for Boolean: \"#{s}\"")
end
def self.openid_enabled?
Tracks::Config.openid_enabled?
end
def openid_enabled?
self.class.openid_enabled?
end
@ -239,12 +260,30 @@ class ApplicationController < ActionController::Base
self.class.prefered_auth?
end
def get_done_today(completed_todos, includes = {:include => Todo::DEFAULT_INCLUDES})
start_of_this_day = Time.zone.now.beginning_of_day
completed_todos.completed_after(start_of_this_day).all(includes)
end
def get_done_this_week(completed_todos, includes = {:include => Todo::DEFAULT_INCLUDES})
start_of_this_week = Time.zone.now.beginning_of_week
start_of_this_day = Time.zone.now.beginning_of_day
completed_todos.completed_after(start_of_this_week).completed_before(start_of_this_day).all(includes)
end
def get_done_this_month(completed_todos, includes = {:include => Todo::DEFAULT_INCLUDES})
start_of_this_month = Time.zone.now.beginning_of_month
start_of_this_week = Time.zone.now.beginning_of_week
completed_todos.completed_after(start_of_this_month).completed_before(start_of_this_week).all(includes)
end
private
def parse_date_per_user_prefs( s )
prefs.parse_date(s)
end
def init_data_for_sidebar
@completed_projects = current_user.projects.completed
@hidden_projects = current_user.projects.hidden
@ -258,19 +297,19 @@ class ApplicationController < ActionController::Base
init_project_hidden_todo_counts(['project'])
end
end
def init_not_done_counts(parents = ['project','context'])
parents.each do |parent|
eval("@#{parent}_not_done_counts = @#{parent}_not_done_counts || current_user.todos.active.count(:group => :#{parent}_id)")
end
end
def init_project_hidden_todo_counts(parents = ['project','context'])
parents.each do |parent|
eval("@#{parent}_project_hidden_todo_counts = @#{parent}_project_hidden_todo_counts || current_user.todos.count(:conditions => ['state = ? or state = ?', 'project_hidden', 'active'], :group => :#{parent}_id)")
end
end
end
# Set the contents of the flash message from a controller Usage: notify
# :warning, "This is the message" Sets the flash of type 'warning' to "This is
# the message"
@ -278,7 +317,7 @@ class ApplicationController < ActionController::Base
flash[type] = message
logger.error("ERROR: #{message}") if type == :error
end
def set_time_zone
Time.zone = current_user.prefs.time_zone if logged_in?
end
@ -287,5 +326,5 @@ class ApplicationController < ActionController::Base
# this counter can be used to handle the IE z-index bug
@z_index_counter = 500
end
end

View file

@ -1,6 +1,7 @@
class CannotAccessContext < RuntimeError; end
class BackendController < ApplicationController
acts_as_web_service
wsdl_service_name 'Backend'
web_service_api TodoApi
web_service_scaffold :invoke

View file

@ -11,7 +11,7 @@ class ContextsController < ApplicationController
def index
# #true is passed here to force an immediate load so that size and empty?
# checks later don't result in separate SQL queries
@active_contexts = current_user.contexts.active(true)
@active_contexts = current_user.contexts.active(true)
@hidden_contexts = current_user.contexts.hidden(true)
@new_context = current_user.contexts.build
@ -19,8 +19,8 @@ class ContextsController < ApplicationController
@all_contexts = @active_contexts + @hidden_contexts
@count = @all_contexts.size
init_not_done_counts(['context'])
respond_to do |format|
format.html &render_contexts_html
format.m &render_contexts_mobile
@ -34,10 +34,10 @@ class ContextsController < ApplicationController
format.autocomplete { render :text => for_autocomplete(@active_contexts + @hidden_contexts, params[:term])}
end
end
def show
@contexts = current_user.contexts(true)
if (@context.nil?)
if @context.nil?
respond_to do |format|
format.html { render :text => 'Context not found', :status => 404 }
format.xml { render :xml => '<error>Context not found</error>', :status => 404 }
@ -51,7 +51,7 @@ class ContextsController < ApplicationController
end
end
end
# Example XML usage: curl -H 'Accept: application/xml' -H 'Content-Type:
# application/xml'
# -u username:password
@ -86,14 +86,14 @@ class ContextsController < ApplicationController
end
end
end
# Edit the details of the context
#
def update
params['context'] ||= {}
success_text = if params['field'] == 'name' && params['value']
params['context']['id'] = params['id']
params['context']['name'] = params['value']
params['context']['id'] = params['id']
params['context']['name'] = params['value']
end
@original_context_hidden = @context.hidden?
@ -110,7 +110,7 @@ class ContextsController < ApplicationController
end
# TODO is this param ever used? is this dead code?
elsif boolean_param('update_context_name')
@contexts = current_user.projects
render :template => 'contexts/update_context_name.js.rjs'
@ -121,6 +121,13 @@ class ContextsController < ApplicationController
else
respond_to do |format|
format.js
format.xml {
if @saved
render :xml => @context.to_xml( :except => :user_id )
else
render :text => "Error on update: #{@context.errors.full_messages.inject("") {|v, e| v + e + " " }}", :status => 409
end
}
end
end
end
@ -138,7 +145,7 @@ class ContextsController < ApplicationController
def destroy
# make sure the deleted recurring patterns are removed from associated todos
@context.recurring_todos.each { |rt| rt.clear_todos_association } unless @context.recurring_todos.nil?
@context.destroy
respond_to do |format|
format.js do
@ -159,7 +166,32 @@ class ContextsController < ApplicationController
notify :error, $!
redirect_to :action => 'index'
end
def done_todos
@source_view = 'context'
@context = current_user.contexts.find(params[:id])
@page_title = t('contexts.completed_tasks_title', :context_name => @context.name)
completed_todos = @context.todos.completed
@done_today = get_done_today(completed_todos)
@done_this_week = get_done_this_week(completed_todos)
@done_this_month = get_done_this_month(completed_todos)
@count = @done_today.size + @done_this_week.size + @done_this_month.size
render :template => 'todos/done'
end
def all_done_todos
@source_view = 'context'
@context = current_user.contexts.find(params[:id])
@page_title = t('contexts.all_completed_tasks_title', :context_name => @context.name)
@done = @context.todos.completed.paginate :page => params[:page], :per_page => 20, :order => 'completed_at DESC', :include => Todo::DEFAULT_INCLUDES
@count = @done.size
render :template => 'todos/all_done'
end
protected
def update_state_counts
@ -179,23 +211,23 @@ class ContextsController < ApplicationController
render
end
end
def render_contexts_mobile
lambda do
@page_title = "TRACKS::List Contexts"
@active_contexts = current_user.contexts.active
@hidden_contexts = current_user.contexts.hidden
@down_count = @active_contexts.size + @hidden_contexts.size
@down_count = @active_contexts.size + @hidden_contexts.size
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
render :action => 'index_mobile'
end
end
def render_context_mobile
lambda do
@page_title = "TRACKS::List actions in "+@context.name
@not_done = @not_done_todos.select {|t| t.context_id == @context.id }
@down_count = @not_done.size
@not_done = @not_done_todos.select {|t| t.context_id == @context.id }
@down_count = @not_done.size
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
@mobile_from_context = @context.id
render :action => 'mobile_show_context'
@ -205,18 +237,18 @@ class ContextsController < ApplicationController
def render_contexts_rss_feed
lambda do
render_rss_feed_for current_user.contexts.all, :feed => feed_options,
:item => { :description => lambda { |c| c.summary(count_undone_todos_phrase(c)) } }
:item => { :description => lambda { |c| @template.summary(c, count_undone_todos_phrase(c)) } }
end
end
def render_contexts_atom_feed
lambda do
render_atom_feed_for current_user.contexts.all, :feed => feed_options,
:item => { :description => lambda { |c| c.summary(count_undone_todos_phrase(c)) },
:item => { :description => lambda { |c| @template.summary(c, count_undone_todos_phrase(c)) },
:author => lambda { |c| nil } }
end
end
def feed_options
Context.feed_options(current_user)
end
@ -226,7 +258,7 @@ class ContextsController < ApplicationController
rescue
@context = nil
end
def init
@source_view = params['_source_view'] || 'context'
init_data_for_sidebar
@ -235,21 +267,19 @@ class ContextsController < ApplicationController
def init_todos
set_context_from_params
unless @context.nil?
@context.todos.send :with_scope, :find => { :include => [:project, :tags] } do
@context.todos.send :with_scope, :find => { :include => Todo::DEFAULT_INCLUDES } do
@done = @context.done_todos
end
@max_completed = current_user.prefs.show_number_completed
# @not_done_todos = @context.not_done_todos TODO: Temporarily doing this
# search manually until I can work out a way to do the same thing using
# not_done_todos acts_as_todo_container method Hides actions in hidden
# projects from context.
@not_done_todos = @context.todos.find(
:all,
:conditions => ['todos.state = ? AND (todos.project_id IS ? OR projects.state = ?)', 'active', nil, 'active'],
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
:include => [:project, :tags])
@not_done_todos = @context.todos.active(
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
:include => Todo::DEFAULT_INCLUDES)
@projects = current_user.projects

View file

@ -1,6 +1,7 @@
class IntegrationsController < ApplicationController
skip_before_filter :login_required, :only => [:search_plugin, :google_gadget]
require 'mail'
skip_before_filter :login_required, :only => [:cloudmailin, :search_plugin, :google_gadget]
def index
@page_title = 'TRACKS::Integrations'
@ -26,14 +27,59 @@ class IntegrationsController < ApplicationController
end
def search_plugin
@icon_data = [File.open(RAILS_ROOT + '/public/images/done.png').read].
pack('m').gsub(/\n/, '')
render :layout => false
@icon_data = [File.open(RAILS_ROOT + '/public/images/done.png').read].
pack('m').gsub(/\n/, '')
render :layout => false
end
def google_gadget
render :layout => false, :content_type => Mime::XML
end
def cloudmailin
# verify cloudmailin signature
provided = request.request_parameters.delete(:signature)
signature = Digest::MD5.hexdigest(request.request_parameters.sort{|a,b| a[0].to_s <=> b[0].to_s}.map{|k,v| v}.join + SITE_CONFIG['cloudmailin'])
# if signature does not match, return 403
if provided != signature
render :text => "Message signature verification failed.", :status => 403
return false
end
# parse message
message = Mail.new(params[:message])
# find user
user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", message.from])
if user.nil?
render :text => "No user found", :status => 404
return false
end
# load user settings
context = user.prefs.sms_context
# prepare body
if message.body.multipart?
body = message.body.preamble
else
body = message.body.to_s
end
# parse mail
if message.subject.to_s.empty?
description = body
notes = nil
else
description = message.subject.to_s
notes = body
end
# create todo
todo = Todo.from_rich_message(user, context.id, description, notes)
todo.save!
render :text => 'success', :status => 200
end
end

View file

@ -84,6 +84,12 @@ class LoginController < ApplicationController
def expire_session
# this is a hack to enable cucumber to expire a session by calling this
# method. The method will be unavailable for production environment
@user.forget_me if logged_in?
cookies.delete :auth_token
session['user_id'] = nil
reset_session
unless Rails.env.production?
session['expiry_time'] = Time.now
respond_to do |format|

View file

@ -1,23 +1,31 @@
class PreferencesController < ApplicationController
def index
@page_title = t('preferences.page_title')
@prefs = current_user.prefs
@user = current_user
end
def edit
@page_title = t('preferences.page_title_edit')
@prefs = current_user.prefs
end
def update
@prefs = current_user.prefs
@user = current_user
user_updated = current_user.update_attributes(params['user'])
prefs_updated = current_user.preference.update_attributes(params['prefs'])
if user_updated && prefs_updated
if (user_updated && prefs_updated)
notify :notice, "Preferences updated"
redirect_to :action => 'index'
else
render :action => 'edit'
msg = "Preferences could not be updated: "
msg += "User model errors; " unless user_updated
msg += "Prefs model errors; " unless prefs_updated
notify :warning, msg
render 'index'
end
end
def render_date_format
format = params[:date_format]
render :text => l(Date.today, :format => format)
end
end

View file

@ -2,7 +2,7 @@ class ProjectsController < ApplicationController
helper :application, :todos, :notes
before_filter :set_source_view
before_filter :set_project_from_params, :only => [:update, :destroy, :show, :edit]
before_filter :set_project_from_params, :only => [:update, :destroy, :show, :edit, :set_reviewed]
before_filter :default_context_filter, :only => [:create, :update]
skip_before_filter :login_required, :only => [:index]
prepend_before_filter :login_or_feed_token_required, :only => [:index]
@ -12,15 +12,15 @@ class ProjectsController < ApplicationController
@new_project = current_user.projects.build
if params[:projects_and_actions]
projects_and_actions
else
else
@contexts = current_user.contexts.all
init_not_done_counts(['project'])
init_project_hidden_todo_counts(['project'])
if params[:only_active_with_no_next_actions]
@projects = current_user.projects.active.select { |p| count_undone_todos(p) == 0 }
@projects = current_user.projects.active.select { |p| count_undone_todos(p) == 0 }
else
@projects = current_user.projects.all
end
init_project_hidden_todo_counts(['project'])
respond_to do |format|
format.html &render_projects_html
format.m &render_projects_mobile
@ -33,10 +33,53 @@ class ProjectsController < ApplicationController
end
end
def review
@page_title = t('projects.list_reviews')
@projects = current_user.projects.all
@contexts = current_user.contexts.all
@projects_to_review = current_user.projects.select {|p| p.needs_review?(current_user)}
@stalled_projects = current_user.projects.select {|p| p.stalled?}
@blocked_projects = current_user.projects.select {|p| p.blocked?}
@current_projects = current_user.projects.uncompleted.select {|p| not(p.needs_review?(current_user))}
init_not_done_counts(['project'])
init_project_hidden_todo_counts(['project'])
current_user.projects.cache_note_counts
@page_title = t('projects.list_reviews')
@count = @projects_to_review.count + @blocked_projects.count + @stalled_projects.count + @current_projects.count
@no_projects = current_user.projects.empty?
@new_project = current_user.projects.build
end
def done
@source_view = params['_source_view'] || 'project_list'
@page_title = t('projects.list_completed_projects')
page = params[:page] || 1
projects_per_page = 20
@projects = current_user.projects.completed.paginate :page => page, :per_page => projects_per_page
@count = @projects.count
@total = current_user.projects.completed.count
@no_projects = @projects.empty?
@range_low = (page.to_i-1) * projects_per_page + 1
@range_high = @range_low + @projects.size - 1
init_not_done_counts(['project'])
end
def set_reviewed
@project.last_reviewed = Time.zone.now
@project.save
redirect_to :action => 'show'
end
def projects_and_actions
@projects = current_user.projects.active
respond_to do |format|
format.text {
format.text {
render :action => 'index_text_projects_and_actions', :layout => false, :content_type => Mime::TEXT
}
end
@ -46,12 +89,12 @@ class ProjectsController < ApplicationController
@max_completed = current_user.prefs.show_number_completed
init_data_for_sidebar unless mobile?
@page_title = t('projects.page_title', :project => @project.name)
@not_done = @project.todos.active_or_hidden(:include => [:tags, :context, :predecessors])
@deferred = @project.deferred_todos(:include => [:tags, :context, :predecessors])
@pending = @project.pending_todos(:include => [:tags, :context, :predecessors])
@not_done = @project.todos.active_or_hidden(:include => Todo::DEFAULT_INCLUDES)
@deferred = @project.todos.deferred(:include => Todo::DEFAULT_INCLUDES)
@pending = @project.todos.pending(:include => Todo::DEFAULT_INCLUDES)
@done = @project.todos.find_in_state(:all, :completed,
:order => "todos.completed_at DESC", :limit => current_user.prefs.show_number_completed, :include => [:context, :tags, :predecessors])
:order => "todos.completed_at DESC", :limit => current_user.prefs.show_number_completed, :include => Todo::DEFAULT_INCLUDES)
@count = @not_done.size
@down_count = @count + @deferred.size + @pending.size
@ -64,10 +107,17 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html
format.m &render_project_mobile
format.xml { render :xml => @project.to_xml( :except => :user_id ) }
format.xml {
render :xml => @project.to_xml(:except => :user_id) { |xml|
xml.not_done { @not_done.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
xml.deferred { @deferred.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
xml.pending { @pending.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
xml.done { @done.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
}
}
end
end
# Example XML usage: curl -H 'Accept: application/xml' -H 'Content-Type:
# application/xml'
# -u username:password
@ -111,6 +161,8 @@ class ProjectsController < ApplicationController
# Edit the details of the project
#
def update
template = ""
params['project'] ||= {}
if params['project']['state']
@new_state = params['project']['state']
@ -118,8 +170,8 @@ class ProjectsController < ApplicationController
params['project'].delete('state')
end
success_text = if params['field'] == 'name' && params['value']
params['project']['id'] = params['id']
params['project']['name'] = params['value']
params['project']['id'] = params['id']
params['project']['name'] = params['value']
end
@project.attributes = params['project']
@ -137,43 +189,50 @@ class ProjectsController < ApplicationController
@contexts = current_user.contexts
update_state_counts
init_data_for_sidebar
render :template => 'projects/update.js.erb'
return
template = 'projects/update.js.erb'
# TODO: are these params ever set? or is this dead code?
elsif boolean_param('update_status')
render :template => 'projects/update_status.js.rjs'
return
template = 'projects/update_status.js.rjs'
elsif boolean_param('update_default_context')
@initial_context_name = @project.default_context.name
render :template => 'projects/update_default_context.js.rjs'
return
@initial_context_name = @project.default_context.name
template = 'projects/update_default_context.js.rjs'
elsif boolean_param('update_default_tags')
render :template => 'projects/update_default_tags.js.rjs'
return
template = 'projects/update_default_tags.js.rjs'
elsif boolean_param('update_project_name')
@projects = current_user.projects
render :template => 'projects/update_project_name.js.rjs'
return
template = 'projects/update_project_name.js.rjs'
else
render :text => success_text || 'Success'
return
end
else
init_data_for_sidebar
render :template => 'projects/update.js.erb'
return
template = 'projects/update.js.erb'
end
render :template => 'projects/update.js.erb'
respond_to do |format|
format.js { render :template => template }
format.html { redirect_to :action => 'index'}
format.xml {
if @saved
render :xml => @project.to_xml( :except => :user_id )
else
render :text => "Error on update: #{@project.errors.full_messages.inject("") {|v, e| v + e + " " }}", :status => 409
end
}
end
end
def edit
respond_to do |format|
format.js
end
end
def destroy
@project.recurring_todos.each {|rt| rt.remove_from_project!}
@project.destroy
@ -186,7 +245,7 @@ class ProjectsController < ApplicationController
format.xml { render :text => "Deleted project #{@project.name}" }
end
end
def order
project_ids = params["container_project"]
@projects = current_user.projects.update_positions( project_ids )
@ -195,23 +254,46 @@ class ProjectsController < ApplicationController
notify :error, $!
redirect_to :action => 'index'
end
def alphabetize
@state = params['state']
@projects = current_user.projects.alphabetize(:state => @state) if @state
@contexts = current_user.contexts
init_not_done_counts(['project'])
init_project_hidden_todo_counts(['project']) if @state == 'hidden'
end
def actionize
@state = params['state']
@projects = current_user.projects.actionize(current_user.id, :state => @state) if @state
@projects = current_user.projects.actionize(:state => @state) if @state
@contexts = current_user.contexts
init_not_done_counts(['project'])
init_project_hidden_todo_counts(['project']) if @state == 'hidden'
end
def done_todos
@source_view = 'project'
@project = current_user.projects.find(params[:id])
@page_title = t('projects.completed_tasks_title', :project_name => @project.name)
completed_todos = @project.todos.completed
@done_today = get_done_today(completed_todos)
@done_this_week = get_done_this_week(completed_todos)
@done_this_month = get_done_this_month(completed_todos)
@count = @done_today.size + @done_this_week.size + @done_this_month.size
render :template => 'todos/done'
end
def all_done_todos
@source_view = 'project'
@project = current_user.projects.find(params[:id])
@page_title = t('projects.all_completed_tasks_title', :project_name => @project.name)
@done = @project.todos.completed.paginate :page => params[:page], :per_page => 20, :order => 'completed_at DESC', :include => Todo::DEFAULT_INCLUDES
@count = @done.size
render :template => 'todos/all_done'
end
protected
def update_state_counts
@ -229,7 +311,8 @@ class ProjectsController < ApplicationController
@count = current_user.projects.count
@active_projects = current_user.projects.active
@hidden_projects = current_user.projects.hidden
@completed_projects = current_user.projects.completed
@completed_projects = current_user.projects.completed.find(:all, :limit => 10)
@completed_count = current_user.projects.completed.count
@no_projects = current_user.projects.empty?
current_user.projects.cache_note_counts
@new_project = current_user.projects.build
@ -242,12 +325,12 @@ class ProjectsController < ApplicationController
@active_projects = current_user.projects.active
@hidden_projects = current_user.projects.hidden
@completed_projects = current_user.projects.completed
@down_count = @active_projects.size + @hidden_projects.size + @completed_projects.size
@down_count = @active_projects.size + @hidden_projects.size + @completed_projects.size
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
render :action => 'index_mobile'
end
end
def render_project_mobile
lambda do
if @project.default_context.nil?
@ -265,38 +348,37 @@ class ProjectsController < ApplicationController
lambda do
render_rss_feed_for @projects, :feed => feed_options,
:title => :name,
:item => { :description => lambda { |p| summary(p) } }
:item => { :description => lambda { |p| @template.summary(p) } }
end
end
def render_atom_feed
lambda do
render_atom_feed_for @projects, :feed => feed_options,
:item => { :description => lambda { |p| summary(p) },
:item => { :description => lambda { |p| @template.summary(p) },
:title => :name,
:author => lambda { |p| nil } }
end
end
def feed_options
Project.feed_options(current_user)
end
def render_text_feed
lambda do
init_project_hidden_todo_counts(['project'])
render :action => 'index', :layout => false, :content_type => Mime::TEXT
end
end
def set_project_from_params
@project = current_user.projects.find_by_params(params)
end
def set_source_view
@source_view = params['_source_view'] || 'project'
end
def default_context_filter
p = params['project']
p = params['request']['project'] if p.nil? && params['request']
@ -310,13 +392,4 @@ class ProjectsController < ApplicationController
end
end
def summary(project)
project_description = ''
project_description += sanitize(markdown( project.description )) unless project.description.blank?
project_description += "<p>#{count_undone_todos_phrase(p)}. "
project_description += t('projects.project_state', :state => project.state)
project_description += "</p>"
project_description
end
end

View file

@ -7,10 +7,10 @@ class RecurringTodosController < ApplicationController
def index
@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
@completed_recurring_todos = current_user.recurring_todos.completed.find(:all, :limit => 10)
@no_recurring_todos = @recurring_todos.size == 0
@no_completed_recurring_todos = @completed_recurring_todos.size == 0
@ -24,6 +24,17 @@ class RecurringTodosController < ApplicationController
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
@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
@range_high = @range_low + @completed_recurring_todos.size - 1
end
def edit
respond_to do |format|
@ -131,7 +142,6 @@ class RecurringTodosController < ApplicationController
end
def destroy
# remove all references to this recurring todo
@todos = @recurring_todo.todos
@number_of_todos = @todos.size
@ -176,7 +186,6 @@ class RecurringTodosController < ApplicationController
@completed_remaining = current_user.recurring_todos.completed.count
# from completed back to active -> check if there is an active todo
# current_user.todos.count(:all, {:conditions => ["state = ? AND recurring_todo_id = ?", 'active',params[:id]]})
@active_todos = @recurring_todo.todos.active.count
# create todo if there is no active todo belonging to the activated
# recurring_todo

View file

@ -1,12 +1,22 @@
class SearchController < ApplicationController
helper :todos, :application, :notes, :projects
helper :todos, :application, :notes, :projects
def results
@source_view = params['_source_view'] || 'search'
@page_title = "TRACKS::Search Results for #{params[:search]}"
terms = '%' + params[:search] + '%'
@found_todos = current_user.todos.find(:all, :conditions => ["todos.description LIKE ? OR todos.notes LIKE ?", terms, terms], :include => [:tags, :project, :context])
@found_not_complete_todos = current_user.todos.find(:all,
:conditions => ["(todos.description LIKE ? OR todos.notes LIKE ?) AND todos.completed_at IS NULL", terms, terms],
:include => Todo::DEFAULT_INCLUDES,
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC")
@found_complete_todos = current_user.todos.find(:all,
:conditions => ["(todos.description LIKE ? OR todos.notes LIKE ?) AND NOT (todos.completed_at IS NULL)", terms, terms],
:include => Todo::DEFAULT_INCLUDES,
:order => "todos.completed_at DESC")
@found_todos = @found_not_complete_todos + @found_complete_todos
@found_projects = current_user.projects.find(:all, :conditions => ["name LIKE ? OR description LIKE ?", terms, terms])
@found_notes = current_user.notes.find(:all, :conditions => ["body LIKE ?", terms])
@found_contexts = current_user.contexts.find(:all, :conditions => ["name LIKE ?", terms])
@ -18,17 +28,17 @@ class SearchController < ApplicationController
"LEFT JOIN todos ON taggings.taggable_id = todos.id "+
"WHERE todos.user_id=? "+
"AND tags.name LIKE ? ", current_user.id, terms])
@count = @found_todos.size + @found_projects.size + @found_notes.size + @found_contexts.size + @found_tags.size
init_not_done_counts
init_project_hidden_todo_counts
end
def index
@page_title = "TRACKS::Search"
@page_title = "TRACKS::Search"
end
def init
@source_view = params['_source_view'] || 'search'
end

View file

@ -1,6 +1,6 @@
class StatsController < ApplicationController
helper :todos
helper :todos, :projects, :recurring_todos
append_before_filter :init, :exclude => []
@ -643,6 +643,18 @@ class StatsController < ApplicationController
end
end
def done
@source_view = 'done'
init_not_done_counts
@done_recently = current_user.todos.completed.all(:limit => 10, :order => 'completed_at DESC', :include => Todo::DEFAULT_INCLUDES)
@last_completed_projects = current_user.projects.completed.all(:limit => 10, :order => 'completed_at DESC', :include => [:todos, :notes])
@last_completed_contexts = []
@last_completed_recurring_todos = current_user.recurring_todos.completed.all(:limit => 10, :order => 'completed_at DESC', :include => [:tags, :taggings])
#TODO: @last_completed_contexts = current_user.contexts.completed.all(:limit => 10, :order => 'completed_at DESC')
end
private
def get_unique_tags_of_user

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,12 @@
class UsersController < ApplicationController
before_filter :admin_login_required, :only => [ :index, :show, :destroy ]
skip_before_filter :login_required, :only => [ :new, :create ]
skip_before_filter :check_for_deprecated_password_hash,
:only => [ :change_password, :update_password ]
prepend_before_filter :login_optional, :only => [ :new, :create ]
# GET /users GET /users.xml
def index
@users = User.find(:all, :order => 'login')
respond_to do |format|
format.html do
@page_title = "TRACKS::Manage Users"
@ -15,10 +16,13 @@ class UsersController < ApplicationController
# we get returned here when signup is successful
store_location
end
format.xml { render :xml => @users.to_xml(:except => [ :password ]) }
format.xml do
@users = User.find(:all, :order => 'login')
render :xml => @users.to_xml(:except => [ :password ])
end
end
end
# GET /users/id GET /users/id.xml
def show
@user = User.find_by_id(params[:id])
@ -50,7 +54,7 @@ class UsersController < ApplicationController
end
render :layout => "login"
end
# Example usage: curl -H 'Accept: application/xml' -H 'Content-Type:
# application/xml'
# -u admin:up2n0g00d
@ -128,14 +132,14 @@ class UsersController < ApplicationController
return
end
end
end
end
# DELETE /users/id DELETE /users/id.xml
def destroy
@deleted_user = User.find_by_id(params[:id])
@saved = @deleted_user.destroy
@total_users = User.find(:all).size
respond_to do |format|
format.html do
if @saved
@ -149,14 +153,15 @@ class UsersController < ApplicationController
format.xml { head :ok }
end
end
def change_password
@page_title = t('users.change_password_title')
end
def update_password
@user.change_password(params[:updateuser][:password], params[:updateuser][:password_confirmation])
# is used for focing password change after sha->bcrypt upgrade
@user.change_password(params[:user][:password], params[:user][:password_confirmation])
notify :notice, t('users.password_updated')
redirect_to preferences_path
rescue Exception => error
@ -167,7 +172,7 @@ class UsersController < ApplicationController
def change_auth_type
@page_title = t('users.change_auth_type_title')
end
def update_auth_type
if (params[:open_id_complete] || (params[:user][:auth_type] == 'open_id')) && openid_enabled?
authenticate_with_open_id do |result, identity_url|
@ -199,16 +204,16 @@ class UsersController < ApplicationController
redirect_to :action => 'change_auth_type'
end
end
def refresh_token
@user.generate_token
@user.save!
notify :notice, t('users.new_token_generated')
redirect_to preferences_path
end
private
def get_new_user
if session['new_user']
user = session['new_user']
@ -218,7 +223,7 @@ class UsersController < ApplicationController
end
user
end
def check_create_user_params
return false unless params.has_key?(:request)
return false unless params[:request].has_key?(:login)
@ -227,5 +232,5 @@ class UsersController < ApplicationController
return false if params[:request][:password].empty?
return true
end
end

View file

@ -27,33 +27,42 @@ module ApplicationHelper
# a 'traffic light' colour code
#
def due_date(due)
if due == nil
return ""
end
return "" if due.nil?
days = days_from_today(due)
case days
when 0
"<a title='#{format_date(due)}'><span class=\"amber\">Due Today</span></a> "
when 1
"<a title='#{format_date(due)}'><span class=\"amber\">Due Tomorrow</span></a> "
# due 2-7 days away
when 2..7
if prefs.due_style == Preference.due_styles[:due_on]
"<a title='#{format_date(due)}'><span class=\"orange\">Due on #{due.strftime("%A")}</span></a> "
else
"<a title='#{format_date(due)}'><span class=\"orange\">Due in #{pluralize(days, 'day')}</span></a> "
end
else
# overdue or due very soon! sound the alarm!
if days < 0
"<a title='#{format_date(due)}'><span class=\"red\">Overdue by #{pluralize(days * -1, 'day')}</span></a> "
else
# more than a week away - relax
"<a title='#{format_date(due)}'><span class=\"green\">Due in #{pluralize(days, 'day')}</span></a> "
end
end
colors = ['amber','amber','orange','orange','orange','orange','orange','orange']
color = :red if days < 0
color = :green if days > 7
color = colors[days] if color.nil?
return content_tag(:a, {:title => format_date(due)}) {
content_tag(:span, {:class => color}) {
case days
when 0
t('todos.next_actions_due_date.due_today')
when 1
t('todos.next_actions_due_date.due_tomorrow')
when 2..7
if prefs.due_style == Preference.due_styles[:due_on]
# TODO: internationalize strftime here
t('models.preference.due_on', :date => due.strftime("%A"))
else
t('models.preference.due_in', :days => days)
end
else
# overdue or due very soon! sound the alarm!
if days == -1
t('todos.next_actions_due_date.overdue_by', :days => days * -1)
elsif days < -1
t('todos.next_actions_due_date.overdue_by_plural', :days => days * -1)
else
# more than a week away - relax
t('models.preference.due_in', :days => days)
end
end
}
}
end
# Check due date in comparison to today's date Flag up date appropriately with
@ -152,12 +161,12 @@ module ApplicationHelper
def recurrence_time_span(rt)
case rt.ends_on
when "no_end_date"
return rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
return rt.start_from.nil? ? "" : I18n.t("todos.recurrence.pattern.from") + " " + format_date(rt.start_from)
when "ends_on_number_of_times"
return "for "+rt.number_of_occurences.to_s + " times"
return I18n.t("todos.recurrence.pattern.times", :number => rt.number_of_occurences)
when "ends_on_end_date"
starts = rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
ends = rt.end_date.nil? ? "" : " until " + format_date(rt.end_date)
starts = rt.start_from.nil? ? "" : I18n.t("todos.recurrence.pattern.from") + " " + format_date(rt.start_from)
ends = rt.end_date.nil? ? "" : " " + I18n.t("todos.recurrence.pattern.until") + " " + format_date(rt.end_date)
return starts+ends
else
raise Exception.new, "unknown recurrence time span selection (#{rt.ends_on})"
@ -252,7 +261,7 @@ module ApplicationHelper
contexts.show_form contexts.show_form_title
contexts.new_context_pre contexts.new_context_post
common.cancel common.ok
common.ajaxError
common.ajaxError todos.unresolved_dependency
}.each do |s|
js << "i18n['#{s}'] = '#{ t(s).gsub(/'/, "\\\\'") }';\n"
end
@ -266,5 +275,39 @@ module ApplicationHelper
javascript_include_tag("i18n/jquery.ui.datepicker-#{locale}.js")
end
end
def determine_done_path
case @controller.controller_name
when "contexts"
done_todos_context_path(@context)
when "projects"
done_todos_project_path(@project)
when "todos"
if source_view_is(:tag)
done_tag_path(@tag_name)
else
done_todos_path
end
else
done_todos_path
end
end
def determine_all_done_path
case @controller.controller_name
when "contexts"
all_done_todos_context_path(@context)
when "projects"
all_done_todos_project_path(@project)
when "todos"
if source_view_is(:tag)
all_done_tag_path(@tag_name)
else
all_done_todos_path
end
else
all_done_todos_path
end
end
end

View file

@ -1,25 +1,30 @@
module ContextsHelper
def get_listing_sortable_options
{
:tag => 'div',
:handle => 'handle',
:complete => visual_effect(:highlight, 'list-contexts'),
:url => order_contexts_path
}
end
def get_listing_sortable_options
{
:tag => 'div',
:handle => 'handle',
:complete => visual_effect(:highlight, 'list-contexts'),
:url => order_contexts_path
}
end
def link_to_delete_context(context, descriptor = sanitize(context.name))
link_to(
descriptor,
context_path(context, :format => 'js'),
{
:id => "delete_context_#{context.id}",
:class => "delete_context_button",
:x_confirm_message => t('contexts.delete_context_confirmation', :name => context.name),
:title => t('contexts.delete_context_title')
}
)
end
def summary(context, undone_todo_count)
content_tag(:p, "#{undone_todo_count}. Context is #{context.hidden? ? 'Hidden' : 'Active'}.")
end
def link_to_delete_context(context, descriptor = sanitize(context.name))
link_to(
descriptor,
context_path(context, :format => 'js'),
{
:id => "delete_context_#{context.id}",
:class => "delete_context_button",
:x_confirm_message => t('contexts.delete_context_confirmation', :name => context.name),
:title => t('contexts.delete_context_title')
}
)
end
end

View file

@ -1,2 +1,18 @@
module PreferencesHelper
def pref(model, pref_name, &block)
s = "<label for #{model+pref_name}>#{Preference.human_attribute_name(pref_name)}:</label><br/>"
s << yield
s << "<br/><br/>"
s
end
def pref_with_select_field(model, pref_name, collection = [ [t('preferences.is_true'),true], [t('preferences.is_false'), false] ])
pref(model, pref_name) { select(model, pref_name, collection) }
end
def pref_with_text_field(model, pref_name)
pref(model, pref_name) { text_field(model, pref_name) }
end
end

View file

@ -8,7 +8,7 @@ module ProjectsHelper
:url => order_projects_path
}
end
def set_element_visible(id,test)
if (test)
page.show id
@ -30,7 +30,7 @@ module ProjectsHelper
end
html
end
def project_next_prev_mobile
html = ''
unless @previous_project.nil?
@ -57,5 +57,19 @@ module ProjectsHelper
}
)
end
def summary(project)
project_description = ''
project_description += sanitize(markdown( project.description )) unless project.description.blank?
project_description += content_tag(:p,
"#{count_undone_todos_phrase(p)}. " + t('projects.project_state', :state => project.state)
)
project_description
end
def needsreview_class(item)
raise "item must be a Project " unless item.kind_of? Project
return item.needs_review?(current_user) ? "needsreview" : "needsnoreview"
end
end

View file

@ -3,9 +3,8 @@ module RecurringTodosHelper
def recurring_todo_tag_list
tags_except_starred = @recurring_todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
tag_list = tags_except_starred.collect{|t| "<span class=\"tag #{t.name.gsub(' ','-')}\">" +
# link_to(t.name, :controller => "todos", :action => "tag", :id =>
# t.name) + TODO: tag view for recurring_todos (yet?)
t.name +
link_to(t.name, :controller => "todos", :action => "tag", :id =>
t.name) + #TODO: tag view for recurring_todos (yet?)
"</span>"}.join('')
"<span class='tags'>#{tag_list}</span>"
end

View file

@ -18,7 +18,7 @@ module TodosHelper
def remote_delete_menu_item(todo)
return link_to(
image_tag("delete_off.png", :mouseover => "delete_on.png", :alt => t('todos.delete'), :align => "absmiddle")+" "+t('todos.delete'),
{:controller => 'todos', :action => 'destroy', :id => todo.id},
{:controller => 'todos', :action => 'destroy', :id => todo.id},
:class => "icon_delete_item",
:id => "delete_#{dom_id(todo)}",
:x_confirm_message => t('todos.confirm_delete', :description => todo.description),
@ -30,7 +30,7 @@ module TodosHelper
:_source_view => (@source_view.underscore.gsub(/\s+/,'_') rescue "")}
url[:_tag_name] = @tag_name if @source_view == 'tag'
options = {:x_defer_alert => false, :class => "icon_defer_item" }
options = {:x_defer_alert => false, :class => "icon_defer_item", :id => "defer_#{days}_#{dom_id(todo)}" }
if todo.due
futuredate = (todo.show_from || todo.user.date) + days.days
if futuredate > todo.due
@ -55,18 +55,13 @@ module TodosHelper
:_source_view => (@source_view.underscore.gsub(/\s+/,'_') rescue "")}
url[:_tag_name] = @tag_name if @source_view == 'tag'
return link_to(image_tag("to_project_off.png", :align => "absmiddle")+" " + t('todos.convert_to_project'), url)
return link_to(image_tag("to_project_off.png", :align => "absmiddle")+" " + t('todos.convert_to_project'), url, {:id => "to_project_#{dom_id(todo)}"})
end
def image_tag_for_defer(days)
image_tag("defer_#{days}_off.png", :mouseover => "defer_#{days}.png", :alt => t('todos.defer_x_days', :count => days), :align => "absmiddle")+" "+t('todos.defer_x_days', :count => days)
end
# waiting stuff can be deleted after migration of defer and dependencies
def successor_start_waiting_js(successor)
return "$('##{dom_id(successor, "successor")}').block({message: null})"
end
def collapsed_notes_image(todo)
link = link_to(image_tag( 'blank.png', :width=>'16', :height=>'16', :border=>'0' ), "#", {:class => 'show_notes', :title => 'Show notes'})
notes = content_tag(:div, {:class => "todo_notes", :id => dom_id(todo, 'notes'), :style => "display:none"}) { format_note(todo.notes) }
@ -88,24 +83,25 @@ module TodosHelper
{:controller => "recurring_todos", :action => "index"},
:class => "recurring_icon", :title => recurrence_pattern_as_text(todo.recurring_todo))
end
def remote_toggle_checkbox(todo=@todo)
check_box_tag("mark_complete_#{todo.id}", toggle_check_todo_path(todo), todo.completed?, :class => 'item-checkbox',
:title => todo.pending? ? t('todos.blocked_by', :predecessors => todo.uncompleted_predecessors.map(&:description).join(', ')) : "", :readonly => todo.pending?)
end
def date_span(todo=@todo)
if todo.completed?
"<span class=\"grey\">#{format_date( todo.completed_at )}</span>"
content_tag(:span, {:class => :grey}) { format_date( todo.completed_at ) }
elsif todo.pending?
"<a title='#{t('todos.depends_on')}: #{todo.uncompleted_predecessors.map(&:description).join(', ')}'><span class=\"orange\">#{t('todos.pending')}</span></a> "
title = t('todos.depends_on')+ ": " + todo.uncompleted_predecessors.map(&:description).join(', ')
content_tag(:a, {:title => title}) { content_tag(:span, {:class => :orange}) { t('todos.pending') } }
elsif todo.deferred?
show_date( todo.show_from )
else
due_date( todo.due )
end
end
def successors_span(todo=@todo)
unless todo.pending_successors.empty?
pending_count = todo.pending_successors.length
@ -113,41 +109,41 @@ module TodosHelper
image_tag( 'successor_off.png', :width=>'10', :height=>'16', :border=>'0', :title => title )
end
end
def grip_span(todo=@todo)
unless todo.completed?
image_tag('grip.png', :width => '7', :height => '16', :border => '0',
image_tag('grip.png', :width => '7', :height => '16', :border => '0',
:title => t('todos.drag_action_title'),
:class => 'grip')
end
end
def tag_list_text(todo=@todo)
todo.tags.collect{|t| t.name}.join(', ')
end
def tag_list(todo=@todo)
tags_except_starred = todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
tag_list = tags_except_starred.collect{|t| "<span class=\"tag #{t.name.gsub(' ','-')}\">" + link_to(t.name, :controller => "todos", :action => "tag", :id => t.name) + "</span>"}.join('')
"<span class='tags'>#{tag_list}</span>"
def tag_span (tag, mobile=false)
content_tag(:span, :class => "tag #{tag.name.gsub(' ','-')}") { link_to(tag.name, (mobile ? mobile_tag_path(tag.name) : tag_path(tag.name))) }
end
def tag_list(todo=@todo, mobile=false)
content_tag(:span, :class => 'tags') { todo.tags.all_except_starred.collect{|tag| tag_span(tag, mobile)}.join('') }
end
def tag_list_mobile(todo=@todo)
tags_except_starred = todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
# removed the link. TODO: add link to mobile view of tagged actions
tag_list = tags_except_starred.collect{|t|
"<span class=\"tag\">" +
link_to(t.name, {:action => "tag", :controller => "todos", :id => t.name+".m"}) +
"</span>"}.join('')
if tag_list.empty? then "" else "<span class=\"tags\">#{tag_list}</span>" end
unless todo.tags.all_except_starred.empty?
return tag_list(todo, true)
else
return ""
end
end
def deferred_due_date(todo=@todo)
if todo.deferred? && todo.due
t('todos.action_due_on', :date => format_date(todo.due))
end
end
def project_and_context_links(todo, parent_container_type, opts = {})
str = ''
if todo.completed?
@ -166,7 +162,7 @@ module TodosHelper
end
return str
end
# Uses the 'staleness_starts' value from settings.yml (in days) to colour the
# background of the action appropriately according to the age of the creation
# date:
@ -188,37 +184,41 @@ module TodosHelper
end
end
def show_date_tag(date, the_class, text)
content_tag(:a, :title => format_date(date)) do
content_tag(:span, :class => the_class) { text + " "}
end
end
# Check show_from date in comparison to today's date Flag up date
# appropriately with a 'traffic light' colour code
#
def show_date(d)
if d == nil
return ""
end
return "" if d == nil
days = days_from_today(d)
case days
# overdue or due very soon! sound the alarm!
when -1000..-1
"<a title=\"" + format_date(d) + "\"><span class=\"red\">#{t('todos.scheduled_overdue', :days => (days * -1).to_s)}</span></a> "
show_date_tag(d, :red, t('todos.scheduled_overdue', :days => (days * -1).to_s))
when 0
"<a title=\"" + format_date(d) + "\"><span class=\"amber\">#{t('todos.show_today')}</span></a> "
show_date_tag(d, :amber, t('todos.show_today'))
when 1
"<a title=\"" + format_date(d) + "\"><span class=\"amber\">#{t('todos.show_tomorrow')}</span></a> "
show_date_tag(d, :amber, t('todos.show_tomorrow'))
# due 2-7 days away
when 2..7
if prefs.due_style == Preference.due_styles[:due_on]
"<a title=\"" + format_date(d) + "\"><span class=\"orange\">#{t('todos.show_on_date', :date => d.strftime("%A"))}</span></a> "
show_date_tag(d, :orange, t('todos.show_on_date', :date => d.strftime("%A")) )
else
"<a title=\"" + format_date(d) + "\"><span class=\"orange\">#{t('todos.show_in_days', :days => days.to_s)}</span></a> "
show_date_tag(d, :orange, t('todos.show_in_days', :days => days.to_s) )
end
# more than a week away - relax
else
"<a title=\"" + format_date(d) + "\"><span class=\"green\">#{t('todos.show_in_days', :days => days.to_s)}</span></a> "
show_date_tag(d, :green, t('todos.show_in_days', :days => days.to_s) )
end
end
def should_show_new_item
source_view do |page|
page.todo { return !@todo.hidden? }
@ -250,7 +250,7 @@ module TodosHelper
def should_add_new_context
return @new_context_created && !source_view_is(:project)
end
def parent_container_type
return 'tickler' if source_view_is :deferred
return 'project' if source_view_is :project
@ -258,18 +258,18 @@ module TodosHelper
return 'tag' if source_view_is :tag
return 'context'
end
def todo_container_is_empty
default_container_empty = ( @down_count == 0 )
deferred_container_empty = ( @todo.deferred? && @remaining_deferred_count == 0)
return default_container_empty || deferred_container_empty
end
def default_contexts_for_autocomplete
projects = current_user.uncompleted.projects.find(:all, :include => [:context], :conditions => ['default_context_id is not null'])
Hash[*projects.map{ |p| [escape_javascript(p.name), escape_javascript(p.default_context.name)] }.flatten].to_json
end
def default_tags_for_autocomplete
projects = current_user.projects.uncompleted.find(:all, :conditions => ["default_tags != ''"])
Hash[*projects.map{ |p| [escape_javascript(p.name), p.default_tags] }.flatten].to_json
@ -282,7 +282,7 @@ module TodosHelper
end
joined_notes || ""
end
def formatted_pagination(total)
s = will_paginate(@todos)
(s.gsub(/(<\/[^<]+>)/, '\1 ')).chomp(' ')
@ -295,6 +295,7 @@ module TodosHelper
def update_needs_to_hide_context
return (@remaining_in_context == 0 && (@todo_hidden_state_changed && @todo.hidden?)) ||
(@remaining_in_context == 0 && @todo_was_deferred_from_active_state) ||
(@remaining_in_context == 0 && @tag_was_removed) ||
(@remaining_in_context == 0 && @todo.completed? && !(@original_item_was_deferred || @original_item_was_hidden)) if source_view_is(:tag)
return false if source_view_is_one_of(:project, :calendar)
@ -304,13 +305,14 @@ module TodosHelper
def update_needs_to_remove_todo_from_container
source_view do |page|
page.context { return @context_changed || @todo.deferred? || @todo.pending? }
page.context { return @context_changed || @todo.deferred? || @todo.pending? || @todo_should_be_hidden }
page.project { return @todo_deferred_state_changed || @todo_pending_state_changed || @project_changed}
page.deferred { return @context_changed || !(@todo.deferred? || @todo.pending?) }
page.calendar { return @due_date_changed || !@todo.due }
page.stats { return @todo.completed? }
page.tag { return (@context_changed && !@todo.hidden?) || @tag_was_removed || @todo_hidden_state_changed || @todo_deferred_state_changed }
page.todo { return @context_changed || @todo.hidden? || @todo.deferred? || @todo.pending?}
page.search { return false }
end
return false
end
@ -324,6 +326,7 @@ module TodosHelper
page.stats { return !@todo.completed? }
page.tag { return !update_needs_to_remove_todo_from_container && !@tag_was_removed }
page.todo { return !update_needs_to_remove_todo_from_container }
page.search { return true }
end
return false
end
@ -356,11 +359,21 @@ module TodosHelper
source_view do |page|
page.project {
return "tickler-empty-nd" if @todo_was_deferred_from_active_state || @todo_was_blocked_from_active_state || @todo_was_destroyed_from_deferred_state || @todo_was_created_deferred
return "tickler-empty-nd" if
@todo_was_deferred_from_active_state ||
@todo_was_blocked_from_active_state ||
@todo_was_destroyed_from_deferred_state ||
@todo_was_created_deferred ||
@todo_was_blocked_from_completed_state
return "p#{todo.project_id}empty-nd"
}
page.tag {
return "tickler-empty-nd" if @todo_was_deferred_from_active_state || @todo_was_destroyed_from_deferred_state || @todo_was_created_deferred
return "tickler-empty-nd" if
@todo_was_deferred_from_active_state ||
@todo_was_blocked_from_active_state ||
@todo_was_destroyed_from_deferred_state ||
@todo_was_created_deferred ||
@todo_was_blocked_from_completed_state
return "hidden-empty-nd" if @todo.hidden?
return "c#{todo.context_id}empty-nd"
}
@ -372,22 +385,27 @@ module TodosHelper
return "c#{todo.context_id}empty-nd"
end
def todo_was_removed_from_deferred_or_blocked_container
return @todo_was_activated_from_deferred_state ||
@todo_was_activated_from_pending_state ||
@todo_was_destroyed_from_deferred_or_pending_state ||
@todo_was_completed_from_deferred_or_blocked_state
end
def show_empty_message_in_source_container
container_id = ""
source_view do |page|
page.project {
container_id = "p#{@original_item_project_id}empty-nd" if @remaining_in_context == 0
container_id = "tickler-empty-nd" if (
( (@todo_was_activated_from_deferred_state || @todo_was_activated_from_pending_state) && @remaining_deferred_or_pending_count == 0) ||
(@original_item_was_deferred && @remaining_deferred_or_pending_count == 0 && @todo.completed?) )
container_id = "empty-d" if @completed_count && @completed_count == 0 && !@todo.completed?
container_id = "tickler-empty-nd" if todo_was_removed_from_deferred_or_blocked_container && @remaining_deferred_or_pending_count == 0
container_id = "empty-d" if @completed_count && @completed_count == 0 && !@todo.completed?
}
page.deferred { container_id = "c#{@original_item_context_id}empty-nd" if @remaining_in_context == 0 }
page.calendar { container_id = "empty_#{@original_item_due_id}" if @old_due_empty }
page.tag {
container_id = "hidden-empty-nd" if (@remaining_hidden_count == 0 && !@todo.hidden? && @todo_hidden_state_changed) ||
(@remaining_hidden_count == 0 && @todo.completed? && @original_item_was_hidden)
container_id = "tickler-empty-nd" if (@todo_was_activated_from_deferred_state && @remaining_deferred_or_pending_count == 0) ||
container_id = "tickler-empty-nd" if (todo_was_removed_from_deferred_or_blocked_container && @remaining_deferred_or_pending_count == 0) ||
(@original_item_was_deferred && @remaining_deferred_or_pending_count == 0 && (@todo.completed? || @tag_was_removed))
container_id = "empty-d" if @completed_count && @completed_count == 0 && !@todo.completed?
}
@ -411,11 +429,22 @@ module TodosHelper
return html + ";"
end
private
def image_tag_for_star(todo)
class_str = todo.starred? ? "starred_todo" : "unstarred_todo"
image_tag("blank.png", :title =>t('todos.star_action'), :class => class_str, :id => "star_img_"+todo.id.to_s)
def reset_tab_index
$tracks_tab_index = 0
end
def next_tab_index
# make sure it exists if reset was not called. Set to 20 to avoid clashes with existing form in sidebar
$tracks_tab_index ||= 20
$tracks_tab_index = $tracks_tab_index + 1
return $tracks_tab_index
end
private
def image_tag_for_star(todo)
image_tag("blank.png", :title =>t('todos.star_action'), :class => "todo_star"+(todo.starred? ? " starred":""), :id => "star_img_"+todo.id.to_s)
end
end

View file

@ -1,13 +1,14 @@
class Context < ActiveRecord::Base
has_many :todos, :dependent => :delete_all, :include => :project, :order => "todos.completed_at DESC"
has_many :todos, :dependent => :delete_all, :include => :project,
:order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC'
has_many :recurring_todos, :dependent => :delete_all
belongs_to :user
named_scope :active, :conditions => { :hide => false }
named_scope :hidden, :conditions => { :hide => true }
acts_as_list :scope => :user
acts_as_list :scope => :user, :top_of_list => 0
extend NamePartFinder
include Tracks::TodoList
@ -19,12 +20,13 @@ class Context < ActiveRecord::Base
validates_does_not_contain :name, :string => ',', :message => "cannot contain the comma (',') character"
def self.feed_options(user)
# TODO: move to view or helper
{
:title => 'Tracks Contexts',
:description => "Lists all the contexts for #{user.display_name}"
}
end
def self.null_object
NullContext.new
end
@ -32,36 +34,29 @@ class Context < ActiveRecord::Base
def hidden?
self.hide == true || self.hide == 1
end
def title
name
end
def summary(undone_todo_count)
s = "<p>#{undone_todo_count}. "
s += "Context is #{hidden? ? 'Hidden' : 'Active'}."
s += "</p>"
s
end
def new_record_before_save?
@new_record_before_save
end
end
end
class NullContext
def nil?
true
end
def id
nil
end
def name
''
end
end

View file

@ -1,28 +1,5 @@
class Project < ActiveRecord::Base
has_many :todos, :dependent => :delete_all
# TODO: remove these scopes. Can be replaced by the named scopes on the todo relation
has_many :not_done_todos,
:include => [:context,:tags,:project],
:class_name => 'Todo',
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
:conditions => ["todos.state = ?", 'active']
has_many :done_todos,
:include => [:context,:tags,:project],
:class_name => 'Todo',
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
:conditions => ["todos.state = ?", 'completed']
has_many :deferred_todos,
:include => [:context,:tags,:project],
:class_name => 'Todo',
:conditions => ["todos.state = ? ", "deferred"],
:order => "show_from"
has_many :pending_todos,
:include => [:context,:tags,:project],
:class_name => 'Todo',
:conditions => ["todos.state = ? ", "pending"],
:order => "show_from"
has_many :notes, :dependent => :delete_all, :order => "created_at DESC"
has_many :recurring_todos
@ -32,48 +9,51 @@ class Project < ActiveRecord::Base
named_scope :active, :conditions => { :state => 'active' }
named_scope :hidden, :conditions => { :state => 'hidden' }
named_scope :completed, :conditions => { :state => 'completed'}
named_scope :uncompleted, :conditions => ["NOT state = ?", 'completed']
named_scope :uncompleted, :conditions => ["NOT(state = ?)", 'completed']
validates_presence_of :name
validates_length_of :name, :maximum => 255
validates_uniqueness_of :name, :scope => "user_id"
validates_does_not_contain :name, :string => ','
acts_as_list :scope => 'user_id = #{user_id} AND state = \'#{state}\''
acts_as_state_machine :initial => :active, :column => 'state'
acts_as_list :scope => 'user_id = #{user_id} AND state = \'#{state}\'', :top_of_list => 0
include AASM
aasm_column :state
aasm_initial_state :active
extend NamePartFinder
#include Tracks::TodoList
state :active
state :hidden, :enter => :hide_todos, :exit => :unhide_todos
state :completed, :enter => Proc.new { |p| p.completed_at = Time.zone.now }, :exit => Proc.new { |p| p.completed_at = nil }
event :activate do
transitions :to => :active, :from => [:hidden, :completed]
aasm_state :active
aasm_state :hidden, :enter => :hide_todos, :exit => :unhide_todos
aasm_state :completed, :enter => :set_completed_at_date, :exit => :clear_completed_at_date
aasm_event :activate do
transitions :to => :active, :from => [:active, :hidden, :completed]
end
event :hide do
aasm_event :hide do
transitions :to => :hidden, :from => [:active, :completed]
end
event :complete do
aasm_event :complete do
transitions :to => :completed, :from => [:active, :hidden]
end
attr_protected :user
attr_accessor :cached_note_count
def self.null_object
NullProject.new
end
def self.feed_options(user)
{
:title => I18n.t('models.project.feed_title'),
:description => I18n.t('models.project.feed_description', :username => user.display_name)
}
end
def hide_todos
todos.each do |t|
unless t.completed? || t.deferred?
@ -82,7 +62,7 @@ class Project < ActiveRecord::Base
end
end
end
def unhide_todos
todos.each do |t|
if t.project_hidden?
@ -91,22 +71,32 @@ class Project < ActiveRecord::Base
end
end
end
def set_completed_at_date
self.completed_at = Time.zone.now
end
def clear_completed_at_date
self.completed_at = nil
end
def note_count
# TODO: test this for eager and not eager loading!!!
return 0 if notes.size == 0
cached_note_count || notes.count
end
alias_method :original_default_context, :default_context
def default_context
original_default_context.nil? ? Context.null_object : original_default_context
end
# would prefer to call this method state=(), but that causes an endless loop
# as a result of acts_as_state_machine calling state=() to update the attribute
def transition_to(candidate_state)
case candidate_state.to_sym
when current_state
when aasm_current_state
return
when :hidden
hide!
@ -116,29 +106,48 @@ class Project < ActiveRecord::Base
complete!
end
end
def needs_review?(current_user)
return active? && ( last_reviewed.nil? ||
(last_reviewed < current_user.time - current_user.prefs.review_period.days))
end
def blocked?
## mutually exclusive for stalled and blocked
# blocked is uncompleted project with deferred or pending todos, but no next actions
return false if self.completed?
return !self.todos.deferred_or_blocked.empty? && self.todos.not_deferred_or_blocked.empty?
end
def stalled?
# stalled is active/hidden project with no active todos
return false if self.completed?
return self.todos.deferred_or_blocked.empty? && self.todos.not_deferred_or_blocked.empty?
end
def name=(value)
self[:name] = value.gsub(/\s{2,}/, " ").strip
end
def new_record_before_save?
@new_record_before_save
end
end
end
class NullProject
def hidden?
false
end
def nil?
true
end
def id
nil
end
end

View file

@ -10,20 +10,19 @@ class RecurringTodo < ActiveRecord::Base
named_scope :completed, :conditions => { :state => 'completed'}
attr_protected :user
acts_as_state_machine :initial => :active, :column => 'state'
state :active, :enter => Proc.new { |t|
t[:show_from], t.completed_at = nil, nil
t.occurences_count = 0
}
state :completed, :enter => Proc.new { |t| t.completed_at = Time.zone.now }, :exit => Proc.new { |t| t.completed_at = nil }
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 }
event :complete do
aasm_event :complete do
transitions :to => :completed, :from => [:active]
end
event :activate do
aasm_event :activate do
transitions :to => :active, :from => [:completed]
end
@ -41,8 +40,7 @@ class RecurringTodo < ActiveRecord::Base
validate :set_recurrence_on_validations
def period_specific_validations
periods = %W[daily weekly monthly yearly]
if periods.include?(recurring_period)
if %W[daily weekly monthly yearly].include?(recurring_period)
self.send("validate_#{recurring_period}")
else
errors.add(:recurring_period, "is an unknown recurrence pattern: '#{self.recurring_period}'")
@ -94,7 +92,6 @@ class RecurringTodo < ActiveRecord::Base
end
end
def starts_and_ends_on_validations
errors.add_to_base("The start date needs to be filled in") if start_from.nil? || start_from.blank?
case self.ends_on
@ -388,9 +385,9 @@ class RecurringTodo < ActiveRecord::Base
def recurring_target_as_text
case self.target
when 'due_date'
return "due"
return I18n.t("todos.recurrence.pattern.due")
when 'show_from_date'
return "show"
return I18n.t("todos.recurrence.pattern.show")
else
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
end
@ -403,38 +400,51 @@ class RecurringTodo < ActiveRecord::Base
def recurring_show_always=(value)
self.show_always=value
end
def recurrence_pattern
return "invalid repeat pattern" if every_other1.nil?
case recurring_period
when 'daily'
if only_work_days
return "on work days"
return I18n.t("todos.recurrence.pattern.on_work_days")
else
if every_other1 > 1
return "every #{every_other1} days"
return I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.days")
else
return "every day"
return I18n.t("todos.recurrence.pattern.every_day")
end
end
when 'weekly'
if every_other1 > 1
return "every #{every_other1} weeks"
return I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.weeks")
else
return 'weekly'
return I18n.t('todos.recurrence.pattern.weekly')
end
when 'monthly'
return "invalid repeat pattern" if every_other2.nil?
if self.recurrence_selector == 0
return "every #{self.every_other2} month#{self.every_other2>1?'s':''} on day #{self.every_other1}"
on_day = " " + I18n.t('todos.recurrence.pattern.on_day_n', :n => self.every_other1)
if self.every_other2>1
return I18n.t("todos.recurrence.pattern.every_n", :n => self.every_other2) + " " + I18n.t('common.months') + on_day
else
return I18n.t("todos.recurrence.pattern.every_month") + on_day
end
else
return "every #{self.xth} #{self.day_of_week} of every #{self.every_other2} month#{self.every_other2>1?'s':''}"
if self.every_other2>1
n_months = "#{self.every_other2} " + I18n.t('common.months')
else
n_months = I18n.t('common.month')
end
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 "every year on #{self.month_of_year} #{self.every_other1}"
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 "every year on the #{self.xth} #{self.day_of_week} of #{self.month_of_year}"
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'
@ -442,18 +452,18 @@ class RecurringTodo < ActiveRecord::Base
end
def xth
xth_day = ['first','second','third','fourth','last']
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
days_of_week = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']
return (self.every_count.nil? ? '??' : days_of_week[self.every_count])
return (self.every_count.nil? ? '??' : I18n.t('todos.recurrence.pattern.day_names')[self.every_count])
end
def month_of_year
months_of_year = ['January','Februari','March','April','May','June','July','August','September','October','November','December']
return self.every_other2.nil? ? '??' : months_of_year[self.every_other2-1]
return self.every_other2.nil? ? '??' : I18n.t('todos.recurrence.pattern.month_names')[self.every_other2]
end
def starred?
@ -692,13 +702,7 @@ class RecurringTodo < ActiveRecord::Base
end
def toggle_completion!
saved = false
if completed?
saved = activate!
else
saved = complete!
end
return saved
return completed? ? activate! : complete!
end
def toggle_star!

View file

@ -11,38 +11,41 @@ class Tag < ActiveRecord::Base
# rescue the ActiveRecord database constraint errors instead.
validates_presence_of :name
validates_uniqueness_of :name, :case_sensitive => false
# Change this validation if you need more complex tag names.
# validates_format_of :name, :with => /^[a-zA-Z0-9\_\-]+$/, :message => "can not contain special characters"
# Set up the polymorphic relationship.
has_many_polymorphs :taggables,
:from => [:todos, :recurring_todos],
:through => :taggings,
has_many_polymorphs :taggables,
:from => [:todos, :recurring_todos],
:through => :taggings,
:dependent => :destroy,
:skip_duplicates => false,
:skip_duplicates => false,
:parent_extend => proc {
# Defined on the taggable models, not on Tag itself. Return the tagnames
# associated with this record as a string.
def to_s
self.map(&:name).sort.join(Tag::JOIN_DELIMITER)
end
def all_except_starred
self.reject{|tag| tag.name == Todo::STARRED_TAG_NAME}
end
}
# Callback to strip extra spaces from the tagname before saving it. If you
# allow tags to be renamed later, you might want to use the
# <tt>before_save</tt> callback instead.
def before_create
def before_create
self.name = name.downcase.strip.squeeze(" ")
end
def on(taggable, user)
taggings.create :taggable => taggable, :user => user
end
# Tag::Error class. Raised by ActiveRecord::Base::TaggingExtensions if
# something goes wrong.
class Error < StandardError
end
end

View file

@ -1,10 +1,13 @@
class Todo < ActiveRecord::Base
after_save :save_predecessors
# relations
belongs_to :context
belongs_to :project
belongs_to :user
belongs_to :recurring_todo
has_many :predecessor_dependencies, :foreign_key => 'predecessor_id', :class_name => 'Dependency', :dependent => :destroy
has_many :successor_dependencies, :foreign_key => 'successor_id', :class_name => 'Dependency', :dependent => :destroy
has_many :predecessors, :through => :successor_dependencies
@ -13,21 +16,18 @@ class Todo < ActiveRecord::Base
:source => :predecessor, :conditions => ['NOT (todos.state = ?)', 'completed']
has_many :pending_successors, :through => :predecessor_dependencies,
:source => :successor, :conditions => ['todos.state = ?', 'pending']
after_save :save_predecessors
# scopes for states of this todo
named_scope :active, :conditions => { :state => 'active' }
named_scope :active_or_hidden, :conditions => ["todos.state = ? OR todos.state = ?", 'active', 'project_hidden']
named_scope :not_completed, :conditions => ['NOT (todos.state = ? )', 'completed']
named_scope :completed, :conditions => ["NOT todos.completed_at IS NULL"]
named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)']
named_scope :deferred, :conditions => ["todos.completed_at IS NULL AND NOT todos.show_from IS NULL"]
named_scope :not_completed, :conditions => ['NOT (todos.state = ?)', 'completed']
named_scope :completed, :conditions => ["NOT (todos.completed_at IS NULL)"]
named_scope :deferred, :conditions => ["todos.completed_at IS NULL AND NOT (todos.show_from IS NULL)"]
named_scope :blocked, :conditions => ['todos.state = ?', 'pending']
named_scope :deferred_or_blocked, :conditions => ["(todos.completed_at IS NULL AND NOT todos.show_from IS NULL) OR (todos.state = ?)", "pending"]
named_scope :not_deferred_or_blocked, :conditions => ["todos.completed_at IS NULL AND todos.show_from IS NULL AND NOT todos.state = ?", "pending"]
named_scope :with_tag, lambda { |tag| {:joins => :taggings, :conditions => ["taggings.tag_id = ? ", tag.id] } }
named_scope :of_user, lambda { |user_id| {:conditions => ["todos.user_id = ? ", user_id] } }
named_scope :hidden,
named_scope :pending, :conditions => ['todos.state = ?', 'pending']
named_scope :deferred_or_blocked, :conditions => ["(todos.completed_at IS NULL AND NOT(todos.show_from IS NULL)) OR (todos.state = ?)", "pending"]
named_scope :not_deferred_or_blocked, :conditions => ["todos.completed_at IS NULL AND todos.show_from IS NULL AND NOT(todos.state = ?)", "pending"]
named_scope :hidden,
:joins => :context,
:conditions => ["todos.state = ? OR (contexts.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?))",
'project_hidden', true, 'active', 'deferred', 'pending']
@ -36,82 +36,100 @@ class Todo < ActiveRecord::Base
:conditions => ['NOT(todos.state = ? OR (contexts.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?)))',
'project_hidden', true, 'active', 'deferred', 'pending']
STARRED_TAG_NAME = "starred"
# other scopes
named_scope :are_due, :conditions => ['NOT (todos.due IS NULL)']
named_scope :with_tag, lambda { |tag| {:joins => :taggings, :conditions => ["taggings.tag_id = ? ", tag.id] } }
named_scope :of_user, lambda { |user_id| {:conditions => ["todos.user_id = ? ", user_id] } }
named_scope :completed_after, lambda { |date| {:conditions => ["todos.completed_at > ? ", date] } }
named_scope :completed_before, lambda { |date| {:conditions => ["todos.completed_at < ? ", date] } }
# regular expressions for dependencies
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
acts_as_state_machine :initial => :active, :column => 'state'
# when entering active state, also remove completed_at date. Looks like :exit
# of state completed is not run, see #679
state :active, :enter => Proc.new { |t| t[:show_from], t.completed_at = nil, nil }
state :project_hidden
state :completed, :enter => Proc.new { |t| t.completed_at = Time.zone.now }, :exit => Proc.new { |t| t.completed_at = nil }
state :deferred
state :pending
event :defer do
# state machine
include AASM
aasm_column :state
aasm_initial_state Proc.new { |t| (t.show_from && t.user && (t.show_from > t.user.date)) ? :deferred : :active}
aasm_state :active
aasm_state :project_hidden
aasm_state :completed, :enter => Proc.new { |t| t.completed_at = Time.zone.now }, :exit => Proc.new { |t| t.completed_at = nil}
aasm_state :deferred, :exit => Proc.new { |t| t[:show_from] = nil }
aasm_state :pending
aasm_event :defer do
transitions :to => :deferred, :from => [:active]
end
event :complete do
transitions :to => :completed, :from => [:active, :project_hidden, :deferred]
aasm_event :complete do
transitions :to => :completed, :from => [:active, :project_hidden, :deferred, :pending]
end
event :activate do
transitions :to => :active, :from => [:project_hidden, :completed, :deferred]
transitions :to => :active, :from => [:pending], :guard => :no_uncompleted_predecessors_or_deferral?
aasm_event :activate do
transitions :to => :active, :from => [:project_hidden, :deferred]
transitions :to => :active, :from => [:completed], :guard => :no_uncompleted_predecessors?
transitions :to => :active, :from => [:pending], :guard => :no_uncompleted_predecessors_or_deferral?
transitions :to => :pending, :from => [:completed], :guard => :uncompleted_predecessors?
transitions :to => :deferred, :from => [:pending], :guard => :no_uncompleted_predecessors?
end
event :hide do
transitions :to => :project_hidden, :from => [:active, :deferred]
aasm_event :hide do
transitions :to => :project_hidden, :from => [:active, :deferred, :pending]
end
event :unhide do
aasm_event :unhide do
transitions :to => :deferred, :from => [:project_hidden], :guard => Proc.new{|t| !t.show_from.blank? }
transitions :to => :pending, :from => [:project_hidden], :guard => :uncompleted_predecessors?
transitions :to => :active, :from => [:project_hidden]
end
event :block do
aasm_event :block do
transitions :to => :pending, :from => [:active, :deferred]
end
attr_protected :user
# Description field can't be empty, and must be < 100 bytes Notes must be <
# 60,000 bytes (65,000 actually, but I'm being cautious)
validates_presence_of :description
validates_length_of :description, :maximum => 100
validates_length_of :notes, :maximum => 60000, :allow_nil => true
validates_length_of :notes, :maximum => 60000, :allow_nil => true
validates_presence_of :show_from, :if => :deferred?
validates_presence_of :context
def initialize(*args)
super(*args)
@predecessor_array = nil # Used for deferred save of predecessors
@removed_predecessors = nil
end
def no_uncompleted_predecessors_or_deferral?
return (show_from.blank? or Time.zone.now > show_from and uncompleted_predecessors.empty?)
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)
end
def no_uncompleted_predecessors?
return uncompleted_predecessors.empty?
return uncompleted_predecessors.all(true).empty?
end
def uncompleted_predecessors?
return !uncompleted_predecessors.all(true).empty?
end
# Returns a string with description <context, project>
def specification
project_name = self.project.is_a?(NullProject) ? "(none)" : self.project.name
return "\'#{self.description}\' <\'#{self.context.title}\'; \'#{project_name}\'>"
end
def validate
if !show_from.blank? && show_from < user.date
errors.add("show_from", I18n.t('models.todo.error_date_must_be_future'))
@ -123,7 +141,7 @@ class Todo < ActiveRecord::Base
end
end
end
def save_predecessors
unless @predecessor_array.nil? # Only save predecessors if they changed
current_array = self.predecessors
@ -145,21 +163,19 @@ class Todo < ActiveRecord::Base
logger.error "Could not find #{todo.description}" # Unexpected since validation passed
end
end
end
end
end
def removed_predecessors
return @removed_predecessors
end
def remove_predecessor(predecessor)
puts "@@@ before delete"
# remove predecessor and activate myself
self.predecessors.delete(predecessor)
puts "@@@ before activate"
self.activate!
end
# Returns true if t is equal to self or a successor of self
def is_successor?(todo)
if self == todo
@ -176,6 +192,10 @@ class Todo < ActiveRecord::Base
return false
end
def has_pending_successors
return !pending_successors.empty?
end
def has_tag?(tag)
return self.tags.select{|t| t.name==tag }.size > 0
end
@ -196,27 +216,26 @@ class Todo < ActiveRecord::Base
end
self.save!
end
def toggle_completion!
saved = false
if completed?
saved = activate!
else
saved = complete!
end
return saved
return completed? ? activate! : complete!
end
def show_from
self[:show_from]
end
def show_from=(date)
# parse Date objects into the proper timezone
date = user.at_midnight(date) if (date.is_a? Date)
# show_from needs to be set before state_change because of "bug" in aasm.
# If show_from is not set, the todo will not validate and thus aasm will not save
# (see http://stackoverflow.com/questions/682920/persisting-the-state-column-on-transition-using-rubyist-aasm-acts-as-state-machi)
self[:show_from] = date
activate! if deferred? && date.blank?
defer! if active? && !date.blank? && date > user.date
self[:show_from] = date
end
alias_method :original_project, :project
@ -225,46 +244,28 @@ class Todo < ActiveRecord::Base
original_project.nil? ? Project.null_object : original_project
end
alias_method :original_set_initial_state, :set_initial_state
def set_initial_state
if show_from && (show_from > user.date)
write_attribute self.class.state_column, 'deferred'
else
original_set_initial_state
end
end
alias_method :original_run_initial_state_actions, :run_initial_state_actions
def run_initial_state_actions
# only run the initial state actions if the standard initial state hasn't
# been changed
if self.class.initial_state.to_sym == current_state
original_run_initial_state_actions
end
end
def self.feed_options(user)
{
:title => 'Tracks Actions',
:description => "Actions for #{user.display_name}"
}
end
def starred?
tags.any? {|tag| tag.name == STARRED_TAG_NAME}
end
def toggle_star!
if starred?
_remove_tags STARRED_TAG_NAME
tags.reload
self.starred= !starred?
end
def starred=(starred)
if starred
_add_tags STARRED_TAG_NAME unless starred?
else
_add_tags(STARRED_TAG_NAME)
tags.reload
end
starred?
_remove_tags STARRED_TAG_NAME
end
starred
end
def from_recurring_todo?
@ -284,20 +285,24 @@ class Todo < ActiveRecord::Base
return @predecessor_array
end
def add_predecessor(t)
@predecessor_array = predecessors
@predecessor_array << t
end
# Return todos that should be activated if the current todo is completed
def pending_to_activate
return successors.find_all {|t| t.uncompleted_predecessors.empty?}
# activate todos that should be activated if the current todo is completed
def activate_pending_todos
pending_todos = successors.find_all {|t| t.uncompleted_predecessors.empty?}
pending_todos.each {|t| t.activate! }
return pending_todos
end
# Return todos that should be blocked if the current todo is undone
def active_to_block
return successors.find_all {|t| t.active? or t.deferred?}
def block_successors
active_successors = successors.find_all {|t| t.active? or t.deferred?}
active_successors.each {|t| t.block!}
return active_successors
end
def raw_notes=(value)
@ -305,27 +310,27 @@ class Todo < ActiveRecord::Base
end
# Rich Todo API
def self.from_rich_message(user, default_context_id, description, notes)
fields = description.match(/([^>@]*)@?([^>]*)>?(.*)/)
description = fields[1].strip
context = fields[2].strip
project = fields[3].strip
context = nil if context == ""
project = nil if project == ""
context_id = default_context_id
unless(context.nil?)
found_context = user.active_contexts.find_by_namepart(context)
found_context = user.contexts.active.find_by_namepart(context)
found_context = user.contexts.find_by_namepart(context) if found_context.nil?
context_id = found_context.id unless found_context.nil?
end
unless user.contexts.exists? context_id
raise(CannotAccessContext, "Cannot access a context that does not belong to this user.")
end
project_id = nil
unless(project.blank?)
if(project[0..3].downcase == "new:")
@ -333,12 +338,12 @@ class Todo < ActiveRecord::Base
found_project.name = project[4..255+4].strip
found_project.save!
else
found_project = user.active_projects.find_by_namepart(project)
found_project = user.projects.active.find_by_namepart(project)
found_project = user.projects.find_by_namepart(project) if found_project.nil?
end
project_id = found_project.id unless found_project.nil?
end
todo = user.todos.build
todo.description = description
todo.raw_notes = notes
@ -346,4 +351,5 @@ class Todo < ActiveRecord::Base
todo.project_id = project_id unless project_id.nil?
return todo
end
end

View file

@ -1,4 +1,5 @@
require 'digest/sha1'
require 'bcrypt'
class User < ActiveRecord::Base
# Virtual attribute for the unencrypted password
@ -59,35 +60,23 @@ class User < ActiveRecord::Base
self.update_positions(projects.map{ |p| p.id })
return projects
end
def actionize(user_id, scope_conditions = {})
@state = scope_conditions[:state]
query_state = ""
query_state = "AND project.state = '" + @state +"' "if @state
projects = Project.find_by_sql([
"SELECT project.id, count(todo.id) as p_count " +
"FROM projects as project " +
"LEFT OUTER JOIN todos as todo ON todo.project_id = project.id "+
"WHERE project.user_id = ? AND NOT (todo.state='completed') " +
query_state +
" GROUP BY project.id ORDER by p_count DESC",user_id])
self.update_positions(projects.map{ |p| p.id })
projects = find(:all, :conditions => scope_conditions)
return projects
def actionize(scope_conditions = {})
todos_in_project = find(:all, :conditions => scope_conditions, :include => [:todos])
todos_in_project.sort!{ |x, y| -(x.todos.active.count <=> y.todos.active.count) }
todos_in_project.reject{ |p| p.todos.active.count > 0 }
sorted_project_ids = todos_in_project.map {|p| p.id}
all_project_ids = find(:all).map {|p| p.id}
other_project_ids = all_project_ids - sorted_project_ids
update_positions(sorted_project_ids + other_project_ids)
return find(:all, :conditions => scope_conditions)
end
end
has_many :active_projects,
:class_name => 'Project',
:order => 'projects.position ASC',
:conditions => [ 'state = ?', 'active' ]
has_many :active_contexts,
:class_name => 'Context',
:order => 'position ASC',
:conditions => [ 'hide = ?', false ]
has_many :todos,
:order => 'todos.completed_at DESC, todos.created_at DESC',
:dependent => :delete_all
has_many :project_hidden_todos,
:conditions => ['(state = ? OR state = ?)', 'project_hidden', 'active']
has_many :recurring_todos,
:order => 'recurring_todos.completed_at DESC, recurring_todos.created_at DESC',
:dependent => :delete_all
@ -99,33 +88,16 @@ class User < ActiveRecord::Base
find(:all, :conditions => ['show_from <= ?', Time.zone.now ]).collect { |t| t.activate! }
end
end
has_many :pending_todos,
:class_name => 'Todo',
:conditions => [ 'state = ?', 'pending' ],
:order => 'show_from ASC, todos.created_at DESC'
has_many :completed_todos,
:class_name => 'Todo',
:conditions => ['todos.state = ? AND NOT(todos.completed_at IS NULL)', 'completed'],
:order => 'todos.completed_at DESC',
:include => [ :project, :context ] do
def completed_within( date )
reject { |x| x.completed_at < date }
end
def completed_more_than( date )
reject { |x| x.completed_at > date }
end
end
has_many :notes, :order => "created_at DESC", :dependent => :delete_all
has_one :preference, :dependent => :destroy
attr_protected :is_admin
validates_presence_of :login
validates_presence_of :password, :if => :password_required?
validates_length_of :password, :within => 5..40, :if => :password_required?
validates_presence_of :password_confirmation, :if => :password_required?
validates_confirmation_of :password
validates_confirmation_of :password
validates_length_of :login, :within => 3..80
validates_uniqueness_of :login, :on => :create
validates_presence_of :open_id_url, :if => :using_openid?
@ -137,7 +109,7 @@ class User < ActiveRecord::Base
#for will_paginate plugin
cattr_accessor :per_page
@@per_page = 5
def validate
unless Tracks::Config.auth_schemes.include?(auth_type)
errors.add("auth_type", "not a valid authentication type (#{auth_type})")
@ -152,43 +124,44 @@ class User < ActiveRecord::Base
return nil if candidate.nil?
if Tracks::Config.auth_schemes.include?('database')
return candidate if candidate.auth_type == 'database' && candidate.crypted_password == sha1(pass)
return candidate if candidate.auth_type == 'database' and
candidate.password_matches? pass
end
if Tracks::Config.auth_schemes.include?('ldap')
return candidate if candidate.auth_type == 'ldap' && SimpleLdapAuthenticator.valid?(login, pass)
end
if Tracks::Config.auth_schemes.include?('cas')
# because we can not auth them with out thier real password we have to settle for this
return candidate if candidate.auth_type.eql?("cas")
end
if Tracks::Config.auth_schemes.include?('open_id')
# hope the user enters the correct data
return candidate if candidate.auth_type.eql?("open_id")
end
return nil
end
def self.find_by_open_id_url(raw_identity_url)
normalized_open_id_url = OpenIdAuthentication.normalize_identifier(raw_identity_url)
find(:first, :conditions => ['open_id_url = ?', normalized_open_id_url])
end
def self.no_users_yet?
count == 0
end
def self.find_admin
find(:first, :conditions => [ "is_admin = ?", true ])
find(:first, :conditions => [ "is_admin = ?", true ])
end
def to_param
login
end
def display_name
if first_name.blank? && last_name.blank?
return login
@ -199,13 +172,13 @@ class User < ActiveRecord::Base
end
"#{first_name} #{last_name}"
end
def change_password(pass,pass_confirm)
self.password = pass
self.password_confirmation = pass_confirm
save!
end
def time
Time.now.in_time_zone(prefs.time_zone)
end
@ -213,23 +186,23 @@ class User < ActiveRecord::Base
def date
time.midnight
end
def at_midnight(date)
return ActiveSupport::TimeZone[prefs.time_zone].local(date.year, date.month, date.day, 0, 0, 0)
end
def generate_token
self.token = Digest::SHA1.hexdigest "#{Time.now.to_i}#{rand}"
self.token = sha1 "#{Time.now.to_i}#{rand}"
end
def remember_token?
remember_token_expires_at && Time.now.utc < remember_token_expires_at
remember_token_expires_at && Time.now.utc < remember_token_expires_at
end
# These create and unset the fields required for remembering users between browser closes
def remember_me
self.remember_token_expires_at = 2.weeks.from_now.utc
self.remember_token ||= self.class.sha1("#{login}--#{remember_token_expires_at}")
self.remember_token ||= sha1("#{login}--#{remember_token_expires_at}")
save(false)
end
@ -239,38 +212,55 @@ class User < ActiveRecord::Base
save(false)
end
# Returns true if the user has a password hashed using SHA-1.
def uses_deprecated_password?
crypted_password =~ /^[a-f0-9]{40}$/i
end
def password_matches?(pass)
if uses_deprecated_password?
crypted_password == sha1(pass)
else
BCrypt::Password.new(crypted_password) == pass
end
end
def salted(s)
"#{Tracks::Config.salt}--#{s}--"
end
def sha1(s)
Digest::SHA1.hexdigest salted s
end
def hash(s)
BCrypt::Password.create s
end
protected
def self.sha1(s)
Digest::SHA1.hexdigest("#{Tracks::Config.salt}--#{s}--")
end
def crypt_password
return if password.blank?
write_attribute("crypted_password", self.class.sha1(password)) if password == password_confirmation
write_attribute("crypted_password", hash(password)) if password == password_confirmation
end
def password_required?
auth_type == 'database' && crypted_password.blank? || !password.blank?
end
def using_openid?
auth_type == 'open_id'
end
def password_matches?(pass)
crypted_password == sha1(pass)
end
def normalize_open_id_url
return if open_id_url.nil?
# fixup empty url value
if open_id_url.empty?
self.open_id_url = nil
return
end
self.open_id_url = OpenIdAuthentication.normalize_identifier(open_id_url)
end
end

View file

@ -12,15 +12,15 @@
<label for="context_name">Context name</label><br/>
<%= text_field('context', 'name', :class => 'context-name') %><br/>
<%= text_field('context', 'name', :class => 'context-name', :tabindex => next_tab_index) %><br/>
<label for="context_hide">Hide from front page?</label>
<%= check_box('context', 'hide', :class => 'context-hide') %>
<%= check_box('context', 'hide', {:class => 'context-hide', :tabindex => next_tab_index}) %>
<input type="hidden" name="wants_render" value="true" />
<div class="submit_box">
<div class="widgets">
<button type="submit" class="positive" id="<%= dom_id(context, 'submit') %>" tabindex="15">
<button type="submit" class="positive" id="<%= dom_id(context, 'submit') %>" tabindex="<%=next_tab_index%>">
<%=image_tag("accept.png", :alt => "") %>
<%= t 'common.update' %>
</button>

View file

@ -1,3 +1,4 @@
<%- reset_tab_index %>
<div id="context_new_container">
<div id="toggle_context_link" class="hide_form">
@ -10,10 +11,10 @@
<div id="error_status"><%= error_messages_for('context') %></div>
<label for="context_name"><%= t 'contexts.context_name' %></label><br />
<%= text_field( "context", "name" ) %><br />
<%= text_field( "context", "name", :tabindex => next_tab_index ) %><br />
<label for="context_hide"><%= t 'contexts.context_hide' %></label>
<%= check_box( "context", "hide" ) %><br />
<%= check_box( "context", "hide", {:tabindex => next_tab_index} ) %><br />
<div class="submit_box">
<div class="widgets">

View file

@ -56,6 +56,10 @@
<ul>
<li>/todos.xml</li>
<li>/todos/<code>ID</code>.xml</li>
<li>/tickler.xml</li>
<li>/done.xml</li>
<li>/hidden.xml</li>
<li>/calendar.xml</li>
<li>/contexts.xml</li>
<li>/contexts/<code>ID</code>.xml</li>
<li>/contexts/<code>ID</code>/todos.xml</li>
@ -64,6 +68,17 @@
<li>/projects/<code>ID</code>/todos.xml</li>
</ul>
<p>For the todo resources (todos, tickler, done, hidden and calendar) you can limit the returned
field to <code>ID, created_at, modified_at, completed_at</code> by adding the parameter
<code>limit_fields</code> and setting it to <code>index</code>. For example:</p>
<pre>
<code>
$ curl -u username:p4ssw0rd -H "Content-Type: text/xml" \
<%= home_url %>tickler.xml?limit_fields=index
</code>
</pre>
<h2>Writing to the API</h2>
<p>The API provides mechanisms for adding, updating and deleting resources using the HTTP methods <code>PUT</code>, <code>POST</code> and <code>DELETE</code> in combination with the content.</p>
@ -173,17 +188,17 @@ $ curl -u username:p4ssw0rd -H "Content-Type: text/xml" \
<pre>
<code>
$ script/console
$ script/console
Loading development environment (Rails 1.2.4)
&gt;&gt; class Context &lt; ActiveResource::Base; end
=&gt; nil
&gt;&gt; Context.site = "<%= home_url %>"
=&gt; "<%= home_url %>"
&gt;&gt; Context.site.user = "username"
=&gt; "username"
=&gt; "<%= home_url %>"
&gt;&gt; Context.site.user = "username"
=&gt; "username"
&gt;&gt; Context.site.password = CGI.escape "p4ssw0rd"
=&gt; "p4ssw0rd"
&gt;&gt; Context.site.password = CGI.escape "p4ssw0rd"
=&gt; "p4ssw0rd"
&gt;&gt; Context.find :first
=&gt; #&lt;Context:0x262396c @prefix_options={}, @attributes={...}&gt;
&gt;&gt; &gt;&gt; Context.find :all
@ -201,7 +216,7 @@ $ SITE="http://username:p4ssw0rd@<%= request.host_with_port %>" irb \
irb(main):001:0&gt; inbox = Tracks::Context.find :first
irb(main):002:0&gt; inbox.name
=&gt; "@inbox"
=&gt; "@inbox"
irb(main):003:0&gt;
</code>
</pre>

View file

@ -3,8 +3,8 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<%= stylesheet_link_tag "scaffold" %>
<%= javascript_include_tag 'jquery-1.5.2.min', 'jquery.cookie' %>
<%= javascript_include_tag 'jquery-1.6.2.min', 'jquery.cookie' %>
<title><%= @page_title -%></title>
</head>

View file

@ -2,9 +2,9 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<%= stylesheet_link_tag 'standard','superfish','niftyCorners', 'jquery-ui-1.8.11.custom', :cache => 'tracks-cached' %>
<%= stylesheet_link_tag 'standard','superfish','niftyCorners', 'jquery-ui-1.8.15.custom', :cache => 'tracks-cached' %>
<%= stylesheet_link_tag "print", :media => "print" %>
<%= javascript_include_tag 'jquery-1.5.2.min', 'jquery-ui-1.8.11.custom.min',
<%= javascript_include_tag 'jquery-1.6.2.min', 'jquery-ui-1.8.15.custom.min',
'jquery.truncator','jquery.jeditable.mini', 'jquery.cookie', 'jquery.blockUI',
'jquery.form',
:cache => 'jquery-cached' %>
@ -48,7 +48,9 @@
</h1>
</div>
<div id="minilinks">
<%= link_to(t('layouts.toggle_notes'), "#", {:accesskey => "S", :title => t('layouts.toggle_notes_title'), :id => "toggle-notes-nav"}) %>
<%= link_to(t('layouts.toggle_contexts'), "#", {:title => t('layouts.toggle_contexts_title'), :id => "toggle-contexts-nav"}) %>
&nbsp;|&nbsp;
<%= link_to(t('layouts.toggle_notes'), "#", {:accesskey => "S", :title => t('layouts.toggle_notes_title'), :id => "toggle-notes-nav"}) %>
&nbsp;|&nbsp;
<%= link_to( t('common.logout') + " (#{current_user.display_name}) »", logout_path) %>
</div>
@ -62,13 +64,14 @@
<ul>
<li><%= navigation_link( t('common.contexts'), contexts_path, {:accesskey=>"c", :title=>t('layouts.navigation.contexts_title')} ) %></li>
<li><%= navigation_link( t('common.notes'), notes_path, {:accesskey => "o", :title => t('layouts.navigation.notes_title')} ) %></li>
<li><%= navigation_link( t('common.review'), review_path, {:accesskey => "r", :title => t('layouts.navigation.review_title')} ) %></li>
<li><%= navigation_link( t('layouts.navigation.recurring_todos'), {:controller => "recurring_todos", :action => "index"}, :title => t('layouts.navigation.recurring_todos_title')) %></li>
</ul>
</li>
<li><a href="#"><%= t('layouts.navigation.view') %></a>
<ul>
<li><%= navigation_link( t('layouts.navigation.calendar'), calendar_path, :title => t('layouts.navigation.calendar_title')) %></li>
<li><%= navigation_link( t('layouts.navigation.completed_tasks'), done_path, {:accesskey=>"d", :title=>t('layouts.navigation.completed_tasks_title')} ) %></li>
<li><%= navigation_link( t('layouts.navigation.completed_tasks'), done_overview_path, {:accesskey=>"d", :title=>t('layouts.navigation.completed_tasks_title')} ) %></li>
<li><%= navigation_link( t('layouts.navigation.feeds'), {:controller => "feedlist", :action => "index"}, :title => t('layouts.navigation.feeds_title')) %></li>
<li><%= navigation_link( t('layouts.navigation.stats'), {:controller => "stats", :action => "index"}, :title => t('layouts.navigation.stats_title')) %></li>
</ul>

View file

@ -5,4 +5,5 @@
-%>
$('div#navcontainer').hide();
$('div#content').html('<%=theHtml%>');
refresh_page(); // refresh the page. if it fails, the message above remains
<% end -%>

View file

@ -1,8 +1,9 @@
<%
submit_text ||= t('common.update')
note = note_edit_form
form_for(note_edit_form, :html => {
:id => dom_id(note_edit_form, 'edit_form'),
form_for(note, :html => {
:id => dom_id(note, 'edit_form'),
:class => "inline-form edit-note-form"}) do |f|
-%>
@ -13,11 +14,11 @@ form_for(note_edit_form, :html => {
<div class="submit_box">
<div class="widgets">
<button type="submit" class="positive" id="<%= dom_id(note_edit_form, 'submit') %>" tabindex="15">
<button type="submit" class="positive" id="<%= dom_id(note, 'submit') %>" tabindex="15">
<%=image_tag("accept.png", :alt => "") %>
<%= submit_text %>
</button>
<a href="" class="negative" id="neg_<%=dom_id(note_edit_form, 'edit_form')%>">
<a href="" class="negative" id="neg_<%=dom_id(note, 'edit_form')%>">
<%=image_tag("cancel.png", :alt => "") %>
<%= t 'common.cancel' %>
</a>

View file

@ -0,0 +1,37 @@
<label><%= t('preferences.token_header') %></label><br/><br/>
<%= t('preferences.token_description') %>: <span class="highlight"><%= current_user.token %></span>
<br/><br/>
<div class="pref_new_token">
<% # TODO: make remote AJAX call for new token %>
<%= link_to(
t('preferences.generate_new_token'),
refresh_token_user_path( current_user ),
:method => :post,
:confirm => t('preferences.generate_new_token_confirm'),
:id=>'prefs_new_token') %>
</div>
<br/>
<% if Tracks::Config.auth_schemes.length > 1 %>
<label for="user_auth_type"><%= t('users.label_auth_type') %>:</label><br/>
<% Tracks::Config.auth_schemes.each do |scheme| %>
<%= radio_button_tag('user[auth_type]', scheme, current_user.auth_type == scheme) %><%=scheme%> <br/>
<% end %>
<% end %>
<br/>
<div id="open_id" style="display:<%= current_user.auth_type == 'open_id' ? 'block' : 'none' %>">
<label for="openid_url"><%= t('users.identity_url') %>:</label><br/>
<input type="text" name="user[open_id_url]" value="<%= current_user.open_id_url %>" class="open_id" />
</div>
<div id="database" style="display:<%= current_user.auth_type == 'database' ? 'block' : 'none' %>">
<%= render :partial => 'users/update_password' %>
</div>

View file

@ -0,0 +1,23 @@
<%= pref_with_text_field('prefs', 'date_format') %>
<div class='prefs_example'>This will result in: <span id='prefs.date_format'><%= l(Date.today, :format => current_user.prefs.date_format) %></span></div>
<br/>
Or pick one of the following:<br/>
<% %w{default short long longer}.each do |format| %>
<%= radio_button_tag("date_picker1", t("date.formats.#{format}")) %> <%= l(Date.today, :format => format.to_sym) %> <br/>
<% end %>
<br/>
<%= pref_with_text_field('prefs', 'title_date_format') %>
<div class='prefs_example'>This will result in: <span id='prefs.title_date_format'><%= l(Date.today, :format => current_user.prefs.title_date_format) %></span></div>
<br/>
Or pick one of the following:<br/>
<% %w{default short long longer}.each do |format| %>
<%= radio_button_tag("date_picker2", t("date.formats.#{format}")) %> <%= l(Date.today, :format => format.to_sym) %> <br/>
<% end %>
<br/>
<%= pref('prefs', 'time_zone') { time_zone_select('prefs','time_zone') } %>
<%= pref_with_select_field('prefs', "week_starts", (0..6).to_a.map {|num| [t('date.day_names')[num], num] }) %>

View file

@ -0,0 +1,6 @@
<%= pref_with_text_field 'user', 'first_name' %>
<%= pref_with_text_field 'user', 'last_name' %>
<%= pref_with_select_field('prefs', 'locale', I18n.available_locales.map {|l| l.to_s}) %>
<% if current_user.is_admin? %>
<%= pref_with_text_field('prefs', 'admin_email') %>
<% end %>

View file

@ -0,0 +1,14 @@
<%= pref_with_select_field('prefs', "due_style", [[t('models.preference.due_styles')[0],Preference.due_styles[:due_in_n_days]],[t('models.preference.due_styles')[1],Preference.due_styles[:due_on]]]) %>
<%= pref_with_select_field('prefs', "show_completed_projects_in_sidebar") %>
<%= pref_with_select_field('prefs', "show_hidden_projects_in_sidebar") %>
<%= pref_with_select_field('prefs', "show_hidden_contexts_in_sidebar") %>
<%= pref_with_select_field('prefs', "show_project_on_todo_done") %>
<%= pref_with_text_field('prefs', 'staleness_starts') %>
<%= pref_with_text_field('prefs', 'review_period') %>
<%= pref_with_text_field('prefs', 'show_number_completed') %>
<%= pref_with_text_field('prefs', 'refresh') %>
<%= pref_with_select_field('prefs', "verbose_action_descriptors") %>
<%= pref_with_text_field('prefs', "mobile_todos_per_page") %>
<%= pref_with_text_field('prefs', "sms_email") %>
<%= pref('prefs', "sms_context") { select('prefs', 'sms_context_id', current_user.contexts.map{|c| [c.name, c.id]}) } %>

View file

@ -1,59 +0,0 @@
<div id="display_box" class="container context">
<%= render :partial => 'help' %>
</div>
<div id="input_box" class="container context">
<% form_tag :action => 'update' do %>
<table>
<tr>
<td><label><%= User.human_attribute_name('first_name') %></label></td>
<td><%= text_field 'user', 'first_name' %></td>
</tr>
<tr>
<td><label><%= User.human_attribute_name('last_name') %></label></td>
<td><%= text_field 'user', 'last_name' %></td>
</tr>
<%
def table_row(pref_name, nowrap_label = false, &block)
nowrap_attribute = nowrap_label ? ' nowrap="nowrap"' : ''
s = %Q|<tr>\n<td#{nowrap_attribute}><label>#{Preference.human_attribute_name(pref_name)}:</label></td>\n<td>\n|
s << yield
s << "\n</td></tr>"
s
end
def row_with_select_field(pref_name, collection = [ [t('preferences.is_true'),true], [t('preferences.is_false'), false] ], nowrap_label = false)
table_row(pref_name, nowrap_label) { select('prefs', pref_name, collection) }
end
def row_with_text_field(pref_name, nowrap_label = false)
table_row(pref_name, nowrap_label) { text_field('prefs', pref_name) }
end
%>
<%= row_with_select_field('locale', I18n.available_locales.map {|l| l.to_s}) %>
<%= row_with_text_field('date_format') %>
<%= row_with_text_field('title_date_format') %>
<%= table_row('time_zone', false) { time_zone_select('prefs','time_zone') } %>
<%= row_with_select_field("week_starts", (0..6).to_a.map {|num| [t('date.day_names')[num], num] }) %>
<%= row_with_select_field("due_style", [[t('models.preference.due_styles')[0],Preference.due_styles[:due_in_n_days]],[t('models.preference.due_styles')[1],Preference.due_styles[:due_on]]]) %>
<%= row_with_select_field("show_completed_projects_in_sidebar") %>
<%= row_with_select_field("show_hidden_projects_in_sidebar") %>
<%= row_with_select_field("show_hidden_contexts_in_sidebar") %>
<%= row_with_select_field("show_project_on_todo_done") %>
<% if current_user.is_admin? %> <%= row_with_text_field('admin_email') %> <% end %>
<%= row_with_text_field('staleness_starts', false) %>
<%= row_with_text_field('show_number_completed') %>
<%= row_with_text_field('refresh') %>
<%= row_with_select_field("verbose_action_descriptors") %>
<%= row_with_text_field("mobile_todos_per_page") %>
<%= row_with_text_field("sms_email") %>
<%= table_row("sms_context", false) { select('prefs', 'sms_context_id', current_user.contexts.map{|c| [c.name, c.id]}) } %>
<tr><td><%= submit_tag t('common.update') %></td>
<td><%= link_to t('common.cancel'), :action => 'index' %></td>
</tr>
</table>
<% end %>
</div>

View file

@ -1,68 +1,32 @@
<div id="single_box" class="container context prefscontainer">
<div id="display_box">
<h2><%= t('preferences.title') %></h2>
<div id="edit_error_status"><%= error_messages_for(:user) + error_messages_for(:prefs) %></div>
<ul id="prefs">
<li><%= User.human_attribute_name('first_name') %>: <span class="highlight"><%= current_user.first_name %></span></li>
<li><%= User.human_attribute_name('last_name') %>: <span class="highlight"><%= current_user.last_name %></span></li>
<li><%= Preference.human_attribute_name('date_format') %>: <span class="highlight"><%= current_user.prefs.date_format %></span> Your current date: <%= format_date(current_user.time) %></li>
<li><%= Preference.human_attribute_name('locale') %>: <span class="highlight"><%= current_user.prefs.locale %></span></li>
<li><%= Preference.human_attribute_name('title_date_format') %>: <span class="highlight"><%= current_user.prefs.title_date_format %></span> Your current title date: <%= current_user.time.strftime(current_user.prefs.title_date_format) %></li>
<li><%= Preference.human_attribute_name('time_zone') %>: <span class="highlight"><%= current_user.prefs.time_zone %></span> Your current time: <%= current_user.time.strftime('%I:%M %p') %></li>
<li><%= Preference.human_attribute_name('week_starts') %>: <span class="highlight"><%= t('date.day_names')[current_user.prefs.week_starts] %></span></li>
<li><%= t('preferences.show_number_completed', :number=> "<span class=\"highlight\">#{current_user.prefs.show_number_completed}</span>")%></li>
<li><%= Preference.human_attribute_name('show_completed_projects_in_sidebar') %>: <span class="highlight"><%= current_user.prefs.show_completed_projects_in_sidebar %></span></li>
<li><%= Preference.human_attribute_name('show_hidden_projects_in_sidebar') %>: <span class="highlight"><%= current_user.prefs.show_hidden_projects_in_sidebar %></span></li>
<li><%= Preference.human_attribute_name('show_hidden_contexts_in_sidebar') %>: <span class="highlight"><%= current_user.prefs.show_hidden_contexts_in_sidebar %></span></li>
<li><%= Preference.human_attribute_name('show_project_on_todo_done') %>: <span class="highlight"><%= current_user.prefs.show_project_on_todo_done %></span></li>
<li><%= t('preferences.staleness_starts_after', :days => "<span class=\"highlight\">#{current_user.prefs.staleness_starts}</span>") %></li>
<li><%= Preference.human_attribute_name('due_style') %>: <span class="highlight">
<% if prefs.due_style == Preference.due_styles[:due_in_n_days] %>
<%= t('models.preference.due_styles')[0] %>
<% else %>
<%= t('models.preference.due_styles')[1] %>
<% end %>
</span></li>
<% if current_user.is_admin? %>
<li><%= Preference.human_attribute_name('admin_email') %>: <span class="highlight"><%= current_user.prefs.admin_email %></span></li>
<% end %>
<li><%= Preference.human_attribute_name('refresh') %>: <span class="highlight"><%= current_user.prefs.refresh %></span></li>
<li><%= Preference.human_attribute_name('verbose_action_descriptors') %>: <span class="highlight"><%= current_user.prefs.verbose_action_descriptors %></span></li>
<li><%= Preference.human_attribute_name('mobile_todos_per_page') %>: <span class="highlight"><%= current_user.prefs.mobile_todos_per_page %></span></li>
<li><%= Preference.human_attribute_name('sms_email') %>: <span class="highlight"><%= current_user.prefs.sms_email %></span></li>
<li><%= Preference.human_attribute_name('sms_context') %>: <span class="highlight"><%= current_user.prefs.sms_context.nil? ? t('preferences.sms_context_none') : current_user.prefs.sms_context.name %></span></li>
</ul>
<div class="actions">
<%= link_to t('preferences.edit_preferences') + " &raquo;", { :controller => 'preferences', :action => 'edit'}, :class => 'edit_link' %>
</div>
<h2><%= t('preferences.token_header') %></h2>
<div id="token_area">
<div class="description"><%= t('preferences.token_description') %>:</div>
<div id="token"><span class="highlight"><%= current_user.token %></span></div>
<div class="token_regenerate">
<%= button_to t('preferences.generate_new_token'), refresh_token_user_path(current_user),
:confirm => t('preferences.generate_new_token_confirm') %>
<% form_tag :action => 'update' do %>
<div id="tabs">
<ul>
<li><a href="#tabs-1"><%= t('preferences.tabs.profile')%></a></li>
<li><a href="#tabs-2"><%= t('preferences.tabs.authentication')%></a></li>
<li><a href="#tabs-3"><%= t('preferences.tabs.date_and_time')%></a></li>
<li><a href="#tabs-4"><%= t('preferences.tabs.tracks_behavior')%></a></li>
</ul>
<div id="tabs-1">
<%= render :partial => 'profile'%>
</div>
<div id="tabs-2">
<%= render :partial => 'authentication'%>
</div>
<div id="tabs-3">
<%= render :partial => 'date_and_time'%>
</div>
<div id="tabs-4">
<%= render :partial => 'tracks_behavior'%>
</div>
</div>
<h2><%= t('preferences.authentication_header') %></h2>
<div id="authentication_area">
<% if Tracks::Config.auth_schemes.length > 1 %>
<p><%= t('preferences.current_authentication_type', :auth_type => "<span class=\"highlight\">#{current_user.auth_type}</span>") %>.</p>
<div class="actions">
<%= link_to(t('preferences.change_authentication_type') + " &raquo;", change_auth_type_user_path(current_user), :class => 'edit_link') %>
</div>
<% end %>
<% if current_user.auth_type == 'database' %>
<div class="actions">
<%= link_to(t('preferences.change_password') + ' &raquo;', change_password_user_path(current_user)) %>
</div>
<% end %>
<% if current_user.auth_type == 'open_id' %>
<p><%= t('preferences.open_id_url') %> <span class="highlight"><%= current_user.open_id_url %></span>.</p>
<div class="actions">
<%= link_to(t('preferences.change_identity_url') + ' &raquo;', change_auth_type_user_path(current_user)) %>
</div>
<% end %>
</div>
</div>
<br/>
<button type="submit" id="prefs_submit"><%= t('common.update') %></button>
<% end %>
</div>

View file

@ -1,5 +0,0 @@
<%= hidden_field( "project", "id" ) %>
<label for="project_name">Project name</label><br />
<%= text_field( "project", "name" ) %>
<br />
<br />

View file

@ -1,3 +1,4 @@
<%- reset_tab_index %>
<div id="project_new_project_container">
<div id="toggle_project_link" class="hide_form">
@ -9,19 +10,19 @@
<div id="error_status"><%= error_messages_for("project") %></div>
<label for="project_name"><%= Project.human_attribute_name(:name) %>:</label><br />
<%= text_field 'project', 'name', "tabindex" => 1 %><br />
<%= text_field 'project', 'name', "tabindex" => next_tab_index %><br />
<label for="project_description"><%= Project.human_attribute_name(:description) %> (<%= t('common.optional') %>):</label><br />
<%= text_area 'project', 'description', "cols" => 30, "rows" => 4, "tabindex" => 2 %><br />
<%= text_area 'project', 'description', "cols" => 30, "rows" => 4, "tabindex" => next_tab_index %><br />
<% unless @contexts.empty? -%>
<label for="default_context_name"><%= Project.human_attribute_name(:default_context_name) %> (<%= t('common.optional') %>):</label><br />
<%= text_field_tag("project[default_context_name]", @new_project.default_context.name, :tabindex => 3) %>
<%= text_field_tag("project[default_context_name]", @new_project.default_context.name, :tabindex => next_tab_index) %>
<br />
<% end -%>
<label for="default_tags"><%= Project.human_attribute_name(:default_tags) %> (<%= t('common.optional') %>):</label><br />
<%= text_field_tag("project[default_tags]", @new_project.default_tags, :tabindex => 4) %>
<%= text_field_tag("project[default_tags]", @new_project.default_tags, :tabindex => next_tab_index) %>
<br/>
@ -33,7 +34,7 @@
</div>
</div>
<input id="go_to_project" type="checkbox" tabindex="5" name="go_to_project"/><label for="go_to_project"><%= t('projects.to_new_project_page') %></label><br />
<input id="go_to_project" type="checkbox" tabindex="<%= next_tab_index%>" name="go_to_project"/><label for="go_to_project"><%= t('projects.to_new_project_page') %></label><br />
<% end -%>
</div>

View file

@ -1,9 +1,10 @@
<div class="container">
<h2 id="project_name">
<h2 id="project_name_container">
<% if collapsible -%>
<a href="#" class="container_toggle" id="toggle_p<%= project.id %>"><%= image_tag("collapse.png") %></a>
<% end -%>
<%= project.name -%></h2>
<div id="project_name" style="width: 100%;"><%= project.name -%></div>
</h2>
<div id="<%= dom_id(project, "container")%>" class="list"><%-# list needs to be here for edit form to work -%>
<%= render :partial => "projects/project_settings", :object => project, :locals => { :collapsible => collapsible } %>
</div>

View file

@ -11,28 +11,28 @@ project = project_form
<%= source_view_tag( @source_view ) -%>
<label for="project_name">Name:</label><br/>
<%= text_field :project, 'name', :class => 'project-name' %><br/>
<%= text_field :project, 'name', :class => 'project-name', :tabindex => next_tab_index %><br/>
<label for="project_description">Description (optional):</label><br/>
<%= text_area :project, 'description', "cols" => 30, "rows" => 4, :class => 'project-description' %><br/>
<%= text_area :project, 'description', "cols" => 30, "rows" => 4, :class => 'project-description', :tabindex => next_tab_index %><br/>
<label for="project_done">Project status:</label><br/>
<% ['active', 'hidden', 'completed'].each do | state | %>
<%= radio_button(:project, 'state', state) %> <%= state.titlecase %>
<%= radio_button(:project, 'state', state, {:tabindex => next_tab_index}) %> <%= state.titlecase %>
<% end %><br/>
<label for="project[default_context_name]">Default Context</label><br/>
<%= text_field_tag("project[default_context_name]", project.default_context.name, {:tabindex=>1,:size=> 25}) %>
<%= text_field_tag("project[default_context_name]", project.default_context.name, {:tabindex=>next_tab_index,:size=> 25}) %>
<br/>
<label for="project[default_tags]">Default Tags</label><br/>
<%= text_field_tag("project[default_tags]", project.default_tags, {:tabindex=>2,:size=> 25}) %>
<%= text_field_tag("project[default_tags]", project.default_tags, {:tabindex=>next_tab_index,:size=> 25}) %>
<br/>
<input type="hidden" name="wants_render" value="true" />
<div class="submit_box">
<div class="widgets" id="<%= dom_id(project, 'widgets') %>">
<button type="submit" class="positive" id="<%= dom_id(project, 'submit') %>" tabindex="15">
<button type="submit" class="positive" id="<%= dom_id(project, 'submit') %>" tabindex="<%=next_tab_index%>">
<%=image_tag("accept.png", :alt => "") %>
Update
</button>
@ -40,6 +40,10 @@ project = project_form
<%=image_tag("cancel.png", :alt => "") %>
Cancel
</a>
<a href="<%=set_reviewed_project_path(project)%>" id="<%= dom_id(project, 'reviewed') %>" class="reviewed">
<%=image_tag("reviewed.png", :alt => "") %>
Reviewed
</a>
</div>
</div>
<br/><br/>

View file

@ -2,6 +2,7 @@
project = project_listing
suppress_drag_handle ||= false
suppress_edit_button ||= false
suppress_delete_button ||= false
-%>
<div id="<%= dom_id(project, "container") %>" class="list">
<div id="<%= dom_id(project) %>" class="project sortable_row" style="display:block">
@ -12,13 +13,17 @@ suppress_edit_button ||= false
</div>
<% end -%>
<div class="data">
<%= link_to_project( project ) %><%= " (" + count_undone_todos_and_notes_phrase(project,"actions") + ")" %>
<span class="<%= needsreview_class( project ) %>">
<%= link_to_project( project ) %>
<%= " (" + count_undone_todos_and_notes_phrase(project,"actions") + ")" %>
</span>
</div>
<div class="buttons">
<span class="grey"><%= project.current_state.to_s.upcase %></span>
<%= link_to_delete_project(project, image_tag( "blank.png", :title => t('projects.delete_project_title'), :class=>"delete_item")) %>
<span class="grey"><%= project.aasm_current_state.to_s.upcase %></span>
<%= suppress_delete_button ? "" : link_to_delete_project(project, image_tag( "blank.png", :title => t('projects.delete_project_title'), :class=>"delete_item")) %>
<%= suppress_edit_button ? "" : link_to_edit_project(project, image_tag( "blank.png", :title => t('projects.edit_project_title'), :class=>"edit_item")) %>
</div>

View file

@ -1,16 +1,31 @@
<%-
total_count ||= -1
total_count_string = total_count!=-1 ? " / #{total_count}" : ""
suppress_sort_menu ||= false
suppress_drag_handle ||= false
-%>
<div class="project-state-group" id="list-<%= state %>-projects-container" <%= " style=\"display:none\"" if project_state_group.empty? %>>
<h2><span id="<%= state %>-projects-count" class="badge"><%= project_state_group.length %></span><%= t('states.'+state+'_plural' )%> <%= t('common.projects') %></h2>
<h2>
<span id="<%= state %>-projects-count" class="badge"><%= project_state_group.length%><%= total_count_string%></span>
<%= t('common.last' ) unless ( ['review','stalled','blocked','current'].include?(state) )%>
<%= t('states.'+state+'_plural' )%>
<%= t('common.projects') %><%= total_count==-1 ? "" : " ("+link_to("Show all", done_projects_path)+")"%>
</h2>
<% unless suppress_sort_menu %>
<div class="menu_sort"><span class="sort_separator"><%= t('common.sort.sort') %>&nbsp;</span>
<div class="alpha_sort">
<%= link_to("Alphabetically", alphabetize_projects_path(:state => state),
<%= link_to(t("common.sort.alphabetically"), alphabetize_projects_path(:state => state),
:id => "#{state}_alphabetize_link", :class => "alphabetize_link", :title => t('common.sort.alphabetically_title'), :x_confirm_message => t('common.sort.alphabetically_confirm')) %>
</div><span class="sort_separator">&nbsp;|&nbsp;</span><div class="tasks_sort">
<%= link_to("By number of tasks", actionize_projects_path(:state => state),
<%= link_to(t("common.sort.by_task_count"), actionize_projects_path(:state => state),
:id => "#{state}_actionize_link", :class => "actionize_link", :title => t('common.sort.by_task_count_title'), :x_confirm_message => t('common.sort.by_task_count_title_confirm')) %>
</div>
</div>
<% end %>
<div id="list-<%= state %>-projects" class="project-list">
<%= render :partial => 'project_listing', :collection => project_state_group %>
<%= render :partial => 'projects/project_listing', :collection => project_state_group, :locals => {:suppress_drag_handle => suppress_drag_handle} %>
</div>
</div>

View file

@ -0,0 +1,37 @@
<%
paginate_options = {
:class => :add_note_link,
:previous_label => '&laquo; '+ t('common.previous'),
:next_label => t('common.next')+' &raquo;',
:inner_window => 2
}
%>
<div id="display_box">
<div id="projects-empty-nd" style="<%= @no_projects ? 'display:block' : 'display:none'%>">
<div class="message"><p><%= t('projects.no_projects') %></p></div>
</div>
<div class="project-state-group" id="list-completed-projects-container" <%= " style=\"display:none\"" if @no_projects %>>
<%= will_paginate @projects, paginate_options %>
<h2>
<span id="completed-projects-count" class="badge"><%= "#{@total} (#{@range_low}-#{@range_high})" %></span>
<%= t('states.completed_plural' )%> <%= t('common.projects') %>
</h2>
<div id="list-completed-projects" class="project-list">
<%= render :partial => 'project_listing', :collection => @projects %>
</div>
</div>
<%= will_paginate @projects, paginate_options %>
</div>
<div id="input_box">
<div class="menu_sort"><h2><br/><%= t('common.sort.sort') %>&nbsp;<%= t('states.completed_plural' )%> <%= t('common.projects') %></h2>
<div class="alpha_sort">
<%= link_to(t("common.sort.alphabetically"), alphabetize_projects_path(:state => :completed),
:id => "completed_alphabetize_link", :class => "alphabetize_link", :title => t('common.sort.alphabetically_title'), :x_confirm_message => t('common.sort.alphabetically_confirm')) %>
</div><span class="sort_separator">&nbsp;|&nbsp;</span><div class="tasks_sort">
<%= link_to(t("common.sort.by_task_count"), actionize_projects_path(:state => :completed),
:id => "completed_actionize_link", :class => "actionize_link", :title => t('common.sort.by_task_count_title'), :x_confirm_message => t('common.sort.by_task_count_title_confirm')) %>
</div>
</div>
</div>

View file

@ -4,7 +4,7 @@
</div>
<%= render :partial => 'project_state_group', :object => @active_projects, :locals => { :state => 'active'} %>
<%= render :partial => 'project_state_group', :object => @hidden_projects, :locals => { :state => 'hidden'} %>
<%= render :partial => 'project_state_group', :object => @completed_projects, :locals => { :state => 'completed'} %>
<%= render :partial => 'project_state_group', :object => @completed_projects, :locals => { :state => 'completed', :total_count => @completed_count} %>
</div>
<div id="input_box">

View file

@ -24,4 +24,4 @@
<% else -%><%= render :partial => "notes/mobile_notes_summary", :collection => @project.notes %>
<% end -%>
<h2><%= t('projects.settings') %></h2>
<%= t('projects.state', :state => project.current_state.to_s) %>. <%= @project_default_context %>
<%= t('projects.state', :state => project.aasm_current_state.to_s) %>. <%= @project_default_context %>

View file

@ -0,0 +1,7 @@
<div id="projects-empty-nd" style="<%= @no_projects ? 'display:block' : 'display:none'%>">
<div class="message"><p><%= t('projects.no_projects') %></p></div>
</div>
<%= render :partial => 'project_state_group', :object => @projects_to_review, :locals => { :state => 'review', :suppress_sort_menu => true, :suppress_drag_handle => true} %>
<%= render :partial => 'project_state_group', :object => @stalled_projects, :locals => { :state => 'stalled', :suppress_sort_menu => true, :suppress_drag_handle => true} %>
<%= render :partial => 'project_state_group', :object => @blocked_projects, :locals => { :state => 'blocked', :suppress_sort_menu => true, :suppress_drag_handle => true} %>
<%= render :partial => 'project_state_group', :object => @current_projects, :locals => { :state => 'current', :suppress_sort_menu => true, :suppress_drag_handle => true} %>

View file

@ -6,7 +6,6 @@
-%>
update_project_page();
<% end %>
TracksForm.set_project_name_and_default_project_name("<%= escape_javascript(@project.name)%>");
<% else -%>
TracksPages.show_edit_errors(html_for_error_messages());
<% end %>
@ -24,12 +23,14 @@ function update_project_list_page() {
ProjectListPage.update_all_states_count(<%=@active_projects_count%>, <%=@hidden_projects_count%>, <%=@completed_projects_count%>);
ProjectListPage.show_or_hide_all_state_containers(<%= @show_active_projects %>, <%= @show_hidden_projects %>, <%= @show_completed_projects %>);
TracksForm.set_project_name_and_default_project_name("<%= escape_javascript(@project.name)%>");
}
function update_project_page() {
remove_project_edit_form();
update_and_show_project_settings();
TracksForm.set_project_name("<%= escape_javascript(@project.name)%>");
$("h2#project_name").html("<%= escape_javascript(@project.name)%>");
<% if @project.default_context %>
TracksForm.set_context_name_and_default_context_name("<%= escape_javascript(@project.default_context.name)%>");
<% end %>
@ -75,7 +76,7 @@ function remove_and_re_add_project() {
<%
# the following functions return empty string if rendering the partial is not
# necessary, for example the sidebar is not on the project list page, so do not
# render it into the function.
# render it into the function.
-%>
function html_for_project_listing() {
return "<%= source_view_is(:project_list) ? escape_javascript(render(:partial => 'project_listing', :object => @project )) : "" %>";

View file

@ -1,11 +1,11 @@
# TODO: is this dead code?
page.select('#project_status .active span').each do |element|
element.className = @project.current_state == :active ? 'active_state' : 'inactive_state'
element.className = @project.aasm_current_state == :active ? 'active_state' : 'inactive_state'
end
page.select('#project_status .hidden span').each do |element|
element.className = @project.current_state == :hidden ? 'active_state' : 'inactive_state'
element.className = @project.aasm_current_state == :hidden ? 'active_state' : 'inactive_state'
end
page.select('#project_status .completed span').each do |element|
element.className = @project.current_state == :completed ? 'active_state' : 'inactive_state'
element.className = @project.aasm_current_state == :completed ? 'active_state' : 'inactive_state'
end
page.notify :notice, "Set project status to #{@project.current_state}", 5.0
page.notify :notice, "Set project status to #{@project.aasm_current_state}", 5.0

View file

@ -12,101 +12,100 @@
<div id="recurring_todo_form_container">
<div id="recurring_todo">
<label for="recurring_todo_description"><%= Todo.human_attribute_name('description') %></label><%=
text_field_tag( "recurring_todo[description]", @recurring_todo.description, "size" => 30, "tabindex" => 1, "maxlength" => 100, :id => "edit_recurring_todo_description") -%>
text_field_tag( "recurring_todo[description]", @recurring_todo.description, "size" => 30, "tabindex" => next_tab_index, "maxlength" => 100, :id => "edit_recurring_todo_description") -%>
<label for="recurring_todo_notes"><%= Todo.human_attribute_name('notes') %></label><%=
text_area_tag( "recurring_todo[notes]", @recurring_todo.notes, {:cols => 29, :rows => 6, :tabindex => 2}) -%>
text_area_tag( "recurring_todo[notes]", @recurring_todo.notes, {:cols => 29, :rows => 6, :tabindex => next_tab_index}) -%>
<label for="edit_recurring_todo_project_name"><%= Todo.human_attribute_name('project') %></label>
<input id="edit_recurring_todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="<%= @recurring_todo.project.nil? ? 'None' : @recurring_todo.project.name.gsub(/"/,"&quot;") %>" />
<input id="edit_recurring_todo_project_name" name="project_name" autocomplete="off" tabindex="<%=next_tab_index%>" size="30" type="text" value="<%= @recurring_todo.project.nil? ? 'None' : @recurring_todo.project.name.gsub(/"/,"&quot;") %>" />
<div class="page_name_auto_complete" id="edit_project_list" style="display:none"></div>
<label for="edit_recurring_todo_context_name"><%= Todo.human_attribute_name('context') %></label>
<input id="edit_recurring_todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="<%= @recurring_todo.context.name %>" />
<input id="edit_recurring_todo_context_name" name="context_name" autocomplete="off" tabindex="<%=next_tab_index%>" size="30" type="text" value="<%= @recurring_todo.context.name %>" />
<div class="page_name_auto_complete" id="edit_context_list" style="display:none"></div>
<label for="edit_recurring_todo_tag_list"><%= "#{Todo.human_attribute_name('tags')} #{t('shared.separate_tags_with_commas')}"%></label>
<%= text_field_tag "edit_recurring_todo_tag_list", @recurring_todo.tag_list, :size => 30, :tabindex => 5 -%>
<%= text_field_tag "edit_recurring_todo_tag_list", @recurring_todo.tag_list, :size => 30, :tabindex => next_tab_index -%>
</div>
</div>
<div id="recurring_edit_period_id">
<div id="recurring_edit_period">
<label><%= t('todos.recurrence_period') %></label><br/>
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'daily', @recurring_todo.recurring_period == 'daily')%> <%= t('todos.recurrence.daily') %><br/>
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'weekly', @recurring_todo.recurring_period == 'weekly')%> <%= t('todos.recurrence.weekly') %><br/>
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'monthly', @recurring_todo.recurring_period == 'monthly')%> <%= t('todos.recurrence.monthly') %><br/>
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'yearly', @recurring_todo.recurring_period == 'yearly')%> <%= t('todos.recurrence.yearly') %><br/>
<%- #behaviour is set in index because behaviours in partials are not generated -%>
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'daily', @recurring_todo.recurring_period == 'daily', {:tabindex => next_tab_index})%> <%= t('todos.recurrence.daily') %><br/>
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'weekly', @recurring_todo.recurring_period == 'weekly', {:tabindex => next_tab_index})%> <%= t('todos.recurrence.weekly') %><br/>
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'monthly', @recurring_todo.recurring_period == 'monthly', {:tabindex => next_tab_index})%> <%= t('todos.recurrence.monthly') %><br/>
<%= radio_button_tag('recurring_edit_todo[recurring_period]', 'yearly', @recurring_todo.recurring_period == 'yearly', {:tabindex => next_tab_index})%> <%= t('todos.recurrence.yearly') %><br/>
</div>
<div id="recurring_timespan">
<br/>
<label for="recurring_todo[start_from]"><%= t('todos.recurrence.starts_on') %>: </label><%=
text_field_tag("recurring_todo_edit_start_from", format_date(@recurring_todo.start_from), "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %><br/>
text_field_tag("recurring_todo_edit_start_from", format_date(@recurring_todo.start_from), "size" => 12, "class" => "Date", "tabindex" => next_tab_index, "autocomplete" => "off") %><br/>
<br/>
<label for="recurring_todo[ends_on]"><%= t('todos.recurrence.ends_on') %>:</label><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', @recurring_todo.ends_on == 'no_end_date')%> <%= t('todos.recurrence.no_end_date') %><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', @recurring_todo.ends_on == 'ends_on_number_of_times')%>
<%= t('todos.recurrence.ends_on_number_times', :number => text_field( :recurring_todo, :number_of_occurences, "size" => 3, "tabindex" => 7)) %><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', @recurring_todo.ends_on == 'ends_on_end_date')%>
<%= t('todos.recurrence.ends_on_date', :date => text_field_tag('recurring_todo_edit_end_date', format_date(@recurring_todo.end_date), "size" => 12, "class" => "Date", "tabindex" => 8, "autocomplete" => "off")) %><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', @recurring_todo.ends_on == 'no_end_date', {:tabindex => next_tab_index})%> <%= t('todos.recurrence.no_end_date') %><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', @recurring_todo.ends_on == 'ends_on_number_of_times', {:tabindex => next_tab_index})%>
<%= t('todos.recurrence.ends_on_number_times', :number => text_field( :recurring_todo, :number_of_occurences, "size" => 3, "tabindex" => next_tab_index)) %><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', @recurring_todo.ends_on == 'ends_on_end_date', {:tabindex => next_tab_index})%>
<%= t('todos.recurrence.ends_on_date', :date => text_field_tag('recurring_todo_edit_end_date', format_date(@recurring_todo.end_date), "size" => 12, "class" => "Date", "tabindex" => next_tab_index, "autocomplete" => "off")) %><br/>
</div></div>
<div id="recurring_edit_daily" style="display:<%= @recurring_todo.recurring_period == 'daily' ? 'block' : 'none' %> ">
<label><%= t('todos.recurrence.daily_options') %></label><br/>
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', !@recurring_todo.only_work_days)%>
<%= t('todos.recurrence.daily_every_number_day', :number=> text_field_tag( 'recurring_todo[daily_every_x_days]', @recurring_todo.daily_every_x_days, {"size" => 3, "tabindex" => 9})) %><br/>
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day', @recurring_todo.only_work_days)%> <%= t('todos.recurrence.every_work_day') %><br/>
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', !@recurring_todo.only_work_days, {:tabindex => next_tab_index})%>
<%= t('todos.recurrence.daily_every_number_day', :number=> text_field_tag( 'recurring_todo[daily_every_x_days]', @recurring_todo.daily_every_x_days, {"size" => 3, "tabindex" => next_tab_index})) %><br/>
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day', @recurring_todo.only_work_days, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.every_work_day') %><br/>
</div>
<div id="recurring_edit_weekly" style="display:<%= @recurring_todo.recurring_period == 'weekly' ? 'block' : 'none' %>">
<label><%= t('todos.recurrence.weekly_options') %></label><br/>
<%= t('todos.recurrence.weekly_every_number_week', :number => text_field_tag('recurring_todo[weekly_every_x_week]', @recurring_todo.weekly_every_x_week, {"size" => 3, "tabindex" => 9})) %><br/>
<%= check_box_tag('recurring_todo[weekly_return_monday]', 'm', @recurring_todo.on_monday ) %> <%= t('date.day_names')[1] %>
<%= check_box_tag('recurring_todo[weekly_return_tuesday]', 't', @recurring_todo.on_tuesday) %> <%= t('date.day_names')[2] %>
<%= check_box_tag('recurring_todo[weekly_return_wednesday]', 'w', @recurring_todo.on_wednesday) %> <%= t('date.day_names')[3] %>
<%= check_box_tag('recurring_todo[weekly_return_thursday]', 't', @recurring_todo.on_thursday) %> <%= t('date.day_names')[4] %><br/>
<%= check_box_tag('recurring_todo[weekly_return_friday]', 'f', @recurring_todo.on_friday) %> <%= t('date.day_names')[5] %>
<%= check_box_tag('recurring_todo[weekly_return_saturday]', 's', @recurring_todo.on_saturday) %> <%= t('date.day_names')[6] %>
<%= check_box_tag('recurring_todo[weekly_return_sunday]', 's', @recurring_todo.on_sunday) %> <%= t('date.day_names')[0] %><br/>
<%= t('todos.recurrence.weekly_every_number_week', :number => text_field_tag('recurring_todo[weekly_every_x_week]', @recurring_todo.weekly_every_x_week, {"size" => 3, "tabindex" => next_tab_index})) %><br/>
<%= check_box_tag('recurring_todo[weekly_return_monday]', 'm', @recurring_todo.on_monday, {:tabindex => next_tab_index} ) %> <%= t('date.day_names')[1] %>
<%= check_box_tag('recurring_todo[weekly_return_tuesday]', 't', @recurring_todo.on_tuesday, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[2] %>
<%= check_box_tag('recurring_todo[weekly_return_wednesday]', 'w', @recurring_todo.on_wednesday, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[3] %>
<%= check_box_tag('recurring_todo[weekly_return_thursday]', 't', @recurring_todo.on_thursday, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[4] %><br/>
<%= check_box_tag('recurring_todo[weekly_return_friday]', 'f', @recurring_todo.on_friday, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[5] %>
<%= check_box_tag('recurring_todo[weekly_return_saturday]', 's', @recurring_todo.on_saturday, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[6] %>
<%= check_box_tag('recurring_todo[weekly_return_sunday]', 's', @recurring_todo.on_sunday, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[0] %><br/>
</div>
<div id="recurring_edit_monthly" style="display:<%= @recurring_todo.recurring_period == 'monthly' ? 'block' : 'none' %>">
<label><%= t('todos.recurrence.monthly_options') %></label><br/>
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', @recurring_todo.is_monthly_every_x_day || @recurring_todo.recurring_period == 'weekly')%>
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', @recurring_todo.is_monthly_every_x_day || @recurring_todo.recurring_period == 'weekly', {:tabindex => next_tab_index})%>
<%= t('todos.recurrence.day_x_on_every_x_month',
:day => text_field_tag('recurring_todo[monthly_every_x_day]', @recurring_todo.monthly_every_x_day, {"size" => 3, "tabindex" => 9}),
:month => text_field_tag('recurring_todo[monthly_every_x_month]', @recurring_todo.monthly_every_x_month, {"size" => 3, "tabindex" => 10})) %><br/>
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day', @recurring_todo.is_monthly_every_xth_day)%>
:day => text_field_tag('recurring_todo[monthly_every_x_day]', @recurring_todo.monthly_every_x_day, {"size" => 3, "tabindex" => next_tab_index}),
:month => text_field_tag('recurring_todo[monthly_every_x_month]', @recurring_todo.monthly_every_x_month, {"size" => 3, "tabindex" => next_tab_index})) %><br/>
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day', @recurring_todo.is_monthly_every_xth_day, {:tabindex => next_tab_index})%>
<%= t('todos.recurrence.monthly_every_xth_day',
:day => select_tag('recurring_todo[monthly_every_xth_day]', options_for_select(@xth_day, @xth_day[@recurring_todo.monthly_every_xth_day(1)-1][1])),
:day_of_week => select_tag('recurring_todo[monthly_day_of_week]' , options_for_select(@days_of_week, @recurring_todo.monthly_day_of_week), {}),
:month => text_field_tag('recurring_todo[monthly_every_x_month2]', @recurring_todo.monthly_every_x_month2, {"size" => 3, "tabindex" => 11})) %><br/>
:day => select_tag('recurring_todo[monthly_every_xth_day]', options_for_select(@xth_day, @xth_day[@recurring_todo.monthly_every_xth_day(1)-1][1]), {:tabindex => next_tab_index}),
:day_of_week => select_tag('recurring_todo[monthly_day_of_week]' , options_for_select(@days_of_week, @recurring_todo.monthly_day_of_week), {:tabindex => next_tab_index}),
:month => text_field_tag('recurring_todo[monthly_every_x_month2]', @recurring_todo.monthly_every_x_month2, {"size" => 3, "tabindex" => next_tab_index})) %><br/>
</div>
<div id="recurring_edit_yearly" style="display:<%= @recurring_todo.recurring_period == 'yearly' ? 'block' : 'none' %>">
<label><%= t('todos.recurrence.yearly_options') %></label><br/>
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', @recurring_todo.recurrence_selector == 0)%>
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', @recurring_todo.recurrence_selector == 0, {:tabindex => next_tab_index})%>
<%= t('todos.recurrence.yearly_every_x_day',
:month => select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year), {}),
:day => text_field_tag('recurring_todo[yearly_every_x_day]', @recurring_todo.yearly_every_x_day, "size" => 3, "tabindex" => 9)) %><br/>
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day', @recurring_todo.recurrence_selector == 1)%>
:month => select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year), {:tabindex => next_tab_index}),
:day => text_field_tag('recurring_todo[yearly_every_x_day]', @recurring_todo.yearly_every_x_day, "size" => 3, "tabindex" => next_tab_index)) %><br/>
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day', @recurring_todo.recurrence_selector == 1, {:tabindex => next_tab_index})%>
<%= t('todos.recurrence.yearly_every_xth_day',
:day => select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day, @recurring_todo.yearly_every_xth_day), {}),
:day_of_week => select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, @recurring_todo.yearly_day_of_week), {}),
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year2), {})) %><br/>
:day => select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day, @recurring_todo.yearly_every_xth_day), {:tabindex => next_tab_index}),
:day_of_week => select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, @recurring_todo.yearly_day_of_week), {:tabindex => next_tab_index}),
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, @recurring_todo.yearly_month_of_year2), {:tabindex => next_tab_index})) %><br/>
</div>
<div id="recurring_target">
<label><%= t('todos.recurrence.recurrence_on_options') %></label><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @recurring_todo.target == 'due_date')%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @recurring_todo.show_always?)%> <%= t('todos.recurrence.show_option_always') %>
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', !@recurring_todo.show_always?)%>
<%= t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', @recurring_todo.show_from_delta, {"size" => 3, "tabindex" => 12})) %><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', @recurring_todo.target == 'show_from_date')%> <%= t('todos.recurrence.from_tickler') %><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', @recurring_todo.target == 'due_date', {:tabindex => next_tab_index})%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', @recurring_todo.show_always?, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.show_option_always') %>
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', !@recurring_todo.show_always?, {:tabindex => next_tab_index})%>
<%= t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', @recurring_todo.show_from_delta, {"size" => 3, "tabindex" => next_tab_index})) %><br/>
<%= 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="15">
<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="15">
<button type="button" class="positive" id="recurring_todo_edit_action_cancel" tabindex="<%=next_tab_index%>">
<%=image_tag("cancel.png", :alt => "") %>
<%= t('common.cancel') %>
</button>

View file

@ -1,3 +1,4 @@
<%- 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
@ -7,60 +8,60 @@
<div id="recurring_todo_form_container">
<div id="recurring_todo">
<label for="recurring_todo_description"><%= Todo.human_attribute_name('description') %></label><%=
text_field_tag( "recurring_todo[description]", "", "size" => 30, "tabindex" => 1, "maxlength" => 100) -%>
text_field_tag( "recurring_todo[description]", "", "size" => 30, "tabindex" => next_tab_index, "maxlength" => 100) -%>
<label for="recurring_todo_notes"><%= Todo.human_attribute_name('notes') %></label><%=
text_area_tag( "recurring_todo[notes]", nil, {:cols => 29, :rows => 6, :tabindex => 2}) -%>
text_area_tag( "recurring_todo[notes]", nil, {:cols => 29, :rows => 6, :tabindex => next_tab_index}) -%>
<label for="recurring_todo_project_name"><%= Todo.human_attribute_name('project') %></label>
<input id="recurring_todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="" />
<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="4" size="30" type="text" value="<%= current_user.contexts.first.name unless current_user.contexts.first.nil?%>" />
<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>
<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 => 5 -%>
<%= text_field_tag "tag_list", nil, :size => 30, :tabindex => next_tab_index -%>
</div>
</div>
<div id="recurring_period_id">
<div id="recurring_period">
<label><%= t('todos.recurrence_period') %></label><br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'daily', true)%> <%= t('todos.recurrence.daily') %><br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'weekly')%> <%= t('todos.recurrence.weekly') %><br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'monthly')%> <%= t('todos.recurrence.monthly') %><br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'yearly')%> <%= t('todos.recurrence.yearly') %><br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'daily', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.daily') %><br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'weekly', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.weekly') %><br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'monthly', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.monthly') %><br/>
<%= radio_button_tag('recurring_todo[recurring_period]', 'yearly', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.yearly') %><br/>
</div>
<div id="recurring_timespan">
<br/>
<label for="recurring_todo[start_from]"><%= t('todos.recurrence.starts_on') %>:</label><%=
text_field(:recurring_todo, :start_from, "value" => format_date(current_user.time), "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %><br/>
text_field(:recurring_todo, :start_from, "value" => format_date(current_user.time), "size" => 12, "class" => "Date", "tabindex" => next_tab_index, "autocomplete" => "off") %><br/>
<br/>
<label for="recurring_todo[ends_on]"><%= t('todos.recurrence.ends_on') %>:</label><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', true)%> <%= t('todos.recurrence.no_end_date') %><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times')%> <%= t('todos.recurrence.ends_on_number_times', :number => text_field( :recurring_todo, :number_of_occurences, "size" => 3, "tabindex" => 7)) %> <br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date')%> <%= t('todos.recurrence.ends_on_date', :date => text_field(:recurring_todo, :end_date, "size" => 12, "class" => "Date", "tabindex" => 8, "autocomplete" => "off", "value" => "")) %><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.no_end_date') %><br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.ends_on_number_times', :number => text_field( :recurring_todo, :number_of_occurences, "size" => 3, "tabindex" => next_tab_index)) %> <br/>
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.ends_on_date', :date => text_field(:recurring_todo, :end_date, "size" => 12, "class" => "Date", "tabindex" => next_tab_index, "autocomplete" => "off", "value" => "")) %><br/>
</div></div>
<div id="recurring_daily" style="display:block">
<label><%= t('todos.recurrence.daily_options') %></label><br/>
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', true)%> <%= t('todos.recurrence.daily_every_number_day', :number=> text_field_tag( 'recurring_todo[daily_every_x_days]', "1", {"size" => 3, "tabindex" => 9})) %><br/>
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day')%> <%= t('todos.recurrence.every_work_day') %><br/>
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.daily_every_number_day', :number=> text_field_tag( 'recurring_todo[daily_every_x_days]', "1", {"size" => 3, "tabindex" => next_tab_index})) %><br/>
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.every_work_day') %><br/>
</div>
<div id="recurring_weekly" style="display:none">
<label><%= t('todos.recurrence.weekly_options') %></label><br/>
<%= t('todos.recurrence.weekly_every_number_week', :number => text_field_tag('recurring_todo[weekly_every_x_week]', 1, {"size" => 3, "tabindex" => 9})) %><br/>
<%= t('todos.recurrence.weekly_every_number_week', :number => text_field_tag('recurring_todo[weekly_every_x_week]', 1, {"size" => 3, "tabindex" => next_tab_index})) %><br/>
<% week_day = Time.new.wday -%>
<%# TODO: this should ideally use the 'week starts on' preferences setting, too? %>
<%= check_box_tag('recurring_todo[weekly_return_monday]', 'm', week_day == 1 ? true : false) %> <%= t('date.day_names')[1] %>
<%= check_box_tag('recurring_todo[weekly_return_tuesday]', 't', week_day == 2 ? true : false) %> <%= t('date.day_names')[2] %>
<%= check_box_tag('recurring_todo[weekly_return_wednesday]', 'w', week_day == 3 ? true : false) %> <%= t('date.day_names')[3] %>
<%= check_box_tag('recurring_todo[weekly_return_thursday]', 't', week_day == 4 ? true : false) %> <%= t('date.day_names')[4] %>
<%= check_box_tag('recurring_todo[weekly_return_friday]', 'f', week_day == 5 ? true : false) %> <%= t('date.day_names')[5] %>
<%= check_box_tag('recurring_todo[weekly_return_saturday]', 's', week_day == 6 ? true : false) %> <%= t('date.day_names')[6] %>
<%= check_box_tag('recurring_todo[weekly_return_sunday]', 's', week_day == 0 ? true : false) %> <%= t('date.day_names')[0] %>
<%= check_box_tag('recurring_todo[weekly_return_monday]', 'm', week_day == 1 ? true : false, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[1] %>
<%= check_box_tag('recurring_todo[weekly_return_tuesday]', 't', week_day == 2 ? true : false, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[2] %>
<%= check_box_tag('recurring_todo[weekly_return_wednesday]', 'w', week_day == 3 ? true : false, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[3] %>
<%= check_box_tag('recurring_todo[weekly_return_thursday]', 't', week_day == 4 ? true : false, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[4] %>
<%= check_box_tag('recurring_todo[weekly_return_friday]', 'f', week_day == 5 ? true : false, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[5] %>
<%= check_box_tag('recurring_todo[weekly_return_saturday]', 's', week_day == 6 ? true : false, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[6] %>
<%= check_box_tag('recurring_todo[weekly_return_sunday]', 's', week_day == 0 ? true : false, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[0] %>
</div>
<div id="recurring_monthly" style="display:none">
<label><%= t('todos.recurrence.monthly_options') %></label><br/>
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', true)%> <%= t('todos.recurrence.day_x_on_every_x_month',
:day => text_field_tag('recurring_todo[monthly_every_x_day]', Time.zone.now.mday, {"size" => 3, "tabindex" => 9}),
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.day_x_on_every_x_month',
:day => text_field_tag('recurring_todo[monthly_every_x_day]', Time.zone.now.mday, {"size" => 3, "tabindex" => next_tab_index}),
:month => text_field_tag('recurring_todo[monthly_every_x_month]', 1, {"size" => 3, "tabindex" => 10})) %><br/>
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day')%> <%= t('todos.recurrence.monthly_every_xth_day',
:day => select_tag('recurring_todo[monthly_every_xth_day]', options_for_select(@xth_day), {}),
@ -69,31 +70,31 @@
</div>
<div id="recurring_yearly" style="display:none">
<label><%= t('todos.recurrence.yearly_options') %></label><br/>
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', true)%> <%= t('todos.recurrence.yearly_every_x_day',
:month => select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, Time.zone.now.month), {}),
:day => text_field_tag('recurring_todo[yearly_every_x_day]', Time.zone.now.day, "size" => 3, "tabindex" => 9)) %><br/>
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day')%> <%= t('todos.recurrence.yearly_every_xth_day',
:day => select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day), {}),
:day_of_week => select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, Time.zone.now.wday), {}),
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, Time.zone.now.month), {})) %><br/>
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.yearly_every_x_day',
:month => select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, Time.zone.now.month), {:tabindex => next_tab_index}),
:day => text_field_tag('recurring_todo[yearly_every_x_day]', Time.zone.now.day, "size" => 3, "tabindex" => next_tab_index)) %><br/>
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.yearly_every_xth_day',
:day => select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day), {:tabindex => next_tab_index}),
:day_of_week => select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, Time.zone.now.wday), {:tabindex => next_tab_index}),
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, Time.zone.now.month), {:tabindex => next_tab_index})) %><br/>
</div>
<div id="recurring_target">
<label><%= t('todos.recurrence.recurrence_on_options') %></label><br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', true)%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', true)%> <%= t('todos.recurrence.show_option_always') %>
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', false)%>
<%= t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3, "tabindex" => 12})) %>
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>:
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.show_option_always') %>
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', false, {:tabindex => next_tab_index})%>
<%= t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3, "tabindex" => next_tab_index})) %>
<br/>
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', false)%> <%= t('todos.recurrence.from_tickler') %><br/>
<%= 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="15">
<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="15">
<button type="button" class="positive" id="recurring_todo_new_action_cancel" tabindex="<%=next_tab_index%>">
<%=image_tag("cancel.png", :alt => "") %>
<%= t('common.cancel') %>
</button>

View file

@ -1,6 +1,6 @@
<%- if @saved -%>
show_empty_messages();
TracksPages.page_notify('notice', '<%= escape_javascript(t('todos.recurring_deleted_success') + t(:todo_removed, :count => @number_of_todos)) %>', 5);
TracksPages.page_notify('notice', '<%= escape_javascript(t('todos.recurring_deleted_success') + t('todos.recurring_pattern_removed', :count => pluralize(@number_of_todos,t('common.todo')))) %>', 5);
remove_recurring_todo_from_page();
<%- else -%>
TracksPages.page_notify('error', '<%= t('todos.error_deleting_recurring', :description => @recurring_todo.description) %>', 8);

View file

@ -0,0 +1,24 @@
<%
paginate_options = {
:class => :add_note_link,
:previous_label => '&laquo; '+ t('common.previous'),
:next_label => t('common.next')+' &raquo;',
:inner_window => 2
}
%>
<div id="display_box">
<div class="container" id="completed_recurring_todos_container">
<%= will_paginate @completed_recurring_todos, paginate_options %>
<h2>
<span id="completed-projects-count" class="badge"><%= "#{@total} (#{@range_low}-#{@range_high})" %></span>
<%= t('todos.completed_recurring') %>
</h2>
<div id="completed_recurring_todos_container">
<div id="completed-empty-nd" style="<%= @no_completed_recurring_todos ? 'display:block' : 'display:none'%>">
<div class="message"><p><%= t('todos.no_completed_recurring') %></p></div>
</div>
<%= render :partial => @completed_recurring_todos %>
</div>
<%= will_paginate @completed_recurring_todos, paginate_options %>
</div>
</div>

View file

@ -10,7 +10,8 @@
</div>
<div class="container" id="completed_recurring_todos_container">
<h2><%= t('todos.completed_recurring') %></h2>
<div class=add_note_link><%= link_to "Show all", done_recurring_todos_path%></div>
<h2><%= t('common.last') %> <%= t('todos.completed_recurring') %></h2>
<div id="completed_recurring_todos_container">
<div id="completed-empty-nd" style="<%= @no_completed_recurring_todos ? 'display:block' : 'display:none'%>">
<div class="message"><p><%= t('todos.no_completed_recurring') %></p></div>

View file

@ -1,2 +0,0 @@
<h1>RecurringTodo#show</h1>
<p>Find me in app/views/recurring_todo/show.html.erb</p>

View file

@ -1,5 +1,5 @@
<%- if @saved -%>
$('div#recurring_todo_<%= @recurring_todo.id %> a.star_item img').toggleClass('starred_todo').toggleClass('unstarred_todo');
$('div#recurring_todo_<%= @recurring_todo.id %> a.star_item img').toggleClass('starred');
<%- else -%>
TracksPages.page_notify('error', '<%= t('todos.error_starring_recurring', :description => @recurring_todo.description) %>', 8);
<%- end -%>

View file

@ -3,11 +3,11 @@
<% else -%>
<% render :layout => 'show_results_collection', :object => @found_todos, :locals => { :collection_name => "found-todos", :collection_title => t('search.todos_matching_query')} do %>
<%= render :partial => "todos/todo", :collection => @found_todos, :locals => { :parent_container_type => 'search', :suppress_context => false, :suppress_project => false, :suppress_edit_button => true } %>
<%= render :partial => "todos/todo", :collection => @found_todos, :locals => { :parent_container_type => 'search', :suppress_context => false, :suppress_project => false, :suppress_edit_button => false } %>
<% end -%>
<% render :layout => 'show_results_collection', :object => @found_projects, :locals => { :collection_name => "found-project", :collection_title => t('search.projects_matching_query')} do %>
<%= render :partial => "projects/project_listing", :collection => @found_projects, :locals => { :suppress_drag_handle => true, :suppress_edit_button => true } %>
<%= render :partial => "projects/project_listing", :collection => @found_projects, :locals => { :suppress_drag_handle => true, :suppress_edit_button => true, :suppress_delete_button => true } %>
<% end -%>
<% render :layout => 'show_results_collection', :object => @found_notes, :locals => { :collection_name => "found-notes", :collection_title => t('search.notes_matching_query')} do %>

View file

@ -3,6 +3,9 @@
@initial_context_name ||= @project.default_context.name unless @project.nil? || @project.default_context.nil?
@initial_context_name ||= @contexts.first.name unless @contexts.first.nil?
@initial_project_name = @project.name unless @project.nil?
@initial_tags ||= @default_tags
@initial_tags ||= @project.default_tags unless @project.nil?
reset_tab_index
-%>
<div id="todo_new_action_container">

View file

@ -1,7 +1,7 @@
<div id="footer">
<p><%= t('footer.send_feedback', :version => TRACKS_VERSION) %>: <a href="http://www.assembla.com/spaces/tracks-tickets/tickets"><%= t('common.bugs')%></a> |
<p><%= t('footer.send_feedback', :version => TRACKS_VERSION) %>: <a href="http://www.assembla.com/spaces/tracks-tickets/tickets"><%= t('common.bugs')%></a> |
<a href="http://www.getontracks.org/forums/"><%= t('common.forum')%></a> |
<a href="http://www.getontracks.org/wiki/"><%= t('common.wiki')%></a> |
<a href="https://github.com/TracksApp/tracks/wiki"><%= t('common.wiki')%></a> |
<a href="mailto:butshesagirl@rousette.org.uk?subject=Tracks feedback"><%= t('common.email')%></a> |
<a href="http://www.getontracks.org/"><%= t('common.website')%></a> |
<a href="http://getontracks.org/tracks/contribute"><%= t('common.contribute')%></a></p>

View file

@ -0,0 +1,45 @@
<div id="display_box">
<div class="container">
<div class=add_note_link><%= link_to "Show all", done_todos_path%></div>
<h2>
<%= t('common.last') %> <%= t('states.completed_plural' )%> <%= t('common.actions') %>
</h2>
<% if @done_recently.empty? -%>
<div class="message"><p><%= t('todos.no_last_completed_actions') %></p></div>
<% else -%>
<%= render :partial => "todos/todo", :collection => @done_recently, :locals => { :parent_container_type => "completed", :suppress_context => false, :suppress_project => false } %>
<% end -%>
</div>
<div class="container">
<div class=add_note_link><%= link_to "Show all", done_projects_path%></div>
<h2>
<%= t('common.last') %> <%= t('states.completed_plural' )%> <%= t('common.projects') %>
</h2>
<% if @last_completed_projects.empty? -%>
<div class="message"><p><%= t('projects.no_last_completed_projects') %></p></div>
<% else -%>
<div id="list-completed-projects" class="project-list">
<%= render :partial => '/projects/project_listing',
:collection => @last_completed_projects,
:locals => {:suppress_drag_handle => true}
%>
</div>
<% end -%>
</div>
<div class="container">
<div class=add_note_link><%= link_to "Show all", done_recurring_todos_path%></div>
<h2>
<%= t('common.last') %> <%= t('states.completed_plural' )%> <%= t('common.recurring_todos') %>
</h2>
<% if @last_completed_recurring_todos.empty? -%>
<div class="message"><p><%= t('projects.no_last_completed_recurring_todos') %></p></div>
<% else -%>
<%= render :partial => '/recurring_todos/recurring_todo', :collection => @last_completed_recurring_todos %>
<% end -%>
</div>
</div><!-- End of display_box -->

View file

@ -3,6 +3,7 @@
suppress_project ||= false
-%>
<div class="container completed" id="completed_container">
<div class=add_note_link><%= link_to "Show all", determine_done_path%></div>
<h2>
<% if collapsible %>
<a href="#" class="container_toggle" id="toggle_completed"><%= image_tag("collapse.png") %></a>

View file

@ -1,34 +1,34 @@
<%
todo = edit_form
form_for(todo, :html=> { :name=>'todo', :id => dom_id(@todo, 'form'), :class => 'inline-form edit_todo_form' }) do |t|%>
<div id="error_status"><%= error_messages_for("todo", :object_name => 'action') %></div>
<div id="edit_error_status"><%= error_messages_for("todo", :object_name => 'action') %></div>
<%= t.hidden_field( "id" ) -%>
<%= source_view_tag( @source_view ) -%>
<%= content_tag(:input, "", :type=>"hidden", :name=>"_tag_name", :value=>"#{@tag_name}") if @tag_name -%>
<label for="<%= dom_id(@todo, 'description') %>"><%= t('common.description') %></label>
<%= t.text_field( "description", "size" => 30, "tabindex" => 8, "maxlength" => 100) %>
<%= t.text_field( "description", "size" => 30, "tabindex" => next_tab_index, "maxlength" => 100) %>
<label for="<%= dom_id(@todo, 'notes') %>"><%= t('common.notes') %></label>
<%= t.text_area( "notes", "cols" => 29, "rows" => 4, "tabindex" => 9) %>
<%= t.text_area( "notes", "cols" => 29, "rows" => 4, "tabindex" => next_tab_index) %>
<div class="project_input">
<label for="<%= dom_id(@todo, 'project_name') %>"><%= t('common.project') %></label>
<input id="<%= dom_id(@todo, 'project_name') %>" name="project_name" autocomplete="off" tabindex="10" size="30" type="text" value="<%= @todo.project.nil? ? 'None' : h(@todo.project.name) %>" />
<input id="<%= dom_id(@todo, 'project_name') %>" name="project_name" autocomplete="off" tabindex="<%= next_tab_index%>" size="30" type="text" value="<%= @todo.project.nil? ? 'None' : h(@todo.project.name) %>" />
</div>
<div class="context_input">
<label for="<%= dom_id(@todo, 'context_name') %>"><%= t('common.context') %></label>
<input id="<%= dom_id(@todo, 'context_name') %>" name="context_name" autocomplete="off" tabindex="11" size="30" type="text" value="<%= h @todo.context.name %>" />
<input id="<%= dom_id(@todo, 'context_name') %>" name="context_name" autocomplete="off" tabindex="<%= next_tab_index%>" size="30" type="text" value="<%= h @todo.context.name %>" />
</div>
<label class="tag_list_label" for="<%= dom_id(@todo, 'tag_list') %>"><%= t('todos.tags') %></label>
<%= text_field_tag 'tag_list', tag_list_text, :id => dom_id(@todo, 'tag_list'), :size => 30, :tabindex => 12 %>
<%= text_field_tag 'tag_list', tag_list_text, :id => dom_id(@todo, 'tag_list'), :size => 30, :tabindex => next_tab_index %>
<div class="due_input">
<label for="<%= dom_id(@todo, 'due_label') %>">Due</label>
<%= date_field_tag("todo[due]", dom_id(@todo, 'due'), format_date(@todo.due), "tabindex" => 13) %>
<label for="<%= dom_id(@todo, 'due_label') %>"><%= Todo.human_attribute_name('due') %></label>
<%= date_field_tag("todo[due]", dom_id(@todo, 'due'), format_date(@todo.due), "tabindex" => next_tab_index) %>
<a href="#" id="<%= dom_id(@todo, 'due_x') %>" class="date_clear" title="<%= t('todos.clear_due_date') %>">
<%= image_tag("delete_off.png", :alt => "Clear due date") %>
</a>
@ -36,7 +36,7 @@ form_for(todo, :html=> { :name=>'todo', :id => dom_id(@todo, 'form'), :class =>
<div class="show_from_input">
<label for="<%= dom_id(@todo, 'show_from') %>"><%= t('todos.show_from') %></label>
<%= date_field_tag("todo[show_from]", dom_id(@todo, 'show_from'), format_date(@todo.show_from), "tabindex" => 14) %>
<%= date_field_tag("todo[show_from]", dom_id(@todo, 'show_from'), format_date(@todo.show_from), "tabindex" => next_tab_index) %>
<a href="#" id="<%= dom_id(@todo, 'show_from_x') %>" class="date_clear" title="<%= t('todos.clear_show_from_date') %>">
<%= image_tag("delete_off.png", :alt => "Clear show from date") %>
</a>
@ -49,20 +49,20 @@ form_for(todo, :html=> { :name=>'todo', :id => dom_id(@todo, 'form'), :class =>
</ul>
</div>
<label id="label_for_predecessor_input" for="predecessor_input" style="display:none"><%= t('todos.add_another_dependency')%></label>
<%= text_field_tag "predecessor_input", nil, :size => 30, :tabindex => 8 %>
<%= text_field_tag "predecessor_input", nil, :size => 30, :tabindex => next_tab_index %>
<%= hidden_field_tag "predecessor_list", @todo.predecessors.map{|t| t.id.to_s}.join(', ') %>
</div>
<div class="submit_box">
<div class="widgets">
<button type="submit" class="positive" id="<%= dom_id(@todo, 'submit') %>" tabindex="16">
<button type="submit" class="positive" id="<%= dom_id(@todo, 'submit') %>" tabindex="<%= next_tab_index %>">
<%=image_tag("accept.png", :alt => "") %>
Update
<%= t('common.update') %>
</button>
<a href="#" class="negative">
<%=image_tag("cancel.png", :alt => "") %>
Cancel
<%= t('common.cancel') %>
</a>
</div>
</div>

View file

@ -1,10 +1,10 @@
<% @tag_list_text = ""
<% @tag_list_text = ""
@tag_list_text = tag_list_text if @todo -%>
<span class="errors">
<%= error_messages_for("todo") %>
</span>
<% this_year = current_user.time.to_date.strftime("%Y").to_i
if parent_container_type == 'show_mobile' -%>
<% 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>&nbsp;<%= 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>
@ -13,10 +13,10 @@ if parent_container_type == 'show_mobile' -%>
<%= text_area( "todo", "notes", "cols" => 40, "rows" => 3, "tabindex" => 3) %>
<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" => 4} )
else
select_tag("todo[context_id]", options_from_collection_for_select(
@contexts, "id", "name", @mobile_from_context.id),
@contexts, "id", "name", @mobile_from_context.id),
{"id" => :todo_context_id, :tabindex => 4} )
end %>
<h2><label for="todo_project_id"><%= t('common.project') %></label></h2>
@ -24,10 +24,10 @@ end %>
collection_select( "todo", "project_id", @projects, "id", "name",
{:include_blank => t('todos.no_project')}, {"tabindex" => 5} )
else
# manually add blank option since :include_blank does not work
# 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),
@projects, "id", "name", @mobile_from_project.id),
{"id" => :todo_project_id, :tabindex => 5} )
end %>
<h2><label for="tag_list"><%= t('todos.tags') %></label></h2>
@ -36,5 +36,5 @@ end %>
<%= date_select("todo", "due", {:order => [:day, :month, :year],
:start_year => this_year, :include_blank => '--'}, :tabindex => 7) %>
<h2><label for="todo_show_from"><%= t('todos.show_from') %></label></h2>
<%= date_select("todo", "show_from", {:order => [:day, :month, :year],
<%= date_select("todo", "show_from", {:order => [:day, :month, :year],
:start_year => this_year, :include_blank => true}, :tabindex => 8) %>

View file

@ -13,7 +13,7 @@ end -%>
-%><span class="m_t">
<% end -%>
<%= date_span -%> <%= link_to mobile_todo.description, todo_path(mobile_todo, :format => 'm') -%>
<% if mobile_todo.notes? %>
<% 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 parent_container_type == 'context' or parent_container_type == 'tag' -%>

View file

@ -8,23 +8,27 @@
<div id="multiple_error_status"><%= error_messages_for("item", :object_name => 'action') %></div>
<label for="todo_notes"><%= t('shared.multiple_next_actions') %></label>
<%= text_area_tag( "todo[multiple_todos]", "", :cols => 29, :rows => 6, :tabindex => 2) %>
<%= text_area_tag( "todo[multiple_todos]", "", :cols => 29, :rows => 6, :tabindex => next_tab_index) %>
<label for="todo_project_name"><%= t('shared.project_for_all_actions') %></label>
<input id="multi_todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="<%=h @initial_project_name %>" />
<input id="multi_todo_project_name" name="project_name" autocomplete="off" tabindex="<%= next_tab_index%>" size="30" type="text" value="<%=h @initial_project_name %>" />
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
<label for="todo_context_name"><%= t('shared.context_for_all_actions') %></label>
<input id="multi_todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="<%=h @initial_context_name %>" />
<input id="multi_todo_context_name" name="context_name" autocomplete="off" tabindex="<%= next_tab_index%>" size="30" type="text" value="<%=h @initial_context_name %>" />
<div class="page_name_auto_complete" id="context_list" style="display:none"></div>
<label for="tag_list"><%= t('shared.tags_for_all_actions') %></label>
<%= text_field_tag "multi_tag_list", @default_tags, :name=>:tag_list, :size => 30, :tabindex => 5 %>
<%= hidden_field_tag "initial_tag_list", @initial_tags%>
<%= text_field_tag "multi_tag_list", @initial_tags, :name=>:tag_list, :size => 30, :tabindex => next_tab_index %>
<%= content_tag("div", "", :id => "tag_list_auto_complete", :class => "auto_complete") %>
<%= check_box_tag('todos_sequential', 'true', false, {:tabindex => next_tab_index}) %>
<label for="todos_sequential"><%= t('shared.make_actions_dependent') %></label>
<div class="submit_box">
<div class="widgets">
<button type="submit" class="positive" id="todo_multi_new_action_submit" tabindex="8">
<button type="submit" class="positive" id="todo_multi_new_action_submit" tabindex="<%= next_tab_index%>">
<%= image_tag("accept.png", :alt => "") %><%= t('shared.add_actions') %>
</button>
</div>

View file

@ -4,34 +4,37 @@
<% form_for(todo, :html=> { :id=>'todo-form-new-action', :name=>'todo', :class => 'inline-form new_todo_form' }) do |t|%>
<input id="default_project_name_id" name="default_project_name" type="hidden" value="<%= h(@initial_project_name)-%>" />
<input id="default_context_name_id" name="default_context_name" type="hidden" value="<%= h(@initial_context_name)-%>" />
<input type="hidden" id="new_todo_starred" name="new_todo_starred" value="false" />
<div id="error_status"><%= error_messages_for("item", :object_name => 'action') %></div>
<label for="todo_description"><%= Todo.human_attribute_name('description') %></label>
<%= t.text_field("description", "size" => 30, "tabindex" => 1, "maxlength" => 100, "autocomplete" => "off", :autofocus => 1) %>
<label for="todo_description" style="float:left"><%= Todo.human_attribute_name('description') %></label>
<a href="#" id="new_todo_starred_link" class="undecorated_link" ><%= image_tag("blank.png", :title =>t('todos.star_action'), :class => "todo_star", :style=> "float: right")%></a>
<%= t.text_field("description", "size" => 30, "tabindex" => next_tab_index, "maxlength" => 100, "autocomplete" => "off", :autofocus => 1) %>
<label for="todo_notes"><%= Todo.human_attribute_name('notes') %></label>
<%= t.text_area("notes", "cols" => 29, "rows" => 6, "tabindex" => 2) %>
<%= t.text_area("notes", "cols" => 29, "rows" => 6, "tabindex" => next_tab_index) %>
<label for="todo_project_name"><%= Todo.human_attribute_name('project') %></label>
<input id="todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="<%= h(@initial_project_name) %>" />
<input id="todo_project_name" name="project_name" autocomplete="off" tabindex="<%= next_tab_index%>" size="30" type="text" value="<%= h(@initial_project_name) %>" />
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
<label for="todo_context_name"><%= Todo.human_attribute_name('context') %></label>
<input id="todo_context_name" name="context_name" autocomplete="off" tabindex="4" size="30" type="text" value="<%= h(@initial_context_name) %>" />
<input id="todo_context_name" name="context_name" autocomplete="off" tabindex="<%= next_tab_index%>" size="30" type="text" value="<%= h(@initial_context_name) %>" />
<label for="tag_list"><%= Todo.human_attribute_name('tags') + ' (' + t('shared.separate_tags_with_commas') + ')' %></label>
<%= text_field_tag "tag_list", @default_tags, :size => 30, :tabindex => 5 %>
<label for="todo_tag_list"><%= Todo.human_attribute_name('tags') + ' (' + t('shared.separate_tags_with_commas') + ')' %></label>
<%= hidden_field_tag "initial_tag_list", @initial_tags%>
<%= text_field_tag "todo_tag_list", @initial_tags, :size => 30, :tabindex => next_tab_index %>
<%= content_tag("div", "", :id => "tag_list_auto_complete", :class => "auto_complete") %>
<div class="due_input">
<label for="todo_due"><%= Todo.human_attribute_name('due') %></label>
<%= t.text_field("due", "size" => 12, "class" => "Date", "tabindex" => 6, "autocomplete" => "off") %>
<%= t.text_field("due", "size" => 12, "class" => "Date", "tabindex" => next_tab_index, "autocomplete" => "off") %>
</div>
<div class="show_from_input">
<label for="todo_show_from"><%= Todo.human_attribute_name('show_from') %></label>
<%= t.text_field("show_from", "size" => 12, "class" => "Date", "tabindex" => 7, "autocomplete" => "off") %>
<%= t.text_field("show_from", "size" => 12, "class" => "Date", "tabindex" => next_tab_index, "autocomplete" => "off") %>
</div>
<div class="depends_on">
@ -41,7 +44,7 @@
</ul>
</div>
<label id="label_for_predecessor_input" for="predecessor_input" style="display:none"><%= t('todos.add_another_dependency')%></label>
<%= text_field_tag "predecessor_input", nil, :size => 30, :tabindex => 8 %>
<%= text_field_tag "predecessor_input", nil, :size => 30, :tabindex => next_tab_index %>
<%= hidden_field_tag "predecessor_list", ""%>
</div>
@ -50,11 +53,11 @@
<div class="submit_box">
<div class="widgets">
<button type="submit" class="positive" id="todo_new_action_submit" tabindex="8">
<button type="submit" class="positive" id="todo_new_action_submit" tabindex="<%= next_tab_index%>">
<%= image_tag("accept.png", :alt => "") + t('shared.add_action') %>
</button>
</div>
</div>
<% end # form_for -%>
</div>
</div>

View file

@ -13,14 +13,6 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
<%= remote_delete_dependency(successor, predecessor) %>
<%# link_to_remote(
image_tag("blank.png", :title => t('todos.remove_dependency'), :align => "absmiddle", :class => "delete_item"),
{:url => {:controller => 'todos', :action => 'remove_predecessor', :id => successor.id},
:method => 'delete',
:with => "'#{parameters}&predecessor=#{predecessor.id}'",
:before => successor_start_waiting_js(successor)},
{:style => "background: transparent;"}) %>
<% unless successor.pending_successors.empty? %>
<div class="todo_successors" id="<%= dom_id(successor, 'successors') %>">
<%= render :partial => "todos/successor",
@ -34,5 +26,4 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
<% end %>
</div>
</div>
</div>
</div>

View file

@ -1,4 +1,7 @@
<%
require 'htmlentities'
htmlentities = HTMLEntities.new
todo = text_todo
if (todo.starred?)
@ -8,11 +11,11 @@ else
end
if (todo.completed?) && todo.completed_at
result_string << "["+ t('todos.completed') +": " + format_date(todo.completed_at) + "] "
result_string << "["+ htmlentities.decode(t('todos.completed')) +": " + format_date(todo.completed_at) + "] "
end
if todo.due
result_string << "[" + t('todos.due') + ": " + format_date(todo.due) + "] "
result_string << "[" + htmlentities.decode(t('todos.due')) + ": " + format_date(todo.due) + "] "
result_string << todo.description + " "
else
result_string << todo.description + " "

View file

@ -9,7 +9,7 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
<div id="<%= dom_id(todo) %>" class="item-container">
<div id="<%= dom_id(todo, 'line') %>" class="item-show">
<%= remote_star_icon(todo) %>
<%= remote_toggle_checkbox(todo) unless source_view_is :deferred %>
<%= remote_toggle_checkbox(todo) %>
<%= remote_edit_button(todo) unless suppress_edit_button %>
<ul class="sf-menu sf-item-menu">
<li style="z-index:<%=@z_index_counter%>"><%= image_tag "downarrow.png", :alt=> "" %>
@ -18,8 +18,8 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
<% unless todo.completed? || todo.deferred? -%>
<li><%= remote_defer_menu_item(1, todo) %></li>
<li><%= remote_defer_menu_item(7, todo) %></li>
<li><%= remote_promote_to_project_menu_item(todo) %></li>
<% end -%>
<li><%= remote_promote_to_project_menu_item(todo) %></li>
</ul>
</li>
</ul>
@ -31,12 +31,12 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
<%= tag_list(todo) %>
<%= deferred_due_date(todo) %>
<%= project_and_context_links( todo, parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %>
<%= collapsed_notes_image(todo) if todo.notes? %>
<%= collapsed_successors_image(todo) unless todo.pending_successors.empty? %>
<%= collapsed_notes_image(todo) unless todo.notes.blank? %>
<%= collapsed_successors_image(todo) if todo.has_pending_successors %>
</div>
</div>
<div id="<%= dom_id(todo, 'edit') %>" class="edit-form" style="display:none">
<% #note: edit form will load here remotely -%>
<div class="placeholder"> </div>
</div>
</div>
</div>

View file

@ -1,6 +1,6 @@
<% if !@saved
if @predecessor.completed? -%>
TracksPages.page_notify('error', "<%= t('todos.cannot_add_dependency_to_completed_todo') %>", 8);
TracksPages.page_notify('error', "<%= t('todos.cannot_add_dependency_to_completed_todo') %>", 8);
$('#<%=dom_id(@todo)%>').html(html_for_todo());
<% else -%>
TracksPages.page_notify('error', "<%= t('todos.unable_to_add_dependency') %>", 8);
@ -29,7 +29,7 @@ function show_in_tickler_box() {
function regenerate_predecessor_family() {
<%
parents = @predecessor.predecessors
parents = @predecessors
until parents.empty?
parent = parents.pop
parents += parent.predecessors -%>

View file

@ -0,0 +1,22 @@
<%
paginate_options = {
:class => :add_note_link,
:previous_label => '&laquo; '+ t('common.previous'),
:next_label => t('common.next')+' &raquo;',
:inner_window => 2
}
%>
<div id="display_box_projects">
<div class="container">
<%= will_paginate @done, paginate_options %>
<h2><%= t('todos.all_completed') %></h2>
<% if @done.empty? -%>
<div class="message"><p><%= t('todos.no_completed_actions') %></p></div>
<% else -%>
<%= render :partial => "todos/todo", :collection => @done, :locals => { :parent_container_type => "completed", :suppress_context => false, :suppress_project => false } %>
<% end -%>
</div>
<%= will_paginate @done, paginate_options %>
</div><!-- End of display_box -->

View file

@ -31,7 +31,7 @@
</div>
<div class="container">
<h2><%= t('todos.calendar.due_this_month', :month => Time.zone.now.strftime("%B")) %></h2>
<h2><%= t('todos.calendar.due_this_month', :month => l(Time.zone.now, :format => "%B")) %></h2>
<div id="empty_due_this_month" <%= "style=\"display:none\"" unless @due_this_month.empty? %>>
<%= t('todos.calendar.no_actions_due_this_month') %>
</div>
@ -41,7 +41,7 @@
</div>
<div class="container">
<h2><%= t('todos.calendar.due_next_month_and_later', :month => (Time.zone.now+1.month).strftime("%B")) %></h2>
<h2><%= t('todos.calendar.due_next_month_and_later', :month => l(Time.zone.now+1.month, :format => "%B")) %></h2>
<div id="empty_due_after_this_month" <%= "style=\"display:none\"" unless @due_after_this_month.empty? %>>
<%= t('todos.calendar.no_actions_due_after_this_month') %>
</div>

View file

@ -1,3 +1,3 @@
<% unless @due_tickles.empty? -%>
TracksPages.page_notify('notice', "<%=t('todos.tickler_items_due', :count => @due_tickles.length)%>", 5);
TracksPages.page_notify('notice', "<%=t('todos.tickler_items_due', :count => @due_tickles.length)%>", 8);
<% end -%>

View file

@ -1,25 +0,0 @@
<div id="display_box_projects">
<p><%= t('todos.completed_today', :count => @due_tickles.nil? ? 0 : @due_tickles.length) %></p>
<div class="container">
<h2><%= t('todos.completed_last_day') %></h2>
<table class="next_actions" border="0">
<%= render :partial => "done", :collection => @done_today %>
</table>
</div>
<div class="container">
<h2><%= t('todos.completed_last_x_days', :count => 7) %></h2>
<table class="next_actions" border="0">
<%= render :partial => "done", :collection => @done_this_week %>
</table>
</div>
<div class="container">
<h2><%= t('todos.completed_last_x_days', :count => 28) %></h2>
<table class="next_actions" border="0">
<%= render :partial => "done", :collection => @done_this_month %>
</table>
</div>
<p><%= t('todos.older_completed_items') %>: <%= link_to( t('todos.older_than_days', :count => 31), done_archive_path ) %></p>
</div><!-- End of display_box -->

View file

@ -1,11 +0,0 @@
<div id="display_box_projects">
<p><%= t('todos.completed_in_archive', :count => @done_archive.length) %></p>
<div class="container">
<h2><%= t('todos.completed_more_than_x_days_ago', :count => 31) %></h2>
<table class="next_actions" cellspacing="5" cellpadding="0" border="0">
<%= render :partial => "done", :collection => @done_archive %>
</table>
</div>
</div><!-- End of display_box -->

View file

@ -1,5 +1,5 @@
<% if @saved -%>
TracksPages.page_notify('notice', "<%=escape_javascript @status_message%>", 5);
TracksPages.page_notify('notice', "<%=escape_javascript @status_message%>", 8);
TracksPages.hide_errors();
TracksPages.set_page_badge(<%= @down_count %>);
<% if should_show_new_item -%>
@ -20,20 +20,27 @@ function clear_form() {
$('#todo-form-new-action').clearDeps();
TracksForm.set_context_name('<%=escape_javascript @initial_context_name%>');
TracksForm.set_project_name('<%=escape_javascript @initial_project_name%>');
TracksForm.set_tag_list('<%=escape_javascript @default_tags%>');
TracksForm.set_tag_list('<%=escape_javascript @initial_tags%>');
$('#todo-form-new-action input:text:first').focus();
$('#new_todo_starred_link .todo_star').removeClass('starred');
$('#new_todo_starred').val('false');
}
function insert_new_context_with_new_todo() {
$('#no_todos_in_view').slideUp(100);
<%-
empty_id = '#no_todos_in_view'
empty_id = '#tickler-empty-nd' if source_view_is :tickler
-%>
$('<%=empty_id%>').slideUp(100);
$('#display_box').prepend(html_for_new_context());
}
function add_todo_to_existing_context() {
<% if source_view_is_one_of(:todo, :deferred, :tag) -%>
<% unless source_view_is_one_of(:todo, :tag) && @todo.deferred? -%>
<% unless source_view_is_one_of(:todo, :tag) && (@todo.deferred?||@todo.hidden?) -%>
$('#c<%= @todo.context_id %>').fadeIn(500, function() {});
$('#no_todos_in_view').slideUp(100);
<%= "$('#tickler-empty-nd').slideUp(100);" if source_view_is(:deferred) && @todo.deferred? %>
<% end -%>
<% end -%>
$('#<%=empty_container_msg_div_id%>').hide();
@ -59,4 +66,4 @@ function html_for_new_context() {
function html_for_new_todo() {
return "<%= @saved ? escape_javascript(render(:partial => @todo, :locals => { :parent_container_type => parent_container_type, :source_view => @source_view })) : "" %>";
}
}

View file

@ -28,7 +28,7 @@ function clear_form() {
$('#todo-form-multi-new-action').clearForm();
TracksForm.set_context_name_for_multi_add('<%=escape_javascript @initial_context_name%>');
TracksForm.set_project_name_for_multi_add('<%=escape_javascript @initial_project_name%>');
TracksForm.set_tag_list_for_multi_add('<%=escape_javascript @default_tags%>');
TracksForm.set_tag_list_for_multi_add('<%=escape_javascript @initial_tags%>');
$('#todo-form-multi-new-action input:text:first').focus();
}

View file

@ -4,12 +4,13 @@
remove_todo_from_page();
show_new_todo_if_todo_was_recurring();
activate_pending_todos();
update_predecessors();
show_empty_messages();
<%- else -%>
TracksPages.page_notify('error', "<%= t('todos.error_deleting_item', :description => @todo.description) %>", 8);
<%- end -%>
<% if @saved
<% if @saved
# do not send the js in case of an error
-%>
@ -32,19 +33,24 @@ function show_empty_messages() {
function remove_todo_from_page() {
<% if (@remaining_in_context == 0) && update_needs_to_hide_context
# remove context with deleted todo
# remove context with deleted todo
-%>
$('#c<%=@todo.context_id%>').fadeOut(400, function() {
$('#<%=dom_id(@todo)%>').remove();
});
<%= show_empty_message_in_source_container -%>
$('#c<%=@todo.context_id%>').fadeOut(400, function() {
$('#<%=dom_id(@todo)%>').remove();
});
<%= show_empty_message_in_source_container -%>
<% else
# remove only the todo
# remove only the todo
-%>
<%= show_empty_message_in_source_container %>
$('#<%=dom_id(@todo)%>').slideUp(400, function() {
$('#<%=dom_id(@todo)%>').remove();
});
<%= show_empty_message_in_source_container %>
$('#<%=dom_id(@todo)%>').slideUp(400, function() {
$('#<%=dom_id(@todo)%>').remove();
<% if source_view_is :calendar
# in calendar view it is possible to have a todo twice on the page
-%>
$('#<%=dom_id(@todo)%>').remove();
<% end %>
});
<% end -%>
}
@ -64,7 +70,7 @@ function show_new_todo_if_todo_was_recurring() {
}
function activate_pending_todos() {
<% # Activate pending todos that are successors of the completed
<% # Activate pending todos that are successors of the deleted
if @saved && @pending_to_activate
# do not render the js in case of an error or if no todos to activate
@pending_to_activate.each do |t|
@ -84,10 +90,22 @@ function activate_pending_todos() {
<% end -%>
}
function update_predecessors() {
<%
if @todo_was_destroyed_from_pending_state
@uncompleted_predecessors.each do |p| -%>
if ($('#<%=item_container_id(p)%>')) {
$('#<%=dom_id(p)%>').html('<%=escape_javascript(render(:partial => p, :locals => { :parent_container_type => parent_container_type }))%>');
}
<% end
end
%>
}
function html_for_new_recurring_todo() {
return "<%= @saved && @new_recurring_todo ? escape_javascript(render(:partial => @new_recurring_todo, :locals => { :parent_container_type => parent_container_type })) : "" %>";
}
<% end
<% end
# if @saved
-%>

View file

@ -0,0 +1,31 @@
<div id="display_box_projects">
<div class="container">
<h2><%= t('todos.completed_today') %></h2>
<% if @done_today.empty? -%>
<div class="message"><p><%= t('todos.no_completed_actions') %></p></div>
<% else -%>
<%= render :partial => "todos/todo", :collection => @done_today, :locals => { :parent_container_type => "completed", :suppress_context => false, :suppress_project => false } %>
<% end -%>
</div>
<div class="container">
<h2><%= t('todos.completed_rest_of_week') %></h2>
<% if @done_this_week.empty? -%>
<div class="message"><p><%= t('todos.no_completed_actions') %></p></div>
<% else -%>
<%= render :partial => "todos/todo", :collection => @done_this_week, :locals => { :parent_container_type => "completed", :suppress_context => false, :suppress_project => false } %>
<% end -%>
</div>
<div class="container">
<h2><%= t('todos.completed_rest_of_month') %></h2>
<% if @done_this_month.empty? -%>
<div class="message"><p><%= t('todos.no_completed_actions') %></p></div>
<% else -%>
<%= render :partial => "todos/todo", :collection => @done_this_month, :locals => { :parent_container_type => "completed", :suppress_context => false, :suppress_project => false } %>
<% end -%>
</div>
<p>You can see all completed actions <%= link_to "here", determine_all_done_path %></p>
</div><!-- End of display_box -->

View file

@ -0,0 +1,5 @@
<% form_tag todo_path(@todo, :format => 'm'), :name => 'mobileEdit', :method => :put do %>
<%= render :partial => 'edit_mobile_form', :locals => { :parent_container_type => "show_mobile" } %>
<p><input type="submit" value="<%= t('common.update') %>" tabindex="6" accesskey="#" /></p>
<% end -%>
<%= link_to t('common.cancel'), @return_path %>

View file

@ -1,5 +1,5 @@
<% form_tag todos_path(:format => 'm'), :method => :post do %>
<%= render :partial => 'edit_mobile' %>
<%= render :partial => 'edit_mobile_form' %>
<p><input type="submit" value="<%= t('common.create') %>" tabindex="12" accesskey="#" /></p>
<% end -%>
<%= link_to t('common.back'), @return_path %>

View file

@ -1,7 +1,7 @@
<% # TODO: lots of overlap with add_predecessor --> helpers?
if @removed -%>
TracksPages.page_notify('notice', "<%= t('todos.removed_predecessor', :successor => @successor.description, :predecessor => @predecessor.description) %>", 8);
replace_updated_predecessor();
regenerate_predecessor_family();
update_successor();
@ -15,7 +15,7 @@ function replace_updated_predecessor() {
function regenerate_predecessor_family() {
<%
parents = @predecessor.predecessors
parents = @predecessors
until parents.empty?
parent = parents.pop
parents += parent.predecessors -%>

View file

@ -1,5 +1,29 @@
<% form_tag todo_path(@todo, :format => 'm'), :name => 'mobileEdit', :method => :put do %>
<%= render :partial => 'edit_mobile', :locals => { :parent_container_type => "show_mobile" } %>
<p><input type="submit" value="<%= t('common.update') %>" tabindex="6" accesskey="#" /></p>
<% end -%>
<%= link_to t('common.cancel'), @return_path %>
<h2>Description</h2>
<%= @todo.description %><br/>
<h2>Actions</h2>
<form method="get" action="<%= edit_todo_path(@todo, :format => :m)%>">
<button>Edit this action</button>
<input type="hidden" name="_method" value="put" />
</form><br/>
<form method="post" action="<%=toggle_star_todo_path(@todo, :format=>:m)%>">
<button>Toggle Star</button>
<input type="hidden" name="_method" value="put" />
</form><br/>
<form method="post" action="<%=toggle_check_todo_path(@todo, :format=>:m)%>">
<button>Mark complete</button>
<input type="hidden" name="_method" value="put" />
</form><br/>
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 1)%>">
<button>Defer 1 day</button>
<input type="hidden" name="_method" value="put" />
</form><br/>
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 7)%>">
<button>Defer 7 days</button>
<input type="hidden" name="_method" value="put" />
</form><br/>

View file

@ -5,17 +5,22 @@
redirect_after_complete();
<% else
animation = []
animation << "remove_todo"
if @todo.completed?
animation << "add_to_completed_container" unless source_view_is(:calendar)
animation << "add_new_recurring_todo"
animation << "activate_pending_todos"
animation << "remove_source_container"
unless source_view_is(:search)
animation << "remove_todo"
if @todo.completed?
animation << "add_to_completed_container" unless source_view_is_one_of(:calendar, :deferred)
animation << "add_new_recurring_todo"
animation << "activate_pending_todos"
animation << "remove_source_container"
else
animation << "add_todo_to_context" unless source_view_is(:done)
animation << "block_predecessors"
end
animation << "update_empty_container" if source_view_is_one_of(:tag, :todo)
animation << "regenerate_predecessor_family"
else
animation << "add_todo_to_context"
animation << "block_predecessors"
end
animation << "update_empty_container" if source_view_is_one_of(:tag, :todo) -%>
animation << "replace_todo"
end -%>
<%= render_animation(animation) %>
TracksPages.set_page_badge(<%= @down_count %>);
<% end -%>
@ -54,6 +59,11 @@ function add_to_completed_container(next_steps) {
<% end -%>
}
function replace_todo(next_steps) {
$('#<%= dom_id(@todo) %>').html(html_for_todo());
next_steps.go();
}
function add_todo_to_context(next_steps) {
$('#<%= item_container_id(@todo) %>').append(html_for_todo());
<% if should_make_context_visible -%>
@ -65,12 +75,15 @@ function add_todo_to_context(next_steps) {
$("#<%= empty_container_msg_div_id(@todo) %>").slideUp(100);
highlight_updated_todo(next_steps);
<% end -%>
<% if @completed_count == 0 -%>
$("#empty-d").slideDown(100);
<% end -%>
}
function add_new_recurring_todo(next_steps) {
<% # show new todo if the completed todo was recurring
if @todo.from_recurring_todo?
unless @new_recurring_todo.nil? || @new_recurring_todo.deferred? -%>
unless @new_recurring_todo.nil? || (@new_recurring_todo.deferred? && !source_view_is(:deferred)) -%>
$('#<%= item_container_id(@new_recurring_todo) %>').append(html_for_recurring_todo());
$('#c<%= @new_recurring_todo.context_id %>').fadeIn(500, function() {
highlight_updated_recurring_todo(next_steps);
@ -108,7 +121,7 @@ function highlight_updated_todo(next_steps) {
function activate_pending_todos(next_steps) {
<% # Activate pending todos that are successors of the completed
if @saved && @pending_to_activate
if @saved && @pending_to_activate
# do not render the js in case of an error or if no todos to activate
@pending_to_activate.each do |t|
html = escape_javascript(render(:partial => t, :locals => { :parent_container_type => parent_container_type }))
@ -121,7 +134,7 @@ function activate_pending_todos(next_steps) {
});
<% else -%>
$('#<%= item_container_id(t) %>').append("<%= html%>");
<% end -%>
<% end -%>
TodoItems.highlight_todo('#<%= dom_id(t)%>');
<% end -%>
<% end -%>
@ -129,7 +142,7 @@ function activate_pending_todos(next_steps) {
}
function block_predecessors(next_steps) {
<% # Activate pending todos that are successors of the completed
<% # Block active todos that are successors of the uncompleted
if @saved && @active_to_block
# do not render the js in case of an error or if no todos to block
@active_to_block.each do |t| %>
@ -145,6 +158,20 @@ function block_predecessors(next_steps) {
next_steps.go();
}
function regenerate_predecessor_family(next_steps) {
<%
if @predecessors
parents = @predecessors
until parents.empty?
parent = parents.pop
parents += parent.predecessors -%>
$('#<%= dom_id(parent) %>').html("<%= escape_javascript(render(:partial => parent, :locals => { :parent_container_type => parent_container_type })) %>");
<%end
end
-%>
next_steps.go();
}
function remove_source_container(next_steps) {
<% if (@remaining_in_context == 0 && source_view_is_one_of(:todo, :tag))
# remove context with deleted todo
@ -158,7 +185,7 @@ function remove_source_container(next_steps) {
}
function html_for_todo() {
return "<%= @saved ? escape_javascript(render(:partial => @todo, :locals => {
return "<%= @saved && !source_view_is(:done) ? escape_javascript(render(:partial => @todo, :locals => {
:parent_container_type => parent_container_type,
:suppress_project => source_view_is(:project),
:suppress_context => source_view_is(:context)
@ -167,4 +194,4 @@ function html_for_todo() {
function html_for_recurring_todo() {
return "<%= @saved ? escape_javascript(render(:partial => @new_recurring_todo, :locals => { :parent_container_type => parent_container_type })) : "" %>";
}
}

View file

@ -1,5 +1,5 @@
<%- if @saved -%>
$('div#line_todo_<%= @todo.id %> a.star_item img').toggleClass('starred_todo').toggleClass('unstarred_todo');
$('div#line_todo_<%= @todo.id %> a.star_item img').toggleClass('starred');
<%- else -%>
TracksPages.page_notify('error', '<%= t('todos.error_starring', :description => @todo.description) %>', 8);
<%- end -%>

View file

@ -27,6 +27,11 @@
function remove_todo(next_steps) {
$('#<%= dom_id(@todo) %>').fadeOut(400, function() {
$('#<%= dom_id(@todo) %>').remove();
<% if source_view_is :calendar
# in calendar view it is possible to have a todo twice on the page
-%>
$('#<%= dom_id(@todo) %>').remove();
<% end %>
<%= show_empty_message_in_source_container -%>
next_steps.go();
});
@ -41,7 +46,7 @@ function add_to_existing_container(next_steps) {
<% end -%>
<% else -%>
<% unless (@todo_hidden_state_changed && @todo.hidden?) || @todo_was_deferred_from_active_state -%>
$('#c<%= @todo.context_id %>').fadeIn(500, function() {
$('#c<%= @todo.context_id %>').fadeIn(500, function() {
next_steps.go();
<% if @target_context_count==1 -%>
$("#<%= empty_container_msg_div_id %>").slideUp(100);
@ -64,6 +69,7 @@ function replace_todo(next_steps) {
function hide_context(next_steps) {
<% context_id = @context_changed ? @original_item_context_id : @todo.context_id -%>
$('#c<%= context_id %>').fadeOut(400, function(){ next_steps.go(); });
<%= "$('#tickler-empty-nd').slideDown(400);" if source_view_is(:deferred) && @down_count == 0 %>
}
function highlight_updated_todo(next_steps) {
@ -104,7 +110,7 @@ function html_for_error_messages() {
return "<%= escape_javascript(error_messages_for('todo')) %>";
}
function update_predecessors() {
function update_predecessors(next_steps) {
<% @todo.uncompleted_predecessors.each do |p| -%>
if ($('#<%=item_container_id(p)%>')) {
$('#<%=dom_id(p)%>').html('<%=escape_javascript(render(:partial => p, :locals => { :parent_container_type => parent_container_type }))%>');
@ -117,4 +123,5 @@ function update_predecessors() {
}
<% end -%>
<% end -%>
next_steps.go();
}

View file

@ -0,0 +1,4 @@
<label for="user[password]"><%= t('users.new_password_label') %>:</label><br/>
<%= password_field "user", "password", :size => 40 %><br/>
<label for="user[password_confirmation]"><%= t('users.password_confirmation_label') %>:</label><br/>
<%= password_field "user", "password_confirmation", :size => 40 %><br/>

View file

@ -1,5 +1,5 @@
<div id="single_box" class="container context">
<h2><%= @page_title %></h2>
<%= error_messages_for 'user' %>
@ -7,20 +7,10 @@
<p><%= t('users.change_password_prompt') %></p>
<% form_tag :action => 'update_password' do %>
<table width="440px">
<tr>
<td><label for="updateuser_password"><%= t('users.new_password_label') %>:</label></td>
<td><%= password_field "updateuser", "password", :size => 40 %></td>
</tr>
<tr>
<td><label for="updateuser_password_confirmation"><%= t('users.password_confirmation_label') %>:</label></td>
<td><%= password_field "updateuser", "password_confirmation", :size => 40 %></td>
</tr>
<tr>
<td><%= link_to t('common.cancel'), preferences_path %></td>
<td><%= submit_tag t('users.change_password_submit') %></td>
</tr>
</table>
<%= render :partial => 'update_password' %>
<br/>
<%= link_to t('common.cancel'), preferences_path %>
<%= submit_tag t('users.change_password_submit') %>
<% end %>
</div>

View file

@ -1,7 +1,7 @@
<% if @saved -%>
remove_user_from_page();
TracksPages.set_page_badge(<%= @total_users %>);
TracksPages.page_notify('notice', '<%= t('users.destroy_successful', :login => @deleted_user.login) %>', 5);
TracksPages.page_notify('notice', '<%= t('users.destroy_successful', :login => @deleted_user.login) %>', 8);
<% else -%>
TracksPages.page_notify('error', '<%= t('users.destroy_error', :login => @deleted_user.login) %>', 8);
<% end -%>

View file

@ -110,5 +110,19 @@ module Rails
end
end
class Rails::Boot
def run
load_initializer
Rails::Initializer.class_eval do
def load_gems
@bundler_loaded ||= Bundler.require :default, Rails.env
end
end
Rails::Initializer.run(:set_load_path)
end
end
# All that for this:
Rails.boot!

View file

@ -21,15 +21,6 @@ Rails::Initializer.run do |config|
# config.frameworks -= [ :action_web_service, :action_mailer ]
config.autoload_paths += %W( #{RAILS_ROOT}/app/apis )
config.gem "highline"
config.gem "RedCloth"
config.gem "soap4r", :lib => false
config.gem 'datanoise-actionwebservice', :lib => 'actionwebservice', :source => "http://gems.github.com"
config.gem 'sanitize', :version => '~> 1.2.1'
config.gem 'rack', :version => '1.1.0'
config.gem 'will_paginate', :version => '~> 2.3.15'
config.gem 'has_many_polymorphs'
config.action_controller.use_accept_header = true
# Use the database for sessions instead of the file system
@ -69,12 +60,7 @@ Rails::Initializer.run do |config|
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
# See Rails::Configuration for more options
if ( SITE_CONFIG['authentication_schemes'].include? 'cas')
#requires rubycas-client gem to be installed
config.gem "rubycas-client", :lib => 'casclient'
end
end
# Add new inflection rules using the following format
@ -121,7 +107,7 @@ if ( SITE_CONFIG['authentication_schemes'].include? 'cas')
end
end
tracks_version='2.0devel'
tracks_version='2.1devel'
# comment out next two lines if you do not want (or can not) the date of the
# last git commit in the footer
info=`git log --pretty=format:"%ai" -1`

View file

@ -26,8 +26,3 @@ config.action_mailer.delivery_method = :test
config.action_controller.session_store = :cookie_store
config.action_controller.session = { :key => 'TracksCucumber', :secret => SITE_CONFIG['salt'] * (30.0 / SITE_CONFIG['salt'].length).ceil }
config.gem 'cucumber', :lib => false, :version => '<0.10.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber'))
config.gem 'cucumber-rails', :lib => false, :version => '>=0.3.2' unless File.directory?(File.join(Rails.root, 'vendor/plugins/cucumber-rails'))
config.gem 'gherkin', :lib => false, :version => '2.2.9' unless File.directory?(File.join(Rails.root, 'vendor/plugins/gherkin'))
config.gem 'database_cleaner', :lib => false, :version => '>=0.5.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner'))
config.gem 'webrat', :lib => false, :version => '>=0.7.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/webrat'))

View file

@ -24,6 +24,3 @@ config.action_mailer.delivery_method = :test
# Unique cookies
config.action_controller.session = { :key => 'TracksSelenium' }
config.gem 'database_cleaner', :lib => false, :version => '>=0.5.0' unless File.directory?(File.join(Rails.root, 'vendor/plugins/database_cleaner'))
config.gem 'selenium-client', :lib => false unless File.directory?(File.join(Rails.root, 'vendor/plugins/selenium-client'))
config.gem 'mongrel' # required by webrat for selenium

Some files were not shown because too many files have changed in this diff Show more