* Made links work for project and context links that contain an underscore('_') or a dot('.'). I removed urlize() and deurlize() in favor or methods on the models mixed in by the new UrlFriendlyName module.

* Removed tag_object() from application_helper as it was not being used.
* Introduced link_to_context() and link_to_project() helper methods.
* Fixed a javascript syntax error on the context page.



git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@373 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
lukemelia 2006-12-11 04:48:56 +00:00
parent ee121e3dfb
commit aa41974d4c
22 changed files with 119 additions and 66 deletions

View file

@ -23,12 +23,6 @@ class ApplicationController < ActionController::Base
headers["Content-Type"] ||= "text/html; charset=UTF-8"
end
# Reverses the urlize() method by substituting underscores for spaces
#
def deurlize(name)
name.to_s.gsub(/_/, " ")
end
def set_session_expiration
# http://wiki.rubyonrails.com/rails/show/HowtoChangeSessionOptions
unless session == nil

View file

@ -46,7 +46,6 @@ class ContextController < ApplicationController
@context.attributes = params['context'] || params['request']['context']
params_are_invalid = false
end
@context.name = deurlize(@context.name)
@saved = @context.save
@context_not_done_counts = { @context.id => 0 }
respond_to do |wants|
@ -73,7 +72,6 @@ class ContextController < ApplicationController
params['context']['name'] = params['value']
end
@context.attributes = params["context"]
@context.name = deurlize(@context.name)
if @context.save
if params['wants_render']
render
@ -113,10 +111,10 @@ class ContextController < ApplicationController
protected
def check_user_set_context
if params["name"]
@context = Context.find_by_name_and_user_id(deurlize(params["name"]), @user.id)
if params["url_friendly_name"]
@context = @user.contexts.find_by_url_friendly_name(params["url_friendly_name"])
elsif params['id']
@context = Context.find_by_id_and_user_id(params["id"], @user.id)
@context = @user.contexts.find(params["id"])
else
redirect_to(:controller => "context", :action => "list" )
end

View file

@ -70,7 +70,6 @@ class ProjectController < ApplicationController
@project.attributes = params['project'] || params['request']['project']
params_are_invalid = false
end
@project.name = deurlize(@project.name)
@saved = @project.save
@project_not_done_counts = { @project.id => 0 }
respond_to do |wants|
@ -101,7 +100,6 @@ class ProjectController < ApplicationController
params['project']['name'] = params['value']
end
@project.attributes = params['project']
@project.name = deurlize(@project.name)
if @project.save
if params['wants_render']
if (@project.hidden?)
@ -157,10 +155,10 @@ class ProjectController < ApplicationController
protected
def check_user_set_project
if params["name"]
@project = Project.find_by_name_and_user_id(deurlize(params["name"]), @user.id)
if params["url_friendly_name"]
@project = @user.projects.find_by_url_friendly_name(params["url_friendly_name"])
elsif params['id']
@project = Project.find_by_id_and_user_id(params["id"], @user.id)
@project = @user.projects.find(params["id"])
else
redirect_to(:controller => "project", :action => "list" )
end

View file

@ -20,18 +20,6 @@ module ApplicationHelper
def markdown(text)
RedCloth.new(text).to_html
end
# Wraps object in HTML tags, tag
#
def tag_object(object, tag)
tagged = "<#{tag}>#{object}</#{tag}>"
end
# Converts names to URL-friendly format by substituting underscores for spaces
#
def urlize(name)
name.to_s.gsub(/ /, "_")
end
# Replicates the link_to method but also checks request.request_uri to find
# current page. If that matches the url, the link is marked
@ -132,16 +120,24 @@ module ApplicationHelper
return count.to_s + " " + word
end
def link_to_context(context, descriptor = sanitize(context.name))
link_to( descriptor, { :controller => "context", :action => "show", :url_friendly_name => context.url_friendly_name }, :title => "View context: #{context.name}" )
end
def link_to_project(project, descriptor = sanitize(project.name))
link_to( descriptor, { :controller => "project", :action => "show", :url_friendly_name => project.url_friendly_name }, :title => "View project: #{project.name}" )
end
def item_link_to_context(item)
descriptor = "[C]"
descriptor = "[#{item.context.name}]" if (@user.preference.verbose_action_descriptors)
link_to( descriptor, { :controller => "context", :action => "show", :name => urlize(item.context.name) }, :title => "View context: #{item.context.name}" )
link_to_context( item.context, descriptor )
end
def item_link_to_project(item)
descriptor = "[P]"
descriptor = "[#{item.project.name}]" if (@user.preference.verbose_action_descriptors)
link_to( descriptor, { :controller => "project", :action => "show", :name => urlize(item.project.name) }, :title => "View project: #{item.project.name}" )
link_to_project( item.project, descriptor )
end
def render_flash

