mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-08 08:34:21 +01:00
Merge branch 'master' of git@github.com:gorn/tracks
This commit is contained in:
commit
8aa573a73e
106 changed files with 2221 additions and 585 deletions
|
|
@ -9,12 +9,14 @@ require "redcloth"
|
|||
require 'date'
|
||||
require 'time'
|
||||
|
||||
# Commented the following line because of #744. It prevented rake db:migrate to
|
||||
# Commented the following line because of #744. It prevented rake db:migrate to
|
||||
# run because this tag went looking for the taggings table that did not exist
|
||||
# when you feshly create a new database
|
||||
# Old comment: We need this in development mode, or you get 'method missing' errors
|
||||
# when you feshly create a new database Old comment: We need this in development
|
||||
# mode, or you get 'method missing' errors
|
||||
#
|
||||
# Tag
|
||||
# Tag
|
||||
|
||||
class CannotAccessContext < RuntimeError; end
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
|
||||
|
|
@ -113,7 +115,7 @@ class ApplicationController < ActionController::Base
|
|||
def format_date(date)
|
||||
if date
|
||||
date_format = prefs.date_format
|
||||
formatted_date = date.strftime("#{date_format}")
|
||||
formatted_date = date.in_time_zone(prefs.time_zone).strftime("#{date_format}")
|
||||
else
|
||||
formatted_date = ''
|
||||
end
|
||||
|
|
@ -160,7 +162,39 @@ class ApplicationController < ActionController::Base
|
|||
response.content_type = 'text/html'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
else
|
||||
# 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, current_user)
|
||||
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
|
||||
|
||||
protected
|
||||
|
||||
def admin_login_required
|
||||
|
|
@ -192,7 +226,7 @@ class ApplicationController < ActionController::Base
|
|||
def openid_enabled?
|
||||
self.class.openid_enabled?
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def parse_date_per_user_prefs( s )
|
||||
|
|
@ -231,29 +265,5 @@ class ApplicationController < ActionController::Base
|
|||
def set_time_zone
|
||||
Time.zone = current_user.prefs.time_zone if logged_in?
|
||||
end
|
||||
|
||||
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.due = rt.get_due_date(date)
|
||||
todo.show_from = rt.get_show_from_date(date)
|
||||
todo.recurring_todo_id = rt.id
|
||||
saved = todo.save
|
||||
if saved
|
||||
todo.tag_with(rt.tag_list, current_user)
|
||||
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
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,36 +14,10 @@ class BackendController < ApplicationController
|
|||
|
||||
def new_rich_todo(username, token, default_context_id, description, notes)
|
||||
check_token(username,token)
|
||||
description,context = split_by_char('@',description)
|
||||
description,project = split_by_char('>',description)
|
||||
if(!context.nil? && project.nil?)
|
||||
context,project = split_by_char('>',context)
|
||||
end
|
||||
# logger.info("context='#{context}' project='#{project}")
|
||||
|
||||
context_id = default_context_id
|
||||
unless(context.nil?)
|
||||
found_context = @user.active_contexts.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
|
||||
check_context_belongs_to_user(context_id)
|
||||
|
||||
project_id = nil
|
||||
unless(project.blank?)
|
||||
if(project[0..3].downcase == "new:")
|
||||
found_project = @user.projects.build
|
||||
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.find_by_namepart(project) if found_project.nil?
|
||||
end
|
||||
project_id = found_project.id unless found_project.nil?
|
||||
end
|
||||
|
||||
todo = create_todo(description, context_id, project_id, notes)
|
||||
todo.id
|
||||
item = Todo.from_rich_message(@user, default_context_id, description, notes)
|
||||
item.save
|
||||
raise item.errors.full_messages.to_s if item.new_record?
|
||||
item.id
|
||||
end
|
||||
|
||||
def list_contexts(username, token)
|
||||
|
|
@ -84,25 +58,6 @@ class BackendController < ApplicationController
|
|||
raise item.errors.full_messages.to_s if item.new_record?
|
||||
item
|
||||
end
|
||||
|
||||
def split_by_char(separator,string)
|
||||
parts = string.split(separator)
|
||||
|
||||
# if the separator is used more than once, concat the last parts this is
|
||||
# needed to get 'description @ @home > project' working for contexts
|
||||
# starting with @
|
||||
if parts.length > 2
|
||||
2.upto(parts.length-1) { |i| parts[1] += (separator +parts[i]) }
|
||||
end
|
||||
|
||||
return safe_strip(parts[0]), safe_strip(parts[1])
|
||||
end
|
||||
|
||||
def safe_strip(s)
|
||||
s.strip! unless s.nil?
|
||||
s
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidToken < RuntimeError; end
|
||||
class CannotAccessContext < RuntimeError; end
|
||||
|
|
|
|||
|
|
@ -83,10 +83,14 @@ class ContextsController < ApplicationController
|
|||
end
|
||||
@context.attributes = params["context"]
|
||||
if @context.save
|
||||
if params['wants_render']
|
||||
if boolean_param('wants_render')
|
||||
respond_to do |format|
|
||||
format.js
|
||||
end
|
||||
elsif boolean_param('update_context_name')
|
||||
@contexts = current_user.projects
|
||||
render :template => 'contexts/update_context_name.js.rjs'
|
||||
return
|
||||
else
|
||||
render :text => success_text || 'Success'
|
||||
end
|
||||
|
|
@ -130,10 +134,10 @@ class ContextsController < ApplicationController
|
|||
def render_contexts_mobile
|
||||
lambda do
|
||||
@page_title = "TRACKS::List Contexts"
|
||||
@active_contexts = @contexts.find(:all, { :conditions => ["hide = ?", false]})
|
||||
@hidden_contexts = @contexts.find(:all, { :conditions => ["hide = ?", true]})
|
||||
@active_contexts = @contexts.active
|
||||
@hidden_contexts = @contexts.hidden
|
||||
@down_count = @active_contexts.size + @hidden_contexts.size
|
||||
cookies[:mobile_url]=request.request_uri
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
||||
render :action => 'index_mobile'
|
||||
end
|
||||
end
|
||||
|
|
@ -143,7 +147,7 @@ class ContextsController < ApplicationController
|
|||
@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
|
||||
cookies[:mobile_url]=request.request_uri
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
||||
@mobile_from_context = @context.id
|
||||
render :action => 'mobile_show_context'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ class DataController < ApplicationController
|
|||
require 'csv'
|
||||
|
||||
def index
|
||||
@page_title = "TRACKS::Export"
|
||||
@page_title = "TRACKS::Export"
|
||||
end
|
||||
|
||||
def import
|
||||
|
|
@ -24,6 +24,7 @@ class DataController < ApplicationController
|
|||
all_tables['tags'] = current_user.tags.find(:all)
|
||||
all_tables['taggings'] = current_user.taggings.find(:all)
|
||||
all_tables['notes'] = current_user.notes.find(:all)
|
||||
all_tables['recurring_todos'] = current_user.recurring_todos.find(:all)
|
||||
|
||||
result = all_tables.to_yaml
|
||||
result.gsub!(/\n/, "\r\n") # TODO: general functionality for line endings
|
||||
|
|
@ -34,21 +35,21 @@ class DataController < ApplicationController
|
|||
content_type = 'text/csv'
|
||||
CSV::Writer.generate(result = "") do |csv|
|
||||
csv << ["id", "Context", "Project", "Description", "Notes", "Tags",
|
||||
"Created at", "Due", "Completed at", "User ID", "Show from",
|
||||
"state"]
|
||||
"Created at", "Due", "Completed at", "User ID", "Show from",
|
||||
"state"]
|
||||
current_user.todos.find(:all, :include => [:context, :project]).each do |todo|
|
||||
# Format dates in ISO format for easy sorting in spreadsheet
|
||||
# Print context and project names for easy viewing
|
||||
# Format dates in ISO format for easy sorting in spreadsheet Print
|
||||
# context and project names for easy viewing
|
||||
csv << [todo.id, todo.context.name,
|
||||
todo.project_id = todo.project_id.nil? ? "" : todo.project.name,
|
||||
todo.description,
|
||||
todo.notes, todo.tags.collect{|t| t.name}.join(', '),
|
||||
todo.created_at.to_formatted_s(:db),
|
||||
todo.due = todo.due? ? todo.due.to_formatted_s(:db) : "",
|
||||
todo.completed_at = todo.completed_at? ? todo.completed_at.to_formatted_s(:db) : "",
|
||||
todo.user_id,
|
||||
todo.show_from = todo.show_from? ? todo.show_from.to_formatted_s(:db) : "",
|
||||
todo.state]
|
||||
todo.project_id = todo.project_id.nil? ? "" : todo.project.name,
|
||||
todo.description,
|
||||
todo.notes, todo.tags.collect{|t| t.name}.join(', '),
|
||||
todo.created_at.to_formatted_s(:db),
|
||||
todo.due = todo.due? ? todo.due.to_formatted_s(:db) : "",
|
||||
todo.completed_at = todo.completed_at? ? todo.completed_at.to_formatted_s(:db) : "",
|
||||
todo.user_id,
|
||||
todo.show_from = todo.show_from? ? todo.show_from.to_formatted_s(:db) : "",
|
||||
todo.state]
|
||||
end
|
||||
end
|
||||
send_data(result, :filename => "todos.csv", :type => content_type)
|
||||
|
|
@ -58,16 +59,17 @@ class DataController < ApplicationController
|
|||
content_type = 'text/csv'
|
||||
CSV::Writer.generate(result = "") do |csv|
|
||||
csv << ["id", "User ID", "Project", "Note",
|
||||
"Created at", "Updated at"]
|
||||
# had to remove project include because it's association order is leaking through
|
||||
# and causing an ambiguous column ref even with_exclusive_scope didn't seem to help -JamesKebinger
|
||||
"Created at", "Updated at"]
|
||||
# had to remove project include because it's association order is leaking
|
||||
# through and causing an ambiguous column ref even with_exclusive_scope
|
||||
# didn't seem to help -JamesKebinger
|
||||
current_user.notes.find(:all,:order=>"notes.created_at").each do |note|
|
||||
# Format dates in ISO format for easy sorting in spreadsheet
|
||||
# Print context and project names for easy viewing
|
||||
# Format dates in ISO format for easy sorting in spreadsheet Print
|
||||
# context and project names for easy viewing
|
||||
csv << [note.id, note.user_id,
|
||||
note.project_id = note.project_id.nil? ? "" : note.project.name,
|
||||
note.body, note.created_at.to_formatted_s(:db),
|
||||
note.updated_at.to_formatted_s(:db)]
|
||||
note.project_id = note.project_id.nil? ? "" : note.project.name,
|
||||
note.body, note.created_at.to_formatted_s(:db),
|
||||
note.updated_at.to_formatted_s(:db)]
|
||||
end
|
||||
end
|
||||
send_data(result, :filename => "notes.csv", :type => content_type)
|
||||
|
|
@ -81,6 +83,7 @@ class DataController < ApplicationController
|
|||
result << current_user.tags.find(:all).to_xml(:skip_instruct => true)
|
||||
result << current_user.taggings.find(:all).to_xml(:skip_instruct => true)
|
||||
result << current_user.notes.find(:all).to_xml(:skip_instruct => true)
|
||||
result << current_user.recurring_todos.find(:all).to_xml(:skip_instruct => true)
|
||||
send_data(result, :filename => "tracks_backup.xml", :type => 'text/xml')
|
||||
end
|
||||
|
||||
|
|
@ -102,7 +105,6 @@ class DataController < ApplicationController
|
|||
@inarray = YAML::load(params['import']['yaml'])
|
||||
# arrays to handle id translations
|
||||
|
||||
|
||||
# contexts
|
||||
translate_context = Hash.new
|
||||
translate_context[nil] = nil
|
||||
|
|
@ -151,18 +153,18 @@ class DataController < ApplicationController
|
|||
|
||||
# state + dates
|
||||
case item.ivars['attributes']['state']
|
||||
when 'active' : newitem.activate!
|
||||
when 'project_hidden' : newitem.hide!
|
||||
when 'completed'
|
||||
newitem.complete!
|
||||
newitem.completed_at = adjust_time(item.ivars['attributes']['completed_at'])
|
||||
when 'deferred' : newitem.defer!
|
||||
when 'active' then newitem.activate!
|
||||
when 'project_hidden' then newitem.hide!
|
||||
when 'completed'
|
||||
newitem.complete!
|
||||
newitem.completed_at = adjust_time(item.ivars['attributes']['completed_at'])
|
||||
when 'deferred' then newitem.defer!
|
||||
end
|
||||
newitem.created_at = adjust_time(item.ivars['attributes']['created_at'])
|
||||
newitem.save(false)
|
||||
}
|
||||
|
||||
#tags
|
||||
# tags
|
||||
translate_tag = Hash.new
|
||||
translate_tag[nil] = nil
|
||||
current_user.tags.each { |item| item.destroy }
|
||||
|
|
@ -180,8 +182,8 @@ class DataController < ApplicationController
|
|||
newitem.user_id = current_user.id
|
||||
newitem.tag_id = translate_tag[newitem.tag_id]
|
||||
case newitem.taggable_type
|
||||
when 'Todo' : newitem.taggable_id = translate_todo[newitem.taggable_id]
|
||||
else newitem.taggable_id = 0
|
||||
when 'Todo' then newitem.taggable_id = translate_todo[newitem.taggable_id]
|
||||
else newitem.taggable_id = 0
|
||||
end
|
||||
newitem.save
|
||||
}
|
||||
|
|
@ -196,7 +198,6 @@ class DataController < ApplicationController
|
|||
newitem.created_at = adjust_time(item.ivars['attributes']['created_at'])
|
||||
newitem.save
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -12,12 +12,12 @@ class FeedlistController < ApplicationController
|
|||
@contexts = current_user.contexts
|
||||
end
|
||||
|
||||
@active_projects = @projects.select{ |p| p.active? }
|
||||
@hidden_projects = @projects.select{ |p| p.hidden? }
|
||||
@completed_projects = @projects.select{ |p| p.completed? }
|
||||
@active_projects = current_user.projects.active
|
||||
@hidden_projects = current_user.projects.hidden
|
||||
@completed_projects = current_user.projects.completed
|
||||
|
||||
@active_contexts = @contexts.select{ |c| !c.hidden? }
|
||||
@hidden_contexts = @contexts.select{ |c| c.hidden? }
|
||||
@active_contexts = current_user.contexts.active
|
||||
@hidden_contexts = current_user.contexts.hidden
|
||||
|
||||
respond_to do |format|
|
||||
format.html { render :layout => 'standard' }
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ class LoginController < ApplicationController
|
|||
session['noexpiry'] = params['user_noexpiry']
|
||||
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
||||
notify :notice, "Login successful: session #{msg}"
|
||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year }
|
||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
||||
unless should_expire_sessions?
|
||||
@user.remember_me
|
||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at }
|
||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
||||
end
|
||||
redirect_back_or_home
|
||||
return
|
||||
|
|
@ -94,12 +94,12 @@ class LoginController < ApplicationController
|
|||
session['noexpiry'] = session['user_noexpiry']
|
||||
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
||||
notify :notice, "You have successfully verified #{openid_url} as your identity. Login successful: session #{msg}"
|
||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year }
|
||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
||||
unless should_expire_sessions?
|
||||
@user.remember_me
|
||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at }
|
||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
|
||||
end
|
||||
cookies[:openid_url] = { :value => openid_url, :expires => Time.now + 1.year }
|
||||
cookies[:openid_url] = { :value => openid_url, :expires => Time.now + 1.year, :secure => TRACKS_COOKIES_SECURE }
|
||||
redirect_back_or_home
|
||||
else
|
||||
notify :warning, "You have successfully verified #{openid_url} as your identity, but you do not have a Tracks account. Please ask your administrator to sign you up."
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class ProjectsController < ApplicationController
|
|||
end
|
||||
|
||||
def projects_and_actions
|
||||
@projects = @projects.select { |p| p.active? }
|
||||
@projects = @projects.active
|
||||
respond_to do |format|
|
||||
format.text {
|
||||
render :action => 'index_text_projects_and_actions', :layout => false, :content_type => Mime::TEXT
|
||||
|
|
@ -43,7 +43,7 @@ class ProjectsController < ApplicationController
|
|||
init_data_for_sidebar unless mobile?
|
||||
@projects = current_user.projects
|
||||
@page_title = "TRACKS::Project: #{@project.name}"
|
||||
@project.todos.send :with_scope, :find => { :include => [:context, :tags] } do
|
||||
@project.todos.send :with_scope, :find => { :include => [:context] } do
|
||||
@not_done = @project.not_done_todos(:include_project_hidden_todos => true)
|
||||
@deferred = @project.deferred_todos.sort_by { |todo| todo.show_from }
|
||||
@done = @project.done_todos
|
||||
|
|
@ -83,7 +83,7 @@ class ProjectsController < ApplicationController
|
|||
@go_to_project = params['go_to_project']
|
||||
@saved = @project.save
|
||||
@project_not_done_counts = { @project.id => 0 }
|
||||
@active_projects_count = current_user.projects.count(:conditions => "state = 'active'")
|
||||
@active_projects_count = current_user.projects.active.count
|
||||
@contexts = current_user.contexts
|
||||
respond_to do |format|
|
||||
format.js { @down_count = current_user.projects.size }
|
||||
|
|
@ -124,9 +124,9 @@ class ProjectsController < ApplicationController
|
|||
@project_not_done_counts[@project.id] = @project.reload().not_done_todo_count(:include_project_hidden_todos => true)
|
||||
end
|
||||
@contexts = current_user.contexts
|
||||
@active_projects_count = current_user.projects.count(:conditions => "state = 'active'")
|
||||
@hidden_projects_count = current_user.projects.count(:conditions => "state = 'hidden'")
|
||||
@completed_projects_count = current_user.projects.count(:conditions => "state = 'completed'")
|
||||
@active_projects_count = current_user.projects.active.count
|
||||
@hidden_projects_count = current_user.projects.hidden.count
|
||||
@completed_projects_count = current_user.projects.completed.count
|
||||
render :template => 'projects/update.js.rjs'
|
||||
return
|
||||
elsif boolean_param('update_status')
|
||||
|
|
@ -136,6 +136,10 @@ class ProjectsController < ApplicationController
|
|||
@initial_context_name = @project.default_context.name
|
||||
render :template => 'projects/update_default_context.js.rjs'
|
||||
return
|
||||
elsif boolean_param('update_project_name')
|
||||
@projects = current_user.projects
|
||||
render :template => 'projects/update_project_name.js.rjs'
|
||||
return
|
||||
else
|
||||
render :text => success_text || 'Success'
|
||||
return
|
||||
|
|
@ -157,9 +161,9 @@ class ProjectsController < ApplicationController
|
|||
|
||||
def destroy
|
||||
@project.destroy
|
||||
@active_projects_count = current_user.projects.count(:conditions => "state = 'active'")
|
||||
@hidden_projects_count = current_user.projects.count(:conditions => "state = 'hidden'")
|
||||
@completed_projects_count = current_user.projects.count(:conditions => "state = 'completed'")
|
||||
@active_projects_count = current_user.projects.active.count
|
||||
@hidden_projects_count = current_user.projects.hidden.count
|
||||
@completed_projects_count = current_user.projects.completed.count
|
||||
respond_to do |format|
|
||||
format.js { @down_count = current_user.projects.size }
|
||||
format.xml { render :text => "Deleted project #{@project.name}" }
|
||||
|
|
@ -182,15 +186,22 @@ class ProjectsController < ApplicationController
|
|||
init_not_done_counts(['project'])
|
||||
end
|
||||
|
||||
def actionize
|
||||
@state = params['state']
|
||||
@projects = current_user.projects.actionize(current_user.id, :state => @state) if @state
|
||||
@contexts = current_user.contexts
|
||||
init_not_done_counts(['project'])
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def render_projects_html
|
||||
lambda do
|
||||
@page_title = "TRACKS::List Projects"
|
||||
@count = current_user.projects.size
|
||||
@active_projects = @projects.select{ |p| p.active? }
|
||||
@hidden_projects = @projects.select{ |p| p.hidden? }
|
||||
@completed_projects = @projects.select{ |p| p.completed? }
|
||||
@active_projects = @projects.active
|
||||
@hidden_projects = @projects.hidden
|
||||
@completed_projects = @projects.completed
|
||||
@no_projects = @projects.empty?
|
||||
@projects.cache_note_counts
|
||||
@new_project = current_user.projects.build
|
||||
|
|
@ -200,11 +211,11 @@ class ProjectsController < ApplicationController
|
|||
|
||||
def render_projects_mobile
|
||||
lambda do
|
||||
@active_projects = @projects.select{ |p| p.active? }
|
||||
@hidden_projects = @projects.select{ |p| p.hidden? }
|
||||
@completed_projects = @projects.select{ |p| p.completed? }
|
||||
@active_projects = @projects.active
|
||||
@hidden_projects = @projects.hidden
|
||||
@completed_projects = @projects.completed
|
||||
@down_count = @active_projects.size + @hidden_projects.size + @completed_projects.size
|
||||
cookies[:mobile_url]=request.request_uri
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
||||
render :action => 'index_mobile'
|
||||
end
|
||||
end
|
||||
|
|
@ -217,7 +228,7 @@ class ProjectsController < ApplicationController
|
|||
@project_default_context = "The default context for this project is "+
|
||||
@project.default_context.name
|
||||
end
|
||||
cookies[:mobile_url]=request.request_uri
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
||||
@mobile_from_project = @project.id
|
||||
render :action => 'project_mobile'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ class RecurringTodosController < ApplicationController
|
|||
append_before_filter :get_recurring_todo_from_param, :only => [:destroy, :toggle_check, :toggle_star, :edit, :update]
|
||||
|
||||
def index
|
||||
@recurring_todos = current_user.recurring_todos.find(:all, :conditions => ["state = ?", "active"])
|
||||
@completed_recurring_todos = current_user.recurring_todos.find(:all, :conditions => ["state = ?", "completed"])
|
||||
find_and_inactivate
|
||||
|
||||
@recurring_todos = current_user.recurring_todos.active
|
||||
@completed_recurring_todos = current_user.recurring_todos.completed
|
||||
@no_recurring_todos = @recurring_todos.size == 0
|
||||
@no_completed_recurring_todos = @completed_recurring_todos.size == 0
|
||||
@count = @recurring_todos.size
|
||||
|
|
@ -36,8 +38,8 @@ class RecurringTodosController < ApplicationController
|
|||
# the form for a new recurring todo and the edit form are on the same page.
|
||||
# Same goes for start_from and end_date
|
||||
params['recurring_todo']['recurring_period']=params['recurring_edit_todo']['recurring_period']
|
||||
params['recurring_todo']['end_date']=params['recurring_todo_edit_end_date']
|
||||
params['recurring_todo']['start_from']=params['recurring_todo_edit_start_from']
|
||||
params['recurring_todo']['end_date']=parse_date_per_user_prefs(params['recurring_todo_edit_end_date'])
|
||||
params['recurring_todo']['start_from']=parse_date_per_user_prefs(params['recurring_todo_edit_start_from'])
|
||||
|
||||
# update project
|
||||
if params['recurring_todo']['project_id'].blank? && !params['project_name'].nil?
|
||||
|
|
@ -84,6 +86,9 @@ class RecurringTodosController < ApplicationController
|
|||
|
||||
def create
|
||||
p = RecurringTodoCreateParamsHelper.new(params)
|
||||
p.attributes['end_date']=parse_date_per_user_prefs(p.attributes['end_date'])
|
||||
p.attributes['start_from']=parse_date_per_user_prefs(p.attributes['start_from'])
|
||||
|
||||
@recurring_todo = current_user.recurring_todos.build(p.selector_attributes)
|
||||
@recurring_todo.update_attributes(p.attributes)
|
||||
|
||||
|
|
@ -113,7 +118,7 @@ class RecurringTodosController < ApplicationController
|
|||
else
|
||||
@message += " / did not create todo"
|
||||
end
|
||||
@count = current_user.recurring_todos.count(:all, :conditions => ["state = ?", "active"])
|
||||
@count = current_user.recurring_todos.active.count
|
||||
else
|
||||
@message = "Error saving recurring todo"
|
||||
end
|
||||
|
|
@ -126,7 +131,7 @@ class RecurringTodosController < ApplicationController
|
|||
def destroy
|
||||
|
||||
# remove all references to this recurring todo
|
||||
@todos = current_user.todos.find(:all, {:conditions => ["recurring_todo_id = ?", params[:id]]})
|
||||
@todos = @recurring_todo.todos
|
||||
@number_of_todos = @todos.size
|
||||
@todos.each do |t|
|
||||
t.recurring_todo_id = nil
|
||||
|
|
@ -135,7 +140,7 @@ class RecurringTodosController < ApplicationController
|
|||
|
||||
# delete the recurring todo
|
||||
@saved = @recurring_todo.destroy
|
||||
@remaining = current_user.recurring_todos.count(:all)
|
||||
@remaining = current_user.recurring_todos.count
|
||||
|
||||
respond_to do |format|
|
||||
|
||||
|
|
@ -158,14 +163,15 @@ class RecurringTodosController < ApplicationController
|
|||
def toggle_check
|
||||
@saved = @recurring_todo.toggle_completion!
|
||||
|
||||
@count = current_user.recurring_todos.count(:all, :conditions => ["state = ?", "active"])
|
||||
@count = current_user.recurring_todos.active.count
|
||||
@remaining = @count
|
||||
|
||||
if @recurring_todo.active?
|
||||
@remaining = current_user.recurring_todos.count(:all, :conditions => ["state = ?", 'completed'])
|
||||
@remaining = current_user.recurring_todos.completed.count
|
||||
|
||||
# from completed back to active -> check if there is an active todo
|
||||
@active_todos = current_user.todos.count(:all, {:conditions => ["state = ? AND recurring_todo_id = ?", 'active',params[:id]]})
|
||||
# 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
|
||||
@new_recurring_todo = create_todo_from_recurring_todo(@recurring_todo) if @active_todos == 0
|
||||
|
|
@ -252,5 +258,11 @@ class RecurringTodosController < ApplicationController
|
|||
def get_recurring_todo_from_param
|
||||
@recurring_todo = current_user.recurring_todos.find(params[:id])
|
||||
end
|
||||
|
||||
def find_and_inactivate
|
||||
# find active recurring todos without active todos and inactivate them
|
||||
recurring_todos = current_user.recurring_todos.active
|
||||
recurring_todos.each { |rt| rt.toggle_completion! if rt.todos.not_completed.count == 0}
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ class StatsController < ApplicationController
|
|||
@page_title = 'TRACKS::Statistics'
|
||||
|
||||
@unique_tags = @tags.count(:all, {:group=>"tag_id"})
|
||||
@hidden_contexts = @contexts.find(:all, {:conditions => ["hide = ? ", true]})
|
||||
@hidden_contexts = @contexts.hidden
|
||||
@first_action = @actions.find(:first, :order => "created_at ASC")
|
||||
|
||||
get_stats_actions
|
||||
|
|
|
|||
|
|
@ -2,18 +2,19 @@ class TodosController < ApplicationController
|
|||
|
||||
helper :todos
|
||||
|
||||
skip_before_filter :login_required, :only => [:index]
|
||||
prepend_before_filter :login_or_feed_token_required, :only => [:index]
|
||||
append_before_filter :init, :except => [ :destroy, :completed, :completed_archive, :check_deferred, :toggle_check, :toggle_star, :edit, :update, :create ]
|
||||
skip_before_filter :login_required, :only => [:index, :calendar]
|
||||
prepend_before_filter :login_or_feed_token_required, :only => [:index, :calendar]
|
||||
append_before_filter :init, :except => [ :destroy, :completed, :completed_archive, :check_deferred, :toggle_check, :toggle_star, :edit, :update, :create, :calendar ]
|
||||
append_before_filter :get_todo_from_params, :only => [ :edit, :toggle_check, :toggle_star, :show, :update, :destroy ]
|
||||
|
||||
session :off, :only => :index, :if => Proc.new { |req| is_feed_request(req) }
|
||||
|
||||
def index
|
||||
current_user.deferred_todos.find_and_activate_ready
|
||||
@projects = current_user.projects.find(:all, :include => [:default_context])
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
|
||||
@contexts_to_show = @contexts.reject {|x| x.hide? }
|
||||
@contexts_to_show = current_user.contexts.active
|
||||
|
||||
respond_to do |format|
|
||||
format.html &render_todos_html
|
||||
|
|
@ -27,7 +28,7 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
def new
|
||||
@projects = current_user.projects.select { |p| p.active? }
|
||||
@projects = current_user.projects.active
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
respond_to do |format|
|
||||
format.m {
|
||||
|
|
@ -89,6 +90,7 @@ class TodosController < ApplicationController
|
|||
@contexts = current_user.contexts.find(:all) if @new_context_created
|
||||
@projects = current_user.projects.find(:all) if @new_project_created
|
||||
@initial_context_name = params['default_context_name']
|
||||
@initial_project_name = params['default_project_name']
|
||||
render :action => 'create'
|
||||
end
|
||||
format.xml do
|
||||
|
|
@ -105,6 +107,7 @@ class TodosController < ApplicationController
|
|||
@projects = current_user.projects.find(:all)
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
@source_view = params['_source_view'] || 'todo'
|
||||
@tag_name = params['_tag_name']
|
||||
respond_to do |format|
|
||||
format.js
|
||||
end
|
||||
|
|
@ -113,7 +116,7 @@ class TodosController < ApplicationController
|
|||
def show
|
||||
respond_to do |format|
|
||||
format.m do
|
||||
@projects = current_user.projects.select { |p| p.active? }
|
||||
@projects = current_user.projects.active
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
@edit_mobile = true
|
||||
@return_path=cookies[:mobile_url]
|
||||
|
|
@ -124,12 +127,14 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
# Toggles the 'done' status of the action
|
||||
#
|
||||
#
|
||||
def toggle_check
|
||||
@source_view = params['_source_view'] || 'todo'
|
||||
@original_item_due = @todo.due
|
||||
@saved = @todo.toggle_completion!
|
||||
|
||||
|
||||
# check if this todo has a related recurring_todo. If so, create next todo
|
||||
check_for_next_todo if @saved
|
||||
@new_recurring_todo = check_for_next_todo(@todo) if @saved
|
||||
|
||||
respond_to do |format|
|
||||
format.js do
|
||||
|
|
@ -137,6 +142,10 @@ class TodosController < ApplicationController
|
|||
determine_remaining_in_context_count(@todo.context_id)
|
||||
determine_down_count
|
||||
determine_completed_count if @todo.completed?
|
||||
if source_view_is :calendar
|
||||
@original_item_due_id = get_due_id_for_calendar(@original_item_due)
|
||||
@old_due_empty = is_old_due_empty(@original_item_due_id)
|
||||
end
|
||||
end
|
||||
render
|
||||
end
|
||||
|
|
@ -170,6 +179,9 @@ class TodosController < ApplicationController
|
|||
@original_item_context_id = @todo.context_id
|
||||
@original_item_project_id = @todo.project_id
|
||||
@original_item_was_deferred = @todo.deferred?
|
||||
@original_item_due = @todo.due
|
||||
@original_item_due_id = get_due_id_for_calendar(@todo.due)
|
||||
|
||||
if params['todo']['project_id'].blank? && !params['project_name'].nil?
|
||||
if params['project_name'] == 'None'
|
||||
project = Project.null_object
|
||||
|
|
@ -211,7 +223,7 @@ class TodosController < ApplicationController
|
|||
@todo.complete!
|
||||
end
|
||||
# strange. if checkbox is not checked, there is no 'done' in params.
|
||||
# Therfore I've used the negation
|
||||
# Therefore I've used the negation
|
||||
if !(params['done'] == '1') && @todo.completed?
|
||||
@todo.activate!
|
||||
end
|
||||
|
|
@ -219,7 +231,26 @@ class TodosController < ApplicationController
|
|||
@saved = @todo.update_attributes params["todo"]
|
||||
@context_changed = @original_item_context_id != @todo.context_id
|
||||
@todo_was_activated_from_deferred_state = @original_item_was_deferred && @todo.active?
|
||||
determine_remaining_in_context_count(@original_item_context_id) if @context_changed
|
||||
|
||||
if source_view_is :calendar
|
||||
@due_date_changed = @original_item_due != @todo.due
|
||||
if @due_date_changed
|
||||
@old_due_empty = is_old_due_empty(@original_item_due_id)
|
||||
if @todo.due.nil?
|
||||
# do not act further on date change when date is changed to nil
|
||||
@due_date_changed = false
|
||||
else
|
||||
@new_due_id = get_due_id_for_calendar(@todo.due)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if @context_changed
|
||||
determine_remaining_in_context_count(@original_item_context_id)
|
||||
else
|
||||
determine_remaining_in_context_count(@todo.context_id)
|
||||
end
|
||||
|
||||
@project_changed = @original_item_project_id != @todo.project_id
|
||||
if (@project_changed && !@original_item_project_id.nil?) then @remaining_undone_in_project = current_user.projects.find(@original_item_project_id).not_done_todo_count; end
|
||||
determine_down_count
|
||||
|
|
@ -229,7 +260,7 @@ class TodosController < ApplicationController
|
|||
format.m do
|
||||
if @saved
|
||||
if cookies[:mobile_url]
|
||||
cookies[:mobile_url] = nil
|
||||
cookies[:mobile_url] = {:value => nil, :secure => TRACKS_COOKIES_SECURE}
|
||||
redirect_to cookies[:mobile_url]
|
||||
else
|
||||
redirect_to formatted_todos_path(:m)
|
||||
|
|
@ -243,13 +274,14 @@ class TodosController < ApplicationController
|
|||
|
||||
def destroy
|
||||
@todo = get_todo_from_params
|
||||
@original_item_due = @todo.due
|
||||
@context_id = @todo.context_id
|
||||
@project_id = @todo.project_id
|
||||
|
||||
@saved = @todo.destroy
|
||||
|
||||
# check if this todo has a related recurring_todo. If so, create next todo
|
||||
check_for_next_todo
|
||||
|
||||
@saved = @todo.destroy
|
||||
@new_recurring_todo = check_for_next_todo(@todo) if @saved
|
||||
|
||||
respond_to do |format|
|
||||
|
||||
|
|
@ -268,6 +300,9 @@ class TodosController < ApplicationController
|
|||
determine_down_count
|
||||
if source_view_is_one_of(:todo, :deferred)
|
||||
determine_remaining_in_context_count(@context_id)
|
||||
elsif source_view_is :calendar
|
||||
@original_item_due_id = get_due_id_for_calendar(@original_item_due)
|
||||
@old_due_empty = is_old_due_empty(@original_item_due_id)
|
||||
end
|
||||
end
|
||||
render
|
||||
|
|
@ -337,46 +372,109 @@ class TodosController < ApplicationController
|
|||
def tag
|
||||
@source_view = params['_source_view'] || 'tag'
|
||||
@tag_name = params[:name]
|
||||
@page_title = "TRACKS::Tagged with \'#{@tag_name}\'"
|
||||
|
||||
# mobile tags are routed with :name ending on .m. So we need to chomp it
|
||||
@tag_name = @tag_name.chomp('.m') if mobile?
|
||||
|
||||
@tag = Tag.find_by_name(@tag_name)
|
||||
@tag = Tag.new(:name => @tag_name) if @tag.nil?
|
||||
|
||||
tag_collection = @tag.todos
|
||||
@not_done_todos = tag_collection.find(:all, :conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'active'])
|
||||
|
||||
@not_done_todos = tag_collection.find(:all,
|
||||
:conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'active'],
|
||||
:order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC')
|
||||
@hidden_todos = current_user.todos.find(:all,
|
||||
:include => [:taggings, :tags, :context],
|
||||
:conditions => ['tags.name = ? AND (todos.state = ? OR (contexts.hide = ? AND todos.state = ?))', @tag_name, 'project_hidden', true, 'active'])
|
||||
:conditions => ['tags.name = ? AND (todos.state = ? OR (contexts.hide = ? AND todos.state = ?))', @tag_name, 'project_hidden', true, 'active'],
|
||||
:order => 'todos.completed_at DESC, todos.created_at DESC')
|
||||
@deferred = tag_collection.find(:all,
|
||||
:conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'deferred'],
|
||||
:order => 'show_from ASC, todos.created_at DESC')
|
||||
|
||||
# If you've set no_completed to zero, the completed items box isn't shown on
|
||||
# the tag page
|
||||
max_completed = current_user.prefs.show_number_completed
|
||||
@done = tag_collection.find(:all,
|
||||
:limit => max_completed,
|
||||
:conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'completed'],
|
||||
:order => 'todos.completed_at DESC')
|
||||
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
@contexts_to_show = @contexts.reject {|x| x.hide? }
|
||||
|
||||
@deferred = tag_collection.find(:all, :conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'deferred'])
|
||||
|
||||
@page_title = "TRACKS::Tagged with \'#{@tag_name}\'"
|
||||
# If you've set no_completed to zero, the completed items box isn't shown on
|
||||
# the home page
|
||||
max_completed = current_user.prefs.show_number_completed
|
||||
@done = tag_collection.find(:all, :limit => max_completed, :conditions => ['taggings.user_id = ? and state = ?', current_user.id, 'completed'])
|
||||
# Set count badge to number of items with this tag
|
||||
@not_done_todos.empty? ? @count = 0 : @count = @not_done_todos.size
|
||||
@down_count = @count
|
||||
# @default_project_context_name_map =
|
||||
# build_default_project_context_name_map(@projects).to_json
|
||||
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
|
||||
}
|
||||
format.m {
|
||||
cookies[:mobile_url]=request.request_uri
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
||||
render :action => "mobile_tag"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def defer
|
||||
@source_view = params['_source_view'] || 'todo'
|
||||
numdays = params['days'].to_i
|
||||
@todo = Todo.find(params[:id])
|
||||
@todo.show_from = (@todo.show_from || @todo.user.date) + numdays.days
|
||||
@saved = @todo.save
|
||||
|
||||
determine_down_count
|
||||
determine_remaining_in_context_count(@todo.context_id)
|
||||
respond_to do |format|
|
||||
format.html { redirect_to :back }
|
||||
format.js {render :action => 'update'}
|
||||
end
|
||||
end
|
||||
|
||||
def calendar
|
||||
@source_view = params['_source_view'] || 'calendar'
|
||||
@page_title = "TRACKS::Calendar"
|
||||
|
||||
due_today_date = Time.zone.now
|
||||
due_this_week_date = Time.zone.now.end_of_week
|
||||
due_next_week_date = due_this_week_date + 7.days
|
||||
due_this_month_date = Time.zone.now.end_of_month
|
||||
|
||||
@due_today = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due <= ?', due_today_date],
|
||||
:order => "due")
|
||||
@due_this_week = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_today_date, due_this_week_date],
|
||||
:order => "due")
|
||||
@due_next_week = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_this_week_date, due_next_week_date],
|
||||
:order => "due")
|
||||
@due_this_month = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_next_week_date, due_this_month_date],
|
||||
:order => "due")
|
||||
@due_after_this_month = current_user.todos.not_completed.find(:all,
|
||||
:include => [:taggings, :tags],
|
||||
:conditions => ['todos.due > ?', due_this_month_date],
|
||||
:order => "due")
|
||||
|
||||
@count = current_user.todos.not_completed.are_due.count
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.ics {
|
||||
@due_all = current_user.todos.not_completed.are_due.find(:all, :order => "due")
|
||||
render :action => 'calendar', :layout => false, :content_type => Mime::ICS
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_todo_from_params
|
||||
@todo = current_user.todos.find(params['id'])
|
||||
|
|
@ -438,13 +536,17 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
def with_parent_resource_scope(&block)
|
||||
@feed_title = "Actions "
|
||||
if (params[:context_id])
|
||||
@context = current_user.contexts.find_by_params(params)
|
||||
@feed_title = @feed_title + "in context '#{@context.name}'"
|
||||
Todo.send :with_scope, :find => {:conditions => ['todos.context_id = ?', @context.id]} do
|
||||
yield
|
||||
end
|
||||
elsif (params[:project_id])
|
||||
@project = current_user.projects.find_by_params(params)
|
||||
@feed_title = @feed_title + "in project '#{@project.name}'"
|
||||
@project_feed = true
|
||||
Todo.send :with_scope, :find => {:conditions => ['todos.project_id = ?', @project.id]} do
|
||||
yield
|
||||
end
|
||||
|
|
@ -608,7 +710,7 @@ class TodosController < ApplicationController
|
|||
lambda do
|
||||
@page_title = "All actions"
|
||||
@home = true
|
||||
cookies[:mobile_url]=request.request_uri
|
||||
cookies[:mobile_url]= { :value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
|
||||
determine_down_count
|
||||
|
||||
render :action => 'index'
|
||||
|
|
@ -620,7 +722,7 @@ class TodosController < ApplicationController
|
|||
render_rss_feed_for @todos, :feed => todo_feed_options,
|
||||
:item => {
|
||||
:title => :description,
|
||||
:link => lambda { |t| context_url(t.context) },
|
||||
:link => lambda { |t| @project_feed.nil? ? context_url(t.context) : project_url(t.project) },
|
||||
:guid => lambda { |t| todo_url(t) },
|
||||
:description => todo_feed_content
|
||||
}
|
||||
|
|
@ -628,7 +730,9 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
def todo_feed_options
|
||||
Todo.feed_options(current_user)
|
||||
options = Todo.feed_options(current_user)
|
||||
options[:title] = @feed_title
|
||||
return options
|
||||
end
|
||||
|
||||
def todo_feed_content
|
||||
|
|
@ -674,18 +778,85 @@ class TodosController < ApplicationController
|
|||
['rss','atom','txt','ics'].include?(req.parameters[:format])
|
||||
end
|
||||
|
||||
def check_for_next_todo
|
||||
def check_for_next_todo(todo)
|
||||
# check if this todo has a related recurring_todo. If so, create next todo
|
||||
@new_recurring_todo = nil
|
||||
@recurring_todo = nil
|
||||
if @todo.from_recurring_todo?
|
||||
@recurring_todo = current_user.recurring_todos.find(@todo.recurring_todo_id)
|
||||
date_to_check = @todo.due.nil? ? @todo.show_from : @todo.due
|
||||
if @recurring_todo.active? && @recurring_todo.has_next_todo(date_to_check)
|
||||
date = date_to_check >= Date.today() ? date_to_check : Date.today()-1.day
|
||||
@new_recurring_todo = create_todo_from_recurring_todo(@recurring_todo, date)
|
||||
new_recurring_todo = nil
|
||||
recurring_todo = nil
|
||||
if todo.from_recurring_todo?
|
||||
recurring_todo = todo.recurring_todo
|
||||
|
||||
# check if there are active todos belonging to this recurring todo. only
|
||||
# add new one if all active todos are completed
|
||||
if recurring_todo.todos.active.count == 0
|
||||
|
||||
# check for next todo either from the due date or the show_from date
|
||||
date_to_check = todo.due.nil? ? todo.show_from : todo.due
|
||||
|
||||
# if both due and show_from are nil, check for a next todo from now
|
||||
date_to_check = Time.zone.now if date_to_check.nil?
|
||||
|
||||
if recurring_todo.active? && recurring_todo.has_next_todo(date_to_check)
|
||||
|
||||
# shift the reference date to yesterday if date_to_check is furher in
|
||||
# the past. This is to make sure we do not get older todos for overdue
|
||||
# todos. I.e. checking a daily todo that is overdue with 5 days will
|
||||
# create a new todo which is overdue by 4 days if we don't shift the
|
||||
# date. Discard the time part in the compare. We pick yesterday so
|
||||
# that new todos due for today will be created instead of new todos
|
||||
# for tomorrow.
|
||||
date = date_to_check.at_midnight >= Time.zone.now.at_midnight ? date_to_check : Time.zone.now-1.day
|
||||
|
||||
new_recurring_todo = create_todo_from_recurring_todo(recurring_todo, date)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return new_recurring_todo
|
||||
end
|
||||
|
||||
def get_due_id_for_calendar(due)
|
||||
return "" if due.nil?
|
||||
due_today_date = Time.zone.now
|
||||
due_this_week_date = Time.zone.now.end_of_week
|
||||
due_next_week_date = due_this_week_date + 7.days
|
||||
due_this_month_date = Time.zone.now.end_of_month
|
||||
if due <= due_today_date
|
||||
new_due_id = "due_today"
|
||||
elsif due <= due_this_week_date
|
||||
new_due_id = "due_this_week"
|
||||
elsif due <= due_next_week_date
|
||||
new_due_id = "due_next_week"
|
||||
elsif due <= due_this_month_date
|
||||
new_due_id = "due_this_month"
|
||||
else
|
||||
new_due_id = "due_after_this_month"
|
||||
end
|
||||
return new_due_id
|
||||
end
|
||||
|
||||
def is_old_due_empty(id)
|
||||
due_today_date = Time.zone.now
|
||||
due_this_week_date = Time.zone.now.end_of_week
|
||||
due_next_week_date = due_this_week_date + 7.days
|
||||
due_this_month_date = Time.zone.now.end_of_month
|
||||
case id
|
||||
when "due_today"
|
||||
return 0 == current_user.todos.not_completed.count(:all,
|
||||
:conditions => ['todos.due <= ?', due_today_date])
|
||||
when "due_this_week"
|
||||
return 0 == current_user.todos.not_completed.count(:all,
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_today_date, due_this_week_date])
|
||||
when "due_next_week"
|
||||
return 0 == current_user.todos.not_completed.count(:all,
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_this_week_date, due_next_week_date])
|
||||
when "due_this_month"
|
||||
return 0 == current_user.todos.not_completed.count(:all,
|
||||
:conditions => ['todos.due > ? AND todos.due <= ?', due_next_week_date, due_this_month_date])
|
||||
when "due_after_this_month"
|
||||
return 0 == current_user.todos.not_completed.count(:all,
|
||||
:conditions => ['todos.due > ?', due_this_month_date])
|
||||
else
|
||||
raise Exception.new, "unknown due id for calendar: '#{id}'"
|
||||
end
|
||||
end
|
||||
|
||||
class FindConditionBuilder
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ module RecurringTodosHelper
|
|||
def recurrence_time_span(rt)
|
||||
case rt.ends_on
|
||||
when "no_end_date"
|
||||
return ""
|
||||
return rt.start_from.nil? ? "" : "from " + format_date(rt.start_from)
|
||||
when "ends_on_number_of_times"
|
||||
return "for "+rt.number_of_occurences.to_s + " times"
|
||||
when "ends_on_end_date"
|
||||
|
|
@ -11,7 +11,7 @@ module RecurringTodosHelper
|
|||
ends = rt.end_date.nil? ? "" : " until " + format_date(rt.end_date)
|
||||
return starts+ends
|
||||
else
|
||||
raise Exception.new, "unknown recurrence time span selection (#{self.ends_on})"
|
||||
raise Exception.new, "unknown recurrence time span selection (#{self.ends_on})"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -28,10 +28,10 @@ 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(' ','-')}\">" +
|
||||
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 +
|
||||
t.name +
|
||||
"</span>"}.join('')
|
||||
"<span class='tags'>#{tag_list}</span>"
|
||||
end
|
||||
|
|
@ -44,7 +44,7 @@ module RecurringTodosHelper
|
|||
str
|
||||
end
|
||||
|
||||
def recurring_todo_remote_star_icon
|
||||
def recurring_todo_remote_star_icon
|
||||
str = link_to( image_tag_for_star(@recurring_todo),
|
||||
toggle_star_recurring_todo_path(@recurring_todo),
|
||||
:class => "icon star_item", :title => "star the action '#{@recurring_todo.description}'")
|
||||
|
|
|
|||
|
|
@ -280,4 +280,8 @@ module TodosHelper
|
|||
image_tag("blank.png", :title =>"Star action", :class => class_str)
|
||||
end
|
||||
|
||||
def defer_link(days)
|
||||
link_to_remote image_tag("defer_#{days}.png", :alt => "Defer #{pluralize(days, 'day')}"), :url => {:controller => 'todos', :action => 'defer', :id => @todo.id, :days => days, :_source_view => (@source_view.underscore.gsub(/\s+/,'_') rescue "")}
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ class Context < ActiveRecord::Base
|
|||
|
||||
has_many :todos, :dependent => :delete_all, :include => :project, :order => "todos.completed_at DESC"
|
||||
belongs_to :user
|
||||
|
||||
|
||||
named_scope :active, :conditions => { :hide => false }
|
||||
named_scope :hidden, :conditions => { :hide => true }
|
||||
|
||||
acts_as_list :scope => :user
|
||||
extend NamePartFinder
|
||||
include Tracks::TodoList
|
||||
|
|
|
|||
34
app/models/message_gateway.rb
Normal file
34
app/models/message_gateway.rb
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
class MessageGateway < ActionMailer::Base
|
||||
include ActionView::Helpers::SanitizeHelper
|
||||
def receive(email)
|
||||
user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip])
|
||||
if user.nil?
|
||||
user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip[1,100]])
|
||||
end
|
||||
return if user.nil?
|
||||
context = user.prefs.sms_context
|
||||
|
||||
description = nil
|
||||
notes = nil
|
||||
|
||||
if email.content_type == "multipart/related"
|
||||
description = sanitize email.subject
|
||||
body_part = email.parts.find{|m| m.content_type == "text/plain"}
|
||||
notes = sanitize body_part.body.strip
|
||||
else
|
||||
if email.subject.empty?
|
||||
description = sanitize email.body.strip
|
||||
notes = nil
|
||||
else
|
||||
description = sanitize email.subject.strip
|
||||
notes = sanitize email.body.strip
|
||||
end
|
||||
end
|
||||
|
||||
# stupid T-Mobile often sends the same message multiple times
|
||||
return if user.todos.find(:first, :conditions => {:description => description})
|
||||
|
||||
todo = Todo.from_rich_message(user, context.id, description, notes)
|
||||
todo.save!
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
class Preference < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
belongs_to :sms_context, :class_name => 'Context'
|
||||
|
||||
def self.due_styles
|
||||
{ :due_in_n_days => 0, :due_on => 1}
|
||||
|
|
@ -21,7 +22,7 @@ class Preference < ActiveRecord::Base
|
|||
|
||||
def parse_date(s)
|
||||
return nil if s.blank?
|
||||
Date.strptime(s, date_format)
|
||||
user.at_midnight(Date.strptime(s, date_format))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -3,6 +3,10 @@ class Project < ActiveRecord::Base
|
|||
has_many :notes, :dependent => :delete_all, :order => "created_at DESC"
|
||||
belongs_to :default_context, :class_name => "Context", :foreign_key => "default_context_id"
|
||||
belongs_to :user
|
||||
|
||||
named_scope :active, :conditions => { :state => 'active' }
|
||||
named_scope :hidden, :conditions => { :state => 'hidden' }
|
||||
named_scope :completed, :conditions => { :state => 'completed'}
|
||||
|
||||
validates_presence_of :name, :message => "project must have a name"
|
||||
validates_length_of :name, :maximum => 255, :message => "project name must be less than 256 characters"
|
||||
|
|
@ -16,7 +20,7 @@ class Project < ActiveRecord::Base
|
|||
|
||||
state :active
|
||||
state :hidden, :enter => :hide_todos, :exit => :unhide_todos
|
||||
state :completed, :enter => Proc.new { |p| p.completed_at = Time.now.utc }, :exit => Proc.new { |p| p.completed_at = nil }
|
||||
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]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ class RecurringTodo < ActiveRecord::Base
|
|||
belongs_to :project
|
||||
belongs_to :user
|
||||
|
||||
has_many :todos
|
||||
|
||||
attr_protected :user
|
||||
|
||||
acts_as_state_machine :initial => :active, :column => 'state'
|
||||
|
|
@ -12,7 +14,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
t[:show_from], t.completed_at = nil, nil
|
||||
t.occurences_count = 0
|
||||
}
|
||||
state :completed, :enter => Proc.new { |t| t.completed_at = Time.now.utc }, :exit => Proc.new { |t| t.completed_at = nil }
|
||||
state :completed, :enter => Proc.new { |t| t.completed_at = Time.zone.now }, :exit => Proc.new { |t| t.completed_at = nil }
|
||||
|
||||
validates_presence_of :description
|
||||
validates_length_of :description, :maximum => 100
|
||||
|
|
@ -20,6 +22,9 @@ class RecurringTodo < ActiveRecord::Base
|
|||
|
||||
validates_presence_of :context
|
||||
|
||||
named_scope :active, :conditions => { :state => 'active'}
|
||||
named_scope :completed, :conditions => { :state => 'completed'}
|
||||
|
||||
event :complete do
|
||||
transitions :to => :completed, :from => [:active]
|
||||
end
|
||||
|
|
@ -29,7 +34,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
# the following recurrence patterns can be stored:
|
||||
#
|
||||
#
|
||||
# daily todos - recurrence_period = 'daily'
|
||||
# every nth day - nth stored in every_other1
|
||||
# every work day - only_work_days = true
|
||||
|
|
@ -243,7 +248,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
if self.recurrence_selector == 0
|
||||
return self.every_other2
|
||||
else
|
||||
return Time.now.month
|
||||
return Time.zone.now.month
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -257,7 +262,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
if self.recurrence_selector == 1
|
||||
return self.every_other2
|
||||
else
|
||||
return Time.now.month
|
||||
return Time.zone.now.month
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -392,13 +397,21 @@ class RecurringTodo < ActiveRecord::Base
|
|||
# previous is the due date of the previous todo or it is the completed_at
|
||||
# date when the completed_at date is after due_date (i.e. you did not make
|
||||
# the due date in time)
|
||||
#
|
||||
#
|
||||
# assumes self.recurring_period == 'daily'
|
||||
|
||||
# determine start
|
||||
if previous.nil?
|
||||
start = self.start_from.nil? ? Time.now.utc : self.start_from
|
||||
start = self.start_from.nil? ? Time.zone.now : self.start_from
|
||||
else
|
||||
# use the next day
|
||||
start = previous + 1.day
|
||||
|
||||
unless self.start_from.nil?
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
# start_from as start to search for next date
|
||||
start = self.start_from if self.start_from > previous
|
||||
end
|
||||
end
|
||||
|
||||
if self.only_work_days
|
||||
|
|
@ -419,8 +432,9 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def get_weekly_date(previous)
|
||||
# determine start
|
||||
if previous == nil
|
||||
start = self.start_from.nil? ? Time.now.utc : self.start_from
|
||||
start = self.start_from.nil? ? Time.zone.now : self.start_from
|
||||
else
|
||||
start = previous + 1.day
|
||||
if start.wday() == 0
|
||||
|
|
@ -428,7 +442,13 @@ class RecurringTodo < ActiveRecord::Base
|
|||
# that week
|
||||
start += self.every_other1.week
|
||||
end
|
||||
unless self.start_from.nil?
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
# start_from as start to search for next date
|
||||
start = self.start_from if self.start_from > previous
|
||||
end
|
||||
end
|
||||
|
||||
# check if there are any days left this week for the next todo
|
||||
start.wday().upto 6 do |i|
|
||||
return start + (i-start.wday()).days unless self.every_day[i,1] == ' '
|
||||
|
|
@ -447,29 +467,44 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def get_monthly_date(previous)
|
||||
if previous.nil?
|
||||
start = self.start_from.nil? ? Time.now.utc : self.start_from
|
||||
else
|
||||
start = previous
|
||||
end
|
||||
|
||||
start = determine_start(previous)
|
||||
day = self.every_other1
|
||||
n = self.every_other2
|
||||
|
||||
|
||||
case self.recurrence_selector
|
||||
when 0 # specific day of the month
|
||||
if start.mday >= day
|
||||
if start.mday >= day
|
||||
# there is no next day n in this month, search in next month
|
||||
start += n.months
|
||||
#
|
||||
# start += n.months
|
||||
#
|
||||
# The above seems to not work. Fiddle with timezone. Looks like we hit a
|
||||
# bug in rails here where 2008-12-01 +0100 plus 1.month becomes
|
||||
# 2008-12-31 +0100. For now, just calculate in UTC and convert back to
|
||||
# local timezone.
|
||||
#
|
||||
# TODO: recheck if future rails versions have this problem too
|
||||
start = Time.utc(start.year, start.month, start.day)+n.months
|
||||
start = Time.zone.local(start.year, start.month, start.day)
|
||||
|
||||
# go back to day
|
||||
end
|
||||
return Time.utc(start.year, start.month, day)
|
||||
return Time.zone.local(start.year, start.month, day)
|
||||
|
||||
when 1 # relative weekday of a month
|
||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, start.month, start.year)
|
||||
if the_next.nil? || the_next <= start
|
||||
# the nth day is already passed in this month, go to next month and try
|
||||
# again
|
||||
the_next = the_next+n.months
|
||||
|
||||
# fiddle with timezone. Looks like we hit a bug in rails here where
|
||||
# 2008-12-01 +0100 plus 1.month becomes 2008-12-31 +0100. For now, just
|
||||
# calculate in UTC and convert back to local timezone.
|
||||
# TODO: recheck if future rails versions have this problem too
|
||||
the_next = Time.utc(the_next.year, the_next.month, the_next.day)+n.months
|
||||
the_next = Time.zone.local(the_next.year, the_next.month, the_next.day)
|
||||
|
||||
# TODO: if there is still no match, start will be set to nil. if we ever
|
||||
# support 5th day of the month, we need to handle this case
|
||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, the_next.month, the_next.year)
|
||||
|
|
@ -483,14 +518,18 @@ class RecurringTodo < ActiveRecord::Base
|
|||
|
||||
def get_xth_day_of_month(x, weekday, month, year)
|
||||
if x == 5
|
||||
# last -> count backwards
|
||||
# last -> count backwards. use UTC to avoid strange timezone oddities
|
||||
# where last_day -= 1.day seems to shift tz+0100 to tz+0000
|
||||
last_day = Time.utc(year, month, Time.days_in_month(month))
|
||||
while last_day.wday != weekday
|
||||
last_day -= 1.day
|
||||
end
|
||||
return last_day
|
||||
# convert back to local timezone
|
||||
return Time.zone.local(last_day.year, last_day.month, last_day.day)
|
||||
else
|
||||
# 1-4th -> count upwards
|
||||
# 1-4th -> count upwards last -> count backwards. use UTC to avoid strange
|
||||
# timezone oddities where last_day -= 1.day seems to shift tz+0100 to
|
||||
# tz+0000
|
||||
start = Time.utc(year,month,1)
|
||||
n = x
|
||||
while n > 0
|
||||
|
|
@ -500,32 +539,31 @@ class RecurringTodo < ActiveRecord::Base
|
|||
n -= 1
|
||||
start += 1.day unless n==0
|
||||
end
|
||||
return start
|
||||
# convert back to local timezone
|
||||
return Time.zone.local(start.year, start.month, start.day)
|
||||
end
|
||||
end
|
||||
|
||||
def get_yearly_date(previous)
|
||||
if previous.nil?
|
||||
start = self.start_from.nil? ? Time.now.utc : self.start_from
|
||||
else
|
||||
start = previous
|
||||
end
|
||||
|
||||
start = determine_start(previous)
|
||||
day = self.every_other1
|
||||
month = self.every_other2
|
||||
|
||||
case self.recurrence_selector
|
||||
when 0 # specific day of a specific month
|
||||
# if there is no next month n in this year, search in next year
|
||||
if start.month >= month
|
||||
start = Time.utc(start.year+1, month, 1) if start.day >= day
|
||||
start = Time.utc(start.year, month, 1) if start.day <= day
|
||||
if start.month > month || (start.month == month && start.day >= day)
|
||||
# if there is no next month n and day m in this year, search in next
|
||||
# year
|
||||
start = Time.zone.local(start.year+1, month, 1)
|
||||
else
|
||||
# if there is a next month n, stay in this year
|
||||
start = Time.zone.local(start.year, month, 1)
|
||||
end
|
||||
return Time.utc(start.year, month, day)
|
||||
return Time.zone.local(start.year, month, day)
|
||||
|
||||
when 1 # relative weekday of a specific month
|
||||
# if there is no next month n in this year, search in next year
|
||||
the_next = start.month > month ? Time.utc(start.year+1, month, 1) : start
|
||||
the_next = start.month > month ? Time.zone.local(start.year+1, month, 1) : start
|
||||
|
||||
# get the xth day of the month
|
||||
the_next = get_xth_day_of_month(self.every_other3, self.every_count, month, the_next.year)
|
||||
|
|
@ -592,4 +630,19 @@ class RecurringTodo < ActiveRecord::Base
|
|||
errors.add("", "At least one day must be selected in the weekly pattern") if self.every_day == ' '
|
||||
end
|
||||
|
||||
def determine_start(previous)
|
||||
if previous.nil?
|
||||
start = self.start_from.nil? ? Time.zone.now : self.start_from
|
||||
else
|
||||
start = previous
|
||||
|
||||
unless self.start_from.nil?
|
||||
# check if the start_from date is later than previous. If so, use
|
||||
# start_from as start to search for next date
|
||||
start = self.start_from if self.start_from > previous
|
||||
end
|
||||
end
|
||||
return start
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ class Todo < ActiveRecord::Base
|
|||
belongs_to :project
|
||||
belongs_to :user
|
||||
belongs_to :recurring_todo
|
||||
|
||||
|
||||
named_scope :active, :conditions => { :state => 'active' }
|
||||
named_scope :not_completed, :conditions => ['NOT state = ? ', 'completed']
|
||||
named_scope :are_due, :conditions => ['NOT todos.due IS NULL']
|
||||
|
||||
STARRED_TAG_NAME = "starred"
|
||||
|
||||
acts_as_state_machine :initial => :active, :column => 'state'
|
||||
|
|
@ -13,7 +17,7 @@ class Todo < ActiveRecord::Base
|
|||
# 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.now.utc }, :exit => Proc.new { |t| t.completed_at = nil }
|
||||
state :completed, :enter => Proc.new { |t| t.completed_at = Time.zone.now }, :exit => Proc.new { |t| t.completed_at = nil }
|
||||
state :deferred
|
||||
|
||||
event :defer do
|
||||
|
|
@ -68,6 +72,8 @@ class Todo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def show_from=(date)
|
||||
# parse Date objects into the proper timezone
|
||||
date = user.at_midnight(date) if (date.is_a? Date)
|
||||
activate! if deferred? && date.blank?
|
||||
defer! if active? && !date.blank? && date > user.date
|
||||
self[:show_from] = date
|
||||
|
|
@ -125,4 +131,46 @@ class Todo < ActiveRecord::Base
|
|||
return self.recurring_todo_id != nil
|
||||
end
|
||||
|
||||
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.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:")
|
||||
found_project = user.projects.build
|
||||
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.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.notes = notes
|
||||
todo.context_id = context_id
|
||||
todo.project_id = project_id unless project_id.nil?
|
||||
return todo
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,6 +51,21 @@ 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
|
||||
end
|
||||
end
|
||||
has_many :active_projects,
|
||||
:class_name => 'Project',
|
||||
|
|
@ -59,7 +74,7 @@ class User < ActiveRecord::Base
|
|||
has_many :active_contexts,
|
||||
:class_name => 'Context',
|
||||
:order => 'position ASC',
|
||||
:conditions => [ 'hide = ?', 'true' ]
|
||||
:conditions => [ 'hide = ?', false ]
|
||||
has_many :todos,
|
||||
:order => 'todos.completed_at DESC, todos.created_at DESC',
|
||||
:dependent => :delete_all
|
||||
|
|
@ -71,7 +86,7 @@ class User < ActiveRecord::Base
|
|||
:conditions => [ 'state = ?', 'deferred' ],
|
||||
:order => 'show_from ASC, todos.created_at DESC' do
|
||||
def find_and_activate_ready
|
||||
find(:all, :conditions => ['show_from <= ?', proxy_owner.time ]).collect { |t| t.activate! }
|
||||
find(:all, :conditions => ['show_from <= ?', Time.zone.now ]).collect { |t| t.activate! }
|
||||
end
|
||||
end
|
||||
has_many :completed_todos,
|
||||
|
|
@ -169,7 +184,11 @@ class User < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def date
|
||||
time.to_date
|
||||
time.midnight
|
||||
end
|
||||
|
||||
def at_midnight(date)
|
||||
return TimeZone[prefs.time_zone].local(date.year, date.month, date.day, 0, 0, 0)
|
||||
end
|
||||
|
||||
def generate_token
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@
|
|||
<% end -%>
|
||||
<% if source_view_is :context %>
|
||||
<span class="in_place_editor_field" id="context_name_in_place_editor"><%= context.name %></span>
|
||||
<%= in_place_editor 'context_name_in_place_editor', { :url => { :controller => 'contexts', :action => 'update', :id => context.id, :field => 'name', :wants_render => false, :escape => false} , :options=>"{method:'put'}" } %>
|
||||
<%= in_place_editor 'context_name_in_place_editor', {
|
||||
:url => { :controller => 'contexts', :action => 'update', :id => context.id, :field => 'name', :update_context_name => true, :escape => false},
|
||||
:options=>"{method:'put'}", :script => true } %>
|
||||
<% else %>
|
||||
<%= link_to_context( context ) %>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -53,4 +53,9 @@
|
|||
</div>
|
||||
<%
|
||||
sortable_element 'list-contexts', get_listing_sortable_options
|
||||
-%>
|
||||
-%>
|
||||
<script type="text/javascript">
|
||||
window.onload=function(){
|
||||
Nifty("div#context_new_container","normal");
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
10
app/views/contexts/update_context_name.js.rjs
Normal file
10
app/views/contexts/update_context_name.js.rjs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
page['context_name_in_place_editor'].replace_html @context.name
|
||||
|
||||
page['default_context_name_id'].value = @context.name
|
||||
page['todo_context_name'].value = @context.name
|
||||
|
||||
# renew context auto complete array
|
||||
page << "contextAutoCompleter.options.array = #{context_names_for_autocomplete}; contextAutoCompleter.changed = true"
|
||||
|
||||
status_message = "Name of context was changed"
|
||||
page.notify :notice, status_message, 5.0
|
||||
|
|
@ -1,44 +1,49 @@
|
|||
<div id="feeds">
|
||||
<div id="feedlegend">
|
||||
<h3>Exporting data</h3>
|
||||
<p>You can choose between the following formats:</p>
|
||||
<ul>
|
||||
<li><strong>YAML: </strong>Best for exporting data. <br/><i>Please note that importing YAML files is currently supported only in experimentally. Do not rely on it for backing up critical data.</i></li>
|
||||
<li><strong>CSV: </strong>Best for importing into spreadsheet or data analysis software</li>
|
||||
<li><strong>XML: </strong>Best for importing or repurposing the data</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<table class="export_table">
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>Download link</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>YAML file containing all your actions, contexts, projects, tags and notes</td>
|
||||
<td><%= link_to "YAML file", :controller => 'data', :action => 'yaml_export' %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CSV file containing all of your actions, with named contexts and projects</td>
|
||||
<td><%= link_to "CSV file (actions, contexts and projects)", :controller => 'data', :action => 'csv_actions' %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CSV file containing all your notes</td>
|
||||
<td><%= link_to "CSV file (notes only)", :controller => 'data', :action => 'csv_notes' %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>XML file containing all your actions, contexts, projects, tags and notes</td>
|
||||
<td><%= link_to "XML file (actions only)", :controller => 'data', :action => 'xml_export' %></td>
|
||||
</tr>
|
||||
</table>
|
||||
</p>
|
||||
|
||||
<div id="feeds">
|
||||
<div id="feedlegend">
|
||||
<h3>Importing data</h3>
|
||||
<p>Curently there is a experimental support for importing YAML files. Beware: all your current data will be destroyed before importing the YAML file, so if you have access to the database, we strongly reccoment backing up the database right now in case that anything goes wrong.</p>
|
||||
<p><%= link_to "Start import", :controller => 'data', :action => 'yaml_form' %>.</p>
|
||||
<div id="display_box">
|
||||
<div id="feeds">
|
||||
<div id="feedlegend">
|
||||
<h3>Importing data</h3>
|
||||
<p>Curently there is a experimental support for importing YAML files.
|
||||
Beware: all your current data will be destroyed before importing the YAML
|
||||
file, so if you have access to the database, we strongly recomment backing up
|
||||
the database right now in case that anything goes wrong.
|
||||
</p>
|
||||
<p><%= link_to "Start import", :controller => 'data', :action => 'yaml_form' %>.</p>
|
||||
<br/><br/><h3>Exporting data</h3>
|
||||
<p>You can choose between the following formats:</p>
|
||||
<ul>
|
||||
<li><strong>YAML: </strong>Best for exporting data. <br/><i>Please note that importing YAML files is currently supported only in experimentally. Do not rely on it for backing up critical data.</i></li>
|
||||
<li><strong>CSV: </strong>Best for importing into spreadsheet or data analysis software</li>
|
||||
<li><strong>XML: </strong>Best for importing or repurposing the data</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<table class="export_table">
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>Download link</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>YAML file containing all your actions, contexts, projects, tags and notes</td>
|
||||
<td><%= link_to "YAML file", :controller => 'data', :action => 'yaml_export' %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CSV file containing all of your actions, with named contexts and projects</td>
|
||||
<td><%= link_to "CSV file (actions, contexts and projects)", :controller => 'data', :action => 'csv_actions' %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CSV file containing all your notes</td>
|
||||
<td><%= link_to "CSV file (notes only)", :controller => 'data', :action => 'csv_notes' %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>XML file containing all your actions, contexts, projects, tags and notes</td>
|
||||
<td><%= link_to "XML file (actions only)", :controller => 'data', :action => 'xml_export' %></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- End of feeds -->
|
||||
</div>
|
||||
|
||||
</div><!-- End of feeds -->
|
||||
<script type="text/javascript">
|
||||
window.onload=function(){
|
||||
Nifty("div#feedlegend","normal");
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
<div id="display_box">
|
||||
<div id="feeds">
|
||||
<div id="feedlegend">
|
||||
<p><b>Beware</b>: all your current data will be destroyed before importing the YAML file, so if you have access to the database, we strongly reccoment backing up the database right now in case that anything goes wrong.</p>
|
||||
<p>Paste the contents of the YAML file you exported into the text box below:</p>
|
||||
</div>
|
||||
<p>
|
||||
<% form_for :import, @import, :url => {:controller => 'data', :action => 'yaml_import'} do |f| %>
|
||||
<%= f.text_area :yaml %><br />
|
||||
<input type="submit" value="Import data">
|
||||
<% end %>
|
||||
</p>
|
||||
</div><!-- End of feeds -->
|
||||
<div id="feeds">
|
||||
<div id="feedlegend">
|
||||
<p><b>Beware</b>: all your current data will be destroyed before importing
|
||||
the YAML file, so if you have access to the database, we strongly recommend
|
||||
backing up the database right now in case that anything goes wrong.
|
||||
</p>
|
||||
<p>Paste the contents of the YAML file you exported into the text box below:</p>
|
||||
</div>
|
||||
<p>
|
||||
<% form_for :import, @import, :url => {:controller => 'data', :action => 'yaml_import'} do |f| %>
|
||||
<%= f.text_area :yaml %><br />
|
||||
<input type="submit" value="Import data">
|
||||
<% end %>
|
||||
</p>
|
||||
</div><!-- End of feeds -->
|
||||
</div><!-- End of display_box -->
|
||||
|
||||
<div id="input_box">
|
||||
</div><!-- End of input box -->
|
||||
<script type="text/javascript">
|
||||
window.onload=function(){
|
||||
Nifty("div#feedlegend","normal");
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -64,10 +64,13 @@
|
|||
Active projects with their actions
|
||||
</li>
|
||||
<li><h4>Feeds for incomplete actions in a specific context:</h4>
|
||||
<% if @active_contexts.empty? && @hidden_contexts.empty? -%>
|
||||
<ul><li>There need to be at least one context before you can request a feed</li></ul>
|
||||
<% else -%>
|
||||
<ul>
|
||||
<li>Step 1 - Choose the context you want a feed of:
|
||||
<select name="feed-contexts" id="feed-contexts">
|
||||
<%= options_from_collection_for_select(@active_contexts, "id", "name", @active_contexts.first.id) -%>
|
||||
<%= options_from_collection_for_select(@active_contexts, "id", "name", @active_contexts.first.id) unless @active_projects.empty?-%>
|
||||
<%= options_from_collection_for_select(@hidden_contexts, "id", "name") -%>
|
||||
</select>
|
||||
<%= observe_field "feed-contexts", :update => "feeds-for-context",
|
||||
|
|
@ -80,17 +83,21 @@
|
|||
<li>Step 2 - Select the feed for this context
|
||||
<div id="feedicons-context">
|
||||
<div id="feeds-for-context">
|
||||
<%= render :partial => 'feed_for_context', :locals => { :context => @active_contexts.first } %>
|
||||
<%= render :partial => 'feed_for_context', :locals => { :context => @active_contexts.empty? ? @hidden_contexts.first : @active_contexts.first } %>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<% end -%>
|
||||
</li>
|
||||
<li><h4>Feeds for incomplete actions in a specific project:</h4>
|
||||
<% if @active_projects.empty? && @hidden_projects.empty? -%>
|
||||
<ul><li>There need to be at least one project before you can request a feed</li></ul>
|
||||
<% else -%>
|
||||
<ul>
|
||||
<li>Step 1 - Choose the project you want a feed of:
|
||||
<select name="feed-projects" id="feed-projects">
|
||||
<%= options_from_collection_for_select(@active_projects, "id", "name", @active_projects.first.id) -%>
|
||||
<%= options_from_collection_for_select(@active_projects, "id", "name", @active_projects.first.id) unless @active_projects.empty?-%>
|
||||
<%= options_from_collection_for_select(@hidden_projects, "id", "name") -%>
|
||||
<%= options_from_collection_for_select(@completed_projects, "id", "name") -%>
|
||||
</select>
|
||||
|
|
@ -104,11 +111,12 @@
|
|||
<li>Step 2 - Select the feed for this project
|
||||
<div id="feedicons-project">
|
||||
<div id="feeds-for-project">
|
||||
<%= render :partial => 'feed_for_project', :locals => { :project => @active_projects.first } %>
|
||||
<%= render :partial => 'feed_for_project', :locals => { :project => @active_projects.empty? ? @hidden_projects.first : @active_projects.first } %>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<% end -%>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ the newly created action.
|
|||
*)
|
||||
|
||||
(* Edit appropriately for your setup *)
|
||||
property myUsername to "<%= current_user.login %>"
|
||||
property myToken to "<%= current_user.token %>"
|
||||
property myContextID to <%= context.id %> (* <%= context.name %> *)
|
||||
property myUsername : "<%= current_user.login %>"
|
||||
property myToken : "<%= current_user.token %>"
|
||||
property myContextID : <%= context.id %> (* <%= context.name %> *)
|
||||
|
||||
-- this string is used when the message subject is empty
|
||||
property emptySubject : "No Subject Specified"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,18 @@
|
|||
<p>Do you have one of your own to add? <a href="http://www.rousette.org.uk/projects/forums/viewforum/10/" title="Tracks | Tips and Tricks">Tell us about it in our Tips and Tricks forum
|
||||
</a> and we may include it on this page in a future versions of Tracks.</p>
|
||||
|
||||
<a name="message_gateway"> </a>
|
||||
<h2>Integrated email/SMS receiver</h2>
|
||||
<p>
|
||||
If Tracks is running on the same server as your mail server, you can use the integrated mail handler built into tracks. Steps to set it up:
|
||||
<ul>
|
||||
<li>Go to <%= link_to "Preferences", preferences_url %> and set your "From email" and "default email context" for todos sent in via email (which could come from an SMS message)</li>
|
||||
<li>In sendmail/qmail/postfix/whatever, set up an email address alias to pipe messages to <pre style="font-size:1.5em">/PATH/TO/RUBY/ruby /PATH/TO/TRACKS/script/runner -e production 'MessageGateway.receive(STDIN.read)'</pre></li>
|
||||
<li>Send an email to your newly configured address!</li>
|
||||
</ul>
|
||||
You can also use the Rich Todo API to send in tasks like "do laundry @ Home" or "Call Bill > project X". The subject of the message will fill description, context, and project, while the body will populate the tasks's note.
|
||||
</p>
|
||||
|
||||
<a name="applescript1-section"> </a>
|
||||
<h2>Add an Action with Applescript</h2>
|
||||
<p>This is a simple script that pops up a dialog box asking for a description, and then sends that to Tracks with a hard-coded context.</p>
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@
|
|||
<pre>
|
||||
<code>
|
||||
$ curl -u username:p4ssw0rd -H "Content-Type: text/xml" \
|
||||
-d "project[name]=Build a treehouse for the kids" \
|
||||
-d "<project><name>Build a treehouse for the kids</name></project>" \
|
||||
<%= home_url %>projects.xml -i
|
||||
>> HTTP/1.1 201 Created
|
||||
Location: <%= home_url %>projects/65.xml
|
||||
|
|
@ -86,7 +86,7 @@ Location: <%= home_url %>projects/65.xml
|
|||
<pre>
|
||||
<code>
|
||||
$ curl -u username:p4ssw0rd -H "Content-Type: text/xml" \
|
||||
-d "todo[description]=Model treehouse in SketchUp&todo[context_id]=2&todo[project_id]=65" \
|
||||
-d "<todo><description>Model treehouse in SketchUp</description><context_id>2</context_id><project_id>65<project_id>" \
|
||||
<%= home_url %>todos.xml -i
|
||||
>> HTTP/1.1 201 Created
|
||||
Location: <%= home_url %>todos/452.xml
|
||||
|
|
@ -99,7 +99,7 @@ Location: <%= home_url %>todos/452.xml
|
|||
<pre>
|
||||
<code>
|
||||
$ curl -u username:p4ssw0rd -H "Content-Type: text/xml" -X PUT \
|
||||
-d "todo[notes]=use maple texture" \
|
||||
-d "<todo><notes>use maple texture</notes></todos>" \
|
||||
<%= home_url %>todos/452.xml -i
|
||||
>> HTTP/1.1 200 OK
|
||||
...
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@
|
|||
<script type="text/javascript">
|
||||
window.onload=function(){
|
||||
Nifty("div#todo_new_action_container","normal");
|
||||
Nifty("div#project_new_project_container","normal");
|
||||
Nifty("div#context_new_container","normal");
|
||||
Nifty("div#recurring_new_container","normal");
|
||||
if ($('flash').visible()) { new Effect.Fade("flash",{duration:5.0}); }
|
||||
}
|
||||
</script>
|
||||
|
|
@ -49,7 +46,7 @@ window.onload=function(){
|
|||
<li><%= navigation_link("Home", home_path, {:accesskey => "t", :title => "Home"} ) %></li>
|
||||
<li><%= navigation_link( "Contexts", contexts_path, {:accesskey=>"c", :title=>"Contexts"} ) %></li>
|
||||
<li><%= navigation_link( "Projects", projects_path, {:accesskey=>"p", :title=>"Projects"} ) %></li>
|
||||
<li><%= navigation_link( "Tickler", tickler_path, :title => "Tickler" ) %></li>
|
||||
<li><%= navigation_link( "Tickler", tickler_path, {:accesskey =>"k", :title => "Tickler"} ) %></li>
|
||||
<li><%= navigation_link( "Done", done_path, {:accesskey=>"d", :title=>"Completed"} ) %></li>
|
||||
<li><%= navigation_link( "Notes", notes_path, {:accesskey => "o", :title => "Show all notes"} ) %></li>
|
||||
<li><%= navigation_link( "Preferences", preferences_path, {:accesskey => "u", :title => "Show my preferences"} ) %></li>
|
||||
|
|
@ -57,6 +54,7 @@ window.onload=function(){
|
|||
<% if current_user.is_admin? -%>
|
||||
<li><%= navigation_link("Admin", users_path, {:accesskey => "a", :title => "Add or delete users"} ) %></li>
|
||||
<% end -%>
|
||||
<li><%= navigation_link(image_tag("x-office-calendar.png", :size => "16X16", :border => 0), calendar_path, :title => "Calendar of due actions" ) %></li>
|
||||
<li><%= navigation_link(image_tag("recurring_menu16x16.png", :size => "16X16", :border => 0), {:controller => "recurring_todos", :action => "index"}, :title => "Manage recurring actions" ) %></li>
|
||||
<li><%= navigation_link(image_tag("feed-icon.png", :size => "16X16", :border => 0), {:controller => "feedlist", :action => "index"}, :title => "See a list of available feeds" ) %></li>
|
||||
<li><%= navigation_link(image_tag("menustar.gif", :size => "16X16", :border => 0), tag_path("starred"), :title => "See your starred actions" ) %></li>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div id="<%= dom_id(note, 'container') %>">
|
||||
<h2><%= link_to("Note #{note.id}", note_path(note), :title => "Show note #{note.id}" ) %></h2>
|
||||
<div id="<%= dom_id(note) %>">
|
||||
<%= sanitize(textilize(note.body.gsub(/((https?:\/\/[^ \n\t]*))/, '"\1":\2'))) %>
|
||||
<%= sanitize(markdown(auto_link(note.body))) %>
|
||||
|
||||
<div class="note_footer">
|
||||
<%= link_to_remote(
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
<li><strong>refresh:</strong> automatic refresh interval for each of the pages (in minutes)</li>
|
||||
<li><strong>verbose action descriptor:</strong> when true, show project/context name in action listing; when false show [P]/[C] with tool tips</li>
|
||||
<li><strong>mobile todos per page:</strong> the maximum number of actions to show on a single page in the mobile view</li>
|
||||
<li><strong>From email:</string> the email address you use for sending todos as email or SMS messages to your Tracks account</li>
|
||||
<li><string>Default email context:</string> the context to which tasks sent in via email or SMS should be added</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
|
@ -67,6 +69,8 @@
|
|||
<%= 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 "Update" %></td>
|
||||
<td><%= link_to "Cancel", :action => 'index' %></td>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
<li>Refresh interval (in minutes): <span class="highlight"><%= prefs.refresh %></span></li>
|
||||
<li>Verbose action descriptors: <span class="highlight"><%= prefs.verbose_action_descriptors %></span></li>
|
||||
<li>Actions per page (Mobile View): <span class="highlight"><%= prefs.mobile_todos_per_page %></span></li>
|
||||
<li>From email: <span class="highlight"><%= prefs.sms_email %></span></li>
|
||||
<li>Default email context: <span class="highlight"><%= prefs.sms_context.nil? ? "None" : prefs.sms_context.name %></span></li>
|
||||
</ul>
|
||||
<div class="actions">
|
||||
<%= link_to "Edit preferences »", { :controller => 'preferences', :action => 'edit'}, :class => 'edit_link' %>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<% @not_done = project.not_done_todos -%>
|
||||
<% #@not_done = project.not_done_todos -%>
|
||||
|
||||
<div id="p<%= project.id %>" class="container project">
|
||||
<h2>
|
||||
|
|
@ -6,7 +6,9 @@
|
|||
<a href="#" class="container_toggle" id="toggle_p<%= project.id %>"><%= image_tag("collapse.png") %></a>
|
||||
<% end %>
|
||||
<span class="in_place_editor_field" id="project_name_in_place_editor"><%= project.name %></span>
|
||||
<%= in_place_editor 'project_name_in_place_editor', { :url => { :controller => 'projects', :action => 'update', :id => project.id, :field => 'name', :wants_render => false, :escape => false} , :options=>"{method:'put'}" } %>
|
||||
<%= in_place_editor 'project_name_in_place_editor', {
|
||||
:url => { :controller => 'projects', :action => 'update', :id => project.id, :field => 'name', :update_project_name => true, :escape => false} ,
|
||||
:options=>"{method:'put'}", :script => true} %>
|
||||
</h2>
|
||||
<% unless project.description.blank? -%>
|
||||
<div class="project_description"><%= sanitize(project.description) %></div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<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><%= state.titlecase %> Projects</h2>
|
||||
<div class="menu_sort"><span class="sort_separator">Sort </span>
|
||||
<div class="alpha_sort">
|
||||
<%= link_to("Sort Alphabetically", alphabetize_projects_path(:state => state),
|
||||
<%= link_to("Alphabetically", alphabetize_projects_path(:state => state),
|
||||
:class => "alphabetize_link", :title => "Sort these projects alphabetically") %>
|
||||
<% apply_behavior '.alphabetize_link:click', :prevent_default => true do |page, element|
|
||||
page.confirming 'Are you sure that you want to sort these projects alphabetically? This will replace the existing sort order.' do
|
||||
|
|
@ -10,10 +11,21 @@
|
|||
page << remote_to_href(:complete => "alphaSort.stopWaiting()")
|
||||
end
|
||||
end
|
||||
%></div><span class="sort_separator"> | </span><div class="tasks_sort">
|
||||
<%= link_to("By number of tasks", actionize_projects_path(:state => state),
|
||||
:class => "actionize_link", :title => "Sort these projects by number of tasks") %>
|
||||
<% apply_behavior '.actionize_link:click', :prevent_default => true do |page, element|
|
||||
page.confirming 'Are you sure that you want to sort these projects by the number of tasks? This will replace the existing sort order.' do
|
||||
page << "tasksSort = this.up('.tasks_sort');
|
||||
tasksSort.startWaiting();"
|
||||
page << remote_to_href(:complete => "tasksSort.stopWaiting()")
|
||||
end
|
||||
end
|
||||
%></div>
|
||||
</div>
|
||||
|
||||
<div id="list-<%= state %>-projects" class="project-list">
|
||||
<%= render :partial => 'project_listing', :collection => project_state_group %>
|
||||
</div>
|
||||
<%= sortable_element "list-#{state}-projects", get_listing_sortable_options("list-#{state}-projects") %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
6
app/views/projects/actionize.js.rjs
Normal file
6
app/views/projects/actionize.js.rjs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
list_id = "list-#{@state}-projects"
|
||||
page.replace_html list_id,
|
||||
:partial => 'project_listing',
|
||||
:collection => @projects
|
||||
page.sortable list_id, get_listing_sortable_options(list_id)
|
||||
|
||||
|
|
@ -61,4 +61,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload=function(){
|
||||
Nifty("div#project_new_project_container","normal");
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
10
app/views/projects/update_project_name.js.rjs
Normal file
10
app/views/projects/update_project_name.js.rjs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
page['project_name_in_place_editor'].replace_html @project.name
|
||||
|
||||
page['default_project_name_id'].value = @project.name
|
||||
page['todo_project_name'].value = @project.name
|
||||
|
||||
# renew project auto complete array
|
||||
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}; projectAutoCompleter.changed = true"
|
||||
|
||||
status_message = "Name of project was changed"
|
||||
page.notify :notice, status_message, 5.0
|
||||
|
|
@ -6,7 +6,16 @@
|
|||
<div class="description">
|
||||
<span class="todo.descr"><%= sanitize(recurring_todo.description) %></span> <%= recurring_todo_tag_list %>
|
||||
<span class='recurrence_pattern'>
|
||||
[<%=recurrence_target(recurring_todo)%> <%= recurring_todo.recurrence_pattern %> <%= recurrence_time_span(recurring_todo) %>]
|
||||
<%
|
||||
rt = recurrence_target(recurring_todo)
|
||||
rp = recurring_todo.recurrence_pattern
|
||||
# only add space if recurrence_pattern has content
|
||||
rp = " " + rp if !rp.nil?
|
||||
rts = recurrence_time_span(recurring_todo)
|
||||
# only add space if recurrence_time_span has content
|
||||
rts = " " + rts if !(rts == "")
|
||||
%>
|
||||
[<%=rt%><%=rp%><%=rts%>]
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -101,22 +101,22 @@
|
|||
<div id="recurring_monthly" style="display:none">
|
||||
<label>Settings for monthly recurring actions</label><br/>
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', true)%> Day <%=
|
||||
text_field_tag('recurring_todo[monthly_every_x_day]', Time.now.mday, {"size" => 3, "tabindex" => 9}) %> on every <%=
|
||||
text_field_tag('recurring_todo[monthly_every_x_day]', Time.zone.now.mday, {"size" => 3, "tabindex" => 9}) %> on every <%=
|
||||
text_field_tag('recurring_todo[monthly_every_x_month]', 1, {"size" => 3, "tabindex" => 10}) %> month<br/>
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day')%> The <%=
|
||||
select_tag('recurring_todo[monthly_every_xth_day]', options_for_select(@xth_day), {}) %> <%=
|
||||
select_tag('recurring_todo[monthly_day_of_week]' , options_for_select(@days_of_week, Time.now.wday), {}) %> of every <%=
|
||||
select_tag('recurring_todo[monthly_day_of_week]' , options_for_select(@days_of_week, Time.zone.now.wday), {}) %> of every <%=
|
||||
text_field_tag('recurring_todo[monthly_every_x_month2]', 1, {"size" => 3, "tabindex" => 11}) %> month<br/>
|
||||
</div>
|
||||
<div id="recurring_yearly" style="display:none">
|
||||
<label>Settings for yearly recurring actions</label><br/>
|
||||
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', true)%> Every <%=
|
||||
select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, Time.now.month), {}) %> <%=
|
||||
text_field_tag('recurring_todo[yearly_every_x_day]', Time.now.day, "size" => 3, "tabindex" => 9) %><br/>
|
||||
select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, Time.zone.now.month), {}) %> <%=
|
||||
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')%> The <%=
|
||||
select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day), {}) %> <%=
|
||||
select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, Time.now.wday), {}) %> of <%=
|
||||
select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, Time.now.month), {}) %><br/>
|
||||
select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, Time.zone.now.wday), {}) %> of <%=
|
||||
select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, Time.zone.now.month), {}) %><br/>
|
||||
</div>
|
||||
<div id="recurring_target">
|
||||
<label>Set recurrence on</label><br/>
|
||||
|
|
|
|||
|
|
@ -40,3 +40,9 @@
|
|||
apply_behaviour "#recurring_edit_period:click",
|
||||
"TracksForm.hide_all_edit_recurring(); $('recurring_edit_'+TracksForm.get_edit_period()).show();"
|
||||
-%>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload=function(){
|
||||
Nifty("div#recurring_new_container","normal");
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<div id="todo_new_action_container">
|
||||
|
||||
<div id="toggle_action_new" class="hide_form">
|
||||
<a title="Hide new action form" accesskey="n">« Hide form</a>
|
||||
<a title="Hide new action form" accesskey="n" href="#">« Hide form</a>
|
||||
<% apply_behavior '#toggle_action_new a:click', :prevent_default => true do |page|
|
||||
page << "TracksForm.toggle('toggle_action_new', 'todo_new_action', 'todo-form-new-action',
|
||||
'« Hide form', 'Hide next action form',
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
<label for="todo_notes">Notes</label>
|
||||
<%= text_area( "todo", "notes", "cols" => 29, "rows" => 6, "tabindex" => 2) %>
|
||||
|
||||
<input id="default_project_name_id" name="default_project_name" type="hidden" value="<%=@initial_project_name-%>" />
|
||||
<label for="todo_project_name">Project</label>
|
||||
<input id="todo_project_name" name="project_name" autocomplete="off" tabindex="3" size="30" type="text" value="<%= @initial_project_name %>" />
|
||||
<div class="page_name_auto_complete" id="project_list" style="display:none"></div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
<div id="error_status"><%= error_messages_for("todo", :object_name => 'action') %></div>
|
||||
|
||||
<%= hidden_field( "todo", "id" ) %>
|
||||
<%= source_view_tag( @source_view ) %>
|
||||
<%= hidden_field( "todo", "id" ) -%>
|
||||
<%= source_view_tag( @source_view ) -%>
|
||||
<%= "<INPUT TYPE=\"hidden\" name=\"_tag_name\" value=\""+ @tag_name+"\">" unless @tag_name.nil? -%>
|
||||
|
||||
<label for="<%= dom_id(@todo, 'description') %>">Description</label>
|
||||
<%= text_field( "todo", "description", "size" => 30, "tabindex" => 8) %>
|
||||
|
|
@ -48,16 +49,16 @@ Event.observe($('<%= dom_id(@todo, 'context_name') %>'), "click", <%= dom_id(@to
|
|||
<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) %>
|
||||
<a href="#" id="<%= dom_id(@todo, 'due_x') %>" class="date_clear">
|
||||
<%= image_tag("cancel.png", :alt => "") %>
|
||||
<a href="#" id="<%= dom_id(@todo, 'due_x') %>" class="date_clear" title="Clear due date">
|
||||
<%= image_tag("delete_off.png", :alt => "Clear due date") %>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="show_from_input">
|
||||
<label for="<%= dom_id(@todo, 'show_from') %>">Show from</label>
|
||||
<%= date_field_tag("todo[show_from]", dom_id(@todo, 'show_from'), format_date(@todo.show_from), "tabindex" => 14) %>
|
||||
<a href="#" id="<%= dom_id(@todo, 'show_from_x') %>" class="date_clear">
|
||||
<%= image_tag("cancel.png", :alt => "") %>
|
||||
<a href="#" id="<%= dom_id(@todo, 'show_from_x') %>" class="date_clear" title="Clear show from date">
|
||||
<%= image_tag("delete_off.png", :alt => "Clear show from date") %>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ end %>
|
|||
<h2><label for="tag_list">Tags (separate with commas)</label></h2>
|
||||
<%= text_field_tag "tag_list", @tag_list_text, :size => 30, :tabindex => 6 %>
|
||||
<h2><label for="todo_due">Due</label></h2>
|
||||
<%= date_select("todo", "due", :order => [:day, :month, :year],
|
||||
:start_year => this_year, :include_blank => true) %>
|
||||
<%= date_select("todo", "due", {:order => [:day, :month, :year],
|
||||
:start_year => this_year, :include_blank => true}, :tabindex => 7) %>
|
||||
<h2><label for="todo_show_from">Show from</label></h2>
|
||||
<%= date_select("todo", "show_from", :order => [:day, :month, :year],
|
||||
:start_year => this_year, :include_blank => true) %>
|
||||
<%= date_select("todo", "show_from", {:order => [:day, :month, :year],
|
||||
:start_year => this_year, :include_blank => true}, :tabindex => 8) %>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<%= remote_star_icon %>
|
||||
<%= remote_toggle_checkbox unless source_view_is :deferred %>
|
||||
<div class="description<%= staleness_class( todo ) %>">
|
||||
<% unless @todo.completed? %><span class="defer-container"><%= defer_link(1) %> <%= defer_link(7) %></span><% end %>
|
||||
<%= date_span -%>
|
||||
<span class="todo.descr"><%= h sanitize(todo.description) %></span>
|
||||
<%= link_to(image_tag("recurring16x16.png"), {:controller => "recurring_todos", :action => "index"}, :class => "recurring_icon") if @todo.from_recurring_todo? %>
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
element.next('.todo_notes').toggle
|
||||
end -%>
|
||||
<div class="todo_notes" id="<%= dom_id(item, 'notes') %>" style="display:none">
|
||||
<%= markdown( item.notes.gsub(/((https?:\/\/[^ \n\t]*))/, '"\1":\2') ) %>
|
||||
<%= sanitize(markdown( auto_link(item.notes)) ) %>
|
||||
</div>
|
||||
68
app/views/todos/calendar.html.erb
Normal file
68
app/views/todos/calendar.html.erb
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<div id="display_box">
|
||||
|
||||
<div class="container">
|
||||
<h2>Due today</h2>
|
||||
<div id="empty_due_today" <%= "style=\"display:none\"" unless @due_today.empty? %>>
|
||||
No actions due today
|
||||
</div>
|
||||
<div id="due_today">
|
||||
<%= render :partial => "todos/todo", :collection => @due_today %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2>Due in rest of this week</h2>
|
||||
<div id="empty_due_this_week" <%= "style=\"display:none\"" unless @due_this_week.empty? %>>
|
||||
No actions due in rest of this week
|
||||
</div>
|
||||
<div id="due_this_week">
|
||||
<%= render :partial => "todos/todo", :collection => @due_this_week %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2>Due next week</h2>
|
||||
<div id="empty_due_next_week" <%= "style=\"display:none\"" unless @due_next_week.empty? %>>
|
||||
No actions due in next week
|
||||
</div>
|
||||
<div id="due_next_week">
|
||||
<%= render :partial => "todos/todo", :collection => @due_next_week %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2>Due in rest of <%= Time.zone.now.strftime("%B") %> </h2>
|
||||
<div id="empty_due_this_month" <%= "style=\"display:none\"" unless @due_this_month.empty? %>>
|
||||
No actions due in rest of this month
|
||||
</div>
|
||||
<div id="due_this_month">
|
||||
<%= render :partial => "todos/todo", :collection => @due_this_month %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h2>Due in <%= (Time.zone.now+1.month).strftime("%B") %> and later</h2>
|
||||
<div id="empty_due_after_this_month" <%= "style=\"display:none\"" unless @due_after_this_month.empty? %>>
|
||||
No actions due after this month
|
||||
</div>
|
||||
<div id="due_after_this_month">
|
||||
<%= render :partial => "todos/todo", :collection => @due_after_this_month %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- End of display_box -->
|
||||
<div class="input_box" id="input_box">
|
||||
<!--
|
||||
<input class="hide_tickler" id="hide_tickler" type="checkbox" tabindex="5" name="hide_tickler" checked="true"/>
|
||||
<label for="hide_tickler"> Show actions in tickler</label>
|
||||
<br/><br/>
|
||||
-->
|
||||
<p><%= link_to('<span class="feed">iCal</span>', {:format => 'ics', :token => current_user.token}, :title => "iCal feed" ) %>
|
||||
- Get this calendar in iCal format</p>
|
||||
</div>
|
||||
|
||||
<%
|
||||
apply_behavior 'input.hide_tickler:click', :prevent_default => true do |page|
|
||||
page << "alert('hiding action in tickler from calendar is not yet implemented');"
|
||||
end
|
||||
%>
|
||||
31
app/views/todos/calendar.ics.erb
Normal file
31
app/views/todos/calendar.ics.erb
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
BEGIN:VCALENDAR
|
||||
PRODID:-//TRACKS//<%= TRACKS_VERSION %>//EN
|
||||
VERSION:2.0
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:PUBLISH
|
||||
X-WR-CALNAME:Tracks
|
||||
<% for todo in @due_all
|
||||
due_date = todo.due
|
||||
overdue_text = ""
|
||||
if due_date.at_midnight < Time.zone.now.at_midnight
|
||||
due_date = Time.zone.now
|
||||
overdue_text = "Overdue: "
|
||||
end
|
||||
%>BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:<%= due_date.strftime("%Y%m%d") %>
|
||||
DTEND;VALUE=DATE:<%= (due_date+1.day).strftime("%Y%m%d") %>
|
||||
DTSTAMP:<%= due_date.strftime("%Y%m%dT%H%M%SZ") %>
|
||||
UID:<%= todo_url(todo) %>
|
||||
CLASS:PUBLIC
|
||||
CATEGORIES:Tracks
|
||||
CREATED:<%= todo.created_at.strftime("%Y%m%dT%H%M%SZ") %>
|
||||
DESCRIPTION:<%= format_ical_notes(todo.notes) %>
|
||||
LAST-MODIFIED:<%= todo.updated_at.strftime("%Y%m%dT%H%M%SZ") %>
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:<%= overdue_text + todo.description %>
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
<% end
|
||||
%>END:VCALENDAR
|
||||
|
|
@ -8,6 +8,7 @@ if @saved
|
|||
page['badge_count'].replace_html @down_count
|
||||
page.send :record, "Form.reset('todo-form-new-action');Form.focusFirstElement('todo-form-new-action')"
|
||||
page['todo_context_name'].value = @initial_context_name
|
||||
page['todo_project_name'].value = @initial_project_name
|
||||
page << "updateContextNamesForAutoComplete(#{context_names_for_autocomplete})" if @new_context_created
|
||||
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}" if @new_project_created
|
||||
if should_show_new_item()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
if @saved
|
||||
page[@todo].remove
|
||||
page.show "empty_"+@original_item_due_id if @old_due_empty
|
||||
page['badge_count'].replace_html @down_count
|
||||
|
||||
# remove context if empty
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<% form_tag formatted_todos_path(:m), :method => :post do %>
|
||||
<%= render :partial => 'edit_mobile' %>
|
||||
<p><input type="submit" value="Create" tabindex="6" /></p>
|
||||
<p><input type="submit" value="Create" tabindex="12" accesskey="#" /></p>
|
||||
<% end -%>
|
||||
<%= link_to "Back", @return_path %>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<% form_tag formatted_todo_path(@todo, :m), :method => :put do %>
|
||||
<%= render :partial => 'edit_mobile', :locals => { :parent_container_type => "show_mobile" } %>
|
||||
<p><input type="submit" value="Update" tabindex="6" /></p>
|
||||
<p><input type="submit" value="Update" tabindex="6" accesskey="#" /></p>
|
||||
<% end -%>
|
||||
<%= link_to "Cancel", @return_path %>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
if @saved
|
||||
page[@todo].remove
|
||||
page.show "empty_"+@original_item_due_id if @old_due_empty
|
||||
if @todo.completed?
|
||||
|
||||
# completed todos move from their context to the completed container
|
||||
|
|
@ -23,10 +24,12 @@ if @saved
|
|||
page.insert_html :bottom, item_container_id(@new_recurring_todo), :partial => 'todos/todo', :locals => { :todo => @new_recurring_todo, :parent_container_type => parent_container_type }
|
||||
page.visual_effect :highlight, dom_id(@new_recurring_todo, 'line'), {'startcolor' => "'#99ff99'"}
|
||||
else
|
||||
page.notify :notice, "There is no next action after the recurring action you just finished. The recurrence is completed", 6.0 if @new_recurring_todo.nil?
|
||||
if @todo.recurring_todo.todos.active.count == 0
|
||||
page.notify :notice, "There is no next action after the recurring action you just finished. The recurrence is completed", 6.0 if @new_recurring_todo.nil?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
# todo is activated from completed container
|
||||
page.call "todoItems.ensureVisibleWithEffectAppear", item_container_id(@todo)
|
||||
|
|
|
|||
|
|
@ -6,19 +6,28 @@ if @saved
|
|||
status_message = 'Added new context / ' + status_message if @new_context_created
|
||||
page.notify :notice, status_message, 5.0
|
||||
|
||||
# #update auto completer arrays for context and project
|
||||
# update auto completer arrays for context and project
|
||||
page << "contextAutoCompleter.options.array = #{context_names_for_autocomplete}; contextAutoCompleter.changed = true" if @new_context_created
|
||||
page << "projectAutoCompleter.options.array = #{project_names_for_autocomplete}; projectAutoCompleter.changed = true" if @new_project_created
|
||||
|
||||
if source_view_is_one_of(:todo, :context, :tag)
|
||||
if @context_changed || @todo.deferred?
|
||||
page[@todo].remove
|
||||
|
||||
if (@remaining_in_context == 0)
|
||||
# remove context container from page if empty
|
||||
source_view do |from|
|
||||
from.todo { page.visual_effect :fade, "c#{@original_item_context_id}", :duration => 0.4 }
|
||||
from.tag { page.visual_effect :fade, "c#{@original_item_context_id}", :duration => 0.4 }
|
||||
from.context { page.show "c#{@original_item_context_id}empty-nd" }
|
||||
if @context_changed
|
||||
source_view do |from|
|
||||
from.todo { page.visual_effect :fade, "c#{@original_item_context_id}", :duration => 0.4 }
|
||||
from.tag { page.visual_effect :fade, "c#{@original_item_context_id}", :duration => 0.4 }
|
||||
from.context { page.show "c#{@original_item_context_id}empty-nd" }
|
||||
end
|
||||
else
|
||||
source_view do |from|
|
||||
from.todo { page.visual_effect :fade, item_container_id(@todo), :duration => 0.4 }
|
||||
from.tag { page.visual_effect :fade, item_container_id(@todo), :duration => 0.4 }
|
||||
from.context { page.show "c#{@original_item_context_id}empty-nd" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -92,6 +101,23 @@ if @saved
|
|||
elsif source_view_is :stats
|
||||
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||
elsif source_view_is :calendar
|
||||
if @due_date_changed
|
||||
page[@todo].remove
|
||||
page.show "empty_"+@original_item_due_id if @old_due_empty
|
||||
page.hide "empty_"+@new_due_id
|
||||
page.insert_html :bottom, @new_due_id, :partial => 'todos/todo'
|
||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||
else
|
||||
if @todo.due.nil?
|
||||
# due date removed
|
||||
page[@todo].remove
|
||||
page.show "empty_"+@original_item_due_id if @old_due_empty
|
||||
else
|
||||
page.replace dom_id(@todo), :partial => 'todos/todo', :locals => { :parent_container_type => parent_container_type }
|
||||
page.visual_effect :highlight, dom_id(@todo), :duration => 3
|
||||
end
|
||||
end
|
||||
else
|
||||
logger.error "unexpected source_view '#{params[:_source_view]}'"
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue