Merge branch 'master' into new-gui

Conflicts:
	Gemfile.lock
This commit is contained in:
Reinier Balt 2014-01-07 21:01:55 +01:00
parent fa537fbeb0
commit eb1502d4e0
28 changed files with 385 additions and 221 deletions

View file

@ -1,9 +1,15 @@
language: ruby
rvm:
- 1.9.3
- 2.0.0
- 2.1
bundler_args: --without development
before_install:
before_install:
- "gem install bundler -v=1.5.1"
- "mysql -e 'create database tracks_test;'"
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
@ -12,7 +18,6 @@ before_install:
script: "CODECLIMATE_REPO_TOKEN=5c52fdd2bbcd0734d56ddb2c3cbaac782da345273e8689d25f54a065ccc3397c bundle exec rake ci RACK_ENV=test"
bundler_args: --without development
notifications:
email: false

View file

@ -1,6 +1,6 @@
GIT
remote: https://github.com/cucumber/aruba
revision: adbfc240d69254d7b525876b4c5bff6b721b7d65
revision: 7afbc5c0cbae9c9a946d70c4c2735ccb86e00f08
specs:
aruba (0.5.3)
childprocess (>= 0.3.6)
@ -9,35 +9,35 @@ GIT
GIT
remote: https://github.com/rails/actionpack-xml_parser
revision: 246653ab3670f329176c1e77e6cd1a632466f06e
revision: e1516064761ea26502cd79b283f6af0fa2b1edf5
specs:
actionpack-xml_parser (1.0.0)
actionpack (>= 4.0.0.rc1, < 4.1)
actionpack-xml_parser (1.0.1)
actionpack (>= 4.0.0, < 5)
GEM
remote: https://rubygems.org/
specs:
RedCloth (4.2.9)
aasm (3.0.22)
actionmailer (4.0.0)
actionpack (= 4.0.0)
mail (~> 2.5.3)
actionpack (4.0.0)
activesupport (= 4.0.0)
aasm (3.0.25)
actionmailer (4.0.2)
actionpack (= 4.0.2)
mail (~> 2.5.4)
actionpack (4.0.2)
activesupport (= 4.0.2)
builder (~> 3.1.0)
erubis (~> 2.7.0)
rack (~> 1.5.2)
rack-test (~> 0.6.2)
activemodel (4.0.0)
activesupport (= 4.0.0)
activemodel (4.0.2)
activesupport (= 4.0.2)
builder (~> 3.1.0)
activerecord (4.0.0)
activemodel (= 4.0.0)
activerecord (4.0.2)
activemodel (= 4.0.2)
activerecord-deprecated_finders (~> 1.0.2)
activesupport (= 4.0.0)
activesupport (= 4.0.2)
arel (~> 4.0.0)
activerecord-deprecated_finders (1.0.3)
activesupport (4.0.0)
activesupport (4.0.2)
i18n (~> 0.6, >= 0.6.4)
minitest (~> 4.2)
multi_json (~> 1.3)
@ -45,16 +45,17 @@ GEM
tzinfo (~> 0.3.37)
acts_as_list (0.3.0)
activerecord (>= 3.0)
arel (4.0.0)
arel (4.0.1)
atomic (1.1.14)
bcrypt-ruby (3.0.1)
builder (3.1.4)
bullet (4.6.0)
uniform_notifier
bullet (4.7.1)
activesupport
uniform_notifier (>= 1.4.0)
cache_digests (0.3.1)
actionpack (>= 3.2)
thread_safe
capybara (2.1.0)
capybara (2.2.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
@ -62,20 +63,20 @@ GEM
xpath (~> 2.0)
childprocess (0.3.9)
ffi (~> 1.0, >= 1.0.11)
codeclimate-test-reporter (0.1.1)
codeclimate-test-reporter (0.2.0)
simplecov (>= 0.7.1, < 1.0.0)
coffee-rails (4.0.0)
coffee-rails (4.0.1)
coffee-script (>= 2.2.0)
railties (>= 4.0.0.beta, < 5.0)
railties (>= 4.0.0, < 5.0)
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.6.3)
commonjs (0.2.7)
cucumber (1.3.8)
cucumber (1.3.10)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.12.1)
gherkin (~> 2.12)
multi_json (>= 1.7.5, < 2.0)
multi_test (>= 0.0.2)
cucumber-rails (1.4.0)
@ -83,25 +84,26 @@ GEM
cucumber (>= 1.2.0)
nokogiri (>= 1.5.0)
rails (>= 3.0.0)
database_cleaner (1.1.1)
diff-lcs (1.2.4)
database_cleaner (1.2.0)
diff-lcs (1.2.5)
docile (1.1.1)
erubis (2.7.0)
execjs (2.0.1)
factory_girl (4.2.0)
execjs (2.0.2)
factory_girl (4.3.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.2.1)
factory_girl (~> 4.2.0)
factory_girl_rails (4.3.0)
factory_girl (~> 4.3.0)
railties (>= 3.0.0)
ffi (1.9.0)
gherkin (2.12.1)
ffi (1.9.3)
gherkin (2.12.2)
multi_json (~> 1.3)
hike (1.2.3)
htmlentities (4.3.1)
i18n (0.6.5)
i18n (0.6.9)
jquery-rails (3.0.4)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
json (1.8.0)
json (1.8.1)
less (2.4.0)
commonjs (~> 0.2.7)
less-rails (2.4.2)
@ -112,66 +114,69 @@ GEM
mime-types (~> 1.16)
treetop (~> 1.4.8)
metaclass (0.0.1)
mime-types (1.25)
mini_portile (0.5.1)
mime-types (1.25.1)
mini_portile (0.5.2)
minitest (4.7.5)
mocha (0.14.0)
metaclass (~> 0.0.1)
mousetrap-rails (0.0.12)
multi_json (1.8.0)
multi_test (0.0.2)
mysql2 (0.3.13)
nokogiri (1.6.0)
mousetrap-rails (1.4.6)
multi_json (1.8.2)
multi_test (0.0.3)
mysql2 (0.3.14)
nokogiri (1.6.1)
mini_portile (~> 0.5.0)
polyglot (0.3.3)
protected_attributes (1.0.5)
activemodel (>= 4.0.1, < 5.0)
rack (1.5.2)
rack-mini-profiler (0.1.31)
rack-mini-profiler (0.9.0)
rack (>= 1.1.3)
rack-test (0.6.2)
rack (>= 1.0)
rails (4.0.0)
actionmailer (= 4.0.0)
actionpack (= 4.0.0)
activerecord (= 4.0.0)
activesupport (= 4.0.0)
rails (4.0.2)
actionmailer (= 4.0.2)
actionpack (= 4.0.2)
activerecord (= 4.0.2)
activesupport (= 4.0.2)
bundler (>= 1.3.0, < 2.0)
railties (= 4.0.0)
railties (= 4.0.2)
sprockets-rails (~> 2.0.0)
rails_autolink (1.1.4)
rails_autolink (1.1.5)
rails (> 3.1)
railties (4.0.0)
actionpack (= 4.0.0)
activesupport (= 4.0.0)
railties (4.0.2)
actionpack (= 4.0.2)
activesupport (= 4.0.2)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rake (10.1.0)
rake (10.1.1)
ref (1.0.5)
rspec-expectations (2.14.3)
rspec-expectations (2.14.4)
diff-lcs (>= 1.1.3, < 2.0)
rubyzip (0.9.9)
rubyzip (1.1.0)
safe_yaml (0.9.7)
sanitize (2.0.6)
nokogiri (>= 1.4.4)
sass (3.2.10)
sass-rails (4.0.0)
railties (>= 4.0.0.beta, < 5.0)
sass (3.2.13)
sass-rails (4.0.1)
railties (>= 4.0.0, < 5.0)
sass (>= 3.1.10)
sprockets-rails (~> 2.0.0)
selenium-webdriver (2.35.1)
selenium-webdriver (2.39.0)
childprocess (>= 0.2.5)
multi_json (~> 1.0)
rubyzip (< 1.0.0)
rubyzip (~> 1.0)
websocket (~> 1.0.4)
simplecov (0.7.1)
multi_json (~> 1.0)
simplecov-html (~> 0.7.1)
simplecov-html (0.7.1)
sprockets (2.10.0)
simplecov (0.8.2)
docile (~> 1.1.0)
multi_json
simplecov-html (~> 0.8.0)
simplecov-html (0.8.0)
sprockets (2.10.1)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-rails (2.0.0)
sprockets-rails (2.0.1)
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (~> 2.8)
@ -187,31 +192,32 @@ GEM
atomic
tilt (1.4.1)
timecop (0.6.3)
tolk (1.3.11)
tolk (1.4.00)
protected_attributes
safe_yaml (~> 0.8)
will_paginate
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
turbolinks (1.3.0)
turbolinks (2.1.0)
coffee-rails
twitter-bootstrap-rails (2.2.8)
actionpack (>= 3.1)
execjs
rails (>= 3.1)
railties (>= 3.1)
tzinfo (0.3.37)
uglifier (2.2.1)
tzinfo (0.3.38)
uglifier (2.4.0)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
uniform_notifier (1.3.0)
json (>= 1.8.0)
uniform_notifier (1.4.0)
websocket (1.0.7)
will_paginate (3.0.5)
will_paginate-bootstrap (0.2.5)
will_paginate-bootstrap (1.0.0)
will_paginate (>= 3.0.3)
xpath (2.0.0)
nokogiri (~> 1.3)
yard (0.8.7.2)
yard (0.8.7.3)
PLATFORMS
ruby