View file

@ -6,6 +6,7 @@ class Context < ActiveRecord::Base
acts_as_list :scope => :user
extend NamePartFinder
include Tracks::TodoList
include UrlFriendlyName
attr_protected :user
@ -19,5 +20,5 @@ class Context < ActiveRecord::Base
def hidden?
self.hide == true
end
end

View file

@ -14,6 +14,7 @@ class Project < ActiveRecord::Base
acts_as_state_machine :initial => :active, :column => 'state'
extend NamePartFinder
include Tracks::TodoList
include UrlFriendlyName
state :active
state :hidden, :enter => :hide_todos, :exit => :unhide_todos
@ -44,7 +45,7 @@ class Project < ActiveRecord::Base
def linkurl_present?
attribute_present?("linkurl")
end
def hide_todos
todos.each do |t|
t.hide! unless t.completed?

View file

@ -7,7 +7,7 @@
<% if source_view_is :context %>
<%= in_place_editor_field :context, :name, {}, { :url => url_for(:controller => 'context', :action => 'update', :id => context.id, :field => 'name', :wants_render => false) } %>
<% else %>
<%= link_to( sanitize("#{context.name}"), { :controller => "context", :action => "show", :name => urlize(context.name) }, { :title => "Go to the #{context.name} context page" } ) %>
<%= link_to_context( context ) %>
<% end %>
</h2>
<div id="c<%= context.id %>items" class="items toggle_target">

View file

@ -6,7 +6,7 @@
<span class="handle">DRAG</span>
</div>
<div class="data">
<%= link_to( sanitize("#{context.name}"), :action => "show", :name => urlize(context.name) ) %>
<%= link_to_context( context ) %>
<%= " (" + count_undone_todos(context,"actions") + ")" %>
</div>
@ -21,7 +21,7 @@
:loading => visual_effect(:fade, "container_#{context.id}"),
:url => { :controller => "context", :action => "destroy", :id => context.id },
:confirm => "Are you sure that you want to delete the context \'#{context.name}\'?" ) + " " +
link_to_function( image_tag( "blank.png", :title => "Edit context", :class=>"edit_item"), "Element.toggle('context-#{context.id}');,Element.toggle('context-#{context.id}-edit-form'); new Effect.Appear('context-#{context.id}-edit-form'); Form.focusFirstElement('form-context-#{context.id}');" ) %>
link_to_function( image_tag( "blank.png", :title => "Edit context", :class=>"edit_item"), "Element.toggle('context-#{context.id}');Element.toggle('context-#{context.id}-edit-form'); new Effect.Appear('context-#{context.id}-edit-form'); Form.focusFirstElement('form-context-#{context.id}');" ) %>
</div>
</div><!-- [end:context-context.id] -->

View file

@ -7,7 +7,7 @@ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
@contexts.each do |c|
xml.item do
xml.title(c.name)
xml.link(url_for(:only_path => false, :controller => 'context', :action => 'show', :name => urlize(c.name)))
xml.link(url_for(:only_path => false, :controller => 'context', :action => 'show', :url_friendly_name => c.url_friendly_name))
context_description = ''
context_description += "<p>#{count_undone_todos(c)}. "
context_description += "Context is #{c.hidden? ? 'Hidden' : 'Active'}. "

View file

@ -7,7 +7,7 @@ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
@projects.each do |p|
xml.item do
xml.title(p.name)
xml.link(url_for(:only_path => false, :controller => 'project', :action => 'show', :name => urlize(p.name)))
xml.link(url_for(:only_path => false, :controller => 'project', :action => 'show', :url_friendly_name => p.url_friendly_name))
project_description = ''
project_description += sanitize(markdown( p.description )) if p.description_present?
project_description += "<p>#{count_undone_todos(p)}. "

View file

@ -6,15 +6,15 @@ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
@todos.each do |i|
xml.item do
xml.title(i.description)
xml.link(url_for(:only_path => false, :controller => 'context', :action => 'show', :name => urlize(i.context.name)))
xml.link(url_for(:only_path => false, :controller => 'context', :action => 'show', :url_friendly_name => i.context.url_friendly_name))
item_notes = sanitize(markdown( i.notes )) if i.notes?
due = "<div>Due: #{format_date(i.due)}</div>\n" if i.due?
toggle_link = link_to( "mark as done", {:only_path => false, :controller => "todo", :action => "toggle_check", :id => i.id})
done = "<div>#{toggle_link}</div>" unless i.completed?
done = "<div>Completed: #{format_date(i.completed_at)}</div>\n" if i.completed?
context_link = link_to( i.context.name, { :only_path => false, :controller => "context", :action => "show", :name => urlize(i.context.name) } )
context_link = link_to( i.context.name, { :only_path => false, :controller => "context", :action => "show", :url_friendly_name => i.context.url_friendly_name } )
if i.project_id?
project_link = link_to (i.project.name, { :only_path => false, :controller => "project", :action => "show", :name => urlize(i.project.name)} )
project_link = link_to (i.project.name, { :only_path => false, :controller => "project", :action => "show", :url_friendly_name => i.project.url_friendly_name} )
else
project_link = "<em>none</em>"
end

View file

@ -13,7 +13,7 @@
:url => { :controller => "note", :action => "delete", :id => note.id },
:confirm => "Are you sure that you want to delete the note \'#{note.id.to_s}\'?" ) + "&nbsp;"%><%= link_to_function(image_tag( "blank.png", :title => "Edit item", :class=>"edit_item"),
"Element.toggle('note-#{note.id}'); Element.toggle('note-#{note.id}-edit-form'); Effect.Appear('note-#{note.id}-edit-form'); Form.focusFirstElement('form-note-#{note.id}');" ) + " | " %>
<%= link_to("In: " + note.project.name, {:controller => "project", :action => "show", :name => urlize(note.project.name)}, :class=>"footer_link" ) %>&nbsp;|&nbsp;
<%= link_to("In: " + note.project.name, {:controller => "project", :action => "show", :url_friendly_name => note.project.url_friendly_name}, :class=>"footer_link" ) %>&nbsp;|&nbsp;
Created: <%= format_date(note.created_at) %>
<% if note.updated_at? -%>
&nbsp;|&nbsp;Modified: <%= format_date(note.updated_at) %>

View file

@ -6,7 +6,7 @@
<span class="handle">DRAG</span>
</div>
<div class="data">
<%= link_to( sanitize("#{project.name}"), :action => "show", :name => urlize(project.name) ) %><%= " (" + count_undone_todos(project,"actions") + ")" %>
<%= link_to_project( project ) %><%= " (" + count_undone_todos(project,"actions") + ")" %>
</div>
<div class="buttons">
<% if project.completed? -%>

View file

@ -1 +1 @@
<li><%= link_to( sanitize(context.name), { :controller => "context", :action => "show", :name => urlize(context.name) } ) + " (" + count_undone_todos(context,"actions") + ")"%></li>
<li><%= link_to_context( context ) + " (" + count_undone_todos(context,"actions") + ")"%></li>

View file

@ -1 +1 @@
<li><%= link_to( sanitize(project.name), { :controller => "project", :action => "show", :name => urlize(project.name) } ) + " (" + count_undone_todos(project,"actions") + ")" %></li>
<li><%= link_to_project( project ) + " (" + count_undone_todos(project,"actions") + ")" %></li>

View file

@ -9,7 +9,7 @@
<div id="completed" class="items toggle_target">
<div id="empty-d" style="display:<%= @done.empty? ? 'block' : 'none' %>">
<div class="message"><p>Currently there are no completed actions <%= append_descriptor %></p></div>
<div class="message"><p>Currently there are no completed actions.</p></div>
</div>
<%= render :partial => "todo/item", :collection => done, :locals => { :parent_container_type => "completed" } %>

View file

@ -64,6 +64,7 @@ AUTHENTICATION_SCHEMES = ['database']
require 'name_part_finder'
require 'todo_list'
require 'url_friendly_name'
require 'config'
if (AUTHENTICATION_SCHEMES.include? 'ldap')

View file

@ -39,22 +39,22 @@ ActionController::Routing::Routes.draw do |map|
map.connect 'context/create', :controller => 'context', :action => 'create'
map.connect 'context/order', :controller => 'context', :action => 'order'
map.connect 'context/:id', :controller=> 'context', :action => 'show', :requirements => {:id => /\d+/}
map.connect 'context/:context/feed/:action/:name/:token', :controller => 'feed'
map.connect 'context/:name', :controller => 'context', :action => 'show'
map.connect 'context/:context/feed/:action/:url_friendly_name/:token', :controller => 'feed'
map.connect 'context/:url_friendly_name', :controller => 'context', :action => 'show'
map.connect 'contexts', :controller => 'context', :action => 'list'
map.connect 'contexts/feed/:feedtype/:name/:token', :controller => 'feed', :action => 'list_contexts_only'
map.connect 'contexts/feed/:feedtype/:url_friendly_name/:token', :controller => 'feed', :action => 'list_contexts_only'
# Projects Routes
map.connect 'project/create', :controller => 'project', :action => 'create'
map.connect 'project/toggle_check/:id', :controller => 'project', :action => 'toggle_check'
map.connect 'project/order', :controller => 'project', :action => 'order'
map.connect 'project/:project/feed/:action/:name/:token', :controller => 'feed'
map.connect 'project/:project/feed/:action/:url_friendly_name/:token', :controller => 'feed'
map.connect 'project/:id', :controller => 'project', :action => 'show', :requirements => {:id => /\d+/}
map.connect 'project/:name', :controller => 'project', :action => 'show'
map.connect 'project/:url_friendly_name', :controller => 'project', :action => 'show'
map.connect 'projects', :controller => 'project', :action => 'list'
map.connect 'projects/feed/:feedtype/:name/:token', :controller => 'feed', :action => 'list_projects_only'
map.connect 'projects/feed/:feedtype/:url_friendly_name/:token', :controller => 'feed', :action => 'list_projects_only'
# Notes Routes
map.connect 'note/add', :controller => 'note', :action => 'add'
@ -64,7 +64,7 @@ ActionController::Routing::Routes.draw do |map|
# Feed Routes
map.connect 'feeds', :controller => 'feed', :action => 'index'
map.connect 'feed/:action/:name/:token', :controller => 'feed'
map.connect 'feed/:action/:url_friendly_name/:token', :controller => 'feed'
# Install the default route as the lowest priority.
map.connect ':controller/:action/:id'

View file

