mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-03 06:21:49 +01:00
Made TodosController more RESTful and use it to fulfill all feeds, eliminating the need for the FeedController and it's helper and views. Also added an ATOM feed (not linked in the UI anywhere, just substitute .atom for .rss).
I also ran rcov on unit tests and added tests to improve test coverage, uncovering a couple of bugs along the way. git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@476 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
parent
84357b67d5
commit
106d5ee448
38 changed files with 1007 additions and 680 deletions
|
|
@ -13,7 +13,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
helper :application
|
||||
include LoginSystem
|
||||
|
||||
|
||||
layout 'standard'
|
||||
|
||||
before_filter :set_session_expiration
|
||||
|
|
@ -21,6 +21,9 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
after_filter :set_charset
|
||||
|
||||
include ActionView::Helpers::TextHelper
|
||||
helper_method :format_date, :markdown
|
||||
|
||||
# By default, sets the charset to UTF-8 if it isn't already set
|
||||
def set_charset
|
||||
headers["Content-Type"] ||= "text/html; charset=UTF-8"
|
||||
|
|
@ -78,13 +81,35 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def count_undone_todos(todos_parent)
|
||||
if (todos_parent.is_a?(Project) && todos_parent.hidden?)
|
||||
if todos_parent.nil?
|
||||
count = 0
|
||||
elsif (todos_parent.is_a?(Project) && todos_parent.hidden?)
|
||||
count = eval "@project_project_hidden_todo_counts[#{todos_parent.id}]"
|
||||
else
|
||||
count = eval "@#{todos_parent.class.to_s.downcase}_not_done_counts[#{todos_parent.id}]"
|
||||
end
|
||||
count = 0 if count == nil
|
||||
count
|
||||
count || 0
|
||||
end
|
||||
|
||||
# Convert a date object to the format specified
|
||||
# in config/settings.yml
|
||||
#
|
||||
def format_date(date)
|
||||
if date
|
||||
date_format = @user.prefs.date_format
|
||||
formatted_date = date.strftime("#{date_format}")
|
||||
else
|
||||
formatted_date = ''
|
||||
end
|
||||
formatted_date
|
||||
end
|
||||
|
||||
# Uses RedCloth to transform text using either Textile or Markdown
|
||||
# Need to require redcloth above
|
||||
# RedCloth 3.0 or greater is needed to use Markdown, otherwise it only handles Textile
|
||||
#
|
||||
def markdown(text)
|
||||
RedCloth.new(text).to_html
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
|||
|
|
@ -1,133 +0,0 @@
|
|||
# Produces an feeds of the next actions, both RSS and plain text
|
||||
#
|
||||
class FeedController < ApplicationController
|
||||
|
||||
helper :feed
|
||||
layout nil
|
||||
|
||||
session :disabled => true # Prevents session control from interfering with feed
|
||||
|
||||
skip_before_filter :login_required
|
||||
before_filter :check_token_against_user_word
|
||||
before_filter :prepare_for_feed, :only => [:rss, :text, :ical]
|
||||
before_filter :identify_contexts, :only => [:text, :ical]
|
||||
|
||||
# Build an RSS feed
|
||||
def rss
|
||||
headers["Content-Type"] = "text/xml; charset=utf-8"
|
||||
end
|
||||
|
||||
# Builds a plain text page listing incomplete next actions,
|
||||
# grouped by context (contexts are sorted by position, as on the home page).
|
||||
# Showing notes doesn't make much sense here so they are omitted.
|
||||
# Hidden contexts are also hidden in the text view
|
||||
# You can use this with GeekTool to get your next actions
|
||||
# on the desktop:
|
||||
# curl [url from "TXT" link on todo/list]
|
||||
#
|
||||
def text
|
||||
headers["Content-Type"] = "text/plain; charset=utf-8"
|
||||
end
|
||||
|
||||
# Builds an iCal compatible export of incomplete todos
|
||||
# so that each action forms a VTODO in your iCal calendar.
|
||||
# Due dates are supported, and notes are included.
|
||||
#
|
||||
def ical
|
||||
headers["Content-Type"] = "text/calendar"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Check whether the token in the URL matches the word in the User's table
|
||||
def check_token_against_user_word
|
||||
@user = User.find_by_login( params['login'] )
|
||||
unless ( params['token'] == @user.word)
|
||||
render :text => "Sorry, you don't have permission to view this page."
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def identify_contexts
|
||||
if params.key?('context')
|
||||
@contexts = [ @user.contexts.find_by_params(params) ]
|
||||
else
|
||||
@contexts = @user.contexts.find_all_by_hide(false, "position ASC")
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_for_feed
|
||||
condition_builder = FindConditionBuilder.new
|
||||
options = Hash.new
|
||||
|
||||
if params.key?('done')
|
||||
condition_builder.add 'todos.state = ?', 'completed'
|
||||
else
|
||||
condition_builder.add 'todos.state = ?', 'active'
|
||||
end
|
||||
|
||||
if params.key?('limit')
|
||||
options[:limit] = limit = params['limit']
|
||||
@description = limit ? "Lists the last #{limit} incomplete next actions" : "Lists incomplete next actions"
|
||||
end
|
||||
@title = "Tracks - Next Actions"
|
||||
@description = "Filter: "
|
||||
|
||||
if params.key?('due')
|
||||
due_within = params['due'].to_i
|
||||
due_within_when = @user.time + due_within.days
|
||||
condition_builder.add('todos.due <= ?', due_within_when)
|
||||
due_within_date_s = due_within_when.strftime("%Y-%m-%d")
|
||||
@title << " due today" if (due_within == 0)
|
||||
@title << " due within a week" if (due_within == 6)
|
||||
@description << " with a due date #{due_within_date_s} or earlier"
|
||||
end
|
||||
|
||||
if params.key?('done')
|
||||
done_in_last = params['done'].to_i
|
||||
condition_builder.add('todos.completed_at >= ?', @user.time - done_in_last.days)
|
||||
@title << " actions completed"
|
||||
@description << " in the last #{done_in_last.to_s} days"
|
||||
end
|
||||
|
||||
if params.key?('context')
|
||||
context = @user.contexts.find_by_params(params)
|
||||
condition_builder.add('todos.context_id = ?', context.id)
|
||||
@title << " in #{context.name}"
|
||||
@description << " in context '#{context.name}'"
|
||||
end
|
||||
|
||||
if params.key?('project')
|
||||
project = @user.projects.find_by_params(params)
|
||||
condition_builder.add('todos.project_id = ?', project.id)
|
||||
@title << " for #{project.name}"
|
||||
@description << " for project '#{project.name}'"
|
||||
end
|
||||
|
||||
options[:conditions] = condition_builder.to_conditions
|
||||
options[:order] = "todos.due IS NULL, todos.due ASC, todos.created_at ASC"
|
||||
options[:include] = :project
|
||||
|
||||
@todos = @user.todos.find(:all, options )
|
||||
|
||||
end
|
||||
|
||||
class FindConditionBuilder
|
||||
|
||||
def initialize
|
||||
@queries = Array.new
|
||||
@params = Array.new
|
||||
end
|
||||
|
||||
def add(query, param)
|
||||
@queries << query
|
||||
@params << param
|
||||
end
|
||||
|
||||
def to_conditions
|
||||
[@queries.join(' AND ')] + @params
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
@ -138,14 +138,15 @@ class ProjectsController < ApplicationController
|
|||
def render_rss_feed
|
||||
lambda do
|
||||
render_rss_feed_for @projects, :feed => Project.feed_options(@user),
|
||||
:item => { :description => lambda { |p| p.summary(count_undone_todos_phrase(p)) } }
|
||||
:item => { :title => :name, :description => lambda { |p| summary(p) } }
|
||||
end
|
||||
end
|
||||
|
||||
def render_atom_feed
|
||||
lambda do
|
||||
render_atom_feed_for @projects, :feed => Project.feed_options(@user),
|
||||
:item => { :description => lambda { |p| p.summary(count_undone_todos_phrase(p)) },
|
||||
:item => { :description => lambda { |p| summary(p) },
|
||||
:title => :name,
|
||||
:author => lambda { |p| nil } }
|
||||
end
|
||||
end
|
||||
|
|
@ -181,4 +182,13 @@ class ProjectsController < ApplicationController
|
|||
init_data_for_sidebar
|
||||
end
|
||||
|
||||
def summary(project)
|
||||
project_description = ''
|
||||
project_description += sanitize(markdown( project.description )) unless project.description.blank?
|
||||
project_description += "<p>#{count_undone_todos_phrase(p)}. "
|
||||
project_description += "Project is #{project.state}."
|
||||
project_description += "</p>"
|
||||
project_description
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,33 +3,28 @@ class TodosController < ApplicationController
|
|||
helper :todos
|
||||
|
||||
append_before_filter :init, :except => [ :destroy, :completed, :completed_archive, :check_deferred ]
|
||||
skip_before_filter :login_required, :only => [:index]
|
||||
prepend_before_filter :login_or_feed_token_required, :only => [:index]
|
||||
session :off, :only => :index, :if => Proc.new { |req| is_feed_request(req) }
|
||||
|
||||
layout 'standard'
|
||||
|
||||
# Main method for listing tasks
|
||||
# Set page title, and fill variables with contexts and done and not-done tasks
|
||||
# Number of completed actions to show is determined by a setting in settings.yml
|
||||
def index
|
||||
@projects = @user.projects.find(:all, :include => [ :todos ])
|
||||
@contexts = @user.contexts.find(:all, :include => [ :todos ])
|
||||
|
||||
@page_title = "TRACKS::List tasks"
|
||||
|
||||
# If you've set no_completed to zero, the completed items box
|
||||
# isn't shown on the home page
|
||||
max_completed = @user.prefs.show_number_completed
|
||||
@done = @user.completed_todos.find(:all, :limit => max_completed, :include => [ :context, :project, :tags ]) unless max_completed == 0
|
||||
|
||||
|
||||
@contexts_to_show = @contexts.reject {|x| x.hide? }
|
||||
|
||||
# Set count badge to number of not-done, not hidden context items
|
||||
@count = @todos.reject { |x| !x.active? || x.context.hide? }.size
|
||||
|
||||
respond_to do |wants|
|
||||
wants.html
|
||||
wants.xml { render :action => 'list.rxml', :layout => false }
|
||||
respond_to do |format|
|
||||
format.html &render_todos_html
|
||||
format.xml { render :action => 'list.rxml', :layout => false }
|
||||
format.rss &render_rss_feed
|
||||
format.atom &render_atom_feed
|
||||
format.text &render_text_feed
|
||||
format.ics &render_ical_feed
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def create
|
||||
@todo = @user.todos.build
|
||||
p = params['request'] || params
|
||||
|
|
@ -100,7 +95,7 @@ class TodosController < ApplicationController
|
|||
#
|
||||
def toggle_check
|
||||
@todo = check_user_return_todo
|
||||
@todo.toggle_completion()
|
||||
@todo.toggle_completion!
|
||||
@saved = @todo.save
|
||||
respond_to do |format|
|
||||
format.js do
|
||||
|
|
@ -279,13 +274,89 @@ class TodosController < ApplicationController
|
|||
init_data_for_sidebar
|
||||
init_todos
|
||||
end
|
||||
|
||||
def init_todos
|
||||
# Exclude hidden projects from count on home page
|
||||
@todos = @user.todos.find(:all, :conditions => ['todos.state = ? or todos.state = ?', 'active', 'complete'], :include => [ :project, :context, :tags ])
|
||||
|
||||
# Exclude hidden projects from the home page
|
||||
@not_done_todos = @user.todos.find(:all, :conditions => ['todos.state = ?', 'active'], :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC", :include => [ :project, :context, :tags ])
|
||||
def with_feed_query_scope(&block)
|
||||
unless TodosController.is_feed_request(request)
|
||||
yield
|
||||
return
|
||||
end
|
||||
|
||||
condition_builder = FindConditionBuilder.new
|
||||
options = Hash.new
|
||||
|
||||
if params.key?('done')
|
||||
condition_builder.add 'todos.state = ?', 'completed'
|
||||
else
|
||||
condition_builder.add 'todos.state = ?', 'active'
|
||||
end
|
||||
|
||||
@title = "Tracks - Next Actions"
|
||||
@description = "Filter: "
|
||||
|
||||
if params.key?('due')
|
||||
due_within = params['due'].to_i
|
||||
due_within_when = @user.time + due_within.days
|
||||
condition_builder.add('todos.due <= ?', due_within_when)
|
||||
due_within_date_s = due_within_when.strftime("%Y-%m-%d")
|
||||
@title << " due today" if (due_within == 0)
|
||||
@title << " due within a week" if (due_within == 6)
|
||||
@description << " with a due date #{due_within_date_s} or earlier"
|
||||
end
|
||||
|
||||
if params.key?('done')
|
||||
done_in_last = params['done'].to_i
|
||||
condition_builder.add('todos.completed_at >= ?', @user.time - done_in_last.days)
|
||||
@title << " actions completed"
|
||||
@description << " in the last #{done_in_last.to_s} days"
|
||||
end
|
||||
|
||||
Todo.with_scope :find => {:conditions => condition_builder.to_conditions} do
|
||||
yield
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def with_parent_resource_scope(&block)
|
||||
if (params[:context_id])
|
||||
context = @user.contexts.find_by_params(params)
|
||||
Todo.with_scope :find => {:conditions => ['todos.context_id = ?', context.id]} do
|
||||
yield
|
||||
end
|
||||
elsif (params[:project_id])
|
||||
project = @user.projects.find_by_params(params)
|
||||
Todo.with_scope :find => {:conditions => ['todos.project_id = ?', project.id]} do
|
||||
yield
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def with_limit_scope(&block)
|
||||
if params.key?('limit')
|
||||
Todo.with_scope :find => {:limit => params['limit']} do
|
||||
yield
|
||||
end
|
||||
#@description = limit ? "Lists the last #{limit} incomplete next actions" : "Lists incomplete next actions"
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def init_todos
|
||||
with_feed_query_scope do
|
||||
with_parent_resource_scope do
|
||||
with_limit_scope do
|
||||
|
||||
# Exclude hidden projects from count on home page
|
||||
@todos = @user.todos.find(:all, :conditions => ['todos.state = ? or todos.state = ?', 'active', 'complete'], :include => [ :project, :context, :tags ])
|
||||
|
||||
# Exclude hidden projects from the home page
|
||||
@not_done_todos = @user.todos.find(:all, :conditions => ['todos.state = ?', 'active'], :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC", :include => [ :project, :context, :tags ])
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def determine_down_count
|
||||
|
|
@ -323,5 +394,92 @@ class TodosController < ApplicationController
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def render_todos_html
|
||||
lambda do
|
||||
@page_title = "TRACKS::List tasks"
|
||||
|
||||
# If you've set no_completed to zero, the completed items box
|
||||
# isn't shown on the home page
|
||||
max_completed = @user.prefs.show_number_completed
|
||||
@done = @user.completed_todos.find(:all, :limit => max_completed, :include => [ :context, :project, :tags ]) unless max_completed == 0
|
||||
|
||||
# Set count badge to number of not-done, not hidden context items
|
||||
@count = @todos.reject { |x| !x.active? || x.context.hide? }.size
|
||||
|
||||
render
|
||||
end
|
||||
end
|
||||
|
||||
def render_rss_feed
|
||||
lambda do
|
||||
render_rss_feed_for @todos, :feed => Todo.feed_options(@user),
|
||||
:item => {
|
||||
:title => :description,
|
||||
:link => lambda { |t| context_url(t.context) },
|
||||
:description => todo_feed_content
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def todo_feed_content
|
||||
lambda do |i|
|
||||
item_notes = sanitize(markdown( i.notes )) if i.notes?
|
||||
due = "<div>Due: #{format_date(i.due)}</div>\n" if i.due?
|
||||
done = "<div>Completed: #{format_date(i.completed_at)}</div>\n" if i.completed?
|
||||
context_link = "<a href=\"#{ context_url(i.context) }\">#{ i.context.name }</a>"
|
||||
if i.project_id?
|
||||
project_link = "<a href=\"#{ project_url(i.project) }\">#{ i.project.name }</a>"
|
||||
else
|
||||
project_link = "<em>none</em>"
|
||||
end
|
||||
"#{done||''}#{due||''}#{item_notes||''}\n<div>Project: #{project_link}</div>\n<div>Context: #{context_link}</div>"
|
||||
end
|
||||
end
|
||||
|
||||
def render_atom_feed
|
||||
lambda do
|
||||
render_atom_feed_for @todos, :feed => Todo.feed_options(@user),
|
||||
:item => {
|
||||
:title => :description,
|
||||
:link => lambda { |t| context_url(t.context) },
|
||||
:description => todo_feed_content,
|
||||
:author => lambda { |p| nil }
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def render_text_feed
|
||||
lambda do
|
||||
render :action => 'index_text', :layout => false, :content_type => Mime::TEXT
|
||||
end
|
||||
end
|
||||
|
||||
def render_ical_feed
|
||||
lambda do
|
||||
render :action => 'index_ical', :layout => false, :content_type => Mime::ICS
|
||||
end
|
||||
end
|
||||
|
||||
def self.is_feed_request(req)
|
||||
['rss','atom','txt','ics'].include?(req.parameters[:format])
|
||||
end
|
||||
|
||||
class FindConditionBuilder
|
||||
|
||||
def initialize
|
||||
@queries = Array.new
|
||||
@params = Array.new
|
||||
end
|
||||
|
||||
def add(query, param)
|
||||
@queries << query
|
||||
@params << param
|
||||
end
|
||||
|
||||
def to_conditions
|
||||
[@queries.join(' AND ')] + @params
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,32 +1,10 @@
|
|||
# The methods added to this helper will be available to all templates in the application.
|
||||
module ApplicationHelper
|
||||
|
||||
# Convert a date object to the format specified
|
||||
# in config/settings.yml
|
||||
#
|
||||
def format_date(date)
|
||||
if date
|
||||
date_format = @user.prefs.date_format
|
||||
formatted_date = date.strftime("#{date_format}")
|
||||
else
|
||||
formatted_date = ''
|
||||
end
|
||||
formatted_date
|
||||
end
|
||||
|
||||
def user_time
|
||||
@user.time
|
||||
end
|
||||
|
||||
|
||||
# Uses RedCloth to transform text using either Textile or Markdown
|
||||
# Need to require redcloth above
|
||||
# RedCloth 3.0 or greater is needed to use Markdown, otherwise it only handles Textile
|
||||
#
|
||||
def markdown(text)
|
||||
RedCloth.new(text).to_html
|
||||
end
|
||||
|
||||
# Replicates the link_to method but also checks request.request_uri to find
|
||||
# current page. If that matches the url, the link is marked
|
||||
# id = "current"
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
module FeedHelper
|
||||
|
||||
# Build a nicely formatted text string for display
|
||||
# Context forms the heading, then the items are
|
||||
# indented underneath. If there is a due date
|
||||
# and the item is in a project, these are also displayed
|
||||
#
|
||||
def build_text_page(list,context)
|
||||
result_string = ""
|
||||
list.each do |item|
|
||||
if item.context_id == context.id
|
||||
result_string << "\n" + context.name.upcase + ":\n" if result_string.empty?
|
||||
|
||||
if (item.completed?) && item.completed_at
|
||||
result_string << " [Completed: " + format_date(item.completed_at) + "] "
|
||||
end
|
||||
|
||||
if item.due
|
||||
result_string << " [Due: " + format_date(item.due) + "] "
|
||||
result_string << item.description + " "
|
||||
else
|
||||
result_string << " " + item.description + " "
|
||||
end
|
||||
|
||||
if item.project_id
|
||||
result_string << "(" + item.project.name + ")"
|
||||
end
|
||||
result_string << "\n"
|
||||
end
|
||||
end
|
||||
return result_string
|
||||
end
|
||||
|
||||
def format_ical_notes(notes)
|
||||
split_notes = notes.split(/\n/)
|
||||
joined_notes = split_notes.join("\\n")
|
||||
end
|
||||
|
||||
def format_ical_uid(todo)
|
||||
sprintf("%s%s%s%s", request.protocol, request.host, request.port_string, todo_url(todo))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,24 +1,11 @@
|
|||
module FeedlistHelper
|
||||
|
||||
def rss_feed_link(options = {})
|
||||
image_tag = image_tag("feed-icon.png", :size => "16X16", :border => 0, :class => "rss-icon")
|
||||
linkoptions = {:controller => 'feed', :action => 'rss', :login => "#{@user.login}", :token => "#{@user.word}"}
|
||||
linkoptions.merge!(options)
|
||||
link_to(image_tag, linkoptions, :title => "RSS feed")
|
||||
end
|
||||
|
||||
def rss_formatted_link(options = {})
|
||||
image_tag = image_tag("feed-icon.png", :size => "16X16", :border => 0, :class => "rss-icon")
|
||||
linkoptions = { :token => @user.word, :format => 'rss' }
|
||||
linkoptions.merge!(options)
|
||||
link_to(image_tag, linkoptions, :title => "RSS feed")
|
||||
end
|
||||
|
||||
def text_feed_link(options = {})
|
||||
linkoptions = {:controller => 'feed', :action => 'text', :login => "#{@user.login}", :token => "#{@user.word}"}
|
||||
linkoptions.merge!(options)
|
||||
link_to('<span class="feed">TXT</span>', linkoptions, :title => "Plain text feed" )
|
||||
end
|
||||
|
||||
def text_formatted_link(options = {})
|
||||
linkoptions = { :token => @user.word, :format => 'txt' }
|
||||
|
|
@ -26,12 +13,10 @@ module FeedlistHelper
|
|||
link_to('<span class="feed">TXT</span>', linkoptions, :title => "Plain text feed" )
|
||||
end
|
||||
|
||||
|
||||
def ical_feed_link(options = {})
|
||||
linkoptions = {:controller => 'feed', :action => 'ical', :login => "#{@user.login}", :token => "#{@user.word}"}
|
||||
def ical_formatted_link(options = {})
|
||||
linkoptions = { :token => @user.word, :format => 'ics' }
|
||||
linkoptions.merge!(options)
|
||||
link_to('<span class="feed">iCal</span>', linkoptions, :title => "iCal feed")
|
||||
link_to('<span class="feed">iCal</span>', linkoptions, :title => "iCal feed" )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,18 +1,3 @@
|
|||
module LoginHelper
|
||||
|
||||
|
||||
def render_errors(obj)
|
||||
return "" unless obj
|
||||
return "" unless request.post?
|
||||
tag = String.new
|
||||
|
||||
unless obj.valid?
|
||||
tag << %{<ul class="objerrors">}
|
||||
obj.errors.each_full { |message| tag << %{<li>#{message}</li>} }
|
||||
tag << %{</ul>}
|
||||
end
|
||||
tag
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
@ -180,6 +180,11 @@ module TodosHelper
|
|||
return array_or_string_for_javascript(['Create a new context']) if @contexts.empty?
|
||||
array_or_string_for_javascript( @contexts.collect{|c| escape_javascript(c.name) } )
|
||||
end
|
||||
|
||||
def format_ical_notes(notes)
|
||||
split_notes = notes.split(/\n/)
|
||||
joined_notes = split_notes.join("\\n")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class Context < ActiveRecord::Base
|
|||
def self.feed_options(user)
|
||||
{
|
||||
:title => 'Tracks Contexts',
|
||||
:description => "Lists all the contexts for #{user.display_name}."
|
||||
:description => "Lists all the contexts for #{user.display_name}"
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ class Context < ActiveRecord::Base
|
|||
|
||||
def summary(undone_todo_count)
|
||||
s = "<p>#{undone_todo_count}. "
|
||||
s += "Context is #{hidden? ? 'Hidden' : 'Active'}. "
|
||||
s += "Context is #{hidden? ? 'Hidden' : 'Active'}."
|
||||
s += "</p>"
|
||||
s
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,50 +40,34 @@ class Project < ActiveRecord::Base
|
|||
def self.feed_options(user)
|
||||
{
|
||||
:title => 'Tracks Projects',
|
||||
:description => "Lists all the projects for #{user.display_name}."
|
||||
:description => "Lists all the projects for #{user.display_name}"
|
||||
}
|
||||
end
|
||||
|
||||
def to_param
|
||||
url_friendly_name
|
||||
end
|
||||
|
||||
def description_present?
|
||||
attribute_present?("description")
|
||||
end
|
||||
|
||||
def linkurl_present?
|
||||
attribute_present?("linkurl")
|
||||
end
|
||||
|
||||
def title
|
||||
name
|
||||
end
|
||||
|
||||
def summary(undone_todo_count)
|
||||
project_description = ''
|
||||
project_description += sanitize(markdown( description )) if description_present?
|
||||
project_description += "<p>#{undone_todo_count}. "
|
||||
project_description += "Project is #{state}. "
|
||||
project_description += "<a href=\"#{linkurl}\">#{linkurl}</a>" if linkurl_present?
|
||||
project_description += "</p>"
|
||||
project_description
|
||||
end
|
||||
|
||||
def hide_todos
|
||||
todos.each do |t|
|
||||
t.hide! unless t.completed? || t.deferred?
|
||||
t.save
|
||||
unless t.completed? || t.deferred?
|
||||
t.hide!
|
||||
t.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unhide_todos
|
||||
todos.each do |t|
|
||||
t.unhide! if t.project_hidden?
|
||||
t.save
|
||||
if t.project_hidden?
|
||||
t.unhide!
|
||||
t.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# would prefer to call this method state=(), but that causes an endless loop
|
||||
# as a result of acts_as_state_machine calling state=() to update the attribute
|
||||
def transition_to(candidate_state)
|
||||
case candidate_state.to_sym
|
||||
when current_state
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
class Todo < ActiveRecord::Base
|
||||
require 'validations'
|
||||
|
||||
belongs_to :context, :order => 'name'
|
||||
belongs_to :project
|
||||
|
|
@ -45,12 +44,12 @@ class Todo < ActiveRecord::Base
|
|||
validates_presence_of :context
|
||||
|
||||
def validate
|
||||
if deferred? && !show_from.blank? && show_from < user.date
|
||||
errors.add("Show From", "must be a date in the future.")
|
||||
if !show_from.blank? && show_from < user.date
|
||||
errors.add("show_from", "must be a date in the future")
|
||||
end
|
||||
end
|
||||
|
||||
def toggle_completion
|
||||
def toggle_completion!
|
||||
if completed?
|
||||
activate!
|
||||
else
|
||||
|
|
@ -78,11 +77,18 @@ class Todo < ActiveRecord::Base
|
|||
alias_method :original_set_initial_state, :set_initial_state
|
||||
|
||||
def set_initial_state
|
||||
if show_from && (show_from > Time.now.utc.to_date)
|
||||
if show_from && (show_from > user.date)
|
||||
write_attribute self.class.state_column, 'deferred'
|
||||
else
|
||||
original_set_initial_state
|
||||
end
|
||||
end
|
||||
|
||||
def self.feed_options(user)
|
||||
{
|
||||
:title => 'Tracks Actions',
|
||||
:description => "Actions for #{user.display_name}"
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -13,6 +13,8 @@ class User < ActiveRecord::Base
|
|||
find_by_url_friendly_name(params['id'])
|
||||
elsif params['context']
|
||||
find_by_url_friendly_name(params['context'])
|
||||
elsif params['context_id']
|
||||
find_by_url_friendly_name(params['context_id'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -28,6 +30,8 @@ class User < ActiveRecord::Base
|
|||
find_by_url_friendly_name(params['id'])
|
||||
elsif params['project']
|
||||
find_by_url_friendly_name(params['project'])
|
||||
elsif params['project_id']
|
||||
find_by_url_friendly_name(params['project_id'])
|
||||
end
|
||||
end
|
||||
def update_positions(project_ids)
|
||||
|
|
@ -61,7 +65,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 <= ?', Time.now.utc.to_date ]).collect { |t| t.activate_and_save! }
|
||||
find(:all, :conditions => ['show_from <= ?', Time.now.utc.to_date.to_time ]).collect { |t| t.activate_and_save! }
|
||||
end
|
||||
end
|
||||
has_many :completed_todos,
|
||||
|
|
@ -90,9 +94,14 @@ class User < ActiveRecord::Base
|
|||
validates_confirmation_of :password
|
||||
validates_length_of :login, :within => 3..80
|
||||
validates_uniqueness_of :login, :on => :create
|
||||
validates_inclusion_of :auth_type, :in => Tracks::Config.auth_schemes, :message=>"not a valid authentication type"
|
||||
validates_presence_of :open_id_url, :if => Proc.new{|user| user.auth_type == 'open_id'}
|
||||
|
||||
def validate
|
||||
unless Tracks::Config.auth_schemes.include?(auth_type)
|
||||
errors.add("auth_type", "not a valid authentication type")
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :prefs, :preference
|
||||
|
||||
def self.authenticate(login, pass)
|
||||
|
|
|
|||
8
tracks/app/views/contexts/_text_context.rhtml
Normal file
8
tracks/app/views/contexts/_text_context.rhtml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<%
|
||||
context = text_context
|
||||
todos_in_context = todos.select { |t| t.context_id == context.id }
|
||||
if todos_in_context.length > 0
|
||||
%>
|
||||
<%= context.name.upcase %>:
|
||||
<% end -%>
|
||||
<%= render :partial => "todos/text_todo", :collection => todos_in_context -%>
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
|
||||
xml.channel do
|
||||
xml.title(@title)
|
||||
xml.link("http://#{request.host}:#{request.port}/todos/list")
|
||||
xml.description(@description)
|
||||
@todos.each do |i|
|
||||
xml.item do
|
||||
xml.title(i.description)
|
||||
xml.link(context_url(i.context))
|
||||
item_notes = sanitize(markdown( i.notes )) if i.notes?
|
||||
due = "<div>Due: #{format_date(i.due)}</div>\n" if i.due?
|
||||
toggle_link = link_to( "mark as done", {:only_path => false, :controller => "todos", :action => "toggle_check", :id => i.id})
|
||||
done = "<div>#{toggle_link}</div>" unless i.completed?
|
||||
done = "<div>Completed: #{format_date(i.completed_at)}</div>\n" if i.completed?
|
||||
context_link = link_to( i.context.name, context_url(i.context) )
|
||||
if i.project_id?
|
||||
project_link = link_to(i.project.name, project_url(i.project) )
|
||||
else
|
||||
project_link = "<em>none</em>"
|
||||
end
|
||||
xml.description("#{done||''}#{due||''}#{item_notes||''}\n<div>Project: #{project_link}</div>\n<div>Context: #{context_link}</div>")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<% for @context in @contexts -%>
|
||||
<%= build_text_page( @todos, @context ) -%>
|
||||
<% end -%>
|
||||
|
|
@ -11,32 +11,32 @@
|
|||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<%= rss_feed_link({ :limit => 15 }) %>
|
||||
<%= text_feed_link({ :limit => 15 }) %>
|
||||
<%= ical_feed_link({ :limit => 15 }) %>
|
||||
<%= rss_formatted_link({ :controller => 'todos', :action => 'index', :limit => 15 }) %>
|
||||
<%= text_formatted_link({ :controller => 'todos', :action => 'index', :limit => 15 }) %>
|
||||
<%= ical_formatted_link({ :controller => 'todos', :action => 'index', :limit => 15 }) %>
|
||||
Last 15 actions
|
||||
</li>
|
||||
<li>
|
||||
<%= rss_feed_link %>
|
||||
<%= text_feed_link %>
|
||||
<%= ical_feed_link %>
|
||||
<%= rss_formatted_link( { :controller => 'todos', :action => 'index' } ) %>
|
||||
<%= text_formatted_link( { :controller => 'todos', :action => 'index' } ) %>
|
||||
<%= ical_formatted_link( { :controller => 'todos', :action => 'index' } ) %>
|
||||
All actions
|
||||
</li>
|
||||
<li>
|
||||
<%= rss_feed_link({ :due => 0 }) %>
|
||||
<%= text_feed_link({ :due => 0 }) %>
|
||||
<%= ical_feed_link({ :due => 0 }) %>
|
||||
<%= rss_formatted_link({ :controller => 'todos', :action => 'index', :due => 0 }) %>
|
||||
<%= text_formatted_link({ :controller => 'todos', :action => 'index', :due => 0 }) %>
|
||||
<%= ical_formatted_link({ :controller => 'todos', :action => 'index', :due => 0 }) %>
|
||||
Actions due today or earlier
|
||||
</li>
|
||||
<li>
|
||||
<%= rss_feed_link({ :due => 6 }) %>
|
||||
<%= text_feed_link({ :due => 6 }) %>
|
||||
<%= ical_feed_link({ :due => 6 }) %>
|
||||
<%= rss_formatted_link({ :controller => 'todos', :action => 'index', :due => 6 }) %>
|
||||
<%= text_formatted_link({ :controller => 'todos', :action => 'index', :due => 6 }) %>
|
||||
<%= ical_formatted_link({ :controller => 'todos', :action => 'index', :due => 6 }) %>
|
||||
Actions due in 7 days or earlier
|
||||
</li>
|
||||
<li>
|
||||
<%= rss_feed_link({ :done => 7 }) %>
|
||||
<%= text_feed_link({ :done => 7 }) %>
|
||||
<%= rss_formatted_link({ :controller => 'todos', :action => 'index', :done => 7 }) %>
|
||||
<%= text_formatted_link({ :controller => 'todos', :action => 'index', :done => 7 }) %>
|
||||
Actions completed in the last 7 days
|
||||
</li>
|
||||
<li>
|
||||
|
|
@ -58,9 +58,9 @@
|
|||
<ul>
|
||||
<% for context in @contexts %>
|
||||
<li>
|
||||
<%= rss_feed_link({ :context => context }) %>
|
||||
<%= text_feed_link({ :context => context }) %>
|
||||
<%= ical_feed_link({ :context => context }) %>
|
||||
<%= rss_formatted_link({ :controller=> 'todos', :action => 'index', :context_id => context.to_param }) %>
|
||||
<%= text_formatted_link({ :controller=> 'todos', :action => 'index', :context_id => context.to_param }) %>
|
||||
<%= ical_formatted_link({ :controller=> 'todos', :action => 'index', :context_id => context.to_param }) %>
|
||||
Next actions in <strong><%=h context.name %></strong>
|
||||
</li>
|
||||
<% end %>
|
||||
|
|
@ -70,9 +70,9 @@
|
|||
<ul>
|
||||
<% for project in @projects %>
|
||||
<li>
|
||||
<%= rss_feed_link({ :project => project }) %>
|
||||
<%= text_feed_link({ :project => project }) %>
|
||||
<%= ical_feed_link({ :project => project }) %>
|
||||
<%= rss_formatted_link({ :controller=> 'todos', :action => 'index', :project_id => project.to_param }) %>
|
||||
<%= text_formatted_link({ :controller=> 'todos', :action => 'index', :project_id => project.to_param }) %>
|
||||
<%= ical_formatted_link({ :controller=> 'todos', :action => 'index', :project_id => project.to_param }) %>
|
||||
Next actions for <strong><%=h project.name %></strong>
|
||||
</li>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<% @projects.each do |p| -%>
|
||||
|
||||
<%= p.name.upcase %>
|
||||
<%= p.description + "\n" if p.description_present? -%>
|
||||
<%= p.description + "\n" unless p.description.blank? -%>
|
||||
<%= count_undone_todos_phrase_text(p)%>. Project is <%= p.state %>.
|
||||
<%= p.linkurl + "\n" if p.linkurl_present? -%>
|
||||
<% end -%>
|
||||
19
tracks/app/views/todos/_text_todo.rhtml
Normal file
19
tracks/app/views/todos/_text_todo.rhtml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<%
|
||||
todo = text_todo
|
||||
result_string = ''
|
||||
if (todo.completed?) && todo.completed_at
|
||||
result_string << " [Completed: " + format_date(todo.completed_at) + "] "
|
||||
end
|
||||
|
||||
if todo.due
|
||||
result_string << " [Due: " + format_date(todo.due) + "] "
|
||||
result_string << todo.description + " "
|
||||
else
|
||||
result_string << " " + todo.description + " "
|
||||
end
|
||||
|
||||
unless todo.project.nil?
|
||||
result_string << "(" + todo.project.name + ")"
|
||||
end
|
||||
-%>
|
||||
<%= result_string %>
|
||||
|
|
@ -1,25 +1,25 @@
|
|||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//rousette.org.uk//Tracks 1.04//EN
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:PUBLISH
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:<%= ENV['TZ'] || 'GMT' %>
|
||||
LAST-MODIFIED:<%= Time.now.strftime("%Y%m%dT%H%M%SZ") %>
|
||||
TZNAME:<%= ENV['TZ'] %>
|
||||
END:VTIMEZONE
|
||||
<% for @todo in @todos -%>
|
||||
BEGIN:VTODO
|
||||
DTSTAMP:<%= @todo.created_at.strftime("%Y%m%dT%H%M%SZ") %>
|
||||
DTSTART;VALUE=DATE:<%= @todo.created_at.strftime("%Y%m%d") %>
|
||||
SUMMARY:<%= @todo.description %>
|
||||
UID:<%= format_ical_uid(@todo) %>
|
||||
<% if @todo.notes? -%>
|
||||
DESCRIPTION:<%= format_ical_notes(@todo.notes) %>
|
||||
<% end -%>
|
||||
<% if @todo.due -%>
|
||||
DUE;VALUE=DATE:<%= @todo.due.strftime("%Y%m%d") %>
|
||||
<% end -%>
|
||||
END:VTODO
|
||||
<% end -%>
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//rousette.org.uk//Tracks 1.04//EN
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:PUBLISH
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:<%= ENV['TZ'] || 'GMT' %>
|
||||
LAST-MODIFIED:<%= Time.now.strftime("%Y%m%dT%H%M%SZ") %>
|
||||
TZNAME:<%= ENV['TZ'] %>
|
||||
END:VTIMEZONE
|
||||
<% for @todo in @todos -%>
|
||||
BEGIN:VTODO
|
||||
DTSTAMP:<%= @todo.created_at.strftime("%Y%m%dT%H%M%SZ") %>
|
||||
DTSTART;VALUE=DATE:<%= @todo.created_at.strftime("%Y%m%d") %>
|
||||
SUMMARY:<%= @todo.description %>
|
||||
UID:<%= todo_url(@todo) %>
|
||||
<% if @todo.notes? -%>
|
||||
DESCRIPTION:<%= format_ical_notes(@todo.notes) %>
|
||||
<% end -%>
|
||||
<% if @todo.due -%>
|
||||
DUE;VALUE=DATE:<%= @todo.due.strftime("%Y%m%d") %>
|
||||
<% end -%>
|
||||
END:VTODO
|
||||
<% end -%>
|
||||
END:VCALENDAR
|
||||
1
tracks/app/views/todos/index_text.rhtml
Normal file
1
tracks/app/views/todos/index_text.rhtml
Normal file
|
|
@ -0,0 +1 @@
|
|||
<%= render :partial => "contexts/text_context", :collection => @contexts, :locals => { :todos => @todos } %>
|
||||
|
|
@ -17,7 +17,17 @@ ActionController::Routing::Routes.draw do |map|
|
|||
users.signup 'signup', :action => "new"
|
||||
end
|
||||
|
||||
# ToDo Routes
|
||||
# Context Routes
|
||||
map.resources :contexts, :collection => {:order => :post} do |contexts|
|
||||
contexts.resources :todos
|
||||
end
|
||||
|
||||
# Projects Routes
|
||||
map.resources :projects, :collection => {:order => :post} do |projects|
|
||||
projects.resources :todos
|
||||
end
|
||||
|
||||
# ToDo Routes
|
||||
map.resources :todos,
|
||||
:member => {:toggle_check => :post},
|
||||
:collection => {:check_deferred => :post}
|
||||
|
|
@ -28,21 +38,12 @@ ActionController::Routing::Routes.draw do |map|
|
|||
todos.done_archive 'done/archive', :action => "completed_archive"
|
||||
todos.tag 'todos/tag/:name', :action => "tag"
|
||||
end
|
||||
|
||||
# Context Routes
|
||||
map.resources :contexts, :collection => {:order => :post}
|
||||
map.connect 'context/:context/feed/:action/:login/:token', :controller => 'feed'
|
||||
|
||||
# Projects Routes
|
||||
map.resources :projects, :collection => {:order => :post}
|
||||
map.connect 'project/:project/feed/:action/:login/:token', :controller => 'feed'
|
||||
|
||||
|
||||
# Notes Routes
|
||||
map.resources :notes
|
||||
|
||||
# Feed Routes
|
||||
map.connect 'feeds', :controller => 'feedlist', :action => 'index'
|
||||
map.connect 'feed/:action/:login/:token', :controller => 'feed'
|
||||
|
||||
# Install the default route as the lowest priority.
|
||||
map.connect ':controller/:action/:id'
|
||||
|
|
|
|||
|
|
@ -1,136 +1,136 @@
|
|||
# This file is autogenerated. Instead of editing this file, please use the
|
||||
# migrations feature of ActiveRecord to incrementally modify your database, and
|
||||
# then regenerate this schema definition.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 30) do
|
||||
|
||||
create_table "contexts", :force => true do |t|
|
||||
t.column "name", :string, :default => "", :null => false
|
||||
t.column "hide", :integer, :limit => 4, :default => 0, :null => false
|
||||
t.column "position", :integer, :default => 0, :null => false
|
||||
t.column "user_id", :integer, :default => 0, :null => false
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
add_index "contexts", ["user_id"], :name => "index_contexts_on_user_id"
|
||||
add_index "contexts", ["user_id", "name"], :name => "index_contexts_on_user_id_and_name"
|
||||
|
||||
create_table "notes", :force => true do |t|
|
||||
t.column "user_id", :integer, :default => 0, :null => false
|
||||
t.column "project_id", :integer, :default => 0, :null => false
|
||||
t.column "body", :text
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
create_table "open_id_associations", :force => true do |t|
|
||||
t.column "server_url", :binary
|
||||
t.column "handle", :string
|
||||
t.column "secret", :binary
|
||||
t.column "issued", :integer
|
||||
t.column "lifetime", :integer
|
||||
t.column "assoc_type", :string
|
||||
end
|
||||
|
||||
create_table "open_id_nonces", :force => true do |t|
|
||||
t.column "nonce", :string
|
||||
t.column "created", :integer
|
||||
end
|
||||
|
||||
create_table "open_id_settings", :force => true do |t|
|
||||
t.column "setting", :string
|
||||
t.column "value", :binary
|
||||
end
|
||||
|
||||
create_table "preferences", :force => true do |t|
|
||||
t.column "user_id", :integer, :default => 0, :null => false
|
||||
t.column "date_format", :string, :limit => 40, :default => "%d/%m/%Y", :null => false
|
||||
t.column "week_starts", :integer, :default => 0, :null => false
|
||||
t.column "show_number_completed", :integer, :default => 5, :null => false
|
||||
t.column "staleness_starts", :integer, :default => 7, :null => false
|
||||
t.column "show_completed_projects_in_sidebar", :boolean, :default => true, :null => false
|
||||
t.column "show_hidden_contexts_in_sidebar", :boolean, :default => true, :null => false
|
||||
t.column "due_style", :integer, :default => 0, :null => false
|
||||
t.column "admin_email", :string, :default => "butshesagirl@rousette.org.uk", :null => false
|
||||
t.column "refresh", :integer, :default => 0, :null => false
|
||||
t.column "verbose_action_descriptors", :boolean, :default => false, :null => false
|
||||
t.column "show_hidden_projects_in_sidebar", :boolean, :default => true, :null => false
|
||||
t.column "time_zone", :string, :default => "London", :null => false
|
||||
t.column "show_project_on_todo_done", :boolean, :default => false, :null => false
|
||||
t.column "title_date_format", :string, :default => "%A, %d %B %Y", :null => false
|
||||
end
|
||||
|
||||
add_index "preferences", ["user_id"], :name => "index_preferences_on_user_id"
|
||||
|
||||
create_table "projects", :force => true do |t|
|
||||
t.column "name", :string, :default => "", :null => false
|
||||
t.column "position", :integer, :default => 0, :null => false
|
||||
t.column "user_id", :integer, :default => 0, :null => false
|
||||
t.column "description", :text
|
||||
t.column "state", :string, :limit => 20, :default => "active", :null => false
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
add_index "projects", ["user_id"], :name => "index_projects_on_user_id"
|
||||
add_index "projects", ["user_id", "name"], :name => "index_projects_on_user_id_and_name"
|
||||
|
||||
create_table "sessions", :force => true do |t|
|
||||
t.column "session_id", :string
|
||||
t.column "data", :text
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
add_index "sessions", ["session_id"], :name => "sessions_session_id_index"
|
||||
|
||||
create_table "taggings", :force => true do |t|
|
||||
t.column "taggable_id", :integer
|
||||
t.column "tag_id", :integer
|
||||
t.column "taggable_type", :string
|
||||
t.column "user_id", :integer
|
||||
end
|
||||
|
||||
add_index "taggings", ["tag_id", "taggable_id", "taggable_type"], :name => "index_taggings_on_tag_id_and_taggable_id_and_taggable_type"
|
||||
|
||||
create_table "tags", :force => true do |t|
|
||||
t.column "name", :string
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
add_index "tags", ["name"], :name => "index_tags_on_name"
|
||||
|
||||
create_table "todos", :force => true do |t|
|
||||
t.column "context_id", :integer, :default => 0, :null => false
|
||||
t.column "description", :string, :limit => 100, :default => "", :null => false
|
||||
t.column "notes", :text
|
||||
t.column "created_at", :datetime
|
||||
t.column "due", :date
|
||||
t.column "completed_at", :datetime
|
||||
t.column "project_id", :integer
|
||||
t.column "user_id", :integer, :default => 0, :null => false
|
||||
t.column "show_from", :date
|
||||
t.column "state", :string, :limit => 20, :default => "immediate", :null => false
|
||||
end
|
||||
|
||||
add_index "todos", ["user_id", "state"], :name => "index_todos_on_user_id_and_state"
|
||||
add_index "todos", ["user_id", "project_id"], :name => "index_todos_on_user_id_and_project_id"
|
||||
add_index "todos", ["project_id"], :name => "index_todos_on_project_id"
|
||||
add_index "todos", ["context_id"], :name => "index_todos_on_context_id"
|
||||
add_index "todos", ["user_id", "context_id"], :name => "index_todos_on_user_id_and_context_id"
|
||||
|
||||
create_table "users", :force => true do |t|
|
||||
t.column "login", :string, :limit => 80
|
||||
t.column "password", :string, :limit => 40
|
||||
t.column "word", :string
|
||||
t.column "is_admin", :integer, :limit => 4, :default => 0, :null => false
|
||||
t.column "first_name", :string
|
||||
t.column "last_name", :string
|
||||
t.column "auth_type", :string, :default => "database", :null => false
|
||||
t.column "open_id_url", :string
|
||||
end
|
||||
|
||||
add_index "users", ["login"], :name => "index_users_on_login"
|
||||
|
||||
end
|
||||
# This file is autogenerated. Instead of editing this file, please use the
|
||||
# migrations feature of ActiveRecord to incrementally modify your database, and
|
||||
# then regenerate this schema definition.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 30) do
|
||||
|
||||
create_table "contexts", :force => true do |t|
|
||||
t.column "name", :string, :default => "", :null => false
|
||||
t.column "position", :integer, :null => false
|
||||
t.column "hide", :boolean, :default => false
|
||||
t.column "user_id", :integer, :default => 1
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
add_index "contexts", ["user_id"], :name => "index_contexts_on_user_id"
|
||||
add_index "contexts", ["user_id", "name"], :name => "index_contexts_on_user_id_and_name"
|
||||
|
||||
create_table "notes", :force => true do |t|
|
||||
t.column "user_id", :integer, :null => false
|
||||
t.column "project_id", :integer, :null => false
|
||||
t.column "body", :text
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
create_table "open_id_associations", :force => true do |t|
|
||||
t.column "server_url", :binary
|
||||
t.column "handle", :string
|
||||
t.column "secret", :binary
|
||||
t.column "issued", :integer
|
||||
t.column "lifetime", :integer
|
||||
t.column "assoc_type", :string
|
||||
end
|
||||
|
||||
create_table "open_id_nonces", :force => true do |t|
|
||||
t.column "nonce", :string
|
||||
t.column "created", :integer
|
||||
end
|
||||
|
||||
create_table "open_id_settings", :force => true do |t|
|
||||
t.column "setting", :string
|
||||
t.column "value", :binary
|
||||
end
|
||||
|
||||
create_table "preferences", :force => true do |t|
|
||||
t.column "user_id", :integer, :null => false
|
||||
t.column "date_format", :string, :limit => 40, :default => "%d/%m/%Y", :null => false
|
||||
t.column "week_starts", :integer, :default => 0, :null => false
|
||||
t.column "show_number_completed", :integer, :default => 5, :null => false
|
||||
t.column "staleness_starts", :integer, :default => 7, :null => false
|
||||
t.column "show_completed_projects_in_sidebar", :boolean, :default => true, :null => false
|
||||
t.column "show_hidden_contexts_in_sidebar", :boolean, :default => true, :null => false
|
||||
t.column "due_style", :integer, :default => 0, :null => false
|
||||
t.column "admin_email", :string, :default => "butshesagirl@rousette.org.uk", :null => false
|
||||
t.column "refresh", :integer, :default => 0, :null => false
|
||||
t.column "verbose_action_descriptors", :boolean, :default => false, :null => false
|
||||
t.column "show_hidden_projects_in_sidebar", :boolean, :default => true, :null => false
|
||||
t.column "time_zone", :string, :default => "London", :null => false
|
||||
t.column "show_project_on_todo_done", :boolean, :default => false, :null => false
|
||||
t.column "title_date_format", :string, :default => "%A, %d %B %Y", :null => false
|
||||
end
|
||||
|
||||
add_index "preferences", ["user_id"], :name => "index_preferences_on_user_id"
|
||||
|
||||
create_table "projects", :force => true do |t|
|
||||
t.column "name", :string, :default => "", :null => false
|
||||
t.column "position", :integer, :null => false
|
||||
t.column "user_id", :integer, :default => 1
|
||||
t.column "description", :text
|
||||
t.column "state", :string, :limit => 20, :default => "active", :null => false
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
add_index "projects", ["user_id"], :name => "index_projects_on_user_id"
|
||||
add_index "projects", ["user_id", "name"], :name => "index_projects_on_user_id_and_name"
|
||||
|
||||
create_table "sessions", :force => true do |t|
|
||||
t.column "session_id", :string
|
||||
t.column "data", :text
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
|
||||
|
||||
create_table "taggings", :force => true do |t|
|
||||
t.column "taggable_id", :integer
|
||||
t.column "tag_id", :integer
|
||||
t.column "taggable_type", :string
|
||||
t.column "user_id", :integer
|
||||
end
|
||||
|
||||
add_index "taggings", ["tag_id", "taggable_id", "taggable_type"], :name => "index_taggings_on_tag_id_and_taggable_id_and_taggable_type"
|
||||
|
||||
create_table "tags", :force => true do |t|
|
||||
t.column "name", :string
|
||||
t.column "created_at", :datetime
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
add_index "tags", ["name"], :name => "index_tags_on_name"
|
||||
|
||||
create_table "todos", :force => true do |t|
|
||||
t.column "context_id", :integer, :null => false
|
||||
t.column "project_id", :integer
|
||||
t.column "description", :string, :default => "", :null => false
|
||||
t.column "notes", :text
|
||||
t.column "created_at", :datetime
|
||||
t.column "due", :date
|
||||
t.column "completed_at", :datetime
|
||||
t.column "user_id", :integer, :default => 1
|
||||
t.column "show_from", :date
|
||||
t.column "state", :string, :limit => 20, :default => "immediate", :null => false
|
||||
end
|
||||
|
||||
add_index "todos", ["user_id", "state"], :name => "index_todos_on_user_id_and_state"
|
||||
add_index "todos", ["user_id", "project_id"], :name => "index_todos_on_user_id_and_project_id"
|
||||
add_index "todos", ["project_id"], :name => "index_todos_on_project_id"
|
||||
add_index "todos", ["context_id"], :name => "index_todos_on_context_id"
|
||||
add_index "todos", ["user_id", "context_id"], :name => "index_todos_on_user_id_and_context_id"
|
||||
|
||||
create_table "users", :force => true do |t|
|
||||
t.column "login", :string, :limit => 80, :default => "", :null => false
|
||||
t.column "password", :string, :limit => 40, :default => "", :null => false
|
||||
t.column "word", :string
|
||||
t.column "is_admin", :boolean, :default => false, :null => false
|
||||
t.column "first_name", :string
|
||||
t.column "last_name", :string
|
||||
t.column "auth_type", :string, :default => "database", :null => false
|
||||
t.column "open_id_url", :string
|
||||
end
|
||||
|
||||
add_index "users", ["login"], :name => "index_users_on_login"
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ module LoginSystem
|
|||
end
|
||||
|
||||
def login_or_feed_token_required
|
||||
if ['rss', 'atom', 'txt'].include?(params[:format])
|
||||
if ['rss', 'atom', 'txt', 'ics'].include?(params[:format])
|
||||
if user = User.find_by_word(params[:token])
|
||||
set_current_user(user)
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
ActiveRecord::Validations::ClassMethods.class_eval do
|
||||
# Custom validations
|
||||
|
||||
# Validating a date field parsed by Chronic. If Chronic cannot parse the
|
||||
# date, it returns nil
|
||||
# Adapted from Stuart Rackham's custom date validation
|
||||
# http://www.bigbold.com/snippets/posts/show/1548
|
||||
#
|
||||
def validates_chronic_date(*attr_names)
|
||||
configuration =
|
||||
{ :message => 'is an invalid date. Here are some valid examples: feb 23, 23 feb 06, 6 feb 2006, 2006-02-23, tomorrow, today, 5 days (hence), 1 month hence, etc.)',
|
||||
:on => :save,
|
||||
}
|
||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||
# Don't let validates_each handle allow_nils, it checks the cast value.
|
||||
allow_nil = configuration.delete(:allow_nil)
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
before_cast = record.send("#{attr_name}_before_type_cast")
|
||||
next if allow_nil and (before_cast == '')
|
||||
begin
|
||||
date = Chronic.parse(before_cast.to_s).nil?
|
||||
rescue
|
||||
record.errors.add(attr_name, configuration[:message])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
2
tracks/test/fixtures/projects.yml
vendored
2
tracks/test/fixtures/projects.yml
vendored
|
|
@ -2,7 +2,7 @@
|
|||
<%
|
||||
|
||||
def today
|
||||
Time.now.utc.to_s(:db)
|
||||
Time.now.utc.to_date.to_time.to_s(:db)
|
||||
end
|
||||
|
||||
%>
|
||||
|
|
|
|||
12
tracks/test/fixtures/todos.yml
vendored
12
tracks/test/fixtures/todos.yml
vendored
|
|
@ -2,19 +2,19 @@
|
|||
<%
|
||||
|
||||
def today
|
||||
Time.now.utc.to_s(:db)
|
||||
Time.now.utc.to_date.to_time.to_s(:db)
|
||||
end
|
||||
|
||||
def next_week
|
||||
1.week.from_now.utc.to_s(:db)
|
||||
1.week.from_now.to_date.to_time.utc.to_s(:db)
|
||||
end
|
||||
|
||||
def last_week
|
||||
1.week.ago.utc.to_s(:db)
|
||||
1.week.ago.utc.to_date.to_time.to_s(:db)
|
||||
end
|
||||
|
||||
def two_weeks_hence
|
||||
2.week.from_now.utc.to_s(:db)
|
||||
2.weeks.from_now.utc.to_date.to_time.to_s(:db)
|
||||
end
|
||||
|
||||
%>
|
||||
|
|
@ -136,7 +136,7 @@ end
|
|||
state: completed
|
||||
created_at: <%= today %>
|
||||
due: <%= two_weeks_hence %>
|
||||
completed_at: <%= today %>
|
||||
completed_at: <%= last_week %>
|
||||
user_id: 1
|
||||
|
||||
11:
|
||||
|
|
@ -197,7 +197,7 @@ end
|
|||
created_at: <%= today %>
|
||||
due: ~
|
||||
completed_at: ~
|
||||
show_from: <%= next_week %>
|
||||
show_from: <%= today %>
|
||||
user_id: 1
|
||||
|
||||
16:
|
||||
|
|
|
|||
|
|
@ -47,19 +47,23 @@ class ContextsControllerTest < TodoContainerControllerTestBase
|
|||
#puts @response.body
|
||||
|
||||
assert_xml_select 'rss[version="2.0"]' do
|
||||
assert_xml_select 'channel' do
|
||||
assert_xml_select '>title', 'Tracks Contexts'
|
||||
assert_xml_select '>description', "Lists all the contexts for #{users(:admin_user).display_name}."
|
||||
assert_xml_select 'language', 'en-us'
|
||||
assert_xml_select 'ttl', '40'
|
||||
assert_select 'channel' do
|
||||
assert_select '>title', 'Tracks Contexts'
|
||||
assert_select '>description', "Lists all the contexts for #{users(:admin_user).display_name}"
|
||||
assert_select 'language', 'en-us'
|
||||
assert_select 'ttl', '40'
|
||||
end
|
||||
assert_xml_select 'item', 9 do
|
||||
assert_xml_select 'title', /.+/
|
||||
assert_xml_select 'description', /<p>\d+ actions. Context is (active|hidden). <\/p>/
|
||||
%w(guid link).each do |node|
|
||||
assert_xml_select node, /http:\/\/test.host\/contexts\/.+/
|
||||
assert_select 'item', 9 do
|
||||
assert_select 'title', /.+/
|
||||
assert_select 'description' do
|
||||
assert_select_encoded do
|
||||
assert_select 'p', /\d+ actions. Context is (Active|Hidden)./
|
||||
end
|
||||
end
|
||||
assert_xml_select 'pubDate', /(#{contexts(:agenda).created_at.to_s(:rfc822)}|#{contexts(:library).created_at.to_s(:rfc822)})/
|
||||
%w(guid link).each do |node|
|
||||
assert_select node, /http:\/\/test.host\/contexts\/.+/
|
||||
end
|
||||
assert_select 'pubDate', /(#{contexts(:agenda).created_at.to_s(:rfc822)}|#{contexts(:library).created_at.to_s(:rfc822)})/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -89,12 +93,16 @@ class ContextsControllerTest < TodoContainerControllerTestBase
|
|||
#puts @response.body
|
||||
|
||||
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
|
||||
assert_xml_select '>title', 'Tracks Contexts'
|
||||
assert_xml_select '>subtitle', "Lists all the contexts for #{users(:admin_user).display_name}."
|
||||
assert_xml_select 'entry', 3 do
|
||||
assert_xml_select 'title', /.+/
|
||||
assert_xml_select 'content[type="html"]', /<p>\d+ actions. Context is (active|hidden). <\/p>/
|
||||
assert_xml_select 'published', /(#{contexts(:agenda).created_at.to_s(:rfc822)}|#{contexts(:library).created_at.to_s(:rfc822)})/
|
||||
assert_select '>title', 'Tracks Contexts'
|
||||
assert_select '>subtitle', "Lists all the contexts for #{users(:admin_user).display_name}"
|
||||
assert_select 'entry', 9 do
|
||||
assert_select 'title', /.+/
|
||||
assert_select 'content[type="html"]' do
|
||||
assert_select_encoded do
|
||||
assert_select 'p', /\d+ actions. Context is (Active|Hidden)./
|
||||
end
|
||||
end
|
||||
assert_select 'published', /(#{contexts(:agenda).created_at.xmlschema}|#{contexts(:library).created_at.xmlschema})/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'feed_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class FeedController; def rescue_action(e) raise e end; end
|
||||
|
||||
class FeedControllerTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@controller = FeedController.new
|
||||
request, response = ActionController::TestRequest.new, ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
# Replace this with your real tests.
|
||||
def test_truth
|
||||
assert true
|
||||
end
|
||||
end
|
||||
|
|
@ -101,19 +101,23 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
|
|||
#puts @response.body
|
||||
|
||||
assert_xml_select 'rss[version="2.0"]' do
|
||||
assert_xml_select 'channel' do
|
||||
assert_xml_select '>title', 'Tracks Projects'
|
||||
assert_xml_select '>description', "Lists all the projects for #{users(:admin_user).display_name}."
|
||||
assert_xml_select 'language', 'en-us'
|
||||
assert_xml_select 'ttl', '40'
|
||||
assert_select 'channel' do
|
||||
assert_select '>title', 'Tracks Projects'
|
||||
assert_select '>description', "Lists all the projects for #{users(:admin_user).display_name}"
|
||||
assert_select 'language', 'en-us'
|
||||
assert_select 'ttl', '40'
|
||||
end
|
||||
assert_xml_select 'item', 3 do
|
||||
assert_xml_select 'title', /.+/
|
||||
assert_xml_select 'description', /<p>\d+ actions. Project is (active|hidden|completed). <\/p>/
|
||||
%w(guid link).each do |node|
|
||||
assert_xml_select node, /http:\/\/test.host\/projects\/.+/
|
||||
assert_select 'item', 3 do
|
||||
assert_select 'title', /.+/
|
||||
assert_select 'description' do
|
||||
assert_select_encoded do
|
||||
assert_select 'p', /^\d+ actions\. Project is (active|hidden|completed)\.$/
|
||||
end
|
||||
end
|
||||
assert_xml_select 'pubDate', /(#{projects(:timemachine).updated_at.to_s(:rfc822)}|#{projects(:moremoney).updated_at.to_s(:rfc822)}})/
|
||||
%w(guid link).each do |node|
|
||||
assert_select node, /http:\/\/test.host\/projects\/.+/
|
||||
end
|
||||
assert_select 'pubDate', /(#{projects(:timemachine).updated_at.to_s(:rfc822)}|#{projects(:moremoney).updated_at.to_s(:rfc822)})/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -143,12 +147,16 @@ class ProjectsControllerTest < TodoContainerControllerTestBase
|
|||
#puts @response.body
|
||||
|
||||
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
|
||||
assert_xml_select '>title', 'Tracks Projects'
|
||||
assert_xml_select '>subtitle', "Lists all the projects for #{users(:admin_user).display_name}."
|
||||
assert_xml_select 'entry', 3 do
|
||||
assert_xml_select 'title', /.+/
|
||||
assert_xml_select 'content[type="html"]', /<p>\d+ actions. Project is (active|hidden|completed). <\/p>/
|
||||
assert_xml_select 'published', /(#{projects(:timemachine).updated_at.to_s(:rfc822)}|#{projects(:moremoney).updated_at.to_s(:rfc822)}})/
|
||||
assert_select '>title', 'Tracks Projects'
|
||||
assert_select '>subtitle', "Lists all the projects for #{users(:admin_user).display_name}"
|
||||
assert_select 'entry', 3 do
|
||||
assert_select 'title', /.+/
|
||||
assert_select 'content[type="html"]' do
|
||||
assert_select_encoded do
|
||||
assert_select 'p', /\d+ actions. Project is (active|hidden|completed)./
|
||||
end
|
||||
end
|
||||
assert_select 'published', /(#{projects(:timemachine).updated_at.xmlschema}|#{projects(:moremoney).updated_at.xmlschema})/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -123,6 +123,116 @@ class TodosControllerTest < Test::Unit::TestCase
|
|||
assert_response :success
|
||||
assert_equal 3, @tagged
|
||||
end
|
||||
|
||||
|
||||
def test_rss_feed_content
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :index, { :format => "rss" }
|
||||
assert_equal 'application/rss+xml; charset=utf-8', @response.headers["Content-Type"]
|
||||
#puts @response.body
|
||||
|
||||
assert_xml_select 'rss[version="2.0"]' do
|
||||
assert_select 'channel' do
|
||||
assert_select '>title', 'Tracks Actions'
|
||||
assert_select '>description', "Actions for #{users(:admin_user).display_name}"
|
||||
assert_select 'language', 'en-us'
|
||||
assert_select 'ttl', '40'
|
||||
assert_select 'item', 10 do
|
||||
assert_select 'title', /.+/
|
||||
assert_select 'description', /.*/
|
||||
%w(guid link).each do |node|
|
||||
assert_select node, /http:\/\/test.host\/contexts\/.+/
|
||||
end
|
||||
assert_select 'pubDate', /(#{projects(:timemachine).updated_at.to_s(:rfc822)}|#{projects(:moremoney).updated_at.to_s(:rfc822)})/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_rss_feed_not_accessible_to_anonymous_user_without_token
|
||||
@request.session['user_id'] = nil
|
||||
get :index, { :format => "rss" }
|
||||
assert_response 401
|
||||
end
|
||||
|
||||
def test_rss_feed_not_accessible_to_anonymous_user_with_invalid_token
|
||||
@request.session['user_id'] = nil
|
||||
get :index, { :format => "rss", :token => 'foo' }
|
||||
assert_response 401
|
||||
end
|
||||
|
||||
def test_rss_feed_accessible_to_anonymous_user_with_valid_token
|
||||
@request.session['user_id'] = nil
|
||||
get :index, { :format => "rss", :token => users(:admin_user).word }
|
||||
assert_response :ok
|
||||
end
|
||||
|
||||
def test_atom_feed_content
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :index, { :format => "atom" }
|
||||
assert_equal 'application/atom+xml; charset=utf-8', @response.headers["Content-Type"]
|
||||
#puts @response.body
|
||||
|
||||
assert_xml_select 'feed[xmlns="http://www.w3.org/2005/Atom"]' do
|
||||
assert_xml_select '>title', 'Tracks Actions'
|
||||
assert_xml_select '>subtitle', "Actions for #{users(:admin_user).display_name}"
|
||||
assert_xml_select 'entry', 10 do
|
||||
assert_xml_select 'title', /.+/
|
||||
assert_xml_select 'content[type="html"]', /.*/
|
||||
assert_xml_select 'published', /(#{projects(:timemachine).updated_at.xmlschema}|#{projects(:moremoney).updated_at.xmlschema})/
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_atom_feed_not_accessible_to_anonymous_user_without_token
|
||||
@request.session['user_id'] = nil
|
||||
get :index, { :format => "atom" }
|
||||
assert_response 401
|
||||
end
|
||||
|
||||
def test_atom_feed_not_accessible_to_anonymous_user_with_invalid_token
|
||||
@request.session['user_id'] = nil
|
||||
get :index, { :format => "atom", :token => 'foo' }
|
||||
assert_response 401
|
||||
end
|
||||
|
||||
def test_atom_feed_accessible_to_anonymous_user_with_valid_token
|
||||
@request.session['user_id'] = nil
|
||||
get :index, { :format => "atom", :token => users(:admin_user).word }
|
||||
assert_response :ok
|
||||
end
|
||||
|
||||
def test_text_feed_content
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :index, { :format => "txt" }
|
||||
assert_equal 'text/plain; charset=utf-8', @response.headers["Content-Type"]
|
||||
assert !(/ /.match(@response.body))
|
||||
#puts @response.body
|
||||
end
|
||||
|
||||
def test_text_feed_not_accessible_to_anonymous_user_without_token
|
||||
@request.session['user_id'] = nil
|
||||
get :index, { :format => "txt" }
|
||||
assert_response 401
|
||||
end
|
||||
|
||||
def test_text_feed_not_accessible_to_anonymous_user_with_invalid_token
|
||||
@request.session['user_id'] = nil
|
||||
get :index, { :format => "txt", :token => 'foo' }
|
||||
assert_response 401
|
||||
end
|
||||
|
||||
def test_text_feed_accessible_to_anonymous_user_with_valid_token
|
||||
@request.session['user_id'] = nil
|
||||
get :index, { :format => "txt", :token => users(:admin_user).word }
|
||||
assert_response :ok
|
||||
end
|
||||
|
||||
def test_ical_feed_content
|
||||
@request.session['user_id'] = users(:admin_user).id
|
||||
get :index, { :format => "ics" }
|
||||
assert_equal 'text/calendar; charset=utf-8', @response.headers["Content-Type"]
|
||||
assert !(/ /.match(@response.body))
|
||||
#puts @response.body
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,18 +40,32 @@ class ContextXmlApiTest < ActionController::IntegrationTest
|
|||
def test_fails_with_too_long_name
|
||||
invalid_with_long_name_postdata = "<request><context><name>foobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoo arfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoo arfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfoobarfo barfoobarfoobarfoobarfoobarfoobarfoobar</name></context></request>"
|
||||
authenticated_post_xml_to_context_create invalid_with_long_name_postdata
|
||||
assert_response_and_body 404, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<errors>\n <error>Name context name must be less than 256 characters</error>\n</errors>\n"
|
||||
assert_response 404
|
||||
assert_xml_select 'errors' do
|
||||
assert_select 'error', 1, 'Name context name must be less than 256 characters'
|
||||
end
|
||||
end
|
||||
|
||||
def test_fails_with_slash_in_name
|
||||
authenticated_post_xml_to_context_create "<request><context><name>foo/bar</name></context></request>"
|
||||
assert_response_and_body 404, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<errors>\n <error>Name cannot contain the slash ('/') character</error>\n</errors>\n"
|
||||
assert_response 404
|
||||
assert_xml_select 'errors' do
|
||||
assert_select 'error', 1, 'Name cannot contain the slash (\'/\') character'
|
||||
end
|
||||
end
|
||||
|
||||
def test_creates_new_context
|
||||
initial_count = Context.count
|
||||
authenticated_post_xml_to_context_create
|
||||
assert_response_and_body_matches 200, %r|^<\?xml version="1.0" encoding="UTF-8"\?>\n<context>\n <created-at type=\"datetime\">\d{4}+-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z</created-at>\n <hide type="integer">0</hide>\n <id type="integer">\d+</id>\n <name>#{@@context_name}</name>\n <position type="integer">3</position>\n <updated-at type=\"datetime\">\d{4}+-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z</updated-at>\n</context>\n$|
|
||||
assert_response 200
|
||||
assert_xml_select 'context' do
|
||||
assert_select 'created-at', /\d{4}+-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/
|
||||
assert_select 'hide', 'false'
|
||||
assert_select 'id', /\d+/
|
||||
assert_select 'name', @@context_name
|
||||
assert_select 'position', '3'
|
||||
assert_select 'updated-at', /\d{4}+-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/
|
||||
end
|
||||
assert_equal initial_count + 1, Context.count
|
||||
context1 = Context.find_by_name(@@context_name)
|
||||
assert_not_nil context1, "expected context '#{@@context_name}' to be created"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
require 'projects_controller'
|
||||
require 'contexts_controller'
|
||||
require 'feed_controller'
|
||||
require 'todos_controller'
|
||||
|
||||
# Re-raise errors caught by the controller.
|
||||
class ProjectsController; def rescue_action(e) raise e end; end
|
||||
|
|
@ -16,59 +16,87 @@ class FeedSmokeTest < ActionController::IntegrationTest
|
|||
end
|
||||
|
||||
def test_last_15_actions_rss
|
||||
assert_success "/feed/rss/admin/#{ users(:admin_user).word }?limit=15"
|
||||
assert_success "/todos.rss?token=#{ users(:admin_user).word }&limit=15"
|
||||
end
|
||||
|
||||
|
||||
def test_last_15_actions_atom
|
||||
assert_success "/todos.atom?token=#{ users(:admin_user).word }&limit=15"
|
||||
end
|
||||
|
||||
def test_last_15_actions_txt
|
||||
assert_success "/feed/text/admin/#{ users(:admin_user).word }?limit=15"
|
||||
assert_success "/todos.txt?token=#{ users(:admin_user).word }&limit=15"
|
||||
end
|
||||
|
||||
def test_last_15_actions_ical
|
||||
assert_success "/feed/ical/admin/#{ users(:admin_user).word }?limit=15"
|
||||
assert_success "/todos.ics?token=#{ users(:admin_user).word }&limit=15"
|
||||
end
|
||||
|
||||
def test_all_actions_rss
|
||||
assert_success "/feed/rss/admin/#{ users(:admin_user).word }"
|
||||
assert_success "/todos.rss?token=#{ users(:admin_user).word }"
|
||||
end
|
||||
|
||||
|
||||
def test_all_actions_txt
|
||||
assert_success "/feed/text/admin/#{ users(:admin_user).word }"
|
||||
assert_success "/todos.txt?token=#{ users(:admin_user).word }"
|
||||
end
|
||||
|
||||
|
||||
def test_all_actions_ical
|
||||
assert_success "/feed/ical/admin/#{ users(:admin_user).word }"
|
||||
assert_success "/todos.ics?token=#{ users(:admin_user).word }"
|
||||
end
|
||||
|
||||
|
||||
def test_all_actions_in_context_rss
|
||||
assert_success "/contexts/agenda/todos.rss?token=#{ users(:admin_user).word }"
|
||||
end
|
||||
|
||||
def test_all_actions_in_context_txt
|
||||
assert_success "/contexts/agenda/todos.txt?token=#{ users(:admin_user).word }"
|
||||
end
|
||||
|
||||
def test_all_actions_in_context_ical
|
||||
assert_success "/contexts/agenda/todos.ics?token=#{ users(:admin_user).word }"
|
||||
end
|
||||
|
||||
def test_all_actions_in_project_rss
|
||||
assert_success "/projects/Build_a_working_time_machine/todos.rss?token=#{ users(:admin_user).word }"
|
||||
end
|
||||
|
||||
def test_all_actions_in_project_txt
|
||||
assert_success "/projects/Build_a_working_time_machine/todos.txt?token=#{ users(:admin_user).word }"
|
||||
end
|
||||
|
||||
def test_all_actions_in_project_ical
|
||||
assert_success "/projects/Build_a_working_time_machine/todos.ics?token=#{ users(:admin_user).word }"
|
||||
end
|
||||
|
||||
def test_all_actions_due_today_or_earlier_rss
|
||||
assert_success "/feed/rss/admin/#{ users(:admin_user).word }?due=0"
|
||||
assert_success "/todos.rss?token=#{ users(:admin_user).word }&due=0"
|
||||
end
|
||||
|
||||
def test_all_actions_due_today_or_earlier_txt
|
||||
assert_success "/feed/text/admin/#{ users(:admin_user).word }?due=0"
|
||||
assert_success "/todos.txt?token=#{ users(:admin_user).word }&due=0"
|
||||
end
|
||||
|
||||
def test_all_actions_due_today_or_earlier_ical
|
||||
assert_success "/feed/ical/admin/#{ users(:admin_user).word }?due=0"
|
||||
assert_success "/todos.ics?token=#{ users(:admin_user).word }&due=0"
|
||||
end
|
||||
|
||||
def test_all_actions_due_in_7_days_or_earlier_rss
|
||||
assert_success "/feed/rss/admin/#{ users(:admin_user).word }?due=6"
|
||||
assert_success "/todos.rss?token=#{ users(:admin_user).word }&due=6"
|
||||
end
|
||||
|
||||
def test_all_actions_due_in_7_days_or_earlier_txt
|
||||
assert_success "/feed/text/admin/#{ users(:admin_user).word }?due=6"
|
||||
assert_success "/todos.txt?token=#{ users(:admin_user).word }&due=6"
|
||||
end
|
||||
|
||||
def test_all_actions_due_in_7_days_or_earlier_ical
|
||||
assert_success "/feed/ical/admin/#{ users(:admin_user).word }?due=6"
|
||||
assert_success "/todos.ics?token=#{ users(:admin_user).word }&due=6"
|
||||
end
|
||||
|
||||
def test_all_actions_completed_in_last_7_days_rss
|
||||
assert_success "/feed/rss/admin/#{ users(:admin_user).word }?done=7"
|
||||
assert_success "/todos.rss?token=#{ users(:admin_user).word }&done=7"
|
||||
end
|
||||
|
||||
def test_all_actions_completed_in_last_7_days_txt
|
||||
assert_success "/feed/text/admin/#{ users(:admin_user).word }?done=7"
|
||||
assert_success "/todos.txt?token=#{ users(:admin_user).word }&done=7"
|
||||
end
|
||||
|
||||
def test_all_contexts_rss
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ class Test::Unit::TestCase
|
|||
@xml_document ||= HTML::Document.new(@response.body, false, true)
|
||||
end
|
||||
|
||||
def assert_xml_select(*args)
|
||||
def assert_xml_select(*args, &block)
|
||||
@html_document = xml_document
|
||||
assert_select(*args)
|
||||
assert_select(*args, &block)
|
||||
end
|
||||
|
||||
def next_week
|
||||
|
|
|
|||
|
|
@ -112,5 +112,24 @@ class ContextTest < Test::Unit::TestCase
|
|||
def test_title_reader_returns_name
|
||||
assert_equal @agenda.name, @agenda.title
|
||||
end
|
||||
|
||||
|
||||
def test_feed_options
|
||||
opts = Context.feed_options(users(:admin_user))
|
||||
assert_equal 'Tracks Contexts', opts[:title], 'Unexpected value for :title key of feed_options'
|
||||
assert_equal 'Lists all the contexts for Admin Schmadmin', opts[:description], 'Unexpected value for :description key of feed_options'
|
||||
end
|
||||
|
||||
def test_hidden_attr_reader
|
||||
assert !@agenda.hidden?
|
||||
@agenda.hide = true
|
||||
assert @agenda.hidden?
|
||||
end
|
||||
|
||||
def test_summary
|
||||
undone_todo_count = '5 actions'
|
||||
assert_equal "<p>#{undone_todo_count}. Context is Active.</p>", @agenda.summary(undone_todo_count)
|
||||
@agenda.hide = true
|
||||
assert_equal "<p>#{undone_todo_count}. Context is Hidden.</p>", @agenda.summary(undone_todo_count)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -142,9 +142,55 @@ class ProjectTest < Test::Unit::TestCase
|
|||
def test_to_param_returns_url_friendly_name
|
||||
assert_equal 'Build_a_working_time_machine', @timemachine.to_param
|
||||
end
|
||||
|
||||
def test_title_reader_returns_name
|
||||
assert_equal @timemachine.name, @timemachine.title
|
||||
|
||||
def test_null_object
|
||||
p = Project.null_object
|
||||
assert !p.hidden?
|
||||
assert p.nil?
|
||||
assert_nil p.id
|
||||
end
|
||||
|
||||
|
||||
def test_feed_options
|
||||
opts = Project.feed_options(users(:admin_user))
|
||||
assert_equal 'Tracks Projects', opts[:title], 'Unexpected value for :title key of feed_options'
|
||||
assert_equal 'Lists all the projects for Admin Schmadmin', opts[:description], 'Unexpected value for :description key of feed_options'
|
||||
end
|
||||
|
||||
def test_transition_to_another_state
|
||||
assert_equal :active, @timemachine.current_state
|
||||
@timemachine.transition_to(:hidden)
|
||||
assert_equal :hidden, @timemachine.current_state
|
||||
@timemachine.transition_to(:completed)
|
||||
assert_equal :completed, @timemachine.current_state
|
||||
@timemachine.transition_to(:active)
|
||||
assert_equal :active, @timemachine.current_state
|
||||
end
|
||||
|
||||
def test_transition_to_same_state
|
||||
assert_equal :active, @timemachine.current_state
|
||||
@timemachine.transition_to(:active)
|
||||
assert_equal :active, @timemachine.current_state
|
||||
end
|
||||
|
||||
def test_deferred_todo_count
|
||||
assert_equal 1, @timemachine.deferred_todo_count
|
||||
assert_equal 0, @moremoney.deferred_todo_count
|
||||
@moremoney.todos[0].show_from = next_week
|
||||
assert_equal 1, @moremoney.deferred_todo_count
|
||||
end
|
||||
|
||||
def test_done_todo_count
|
||||
assert_equal 0, @timemachine.done_todo_count
|
||||
assert_equal 0, @moremoney.done_todo_count
|
||||
@moremoney.todos[0].complete!
|
||||
assert_equal 1, @moremoney.done_todo_count
|
||||
end
|
||||
|
||||
def test_not_done_todo_count
|
||||
assert_equal 2, @timemachine.not_done_todo_count
|
||||
assert_equal 3, @moremoney.not_done_todo_count
|
||||
@moremoney.todos[0].complete!
|
||||
assert_equal 2, @moremoney.not_done_todo_count
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ require File.dirname(__FILE__) + '/../test_helper'
|
|||
require 'date'
|
||||
|
||||
class TodoTest < Test::Unit::TestCase
|
||||
fixtures :todos, :users, :contexts
|
||||
fixtures :todos, :users, :contexts, :preferences
|
||||
|
||||
def setup
|
||||
@not_completed1 = Todo.find(1).reload
|
||||
|
|
@ -19,8 +19,8 @@ class TodoTest < Test::Unit::TestCase
|
|||
assert_equal "Call Bill Gates to find out how much he makes per day", @not_completed1.description
|
||||
assert_nil @not_completed1.notes
|
||||
assert @not_completed1.completed? == false
|
||||
assert_equal 1.week.ago.utc.strftime("%Y-%m-%d %H:%M"), @not_completed1.created_at.strftime("%Y-%m-%d %H:%M")
|
||||
assert_equal 2.week.from_now.utc.strftime("%Y-%m-%d"), @not_completed1.due.strftime("%Y-%m-%d")
|
||||
assert_equal 1.week.ago.utc.to_date.to_time.strftime("%Y-%m-%d %H:%M"), @not_completed1.created_at.strftime("%Y-%m-%d %H:%M")
|
||||
assert_equal 2.week.from_now.utc.to_date.to_time.strftime("%Y-%m-%d"), @not_completed1.due.strftime("%Y-%m-%d")
|
||||
assert_nil @not_completed1.completed_at
|
||||
assert_equal 1, @not_completed1.user_id
|
||||
end
|
||||
|
|
@ -56,6 +56,16 @@ class TodoTest < Test::Unit::TestCase
|
|||
assert_equal 1, @not_completed2.errors.count
|
||||
assert_equal "is too long (maximum is 60000 characters)", @not_completed2.errors.on(:notes)
|
||||
end
|
||||
|
||||
def test_validate_show_from_must_be_a_date_in_the_future
|
||||
t = @not_completed2
|
||||
t[:show_from] = 1.week.ago.to_date # we have to set this via the indexer because show_from=() updates the state
|
||||
# and actual show_from value appropriately based on the date
|
||||
assert_equal 1.week.ago.to_date, t.show_from
|
||||
assert !t.save
|
||||
assert_equal 1, t.errors.count
|
||||
assert_equal "must be a date in the future", t.errors.on(:show_from)
|
||||
end
|
||||
|
||||
def test_defer_an_existing_todo
|
||||
@not_completed2
|
||||
|
|
@ -74,4 +84,58 @@ class TodoTest < Test::Unit::TestCase
|
|||
assert item.save, "should have saved successfully" + item.errors.to_xml
|
||||
assert_equal :deferred, item.current_state
|
||||
end
|
||||
|
||||
def test_feed_options
|
||||
opts = Todo.feed_options(users(:admin_user))
|
||||
assert_equal 'Tracks Actions', opts[:title], 'Unexpected value for :title key of feed_options'
|
||||
assert_equal 'Actions for Admin Schmadmin', opts[:description], 'Unexpected value for :description key of feed_options'
|
||||
end
|
||||
|
||||
def test_toggle_completion
|
||||
t = @not_completed1
|
||||
assert_equal :active, t.current_state
|
||||
t.toggle_completion!
|
||||
assert_equal :completed, t.current_state
|
||||
t.toggle_completion!
|
||||
assert_equal :active, t.current_state
|
||||
end
|
||||
|
||||
def test_activate_and_save
|
||||
t = @not_completed1
|
||||
t.show_from = 1.week.from_now.to_date
|
||||
t.save!
|
||||
assert t.deferred?
|
||||
t.reload
|
||||
t.activate_and_save!
|
||||
assert t.active?
|
||||
t.reload
|
||||
assert t.active?
|
||||
end
|
||||
|
||||
def test_project_returns_null_object_when_nil
|
||||
t = @not_completed1
|
||||
assert !t.project.is_a?(NullProject)
|
||||
t.project = nil
|
||||
assert t.project.is_a?(NullProject)
|
||||
end
|
||||
|
||||
def test_initial_state_defaults_to_active
|
||||
t = Todo.new
|
||||
t.description = 'foo'
|
||||
t.context_id = 1
|
||||
t.save!
|
||||
t.reload
|
||||
assert_equal :active, t.current_state
|
||||
end
|
||||
|
||||
def test_initial_state_is_deferred_when_show_from_in_future
|
||||
t = Todo.new
|
||||
t.description = 'foo'
|
||||
t.context_id = 1
|
||||
t.show_from = 1.week.from_now.to_date
|
||||
t.save!
|
||||
t.reload
|
||||
assert_equal :deferred, t.current_state
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,11 +1,28 @@
|
|||
require File.dirname(__FILE__) + '/../test_helper'
|
||||
|
||||
module Tracks
|
||||
class Config
|
||||
def self.auth_schemes
|
||||
['database', 'ldap']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class SimpleLdapAuthenticator
|
||||
cattr_accessor :fake_success
|
||||
|
||||
def self.valid?(login, pass)
|
||||
fake_success
|
||||
end
|
||||
end
|
||||
|
||||
class UserTest < Test::Unit::TestCase
|
||||
fixtures :users, :preferences, :projects, :todos
|
||||
fixtures :users, :preferences, :projects, :contexts, :todos
|
||||
|
||||
def setup
|
||||
assert_equal "test", ENV['RAILS_ENV']
|
||||
assert_equal "change-me", Tracks::Config.salt
|
||||
assert_equal ['database', 'ldap'], Tracks::Config.auth_schemes
|
||||
@admin_user = User.find(1)
|
||||
@other_user = User.find(2)
|
||||
end
|
||||
|
|
@ -28,15 +45,15 @@ class UserTest < Test::Unit::TestCase
|
|||
assert_equal "jane", @other_user.login
|
||||
assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.password
|
||||
assert_not_nil @other_user.word
|
||||
assert @other_user.is_admin == false || @other_user.is_admin == 0
|
||||
assert @other_user.is_admin == false || @other_user.is_admin == 0
|
||||
end
|
||||
|
||||
# ============================================
|
||||
# Validations
|
||||
# ============================================
|
||||
|
||||
|
||||
# Test a password shorter than 5 characters
|
||||
#
|
||||
#
|
||||
def test_validate_short_password
|
||||
assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.password
|
||||
@other_user.password = "four"
|
||||
|
|
@ -46,25 +63,25 @@ class UserTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
# Test a password longer than 40 characters
|
||||
#
|
||||
#
|
||||
def test_validate_long_password
|
||||
assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.password
|
||||
@other_user.password = generate_random_string(41)
|
||||
assert !@other_user.save
|
||||
assert_equal 1, @other_user.errors.count
|
||||
assert_equal "is too long (maximum is 40 characters)", @other_user.errors.on(:password)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Test that correct length password is valid
|
||||
#
|
||||
#
|
||||
def test_validate_correct_length_password
|
||||
assert_equal "#{Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--")}", @other_user.password
|
||||
@other_user.password = generate_random_string(6)
|
||||
assert @other_user.save
|
||||
end
|
||||
|
||||
|
||||
# Test a missing password
|
||||
#
|
||||
#
|
||||
def test_validate_missing_password
|
||||
assert_equal 2, @other_user.id
|
||||
@other_user.password = ""
|
||||
|
|
@ -72,9 +89,9 @@ class UserTest < Test::Unit::TestCase
|
|||
assert_equal 2, @other_user.errors.count
|
||||
assert_equal ["is too short (minimum is 5 characters)", "can't be blank"], @other_user.errors.on(:password)
|
||||
end
|
||||
|
||||
|
||||
# Test a login shorter than 3 characters
|
||||
#
|
||||
#
|
||||
def test_validate_short_login
|
||||
assert_equal "jane", @other_user.login
|
||||
@other_user.login = "ba"
|
||||
|
|
@ -82,27 +99,27 @@ class UserTest < Test::Unit::TestCase
|
|||
assert_equal 1, @other_user.errors.count
|
||||
assert_equal "is too short (minimum is 3 characters)", @other_user.errors.on(:login)
|
||||
end
|
||||
|
||||
|
||||
# Test a login longer than 80 characters
|
||||
#
|
||||
#
|
||||
def test_validate_long_login
|
||||
assert_equal "jane", @other_user.login
|
||||
@other_user.login = generate_random_string(81)
|
||||
assert !@other_user.save
|
||||
assert_equal 1, @other_user.errors.count
|
||||
assert_equal "is too long (maximum is 80 characters)", @other_user.errors.on(:login)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Test that correct length login is valid
|
||||
#
|
||||
#
|
||||
def test_validate_correct_length_login
|
||||
assert_equal "jane", @other_user.login
|
||||
@other_user.login = generate_random_string(6)
|
||||
assert @other_user.save
|
||||
end
|
||||
|
||||
|
||||
# Test a missing login
|
||||
#
|
||||
#
|
||||
def test_validate_missing_login
|
||||
assert_equal 2, @other_user.id
|
||||
@other_user.login = ""
|
||||
|
|
@ -110,39 +127,39 @@ class UserTest < Test::Unit::TestCase
|
|||
assert_equal 2, @other_user.errors.count
|
||||
assert_equal ["is too short (minimum is 3 characters)", "can't be blank"], @other_user.errors.on(:login)
|
||||
end
|
||||
|
||||
|
||||
def test_display_name_with_first_and_last_name_set
|
||||
@other_user.first_name = "Jane"
|
||||
@other_user.last_name = "Doe"
|
||||
assert_equal "Jane Doe", @other_user.display_name
|
||||
end
|
||||
|
||||
|
||||
def test_display_name_with_first_name_set
|
||||
@other_user.first_name = "Jane"
|
||||
@other_user.last_name = nil
|
||||
assert_equal "Jane", @other_user.display_name
|
||||
end
|
||||
|
||||
|
||||
def test_display_name_with_last_name_set
|
||||
@other_user.first_name = nil
|
||||
@other_user.last_name = "Doe"
|
||||
assert_equal "Doe", @other_user.display_name
|
||||
end
|
||||
|
||||
|
||||
def test_display_name_with_neither_first_nor_last_name_set
|
||||
@other_user.first_name = nil
|
||||
@other_user.last_name = nil
|
||||
assert_equal @other_user.login, @other_user.display_name
|
||||
end
|
||||
|
||||
|
||||
def test_prefs_is_short_for_preference
|
||||
assert_equal @admin_user.preference, @admin_user.prefs
|
||||
end
|
||||
|
||||
|
||||
def test_to_param_returns_login
|
||||
assert_equal @admin_user.login, @admin_user.to_param
|
||||
end
|
||||
|
||||
|
||||
def test_change_password
|
||||
assert_not_nil User.authenticate(@admin_user.login, "abracadabra")
|
||||
@admin_user.change_password("foobar", "foobar")
|
||||
|
|
@ -152,28 +169,117 @@ class UserTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
def test_projects_next_project
|
||||
moremoney = projects(:moremoney)
|
||||
next_project = @admin_user.projects.next_from(moremoney)
|
||||
assert_equal projects(:gardenclean), next_project
|
||||
moremoney = projects(:moremoney)
|
||||
next_project = @admin_user.projects.next_from(moremoney)
|
||||
assert_equal projects(:gardenclean), next_project
|
||||
end
|
||||
|
||||
def test_projects_previous_project
|
||||
moremoney = projects(:moremoney)
|
||||
previous_project = @admin_user.projects.previous_from(moremoney)
|
||||
assert_equal projects(:timemachine), previous_project
|
||||
moremoney = projects(:moremoney)
|
||||
previous_project = @admin_user.projects.previous_from(moremoney)
|
||||
assert_equal projects(:timemachine), previous_project
|
||||
end
|
||||
|
||||
def test_projects_next_project_nil
|
||||
gardenclean = projects(:gardenclean)
|
||||
next_project = @admin_user.projects.next_from(gardenclean)
|
||||
assert_nil next_project
|
||||
gardenclean = projects(:gardenclean)
|
||||
next_project = @admin_user.projects.next_from(gardenclean)
|
||||
assert_nil next_project
|
||||
end
|
||||
|
||||
def test_projects_previous_project_nil
|
||||
timemachine = projects(:timemachine)
|
||||
previous_project = @admin_user.projects.previous_from(timemachine)
|
||||
assert_nil previous_project
|
||||
timemachine = projects(:timemachine)
|
||||
previous_project = @admin_user.projects.previous_from(timemachine)
|
||||
assert_nil previous_project
|
||||
end
|
||||
|
||||
def test_no_users_yet
|
||||
assert !User.no_users_yet?
|
||||
User.delete_all
|
||||
assert User.no_users_yet?
|
||||
end
|
||||
|
||||
def test_crypt_word_updates_word
|
||||
old_word = @admin_user.word
|
||||
@admin_user.crypt_word
|
||||
assert_not_equal old_word, @admin_user.word
|
||||
end
|
||||
|
||||
def test_find_admin
|
||||
assert_equal @admin_user, User.find_admin
|
||||
end
|
||||
|
||||
def test_validates_auth_type
|
||||
@other_user.auth_type = 'dnacheck'
|
||||
assert !@other_user.save
|
||||
assert_equal 1, @other_user.errors.count
|
||||
assert_equal "not a valid authentication type", @other_user.errors.on(:auth_type)
|
||||
end
|
||||
|
||||
def test_authenticate_can_use_ldap
|
||||
u = @other_user
|
||||
u.auth_type = 'ldap'
|
||||
u.save!
|
||||
SimpleLdapAuthenticator.fake_success = false
|
||||
assert_nil User.authenticate(u.login, 'foobar')
|
||||
SimpleLdapAuthenticator.fake_success = true
|
||||
assert_equal @other_user, User.authenticate(u.login, 'foobar')
|
||||
end
|
||||
|
||||
def test_find_context_by_params
|
||||
u = @admin_user
|
||||
c = u.contexts.find_by_params('url_friendly_name' => 'agenda')
|
||||
assert_equal contexts(:agenda), c
|
||||
c = u.contexts.find_by_params('id' => 'agenda')
|
||||
assert_equal contexts(:agenda), c
|
||||
c = u.contexts.find_by_params('id' => '1')
|
||||
assert_equal contexts(:agenda), c
|
||||
c = u.contexts.find_by_params('context' => 'agenda')
|
||||
assert_equal contexts(:agenda), c
|
||||
c = u.contexts.find_by_params('context_id' => 'agenda')
|
||||
assert_equal contexts(:agenda), c
|
||||
end
|
||||
|
||||
def test_find_project_by_params
|
||||
u = @admin_user
|
||||
p = u.projects.find_by_params('url_friendly_name' => 'Build_a_working_time_machine')
|
||||
assert_equal projects(:timemachine), p
|
||||
p = u.projects.find_by_params('id' => 'Build_a_working_time_machine')
|
||||
assert_equal projects(:timemachine), p
|
||||
p = u.projects.find_by_params('id' => '1')
|
||||
assert_equal projects(:timemachine), p
|
||||
p = u.projects.find_by_params('project' => 'Build_a_working_time_machine')
|
||||
assert_equal projects(:timemachine), p
|
||||
p = u.projects.find_by_params('project_id' => 'Build_a_working_time_machine')
|
||||
assert_equal projects(:timemachine), p
|
||||
end
|
||||
|
||||
def test_update_project_positions
|
||||
assert_equal 1, Project.find(1).position
|
||||
assert_equal 2, Project.find(2).position
|
||||
assert_equal 3, Project.find(3).position
|
||||
|
||||
@admin_user.projects.update_positions([2,1,3])
|
||||
|
||||
assert_equal 2, Project.find(1).position
|
||||
assert_equal 1, Project.find(2).position
|
||||
assert_equal 3, Project.find(3).position
|
||||
end
|
||||
|
||||
def test_find_and_activate_deferred_todos_that_are_ready
|
||||
assert_equal 1, @admin_user.deferred_todos.count
|
||||
@admin_user.deferred_todos.find_and_activate_ready
|
||||
@admin_user.deferred_todos.reload
|
||||
assert_equal 0, @admin_user.deferred_todos.count
|
||||
end
|
||||
|
||||
def test_completed_todos_completed_within
|
||||
todos = @admin_user.completed_todos.completed_within(@admin_user.time - 1.day)
|
||||
assert_equal 3, todos.length
|
||||
end
|
||||
|
||||
def test_completed_todos_complete_more_than
|
||||
todos = @admin_user.completed_todos.completed_more_than(@admin_user.time - 1.day)
|
||||
assert_equal 1, todos.length
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue