Merge branch 'master' of git://github.com/bsag/tracks

This commit is contained in:
Reinier Balt 2009-02-05 21:13:55 +01:00
commit f69a8e361c
38 changed files with 397 additions and 292 deletions

2
.gitignore vendored
View file

@ -2,8 +2,8 @@
.dotest
/.emacs-project
config/database.yml
config/site.yml
config/deploy.rb
config/environment.rb
db/*.sqlite3
db/data.yml
db/schema.rb

View file

@ -20,7 +20,7 @@ class CannotAccessContext < RuntimeError; end
class ApplicationController < ActionController::Base
protect_from_forgery :secret => SALT
protect_from_forgery :secret => SITE_CONFIG['salt']
helper :application
include LoginSystem

View file

@ -138,7 +138,7 @@ class ContextsController < ApplicationController
@active_contexts = @contexts.active
@hidden_contexts = @contexts.hidden
@down_count = @active_contexts.size + @hidden_contexts.size
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
render :action => 'index_mobile'
end
end
@ -148,7 +148,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]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
@mobile_from_context = @context.id
render :action => 'mobile_show_context'
end

View file

@ -21,10 +21,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, :secure => TRACKS_COOKIES_SECURE }
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] }
unless should_expire_sessions?
@user.remember_me
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] }
end
redirect_back_or_home
return
@ -100,10 +100,10 @@ class LoginController < ApplicationController
session['user_id'] = @user.id
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, :secure => TRACKS_COOKIES_SECURE }
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] }
unless should_expire_sessions?
@user.remember_me
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] }
end
redirect_back_or_home
else

View file

@ -106,9 +106,9 @@ class ProjectsController < ApplicationController
def update
params['project'] ||= {}
if params['project']['state']
@state_changed = @project.state != params['project']['state']
@new_state = params['project']['state']
@state_changed = @project.state != @new_state
logger.info "@state_changed: #{@project.state} == #{params['project']['state']} != #{@state_changed}"
@project.transition_to(params['project']['state'])
params['project'].delete('state')
end
success_text = if params['field'] == 'name' && params['value']
@ -117,6 +117,7 @@ class ProjectsController < ApplicationController
end
@project.attributes = params['project']
if @project.save
@project.transition_to(@new_state) if @state_changed
if boolean_param('wants_render')
if (@project.hidden?)
@project_project_hidden_todo_counts = Hash.new
@ -217,7 +218,7 @@ class ProjectsController < ApplicationController
@hidden_projects = @projects.hidden
@completed_projects = @projects.completed
@down_count = @active_projects.size + @hidden_projects.size + @completed_projects.size
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
render :action => 'index_mobile'
end
end
@ -230,7 +231,7 @@ class ProjectsController < ApplicationController
@project_default_context = "The default context for this project is "+
@project.default_context.name
end
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
@mobile_from_project = @project.id
render :action => 'project_mobile'
end

View file

@ -11,7 +11,13 @@ class SearchController < ApplicationController
@found_notes = current_user.notes.find(:all, :conditions => ["body LIKE ?", terms])
@found_contexts = current_user.contexts.find(:all, :conditions => ["name LIKE ?", terms])
# TODO: limit search to tags on todos
@found_tags = current_user.tags.find(:all, :conditions => ["name LIKE ?", terms])
@found_tags = Tagging.find_by_sql([
"SELECT DISTINCT tags.name as name "+
"FROM tags "+
"LEFT JOIN taggings ON tags.id = taggings.tag_id "+
"LEFT JOIN todos ON taggings.taggable_id = todos.id "+
"WHERE todos.user_id=? "+
"AND tags.name LIKE ? ", current_user.id, terms])
@count = @found_todos.size + @found_projects.size + @found_notes.size + @found_contexts.size + @found_tags.size

View file

@ -652,7 +652,7 @@ class StatsController < ApplicationController
"AND tags.id = taggings.tag_id " +
"AND taggings.taggable_id = todos.id ", current_user.id])
tags_ids_s = tag_ids.map(&:id).sort.join(",")
return {} if tags_ids_s.blank? # return empty array for .size to work
return {} if tags_ids_s.blank? # return empty hash for .size to work
return Tag.find(:all, :conditions => "id in (" + tags_ids_s + ")")
end

View file

@ -65,6 +65,7 @@ class TodosController < ApplicationController
@todo.context_id = context.id
end
@todo.update_state_from_project
@saved = @todo.save
unless (@saved == false) || p.tag_list.blank?
@todo.tag_with(p.tag_list)
@ -231,7 +232,9 @@ class TodosController < ApplicationController
@todo.activate!
end
@saved = @todo.update_attributes params["todo"]
@todo.attributes = params["todo"]
@saved = @todo.save
@context_changed = @original_item_context_id != @todo.context_id
@todo_was_activated_from_deferred_state = @original_item_was_deferred && @todo.active?
@ -255,7 +258,11 @@ class TodosController < ApplicationController
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
if (@project_changed && !@original_item_project_id.nil?) then
@todo.update_state_from_project
@todo.save!
@remaining_undone_in_project = current_user.projects.find(@original_item_project_id).not_done_todo_count
end
determine_down_count
respond_to do |format|
format.js
@ -263,7 +270,7 @@ class TodosController < ApplicationController
format.m do
if @saved
if cookies[:mobile_url]
cookies[:mobile_url] = {:value => nil, :secure => TRACKS_COOKIES_SECURE}
cookies[:mobile_url] = {:value => nil, :secure => SITE_CONFIG['secure_cookies']}
redirect_to cookies[:mobile_url]
else
redirect_to formatted_todos_path(:m)
@ -416,7 +423,7 @@ class TodosController < ApplicationController
@default_project_context_name_map = build_default_project_context_name_map(@projects).to_json
}
format.m {
cookies[:mobile_url]= {:value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
render :action => "mobile_tag"
}
end
@ -714,7 +721,7 @@ class TodosController < ApplicationController
lambda do
@page_title = "All actions"
@home = true
cookies[:mobile_url]= { :value => request.request_uri, :secure => TRACKS_COOKIES_SECURE}
cookies[:mobile_url]= { :value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
determine_down_count
render :action => 'index'

View file

@ -24,7 +24,7 @@ module ApplicationHelper
end
def days_from_today(date)
date.to_date - user_time.to_date
date.in_time_zone.to_date - user_time.to_date
end
# Check due date in comparison to today's date Flag up date appropriately with

View file

@ -1,176 +1,184 @@
class Todo < ActiveRecord::Base
belongs_to :context
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'
# when entering active state, also remove completed_at date. Looks like :exit
# of state completed is not run, see #679
state :active, :enter => Proc.new { |t| t[:show_from], t.completed_at = nil, nil }
state :project_hidden
state :completed, :enter => Proc.new { |t| t.completed_at = Time.zone.now }, :exit => Proc.new { |t| t.completed_at = nil }
state :deferred
event :defer do
transitions :to => :deferred, :from => [:active]
end
event :complete do
transitions :to => :completed, :from => [:active, :project_hidden, :deferred]
end
event :activate do
transitions :to => :active, :from => [:project_hidden, :completed, :deferred]
end
event :hide do
transitions :to => :project_hidden, :from => [:active, :deferred]
end
event :unhide do
transitions :to => :deferred, :from => [:project_hidden], :guard => Proc.new{|t| !t.show_from.blank? }
transitions :to => :active, :from => [:project_hidden]
end
attr_protected :user
# Description field can't be empty, and must be < 100 bytes Notes must be <
# 60,000 bytes (65,000 actually, but I'm being cautious)
validates_presence_of :description
validates_length_of :description, :maximum => 100
validates_length_of :notes, :maximum => 60000, :allow_nil => true
validates_presence_of :show_from, :if => :deferred?
validates_presence_of :context
def validate
if !show_from.blank? && show_from < user.date
errors.add("show_from", "must be a date in the future")
end
end
def toggle_completion!
saved = false
if completed?
saved = activate!
else
saved = complete!
end
return saved
end
def show_from
self[:show_from]
end
def show_from=(date)
# parse Date objects into the proper timezone
date = user.at_midnight(date) if (date.is_a? Date)
activate! if deferred? && date.blank?
defer! if active? && !date.blank? && date > user.date
self[:show_from] = date
end
alias_method :original_project, :project
def project
original_project.nil? ? Project.null_object : original_project
end
alias_method :original_set_initial_state, :set_initial_state
def set_initial_state
if show_from && (show_from > user.date)
write_attribute self.class.state_column, 'deferred'
else
original_set_initial_state
end
end
alias_method :original_run_initial_state_actions, :run_initial_state_actions
def run_initial_state_actions
# only run the initial state actions if the standard initial state hasn't
# been changed
if self.class.initial_state.to_sym == current_state
original_run_initial_state_actions
end
end
def self.feed_options(user)
{
:title => 'Tracks Actions',
:description => "Actions for #{user.display_name}"
}
end
def starred?
tags.any? {|tag| tag.name == STARRED_TAG_NAME}
end
def toggle_star!
if starred?
_remove_tags STARRED_TAG_NAME
tags.reload
else
_add_tags(STARRED_TAG_NAME)
tags.reload
end
starred?
end
def from_recurring_todo?
return self.recurring_todo_id != nil
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
class Todo < ActiveRecord::Base
belongs_to :context
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'
# when entering active state, also remove completed_at date. Looks like :exit
# of state completed is not run, see #679
state :active, :enter => Proc.new { |t| t[:show_from], t.completed_at = nil, nil }
state :project_hidden
state :completed, :enter => Proc.new { |t| t.completed_at = Time.zone.now }, :exit => Proc.new { |t| t.completed_at = nil }
state :deferred
event :defer do
transitions :to => :deferred, :from => [:active]
end
event :complete do
transitions :to => :completed, :from => [:active, :project_hidden, :deferred]
end
event :activate do
transitions :to => :active, :from => [:project_hidden, :completed, :deferred]
end
event :hide do
transitions :to => :project_hidden, :from => [:active, :deferred]
end
event :unhide do
transitions :to => :deferred, :from => [:project_hidden], :guard => Proc.new{|t| !t.show_from.blank? }
transitions :to => :active, :from => [:project_hidden]
end
attr_protected :user
# Description field can't be empty, and must be < 100 bytes Notes must be <
# 60,000 bytes (65,000 actually, but I'm being cautious)
validates_presence_of :description
validates_length_of :description, :maximum => 100
validates_length_of :notes, :maximum => 60000, :allow_nil => true
validates_presence_of :show_from, :if => :deferred?
validates_presence_of :context
def validate
if !show_from.blank? && show_from < user.date
errors.add("show_from", "must be a date in the future")
end
end
def update_state_from_project
if state == 'project_hidden' and !project.hidden?
self.state = 'active'
elsif state == 'active' and project.hidden?
self.state = 'project_hidden'
end
end
def toggle_completion!
saved = false
if completed?
saved = activate!
else
saved = complete!
end
return saved
end
def show_from
self[:show_from]
end
def show_from=(date)
# parse Date objects into the proper timezone
date = user.at_midnight(date) if (date.is_a? Date)
activate! if deferred? && date.blank?
defer! if active? && !date.blank? && date > user.date
self[:show_from] = date
end
alias_method :original_project, :project
def project
original_project.nil? ? Project.null_object : original_project
end
alias_method :original_set_initial_state, :set_initial_state
def set_initial_state
if show_from && (show_from > user.date)
write_attribute self.class.state_column, 'deferred'
else
original_set_initial_state
end
end
alias_method :original_run_initial_state_actions, :run_initial_state_actions
def run_initial_state_actions
# only run the initial state actions if the standard initial state hasn't
# been changed
if self.class.initial_state.to_sym == current_state
original_run_initial_state_actions
end
end
def self.feed_options(user)
{
:title => 'Tracks Actions',
:description => "Actions for #{user.display_name}"
}
end
def starred?
tags.any? {|tag| tag.name == STARRED_TAG_NAME}
end
def toggle_star!
if starred?
_remove_tags STARRED_TAG_NAME
tags.reload
else
_add_tags(STARRED_TAG_NAME)
tags.reload
end
starred?
end
def from_recurring_todo?
return self.recurring_todo_id != nil
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

View file

@ -8,5 +8,5 @@
<div id="input_box">
<%= render :partial => "shared/add_new_item_form" %>
<%= render :template => "sidebar/sidebar" %>
</div><!-- End of input box -->
<%= render :file => "sidebar/sidebar.html.erb" %>
</div><!-- End of input box -->

View file

@ -57,18 +57,18 @@
<li>
<%= rss_formatted_link({:controller => 'todos', :action => 'index', :tag => 'starred'}) %>
<%= text_formatted_link({:controller => 'todos', :action => 'index', :tag => 'starred'}) %>
All starred, active actions
All starred, active actions
</li>
<li>
<%= text_formatted_link({:controller => 'projects', :action => 'index', :projects_and_actions => true}) %>
Active projects with their actions
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:
<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) unless @active_projects.empty?-%>
<%= options_from_collection_for_select(@hidden_contexts, "id", "name") -%>
@ -78,7 +78,7 @@
:url => { :controller => "feedlist", :action => "get_feeds_for_context" },
:before => "$('feeds-for-context').startWaiting()",
:complete => "$('feeds-for-context').stopWaiting()"
-%>
-%>
</li>
<li>Step 2 - Select the feed for this context
<div id="feedicons-context">
@ -95,7 +95,7 @@
<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:
<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) unless @active_projects.empty?-%>
<%= options_from_collection_for_select(@hidden_projects, "id", "name") -%>
@ -106,7 +106,7 @@
:url => { :controller => "feedlist", :action => "get_feeds_for_project" },
:before => "$('feeds-for-project').startWaiting()",
:complete => "$('feeds-for-project').stopWaiting()"
-%>
-%>
</li>
<li>Step 2 - Select the feed for this project
<div id="feedicons-project">
@ -123,7 +123,7 @@
</div><!-- End of display_box -->
<div id="input_box">
<%= render :template => "sidebar/sidebar" %>
<%= render :file => "sidebar/sidebar.html.erb" %>
</div><!-- End of input box -->
<script type="text/javascript">

View file

@ -1,5 +1,5 @@
<h1>REST API Documentation for Developers</h1>
<h2>Introduction</h2>
<p>Tracks is designed to be integrated with scripts, web services, and third-party applications. This page serves as the documentation of our REST API.</p>
@ -86,7 +86,7 @@ Location: <%= home_url %>projects/65.xml
<pre>
<code>
$ curl -u username:p4ssw0rd -H "Content-Type: text/xml" \
-d "&lt;todo&gt;&lt;description&gt;Model treehouse in SketchUp&lt;/description&gt;&lt;context_id&gt;2&lt;/context_id&gt;&lt;project_id&gt;65&lt;project_id&gt;" \
-d "&lt;todo&gt;&lt;description&gt;Model treehouse in SketchUp&lt;/description&gt;&lt;context_id&gt;2&lt;/context_id&gt;&lt;project_id&gt;65&lt;/project_id&gt;&lt;/todo&gt;" \
<%= home_url %>todos.xml -i
&gt;&gt; HTTP/1.1 201 Created
Location: <%= home_url %>todos/452.xml

View file

@ -14,7 +14,7 @@
<div class="add_note_link"><%= link_to_function( "Add a note", "Element.toggle('new-note'); Form.focusFirstElement('form-new-note');", :id=>"add_note_href") %></div>
<h2>Notes</h2>
<div id="empty-n" style="display:<%= @project.notes.empty? ? 'block' : 'none'%>;">
<%= render :partial => "shared/empty",
<%= render :partial => "shared/empty",
:locals => { :message => "Currently there are no notes attached to this project"} %>
</div>
<%= render :partial => "notes/notes_summary", :collection => @project.notes %>
@ -56,7 +56,7 @@
<div id="default_context">
<h2>Default Context</h2>
<div>
<% form_remote_tag( :url => project_path(@project), :method => :put,
<% form_remote_tag( :url => project_path(@project), :method => :put,
:html=> { :id => 'set-default-context-action',
:name => 'default_context',
:class => 'inline-form' }) do -%>
@ -74,5 +74,5 @@
<div id="input_box">
<%= render :partial => "shared/add_new_item_form" %>
<%= render :template => "sidebar/sidebar" %>
<%= render :file => "sidebar/sidebar.html.erb" %>
</div><!-- End of input box -->

View file

@ -1,15 +1,15 @@
<div id="display_box">
<div id="tickler-empty-nd" style="display:<%= (@count == 0) ? 'block' : 'none'%>;">
<div class="message"><p>Currently there are no deferred actions.</p></div>
</div>
<%= render :partial => "contexts/context", :collection => @contexts,
<%= render :partial => "contexts/context", :collection => @contexts,
:locals => { :collapsible => true } %>
</div><!-- End of display_box -->
<div id="input_box">
<%= render :partial => "shared/add_new_item_form" %>
<%= render :template => "sidebar/sidebar" %>
</div><!-- End of input box -->
<%= render :file => "sidebar/sidebar.html.erb" %>
</div><!-- End of input box -->

View file

@ -3,25 +3,25 @@
<h2>No actions found</h2>
<div class="message"><p>Currently there are no incomplete actions with the tag '<%= @tag_name %>'</p></div>
</div>
<%= render :partial => "contexts/context", :collection => @contexts_to_show,
<%= render :partial => "contexts/context", :collection => @contexts_to_show,
:locals => { :collapsible => true } %>
<% unless @deferred.nil? -%>
<%= render :partial => "todos/deferred", :locals => { :deferred => @deferred, :collapsible => true, :append_descriptor => "tagged with &lsquo;#{@tag_name}&rsquo;" } %>
<% end -%>
<% unless @hidden_todos.nil? -%>
<%= render :partial => "todos/hidden", :locals => { :hidden => @hidden_todos, :collapsible => true, :append_descriptor => "tagged with &lsquo;#{@tag_name}&rsquo;" } %>
<% end -%>
<% unless @done.nil? -%>
<%= render :partial => "todos/completed",
<%= render :partial => "todos/completed",
:locals => { :done => @done, :collapsible => true, :append_descriptor => "tagged with &lsquo;#{@tag_name}&rsquo;" } %>
<% end -%>
</div><!-- End of display_box -->
<div id="input_box">
<%= render :partial => "shared/add_new_item_form" %>
<%= render :template => "sidebar/sidebar" %>
</div><!-- End of input box -->
<%= render :file => "sidebar/sidebar.html.erb" %>
</div><!-- End of input box -->

View file

@ -7,18 +7,13 @@
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
# This is the 'salt' to add to the password before it is encrypted
# You need to change this to something unique for yourself
SALT = "change-me"
require 'yaml'
SITE_CONFIG = YAML.load_file(File.join(File.dirname(__FILE__), 'site.yml'))
class Rails::Configuration
attr_accessor :action_web_service
end
# Leave this alone or set it to one or more of ['database', 'ldap', 'open_id'].
# If you choose ldap, see the additional configuration options further down.
AUTHENTICATION_SCHEMES = ['database']
Rails::Initializer.run do |config|
# Skip frameworks you're not going to use
# config.frameworks -= [ :action_web_service, :action_mailer ]
@ -29,23 +24,18 @@ Rails::Initializer.run do |config|
config.gem "highline"
config.action_controller.use_accept_header = true
# Add additional load paths for your own custom dirs
# config.load_paths += %W( #{RAILS_ROOT}/app/services )
# Force all environments to use the same logger level
# (by default production uses :info, the others :debug)
# config.log_level = :debug
# Use the database for sessions instead of the file system
# (create the session table with 'rake create_sessions_table')
config.action_controller.session_store = :active_record_store
config.action_controller.session = {
:session_key => '_tracks_session_id',
:secret => SALT * (30.0 / SALT.length).ceil #must be at least 30 characters
:secret => SITE_CONFIG['salt'] * (30.0 / SITE_CONFIG['salt'].length).ceil #must be at least 30 characters
}
config.action_controller.relative_url_root = SITE_CONFIG['subdir'] if SITE_CONFIG['subdir']
# Enable page/fragment caching by setting a file-based store
# (remember to create the caching directory and make it readable to the application)
# config.action_controller.fragment_cache_store = :file_store, "#{RAILS_ROOT}/cache"
@ -55,11 +45,11 @@ Rails::Initializer.run do |config|
# Make Active Record use UTC-base instead of local time
config.active_record.default_timezone = :utc
# You''ll probably want to change this to the time zone of the computer where Tracks is running
# run rake time:zones:local have Rails suggest time zone names on your system
config.time_zone = 'UTC'
config.time_zone = SITE_CONFIG['time_zone']
# Use Active Record's schema dumper instead of SQL when creating the test database
# (enables use of different database adapters for development and test environments)
config.active_record.schema_format = :ruby
@ -67,7 +57,7 @@ Rails::Initializer.run do |config|
# See Rails::Configuration for more options
end
# Add new inflection rules using the following format
# Add new inflection rules using the following format
# (all these examples are active by default):
# Inflector.inflections do |inflect|
# inflect.plural /^(ox)$/i, '\1en'
@ -86,26 +76,17 @@ require 'tagging_extensions' # Needed for tagging-specific extensions
require 'digest/sha1' #Needed to support 'rake db:fixtures:load' on some ruby installs: http://dev.rousette.org.uk/ticket/557
require 'prototype_helper_extensions'
if (AUTHENTICATION_SCHEMES.include? 'ldap')
if ( SITE_CONFIG['authentication_schemes'].include? 'ldap')
require 'net/ldap' #requires ruby-net-ldap gem be installed
require 'simple_ldap_authenticator'
SimpleLdapAuthenticator.ldap_library = 'net/ldap'
SimpleLdapAuthenticator.servers = %w'localhost'
SimpleLdapAuthenticator.use_ssl = false
SimpleLdapAuthenticator.login_format = 'cn=%s,dc=example,dc=com'
ldap = SITE_CONFIG['ldap']
SimpleLdapAuthenticator.ldap_library = ldap['library']
SimpleLdapAuthenticator.servers = ldap['servers']
SimpleLdapAuthenticator.use_ssl = ldap['ssl']
SimpleLdapAuthenticator.login_format = ldap['login_format']
end
if (AUTHENTICATION_SCHEMES.include? 'open_id')
if ( SITE_CONFIG['authentication_schemes'].include? 'open_id')
#requires ruby-openid gem to be installed
end
# setting this to true will make the cookies only available over HTTPS
TRACKS_COOKIES_SECURE = false
tracks_version='1.7'
# comment out next two lines if you do not want (or can not) the date of the
# last git commit in the footer
# info=`git log --pretty=format:"%ai" -1`
# tracks_version=tracks_version + ' ('+info+')'
TRACKS_VERSION=tracks_version
TRACKS_VERSION='1.7RC2'

View file

@ -19,12 +19,26 @@ config.action_mailer.delivery_method = :test
# Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false
# We store more than 4K of data in the session during some tests.
# Override the hard-coded cookie session store to use a memory store for tests.
# See http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/5519ca7fd4dde3c1
class ActionController::RackRequest
DEFAULT_SESSION_OPTIONS = {
:database_manager => CGI::Session::MemoryStore, # store data in memory
:prefix => "ruby_sess.", # prefix session file names
:session_path => "/", # available to all paths in app
:session_key => "_session_id",
:cookie_only => false,
:session_http_only=> true
}
end
# Overwrite the default settings for fixtures in tests. See Fixtures
# for more details about these settings.
# config.transactional_fixtures = true
# config.instantiated_fixtures = false
# config.pre_loaded_fixtures = false
SALT = "change-me" unless defined?( SALT ).nil?
SITE_CONFIG['salt'] ||= 'change-me'
config.time_zone = 'UTC'

35
config/site.yml.tmpl Normal file
View file

@ -0,0 +1,35 @@
# This is the 'salt' to add to the password before it is encrypted
# You need to change this to something unique for yourself
salt: "change-me"
# Uncomment ldap or open_id if you want to use those authentication schemes.
# If you choose ldap, see the additional configuration options further down.
authentication_schemes:
- "database"
# - "ldap"
# - "open_id"
# You''ll probably want to change this to the time zone of the computer where Tracks is running
# run rake time:zones:local have Rails suggest time zone names on your system
time_zone: "UTC"
# setting this to true will make the cookies only available over HTTPS
secure_cookies: false
# Set this to the subdirectory you're hosting tracks in and uncomment if applicable
# NOTE: you will also need to set up your web server to deal with the relative
# URL. Mongrel, for example, has a --prefix option.
# subdir: "/tracks"
# Only needed if ldap is included in authentication_schemes
# ldap:
# library: 'net/ldap'
# servers:
# - 'localhost'
# use_ssl: false
# login_format: 'cn=%s,dc=example,dc=com'

View file

@ -0,0 +1,18 @@
class FixIncorrectlyHiddenTodos < ActiveRecord::Migration
def self.up
hidden_todos_without_project =
Todo.find(:all, :conditions => "state='project_hidden' AND project_id IS NULL")
active_projects = Project.find(:all, :conditions => "state='active'")
hidden_todos_in_active_projects =
Todo.find(:all, :conditions => ["state='project_hidden' AND project_id IN (?)", active_projects])
todos_to_fix = hidden_todos_without_project + hidden_todos_in_active_projects
todos_to_fix.each do |todo|
todo.update_attribute :state, 'active'
end
end
def self.down
end
end

View file

@ -8,11 +8,11 @@
* Mailing list: http://lists.rousette.org.uk/mailman/listinfo/tracks-discuss
* Original developer: bsag (http://www.rousette.org.uk/)
* Contributors: http://getontracks.org/wiki/Tracks/Contributing/Contributors
* Version: 1.7
* Version: 1.7RC2
* Copyright: (cc) 2004-2008 rousette.org.uk.
* License: GNU GPL
== Version 1.7RC
== Version 1.7RC2
New features:
1. Recurring todos
@ -25,9 +25,11 @@ New features:
8. Support for OpenSearch. This means you can add a Tracks as a search provider in your webbrowser (tested on FF3 and IE7)
Under the hood:
1. Started to move selenium tests to RSpec
2. Upgrade to Rails 2.2.2
2. Bugfixes, including fixing OpenID
1. We now allow users again to stay logged in on two devices at the same time
2. Started to move selenium tests to RSpec
3. Upgrade to Rails 2.2.2
4. Move site specific configuration out of environment.rb for easier updating
5. Bugfixes, including fixing OpenID
== Version 1.6
1. upgrade to rails 2.0.2

View file

@ -1,6 +1,6 @@
1. Wiki
There are some pointers for setting up your Tracks copy for testing at http://www.rousette.org.uk/projects/wiki/Testing/
There are some pointers for setting up your Tracks copy for testing at http://www.getontracks.org/wiki/Testing/
2. SQLITE3 FOR TESTING

@ -1 +1 @@
Subproject commit d3746adc39333f056f14969d40aa1f0072eb4eed
Subproject commit b2fcae5326ff89f2ab5a5a8e5d55b41fd2787e51

View file

@ -48,7 +48,7 @@ module LoginSystem
session['user_id'] = user.id
set_current_user(user)
current_user.remember_me
cookies[:auth_token] = { :value => current_user.remember_token , :expires => current_user.remember_token_expires_at, :secure => TRACKS_COOKIES_SECURE }
cookies[:auth_token] = { :value => current_user.remember_token , :expires => current_user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] }
flash[:notice] = "Logged in successfully. Welcome back!"
end
end

View file

@ -1,20 +1,15 @@
module Tracks
class Config
def self.salt
SALT
SITE_CONFIG['salt']
end
def self.auth_schemes
AUTHENTICATION_SCHEMES
SITE_CONFIG['authentication_schemes'] || []
end
def self.openid_enabled?
auth_schemes.include?('open_id')
end
end
end

View file

@ -127,6 +127,31 @@ describe Todo do
end
end
describe 'when update_state_from_project is called' do
it "should unhide when project is active" do
project = mock_model(Project, :hidden? => false)
todo = Todo.new(:state => 'project_hidden', :project => project)
todo.should be_project_hidden
todo.update_state_from_project
todo.should be_active
end
it "should unhide when project is null" do
todo = Todo.new(:state => 'project_hidden', :project => nil)
todo.should be_project_hidden
todo.update_state_from_project
todo.should be_active
end
it "should hide when project is hidden" do
project = mock_model(Project, :hidden? => true)
todo = Todo.new(:state => 'active', :project => project)
todo.should be_active
todo.update_state_from_project
todo.should be_project_hidden
end
end
it "is deferrable from `active'" do
todo = create_todo
todo.activate!

View file

@ -5,7 +5,7 @@ require 'backend_controller'
class BackendController; def rescue_action(e) raise e end; end
class BackendControllerTest < Test::Rails::TestCase
fixtures :users, :projects, :contexts, :todos, :notes
fixtures :users, :projects, :contexts, :todos, :recurring_todos, :notes
def setup
@controller = BackendController.new

View file

@ -5,7 +5,7 @@ require 'feedlist_controller'
class FeedlistController; def rescue_action(e) raise e end; end
class FeedlistControllerTest < Test::Rails::TestCase
fixtures :users, :preferences, :projects, :contexts, :todos, :notes
fixtures :users, :preferences, :projects, :contexts, :todos, :recurring_todos, :notes
def setup
assert_equal "test", ENV['RAILS_ENV']

View file

@ -5,7 +5,7 @@ require 'integrations_controller'
class IntegrationsController; def rescue_action(e) raise e end; end
class IntegrationsControllerTest < Test::Unit::TestCase
fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings
fixtures :users, :preferences, :projects, :contexts, :todos, :recurring_todos, :tags, :taggings
def setup
@controller = IntegrationsController.new

View file

@ -6,7 +6,7 @@ require 'projects_controller'
class ProjectsController; def rescue_action(e) raise e end; end
class ProjectsControllerTest < TodoContainerControllerTestBase
fixtures :users, :todos, :preferences, :projects, :contexts
fixtures :users, :todos, :preferences, :projects, :contexts, :recurring_todos
def setup
perform_setup(Project, ProjectsController)

View file

@ -5,7 +5,7 @@ require 'stats_controller'
class StatsController; def rescue_action(e) raise e end; end
class StatsControllerTest < Test::Unit::TestCase
fixtures :users, :preferences, :projects, :contexts, :todos, :tags, :taggings
fixtures :users, :preferences, :projects, :contexts, :todos, :recurring_todos, :recurring_todos, :tags, :taggings
def setup
@controller = StatsController.new
@ -99,10 +99,7 @@ class StatsControllerTest < Test::Unit::TestCase
assert_response :success
# clear taggings table and render again
taggings = Tagging.find(:all)
taggings.each do |t|
t.delete
end
Tagging.delete_all
get :index
assert_response :success

View file

@ -94,7 +94,7 @@ class TodosControllerTest < Test::Rails::TestCase
assert_difference Todo, :count do
xml = "<todo><description>Call Warren Buffet to find out how much he makes per day</description><project_id>#{projects(:timemachine).id}</project_id><context_id>#{contexts(:agenda).id}</context_id><show-from type=\"datetime\">#{1.week.from_now.xmlschema}</show-from></todo>"
#p parse_xml_body(xml)
# p parse_xml_body(xml)
post :create, parse_xml_body(xml).update(:format => "xml")
assert_response :created
end
@ -505,4 +505,20 @@ class TodosControllerTest < Test::Rails::TestCase
assert next_todo.due > @todo.due
end
def test_removing_hidden_project_activates_todo
login_as(:admin_user)
# get a project and hide it, todos in the project should be hidden
p = projects(:timemachine)
p.hide!
assert p.reload().hidden?
todo = p.todos.first
assert_equal "project_hidden", todo.state
# clear project from todo: the todo should be unhidden
xhr :post, :update, :id => 5, :_source_view => 'todo', "project_name"=>"None", "todo"=>{}
todo.reload()
assert_equal "active", todo.state
end
end

View file

@ -9,7 +9,7 @@ class ContextsController; def rescue_action(e) raise e end; end
class TodosController; def rescue_action(e) raise e end; end
class FeedSmokeTest < ActionController::IntegrationTest
fixtures :users, :preferences, :projects, :contexts, :todos, :notes
fixtures :users, :preferences, :projects, :contexts, :todos, :recurring_todos, :notes
def setup
assert_test_environment_ok

View file

@ -1,7 +1,7 @@
require "#{File.dirname(__FILE__)}/../test_helper"
class StoriesTest < ActionController::IntegrationTest
fixtures :users, :preferences, :projects, :contexts, :todos, :notes
fixtures :users, :preferences, :projects, :contexts, :todos, :recurring_todos, :notes
def setup
assert_test_environment_ok

View file

@ -1,7 +1,7 @@
require File.dirname(__FILE__) + '/../test_helper'
class ContextTest < Test::Rails::TestCase
fixtures :contexts, :todos, :users, :preferences
fixtures :contexts, :todos, :recurring_todos, :users, :preferences
def setup
@agenda = contexts(:agenda)

View file

@ -1,7 +1,7 @@
require File.dirname(__FILE__) + '/../test_helper'
class ProjectTest < Test::Rails::TestCase
fixtures :projects, :contexts, :todos, :users, :preferences
fixtures :projects, :contexts, :todos, :recurring_todos, :users, :preferences
def setup
@timemachine = projects(:timemachine)

View file

@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/../test_helper'
require 'date'
class TodoTest < Test::Rails::TestCase
fixtures :todos, :users, :contexts, :preferences, :tags, :taggings
fixtures :todos, :recurring_todos, :users, :contexts, :preferences, :tags, :taggings
def setup
@not_completed1 = Todo.find(1).reload

View file

@ -17,7 +17,7 @@ class SimpleLdapAuthenticator
end
class UserTest < Test::Rails::TestCase
fixtures :users, :preferences, :projects, :contexts, :todos
fixtures :users, :preferences, :projects, :contexts, :todos, :recurring_todos
def setup
assert_equal "test", ENV['RAILS_ENV']