@ -5,10 +5,10 @@
ActiveRecord::Schema.define(:version => 20) do
create_table "contexts", :force => true do |t|
t.column "name", :string, :default => "", :null => false
t.column "position", :integer, :default => 0, :null => false
t.column "hide", :boolean, :default => false
t.column "user_id", :integer, :default => 1
t.column "name", :string, :default => "", :null => false
t.column "hide", :integer, :limit => 4, :default => 0, :null => false
t.column "position", :integer, :default => 0, :null => false
t.column "user_id", :integer, :default => 0, :null => false
end
create_table "notes", :force => true do |t|
@ -56,7 +56,7 @@ ActiveRecord::Schema.define(:version => 20) do
create_table "projects", :force => true do |t|
t.column "name", :string, :default => "", :null => false
t.column "position", :integer, :default => 0, :null => false
t.column "user_id", :integer, :default => 1
t.column "user_id", :integer, :default => 0, :null => false
t.column "description", :text
t.column "state", :string, :limit => 20, :default => "active", :null => false
end
@ -70,23 +70,23 @@ ActiveRecord::Schema.define(:version => 20) do
add_index "sessions", ["session_id"], :name => "sessions_session_id_index"
create_table "todos", :force => true do |t|
t.column "context_id", :integer, :default => 0, :null => false
t.column "project_id", :integer
t.column "description", :string, :default => "", :null => false
t.column "context_id", :integer, :default => 0, :null => false
t.column "description", :string, :limit => 100, :default => "", :null => false
t.column "notes", :text
t.column "created_at", :datetime
t.column "due", :date
t.column "completed_at", :datetime
t.column "user_id", :integer, :default => 1
t.column "project_id", :integer
t.column "user_id", :integer, :default => 0, :null => false
t.column "show_from", :date
t.column "state", :string, :limit => 20, :default => "immediate", :null => false
t.column "state", :string, :limit => 20, :default => "immediate", :null => false
end
create_table "users", :force => true do |t|
t.column "login", :string, :limit => 80, :default => "", :null => false
t.column "password", :string, :limit => 40, :default => "", :null => false
t.column "login", :string, :limit => 80
t.column "password", :string, :limit => 40
t.column "word", :string
t.column "is_admin", :boolean, :default => false, :null => false
t.column "is_admin", :integer, :limit => 4, :default => 0, :null => false
t.column "first_name", :string
t.column "last_name", :string
t.column "auth_type", :string, :default => "database", :null => false

View file

@ -0,0 +1,20 @@
module UrlFriendlyName
def self.included(base)
base.extend ClassMethods
end
def url_friendly_name
name.gsub(/_/,'__').gsub(/ /,'_').gsub(/\./,'__dot__')
end
module ClassMethods
def find_by_url_friendly_name(url_friendly_name)
name = url_friendly_name.gsub(/__dot__/,'.').gsub(/([^_])_(?!_)/,'\1 ').gsub(/__/,'_') #second regex replaces all single underscores with spaces
self.find_by_name(name)
end
end
end

View file

@ -76,4 +76,25 @@ class ContextTest < Test::Unit::TestCase
assert_equal 2, Context.find(@agenda.id).done_todos.size
end
def test_url_friendly_name_for_name_with_spaces
assert_url_friendly_name_converts_properly 'any computer', 'any_computer'
end
def test_url_friendly_name_for_name_without_spaces
assert_url_friendly_name_converts_properly 'NoSpacesHere', 'NoSpacesHere'
end
def test_url_friendly_name_for_name_with_underscores
assert_url_friendly_name_converts_properly 'there is an_underscore', 'there_is_an__underscore'
end
def assert_url_friendly_name_converts_properly(name, url_friendly_name)
context = Context.create(:name => name)
assert_equal url_friendly_name, context.url_friendly_name
found_context = Context.find_by_url_friendly_name(url_friendly_name)
assert_not_nil context
assert_equal context.id, found_context.id
end
end

View file

@ -99,5 +99,28 @@ class ProjectTest < Test::Unit::TestCase
assert_equal 1, Project.find(@timemachine.id).done_todos.size
end
def test_url_friendly_name_for_name_with_spaces
assert_url_friendly_name_converts_properly 'Build a playhouse', 'Build_a_playhouse'
end
def test_url_friendly_name_for_name_without_spaces
assert_url_friendly_name_converts_properly 'NoSpacesHere', 'NoSpacesHere'
end
def test_url_friendly_name_for_name_with_an_underscore
assert_url_friendly_name_converts_properly 'there is an_underscore', 'there_is_an__underscore'
end
def test_url_friendly_name_for_name_with_a_dot
assert_url_friendly_name_converts_properly 'hello.com', 'hello__dot__com'
end
def assert_url_friendly_name_converts_properly(name, url_friendly_name)
project = Project.create(:name => name)
assert_equal url_friendly_name, project.url_friendly_name
found_project = Project.find_by_url_friendly_name(url_friendly_name)
assert_not_nil project
assert_equal project.id, found_project.id
end
end