diff --git a/tracks/app/controllers/context_controller.rb b/tracks/app/controllers/context_controller.rb index f1e9acee..9daf6440 100644 --- a/tracks/app/controllers/context_controller.rb +++ b/tracks/app/controllers/context_controller.rb @@ -11,9 +11,15 @@ class ContextController < ApplicationController # Main method for listing contexts # Set page title, and collect existing contexts in @contexts # + + def index + list + render_action "list" + end + def list @page_title = "List Contexts" - @contexts = Context.find_all + @contexts = Context.find_all( conditions = nil, "position ASC", limit = nil ) end @@ -33,7 +39,20 @@ class ContextController < ApplicationController redirect_to( :action => "list" ) end end - + + def new + expire_action(:controller => "context", :action => "list") + context = Context.new + context.attributes = @params["new_context"] + + if context.save + flash["confirmation"] = "Succesfully created context \"#{context.name}\"" + redirect_to( :action => "list" ) + else + flash["warning"] = "Couldn't add new context \"#{context.name}\"" + redirect_to( :action => "list" ) + end + end def edit expire_action(:controller => "context", :action => "list") @@ -56,10 +75,10 @@ class ContextController < ApplicationController # Filter the contexts to show just the one passed in the URL - # e.g. /context/show/ shows just . + # e.g. /context/show/ shows just . # def show - @context = Context.find(@params["id"]) + @context = Context.find_by_name(@params["id"].humanize) @projects = Project.find_all @page_title = "Context: #{@context.name.capitalize}" @not_done = Todo.find_all( "context_id=#{@context.id} AND done=0", "created ASC" ) @@ -74,15 +93,16 @@ class ContextController < ApplicationController expire_action(:controller => "context", :action => "list") item = Todo.new item.attributes = @params["new_item"] + where = Context.find_by_id(item.context_id) - back_to = item.context_id + back_to = urlize(where.name) if item.save flash["confirmation"] = "Succesfully added action \"#{item.description}\" to context" - redirect_to( :controller => "context", :action => "show", :id => "#{back_to}" ) + redirect_to( :controller => "context", :action => "show", :name => "#{back_to}") else flash["warning"] = "Could not add action \"#{item.description}\" to context" - redirect_to( :controller => "context", :action => "show", :id => "#{back_to}" ) + redirect_to( :controller => "context", :action => "show", :name => "#{back_to}" ) end end diff --git a/tracks/app/controllers/project_controller.rb b/tracks/app/controllers/project_controller.rb index 4f462a3d..122f61e5 100644 --- a/tracks/app/controllers/project_controller.rb +++ b/tracks/app/controllers/project_controller.rb @@ -8,6 +8,11 @@ class ProjectController < ApplicationController caches_action :list layout "standard" + def index + list + render_action "list" + end + # Main method for listing projects # Set page title, and collect existing projects in @projects # @@ -21,7 +26,7 @@ class ProjectController < ApplicationController # e.g. /project/show/ shows just . # def show - @project = Project.find(@params["id"]) + @project = Project.find_by_name(@params["name"].humanize) @places = Context.find_all @page_title = "Project: #{@project.name}" @not_done = Todo.find_all( "project_id=#{@project.id} AND done=0", "created DESC" ) @@ -65,6 +70,20 @@ class ProjectController < ApplicationController redirect_to( :action => "list" ) end end + + def new + expire_action(:controller => "project", :action => "list") + project = Project.new + project.name = @params["new_project"]["name"] + + if project.save + flash["confirmation"] = "Succesfully added project \"#{project.name}\"" + redirect_to( :action => "list" ) + else + flash["warning"] = "Couldn't add project \"#{project.name}\"" + redirect_to( :action => "list" ) + end + end # Called by a form button diff --git a/tracks/app/controllers/todo_controller.rb b/tracks/app/controllers/todo_controller.rb index 83142d0e..abc79ae7 100644 --- a/tracks/app/controllers/todo_controller.rb +++ b/tracks/app/controllers/todo_controller.rb @@ -10,12 +10,18 @@ class TodoController < ApplicationController # Main method for listing tasks # Set page title, and fill variables with contexts and done and not-done tasks # + + def index + list + render_action "list" + end + def list @page_title = "List tasks" @projects = Project.find_all @places = Context.find_all - @shown_places = Context.find_all_by_hide( 0, "name DESC") - @hidden_places = Context.find_all_by_hide( 1 ) + @shown_places = Context.find_all_by_hide( 0, "position ASC") + @hidden_places = Context.find_all_by_hide( 1, "position ASC" ) @done = Todo.find_all_by_done( 1, "completed DESC", 5 ) # Set count badge to number of not-done, not hidden context items diff --git a/tracks/app/helpers/application_helper.rb b/tracks/app/helpers/application_helper.rb index a57b17c9..2b7428d0 100644 --- a/tracks/app/helpers/application_helper.rb +++ b/tracks/app/helpers/application_helper.rb @@ -23,6 +23,10 @@ module ApplicationHelper def tag_object(object, tag) tagged = "<#{tag}>#{object}" end + + def urlize(name) + name.to_s.gsub(/ /, "_").downcase + end # Check due date in comparison to today's date diff --git a/tracks/app/models/context.rb b/tracks/app/models/context.rb index cb7eec1f..1c0abccd 100644 --- a/tracks/app/models/context.rb +++ b/tracks/app/models/context.rb @@ -1,4 +1,6 @@ class Context < ActiveRecord::Base + + acts_as_list has_many :todo, :dependent => true # Context name must not be empty diff --git a/tracks/app/models/project.rb b/tracks/app/models/project.rb index 0d4588d9..8af5f9f6 100644 --- a/tracks/app/models/project.rb +++ b/tracks/app/models/project.rb @@ -1,5 +1,7 @@ class Project < ActiveRecord::Base + has_many :todo, :dependent => true + acts_as_list # Project name must not be empty # and must be less than 255 bytes diff --git a/tracks/app/views/context/edit.rhtml b/tracks/app/views/context/edit.rhtml index 37a97497..1d22e1a2 100644 --- a/tracks/app/views/context/edit.rhtml +++ b/tracks/app/views/context/edit.rhtml @@ -1,10 +1,10 @@