View file

@ -95,7 +95,7 @@ class ApplicationController < ActionController::Base
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}]"
count = @project_project_hidden_todo_counts[todos_parent.id]
else
count = eval "@#{todos_parent.class.to_s.downcase}_not_done_counts[#{todos_parent.id}]"
end

View file

@ -747,34 +747,29 @@ class TodosController < ApplicationController
end
end
def get_not_completed_for_predecessor(relation, todo_id=nil)
items = relation.todos.not_completed.
where('(LOWER(todos.description) LIKE ?)', "%#{params[:term].downcase}%")
items = items.where("AND NOT(todos.id=?)", todo_id) unless todo_id.nil?
items.
includes(:context, :project).
reorder('description ASC').
limit(10)
end
def auto_complete_for_predecessor
unless params['id'].nil?
get_todo_from_params
# Begin matching todos in current project, excluding @todo itself
@items = @todo.project.todos.not_completed.
where('(LOWER(todos.description) LIKE ?) AND NOT(todos.id=?)', "%#{params[:term].downcase}%", @todo.id).
includes(:context, :project).
reorder('description ASC').
limit(10) unless @todo.project.nil?
@items = get_not_completed_for_predecessor(@todo.project, @todo.id) unless @todo.project.nil?
# Then look in the current context, excluding @todo itself
@items = @todo.context.todos.not_completed.
where('(LOWER(todos.description) LIKE ?) AND NOT(todos.id=?)', "%#{params[:term].downcase}%", @todo.id).
includes(:context, :project).
reorder('description ASC').
limit(10) unless !@items.empty? || @todo.context.nil?
@items = get_not_completed_for_predecessor(@todo.context, @todo.id) unless !@items.empty? || @todo.context.nil?
# Match todos in other projects, excluding @todo itself
@items = current_user.todos.not_completed.
where('(LOWER(todos.description) LIKE ?) AND NOT(todos.id=?)', "%#{params[:term].downcase}%", @todo.id).
includes(:context, :project).
reorder('description ASC').
limit(10) unless !@items.empty?
@items = get_not_completed_for_predecessor(current_user, @todo.id) unless !@items.empty?
else
# New todo - TODO: Filter on current project in project view
@items = current_user.todos.not_completed.
where('(LOWER(todos.description) LIKE ?)', "%#{params[:term].downcase}%").
includes(:context, :project).
reorder('description ASC').
limit(10)
@items = get_not_complete_for_predecessor(current_user)
end
render :inline => format_dependencies_as_json_for_auto_complete(@items)
end
@ -1020,27 +1015,23 @@ end
end
end
def find_completed(relation, id, include_hidden)
todos = relation.find(id).todos.completed
todos = todos.not_hidden if !include_hidden
return todos
end
def determine_completed_count
todos=nil
source_view do |from|
from.todo do
@completed_count = current_user.todos.not_hidden.completed.count
end
from.context do
todos = current_user.contexts.find(@todo.context_id).todos.completed
todos = todos.not_hidden if !@todo.context.hidden?
@completed_count = todos.count
end
from.project do
unless @todo.project_id == nil
todos = current_user.projects.find(@todo.project_id).todos.completed
todos = todos.not_hidden if !@todo.project.hidden?
@completed_count = todos.count
end
end
from.tag do
@completed_count = current_user.todos.with_tag(@tag.id).completed.count
end
from.todo { todos = current_user.todos.not_hidden.completed }
from.context { todos = find_completed(current_user.contexts, @todo.context_id, @todo.context.hidden?) }
from.project { todos = find_completed(current_user.projects, @todo.project_id, @todo.project.hidden?) unless @todo.project_id.nil? }
from.tag { todos = current_user.todos.with_tag(@tag.id).completed }
end
@completed_count = todos.nil? ? 0 : todos.count
end
def determine_deferred_tag_count(tag_name)
@ -1195,23 +1186,20 @@ end
end
end
def parse_date_for_update(date, error_msg)
begin
parse_date_per_user_prefs(date)
rescue
@todo.errors[:base] << error_msg
end
end
def update_date_for_update(key)
params['todo'][key] = params["todo"].has_key?(key) ? parse_date_for_update(params["todo"][key], t("todos.error.invalid_#{key}_date")) : ""
end
def update_due_and_show_from_dates
if params["todo"].has_key?("due")
begin
params["todo"]["due"] = parse_date_per_user_prefs(params["todo"]["due"])
rescue
@todo.errors[:base] << "Invalid due date"
end
else
params["todo"]["due"] = ""
end
if params['todo']['show_from']
begin
params['todo']['show_from'] = parse_date_per_user_prefs(params['todo']['show_from'])
rescue
@todo.errors[:base] << "Invalid show from date"
end
end
%w{ due show_from }.each {|date| update_date_for_update(date) }
end
def update_completed_state
@ -1283,18 +1271,18 @@ end
completed_todos.completed_after(start_of_this_day).includes(includes[:include])
end
def get_done_in_period(before, after, includes = {:include => Todo::DEFAULT_INCLUDES})
completed_todos.completed_before(before).completed_after(after).includes(includes[:include])
end
# all completed todos [begin_of_week, start_of_today]
def get_done_rest_of_week(completed_todos, includes = {:include => Todo::DEFAULT_INCLUDES})
start_of_this_week = Time.zone.now.beginning_of_week
start_of_this_day = Time.zone.now.beginning_of_day
completed_todos.completed_before(start_of_this_day).completed_after(start_of_this_week).includes(includes[:include])
get_done_in_period(Time.zone.now.beginning_of_day, Time.zone.now.beginning_of_week)
end
# all completed todos [begin_of_month, begin_of_week]
def get_done_rest_of_month(completed_todos, includes = {:include => Todo::DEFAULT_INCLUDES})
start_of_this_month = Time.zone.now.beginning_of_month
start_of_this_week = Time.zone.now.beginning_of_week
completed_todos.completed_before(start_of_this_week).completed_after(start_of_this_month).includes(includes[:include])
get_done_in_period(Time.zone.now.beginning_of_week, Time.zone.now.beginning_of_month)
end
def get_not_done_todos

