From 229a1144413fcf5633730d75cbe61c9314138ab6 Mon Sep 17 00:00:00 2001 From: "Jakub A.Tesinsky" Date: Fri, 5 Sep 2008 03:25:56 +0200 Subject: [PATCH 01/44] mport feature added. Everything gets imported except updated_at fields. IDs of objects are not copied, but created as new so there could be no conflict with existing objects. --- app/controllers/data_controller.rb | 109 +++++++++++++++++++++++++++- app/views/data/index.html.erb | 13 +++- app/views/data/yaml_form.html.erb | 26 +++---- app/views/data/yaml_import.html.erb | 8 +- public/.htaccess | 2 +- 5 files changed, 138 insertions(+), 20 deletions(-) diff --git a/app/controllers/data_controller.rb b/app/controllers/data_controller.rb index a0514945..47a85fd6 100644 --- a/app/controllers/data_controller.rb +++ b/app/controllers/data_controller.rb @@ -88,8 +88,115 @@ class DataController < ApplicationController # Draw the form to input the YAML text data end + # adjusts time to utc + def adjust_time(timestring) + if (timestring=='') or ( timestring == nil) + return nil + else + return Time.parse(timestring + 'UTC') + end + end + def yaml_import - # Logic to load the YAML text file and create new records from data + @errmessage = '' + @inarray = YAML::load(params['import']['yaml']) + # arrays to handle id translations + + + # contexts + translate_context = Hash.new + translate_context[nil] = nil + current_user.contexts.each { |context| context.destroy } + @inarray['contexts'].each { | item | + newitem = Context.new(item.ivars['attributes']) + newitem.user_id = current_user.id + newitem.created_at = adjust_time(item.ivars['attributes']['created_at']) + newitem.save(false) + translate_context[item.ivars['attributes']['id'].to_i] = newitem.id + } + + # projects + translate_project = Hash.new + translate_project[nil] = nil + current_user.projects.each { |item| item.destroy } + @inarray['projects'].each { |item| + newitem = Project.new(item.ivars['attributes']) + # ids + newitem.user_id = current_user.id + newitem.default_context_id = translate_context[newitem.default_context_id] + newitem.save(false) + translate_project[item.ivars['attributes']['id'].to_i] = newitem.id + + # state + dates + newitem.transition_to(item.ivars['attributes']['state']) + newitem.completed_at = adjust_time(item.ivars['attributes']['completed_at']) + newitem.created_at = adjust_time(item.ivars['attributes']['created_at']) + newitem.position = item.ivars['attributes']['position'] + newitem.save(false) + } + + # todos + translate_todo = Hash.new + translate_todo[nil] = nil + current_user.todos.each { |item| item.destroy } + @inarray['todos'].each { |item| + newitem = Todo.new(item.ivars['attributes']) + # ids + newitem.user_id = current_user.id + newitem.context_id = translate_context[newitem.context_id] + newitem.project_id = translate_project[newitem.project_id] + # TODO: vyresit recurring_todo_id + newitem.save(false) + translate_todo[item.ivars['attributes']['id'].to_i] = newitem.id + + # state + dates + case item.ivars['attributes']['state'] + when 'active' : newitem.activate! + when 'project_hidden' : newitem.hide! + when 'completed' + newitem.complete! + newitem.completed_at = adjust_time(item.ivars['attributes']['completed_at']) + when 'deferred' : newitem.defer! + end + newitem.created_at = adjust_time(item.ivars['attributes']['created_at']) + newitem.save(false) + } + + #tags + translate_tag = Hash.new + translate_tag[nil] = nil + current_user.tags.each { |item| item.destroy } + @inarray['tags'].each { |item| + newitem = Tag.new(item.ivars['attributes']) + newitem.created_at = adjust_time(item.ivars['attributes']['created_at']) + newitem.save + translate_tag[item.ivars['attributes']['id'].to_i] = newitem.id + } + + # taggings + current_user.taggings.each { |item| item.destroy } + @inarray['taggings'].each { |item| + newitem = Tagging.new(item.ivars['attributes']) + newitem.user_id = current_user.id + newitem.tag_id = translate_tag[newitem.tag_id] + case newitem.taggable_type + when 'Todo' : newitem.taggable_id = translate_todo[newitem.taggable_id] + else newitem.taggable_id = 0 + end + newitem.save + } + + # notes + current_user.notes.each { |item| item.destroy } + @inarray['notes'].each { |item| + newitem = Note.new(item.ivars['attributes']) + newitem.id = item.ivars['attributes']['id'] + newitem.user_id = current_user.id + newitem.project_id = translate_project[newitem.project_id] + newitem.created_at = adjust_time(item.ivars['attributes']['created_at']) + newitem.save + } + end end diff --git a/app/views/data/index.html.erb b/app/views/data/index.html.erb index 5fb93294..8f7aec2c 100644 --- a/app/views/data/index.html.erb +++ b/app/views/data/index.html.erb @@ -3,7 +3,7 @@

Exporting data

You can choose between the following formats:

@@ -33,5 +33,12 @@

- - \ No newline at end of file + +
+
+

Importing data

+

Curently there is a experimental support for importing YAML files. Beware: all your current data will be destroyed before importing the YAML file, so if you have access to the database, we strongly reccoment backing up the database right now in case that anything goes wrong.

+

<%= link_to "Start import", :controller => 'data', :action => 'yaml_form' %>.

+
+ +
diff --git a/app/views/data/yaml_form.html.erb b/app/views/data/yaml_form.html.erb index 444613d7..d3be8c1e 100644 --- a/app/views/data/yaml_form.html.erb +++ b/app/views/data/yaml_form.html.erb @@ -1,19 +1,17 @@
-
-
-

Paste the contents of the YAML file you exported into the text box below:

+
+
+

Beware: all your current data will be destroyed before importing the YAML file, so if you have access to the database, we strongly reccoment backing up the database right now in case that anything goes wrong.

+

Paste the contents of the YAML file you exported into the text box below:

- -

-<% form_for :import, @import, :url => {:controller => 'data', :action => 'yaml_import'} do |f| %> - <%= f.text_area :yaml %>
- -<% end %> -

- -
+

+ <% form_for :import, @import, :url => {:controller => 'data', :action => 'yaml_import'} do |f| %> + <%= f.text_area :yaml %>
+ + <% end %> +

+
- -
\ No newline at end of file +
diff --git a/app/views/data/yaml_import.html.erb b/app/views/data/yaml_import.html.erb index cf79230b..2e9fcd5f 100644 --- a/app/views/data/yaml_import.html.erb +++ b/app/views/data/yaml_import.html.erb @@ -1 +1,7 @@ -

Import was successful

\ No newline at end of file +<% if !(@errmessage == '') %> +

There were these errors: +

<%= @errmessage  %>
+

+<% else %> +

Import was successful.

+<% end %> diff --git a/public/.htaccess b/public/.htaccess index d579b5cc..3b66fccd 100644 --- a/public/.htaccess +++ b/public/.htaccess @@ -31,7 +31,7 @@ RewriteEngine On RewriteRule ^$ index.html [QSA] RewriteRule ^([^.]+)$ $1.html [QSA] RewriteCond %{REQUEST_FILENAME} !-f -RewriteRule ^(.*)$ dispatch.cgi [QSA,L] +RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] # In case Rails experiences terminal errors # Instead of displaying this message you can supply a file here which will be rendered instead From 88159d3f6ad4b0d0aa4d6b69c94b75d645cd2ecc Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Fri, 5 Sep 2008 17:41:30 +0200 Subject: [PATCH 02/44] add recurring todos to export to yml and xml --- app/controllers/data_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/data_controller.rb b/app/controllers/data_controller.rb index 47a85fd6..884c1496 100644 --- a/app/controllers/data_controller.rb +++ b/app/controllers/data_controller.rb @@ -24,6 +24,7 @@ class DataController < ApplicationController all_tables['tags'] = current_user.tags.find(:all) all_tables['taggings'] = current_user.taggings.find(:all) all_tables['notes'] = current_user.notes.find(:all) + all_tables['recurring_todos'] = current_user.recurring_todos.find(:all) result = all_tables.to_yaml result.gsub!(/\n/, "\r\n") # TODO: general functionality for line endings @@ -81,6 +82,7 @@ class DataController < ApplicationController result << current_user.tags.find(:all).to_xml(:skip_instruct => true) result << current_user.taggings.find(:all).to_xml(:skip_instruct => true) result << current_user.notes.find(:all).to_xml(:skip_instruct => true) + result << current_user.recurring_todos.find(:all).to_xml(:skip_instruct => true) send_data(result, :filename => "tracks_backup.xml", :type => 'text/xml') end From 3a56d2bdd2ad86718f263bc5d1263757d844bbaa Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Fri, 5 Sep 2008 17:53:18 +0200 Subject: [PATCH 03/44] clean up export controller a bit. also some cleanups in the view --- app/controllers/data_controller.rb | 69 ++++++++++++----------- app/views/data/index.html.erb | 89 ++++++++++++++++-------------- app/views/data/yaml_form.html.erb | 34 +++++++----- 3 files changed, 101 insertions(+), 91 deletions(-) diff --git a/app/controllers/data_controller.rb b/app/controllers/data_controller.rb index 884c1496..ba997607 100644 --- a/app/controllers/data_controller.rb +++ b/app/controllers/data_controller.rb @@ -3,7 +3,7 @@ class DataController < ApplicationController require 'csv' def index - @page_title = "TRACKS::Export" + @page_title = "TRACKS::Export" end def import @@ -35,21 +35,21 @@ class DataController < ApplicationController content_type = 'text/csv' CSV::Writer.generate(result = "") do |csv| csv << ["id", "Context", "Project", "Description", "Notes", "Tags", - "Created at", "Due", "Completed at", "User ID", "Show from", - "state"] + "Created at", "Due", "Completed at", "User ID", "Show from", + "state"] current_user.todos.find(:all, :include => [:context, :project]).each do |todo| - # Format dates in ISO format for easy sorting in spreadsheet - # Print context and project names for easy viewing + # Format dates in ISO format for easy sorting in spreadsheet Print + # context and project names for easy viewing csv << [todo.id, todo.context.name, - todo.project_id = todo.project_id.nil? ? "" : todo.project.name, - todo.description, - todo.notes, todo.tags.collect{|t| t.name}.join(', '), - todo.created_at.to_formatted_s(:db), - todo.due = todo.due? ? todo.due.to_formatted_s(:db) : "", - todo.completed_at = todo.completed_at? ? todo.completed_at.to_formatted_s(:db) : "", - todo.user_id, - todo.show_from = todo.show_from? ? todo.show_from.to_formatted_s(:db) : "", - todo.state] + todo.project_id = todo.project_id.nil? ? "" : todo.project.name, + todo.description, + todo.notes, todo.tags.collect{|t| t.name}.join(', '), + todo.created_at.to_formatted_s(:db), + todo.due = todo.due? ? todo.due.to_formatted_s(:db) : "", + todo.completed_at = todo.completed_at? ? todo.completed_at.to_formatted_s(:db) : "", + todo.user_id, + todo.show_from = todo.show_from? ? todo.show_from.to_formatted_s(:db) : "", + todo.state] end end send_data(result, :filename => "todos.csv", :type => content_type) @@ -59,16 +59,17 @@ class DataController < ApplicationController content_type = 'text/csv' CSV::Writer.generate(result = "") do |csv| csv << ["id", "User ID", "Project", "Note", - "Created at", "Updated at"] - # had to remove project include because it's association order is leaking through - # and causing an ambiguous column ref even with_exclusive_scope didn't seem to help -JamesKebinger + "Created at", "Updated at"] + # had to remove project include because it's association order is leaking + # through and causing an ambiguous column ref even with_exclusive_scope + # didn't seem to help -JamesKebinger current_user.notes.find(:all,:order=>"notes.created_at").each do |note| - # Format dates in ISO format for easy sorting in spreadsheet - # Print context and project names for easy viewing + # Format dates in ISO format for easy sorting in spreadsheet Print + # context and project names for easy viewing csv << [note.id, note.user_id, - note.project_id = note.project_id.nil? ? "" : note.project.name, - note.body, note.created_at.to_formatted_s(:db), - note.updated_at.to_formatted_s(:db)] + note.project_id = note.project_id.nil? ? "" : note.project.name, + note.body, note.created_at.to_formatted_s(:db), + note.updated_at.to_formatted_s(:db)] end end send_data(result, :filename => "notes.csv", :type => content_type) @@ -104,7 +105,6 @@ class DataController < ApplicationController @inarray = YAML::load(params['import']['yaml']) # arrays to handle id translations - # contexts translate_context = Hash.new translate_context[nil] = nil @@ -153,18 +153,18 @@ class DataController < ApplicationController # state + dates case item.ivars['attributes']['state'] - when 'active' : newitem.activate! - when 'project_hidden' : newitem.hide! - when 'completed' - newitem.complete! - newitem.completed_at = adjust_time(item.ivars['attributes']['completed_at']) - when 'deferred' : newitem.defer! + when 'active' then newitem.activate! + when 'project_hidden' then newitem.hide! + when 'completed' + newitem.complete! + newitem.completed_at = adjust_time(item.ivars['attributes']['completed_at']) + when 'deferred' then newitem.defer! end newitem.created_at = adjust_time(item.ivars['attributes']['created_at']) newitem.save(false) } - #tags + # tags translate_tag = Hash.new translate_tag[nil] = nil current_user.tags.each { |item| item.destroy } @@ -182,8 +182,8 @@ class DataController < ApplicationController newitem.user_id = current_user.id newitem.tag_id = translate_tag[newitem.tag_id] case newitem.taggable_type - when 'Todo' : newitem.taggable_id = translate_todo[newitem.taggable_id] - else newitem.taggable_id = 0 + when 'Todo' then newitem.taggable_id = translate_todo[newitem.taggable_id] + else newitem.taggable_id = 0 end newitem.save } @@ -198,7 +198,6 @@ class DataController < ApplicationController newitem.created_at = adjust_time(item.ivars['attributes']['created_at']) newitem.save } - end - -end + +end \ No newline at end of file diff --git a/app/views/data/index.html.erb b/app/views/data/index.html.erb index 8f7aec2c..5ed8adaf 100644 --- a/app/views/data/index.html.erb +++ b/app/views/data/index.html.erb @@ -1,44 +1,49 @@ -
-
-