Edit context

<%= error_messages_for 'context' %> -
+ <%= start_form_tag :controller=>"context", :action=>"update", :id=>@context.id %> <%= render_partial "edit_context", "@context" %> -
+ <%= end_form_tag %> <%= link_to 'Cancel', :action => 'list' %>
diff --git a/tracks/app/views/context/list.rhtml b/tracks/app/views/context/list.rhtml index 9d1d1164..bb5a680a 100644 --- a/tracks/app/views/context/list.rhtml +++ b/tracks/app/views/context/list.rhtml @@ -8,8 +8,9 @@ <% else %> <% end %> - <%= @context.id.to_s %> - <%= link_to( "#{@context.name.capitalize}", :action => "show", :id => @context.id ) %> + <%= @context.position.to_s %> + + <%= link_to( "#{@context.name.capitalize}", :action => "show", :name => urlize(@context.name) ) %> <% if @context.hide == 1 %> hidden @@ -26,7 +27,7 @@
-
+ <%= start_form_tag :controller=>'context', :action=>'new' %>
<%= text_field("new_context", "name") %>
@@ -35,8 +36,8 @@

-
+ <%= end_form_tag %>
<% if @flash["confirmation"] %>
<%= @flash["confirmation"] %>
<% end %> -<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> \ No newline at end of file +<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> diff --git a/tracks/app/views/context/show.rhtml b/tracks/app/views/context/show.rhtml index f84af211..908e6034 100644 --- a/tracks/app/views/context/show.rhtml +++ b/tracks/app/views/context/show.rhtml @@ -8,7 +8,7 @@

Other Contexts: <% for other_context in Context.find_all %> - <%= link_to( other_context.name.capitalize, { :controller => "context", :action => "show", :id => other_context.id } ) + " | " %> + <%= link_to( other_context.name.capitalize, { :controller => "context", :action => "show", :name => urlize(other_context.name) } ) + " | " %> <% end %>