View file

@ -1,6 +1,4 @@
class MessageGateway < ActionMailer::Base
include ActionView::Helpers::SanitizeHelper
extend ActionView::Helpers::SanitizeHelper::ClassMethods
def receive(email)
user = get_receiving_user_from_email_address(email)
@ -85,11 +83,11 @@ class MessageGateway < ActionMailer::Base
end
def get_text_or_nil(text)
return text ? sanitize(text.strip) : nil
return text ? text.strip : nil
end
def get_decoded_text_or_nil(text)
return text ? sanitize(text.decoded.strip) : nil
return text ? text.decoded.strip : nil
end
def get_first_text_plain_part(email)
@ -99,7 +97,7 @@ class MessageGateway < ActionMailer::Base
# remove all parts that are not text/plain
parts.reject{|part| !part.content_type.start_with?("text/plain") }
return parts.count > 0 ? sanitize(parts[0].decoded.strip) : ""
return parts.count > 0 ? parts[0].decoded.strip : ""
end
def get_all_parts(parts)

View file

@ -12,6 +12,7 @@ class Project < ActiveRecord::Base
scope :uncompleted, -> { where("NOT(state = ?)", 'completed') }
scope :with_name_or_description, lambda { |body| where("name LIKE ? OR description LIKE ?", body, body) }
scope :with_namepart, lambda { |body| where("name LIKE ?", body + '%') }
validates_presence_of :name
validates_length_of :name, :maximum => 255
@ -88,7 +89,7 @@ class Project < ActiveRecord::Base
# 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 aasm_current_state
when aasm.current_state
return
when :hidden
hide!