Exporting data

-

You can choose between the following formats:

-
    -
  • YAML: Best for exporting data.
    Please note that importing YAML files is currently supported only in experimentally. Do not rely on it for backing up critical data.
  • -
  • CSV: Best for importing into spreadsheet or data analysis software
  • -
  • XML: Best for importing or repurposing the data
  • -
-
- -

- - - - - - - - - - - - - - - - - - - - - -
DescriptionDownload link
YAML file containing all your actions, contexts, projects, tags and notes<%= link_to "YAML file", :controller => 'data', :action => 'yaml_export' %>
CSV file containing all of your actions, with named contexts and projects<%= link_to "CSV file (actions, contexts and projects)", :controller => 'data', :action => 'csv_actions' %>
CSV file containing all your notes<%= link_to "CSV file (notes only)", :controller => 'data', :action => 'csv_notes' %>
XML file containing all your actions, contexts, projects, tags and notes<%= link_to "XML file (actions only)", :controller => 'data', :action => 'xml_export' %>
-

- -
-
-

Importing data

-

Curently there is a experimental support for importing YAML files. Beware: all your current data will be destroyed before importing the YAML file, so if you have access to the database, we strongly reccoment backing up the database right now in case that anything goes wrong.

-

<%= link_to "Start import", :controller => 'data', :action => 'yaml_form' %>.

+
+
+
+

Importing data

+

Curently there is a experimental support for importing YAML files. + Beware: all your current data will be destroyed before importing the YAML + file, so if you have access to the database, we strongly recomment backing up + the database right now in case that anything goes wrong. +

+

<%= link_to "Start import", :controller => 'data', :action => 'yaml_form' %>.

+

Exporting data

+

You can choose between the following formats:

+
    +
  • YAML: Best for exporting data.
    Please note that importing YAML files is currently supported only in experimentally. Do not rely on it for backing up critical data.
  • +
  • CSV: Best for importing into spreadsheet or data analysis software
  • +
  • XML: Best for importing or repurposing the data
  • +
+
+

+ + + + + + + + + + + + + + + + + + + + + +
DescriptionDownload link
YAML file containing all your actions, contexts, projects, tags and notes<%= link_to "YAML file", :controller => 'data', :action => 'yaml_export' %>
CSV file containing all of your actions, with named contexts and projects<%= link_to "CSV file (actions, contexts and projects)", :controller => 'data', :action => 'csv_actions' %>
CSV file containing all your notes<%= link_to "CSV file (notes only)", :controller => 'data', :action => 'csv_notes' %>
XML file containing all your actions, contexts, projects, tags and notes<%= link_to "XML file (actions only)", :controller => 'data', :action => 'xml_export' %>
+
-
+ diff --git a/app/views/data/yaml_form.html.erb b/app/views/data/yaml_form.html.erb index d3be8c1e..edc13098 100644 --- a/app/views/data/yaml_form.html.erb +++ b/app/views/data/yaml_form.html.erb @@ -1,17 +1,23 @@
-
-
-

Beware: all your current data will be destroyed before importing the YAML file, so if you have access to the database, we strongly reccoment backing up the database right now in case that anything goes wrong.

-

Paste the contents of the YAML file you exported into the text box below:

-
-

- <% form_for :import, @import, :url => {:controller => 'data', :action => 'yaml_import'} do |f| %> - <%= f.text_area :yaml %>
- - <% end %> -

-
+
+
+

Beware: all your current data will be destroyed before importing + the YAML file, so if you have access to the database, we strongly recommend + backing up the database right now in case that anything goes wrong. +

+

Paste the contents of the YAML file you exported into the text box below:

+
+

+ <% form_for :import, @import, :url => {:controller => 'data', :action => 'yaml_import'} do |f| %> + <%= f.text_area :yaml %>
+ + <% end %> +

+
-
-
+ From 1d64ca0f34f30fdfde21f16e46624ac9f429de49 Mon Sep 17 00:00:00 2001 From: Reinier Balt Date: Fri, 5 Sep 2008 18:01:22 +0200 Subject: [PATCH 04/44] move nifty js calls to respective pages to reduce js execution a bit (on every page) --- app/views/contexts/index.html.erb | 7 ++++++- app/views/layouts/standard.html.erb | 3 --- app/views/projects/index.html.erb | 8 +++++++- app/views/recurring_todos/index.html.erb | 6 ++++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/views/contexts/index.html.erb b/app/views/contexts/index.html.erb index 846d01ca..ef915ac1 100644 --- a/app/views/contexts/index.html.erb +++ b/app/views/contexts/index.html.erb @@ -53,4 +53,9 @@
<% sortable_element 'list-contexts', get_listing_sortable_options --%> \ No newline at end of file +-%> + diff --git a/app/views/layouts/standard.html.erb b/app/views/layouts/standard.html.erb index 59f34550..21c9cdc4 100644 --- a/app/views/layouts/standard.html.erb +++ b/app/views/layouts/standard.html.erb @@ -16,9 +16,6 @@ diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb index b2108140..e8c7ec54 100644 --- a/app/views/projects/index.html.erb +++ b/app/views/projects/index.html.erb @@ -61,4 +61,10 @@
- \ No newline at end of file + + + diff --git a/app/views/recurring_todos/index.html.erb b/app/views/recurring_todos/index.html.erb index 936773d8..40b586c5 100644 --- a/app/views/recurring_todos/index.html.erb +++ b/app/views/recurring_todos/index.html.erb @@ -40,3 +40,9 @@ apply_behaviour "#recurring_edit_period:click", "TracksForm.hide_all_edit_recurring(); $('recurring_edit_'+TracksForm.get_edit_period()).show();" -%> + + From 8783beb280483d32442705a0b3a8486ba726b178 Mon Sep 17 00:00:00 2001 From: epall Date: Wed, 4 Jun 2008 18:16:06 -0700 Subject: [PATCH 05/44] Added SMS and MMS gateway that can handle a variety of messages formats along with a set of testes for it. NOTE: this is the first patch submitted on ticket --- app/models/preference.rb | 1 + app/models/sms_gateway.rb | 29 ++ app/views/preferences/edit.html.erb | 4 + app/views/preferences/index.html.erb | 2 + db/migrate/039_add_sms_to_preference.rb | 11 + test/fixtures/contexts.yml | 9 + test/fixtures/preferences.yml | 20 ++ test/fixtures/sample_mms.txt | 374 +++++++++++++++++++++++ test/fixtures/sample_sms.txt | 12 + test/fixtures/users.yml | 10 + test/functional/users_controller_test.rb | 2 +- test/integration/users_xml_api_test.rb | 2 +- test/unit/sms_gateway_test.rb | 51 ++++ 13 files changed, 525 insertions(+), 2 deletions(-) create mode 100644 app/models/sms_gateway.rb create mode 100644 db/migrate/039_add_sms_to_preference.rb create mode 100644 test/fixtures/sample_mms.txt create mode 100644 test/fixtures/sample_sms.txt create mode 100644 test/unit/sms_gateway_test.rb diff --git a/app/models/preference.rb b/app/models/preference.rb index 6b76fd97..b61b1566 100644 --- a/app/models/preference.rb +++ b/app/models/preference.rb @@ -1,5 +1,6 @@ class Preference < ActiveRecord::Base belongs_to :user + belongs_to :sms_context, :class_name => 'Context' def self.due_styles { :due_in_n_days => 0, :due_on => 1} diff --git a/app/models/sms_gateway.rb b/app/models/sms_gateway.rb new file mode 100644 index 00000000..0034a4c8 --- /dev/null +++ b/app/models/sms_gateway.rb @@ -0,0 +1,29 @@ +class SMSGateway < ActionMailer::Base + CONTEXT_NAME = 'Inbox' + def receive(email) + user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip]) + context = user.prefs.sms_context + + description = nil + notes = nil + + if email.content_type == "multipart/related" + description = email.subject + body_part = email.parts.find{|m| m.content_type == "text/plain"} + notes = body_part.body.strip + else + if email.subject.empty? + description = email.body.strip + notes = nil + else + description = email.subject.strip + notes = email.body.strip + end + end + + unless user.todos.find(:first, :conditions => {:description => description}) + # stupid T-Mobile often sends the same message multiple times + todo = user.todos.create(:context => context, :description => description, :notes => notes) + end + end +end diff --git a/app/views/preferences/edit.html.erb b/app/views/preferences/edit.html.erb index 86bbac6e..28171a7e 100644 --- a/app/views/preferences/edit.html.erb +++ b/app/views/preferences/edit.html.erb @@ -19,6 +19,8 @@
  • refresh: automatic refresh interval for each of the pages (in minutes)
  • verbose action descriptor: when true, show project/context name in action listing; when false show [P]/[C] with tool tips
  • mobile todos per page: the maximum number of actions to show on a single page in the mobile view
  • +
  • SMS email: the email address of your cell phone for sending text messages to your Tracks account
  • +
  • SMS context: the context to which tasks sent in via SMS should be added
  • @@ -67,6 +69,8 @@ <%= row_with_text_field('refresh') %> <%= row_with_select_field("verbose_action_descriptors") %> <%= row_with_text_field("mobile_todos_per_page") %> + <%= row_with_text_field("sms_email") %> + <%= table_row("sms_context", false) { select('prefs', 'sms_context_id', current_user.contexts.map{|c| [c.name, c.id]}) } %> <%= submit_tag "Update" %> <%= link_to "Cancel", :action => 'index' %> diff --git a/app/views/preferences/index.html.erb b/app/views/preferences/index.html.erb index 985834dd..d86dccf1 100644 --- a/app/views/preferences/index.html.erb +++ b/app/views/preferences/index.html.erb @@ -28,6 +28,8 @@
  • Refresh interval (in minutes): <%= prefs.refresh %>
  • Verbose action descriptors: <%= prefs.verbose_action_descriptors %>
  • Actions per page (Mobile View): <%= prefs.mobile_todos_per_page %>
  • +
  • SMS email address: <%= prefs.sms_email %>
  • +
  • SMS context: <%= prefs.sms_context.nil? ? "None" : prefs.sms_context.name %>
  • <%= link_to "Edit preferences »", { :controller => 'preferences', :action => 'edit'}, :class => 'edit_link' %> diff --git a/db/migrate/039_add_sms_to_preference.rb b/db/migrate/039_add_sms_to_preference.rb new file mode 100644 index 00000000..61f7e05a --- /dev/null +++ b/db/migrate/039_add_sms_to_preference.rb @@ -0,0 +1,11 @@ +class AddSmsToPreference < ActiveRecord::Migration + def self.up + add_column :preferences, :sms_email, :string + add_column :preferences, :sms_context_id, :integer + end + + def self.down + remove_column :preferences, :sms_context_id + remove_column :preferences, :sms_email + end +end diff --git a/test/fixtures/contexts.yml b/test/fixtures/contexts.yml index 0991767f..c9d3d5e0 100644 --- a/test/fixtures/contexts.yml +++ b/test/fixtures/contexts.yml @@ -113,3 +113,12 @@ someday_maybe: user_id: 1 created_at: <%= today %> updated_at: <%= today %> + +inbox: + id: 13 + name: Inbox + position: 1 + hide: false + user_id: 4 + created_at: <%= today %> + updated_at: <%= today %> diff --git a/test/fixtures/preferences.yml b/test/fixtures/preferences.yml index 380d1dc7..cad91d12 100644 --- a/test/fixtures/preferences.yml +++ b/test/fixtures/preferences.yml @@ -34,3 +34,23 @@ other_user_prefs: time_zone: "London" verbose_action_descriptors: false show_project_on_todo_done: true + +sms_user_prefs: + id: 3 + user_id: 4 + staleness_starts: 7 + date_format: "%d/%m/%Y" + title_date_format: "%A, %d %B %Y" + show_number_completed: 5 + show_completed_projects_in_sidebar: true + show_hidden_contexts_in_sidebar: true + show_hidden_projects_in_sidebar: true + admin_email: butshesagirl@rousette.org.uk + week_starts: 1 + due_style: 0 + refresh: 0 + time_zone: "London" + verbose_action_descriptors: false + show_project_on_todo_done: true + sms_email: 5555555555@tmomail.net + sms_context_id: 13 \ No newline at end of file diff --git a/test/fixtures/sample_mms.txt b/test/fixtures/sample_mms.txt new file mode 100644 index 00000000..eb5e7443 --- /dev/null +++ b/test/fixtures/sample_mms.txt @@ -0,0 +1,374 @@ +Return-Path: <15555555555/TYPE=PLMN@tmomail.net> +Date: Fri, 6 Jun 2008 21:38:26 -0400 +From: 5555555555@tmomail.net +To: gtd@tracks.com +Message-ID: <3645873.13759311212802713215.JavaMail.mms@rlyatl28> +Subject: This is the subject +MIME-Version: 1.0 +Content-Type: multipart/related; type="text/html"; + boundary="----=_Part_1240237_22156211.1212802713213" +X-UA: treo_600 +Importance: Normal +X-Mms-Sender-Visibility: Show +X-MMS-Message-Type: MM4_forward.REQ +X-Priority: 3 +X-Proofpoint-Spam-Reason: safe + +------=_Part_1240237_22156211.1212802713213 +Content-Type: text/html +Content-Transfer-Encoding: quoted-printable +Content-ID: <0000> +Content-Location:mms.smil +Content-Disposition: inline + + + + T-Mobile=20 + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    This is the message body
    =20 + +

    <= +/td> +
    + + + +------=_Part_1240237_22156211.1212802713213 +Content-Type: text/plain; charset=utf-8; name=text.txt +Content-Transfer-Encoding: 7bit +Content-ID: <133> +Content-Location: text.txt +Content-Disposition: inline + +This is the message body +------=_Part_1240237_22156211.1212802713213 +Content-Type: image/gif; name=dottedline350.gif +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename=dottedline350.gif +Content-ID: + +R0lGODlhXgEBAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgICAgMDAwP8AAAD/AP//AAAA//8A/wD/ +/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAZgAAmQAAzAAA/wAzAAAzMwAzZgAzmQAzzAAz/wBm +AABmMwBmZgBmmQBmzABm/wCZAACZMwCZZgCZmQCZzACZ/wDMAADMMwDMZgDMmQDMzADM/wD/AAD/ +MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMzADMzMzMzZjMzmTMzzDMz/zNmADNmMzNm +ZjNmmTNmzDNm/zOZADOZMzOZZjOZmTOZzDOZ/zPMADPMMzPMZjPMmTPMzDPM/zP/ADP/MzP/ZjP/ +mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2YzAGYzM2YzZmYzmWYzzGYz/2ZmAGZmM2ZmZmZmmWZm +zGZm/2aZAGaZM2aZZmaZmWaZzGaZ/2bMAGbMM2bMZmbMmWbMzGbM/2b/AGb/M2b/Zmb/mWb/zGb/ +/5kAAJkAM5kAZpkAmZkAzJkA/5kzAJkzM5kzZpkzmZkzzJkz/5lmAJlmM5lmZplmmZlmzJlm/5mZ +AJmZM5mZZpmZmZmZzJmZ/5nMAJnMM5nMZpnMmZnMzJnM/5n/AJn/M5n/Zpn/mZn/zJn//8wAAMwA +M8wAZswAmcwAzMwA/8wzAMwzM8wzZswzmcwzzMwz/8xmAMxmM8xmZsxmmcxmzMxm/8yZAMyZM8yZ +ZsyZmcyZzMyZ/8zMAMzMM8zMZszMmczMzMzM/8z/AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8A +mf8AzP8A//8zAP8zM/8zZv8zmf8zzP8z//9mAP9mM/9mZv9mmf9mzP9m//+ZAP+ZM/+ZZv+Zmf+Z +zP+Z///MAP/MM//MZv/Mmf/MzP/M////AP//M///Zv//mf//zP///yH5BAEAABAALAAAAABeAQEA +AAg1AFP9+yeQ4MCCCA8qNMgwYcOFDiNCnPiwokSLFC9qzMgRo8eNHzuCHCmyZMiTJFGaTMlSYUAA +Ow== +------=_Part_1240237_22156211.1212802713213 +Content-Type: image/gif; name=dottedline600.gif +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename=dottedline600.gif +Content-ID: + +R0lGODlhWAIBAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgICAgMDAwP8AAAD/AP//AAAA//8A/wD/ +/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAZgAAmQAAzAAA/wAzAAAzMwAzZgAzmQAzzAAz/wBm +AABmMwBmZgBmmQBmzABm/wCZAACZMwCZZgCZmQCZzACZ/wDMAADMMwDMZgDMmQDMzADM/wD/AAD/ +MwD/ZgD/mQD/zAD//zMAADMAMzMAZjMAmTMAzDMA/zMzADMzMzMzZjMzmTMzzDMz/zNmADNmMzNm +ZjNmmTNmzDNm/zOZADOZMzOZZjOZmTOZzDOZ/zPMADPMMzPMZjPMmTPMzDPM/zP/ADP/MzP/ZjP/ +mTP/zDP//2YAAGYAM2YAZmYAmWYAzGYA/2YzAGYzM2YzZmYzmWYzzGYz/2ZmAGZmM2ZmZmZmmWZm +zGZm/2aZAGaZM2aZZmaZmWaZzGaZ/2bMAGbMM2bMZmbMmWbMzGbM/2b/AGb/M2b/Zmb/mWb/zGb/ +/5kAAJkAM5kAZpkAmZkAzJkA/5kzAJkzM5kzZpkzmZkzzJkz/5lmAJlmM5lmZplmmZlmzJlm/5mZ +AJmZM5mZZpmZmZmZzJmZ/5nMAJnMM5nMZpnMmZnMzJnM/5n/AJn/M5n/Zpn/mZn/zJn//8wAAMwA +M8wAZswAmcwAzMwA/8wzAMwzM8wzZswzmcwzzMwz/8xmAMxmM8xmZsxmmcxmzMxm/8yZAMyZM8yZ +ZsyZmcyZzMyZ/8zMAMzMM8zMZszMmczMzMzM/8z/AMz/M8z/Zsz/mcz/zMz///8AAP8AM/8AZv8A +mf8AzP8A//8zAP8zM/8zZv8zmf8zzP8z//9mAP9mM/9mZv9mmf9mzP9m//+ZAP+ZM/+ZZv+Zmf+Z +zP+Z///MAP/MM//MZv/Mmf/MzP/M////AP//M///Zv//mf//zP///yH5BAEAABAALAAAAABYAgEA +AAhFAFP9+yeQ4MCCCA8qNMgwYcOFDiNCnPiwokSLFC9qzMgRo8eNHzuCHCmyZMiTJFGaTMlypUuV +MFvGfCmzJs2bM3Pa9BgQADs= +------=_Part_1240237_22156211.1212802713213 +Content-Type: image/gif; name=tmobilelogo.gif +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename=tmobilelogo.gif +Content-ID: + +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 +d3Pf9lg/d3Vf94INCAA7 +------=_Part_1240237_22156211.1212802713213 +Content-Type: image/gif; name=tmobilespace.gif +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename=tmobilespace.gif +Content-ID: + +R0lGODlhAQABAPcAAAAAAIAAAACAAICAAAAAgIAAgACAgMDAwMDcwKbK8AAAMzMAADMAMwAzMxYW +FhwcHCIiIikpKVVVVU1NTUJCQjk5Of98gP9QUNYAk8zs/+/Wxufn1q2pkDP/AGYAAJkAAMwAAAAz +ADMzAGYzAJkzAMwzAP8zAABmADNmAGZmAJlmAMxmAP9mAACZADOZAGaZAJmZAMyZAP+ZAADMADPM +AGbMAJnMAMzMAP/MAGb/AJn/AMz/AAD/MzMA/2YAM5kAM8wAM/8AMwAz/zMzM2YzM5kzM8wzM/8z +MwBmMzNmM2ZmM5lmM8xmM/9mMwCZMzOZM2aZM5mZM8yZM/+ZMwDMMzPMM2bMM5nMM8zMM//MMzP/ +M2b/M5n/M8z/M///MwAAZjMAZmYAZpkAZswAZv8AZgAzZjMzZmYzZpkzZswzZv8zZgBmZjNmZmZm +ZplmZsxmZgCZZjOZZmaZZpmZZsyZZv+ZZgDMZjPMZpnMZszMZv/MZgD/ZjP/Zpn/Zsz/Zv8AzMwA +/wCZmZkzmZkAmcwAmQAAmTMzmWYAmcwzmf8AmQBmmTNmmWYzmZlmmcxmmf8zmTOZmWaZmZmZmcyZ +mf+ZmQDMmTPMmWbMZpnMmczMmf/MmQD/mTP/mWbMmZn/mcz/mf//mQAAzDMAmWYAzJkAzMwAzAAz +mTMzzGYzzJkzzMwzzP8zzABmzDNmzGZmmZlmzMxmzP9mmQCZzDOZzGaZzJmZzMyZzP+ZzADMzDPM +zGbMzJnMzMzMzP/MzAD/zDP/zGb/mZn/zMz/zP//zDMAzGYA/5kA/wAzzDMz/2Yz/5kz/8wz//8z +/wBm/zNm/2ZmzJlm/8xm//9mzACZ/zOZ/2aZ/5mZ/8yZ//+Z/wDM/zPM/2bM/5nM/8zM///M/zP/ +/2b/zJn//8z///9mZmb/Zv//ZmZm//9m/2b//6UAIV9fX3d3d4aGhpaWlsvLy7KystfX193d3ePj +4+rq6vHx8fj4+P/78KCgpICAgP8AAAD/AP//AAAA//8A/wD//////ywAAAAAAQABAAAIBAD/BQQA +Ow== +------=_Part_1240237_22156211.1212802713213-- diff --git a/test/fixtures/sample_sms.txt b/test/fixtures/sample_sms.txt new file mode 100644 index 00000000..002cc204 --- /dev/null +++ b/test/fixtures/sample_sms.txt @@ -0,0 +1,12 @@ +Return-Path: <5555555555@tmomail.net> +Date: Tue, 3 Jun 2008 23:11:26 -0400 +From: 5555555555@tmomail.net +To: gtd@tracks.com +Message-ID: <6100602.65827251212549086388.JavaMail.imb@mgwatl02.cns.mms.com> +Subject: +MIME-Version: 1.0 +Content-Type: text/plain;charset=utf-8 +Content-Transfer-Encoding: 7bit +Importance: Normal + +This is a todo 4112093 \ No newline at end of file diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index ff277ba2..ef6f82c5 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -28,3 +28,13 @@ ldap_user: first_name: John last_name: Deere auth_type: ldap + +sms_user: + id: 4 + login: sms_user + crypted_password: <%= Digest::SHA1.hexdigest("#{Tracks::Config.salt}--sesame--") %> + token: <%= Digest::SHA1.hexdigest("sms_userSun Feb 19 14:42:45 GMT 20060.408173979260027") %> + is_admin: false + first_name: SMS + last_name: Tester + auth_type: database \ No newline at end of file diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb index 47677eb8..daad85d9 100644 --- a/test/functional/users_controller_test.rb +++ b/test/functional/users_controller_test.rb @@ -31,7 +31,7 @@ class UsersControllerTest < Test::Rails::TestCase get :index assert_response :success assert_equal "TRACKS::Manage Users", assigns['page_title'] - assert_equal 3, assigns['total_users'] + assert_equal 4, assigns['total_users'] assert_equal "/users", session['return-to'] end diff --git a/test/integration/users_xml_api_test.rb b/test/integration/users_xml_api_test.rb index 57d14d9f..ed26d194 100644 --- a/test/integration/users_xml_api_test.rb +++ b/test/integration/users_xml_api_test.rb @@ -78,7 +78,7 @@ class UsersXmlApiTest < ActionController::IntegrationTest get '/users.xml', {}, basic_auth_headers() assert_response :success assert_tag :tag => "users", - :children => { :count => 3, :only => { :tag => "user" } } + :children => { :count => 4, :only => { :tag => "user" } } assert_no_tag :tag => "password" end diff --git a/test/unit/sms_gateway_test.rb b/test/unit/sms_gateway_test.rb new file mode 100644 index 00000000..24660dbb --- /dev/null +++ b/test/unit/sms_gateway_test.rb @@ -0,0 +1,51 @@ +require File.dirname(__FILE__) + '/../test_helper' + +class SMSGatewayTest < Test::Rails::TestCase + fixtures :users, :contexts + + def setup + @user = users(:sms_user) + @inbox = contexts(:inbox) + end + + def load_message(filename) + SMSGateway.receive(File.read(File.join(RAILS_ROOT, 'test', 'fixtures', filename))) + end + + def test_sms_with_no_subject + todo_count = Todo.count + + load_message('sample_sms.txt') + # assert some stuff about it being created + assert_equal(todo_count+1, Todo.count) + + message_todo = Todo.find(:first, :conditions => {:description => "This is a todo 4112093"}) + assert_not_nil(message_todo) + + assert_equal(@inbox, message_todo.context) + assert_equal(@user, message_todo.user) + end + + def test_double_sms + todo_count = Todo.count + load_message('sample_sms.txt') + load_message('sample_sms.txt') + assert_equal(todo_count+1, Todo.count) + end + + def test_mms_with_subject + todo_count = Todo.count + + load_message('sample_mms.txt') + + # assert some stuff about it being created + assert_equal(todo_count+1, Todo.count) + + message_todo = Todo.find(:first, :conditions => {:description => "This is the subject"}) + assert_not_nil(message_todo) + + assert_equal(@inbox, message_todo.context) + assert_equal(@user, message_todo.user) + assert_equal("This is the message body", message_todo.notes) + end +end From 920507441dfa3d997564653f9051804b32746c27 Mon Sep 17 00:00:00 2001 From: epall Date: Sat, 7 Jun 2008 17:15:12 -0700 Subject: [PATCH 06/44] Didn't handle case of no user found gracefully --- app/models/sms_gateway.rb | 2 ++ test/unit/sms_gateway_test.rb | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/app/models/sms_gateway.rb b/app/models/sms_gateway.rb index 0034a4c8..6c81e456 100644 --- a/app/models/sms_gateway.rb +++ b/app/models/sms_gateway.rb @@ -2,6 +2,8 @@ class SMSGateway < ActionMailer::Base CONTEXT_NAME = 'Inbox' def receive(email) user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip]) + logger.info "Receiving SMS task from #{email.from[0].strip} For user #{user.nil? nil : user.login}" + return if user.nil? context = user.prefs.sms_context description = nil diff --git a/test/unit/sms_gateway_test.rb b/test/unit/sms_gateway_test.rb index 24660dbb..1c3879c8 100644 --- a/test/unit/sms_gateway_test.rb +++ b/test/unit/sms_gateway_test.rb @@ -48,4 +48,12 @@ class SMSGatewayTest < Test::Rails::TestCase assert_equal(@user, message_todo.user) assert_equal("This is the message body", message_todo.notes) end + + def test_no_user + todo_count = Todo.count + badmessage = File.read(File.join(RAILS_ROOT, 'test', 'fixtures', 'sample_sms.txt')) + badmessage.gsub!("5555555555", "notauser") + SMSGateway.receive(badmessage) + assert_equal(todo_count, Todo.count) + end end From dc0c5bffa4ea88e35af911e0876ec9994760ba4b Mon Sep 17 00:00:00 2001 From: epall Date: Fri, 13 Jun 2008 10:27:58 -0700 Subject: [PATCH 07/44] MMS prepends a 1 that needs to get stripped --- app/models/sms_gateway.rb | 4 +++- test/fixtures/sample_mms.txt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models/sms_gateway.rb b/app/models/sms_gateway.rb index 6c81e456..563fa8a4 100644 --- a/app/models/sms_gateway.rb +++ b/app/models/sms_gateway.rb @@ -2,7 +2,9 @@ class SMSGateway < ActionMailer::Base CONTEXT_NAME = 'Inbox' def receive(email) user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip]) - logger.info "Receiving SMS task from #{email.from[0].strip} For user #{user.nil? nil : user.login}" + if user.nil? + user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", email.from[0].strip[1,100]]) + end return if user.nil? context = user.prefs.sms_context diff --git a/test/fixtures/sample_mms.txt b/test/fixtures/sample_mms.txt index eb5e7443..a532e3ab 100644 --- a/test/fixtures/sample_mms.txt +++ b/test/fixtures/sample_mms.txt @@ -1,6 +1,6 @@ Return-Path: <15555555555/TYPE=PLMN@tmomail.net> Date: Fri, 6 Jun 2008 21:38:26 -0400 -From: 5555555555@tmomail.net +From: 15555555555@tmomail.net To: gtd@tracks.com Message-ID: <3645873.13759311212802713215.JavaMail.mms@rlyatl28> Subject: This is the subject From 6210d3033d74c8e507058b2434dfed21eee90d8e Mon Sep 17 00:00:00 2001 From: epall Date: Sat, 14 Jun 2008 22:12:13 -0700 Subject: [PATCH 08/44] Modified sample_sms.txt to be subbed into in a sane way. Added ability to set context, due date, and show_from date from within an emailed message. --- app/models/sms_gateway.rb | 33 ++++++++++++++- test/fixtures/contexts.yml | 9 ++++ test/fixtures/sample_sms.txt | 2 +- test/unit/sms_gateway_test.rb | 78 ++++++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 4 deletions(-) diff --git a/app/models/sms_gateway.rb b/app/models/sms_gateway.rb index 563fa8a4..d7b3cefa 100644 --- a/app/models/sms_gateway.rb +++ b/app/models/sms_gateway.rb @@ -25,9 +25,38 @@ class SMSGateway < ActionMailer::Base end end - unless user.todos.find(:first, :conditions => {:description => description}) # stupid T-Mobile often sends the same message multiple times - todo = user.todos.create(:context => context, :description => description, :notes => notes) + return if user.todos.find(:first, :conditions => {:description => description}) + + # parse context + context_data = description.match(/^([^ ]*): (.*)/) + if context_data + context_name = context_data[1] + custom_context = user.contexts.find(:first, :conditions => {:name => context_name}) + if custom_context + context = custom_context + description = context_data[2] + end end + + # parse due date + due_regex = / ?due:([0-9\/-]{3,})/ + due_date = description.match(due_regex)[1] rescue nil + if due_date + #strip from description + description.sub!(due_regex, '').strip! + end + + # parse due date + show_regex = / ?show:([0-9\/-]{3,})/ + show_date = description.match(show_regex)[1] rescue nil + if show_date + #strip from description + description.sub!(show_regex, '').strip! + end + + # p "creating todo with description '#{description}', show from #{show_date}, context #{context.name}" + todo = user.todos.create(:context => context, :description => description, :notes => notes, :due => due_date, :show_from => show_date) + # p todo.validate end end diff --git a/test/fixtures/contexts.yml b/test/fixtures/contexts.yml index c9d3d5e0..49ce9d2f 100644 --- a/test/fixtures/contexts.yml +++ b/test/fixtures/contexts.yml @@ -122,3 +122,12 @@ inbox: user_id: 4 created_at: <%= today %> updated_at: <%= today %> + +anothercontext: + id: 14 + name: anothercontext + position: 2 + hide: false + user_id: 4 + created_at: <%= today %> + updated_at: <%= today %> diff --git a/test/fixtures/sample_sms.txt b/test/fixtures/sample_sms.txt index 002cc204..86b449a3 100644 --- a/test/fixtures/sample_sms.txt +++ b/test/fixtures/sample_sms.txt @@ -9,4 +9,4 @@ Content-Type: text/plain;charset=utf-8 Content-Transfer-Encoding: 7bit Importance: Normal -This is a todo 4112093 \ No newline at end of file +message_content \ No newline at end of file diff --git a/test/unit/sms_gateway_test.rb b/test/unit/sms_gateway_test.rb index 1c3879c8..6852566e 100644 --- a/test/unit/sms_gateway_test.rb +++ b/test/unit/sms_gateway_test.rb @@ -19,7 +19,7 @@ class SMSGatewayTest < Test::Rails::TestCase # assert some stuff about it being created assert_equal(todo_count+1, Todo.count) - message_todo = Todo.find(:first, :conditions => {:description => "This is a todo 4112093"}) + message_todo = Todo.find(:first, :conditions => {:description => "message_content"}) assert_not_nil(message_todo) assert_equal(@inbox, message_todo.context) @@ -56,4 +56,80 @@ class SMSGatewayTest < Test::Rails::TestCase SMSGateway.receive(badmessage) assert_equal(todo_count, Todo.count) end + + def test_direct_to_context + message = File.read(File.join(RAILS_ROOT, 'test', 'fixtures', 'sample_sms.txt')) + + valid_context_msg = message.gsub('message_content', 'anothercontext: this is a task') + invalid_context_msg = message.gsub('message_content', 'notacontext: this is a task') + + SMSGateway.receive(valid_context_msg) + valid_context_todo = Todo.find(:first, :conditions => {:description => "this is a task"}) + assert_not_nil(valid_context_todo) + assert_equal(contexts(:anothercontext), valid_context_todo.context) + + SMSGateway.receive(invalid_context_msg) + invalid_context_todo = Todo.find(:first, :conditions => {:description => 'notacontext: this is a task'}) + assert_not_nil(invalid_context_todo) + assert_equal(@inbox, invalid_context_todo.context) + end + + def test_due_date + message = File.read(File.join(RAILS_ROOT, 'test', 'fixtures', 'sample_sms.txt')) + + valid_due_msg1 = message.gsub('message_content', 'do something tomorrow due:6/15/2008') + valid_due_msg2 = message.gsub('message_content', 'do something tomorrow due:6/28/2008 and remember it!') + valid_due_msg3 = message.gsub('message_content', 'due:1/28/2008 funky!') + invalid_due_msg1 = message.gsub('message_content', 'do something tomorrow due:xxxx and remember it!') + + SMSGateway.receive(valid_due_msg1) + valid_due_todo1 = Todo.find(:first, :conditions => {:description => "do something tomorrow"}) + assert_not_nil(valid_due_todo1) + assert_equal(Date.civil(2008, 6, 15), valid_due_todo1.due) + + SMSGateway.receive(valid_due_msg2) + valid_due_todo2 = Todo.find(:first, :conditions => {:description => "do something tomorrow and remember it!"}) + assert_not_nil(valid_due_todo2) + assert_equal(Date.civil(2008, 6, 28), valid_due_todo2.due) + + SMSGateway.receive(valid_due_msg3) + valid_due_todo3 = Todo.find(:first, :conditions => {:description => "funky!"}) + assert_not_nil(valid_due_todo3) + assert_equal(Date.civil(2008, 1, 28), valid_due_todo3.due) + + SMSGateway.receive(invalid_due_msg1) + invalid_due_todo1 = Todo.find(:first, :conditions => {:description => "do something tomorrow due:xxxx and remember it!"}) + assert_not_nil(invalid_due_todo1) + assert_nil(invalid_due_todo1.due) + end + + def test_show_date + message = File.read(File.join(RAILS_ROOT, 'test', 'fixtures', 'sample_sms.txt')) + + valid_show_msg1 = message.gsub('message_content', "do something tomorrow show:#{Date.tomorrow.to_s}") + valid_show_msg2 = message.gsub('message_content', "do something next week show:#{Date.today.next_week.to_s} and remember it!") + valid_show_msg3 = message.gsub('message_content', "show:#{Date.tomorrow.to_s} alternative format") + invalid_show_msg1 = message.gsub('message_content', 'do something tomorrow show:xxxx and remember it!') + + SMSGateway.receive(valid_show_msg1) + valid_show_todo1 = Todo.find(:first, :conditions => {:description => "do something tomorrow"}) + assert_not_nil(valid_show_todo1) + assert_equal(Date.tomorrow, valid_show_todo1.show_from) + + SMSGateway.receive(valid_show_msg2) + valid_show_todo2 = Todo.find(:first, :conditions => {:description => "do something next week and remember it!"}) + assert_not_nil(valid_show_todo2) + assert_equal(Date.tomorrow.next_week, valid_show_todo2.show_from) + + SMSGateway.receive(valid_show_msg3) + valid_show_todo3 = Todo.find(:first, :conditions => {:description => "alternative format"}) + # p @user.todos.last + assert_not_nil(valid_show_todo3) + assert_equal(Date.tomorrow, valid_show_todo3.show_from) + + SMSGateway.receive(invalid_show_msg1) + invalid_show_todo1 = Todo.find(:first, :conditions => {:description => "do something tomorrow show:xxxx and remember it!"}) + assert_not_nil(invalid_show_todo1) + assert_nil(invalid_show_todo1.show_from) + end end From 98136d8ef277ab57f62b1472abaa9ffe199ff813 Mon Sep 17 00:00:00 2001 From: epall Date: Sun, 15 Jun 2008 08:56:07 -0700 Subject: [PATCH 09/44] Inconsistency in test. Why didn't I catch that? --- test/unit/sms_gateway_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/sms_gateway_test.rb b/test/unit/sms_gateway_test.rb index 6852566e..da6c121d 100644 --- a/test/unit/sms_gateway_test.rb +++ b/test/unit/sms_gateway_test.rb @@ -119,7 +119,7 @@ class SMSGatewayTest < Test::Rails::TestCase SMSGateway.receive(valid_show_msg2) valid_show_todo2 = Todo.find(:first, :conditions => {:description => "do something next week and remember it!"}) assert_not_nil(valid_show_todo2) - assert_equal(Date.tomorrow.next_week, valid_show_todo2.show_from) + assert_equal(Date.today.next_week, valid_show_todo2.show_from) SMSGateway.receive(valid_show_msg3) valid_show_todo3 = Todo.find(:first, :conditions => {:description => "alternative format"}) From 539fda21dcefcf90a69f5cbbe1ed1dafb3a807f8 Mon Sep 17 00:00:00 2001 From: epall Date: Mon, 16 Jun 2008 22:37:44 -0700 Subject: [PATCH 10/44] Preliminary defer buttons --- app/controllers/todos_controller.rb | 16 +++++++++++++++- app/helpers/todos_helper.rb | 4 ++++ app/views/todos/_todo.html.erb | 1 + public/stylesheets/standard.css | 6 +++++- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index ac0c251a..d4f95738 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -384,7 +384,21 @@ class TodosController < ApplicationController end end - private + def defer + @source_view = params['_source_view'] || 'todo' + numdays = params['days'].to_i + @todo = Todo.find(params[:id]) + @todo.show_from = (@todo.show_from || Time.now.to_date) + numdays.days + @saved = @todo.save + + respond_to do |format| + format.html { redirect_to :back } + format.js {render :action => 'update'} + end + end + + + private def get_todo_from_params @todo = current_user.todos.find(params['id']) diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index ec70762e..662c3d25 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -280,4 +280,8 @@ module TodosHelper image_tag("blank.png", :title =>"Star action", :class => class_str) end + def defer_link(days) + link_to_remote "+#{days}", :url => {:controller => 'todos', :action => 'defer', :id => @todo.id, :days => days, :_source_view => @source_view.underscore.gsub(/\s+/,'_')} + end + end diff --git a/app/views/todos/_todo.html.erb b/app/views/todos/_todo.html.erb index c03e64e6..8a4f70c2 100644 --- a/app/views/todos/_todo.html.erb +++ b/app/views/todos/_todo.html.erb @@ -18,6 +18,7 @@ <%= deferred_due_date %> <%= project_and_context_links( parent_container_type, :suppress_context => suppress_context, :suppress_project => suppress_project ) %> <%= render(:partial => "todos/toggle_notes", :locals => { :item => todo }) if todo.notes? %> + <%= defer_link(1) %> <%= defer_link(7) %>