@@ -27,13 +27,24 @@ <%= options_from_collection_for_select(@projects, "id", "name" ) %>
-
- <% now = Date.today %> -<%= date_select( "new_item", "due", :include_blank => true, :start_year => now.year, "tabindex" => 5 ) %> -
+
+ <%= text_field( "item", "due", "tabindex" => 5, "cols" => 10 ) %> + + +
+
<% if @flash["confirmation"] %>
<%= @flash["confirmation"] %>
<% end %> -<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> \ No newline at end of file +<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> diff --git a/tracks/app/views/layouts/standard.rhtml b/tracks/app/views/layouts/standard.rhtml index b1269b42..c7a1044e 100644 --- a/tracks/app/views/layouts/standard.rhtml +++ b/tracks/app/views/layouts/standard.rhtml @@ -8,17 +8,15 @@ <%= @page_title %> - + @@ -31,12 +29,12 @@
+ <%= start_form_tag :controller => 'project', :action => 'new' %>

<%= text_field( "new_project", "name" ) %>

-
+ <%= end_form_tag %>
<% if @flash["confirmation"] %>
<%= @flash["confirmation"] %>
<% end %> -<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> \ No newline at end of file +<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> diff --git a/tracks/app/views/project/show.rhtml b/tracks/app/views/project/show.rhtml index ed2b1c7c..ea19858f 100644 --- a/tracks/app/views/project/show.rhtml +++ b/tracks/app/views/project/show.rhtml @@ -14,7 +14,7 @@

Other Projects: <% for other_project in Project.find_all %> - <%= link_to( other_project.name.capitalize, { :controller => "project", :action => "show", :id => other_project.id } ) + " | " %> + <%= link_to( other_project.name.capitalize, { :controller => "project", :action => "show", :name => urlize(other_project.name) } ) + " | " %> <% end %>

@@ -33,13 +33,24 @@ <%= options_from_collection_for_select(@places, "id", "name" ) %>
-
- <% now = Date.today %> - <%= date_select( "new_item", "due", :include_blank => true, :start_year => now.year, "tabindex" => 5 ) %> -
+
+ <%= text_field( "item", "due", "tabindex" => 5, "cols" => 10 ) %> + + +
+
<% if @flash["confirmation"] %>
<%= @flash["confirmation"] %>
<% end %> -<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> \ No newline at end of file +<% if @flash["warning"] %>
<%= @flash["warning"] %>
<% end %> diff --git a/tracks/app/views/todo/_item.rhtml b/tracks/app/views/todo/_item.rhtml index 466ab782..e44ddf31 100644 --- a/tracks/app/views/todo/_item.rhtml +++ b/tracks/app/views/todo/_item.rhtml @@ -35,8 +35,19 @@ <% end %>
+
-<% now = Date.today %> -<%= date_select( "item", "due", :include_blank => true, :start_year => now.year, "tabindex" => 5 ) %> +<%= text_field( "item", "due", "tabindex" => 5, "cols" => 10 ) %> + +

diff --git a/tracks/app/views/todo/_not_done.rhtml b/tracks/app/views/todo/_not_done.rhtml index 0e8c2a06..707935ad 100644 --- a/tracks/app/views/todo/_not_done.rhtml +++ b/tracks/app/views/todo/_not_done.rhtml @@ -5,7 +5,7 @@ <%= due_date( @notdone_item.due ) %> <%= @notdone_item.description %> <% if @notdone_item.project_id %> - <%= link_to( "[P]", { :controller => "project", :action => "show", :id => @notdone_item.project_id }, :title => "View project: #{@notdone_item.project.name}" ) %> + <%= link_to( "[P]", { :controller => "project", :action => "show", :name => urlize(@notdone_item.project.name) }, :title => "View project: #{@notdone_item.project.name}" ) %> <% end %> <% if @notdone_item.notes? %> <%= "" + $notes_img + "" %> @@ -13,4 +13,4 @@ <%= "
" + m_notes + "
" %> <% end %> - \ No newline at end of file + diff --git a/tracks/app/views/todo/list.rhtml b/tracks/app/views/todo/list.rhtml index b6d89171..bec6e6ad 100644 --- a/tracks/app/views/todo/list.rhtml +++ b/tracks/app/views/todo/list.rhtml @@ -3,7 +3,7 @@ <% @not_done = Todo.find_all("done=0 AND context_id=#{@shown_place.id}", "created ASC") %> <% if !@not_done.empty? %>
-