View file

@ -66,7 +66,7 @@ class Todo < ActiveRecord::Base
# state machine
include AASM
aasm_initial_state Proc.new { |t| (t.show_from && t.user && (t.show_from > t.user.date)) ? :deferred : :active}
aasm_initial_state = Proc.new { |t| (t.show_from && t.user && (t.show_from > t.user.date)) ? :deferred : :active}
aasm :column => :state do

View file

@ -1,28 +1,72 @@
require 'date'
class RichMessageExtractor
include ActionView::Helpers::SanitizeHelper
extend ActionView::Helpers::SanitizeHelper::ClassMethods
RICH_MESSAGE_FIELDS_REGEX = /([^>@]*)@?([^>]*)>?(.*)/
PROJECT_MARKER = '~'
CONTEXT_MARKER = '@'
TICKLER_MARKER = '>'
DUE_MARKER = '<'
TAG_MARKER = '#'
STAR_MARKER = '*'
ALL_MARKERS = [
PROJECT_MARKER,
CONTEXT_MARKER,
TICKLER_MARKER,
DUE_MARKER,
TAG_MARKER,
STAR_MARKER
]
def initialize(message)
@message = message
end
def description
fields[1].strip
desc = select_for('')
desc.blank? ? '' : sanitize(desc[1].strip)
end
def context
fields[2].strip
context = select_for(CONTEXT_MARKER)
context.blank? ? '' : sanitize(context[1].strip)
end
def project
stripped = fields[3].strip
stripped.blank? ? nil : stripped
project = select_for PROJECT_MARKER
project.blank? ? nil : sanitize(project[1].strip)
end
def tags
string = @message.dup
tags = []
# Regex only matches one tag, so recurse until we have them all
while string.match /#(.*?)(?=[#{ALL_MARKERS.join}]|\Z)/
tags << sanitize($1)
string.gsub!(/##{$1}/,'')
end
tags.empty? ? nil : tags
end
def due
due = select_for DUE_MARKER
due.blank? ? nil : Date.parse(due[1].strip)
end
def show_from
show_from = select_for TICKLER_MARKER
show_from.blank? ? nil : Date.parse(show_from[1].strip)
end
def starred?
@message.include? '*'
end
private
def fields
@message.match(RICH_MESSAGE_FIELDS_REGEX)
end
def select_for symbol
@message.match /#{symbol}(.*?)(?=[#{ALL_MARKERS.join}]|\Z)/
end
end

View file

@ -14,6 +14,10 @@ class TodoFromRichMessage
description = extractor.description
context = extractor.context
project = extractor.project
show_from = extractor.show_from
due = extractor.due
tags = extractor.tags
star = extractor.starred?
context_id = default_context_id
if context.present?
@ -33,17 +37,21 @@ class TodoFromRichMessage
found_project.name = project[4..259].strip
found_project.save!
else
found_project = user.projects.active.find_by_namepart(project)
found_project = user.projects.find_by_namepart(project) if found_project.nil?
found_project = user.projects.active.with_namepart(project).first
found_project = user.projects.with_namepart(project).first if found_project.nil?
end
project_id = found_project.id unless found_project.nil?
end
todo = user.todos.build
todo = user.todos.build
todo.description = description
todo.raw_notes = notes
todo.context_id = context_id
todo.project_id = project_id unless project_id.nil?
todo.raw_notes = notes
todo.context_id = context_id
todo.project_id = project_id unless project_id.nil?
todo.show_from = show_from if show_from.is_a? Date
todo.due = due if due.is_a? Date
todo.tag_with tags unless tags.nil? || tags.empty?
todo.starred = star
todo
end
end

View file

@ -131,5 +131,5 @@
<li>Enable the "Add any gadget by URL" feature. You will find it at bottom of the list. Select Enable radio button and click Save Changes button.</li>
<li>Now you can see Gadgets tab added to Gmail Settings. Go to the Gadgets tab</li>
<li>Paste following link to the Add a gadget by its URL: and then click Add button:<br/>
<pre><%= integrations_url + "/google_gadget" %></pre></li>
<pre><%= integrations_url + "/google_gadget.xml" %></pre></li>
</ul>

View file

@ -14,6 +14,7 @@
<li><a href="#email-cron-section">Automatically Email Yourself Upcoming Actions</a></li>
<li><a href="#message_gateway">Integrate Tracks with an email server to be able to send an action through email to Tracks</a></li>
<li><a href="#mailgun">Send emails to Tracks with Mailgun</a>
<li><a href="#todo_rich_message_format">Rich Todo Message email format</a>
<li><a href="#google_gadget">Add Tracks as a Google Gmail gadget</a></li>
</ul><br/>
<p>Do you have one of your own to add?
@ -122,6 +123,7 @@
<a name="mailgun"> </a>
<h2>Send emails to Tracks with Mailgun</h2>
<p>
If you want to email tasks to Tracks, but cannot run a mailserver on the same host,
you could use the <a href='www.mailgun.com'>Mailgun</a> support built in to Tracks.
</p>
@ -155,6 +157,36 @@ mailmap:
<p>All the comments about the email format from the section above apply to the
Mailgun handling, as the data is processed the same way</p>
<a name="todo_rich_message_format"> </a>
<h2>Rich Todo Message Format</h2>
<p> For both of the above methods, the follow format can be used:</p>
<pre>my awesome todo @context ~project &lt;131012 &gt;131009 #tag1 #tag2 *</pre>
<p>The fields are:</p>
<table>
<tr>
<th>Symbol</th><th>Meaning</th>
</tr>
<tr>
<td>@</td><td>The context to place the Todo in</td>
</tr>
<tr>
<td>~</td><td>The project to place the Todo in</td>
</tr>
<tr>
<td>&lt;</td><td>The due date for the Todo (may be 2 digits for day, 4 digits for month-day, or 6 digits for yeah-month-day)</td>
</tr>
<tr>
<td>&gt;</td><td>The due date for the Todo (may be 2 digits for day, 4 digits for month-day, or 6 digits for yeah-month-day)</td>
</tr>
<tr>
<td>#</td><td>A tag to apply to the Todo - may be used multiple times</td>
</tr>
<tr>
<td>*</td><td>Flag to star the Todo</td>
</tr>
</table>
<p>All symbols are optional, and text up to the first symbol (or end of string) is used as the description of the todo</p>
<a name="google_gadget"> </a>
<h2>Add Tracks as a Google Gmail gadget</h2>
<p>

View file

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

View file

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

View file

@ -4,36 +4,42 @@
<h2><Actions><%= t('common.actions') %></h2>
<form method="get" action="<%= edit_todo_path(@todo, :format => :m)%>">
<button><%=t('todos.edit_action')%></button>
<input type="hidden" name="_method" value="put" />
<button><%=t('todos.edit_action')%></button>
<input type="hidden" name="_method" value="put" />
</form>
<form method="post" action="<%=toggle_star_todo_path(@todo, :format=>:m)%>">
<button><%=t('todos.star_action')%></button>
<input type="hidden" name="_method" value="put" />
<button><%=t('todos.star_action')%></button>
<input type="hidden" name="_method" value="put" />
<%= token_tag %>
</form>
<form method="post" action="<%=toggle_check_todo_path(@todo, :format=>:m)%>">
<button><%= t('todos.mark_complete')%></button>
<input type="hidden" name="_method" value="put" />
<button><%= t('todos.mark_complete')%></button>
<input type="hidden" name="_method" value="put" />
<%= token_tag %>
</form>
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 1)%>">
<button><%=t('todos.defer_x_days', :count => 1)%></button>
<input type="hidden" name="_method" value="put" />
<button><%=t('todos.defer_x_days', :count => 1)%></button>
<input type="hidden" name="_method" value="put" />
<%= token_tag %>
</form>
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 2)%>">
<button><%=t('todos.defer_x_days', :count => 2)%></button>
<input type="hidden" name="_method" value="put" />
<button><%=t('todos.defer_x_days', :count => 2)%></button>
<input type="hidden" name="_method" value="put" />
<%= token_tag %>
</form>
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 3)%>">
<button><%=t('todos.defer_x_days', :count => 3)%></button>
<input type="hidden" name="_method" value="put" />
<button><%=t('todos.defer_x_days', :count => 3)%></button>
<input type="hidden" name="_method" value="put" />
<%= token_tag %>
</form>
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 7)%>">
<button><%=t('todos.defer_x_days', :count => 7)%></button>
<input type="hidden" name="_method" value="put" />
<button><%=t('todos.defer_x_days', :count => 7)%></button>
<input type="hidden" name="_method" value="put" />
<%= token_tag %>
</form>