<%= link_to( "#{@shown_place.name.capitalize}", :controller => "context", :action => "show", :id => @shown_place.id ) %>

+

<%= link_to( "#{@shown_place.name.capitalize}", :controller => "context", :action => "show", :name => urlize(@shown_place.name) ) %>

<%= render_collection_of_partials "not_done", @not_done %>
@@ -29,43 +29,14 @@

Hidden contexts: <% for @hidden_place in @hidden_places %> <% num = count_items(@hidden_place) %> - "show", :id => "#{@hidden_place.id}" ) %>"><%= @hidden_place.name.capitalize %> (<%= num %>) | + <%= link_to @hidden_place.name.capitalize, :controller=>"context", :action=> "show", :name=> urlize(@hidden_place.name) %> <% end %>

- -
- - - - -
- - - - - - - - - - - - - -
SunMonTueWedThuFriSat
-
-
<%= render_partial "todo/item", @item %> diff --git a/tracks/config/routes.rb b/tracks/config/routes.rb index 86027ebc..d4f4517a 100644 --- a/tracks/config/routes.rb +++ b/tracks/config/routes.rb @@ -9,9 +9,34 @@ ActionController::Routing::Routes.draw do |map| # Allow downloading Web Service WSDL as a file with an extension # instead of a file named 'wsdl' map.connect ':controller/service.wsdl', :action => 'wsdl' - + + # Index Route map.connect '', :controller => 'todo', :action => 'list' - + + # Login Routes + map.connect 'login', :controller => 'login', :action => 'login' + map.connect 'logout', :controller => 'login', :action => 'logout' + map.connect 'signup', :controller => 'login', :action => 'signup' + + # ToDo Routes + map.connect 'completed', :controller => 'todo', :action => 'completed' + map.connect 'delete/todo/:id', :controller =>'todo', :action => 'destroy' + + # Context Routes + map.connect 'contexts', :controller => 'context', :action => 'list' + map.connect 'add/context', :controller => 'context', :action => 'new' + map.connect 'context/:id', :controller=> 'context', :action => 'show' + map.connect 'context/:name', :controller => 'context', :action => 'show' + map.connect 'delete/context/:id', :controller => 'context', :action => 'destroy' + + # Projects Routes + map.connect 'projects', :controller => 'project', :action => 'list' + map.connect 'add/project', :controller => 'project', :action => 'new' + map.connect 'project/:name', :controller => 'project', :action => 'show' + map.connect 'project/:id', :controller => 'project', :action => 'show' + map.connect 'delete/project/:id', :controller => 'project', :action => 'destroy' + + map.connect 'add_item', :controller => 'todo', :action => 'add_item' # Install the default route as the lowest priority. diff --git a/tracks/db/tracks_1.0.2_tables_mysql.sql b/tracks/db/tracks_1.0.2_tables_mysql.sql index d6ba24d0..9b8ec888 100644 --- a/tracks/db/tracks_1.0.2_tables_mysql.sql +++ b/tracks/db/tracks_1.0.2_tables_mysql.sql @@ -14,6 +14,7 @@ CREATE TABLE `contexts` ( `id` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', `hide` tinyint(4) NOT NULL default '0', + `position` int(11) NOT NULL, PRIMARY KEY (`id`) ) TYPE=MyISAM; @@ -25,6 +26,7 @@ CREATE TABLE `contexts` ( CREATE TABLE `projects` ( `id` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL default '', + `position` int(11) NOT NULL, PRIMARY KEY (`id`) ) TYPE=MyISAM; diff --git a/tracks/public/images/calendar.gif b/tracks/public/images/calendar.gif new file mode 100644 index 00000000..49e972ba Binary files /dev/null and b/tracks/public/images/calendar.gif differ diff --git a/tracks/public/javascripts/calendar.js b/tracks/public/javascripts/calendar.js index f7c434fe..e47c6899 100644 --- a/tracks/public/javascripts/calendar.js +++ b/tracks/public/javascripts/calendar.js @@ -1,7 +1,6 @@ -/* Today's date */ -var DOMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; -var lDOMonth = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; -var moty = ["January","February","March","April","May","June","July","August","September","October","November","December"]; +// Popup calendar date picker +// Written by Michele Tranquilli of pxl8.com +// function Calendar_get_daysofmonth(monthNo, p_year) { if ((p_year % 4) == 0) { @@ -11,7 +10,9 @@ function Calendar_get_daysofmonth(monthNo, p_year) { } else return DOMonth[monthNo]; } - +// -- globals used with calendar and date functions +var calField; var calSpan; var calFormat; var calWknd = false; +/* ^^^^^^ end BASIC DATE STARTER-SET ^^^^^^ */ function getNextMonth(m,y,incr){ var ret_arr = new Array(); ret_arr[0] = m + incr; ret_arr[1] = y; @@ -20,16 +21,18 @@ function getNextMonth(m,y,incr){ return ret_arr; } function figureDOTW(m,d,y){ - var tDate = new Date(); tDate.setDate(d); tDate.setMonth(m); tDate.setYear(y); return tDate.getDay(); + var tDate = new Date(); tDate.setDate(d); tDate.setMonth(m); tDate.setFullYear(y); return tDate.getDay(); } function scramKids(n){ // this is a basic removeChild loop for removing all childNodes from node n var numKids = n.childNodes.length; for (i=0;i monthNo) // if this is the first row.... - calTDtext = document.createElement('br'); + if (j == 0 || j == 6 ) // weekends + calTD.style.backgroundColor = '#EDF0FF'; + if ((i==0 && j < dayNo) || dayCount > monthNo) // cells before the first of the month or after the last day + cellContent = document.createElement('br'); else { - calTDtext = document.createTextNode(dayCount.toString()); + var dyA = document.createElement('a'); + dyA.setAttribute('href','javascript:placeDate('+m+','+dayCount+','+y+')'); + calTDtext = document.createTextNode(dayCount.toString()); + cellContent = calTDtext; if (dayCount == curr_dy && m == curr_mn && y == curr_yr) - calTD.style.color = '#ff6600'; + calTD.style.backgroundColor = '#FFFF99'; + if ((j!=0 && j!=6) || calWknd == true){ // if the day is a weekday or weekends allowed + if (dayCount == curr_dy && m == curr_mn && y == curr_yr && calSpan != 3 && calSpan != 0 && calSpan != 4){ + dyA.appendChild(calTDtext); cellContent = dyA; + } + if (calSpan == 1 || calSpan == 4){ + if (y < curr_yr || (m < curr_mn && y == curr_yr) || (m == curr_mn && y == curr_yr && dayCount < curr_dy)) + { + dyA.appendChild(calTDtext); cellContent = dyA; + } + } + if (calSpan == 2 || calSpan == 3){ + if (y > curr_yr || (m > curr_mn && y == curr_yr) || (m == curr_mn && y == curr_yr && dayCount > curr_dy)) + {dyA.appendChild(calTDtext); cellContent = dyA;} + } + if (calSpan == 5){ + dyA.appendChild(calTDtext); cellContent = dyA; + } + } + else { /* else if it's a weekend */ } dayCount++; } - calTD.appendChild(calTDtext); + calTD.appendChild(cellContent); calTD.setAttribute('width','14%'); calTR.appendChild(calTD); } calTB.appendChild(calTR); } - var nMonth = getNextMonth(m,y,+1); var pMonth = getNextMonth(m,y,-1); - - document.getElementById('pyNav').innerHTML = '<<'; - document.getElementById('pmNav').innerHTML = '<'; - document.getElementById('myNav').innerHTML = moty[m] +' '+y; - document.getElementById('nyNav').innerHTML = '>>'; - document.getElementById('nmNav').innerHTML = '>'; + document.getElementById('calNavPY').innerHTML = '<<'; + document.getElementById('calNavPM').innerHTML = '<'; + document.getElementById('calNavMY').innerHTML = moty[m] +' '+y; + document.getElementById('calNavNY').innerHTML = '>>'; + document.getElementById('calNavNM').innerHTML = '>'; +} +function showCal(m,y,f,dateSpan,wknd,format){ + /* + dateSpan - date that should have links; does not include weekends + 0 = no dates + 1 = all past dates up to and including today + 2 = all future dates starting with today + 3 = all future dates NOT including today ( for GTC Dates ) + 4 = all past dates NOT including today ( for start / from dates ) + 5 = all dates + */ + calField = f; calSpan = dateSpan; calFormat = format; calWknd = wknd; + if (m == '' && y == ''){m = curr_mn; y = curr_yr;} + buildCalendar(m,y); + document.getElementById('calDiv').style.display = ''; +} +function placeDate(m,d,y){ + eval(calField).value = dateFormats(m,d,y,calFormat); + document.getElementById('calDiv').style.display = 'none'; +} +function dateFormats(m,d,y,calFormat){ + d = d.toString(); + m = m+1; m = m.toString(); + y = y.toString(); + var sy = y; +// -- convert to 2 digit numbers + if (m.length == 1){m = '0'+ m;} + if (d.length == 1){d = '0'+ d;} + if (y.length == 4) + sy = y.substring(2,4); + var format; + switch (calFormat){ + case 0 : format = m + d + sy; break; // mmddyy + case 1 : format = m + d + y; break; // mmddyyyy + case 2 : format = m +'/'+ d +'/'+ y; break; // mm/dd/yyyy + case 3 : format = m +'/'+ d +'/'+ sy; break; // mm/dd/yy + case 4 : format = y + m; break; // yyyymm + case 5 : format = d + m + sy; break; // ddmmyy + case 6 : format = d +'/'+ m +'/'+ sy; break; // dd/mm/yy + case 7 : format = d + m + y; break; // ddmmyyyy + case 8 : format = d +'/'+ m +'/'+ y; break; // dd/mm/yyyy + case 9 : format = y +'-'+ m +'-'+ d; break; // yyyy-mm-dd + default: format = m + d + y; break; // mmddyyyy + } + return format; } \ No newline at end of file diff --git a/tracks/public/stylesheets/standard.css b/tracks/public/stylesheets/standard.css index b039be50..1aeb6634 100644 --- a/tracks/public/stylesheets/standard.css +++ b/tracks/public/stylesheets/standard.css @@ -276,4 +276,60 @@ label { input { margin-bottom: 5px; - } \ No newline at end of file + } + +/* Popup calendar styles */ + +#calNavPY, #calNavPM {border:1px #999 solid} +#calNavNM, #calNavNY { border:1px #999 solid} + +#calNav td, #calNav td a { + text-align:center; + font-family:"Lucida Grande", Verdana, Geneva, Arial, sans-serif; + font-size:12px; + font-weight:bold; + background-color:#eaeaea; + text-decoration:none + } + +#calNav td a,#calNav td a:visited,#calTbl td a,#calTbl td a:visited {color:#cc3334 } +#calNav td a:hover,#calTbl td a:hover { color:#fff; background:#cc3334; } +#calNav {margin-bottom:5px;width:100%} +#calTbl {width:100%} +#calTbl td {text-align:center;font-family:"Lucida Grande", Verdana, Geneva, Arial, sans-serif;font-size:11px;border:1px #ccc solid} + +#calTbl thead td { + font-weight:bold; + background-color:#eaeaea; + padding: 2px; + border:1px #ccc solid + height: auto; + } + +#calDiv { + border:1px solid #ccc; + padding:2px; + width:200px; + position:absolute; + z-index:276; + top:300px; + left:300px; + background-color:#fff + } + +#calTopBar { + background-color:#ccc; + color:#fff; + padding:2px; + text-align:right; + font-family:"Lucida Grande", Verdana, Geneva, Arial, sans-serif; + font-size:11px; + font-weight:bold; + width:100% + } + +#calClose { + padding:0px 2px;border:1px #fff solid;cursor:pointer;cursor:hand + } + +#calPic {cursor:hand} \ No newline at end of file