View file

@ -68,5 +68,8 @@ module Tracksapp
config.action_view.sanitized_allowed_protocols = 'onenote', 'message'
config.middleware.insert_after ActionDispatch::ParamsParser, ActionDispatch::XmlParamsParser
# if a locale is invalid, the Rails app will raise an error
config.i18n.enforce_available_locales = true
end
end

View file

@ -471,6 +471,9 @@ en:
project_completed: Completed actions in this project
context_completed: Completed actions in this context
tag_hidden: "Hidden actions tagged with '%{param}'"
error:
invalid_due_date: "Invalid due date"
invalid_show_from_date: "Invalid show from date"
completed_today: Completed today
completed_rest_of_week: Completed in the rest of this week
completed_rest_of_month: Completed in the rest of this month

View file

@ -3,7 +3,7 @@ class FixIncorrectlyHiddenTodos < ActiveRecord::Migration
hidden_todos_without_project =
Todo.where(:state => 'project_hidden', :project_id => nil)
active_projects = Project.where(:state => 'active')
active_projects = Project.where(:state => 'active').select("id")
hidden_todos_in_active_projects =
Todo.where(:state => 'project_hidden').where("project_id IN (?)", active_projects)

View file

@ -10,7 +10,7 @@ There are two methods of downloading Tracks:
# If you want to live on the edge, you can get the latest development version from GitHub using git (bear in mind that this may be less stable than the released versions):
bc. cd ~/Sites
git clone git://github.com/tracksapp/tracks.git
git clone https://github.com/TracksApp/tracks.git
cd tracks

View file

@ -4,7 +4,6 @@ class PreferencesControllerTest < ActionController::TestCase
def setup
super
assert_equal "test", Rails.env
assert_equal "change-me", Tracks::Config.salt
end

View file

@ -48,7 +48,7 @@ class ProjectsControllerTest < ActionController::TestCase
login_as(:admin_user)
xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"hidden"}
todos.each do |t|
assert_equal :project_hidden, t.reload().aasm_current_state
assert_equal :project_hidden, t.reload().aasm.current_state
end
assert p.reload().hidden?
end
@ -60,7 +60,7 @@ class ProjectsControllerTest < ActionController::TestCase
xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"hidden"}
xhr :post, :update, :id => 1, "project"=>{"name"=>p.name, "description"=>p.description, "state"=>"active"}
todos.each do |t|
assert_equal :active, t.reload().aasm_current_state
assert_equal :active, t.reload().aasm.current_state
end
assert p.reload().active?
end

View file

@ -84,7 +84,7 @@ class RecurringTodosControllerTest < ActionController::TestCase
xhr :post, :toggle_check, :id=>1, :_source_view=>""
recurring_todo_1 = RecurringTodo.find(1) # reload seems to not work
assert recurring_todo_1.active?, "recurring todo should be active but is #{recurring_todo_1.aasm_current_state}"
assert recurring_todo_1.active?, "recurring todo should be active but is #{recurring_todo_1.aasm.current_state}"
# by making active, a new todo should be created from the pattern
assert_equal todo_count+1, Todo.count

View file

@ -20,7 +20,7 @@ def two_weeks_hence
end
def way_back
Time.zone.local(2008,1,1)
Time.zone.local(2008,1,1).to_s(:db)
end
%>

View file

@ -4,7 +4,6 @@ class PreferenceTest < ActiveSupport::TestCase
fixtures :users, :preferences
def setup
assert_equal "test", ENV['RAILS_ENV']
assert_equal "change-me", Tracks::Config.salt
@admin_user = User.find(1)
@other_user = User.find(2)

View file

@ -48,36 +48,36 @@ class ProjectTest < ActiveSupport::TestCase
# state machine
def test_project_initial_state_is_active
assert_equal :active, @timemachine.aasm_current_state
assert_equal :active, @timemachine.aasm.current_state
assert @timemachine.active?
end
def test_hide_project
@timemachine.hide!
assert_equal :hidden, @timemachine.aasm_current_state
assert_equal :hidden, @timemachine.aasm.current_state
assert @timemachine.hidden?
end
def test_activate_project
@timemachine.activate!
assert_equal :active, @timemachine.aasm_current_state
assert_equal :active, @timemachine.aasm.current_state
assert @timemachine.active?
end
def test_transition_to_another_state
assert_equal :active, @timemachine.aasm_current_state
assert_equal :active, @timemachine.aasm.current_state
@timemachine.transition_to(:hidden)
assert_equal :hidden, @timemachine.aasm_current_state
assert_equal :hidden, @timemachine.aasm.current_state
@timemachine.transition_to(:completed)
assert_equal :completed, @timemachine.aasm_current_state
assert_equal :completed, @timemachine.aasm.current_state
@timemachine.transition_to(:active)
assert_equal :active, @timemachine.aasm_current_state
assert_equal :active, @timemachine.aasm.current_state
end
def test_transition_to_same_state
assert_equal :active, @timemachine.aasm_current_state
assert_equal :active, @timemachine.aasm.current_state
@timemachine.transition_to(:active)
assert_equal :active, @timemachine.aasm_current_state
assert_equal :active, @timemachine.aasm.current_state
end
# other tests
@ -95,7 +95,7 @@ class ProjectTest < ActiveSupport::TestCase
def test_complete_project
assert_nil @timemachine.completed_at
@timemachine.complete!
assert_equal :completed, @timemachine.aasm_current_state
assert_equal :completed, @timemachine.aasm.current_state
assert @timemachine.completed?
assert_not_nil @timemachine.completed_at, "completed_at not expected to be nil"
assert_in_delta Time.now, @timemachine.completed_at, 1
@ -150,7 +150,7 @@ class ProjectTest < ActiveSupport::TestCase
first_todo = @moremoney.todos[0]
first_todo.show_from = Time.zone.now + 1.week
first_todo.save!
assert_equal :deferred, @moremoney.todos[0].aasm_current_state
assert_equal :deferred, @moremoney.todos[0].aasm.current_state
assert_equal 1, @moremoney.todos.deferred.count
end

View file

@ -1,3 +1,4 @@
require 'date'
require 'test/unit'
require 'active_support/core_ext/object/blank'
require_relative '../../app/services/rich_message_extractor.rb'
@ -5,11 +6,15 @@ require_relative '../../app/services/rich_message_extractor.rb'
class RichMessageExtractorTest < Test::Unit::TestCase
def test_message_with_all_options
message = "ohai@some-context>in-this-project"
message = "ohai@some-context~this-project>131012<131014#tag1#tag2*"
extractor = RichMessageExtractor.new(message)
assert_equal "ohai", extractor.description
assert_equal "some-context", extractor.context
assert_equal "in-this-project", extractor.project
assert_equal "this-project", extractor.project
assert_equal "2013-10-12", extractor.show_from.to_s
assert_equal "2013-10-14", extractor.due.to_s
assert_equal ["tag1","tag2"], extractor.tags
assert extractor.starred?
end
def test_message_without_project
@ -20,12 +25,12 @@ class RichMessageExtractorTest < Test::Unit::TestCase
assert_equal nil, extractor.project
end
def test_message_without_project
message = " ohai @ some-context"
def test_message_without_context
message = " ohai ~ some-project"
extractor = RichMessageExtractor.new(message)
assert_equal "ohai", extractor.description
assert_equal "some-context", extractor.context
assert_equal nil, extractor.project
assert_equal "", extractor.context
assert_equal "some-project", extractor.project
end
def test_message_without_project_or_context
@ -52,4 +57,52 @@ class RichMessageExtractorTest < Test::Unit::TestCase
assert_equal nil, extractor.project
end
def test_message_with_tags
message = "some tags#tag 1#tag2"
extractor = RichMessageExtractor.new(message)
assert_equal ["tag 1","tag2"], extractor.tags
end
def test_message_with_no_tags
message = "no tags"
extractor = RichMessageExtractor.new(message)
assert_equal nil, extractor.tags
end
def test_message_with_due_date
message = "datetest<141013"
extractor = RichMessageExtractor.new(message)
assert_equal "2014-10-13", extractor.due.to_s
end
def test_message_with_no_due_date
message = "no date"
extractor = RichMessageExtractor.new(message)
assert_equal nil, extractor.due
end
def test_message_with_show_from
message = "datetest>161013"
extractor = RichMessageExtractor.new(message)
assert_equal "2016-10-13", extractor.show_from.to_s
end
def test_message_with_no_show_from
message = "no tickler"
extractor = RichMessageExtractor.new(message)
assert_equal nil, extractor.show_from
end
def test_message_with_star
message = "star test*"
extractor = RichMessageExtractor.new(message)
assert extractor.starred?
end
def test_message_with_no_star
message = "no star test"
extractor = RichMessageExtractor.new(message)
refute extractor.starred?
end
end

View file

@ -18,4 +18,23 @@ class TodoFromRichMessageTest < ActiveSupport::TestCase
assert_equal default_context_id, new_todo.context_id
end
def test_from_rich_message_adds_all_fields
user = @completed.user
context = Context.create(:name => 'context')
project = Project.create(:name => 'project')
message = "description@context~project>131014<131017#tag1#tag2*"
builder = TodoFromRichMessage.new(user, context.id, message, "notes")
new_todo = builder.construct
assert_not_nil new_todo
assert_equal "description", new_todo.description
assert_equal "notes", new_todo.notes
assert_equal context.id, new_todo.context_id
assert_equal project.id, new_todo.project_id
assert_equal "2013-10-14 00:00:00 +0100", new_todo.show_from.to_s
assert_equal "2013-10-17 00:00:00 +0100", new_todo.due.to_s
assert_equal "starred, tag1, tag2", new_todo.tags.to_s
assert new_todo.starred?
end
end

View file

@ -97,10 +97,10 @@ class TodoTest < ActiveSupport::TestCase
def test_defer_an_existing_todo
@not_completed2
assert_equal :active, @not_completed2.aasm_current_state
assert_equal :active, @not_completed2.aasm.current_state
@not_completed2.show_from = Time.zone.now + 1.week
assert @not_completed2.save, "should have saved successfully" + @not_completed2.errors.to_xml
assert_equal :deferred, @not_completed2.aasm_current_state
assert_equal :deferred, @not_completed2.aasm.current_state
end
def test_create_a_new_deferred_todo
@ -110,41 +110,41 @@ class TodoTest < ActiveSupport::TestCase
todo.context_id = 1
todo.description = 'foo'
assert todo.save, "should have saved successfully" + todo.errors.to_xml
assert_equal :deferred, todo.aasm_current_state
assert_equal :deferred, todo.aasm.current_state
end
def test_create_a_new_deferred_todo_by_passing_attributes
user = users(:other_user)
todo = user.todos.build(:show_from => next_week, :context_id => 1, :description => 'foo')
assert todo.save, "should have saved successfully" + todo.errors.to_xml
assert_equal :deferred, todo.aasm_current_state
assert_equal :deferred, todo.aasm.current_state
end
def test_toggle_completion
t = @not_completed1
assert_equal :active, t.aasm_current_state
assert_equal :active, t.aasm.current_state
t.toggle_completion!
assert_equal :completed, t.aasm_current_state
assert_equal :completed, t.aasm.current_state
t.toggle_completion!
assert_equal :active, t.aasm_current_state
assert_equal :active, t.aasm.current_state
end
def test_toggle_completion_with_show_from_in_future
t = @not_completed1
t.show_from= 1.week.from_now
t.save!
assert_equal :deferred, t.aasm_current_state
assert_equal :deferred, t.aasm.current_state
t.toggle_completion!
assert_equal :completed, t.aasm_current_state
assert_equal :completed, t.aasm.current_state
end
def test_toggle_completion_with_show_from_in_past
t = @not_completed1
t.update_attribute(:show_from, 1.week.ago)
assert_equal :active, t.aasm_current_state
assert_equal :active, t.aasm.current_state
assert t.toggle_completion!, "shoud be able to mark active todo complete even if show_from is set in the past"
assert_equal :completed, t.aasm_current_state
assert_equal :completed, t.aasm.current_state
end
def test_activate_also_saves
@ -225,7 +225,7 @@ class TodoTest < ActiveSupport::TestCase
t.context_id = 1
t.save!
t.reload
assert_equal :active, t.aasm_current_state
assert_equal :active, t.aasm.current_state
end
def test_initial_state_is_deferred_when_show_from_in_future
@ -236,7 +236,7 @@ class TodoTest < ActiveSupport::TestCase
t.show_from = 1.week.from_now.to_date
t.save!
t.reload
assert_equal :deferred, t.aasm_current_state
assert_equal :deferred, t.aasm.current_state
end
def test_todo_is_not_starred

View file

@ -1,10 +1,10 @@
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
require 'timecop'
class UserTest < ActiveSupport::TestCase
fixtures :users, :preferences, :projects, :contexts, :todos, :recurring_todos
def setup
assert_equal "test", ENV['RAILS_ENV']
assert_equal "change-me", Tracks::Config.salt
@admin_user = User.find(1)
@other_user = User.find(2)
@ -374,16 +374,16 @@ class UserTest < ActiveSupport::TestCase
# test group counts for projects and contexts
project_counts = u.todos.count_by_group(:project_id)
assert_equal "6,3,4,4", project_counts.map{|pc|pc[1]}.join(",")
assert_equal [6,3,4,4], project_counts.values
context_counts = u.todos.count_by_group(:context_id)
assert_equal "7,3,1,3,1,2", context_counts.map{|cc|cc[1]}.join(",")
assert_equal [7,3,1,3,1,2], context_counts.values
# add a todo to the first context and check that the count is increased
u.todos.create!(:description => "yet another todo", :context => u.contexts.first)
context_counts = u.todos.reload.count_by_group(:context_id)
assert_equal "8,3,1,3,1,2", context_counts.map{|cc|cc[1]}.join(",")
assert_equal [8,3,1,3,1,2], context_counts.values
end
protected
@ -393,4 +393,4 @@ class UserTest < ActiveSupport::TestCase
User.create({ :login => 'quire', :password => 'quire', :password_confirmation => 'quire' }.merge(options))
end
end
end