Merge branch 'master' of git://github.com/TracksApp/tracks into upstream
25
.gitignore.rails2
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
*~
|
||||
*.tmproj
|
||||
.dotest
|
||||
/.emacs-project
|
||||
/.redcar
|
||||
config/database.yml
|
||||
config/site.yml
|
||||
config/deploy.rb
|
||||
db/*.sqlite3
|
||||
db/data.yml
|
||||
db/schema.rb
|
||||
log
|
||||
nbproject
|
||||
public/javascripts/cache
|
||||
public/stylesheets/cache
|
||||
tmp
|
||||
vendor/plugins/query_trace/
|
||||
rerun.txt
|
||||
public/javascripts/jquery-cached.js
|
||||
public/javascripts/tracks-cached.js
|
||||
public/stylesheets/tracks-cached.css
|
||||
.idea
|
||||
.rvmrc
|
||||
.yardoc
|
||||
tags
|
||||
101
Gemfile
|
|
@ -1,34 +1,55 @@
|
|||
source :gemcutter
|
||||
source "http://gems.github.com/"
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem "rake", "~>0.8.7"
|
||||
gem "rails", "~>2.3.12"
|
||||
gem "highline", "~>1.5.0"
|
||||
gem "RedCloth", "4.2.8"
|
||||
gem "sanitize", "~>1.2.1"
|
||||
gem "rack", "1.1.0"
|
||||
gem "will_paginate", "~> 2.3.15"
|
||||
gem "has_many_polymorphs", "~> 2.13"
|
||||
gem "acts_as_list", "~>0.1.4"
|
||||
gem "aasm", "~>2.2.0"
|
||||
gem "rubyjedi-actionwebservice", :require => "actionwebservice"
|
||||
gem "rubycas-client", "~>2.2.1"
|
||||
gem "ruby-openid", :require => "openid"
|
||||
gem 'rails', '3.2.3'
|
||||
|
||||
# Bundle edge Rails instead:
|
||||
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
||||
|
||||
# you may comment out the database driver you will not be using.
|
||||
# This will prevent a native build of the driver. Building native drivers is not always possible on all hosters
|
||||
gem "sqlite3"
|
||||
gem "mysql"
|
||||
gem 'bcrypt-ruby', '~> 2.1.4'
|
||||
gem 'htmlentities', '~> 4.3.0'
|
||||
gem "mail"
|
||||
gem "mysql2"
|
||||
|
||||
if RUBY_VERSION.to_f >= 1.9
|
||||
gem "soap4r-ruby1.9"
|
||||
else
|
||||
gem "soap4r", "~>1.5.8"
|
||||
# gem "highline", "~>1.5.0"
|
||||
gem "RedCloth"
|
||||
gem "formatize"
|
||||
gem "sanitize"
|
||||
gem "will_paginate"
|
||||
gem "acts_as_list"
|
||||
gem "aasm"
|
||||
gem "htmlentities"
|
||||
gem "swf_fu"
|
||||
gem "rails_autolink"
|
||||
|
||||
# Gems used only for assets and not required
|
||||
# in production environments by default.
|
||||
group :assets do
|
||||
gem 'sass-rails', '~> 3.2.3'
|
||||
gem 'coffee-rails', '~> 3.2.1'
|
||||
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
# gem 'therubyracer', :platform => :ruby
|
||||
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
end
|
||||
|
||||
gem 'jquery-rails'
|
||||
|
||||
# To use ActiveModel has_secure_password
|
||||
gem 'bcrypt-ruby', '~> 3.0.0'
|
||||
|
||||
# To use Jbuilder templates for JSON
|
||||
# gem 'jbuilder'
|
||||
|
||||
# Use unicorn as the app server
|
||||
# gem 'unicorn'
|
||||
|
||||
# Deploy with Capistrano
|
||||
# gem 'capistrano'
|
||||
|
||||
group :development do
|
||||
if RUBY_VERSION.to_f >= 1.9
|
||||
gem "ruby-debug19"
|
||||
# gem "ruby-debug19", :require => 'ruby-debug'
|
||||
gem "mongrel", "1.2.0.pre2"
|
||||
else
|
||||
gem "ruby-debug"
|
||||
|
|
@ -38,16 +59,24 @@ group :development do
|
|||
end
|
||||
|
||||
group :test do
|
||||
gem "test-unit", "1.2.3"
|
||||
gem "flexmock"
|
||||
gem "ZenTest", ">=4.0.0"
|
||||
gem "hpricot"
|
||||
gem "hoe"
|
||||
gem "rspec-rails", "~>1.3.3"
|
||||
gem "thoughtbot-factory_girl"
|
||||
gem 'memory_test_fix', '~>0.1.3'
|
||||
gem "selenium-client"
|
||||
gem "webrat", ">=0.7.0"
|
||||
gem "database_cleaner", ">=0.5.0"
|
||||
gem "cucumber-rails", "~>0.3.0"
|
||||
# gem "test-unit", "1.2.3"
|
||||
# gem "flexmock"
|
||||
# gem "ZenTest", ">=4.0.0"
|
||||
# gem "hpricot"
|
||||
# gem "hoe"
|
||||
# gem "rspec-rails", "~>1.3.3"
|
||||
# gem 'memory_test_fix', '~>0.1.3'
|
||||
gem "factory_girl_rails"
|
||||
gem "capybara"
|
||||
gem "selenium-webdriver" # Note that > 2.14 has problems: https://code.google.com/p/selenium/issues/detail?id=3075
|
||||
gem "database_cleaner"
|
||||
gem "cucumber-rails"
|
||||
gem "aruba"
|
||||
|
||||
# uncomment to use the webkit option. This depends on Qt to be installed
|
||||
#gem "capybara-webkit"
|
||||
|
||||
# uncomment to be able to make screenshots from scenarios
|
||||
#gem "capybara-screenshot"
|
||||
#gem "launchy"
|
||||
end
|
||||
|
|
|
|||
322
Gemfile.lock
|
|
@ -1,150 +1,210 @@
|
|||
GEM
|
||||
remote: http://rubygems.org/
|
||||
remote: http://gems.github.com/
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
RedCloth (4.2.8)
|
||||
ZenTest (4.6.1)
|
||||
aasm (2.2.0)
|
||||
actionmailer (2.3.14)
|
||||
actionpack (= 2.3.14)
|
||||
actionpack (2.3.14)
|
||||
activesupport (= 2.3.14)
|
||||
rack (~> 1.1.0)
|
||||
activerecord (2.3.14)
|
||||
activesupport (= 2.3.14)
|
||||
activeresource (2.3.14)
|
||||
activesupport (= 2.3.14)
|
||||
activesupport (2.3.14)
|
||||
acts_as_list (0.1.4)
|
||||
bcrypt-ruby (2.1.4)
|
||||
builder (3.0.0)
|
||||
cgi_multipart_eof_fix (2.5.0)
|
||||
columnize (0.3.4)
|
||||
cucumber (1.0.2)
|
||||
builder (>= 2.1.2)
|
||||
diff-lcs (>= 1.1.2)
|
||||
gherkin (~> 2.4.5)
|
||||
json (>= 1.4.6)
|
||||
term-ansicolor (>= 1.0.5)
|
||||
cucumber-rails (0.3.2)
|
||||
cucumber (>= 0.8.0)
|
||||
daemons (1.1.4)
|
||||
database_cleaner (0.6.7)
|
||||
diff-lcs (1.1.2)
|
||||
fastthread (1.0.7)
|
||||
flexmock (0.9.0)
|
||||
gem_plugin (0.2.3)
|
||||
gherkin (2.4.11)
|
||||
json (>= 1.4.6)
|
||||
has_many_polymorphs (2.13)
|
||||
RedCloth (4.2.9)
|
||||
aasm (3.0.6)
|
||||
activerecord
|
||||
highline (1.5.2)
|
||||
hoe (2.12.0)
|
||||
rake (~> 0.8)
|
||||
hpricot (0.8.4)
|
||||
htmlentities (4.3.0)
|
||||
httpclient (2.2.1)
|
||||
actionmailer (3.2.3)
|
||||
actionpack (= 3.2.3)
|
||||
mail (~> 2.4.4)
|
||||
actionpack (3.2.3)
|
||||
activemodel (= 3.2.3)
|
||||
activesupport (= 3.2.3)
|
||||
builder (~> 3.0.0)
|
||||
erubis (~> 2.7.0)
|
||||
journey (~> 1.0.1)
|
||||
rack (~> 1.4.0)
|
||||
rack-cache (~> 1.2)
|
||||
rack-test (~> 0.6.1)
|
||||
sprockets (~> 2.1.2)
|
||||
activemodel (3.2.3)
|
||||
activesupport (= 3.2.3)
|
||||
builder (~> 3.0.0)
|
||||
activerecord (3.2.3)
|
||||
activemodel (= 3.2.3)
|
||||
activesupport (= 3.2.3)
|
||||
arel (~> 3.0.2)
|
||||
tzinfo (~> 0.3.29)
|
||||
activeresource (3.2.3)
|
||||
activemodel (= 3.2.3)
|
||||
activesupport (= 3.2.3)
|
||||
activesupport (3.2.3)
|
||||
i18n (~> 0.6)
|
||||
multi_json (~> 1.0)
|
||||
acts_as_list (0.1.6)
|
||||
addressable (2.2.8)
|
||||
arel (3.0.2)
|
||||
aruba (0.4.11)
|
||||
childprocess (>= 0.2.3)
|
||||
cucumber (>= 1.1.1)
|
||||
ffi (>= 1.0.11)
|
||||
rspec (>= 2.7.0)
|
||||
bcrypt-ruby (3.0.1)
|
||||
bluecloth (2.2.0)
|
||||
builder (3.0.0)
|
||||
capybara (1.1.2)
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
selenium-webdriver (~> 2.0)
|
||||
xpath (~> 0.1.4)
|
||||
childprocess (0.3.2)
|
||||
ffi (~> 1.0.6)
|
||||
coffee-rails (3.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (~> 3.2.0)
|
||||
coffee-script (2.2.0)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.3.3)
|
||||
cucumber (1.2.0)
|
||||
builder (>= 2.1.2)
|
||||
diff-lcs (>= 1.1.3)
|
||||
gherkin (~> 2.10.0)
|
||||
json (>= 1.4.6)
|
||||
cucumber-rails (1.3.0)
|
||||
capybara (>= 1.1.2)
|
||||
cucumber (>= 1.1.8)
|
||||
nokogiri (>= 1.5.0)
|
||||
daemons (1.0.10)
|
||||
database_cleaner (0.7.2)
|
||||
diff-lcs (1.1.3)
|
||||
erubis (2.7.0)
|
||||
execjs (1.3.2)
|
||||
multi_json (~> 1.0)
|
||||
factory_girl (3.3.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (3.3.0)
|
||||
factory_girl (~> 3.3.0)
|
||||
railties (>= 3.0.0)
|
||||
ffi (1.0.11)
|
||||
formatize (1.1.0)
|
||||
RedCloth (~> 4.2)
|
||||
actionpack (~> 3.0)
|
||||
bluecloth (~> 2.2)
|
||||
gem_plugin (0.2.3)
|
||||
gherkin (2.10.0)
|
||||
json (>= 1.4.6)
|
||||
hike (1.2.1)
|
||||
htmlentities (4.3.1)
|
||||
i18n (0.6.0)
|
||||
json (1.5.3)
|
||||
linecache (0.46)
|
||||
rbx-require-relative (> 0.0.4)
|
||||
mail (2.3.0)
|
||||
journey (1.0.3)
|
||||
jquery-rails (2.0.2)
|
||||
railties (>= 3.2.0, < 5.0)
|
||||
thor (~> 0.14)
|
||||
json (1.7.3)
|
||||
libwebsocket (0.1.3)
|
||||
addressable
|
||||
mail (2.4.4)
|
||||
i18n (>= 0.4.0)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
memory_test_fix (0.1.3)
|
||||
mime-types (1.16)
|
||||
mongrel (1.1.5)
|
||||
cgi_multipart_eof_fix (>= 2.4)
|
||||
daemons (>= 1.0.3)
|
||||
fastthread (>= 1.0.1)
|
||||
gem_plugin (>= 0.2.3)
|
||||
mysql (2.8.1)
|
||||
nokogiri (1.4.7)
|
||||
polyglot (0.3.2)
|
||||
rack (1.1.0)
|
||||
mime-types (1.18)
|
||||
mongrel (1.2.0.pre2)
|
||||
daemons (~> 1.0.10)
|
||||
gem_plugin (~> 0.2.3)
|
||||
multi_json (1.3.5)
|
||||
mysql2 (0.3.11)
|
||||
nokogiri (1.5.2)
|
||||
polyglot (0.3.3)
|
||||
rack (1.4.1)
|
||||
rack-cache (1.2)
|
||||
rack (>= 0.4)
|
||||
rack-ssl (1.3.2)
|
||||
rack
|
||||
rack-test (0.6.1)
|
||||
rack (>= 1.0)
|
||||
rails (2.3.14)
|
||||
actionmailer (= 2.3.14)
|
||||
actionpack (= 2.3.14)
|
||||
activerecord (= 2.3.14)
|
||||
activeresource (= 2.3.14)
|
||||
activesupport (= 2.3.14)
|
||||
rake (>= 0.8.3)
|
||||
rake (0.8.7)
|
||||
rbx-require-relative (0.0.5)
|
||||
rspec (1.3.2)
|
||||
rspec-rails (1.3.4)
|
||||
rack (>= 1.0.0)
|
||||
rspec (~> 1.3.1)
|
||||
ruby-debug (0.10.4)
|
||||
columnize (>= 0.1)
|
||||
ruby-debug-base (~> 0.10.4.0)
|
||||
ruby-debug-base (0.10.4)
|
||||
linecache (>= 0.3)
|
||||
ruby-openid (2.1.8)
|
||||
rubycas-client (2.2.1)
|
||||
activesupport
|
||||
rubyjedi-actionwebservice (2.3.5.20100714122544)
|
||||
actionpack (~> 2.3.0)
|
||||
activerecord (~> 2.3.0)
|
||||
activesupport (~> 2.3.0)
|
||||
sanitize (1.2.1)
|
||||
nokogiri (~> 1.4.1)
|
||||
selenium-client (1.2.18)
|
||||
soap4r (1.5.8)
|
||||
httpclient (>= 2.1.1)
|
||||
sqlite3 (1.3.4)
|
||||
term-ansicolor (1.0.6)
|
||||
test-unit (1.2.3)
|
||||
hoe (>= 1.5.1)
|
||||
thoughtbot-factory_girl (1.2.2)
|
||||
rails (3.2.3)
|
||||
actionmailer (= 3.2.3)
|
||||
actionpack (= 3.2.3)
|
||||
activerecord (= 3.2.3)
|
||||
activeresource (= 3.2.3)
|
||||
activesupport (= 3.2.3)
|
||||
bundler (~> 1.0)
|
||||
railties (= 3.2.3)
|
||||
rails_autolink (1.0.7)
|
||||
rails (~> 3.1)
|
||||
railties (3.2.3)
|
||||
actionpack (= 3.2.3)
|
||||
activesupport (= 3.2.3)
|
||||
rack-ssl (~> 1.3.2)
|
||||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (~> 0.14.6)
|
||||
rake (0.9.2.2)
|
||||
rdoc (3.12)
|
||||
json (~> 1.4)
|
||||
rspec (2.10.0)
|
||||
rspec-core (~> 2.10.0)
|
||||
rspec-expectations (~> 2.10.0)
|
||||
rspec-mocks (~> 2.10.0)
|
||||
rspec-core (2.10.0)
|
||||
rspec-expectations (2.10.0)
|
||||
diff-lcs (~> 1.1.3)
|
||||
rspec-mocks (2.10.1)
|
||||
rubyzip (0.9.8)
|
||||
sanitize (2.0.3)
|
||||
nokogiri (>= 1.4.4, < 1.6)
|
||||
sass (3.1.18)
|
||||
sass-rails (3.2.5)
|
||||
railties (~> 3.2.0)
|
||||
sass (>= 3.1.10)
|
||||
tilt (~> 1.3)
|
||||
selenium-webdriver (2.21.2)
|
||||
childprocess (>= 0.2.5)
|
||||
ffi (~> 1.0)
|
||||
libwebsocket (~> 0.1.3)
|
||||
multi_json (~> 1.0)
|
||||
rubyzip
|
||||
sprockets (2.1.3)
|
||||
hike (~> 1.2)
|
||||
rack (~> 1.0)
|
||||
tilt (~> 1.1, != 1.3.0)
|
||||
sqlite3 (1.3.6)
|
||||
swf_fu (2.0.2)
|
||||
coffee-script
|
||||
rails (>= 3.1)
|
||||
thor (0.14.6)
|
||||
tilt (1.3.3)
|
||||
treetop (1.4.10)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
webrat (0.7.3)
|
||||
nokogiri (>= 1.2.0)
|
||||
rack (>= 1.0)
|
||||
rack-test (>= 0.5.3)
|
||||
will_paginate (2.3.16)
|
||||
yard (0.7.3)
|
||||
tzinfo (0.3.33)
|
||||
uglifier (1.2.4)
|
||||
execjs (>= 0.3.0)
|
||||
multi_json (>= 1.0.2)
|
||||
will_paginate (3.0.3)
|
||||
xpath (0.1.4)
|
||||
nokogiri (~> 1.3)
|
||||
yard (0.8.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
RedCloth (= 4.2.8)
|
||||
ZenTest (>= 4.0.0)
|
||||
aasm (~> 2.2.0)
|
||||
acts_as_list (~> 0.1.4)
|
||||
bcrypt-ruby (~> 2.1.4)
|
||||
cucumber-rails (~> 0.3.0)
|
||||
database_cleaner (>= 0.5.0)
|
||||
flexmock
|
||||
has_many_polymorphs (~> 2.13)
|
||||
highline (~> 1.5.0)
|
||||
hoe
|
||||
hpricot
|
||||
htmlentities (~> 4.3.0)
|
||||
mail
|
||||
memory_test_fix (~> 0.1.3)
|
||||
mongrel
|
||||
mysql
|
||||
rack (= 1.1.0)
|
||||
rails (~> 2.3.12)
|
||||
rake (~> 0.8.7)
|
||||
rspec-rails (~> 1.3.3)
|
||||
ruby-debug
|
||||
ruby-openid
|
||||
rubycas-client (~> 2.2.1)
|
||||
rubyjedi-actionwebservice
|
||||
sanitize (~> 1.2.1)
|
||||
selenium-client
|
||||
soap4r (~> 1.5.8)
|
||||
RedCloth
|
||||
aasm
|
||||
acts_as_list
|
||||
aruba
|
||||
bcrypt-ruby (~> 3.0.0)
|
||||
capybara
|
||||
coffee-rails (~> 3.2.1)
|
||||
cucumber-rails
|
||||
database_cleaner
|
||||
factory_girl_rails
|
||||
formatize
|
||||
htmlentities
|
||||
jquery-rails
|
||||
mongrel (= 1.2.0.pre2)
|
||||
mysql2
|
||||
rails (= 3.2.3)
|
||||
rails_autolink
|
||||
sanitize
|
||||
sass-rails (~> 3.2.3)
|
||||
selenium-webdriver
|
||||
sqlite3
|
||||
test-unit (= 1.2.3)
|
||||
thoughtbot-factory_girl
|
||||
webrat (>= 0.7.0)
|
||||
will_paginate (~> 2.3.15)
|
||||
swf_fu
|
||||
uglifier (>= 1.0.3)
|
||||
will_paginate
|
||||
yard
|
||||
|
|
|
|||
26
README
|
|
@ -1,28 +1,21 @@
|
|||
# Tracks: a GTD(TM) web application, built with Ruby on Rails
|
||||
|
||||
**IMPORTANT: Tracks is moving to a GitHub Organization to make it easier to continue administering the project. Development will soon cease at bsag/tracks and move to TracksApp/tracks. If you are currently pulling from bsag/tracks, please pull from TracksApp instead.**
|
||||
|
||||
`git clone git://github.com/TracksApp/tracks.git`
|
||||
|
||||
**The new home for Tracks is https://github.com/TracksApp/tracks**
|
||||
|
||||
* Project homepage: http://getontracks.org/
|
||||
* Manual: http://TracksApp.github.com/tracks/
|
||||
* Manual: http://getontracks.org/manual/
|
||||
* Source at GitHub: http://github.com/TracksApp/tracks/
|
||||
* Assembla space (for bug reports and feature requests): http://www.assembla.com/spaces/tracks-tickets/tickets
|
||||
* Wiki (community contributed information): https://github.com/TracksApp/tracks/wiki
|
||||
* Forum: http://getontracks.org/forums/
|
||||
* Forum (read-only): http://getontracks.org/forums/
|
||||
* Mailing list: http://lists.rousette.org.uk/mailman/listinfo/tracks-discuss
|
||||
* Original developer: bsag (http://www.rousette.org.uk/)
|
||||
* Contributors: https://github.com/TracksApp/tracks/wiki/Contributors
|
||||
* Version: 2.1devel
|
||||
* Copyright: (cc) 2004-2011 rousette.org.uk.
|
||||
* Version: 2.2devel
|
||||
* Copyright: (cc) 2004-2012 rousette.org.uk.
|
||||
* License: GNU GPL
|
||||
|
||||
All the documentation for Tracks can be found within the /doc directory and at
|
||||
http://bsag.github.com/tracks/
|
||||
More documentation for Tracks can be found within the /doc directory
|
||||
|
||||
The latter includes full instructions for both new installations and upgrades
|
||||
The manual includes full instructions for both new installations and upgrades
|
||||
from older installations of Tracks.
|
||||
|
||||
The instructions might appear long and intimidatingly complex, but that is
|
||||
|
|
@ -37,12 +30,11 @@ you cannot find a solution to your problem.
|
|||
The wiki has a lot of user contributed installation HOWTOs for various webhosts, specific OS's and more.
|
||||
|
||||
If you are thinking about contributing towards the development of Tracks,
|
||||
please read /doc/README_DEVELOPERS for general information, or
|
||||
/doc/tracks_api_wrapper.rb for information on Tracks' API. Also you can find
|
||||
some information on development on the wiki.
|
||||
please read /doc/README_DEVELOPERS for general information. Also you can find
|
||||
some information on development, testing and contributing on the wiki.
|
||||
|
||||
While fully usable for everyday use, Tracks is still a work in progress. Make
|
||||
sure that you take sensible precautions and back up all your data frequently,
|
||||
taking particular care when you are upgrading.
|
||||
|
||||
Enjoy being productive!
|
||||
Enjoy being productive!
|
||||
15
Rakefile
|
|
@ -1,16 +1,7 @@
|
|||
#!/usr/bin/env rake
|
||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
require(File.join(File.dirname(__FILE__), 'config', 'boot'))
|
||||
require File.expand_path('../config/application', __FILE__)
|
||||
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
require 'tasks/rails'
|
||||
|
||||
begin
|
||||
require 'test/rails/rake_tasks'
|
||||
rescue LoadError => e
|
||||
#It's ok if you don't have ZenTest installed if you're not a developer
|
||||
end
|
||||
Tracksapp::Application.load_tasks
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 781 B After Width: | Height: | Size: 781 B |
|
Before Width: | Height: | Size: 596 B After Width: | Height: | Size: 596 B |
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
|
Before Width: | Height: | Size: 244 B After Width: | Height: | Size: 244 B |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 156 B After Width: | Height: | Size: 156 B |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 587 B After Width: | Height: | Size: 587 B |
|
Before Width: | Height: | Size: 109 B After Width: | Height: | Size: 109 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 138 B After Width: | Height: | Size: 138 B |
|
Before Width: | Height: | Size: 3 KiB After Width: | Height: | Size: 3 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
BIN
app/assets/images/defer_2.png
Normal file
|
After Width: | Height: | Size: 496 B |
BIN
app/assets/images/defer_2_off.png
Normal file
|
After Width: | Height: | Size: 425 B |
BIN
app/assets/images/defer_3.png
Normal file
|
After Width: | Height: | Size: 501 B |
BIN
app/assets/images/defer_3_off.png
Normal file
|
After Width: | Height: | Size: 441 B |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 905 B After Width: | Height: | Size: 905 B |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 216 B After Width: | Height: | Size: 216 B |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 764 B After Width: | Height: | Size: 764 B |
|
Before Width: | Height: | Size: 192 B After Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 618 B After Width: | Height: | Size: 618 B |
|
Before Width: | Height: | Size: 69 B After Width: | Height: | Size: 69 B |
|
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 337 B |
|
Before Width: | Height: | Size: 271 B After Width: | Height: | Size: 271 B |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 161 B After Width: | Height: | Size: 161 B |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 237 B |
BIN
app/assets/images/rails.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 410 B After Width: | Height: | Size: 410 B |
|
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 598 B |
|
Before Width: | Height: | Size: 475 B After Width: | Height: | Size: 475 B |
|
Before Width: | Height: | Size: 814 B After Width: | Height: | Size: 814 B |
|
Before Width: | Height: | Size: 642 B After Width: | Height: | Size: 642 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 84 B After Width: | Height: | Size: 84 B |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 935 B After Width: | Height: | Size: 935 B |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 328 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 515 B After Width: | Height: | Size: 515 B |
30
app/assets/javascripts/application.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||
// listed below.
|
||||
//
|
||||
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
||||
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
||||
//
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// the compiled file.
|
||||
//
|
||||
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
||||
// GO AFTER THE REQUIRES BELOW.
|
||||
//
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
|
||||
// Stuff in app/assets
|
||||
//= require tracks.js
|
||||
|
||||
// Stuff in vendor/assets
|
||||
//= require jquery-ui-1.8.17.custom.min
|
||||
//= require jquery.blockUI
|
||||
//= require jquery.cookie
|
||||
//= require jquery.form
|
||||
//= require jquery.jeditable.mini
|
||||
//= require jquery.simulate.drag-sortable
|
||||
//= require jquery.truncator
|
||||
//= require niftycube
|
||||
//= require superfish
|
||||
//= require supersubs
|
||||
//= require swfobject
|
||||
|
|
@ -434,7 +434,7 @@ var TodoItems = {
|
|||
return confirm(i18n['contexts.new_context_pre'] + givenContextName + i18n['contexts.new_context_post']);
|
||||
},
|
||||
generate_predecessor: function(todo_id, todo_spec) {
|
||||
var img = "<img id=\"delete_dep_"+todo_id+"\" class=\"icon_delete_dep\" src=\""+ relative_to_root('images/blank.png') + "\">";
|
||||
var img = "<img id=\"delete_dep_"+todo_id+"\" class=\"icon_delete_dep\" src=\""+ relative_to_root('assets/blank.png') + "\">";
|
||||
var anchor = "<a class=\"icon_delete_dep\" id=\""+todo_id+"\" href=\"#\">" + img + "</a>";
|
||||
var li = "<li style=\"display:none\" id=\"pred_"+todo_id+"\">"+ anchor +" "+ todo_spec + "</li>";
|
||||
return li;
|
||||
|
|
@ -534,12 +534,12 @@ var TodoItems = {
|
|||
});
|
||||
$('.item-show').droppable({
|
||||
drop: TodoItems.drop_todo,
|
||||
tolerance: 'pointer',
|
||||
tolerance: 'intersect', /* warning: selenium fails on drag_and_drop when this is 'pointer' */
|
||||
hoverClass: 'hover'
|
||||
});
|
||||
$('.context_target').droppable({
|
||||
drop: TodoItems.drop_todo_on_context,
|
||||
tolerance: 'pointer',
|
||||
tolerance: 'intersect', /* warning: selenium fails on drag_and_drop when this is 'pointer' */
|
||||
hoverClass: 'hover'
|
||||
});
|
||||
},
|
||||
|
|
@ -610,7 +610,7 @@ var TodoItems = {
|
|||
return false;
|
||||
});
|
||||
|
||||
/* delete button to delete a project from the list */
|
||||
/* delete button to delete a dependency from the list */
|
||||
$('.item-container a.delete_dependency_button').live('click', function(evt){
|
||||
var predecessor_id=$(this).attr("x_predecessors_id");
|
||||
var ajax_options = default_ajax_options_for_scripts('DELETE', this.href, $(this).parents('.item-container'));
|
||||
|
|
@ -726,9 +726,10 @@ var ProjectListPage = {
|
|||
var highlight = function(){
|
||||
$('h2#project_name').effect('highlight', {}, 500);
|
||||
};
|
||||
$.post(relative_to_root('projects/update/'+project_id), {
|
||||
$.post(relative_to_root('projects/'+project_id), {
|
||||
'project[name]': value,
|
||||
'update_project_name': 'true'
|
||||
'update_project_name': 'true',
|
||||
'_method': 'put'
|
||||
}, highlight, 'script');
|
||||
return(value);
|
||||
},
|
||||
|
|
@ -770,8 +771,9 @@ var ProjectListPage = {
|
|||
|
||||
/* set behavior for edit project settings link in both projects list page and project page */
|
||||
$("a.project_edit_settings").live('click', function (evt) {
|
||||
get_with_ajax_and_block_element(this.href, $(this).parent().parent());
|
||||
return false;
|
||||
$(this).parent().parent().addClass('project-edit-current'); /* mark project in list */
|
||||
get_with_ajax_and_block_element(this.href, $(this).parent().parent());
|
||||
return false;
|
||||
});
|
||||
|
||||
/* submit project form after edit */
|
||||
|
|
@ -785,6 +787,7 @@ var ProjectListPage = {
|
|||
$('form.edit-project-form a.negative').live('click', function(){
|
||||
$('div#project_name').editable('enable');
|
||||
$(this).parents('.edit-form').fadeOut(200, function () {
|
||||
$(this).parents('.project-edit-current').removeClass('project-edit-current');
|
||||
$(this).parents('.list').find('.project').fadeIn(500);
|
||||
$(this).parents('.container').find('.item-show').fadeIn(500);
|
||||
})
|
||||
|
|
@ -806,7 +809,7 @@ var ProjectListPage = {
|
|||
/* make the three lists of project sortable */
|
||||
$(['active', 'hidden', 'completed']).each(function() {
|
||||
$("#list-"+this+"-projects").sortable({
|
||||
handle: '.handle',
|
||||
handle: '.grip',
|
||||
update: update_order
|
||||
});
|
||||
});
|
||||
|
|
@ -841,8 +844,9 @@ var ContextListPage = {
|
|||
var highlight = function(){
|
||||
$('div.context span#context_name').effect('highlight', {}, 500);
|
||||
};
|
||||
$.post(relative_to_root('contexts/update/'+context_id), {
|
||||
'context[name]': value
|
||||
$.post(relative_to_root('contexts/'+context_id), {
|
||||
'context[name]': value,
|
||||
'_method': 'put'
|
||||
}, highlight);
|
||||
return value;
|
||||
},
|
||||
|
|
@ -891,7 +895,7 @@ var ContextListPage = {
|
|||
/* make the two state lists of context sortable */
|
||||
$(['active', 'hidden']).each(function() {
|
||||
$("#list-contexts-"+this).sortable({
|
||||
handle: '.handle',
|
||||
handle: '.grip',
|
||||
update: update_order
|
||||
})
|
||||
});
|
||||
|
|
@ -1014,8 +1018,14 @@ var RecurringTodosPage = {
|
|||
width: 750,
|
||||
modal: true,
|
||||
buttons: {
|
||||
"Create": function() { submit_with_ajax_and_block_element('form.#recurring-todo-form-new-action', $(this).parents(".ui-dialog")); },
|
||||
Cancel: function() { $( this ).dialog( "close" ); }
|
||||
create: {
|
||||
text: i18n['common.create'],
|
||||
click: function() { submit_with_ajax_and_block_element('form.#recurring-todo-form-new-action', $(this).parents(".ui-dialog")); },
|
||||
},
|
||||
cancel: {
|
||||
text: i18n['common.cancel'],
|
||||
click: function() { $( this ).dialog( "close" ); }
|
||||
}
|
||||
},
|
||||
show: "fade",
|
||||
hide: "fade",
|
||||
|
|
@ -1041,11 +1051,14 @@ var RecurringTodosPage = {
|
|||
modal: true,
|
||||
buttons: {
|
||||
"Update": {
|
||||
text: "Update",
|
||||
text: i18n['common.update'],
|
||||
id: 'recurring_todo_edit_update_button',
|
||||
click: function() { submit_with_ajax_and_block_element('form#recurring-todo-form-edit-action', $(this).parents(".ui-dialog")); }
|
||||
},
|
||||
Cancel: function() { $( this ).dialog( "close" ); }
|
||||
cancel: {
|
||||
text: i18n['common.cancel'],
|
||||
click: function() { $( this ).dialog( "close" ); }
|
||||
}
|
||||
},
|
||||
show: "fade",
|
||||
hide: "fade",
|
||||
|
|
@ -1147,7 +1160,7 @@ $.fn.clearDeps = function() {
|
|||
/**************************************/
|
||||
|
||||
function generic_get_script_for_list(element, getter, param){
|
||||
$(element).load(relative_to_root(getter+'?'+param));
|
||||
$(element).load(relative_to_root(getter+'.js?'+param));
|
||||
}
|
||||
|
||||
function default_ajax_options_for_submit(ajax_type, element_to_block) {
|
||||
14
app/assets/stylesheets/application.css
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||
* listed below.
|
||||
*
|
||||
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
||||
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
||||
*
|
||||
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
||||
* compiled file, but it's generally better to create a new file per style scope.
|
||||
*
|
||||
*= require_self
|
||||
*= require tracks
|
||||
*= require_tree ../../../vendor/assets/stylesheets
|
||||
*/
|
||||
|
|
@ -1,22 +1,20 @@
|
|||
body {
|
||||
font-family: Arial,Helvetica,sans-serif;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-top: 50px;
|
||||
margin-top: 4em;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
font-size: XX-small;
|
||||
font-size: small;
|
||||
color: #999999;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
a, a:link, a:active, a:visited {
|
||||
color: #CC3334;
|
||||
padding-left: 1px;
|
||||
padding-right: 1px;
|
||||
padding: 0.25em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
|
@ -25,27 +23,27 @@ a:hover {
|
|||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
div.footer a {
|
||||
text-decoration: underline;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #fff;
|
||||
font-size: small;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
padding-left:8px;
|
||||
margin-top:0;
|
||||
margin-bottom:0;
|
||||
font-size:medium;
|
||||
}
|
||||
|
||||
h2 {
|
||||
background-color: #aaaaaa;
|
||||
font-size : small;
|
||||
margin: .3em 0;
|
||||
padding: .3em 0 .1em .3em;
|
||||
border-top: 1px solid #777777;
|
||||
font-size:medium;
|
||||
}
|
||||
|
||||
h2 a, h2 a:link, h2 a:active, h2 a:visited {
|
||||
|
|
@ -60,27 +58,27 @@ h2 a:hover {
|
|||
}
|
||||
|
||||
h4.alert {
|
||||
border: 1px solid #666666;
|
||||
border: 1px solid #666666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h4.warning {
|
||||
border: 1px solid #ED2E38;
|
||||
border: 1px solid #ED2E38;
|
||||
background-color: #F6979C;
|
||||
color: #000;
|
||||
}
|
||||
h4.error {
|
||||
color:#fff;
|
||||
color:#fff;
|
||||
background:#c00;
|
||||
}
|
||||
h4.notice {
|
||||
border: 1px solid #007E00;
|
||||
border: 1px solid #007E00;
|
||||
background-color: #c2ffc2;
|
||||
color: #007E00;
|
||||
}
|
||||
|
||||
span.tag {
|
||||
font-size: x-small;
|
||||
font-size: small;
|
||||
background-color: #CCE7FF;
|
||||
color: #000;
|
||||
padding: 1px;
|
||||
|
|
@ -88,12 +86,12 @@ span.tag {
|
|||
}
|
||||
|
||||
span.r {
|
||||
font-size: XX-small;
|
||||
font-size: small;
|
||||
color: #777777;
|
||||
}
|
||||
|
||||
span.prj, span.ctx{
|
||||
font-size: X-small;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
#ctx, #pjr {
|
||||
|
|
@ -104,7 +102,7 @@ span.prj, span.ctx{
|
|||
padding: 0.1em 0;
|
||||
}
|
||||
|
||||
/* Draw attention to some text
|
||||
/* Draw attention to some text
|
||||
Same format as traffic lights */
|
||||
.red {
|
||||
color: #fff;
|
||||
|
|
@ -162,7 +160,7 @@ ul.c {
|
|||
}
|
||||
|
||||
ul.c li {
|
||||
padding: 0;
|
||||
padding: 0.25em 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
|
@ -178,9 +176,7 @@ span.r {
|
|||
background-color: #000000;
|
||||
clear: both;
|
||||
color: #EEEEEE;
|
||||
height: 45px;
|
||||
left: 0;
|
||||
margin-bottom: 5px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
|
@ -190,19 +186,16 @@ span.r {
|
|||
.nav {
|
||||
color: #fff;
|
||||
background: #000;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
#topbar .nav {
|
||||
padding-left:8px;
|
||||
margin-bottom:0.3em;
|
||||
padding:0;
|
||||
overflow:auto;
|
||||
list-style:none;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
.nav a, .nav a:link, .nav a:active, .nav a:visited {
|
||||
background: #666;
|
||||
color: #fff;
|
||||
padding-top: 1.0em;
|
||||
padding-bottom: 0.5em;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.nav a:focus, .nav a:hover, .nav a:active {
|
||||
|
|
@ -214,6 +207,17 @@ span.r {
|
|||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
.nav li.link {
|
||||
width:20%;
|
||||
float:left;
|
||||
outline:black solid 1px;
|
||||
}
|
||||
.nav .link a {
|
||||
font-size:small;
|
||||
text-align:center;
|
||||
display:block;
|
||||
}
|
||||
|
||||
#database_auth_form table td {
|
||||
width:7em;
|
||||
}
|
||||
|
|
@ -221,10 +225,54 @@ span.r {
|
|||
table.c {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.mobile-done {
|
||||
display:inline;
|
||||
}
|
||||
|
||||
input#todo_description, input#tag_list, textarea#todo_notes, select#todo_project_id, select#todo_context_id {
|
||||
input#todo_description, input#todo_tag_list, textarea#todo_notes, select#todo_project_id, select#todo_context_id {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.next-prev-project {
|
||||
overflow:auto;
|
||||
padding:0;
|
||||
margin:0;
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
.prev,
|
||||
.next {
|
||||
float:left;
|
||||
width:50%;
|
||||
}
|
||||
|
||||
.next {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.prev a,
|
||||
.next a {
|
||||
display:block;
|
||||
height:1em;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.prev a {
|
||||
background: url(assets/previous.png) left center no-repeat;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.prev a:hover {
|
||||
background: #cc3334 url(assets/previous.png) left center no-repeat;
|
||||
}
|
||||
|
||||
.next a {
|
||||
text-align:right;
|
||||
background: url(assets/next.png) right center no-repeat;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.next a:hover {
|
||||
background: #cc3334 url(assets/next.png) right center no-repeat;
|
||||
}
|
||||
|
|
@ -71,23 +71,23 @@ h4.alert {
|
|||
}
|
||||
|
||||
h4.warning {
|
||||
border: 1px solid #ED2E38;
|
||||
border: 1px solid #ED2E38;
|
||||
background-color: #F6979C;
|
||||
color: #000000;
|
||||
}
|
||||
h4.error {
|
||||
color:#fff;
|
||||
color:#fff;
|
||||
background:#c00;
|
||||
}
|
||||
h4.notice {
|
||||
border: 1px solid #007E00;
|
||||
border: 1px solid #007E00;
|
||||
background-color: #c2ffc2;
|
||||
color: #007E00;
|
||||
}
|
||||
|
||||
/*#notice {
|
||||
padding: 2px;
|
||||
border: 1px solid #007E00;
|
||||
border: 1px solid #007E00;
|
||||
background-color: #c2ffc2;
|
||||
color: #007E00;
|
||||
margin-bottom: 15px;
|
||||
|
|
@ -147,8 +147,8 @@ input.login_text {
|
|||
width:200px;
|
||||
}
|
||||
input.open_id {
|
||||
background: url(../images/open-id-login-bg.gif) no-repeat;
|
||||
background-color: #fff;
|
||||
background: url(/assets/open-id-login-bg.gif) no-repeat;
|
||||
background-color: #fff;
|
||||
background-position: 0 50%;
|
||||
color: #000;
|
||||
padding-left: 18px;
|
||||
|
|
@ -6,6 +6,10 @@
|
|||
float:right;
|
||||
}
|
||||
|
||||
div.depends_on {
|
||||
clear:both;
|
||||
}
|
||||
|
||||
div.depends_on label {
|
||||
float: left;
|
||||
}
|
||||
|
|
@ -102,35 +106,35 @@ h3 {
|
|||
|
||||
/* Rules for the icon links */
|
||||
|
||||
img.edit_item {background-image: url(../images/edit_off.png); background-repeat: no-repeat; border: none;}
|
||||
a:hover img.edit_item {background-image: url(../images/edit_on.png); background-color: transparent; background-repeat: no-repeat; border: none;}
|
||||
img.edit_item {background-image: url(/assets/edit_off.png); background-repeat: no-repeat; border: none;}
|
||||
a:hover img.edit_item {background-image: url(/assets/edit_on.png); background-color: transparent; background-repeat: no-repeat; border: none;}
|
||||
|
||||
img.delete_item {background-image: url(../images/delete_off.png); background-repeat: no-repeat; border: none;}
|
||||
a:hover img.delete_item {background-image: url(../images/delete_on.png);background-color: transparent;background-repeat: no-repeat; border: none;}
|
||||
img.delete_item {background-image: url(/assets/delete_off.png); background-repeat: no-repeat; border: none;}
|
||||
a:hover img.delete_item {background-image: url(/assets/delete_on.png);background-color: transparent;background-repeat: no-repeat; border: none;}
|
||||
|
||||
a.undecorated_link {background-color:transparent;color:transparent;}
|
||||
img.todo_star {background-image: url(../images/staricons.png); background-repeat: no-repeat; border:none; background-position: -32px 0px;}
|
||||
img.todo_star {background-image: url(/assets/staricons.png); background-repeat: no-repeat; border:none; background-position: -32px 0px;}
|
||||
img.todo_star.starred{ background-position: 0px 0px; }
|
||||
a:hover img.todo_star { background-position: -48px 0px;}
|
||||
a:hover img.todo_star.starred { background-position: -16px 0px; }
|
||||
|
||||
a.to_top {background: transparent url(../images/top_off.png) no-repeat;}
|
||||
a.to_top:hover {background: transparent url(../images/top_on.png) no-repeat;}
|
||||
a.to_top {background: transparent url(/assets/top_off.png) no-repeat;}
|
||||
a.to_top:hover {background: transparent url(/assets/top_on.png) no-repeat;}
|
||||
|
||||
a.up {background: transparent url(../images/up_off.png) no-repeat;}
|
||||
a.up:hover {background: transparent url(../images/up_on.png) no-repeat;}
|
||||
a.up {background: transparent url(/assets/up_off.png) no-repeat;}
|
||||
a.up:hover {background: transparent url(/assets/up_on.png) no-repeat;}
|
||||
|
||||
a.down {background: transparent url(../images/down_off.png) no-repeat;}
|
||||
a.down:hover {background: transparent url(../images/down_on.png) no-repeat;}
|
||||
a.down {background: transparent url(/assets/down_off.png) no-repeat;}
|
||||
a.down:hover {background: transparent url(/assets/down_on.png) no-repeat;}
|
||||
|
||||
a.to_bottom {background: transparent url(../images/bottom_off.png) no-repeat;}
|
||||
a.to_bottom:hover {background: transparent url(../images/bottom_on.png) no-repeat;}
|
||||
a.to_bottom {background: transparent url(/assets/bottom_off.png) no-repeat;}
|
||||
a.to_bottom:hover {background: transparent url(/assets/bottom_on.png) no-repeat;}
|
||||
|
||||
a.show_notes, a.link_to_notes {background-image: url(../images/notes_off.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
||||
a.show_notes:hover, a.link_to_notes:hover {background-image: url(../images/notes_on.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
||||
a.show_notes, a.link_to_notes {background-image: url(/assets/notes_off.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
||||
a.show_notes:hover, a.link_to_notes:hover {background-image: url(/assets/notes_on.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
||||
|
||||
a.show_successors, a.link_to_successors {background-image: url(../images/successor_off.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
||||
a.show_successors:hover, a.link_to_successors:hover {background-image: url(../images/successor_on.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
||||
a.show_successors, a.link_to_successors {background-image: url(/assets/successor_off.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
||||
a.show_successors:hover, a.link_to_successors:hover {background-image: url(/assets/successor_on.png); background-repeat: no-repeat; padding: 1px; background-color: transparent;}
|
||||
|
||||
/* Structural divs */
|
||||
|
||||
|
|
@ -251,7 +255,7 @@ a.show_successors:hover, a.link_to_successors:hover {background-image: url(../im
|
|||
|
||||
#develop-notify-bar {
|
||||
line-height:0.5;
|
||||
background-image: url(/images/construction.gif);
|
||||
background-image: url(/assets/construction.gif);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
|
|
@ -361,7 +365,7 @@ div#input_box {
|
|||
}
|
||||
|
||||
.selected_predecessors {
|
||||
clear:right;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
form.new_todo_form ul.predecessor_list,
|
||||
|
|
@ -380,8 +384,8 @@ ul.predecessor_list li {
|
|||
}
|
||||
|
||||
/* deleting dependency from new form of a todo */
|
||||
img.icon_delete_dep {width: 10px; background-image: url(../images/icon_delete.png); background-repeat: no-repeat; background-position: -9px 0; border: none; color:black;}
|
||||
a:hover img.icon_delete_dep {width: 10px; background-image: url(../images/icon_delete.png); background-repeat: no-repeat; background-position: 0 0; border: none; color:black; background-color: black;}
|
||||
img.icon_delete_dep {width: 10px; background-image: url(/assets/icon_delete.png); background-repeat: no-repeat; background-position: -9px 0; border: none; color:black;}
|
||||
a:hover img.icon_delete_dep {width: 10px; background-image: url(/assets/icon_delete.png); background-repeat: no-repeat; background-position: 0 0; border: none; color:black; background-color: black;}
|
||||
a.icon_delete_dep:hover {width: 10px; background-color: black;}
|
||||
|
||||
/* deleting dependency from edit form of a todo */
|
||||
|
|
@ -426,13 +430,13 @@ input.item-checkbox {
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.rec_description, .description {
|
||||
.rec_description, .description, .project_description, .context_description {
|
||||
margin-left: 80px;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.rec_description {
|
||||
margin-left: 80px;
|
||||
.project_description, .context_description {
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
.stale_l1 {
|
||||
|
|
@ -546,6 +550,10 @@ div.note_wrapper p {
|
|||
display: inline;
|
||||
}
|
||||
|
||||
div.note_note {
|
||||
margin-left:24px;
|
||||
}
|
||||
|
||||
div.note_footer {
|
||||
border-top: 1px solid #999;
|
||||
padding-top: 3px;
|
||||
|
|
@ -857,6 +865,7 @@ form {
|
|||
padding: 10px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.inline-form {
|
||||
border: none;
|
||||
padding: 3px;
|
||||
|
|
@ -1152,7 +1161,7 @@ ul#prefs {list-style-type: disc; margin-left: 15px;}
|
|||
}
|
||||
|
||||
input.open_id {
|
||||
background: url(../images/open-id-login-bg.gif) no-repeat;
|
||||
background: url(/assets/open-id-login-bg.gif) no-repeat;
|
||||
background-color: #fff;
|
||||
background-position: 0 50%;
|
||||
color: #000;
|
||||
|
|
@ -1304,7 +1313,7 @@ button.positive, .widgets a.positive{
|
|||
}
|
||||
|
||||
.blockUI.blockOverlay {
|
||||
background-image:url('../images/waiting.gif');
|
||||
background-image:url('/assets/waiting.gif');
|
||||
background-repeat:no-repeat;
|
||||
background-position:center center;
|
||||
background-color:white;
|
||||
|
|
@ -1312,21 +1321,21 @@ button.positive, .widgets a.positive{
|
|||
}
|
||||
|
||||
.bigWaiting {
|
||||
background-image:url('../images/bigWaiting.gif');
|
||||
background-image:url('/assets/bigWaiting.gif');
|
||||
background-repeat:no-repeat;
|
||||
background-position:center 20%;
|
||||
background-color:white;
|
||||
}
|
||||
|
||||
.blackWaiting {
|
||||
background-image:url('../images/blackWaiting.gif');
|
||||
background-image:url('/assets/blackWaiting.gif');
|
||||
background-repeat:no-repeat;
|
||||
background-position:center center;
|
||||
background-color:black;
|
||||
}
|
||||
|
||||
.bigBlackWaiting {
|
||||
background-image:url('../images/bigBlackWaiting.gif');
|
||||
background-image:url('/assets/bigBlackWaiting.gif');
|
||||
background-repeat:no-repeat;
|
||||
background-position:center center;
|
||||
background-color:black;
|
||||
|
|
@ -1415,9 +1424,9 @@ div.auto_complete ul strong.highlight {
|
|||
}
|
||||
|
||||
.ui-datepicker {
|
||||
z-index: 1000;
|
||||
z-index: 1000 !important;
|
||||
}
|
||||
|
||||
.ui-autocomplete-loading {
|
||||
background: white url('/images/ui-anim_basic_16x16.gif') right center no-repeat;
|
||||
background: white url('/assets/ui-anim_basic_16x16.gif') right center no-repeat;
|
||||
}
|
||||
|
|
@ -9,12 +9,11 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
protect_from_forgery
|
||||
|
||||
helper :application
|
||||
include LoginSystem
|
||||
helper_method :current_user, :prefs, :format_date, :markdown
|
||||
|
||||
layout proc{ |controller| controller.mobile? ? "mobile" : "standard" }
|
||||
exempt_from_layout /\.js\.erb$/
|
||||
# exempt_from_layout /\.js\.erb$/
|
||||
|
||||
before_filter :check_for_deprecated_password_hash
|
||||
before_filter :set_session_expiration
|
||||
|
|
@ -73,32 +72,19 @@ class ApplicationController < ActionController::Base
|
|||
render :text => message, :status => status
|
||||
end
|
||||
|
||||
# def rescue_action(exception)
|
||||
# log_error(exception) if logger
|
||||
# respond_to do |format|
|
||||
# format.html do
|
||||
# notify :warning, "An error occurred on the server."
|
||||
# render :action => "index"
|
||||
# end
|
||||
# format.js { render :action => 'error' }
|
||||
# format.xml { render :text => 'An error occurred on the server.' + $! }
|
||||
# end
|
||||
# end
|
||||
|
||||
# Returns a count of next actions in the given context or project The result
|
||||
# is count and a string descriptor, correctly pluralised if there are no
|
||||
# actions or multiple actions
|
||||
#
|
||||
def count_undone_todos_phrase(todos_parent, string="actions")
|
||||
def count_undone_todos_phrase(todos_parent)
|
||||
count = count_undone_todos(todos_parent)
|
||||
deferred_count = count_deferred_todos(todos_parent)
|
||||
if count == 0 && deferred_count > 0
|
||||
word = deferred_count == 1 ? string.singularize : string.pluralize
|
||||
word = "deferred " + word
|
||||
deferred_count.to_s + " " + word
|
||||
word = "#{I18n.t('common.deferred')} #{I18n.t('common.actions_midsentence', :count => deferred_count)}"
|
||||
return "#{deferred_count.to_s} #{word}".html_safe
|
||||
else
|
||||
word = count == 1 ? string.singularize : string.pluralize
|
||||
count.to_s + " " + word
|
||||
word = I18n.t('common.actions_midsentence', :count => count)
|
||||
return "#{count} #{word}".html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -143,14 +129,6 @@ class ApplicationController < ActionController::Base
|
|||
return json_elems
|
||||
end
|
||||
|
||||
# Uses RedCloth to transform text using either Textile or Markdown Need to
|
||||
# require redcloth above RedCloth 3.0 or greater is needed to use Markdown,
|
||||
# otherwise it only handles Textile
|
||||
#
|
||||
def markdown(text)
|
||||
RedCloth.new(text).to_html
|
||||
end
|
||||
|
||||
# Here's the concept behind this "mobile content negotiation" hack: In
|
||||
# addition to the main, AJAXy Web UI, Tracks has a lightweight low-feature
|
||||
# 'mobile' version designed to be suitablef or use from a phone or PDA. It
|
||||
|
|
@ -211,6 +189,10 @@ class ApplicationController < ActionController::Base
|
|||
super # handle xml http auth via our own login code
|
||||
end
|
||||
end
|
||||
|
||||
def sanitize(arg)
|
||||
ActionController::Base.helpers.sanitize(arg)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
|
|
@ -223,7 +205,7 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
def redirect_back_or_home
|
||||
respond_to do |format|
|
||||
format.html { redirect_back_or_default home_url }
|
||||
format.html { redirect_back_or_default root_url }
|
||||
format.m { redirect_back_or_default mobile_url }
|
||||
end
|
||||
end
|
||||
|
|
@ -260,24 +242,26 @@ class ApplicationController < ActionController::Base
|
|||
self.class.prefered_auth?
|
||||
end
|
||||
|
||||
# all completed todos [today@00:00, today@now]
|
||||
def get_done_today(completed_todos, includes = {:include => Todo::DEFAULT_INCLUDES})
|
||||
start_of_this_day = Time.zone.now.beginning_of_day
|
||||
completed_todos.completed_after(start_of_this_day).all(includes)
|
||||
end
|
||||
|
||||
# all completed todos [begin_of_week, start_of_today]
|
||||
def get_done_this_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_after(start_of_this_week).completed_before(start_of_this_day).all(includes)
|
||||
completed_todos.completed_before(start_of_this_day).completed_after(start_of_this_week).all(includes)
|
||||
end
|
||||
|
||||
# all completed todos [begin_of_month, begin_of_week]
|
||||
def get_done_this_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_after(start_of_this_month).completed_before(start_of_this_week).all(includes)
|
||||
completed_todos.completed_before(start_of_this_week).completed_after(start_of_this_month).all(includes)
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def parse_date_per_user_prefs( s )
|
||||
|
|
|
|||
324
app/controllers/application_controller.rb.rails2
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
# The filters added to this controller will be run for all controllers in the
|
||||
# application. Likewise will all the methods added be available for all
|
||||
# controllers.
|
||||
|
||||
require_dependency "login_system"
|
||||
require_dependency "tracks/source_view"
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
|
||||
protect_from_forgery
|
||||
|
||||
helper :application
|
||||
include LoginSystem
|
||||
helper_method :current_user, :prefs, :format_date
|
||||
|
||||
layout proc{ |controller| controller.mobile? ? "mobile" : "standard" }
|
||||
exempt_from_layout /\.js\.erb$/
|
||||
|
||||
before_filter :check_for_deprecated_password_hash
|
||||
before_filter :set_session_expiration
|
||||
before_filter :set_time_zone
|
||||
before_filter :set_zindex_counter
|
||||
before_filter :set_locale
|
||||
prepend_before_filter :login_required
|
||||
prepend_before_filter :enable_mobile_content_negotiation
|
||||
after_filter :set_charset
|
||||
|
||||
# By default, sets the charset to UTF-8 if it isn't already set
|
||||
def set_charset
|
||||
headers["Content-Type"] ||= "text/html; charset=UTF-8"
|
||||
end
|
||||
|
||||
def set_locale
|
||||
locale = params[:locale] # specifying a locale in the request takes precedence
|
||||
locale = locale || prefs.locale unless current_user.nil? # otherwise, the locale of the currently logged in user takes over
|
||||
locale = locale || request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first if request.env['HTTP_ACCEPT_LANGUAGE']
|
||||
I18n.locale = locale.nil? ? I18n.default_locale : (I18n::available_locales.include?(locale.to_sym) ? locale : I18n.default_locale)
|
||||
logger.debug("Selected '#{I18n.locale}' as locale")
|
||||
end
|
||||
|
||||
def set_session_expiration
|
||||
# http://wiki.rubyonrails.com/rails/show/HowtoChangeSessionOptions
|
||||
unless session == nil
|
||||
return if self.controller_name == 'feed' or session['noexpiry'] == "on"
|
||||
# If the method is called by the feed controller (which we don't have
|
||||
# under session control) or if we checked the box to keep logged in on
|
||||
# login don't set the session expiry time.
|
||||
if session
|
||||
# Get expiry time (allow ten seconds window for the case where we have
|
||||
# none)
|
||||
expiry_time = session['expiry_time'] || Time.now + 10
|
||||
if expiry_time < Time.now
|
||||
# Too late, matey... bang goes your session!
|
||||
reset_session
|
||||
else
|
||||
# Okay, you get another hour
|
||||
session['expiry_time'] = Time.now + (60*60)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Redirects to change_password_user_path if the current user uses a
|
||||
# deprecated password hashing algorithm.
|
||||
def check_for_deprecated_password_hash
|
||||
if current_user and current_user.uses_deprecated_password?
|
||||
notify :warning, t('users.you_have_to_reset_your_password')
|
||||
redirect_to change_password_user_path current_user
|
||||
end
|
||||
end
|
||||
|
||||
def render_failure message, status = 404
|
||||
render :text => message, :status => status
|
||||
end
|
||||
|
||||
# def rescue_action(exception)
|
||||
# log_error(exception) if logger
|
||||
# respond_to do |format|
|
||||
# format.html do
|
||||
# notify :warning, "An error occurred on the server."
|
||||
# render :action => "index"
|
||||
# end
|
||||
# format.js { render :action => 'error' }
|
||||
# format.xml { render :text => 'An error occurred on the server.' + $! }
|
||||
# end
|
||||
# end
|
||||
|
||||
# Returns a count of next actions in the given context or project The result
|
||||
# is count and a string descriptor, correctly pluralised if there are no
|
||||
# actions or multiple actions
|
||||
#
|
||||
def count_undone_todos_phrase(todos_parent)
|
||||
count = count_undone_todos(todos_parent)
|
||||
deferred_count = count_deferred_todos(todos_parent)
|
||||
if count == 0 && deferred_count > 0
|
||||
word = I18n.t('common.actions_midsentence', :count => deferred_count)
|
||||
word = I18n.t('common.deferred') + " " + word
|
||||
return deferred_count.to_s + " " + word
|
||||
else
|
||||
word = I18n.t('common.actions_midsentence', :count => count)
|
||||
return count.to_s + " " + word
|
||||
end
|
||||
end
|
||||
|
||||
def count_undone_todos(todos_parent)
|
||||
if todos_parent.nil?
|
||||
count = 0
|
||||
elsif (todos_parent.is_a?(Project) && todos_parent.hidden?)
|
||||
count = eval "@project_project_hidden_todo_counts[#{todos_parent.id}]"
|
||||
else
|
||||
count = eval "@#{todos_parent.class.to_s.downcase}_not_done_counts[#{todos_parent.id}]"
|
||||
end
|
||||
count || 0
|
||||
end
|
||||
|
||||
def count_deferred_todos(todos_parent)
|
||||
if todos_parent.nil?
|
||||
count = 0
|
||||
else
|
||||
count = todos_parent.todos.deferred.count
|
||||
end
|
||||
end
|
||||
|
||||
# Convert a date object to the format specified in the user's preferences in
|
||||
# config/settings.yml
|
||||
#
|
||||
def format_date(date)
|
||||
return date ? date.in_time_zone(prefs.time_zone).strftime("#{prefs.date_format}") : ''
|
||||
end
|
||||
|
||||
def for_autocomplete(coll, substr)
|
||||
if substr # protect agains empty request
|
||||
filtered = coll.find_all{|item| item.name.downcase.include? substr.downcase}
|
||||
json_elems = Array[*filtered.map{ |e| {:id => e.id.to_s, :value => e.name} }].to_json
|
||||
return json_elems
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
def format_dependencies_as_json_for_auto_complete(entries)
|
||||
json_elems = Array[*entries.map{ |e| {:value => e.id.to_s, :label => e.specification} }].to_json
|
||||
return json_elems
|
||||
end
|
||||
|
||||
# Here's the concept behind this "mobile content negotiation" hack: In
|
||||
# addition to the main, AJAXy Web UI, Tracks has a lightweight low-feature
|
||||
# 'mobile' version designed to be suitablef or use from a phone or PDA. It
|
||||
# makes some sense that tne pages of that mobile version are simply alternate
|
||||
# representations of the same Todo resources. The implementation goal was to
|
||||
# treat mobile as another format and be able to use respond_to to render both
|
||||
# versions. Unfortunately, I ran into a lot of trouble simply registering a
|
||||
# new mime type 'text/html' with format :m because :html already is linked to
|
||||
# that mime type and the new registration was forcing all html requests to be
|
||||
# rendered in the mobile view. The before_filter and after_filter hackery
|
||||
# below accomplishs that implementation goal by using a 'fake' mime type
|
||||
# during the processing and then setting it to 'text/html' in an
|
||||
# 'after_filter' -LKM 2007-04-01
|
||||
def mobile?
|
||||
return params[:format] == 'm'
|
||||
end
|
||||
|
||||
def enable_mobile_content_negotiation
|
||||
if mobile?
|
||||
request.format = :m
|
||||
end
|
||||
end
|
||||
|
||||
def create_todo_from_recurring_todo(rt, date=nil)
|
||||
# create todo and initialize with data from recurring_todo rt
|
||||
todo = current_user.todos.build( { :description => rt.description, :notes => rt.notes, :project_id => rt.project_id, :context_id => rt.context_id})
|
||||
todo.recurring_todo_id = rt.id
|
||||
|
||||
# set dates
|
||||
todo.due = rt.get_due_date(date)
|
||||
|
||||
show_from_date = rt.get_show_from_date(date)
|
||||
if show_from_date.nil?
|
||||
todo.show_from=nil
|
||||
else
|
||||
# make sure that show_from is not in the past
|
||||
todo.show_from = show_from_date < Time.zone.now ? nil : show_from_date
|
||||
end
|
||||
|
||||
saved = todo.save
|
||||
if saved
|
||||
todo.tag_with(rt.tag_list)
|
||||
todo.tags.reload
|
||||
end
|
||||
|
||||
# increate number of occurences created from recurring todo
|
||||
rt.inc_occurences
|
||||
|
||||
# mark recurring todo complete if there are no next actions left
|
||||
checkdate = todo.due.nil? ? todo.show_from : todo.due
|
||||
rt.toggle_completion! unless rt.has_next_todo(checkdate)
|
||||
|
||||
return saved ? todo : nil
|
||||
end
|
||||
|
||||
def handle_unverified_request
|
||||
unless request.format=="application/xml"
|
||||
super # handle xml http auth via our own login code
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def admin_login_required
|
||||
unless User.find_by_id_and_is_admin(session['user_id'], true)
|
||||
render :text => t('errors.user_unauthorized'), :status => 401
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def redirect_back_or_home
|
||||
respond_to do |format|
|
||||
format.html { redirect_back_or_default home_url }
|
||||
format.m { redirect_back_or_default mobile_url }
|
||||
end
|
||||
end
|
||||
|
||||
def boolean_param(param_name)
|
||||
return false if param_name.blank?
|
||||
s = params[param_name]
|
||||
return false if s.blank? || s == false || s =~ /^false$/i
|
||||
return true if s == true || s =~ /^true$/i
|
||||
raise ArgumentError.new("invalid value for Boolean: \"#{s}\"")
|
||||
end
|
||||
|
||||
def self.openid_enabled?
|
||||
Tracks::Config.openid_enabled?
|
||||
end
|
||||
|
||||
def openid_enabled?
|
||||
self.class.openid_enabled?
|
||||
end
|
||||
|
||||
def self.cas_enabled?
|
||||
Tracks::Config.cas_enabled?
|
||||
end
|
||||
|
||||
def cas_enabled?
|
||||
self.class.cas_enabled?
|
||||
end
|
||||
|
||||
def self.prefered_auth?
|
||||
Tracks::Config.prefered_auth?
|
||||
end
|
||||
|
||||
def prefered_auth?
|
||||
self.class.prefered_auth?
|
||||
end
|
||||
|
||||
# all completed todos [today@00:00, today@now]
|
||||
def get_done_today(completed_todos, includes = {:include => Todo::DEFAULT_INCLUDES})
|
||||
start_of_this_day = Time.zone.now.beginning_of_day
|
||||
completed_todos.completed_after(start_of_this_day).all(includes)
|
||||
end
|
||||
|
||||
# all completed todos [begin_of_week, start_of_today]
|
||||
def get_done_this_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).all(includes)
|
||||
end
|
||||
|
||||
# all completed todos [begin_of_month, begin_of_week]
|
||||
def get_done_this_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).all(includes)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_date_per_user_prefs( s )
|
||||
prefs.parse_date(s)
|
||||
end
|
||||
|
||||
def init_data_for_sidebar
|
||||
@completed_projects = current_user.projects.completed
|
||||
@hidden_projects = current_user.projects.hidden
|
||||
@active_projects = current_user.projects.active
|
||||
|
||||
@active_contexts = current_user.contexts.active
|
||||
@hidden_contexts = current_user.contexts.hidden
|
||||
|
||||
init_not_done_counts
|
||||
if prefs.show_hidden_projects_in_sidebar
|
||||
init_project_hidden_todo_counts(['project'])
|
||||
end
|
||||
end
|
||||
|
||||
def init_not_done_counts(parents = ['project','context'])
|
||||
parents.each do |parent|
|
||||
eval("@#{parent}_not_done_counts = @#{parent}_not_done_counts || current_user.todos.active.count(:group => :#{parent}_id)")
|
||||
end
|
||||
end
|
||||
|
||||
def init_project_hidden_todo_counts(parents = ['project','context'])
|
||||
parents.each do |parent|
|
||||
eval("@#{parent}_project_hidden_todo_counts = @#{parent}_project_hidden_todo_counts || current_user.todos.count(:conditions => ['state = ? or state = ?', 'project_hidden', 'active'], :group => :#{parent}_id)")
|
||||
end
|
||||
end
|
||||
|
||||
# Set the contents of the flash message from a controller Usage: notify
|
||||
# :warning, "This is the message" Sets the flash of type 'warning' to "This is
|
||||
# the message"
|
||||
def notify(type, message)
|
||||
flash[type] = message
|
||||
logger.error("ERROR: #{message}") if type == :error
|
||||
end
|
||||
|
||||
def set_time_zone
|
||||
Time.zone = current_user.prefs.time_zone if logged_in?
|
||||
end
|
||||
|
||||
def set_zindex_counter
|
||||
# this counter can be used to handle the IE z-index bug
|
||||
@z_index_counter = 500
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -9,32 +9,36 @@ class ContextsController < ApplicationController
|
|||
prepend_before_filter :login_or_feed_token_required, :only => [:index]
|
||||
|
||||
def index
|
||||
# #true is passed here to force an immediate load so that size and empty?
|
||||
# checks later don't result in separate SQL queries
|
||||
@active_contexts = current_user.contexts.active(true)
|
||||
@hidden_contexts = current_user.contexts.hidden(true)
|
||||
@active_contexts = current_user.contexts.active
|
||||
@hidden_contexts = current_user.contexts.hidden
|
||||
@new_context = current_user.contexts.build
|
||||
init_not_done_counts(['context'])
|
||||
|
||||
# save all contexts here as @new_context will add an empty one to current_user.contexts
|
||||
@all_contexts = @active_contexts + @hidden_contexts
|
||||
@count = @all_contexts.size
|
||||
|
||||
init_not_done_counts(['context'])
|
||||
|
||||
respond_to do |format|
|
||||
format.html &render_contexts_html
|
||||
format.m &render_contexts_mobile
|
||||
format.xml { render :xml => @all_contexts.to_xml( :except => :user_id ) }
|
||||
format.rss &render_contexts_rss_feed
|
||||
format.atom &render_contexts_atom_feed
|
||||
format.rss do
|
||||
@feed_title = 'Tracks Contexts'
|
||||
@feed_description = "Lists all the contexts for #{current_user.display_name}"
|
||||
end
|
||||
format.atom do
|
||||
@feed_title = 'Tracks Contexts'
|
||||
@feed_description = "Lists all the contexts for #{current_user.display_name}"
|
||||
end
|
||||
format.text do
|
||||
@all_contexts = current_user.contexts.all
|
||||
# somehow passing Mime::TEXT using content_type to render does not work
|
||||
headers['Content-Type']=Mime::TEXT.to_s
|
||||
render :action => 'index', :layout => false, :content_type => Mime::TEXT
|
||||
end
|
||||
format.autocomplete { render :text => for_autocomplete(@active_contexts + @hidden_contexts, params[:term])}
|
||||
format.autocomplete &render_autocomplete
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def show
|
||||
@contexts = current_user.contexts(true)
|
||||
if @context.nil?
|
||||
|
|
@ -51,24 +55,13 @@ class ContextsController < ApplicationController
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Example XML usage: curl -H 'Accept: application/xml' -H 'Content-Type:
|
||||
# application/xml'
|
||||
# -u username:password
|
||||
# -d '<request><context><name>new context_name</name></context></request>'
|
||||
# http://our.tracks.host/contexts
|
||||
#
|
||||
|
||||
def create
|
||||
if params[:format] == 'application/xml' && params['exception']
|
||||
render_failure "Expected post format is valid xml like so: <request><context><name>context name</name></context></request>.", 400
|
||||
render_failure "Expected post format is valid xml like so: <context><name>context name</name></context>.", 400
|
||||
return
|
||||
end
|
||||
@context = current_user.contexts.build
|
||||
params_are_invalid = true
|
||||
if (params['context'] || (params['request'] && params['request']['context']))
|
||||
@context.attributes = params['context'] || params['request']['context']
|
||||
params_are_invalid = false
|
||||
end
|
||||
@context = current_user.contexts.build(params['context'])
|
||||
@saved = @context.save
|
||||
@context_not_done_counts = { @context.id => 0 }
|
||||
respond_to do |format|
|
||||
|
|
@ -76,10 +69,8 @@ class ContextsController < ApplicationController
|
|||
@down_count = current_user.contexts.size
|
||||
end
|
||||
format.xml do
|
||||
if @context.new_record? && params_are_invalid
|
||||
render_failure "Expected post format is valid xml like so: <request><context><name>context name</name></context></request>.", 400
|
||||
elsif @context.new_record?
|
||||
render_failure @context.errors.to_xml, 409
|
||||
if @context.new_record?
|
||||
render_failure @context.errors.to_xml.html_safe, 409
|
||||
else
|
||||
head :created, :location => context_url(@context)
|
||||
end
|
||||
|
|
@ -218,8 +209,8 @@ class ContextsController < ApplicationController
|
|||
@active_contexts = current_user.contexts.active
|
||||
@hidden_contexts = current_user.contexts.hidden
|
||||
@down_count = @active_contexts.size + @hidden_contexts.size
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
|
||||
render :action => 'index_mobile'
|
||||
cookies[:mobile_url]= {:value => request.fullpath, :secure => SITE_CONFIG['secure_cookies']}
|
||||
render
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -228,24 +219,18 @@ class ContextsController < ApplicationController
|
|||
@page_title = "TRACKS::List actions in "+@context.name
|
||||
@not_done = @not_done_todos.select {|t| t.context_id == @context.id }
|
||||
@down_count = @not_done.size
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
|
||||
cookies[:mobile_url]= {:value => request.fullpath, :secure => SITE_CONFIG['secure_cookies']}
|
||||
@mobile_from_context = @context.id
|
||||
render :action => 'mobile_show_context'
|
||||
render
|
||||
end
|
||||
end
|
||||
|
||||
def render_contexts_rss_feed
|
||||
|
||||
def render_autocomplete
|
||||
lambda do
|
||||
render_rss_feed_for current_user.contexts.all, :feed => feed_options,
|
||||
:item => { :description => lambda { |c| @template.summary(c, count_undone_todos_phrase(c)) } }
|
||||
end
|
||||
end
|
||||
|
||||
def render_contexts_atom_feed
|
||||
lambda do
|
||||
render_atom_feed_for current_user.contexts.all, :feed => feed_options,
|
||||
:item => { :description => lambda { |c| @template.summary(c, count_undone_todos_phrase(c)) },
|
||||
:author => lambda { |c| nil } }
|
||||
# first get active contexts with todos then those without
|
||||
filled_contexts = @active_contexts.reject { |ctx| ctx.todos.count == 0 } + @hidden_contexts.reject { |ctx| ctx.todos.count == 0 }
|
||||
empty_contexts = @active_contexts.find_all { |ctx| ctx.todos.count == 0 } + @hidden_contexts.find_all { |ctx| ctx.todos.count == 0 }
|
||||
render :text => for_autocomplete(filled_contexts + empty_contexts, params[:term])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -254,7 +239,7 @@ class ContextsController < ApplicationController
|
|||
end
|
||||
|
||||
def set_context_from_params
|
||||
@context = current_user.contexts.find_by_params(params)
|
||||
@context = current_user.contexts.find(params[:id])
|
||||
rescue
|
||||
@context = nil
|
||||
end
|
||||
|
|
@ -267,11 +252,8 @@ class ContextsController < ApplicationController
|
|||
def init_todos
|
||||
set_context_from_params
|
||||
unless @context.nil?
|
||||
@context.todos.send :with_scope, :find => { :include => Todo::DEFAULT_INCLUDES } do
|
||||
@done = @context.done_todos
|
||||
end
|
||||
|
||||
@max_completed = current_user.prefs.show_number_completed
|
||||
@done = @context.todos.completed.all(:limit => @max_completed)
|
||||
|
||||
# @not_done_todos = @context.not_done_todos TODO: Temporarily doing this
|
||||
# search manually until I can work out a way to do the same thing using
|
||||
|
|
@ -281,9 +263,12 @@ class ContextsController < ApplicationController
|
|||
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC",
|
||||
:include => Todo::DEFAULT_INCLUDES)
|
||||
|
||||
@deferred = @context.todos.deferred(:include => Todo::DEFAULT_INCLUDES)
|
||||
@pending = @context.todos.pending(:include => Todo::DEFAULT_INCLUDES)
|
||||
|
||||
@projects = current_user.projects
|
||||
|
||||
@count = @not_done_todos.size
|
||||
@count = @not_done_todos.count + @deferred.count + @pending.count
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ class DataController < ApplicationController
|
|||
def yaml_export
|
||||
all_tables = {}
|
||||
|
||||
all_tables['todos'] = current_user.todos.find(:all, :include => [:tags])
|
||||
all_tables['contexts'] = current_user.contexts.find(:all)
|
||||
all_tables['projects'] = current_user.projects.find(:all)
|
||||
all_tables['todos'] = current_user.todos.includes(:tags).all
|
||||
all_tables['contexts'] = current_user.contexts.all
|
||||
all_tables['projects'] = current_user.projects.all
|
||||
|
||||
todo_tag_ids = Tag.find_by_sql([
|
||||
"SELECT DISTINCT tags.id "+
|
||||
|
|
@ -34,13 +34,13 @@ class DataController < ApplicationController
|
|||
"WHERE recurring_todos.user_id=? "+
|
||||
"AND tags.id = taggings.tag_id " +
|
||||
"AND taggings.taggable_id = recurring_todos.id ", current_user.id])
|
||||
tags = Tag.find(:all, :conditions => ["id IN (?) OR id IN (?)", todo_tag_ids, rec_todo_tag_ids])
|
||||
taggings = Tagging.find(:all, :conditions => ["tag_id IN (?) OR tag_id IN(?)", todo_tag_ids, rec_todo_tag_ids])
|
||||
tags = Tag.where("id IN (?) OR id IN (?)", todo_tag_ids, rec_todo_tag_ids)
|
||||
taggings = Tagging.where("tag_id IN (?) OR tag_id IN(?)", todo_tag_ids, rec_todo_tag_ids)
|
||||
|
||||
all_tables['tags'] = tags
|
||||
all_tables['taggings'] = taggings
|
||||
all_tables['notes'] = current_user.notes.find(:all)
|
||||
all_tables['recurring_todos'] = current_user.recurring_todos.find(:all)
|
||||
all_tables['tags'] = tags.all
|
||||
all_tables['taggings'] = taggings.all
|
||||
all_tables['notes'] = current_user.notes.all
|
||||
all_tables['recurring_todos'] = current_user.recurring_todos.all
|
||||
|
||||
result = all_tables.to_yaml
|
||||
result.gsub!(/\n/, "\r\n") # TODO: general functionality for line endings
|
||||
|
|
@ -53,7 +53,7 @@ class DataController < ApplicationController
|
|||
csv << ["id", "Context", "Project", "Description", "Notes", "Tags",
|
||||
"Created at", "Due", "Completed at", "User ID", "Show from",
|
||||
"state"]
|
||||
current_user.todos.find(:all, :include => [:context, :project]).each do |todo|
|
||||
current_user.todos.include(:context, :project).all.each do |todo|
|
||||
csv << [todo.id, todo.context.name,
|
||||
todo.project_id.nil? ? "" : todo.project.name,
|
||||
todo.description,
|
||||
|
|
@ -72,19 +72,19 @@ class DataController < ApplicationController
|
|||
|
||||
def csv_notes
|
||||
content_type = 'text/csv'
|
||||
CSV::Writer.generate(result = "") do |csv|
|
||||
CSV.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
|
||||
current_user.notes.find(:all,:order=>"notes.created_at").each do |note|
|
||||
current_user.notes.reorder("notes.created_at").each do |note|
|
||||
# Format dates in ISO format for easy sorting in spreadsheet Print
|
||||
# context and project names for easy viewing
|
||||
csv << [note.id, note.user_id,
|
||||
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.updated_at.to_formatted_s(:db)]
|
||||
end
|
||||
end
|
||||
send_data(result, :filename => "notes.csv", :type => content_type)
|
||||
|
|
@ -103,17 +103,17 @@ class DataController < ApplicationController
|
|||
"WHERE recurring_todos.user_id=? "+
|
||||
"AND tags.id = taggings.tag_id " +
|
||||
"AND taggings.taggable_id = recurring_todos.id ", current_user.id])
|
||||
tags = Tag.find(:all, :conditions => ["id IN (?) OR id IN (?)", todo_tag_ids, rec_todo_tag_ids])
|
||||
taggings = Tagging.find(:all, :conditions => ["tag_id IN (?) OR tag_id IN(?)", todo_tag_ids, rec_todo_tag_ids])
|
||||
tags = Tag.where("id IN (?) OR id IN (?)", todo_tag_ids, rec_todo_tag_ids)
|
||||
taggings = Tagging.where("tag_id IN (?) OR tag_id IN(?)", todo_tag_ids, rec_todo_tag_ids)
|
||||
|
||||
result = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><tracks_data>"
|
||||
result << current_user.todos.find(:all).to_xml(:skip_instruct => true)
|
||||
result << current_user.contexts.find(:all).to_xml(:skip_instruct => true)
|
||||
result << current_user.projects.find(:all).to_xml(:skip_instruct => true)
|
||||
result << current_user.todos.to_xml(:skip_instruct => true)
|
||||
result << current_user.contexts.to_xml(:skip_instruct => true)
|
||||
result << current_user.projects.to_xml(:skip_instruct => true)
|
||||
result << tags.to_xml(:skip_instruct => true)
|
||||
result << taggings.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)
|
||||
result << current_user.notes.to_xml(:skip_instruct => true)
|
||||
result << current_user.recurring_todos.to_xml(:skip_instruct => true)
|
||||
result << "</tracks_data>"
|
||||
send_data(result, :filename => "tracks_data.xml", :type => 'text/xml')
|
||||
end
|
||||
|
|
@ -126,7 +126,7 @@ class DataController < ApplicationController
|
|||
def adjust_time(timestring)
|
||||
if (timestring=='') or ( timestring == nil)
|
||||
return nil
|
||||
else
|
||||
else
|
||||
return Time.parse(timestring + 'UTC')
|
||||
end
|
||||
end
|
||||
|
|
@ -186,7 +186,7 @@ class DataController < ApplicationController
|
|||
case item.ivars['attributes']['state']
|
||||
when 'active' then newitem.activate!
|
||||
when 'project_hidden' then newitem.hide!
|
||||
when 'completed'
|
||||
when 'completed'
|
||||
newitem.complete!
|
||||
newitem.completed_at = adjust_time(item.ivars['attributes']['completed_at'])
|
||||
when 'deferred' then newitem.defer!
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ class FeedlistController < ApplicationController
|
|||
@page_title = 'TRACKS::Feeds'
|
||||
|
||||
unless mobile?
|
||||
init_data_for_sidebar
|
||||
init_data_for_sidebar
|
||||
else
|
||||
@projects = current_user.projects
|
||||
@contexts = current_user.contexts
|
||||
|
|
@ -21,7 +21,7 @@ class FeedlistController < ApplicationController
|
|||
|
||||
respond_to do |format|
|
||||
format.html { render :layout => 'standard' }
|
||||
format.m { render :action => 'mobile_index' }
|
||||
format.m
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ class IntegrationsController < ApplicationController
|
|||
end
|
||||
|
||||
def search_plugin
|
||||
@icon_data = [File.open(RAILS_ROOT + '/public/images/done.png').read].
|
||||
# TODO: ASSET PATH!!
|
||||
@icon_data = [File.open(Rails.root + '/app/assets/images/done.png').read].
|
||||
pack('m').gsub(/\n/, '')
|
||||
|
||||
render :layout => false
|
||||
|
|
@ -52,7 +53,7 @@ class IntegrationsController < ApplicationController
|
|||
message = Mail.new(params[:message])
|
||||
|
||||
# find user
|
||||
user = User.find(:first, :include => [:preference], :conditions => ["preferences.sms_email = ?", message.from])
|
||||
user = User.where("preferences.sms_email = ?", message.from).includes(:preference).first
|
||||
if user.nil?
|
||||
render :text => "No user found", :status => 404
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
class LoginController < ApplicationController
|
||||
|
||||
layout 'login'
|
||||
filter_parameter_logging :user_password
|
||||
skip_before_filter :set_session_expiration
|
||||
skip_before_filter :login_required
|
||||
before_filter :login_optional
|
||||
|
|
@ -9,62 +8,38 @@ class LoginController < ApplicationController
|
|||
|
||||
protect_from_forgery :except => [:check_expiry, :login]
|
||||
|
||||
if ( SITE_CONFIG['authentication_schemes'].include? 'cas')
|
||||
# This will allow the user to view the index page without authentication
|
||||
# but will process CAS authentication data if the user already
|
||||
# has an SSO session open.
|
||||
if defined? CASClient
|
||||
# Only require sub-library if gem is installed and loaded
|
||||
require 'casclient/frameworks/rails/filter'
|
||||
before_filter CASClient::Frameworks::Rails::GatewayFilter, :only => :login_cas
|
||||
|
||||
# This requires the user to be authenticated for viewing all other pages.
|
||||
before_filter CASClient::Frameworks::Rails::Filter, :only => [:login_cas ]
|
||||
end
|
||||
end
|
||||
|
||||
def login
|
||||
if cas_enabled?
|
||||
@username = session[:cas_user]
|
||||
@login_url = CASClient::Frameworks::Rails::Filter.login_url(self)
|
||||
@page_title = "TRACKS::Login"
|
||||
cookies[:preferred_auth] = prefered_auth? unless cookies[:preferred_auth]
|
||||
case request.method
|
||||
when 'POST'
|
||||
if @user = User.authenticate(params['user_login'], params['user_password'])
|
||||
session['user_id'] = @user.id
|
||||
# If checkbox on login page checked, we don't expire the session after 1 hour
|
||||
# of inactivity and we remember this user for future browser sessions
|
||||
session['noexpiry'] = params['user_noexpiry']
|
||||
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
||||
notify :notice, "Login successful: session #{msg}"
|
||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
unless should_expire_sessions?
|
||||
@user.remember_me
|
||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
end
|
||||
redirect_back_or_home
|
||||
return
|
||||
else
|
||||
@login = params['user_login']
|
||||
notify :warning, t('login.unsuccessful')
|
||||
end
|
||||
when 'GET'
|
||||
if User.no_users_yet?
|
||||
redirect_to signup_path
|
||||
return
|
||||
end
|
||||
end
|
||||
if openid_enabled? && using_open_id?
|
||||
login_openid
|
||||
elsif cas_enabled? && session[:cas_user]
|
||||
login_cas
|
||||
else
|
||||
@page_title = "TRACKS::Login"
|
||||
cookies[:preferred_auth] = prefered_auth? unless cookies[:preferred_auth]
|
||||
case request.method
|
||||
when :post
|
||||
if @user = User.authenticate(params['user_login'], params['user_password'])
|
||||
session['user_id'] = @user.id
|
||||
# If checkbox on login page checked, we don't expire the session after 1 hour
|
||||
# of inactivity and we remember this user for future browser sessions
|
||||
session['noexpiry'] = params['user_noexpiry']
|
||||
msg = (should_expire_sessions?) ? "will expire after 1 hour of inactivity." : "will not expire."
|
||||
notify :notice, "Login successful: session #{msg}"
|
||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
unless should_expire_sessions?
|
||||
@user.remember_me
|
||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
end
|
||||
redirect_back_or_home
|
||||
return
|
||||
else
|
||||
@login = params['user_login']
|
||||
notify :warning, t('login.unsuccessful')
|
||||
end
|
||||
when :get
|
||||
if User.no_users_yet?
|
||||
redirect_to signup_path
|
||||
return
|
||||
end
|
||||
end
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.m { render :action => 'login_mobile.html.erb', :layout => 'mobile' }
|
||||
end
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.m { render :action => 'login', :layout => 'mobile' }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -111,32 +86,6 @@ class LoginController < ApplicationController
|
|||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
def login_cas
|
||||
# If checkbox on login page checked, we don't expire the session after 1 hour
|
||||
# of inactivity and we remember this user for future browser sessions
|
||||
|
||||
session['noexpiry'] ||= params['user_noexpiry']
|
||||
if session[:cas_user]
|
||||
if @user = User.find_by_login(session[:cas_user])
|
||||
session['user_id'] = @user.id
|
||||
msg = (should_expire_sessions?) ? t('login.session_will_expire', :hours => 1) : t('login.session_will_not_expire')
|
||||
notify :notice, (t('login.successful_with_session_info') + msg)
|
||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
unless should_expire_sessions?
|
||||
@user.remember_me
|
||||
cookies[:auth_token] = { :value => @user.remember_token, :expires => @user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
end
|
||||
else
|
||||
notify :warning, t('login.cas_username_not_found', :username => session[:cas_user])
|
||||
redirect_to signup_url ; return
|
||||
end
|
||||
else
|
||||
notify :warning, result.message
|
||||
end
|
||||
redirect_back_or_home
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
|
@ -144,32 +93,4 @@ class LoginController < ApplicationController
|
|||
session['noexpiry'] != "on"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def login_openid
|
||||
# If checkbox on login page checked, we don't expire the session after 1 hour
|
||||
# of inactivity and we remember this user for future browser sessions
|
||||
session['noexpiry'] ||= params['user_noexpiry']
|
||||
authenticate_with_open_id do |result, identity_url|
|
||||
if result.successful?
|
||||
if @user = User.find_by_open_id_url(identity_url)
|
||||
session['user_id'] = @user.id
|
||||
msg = (should_expire_sessions?) ? t('login.session_will_expire', :hours => 1) : t('login.session_will_not_expire')
|
||||
notify :notice, (t('login.successful_with_session_info') + msg)
|
||||
cookies[:tracks_login] = { :value => @user.login, :expires => Time.now + 1.year, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
unless should_expire_sessions?
|
||||
@user.remember_me
|
||||
cookies[:auth_token] = { :value => @user.remember_token , :expires => @user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
end
|
||||
redirect_back_or_home
|
||||
else
|
||||
notify :warning, t('login.openid_identity_url_not_found', :identity_url => identity_url)
|
||||
end
|
||||
else
|
||||
notify :warning, result.message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class NotesController < ApplicationController
|
|||
@page_title = "TRACKS::Note " + @note.id.to_s
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.m { render :action => 'note_mobile' }
|
||||
format.m
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ class NotesController < ApplicationController
|
|||
@saved = @note.save
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.js
|
||||
format.xml do
|
||||
if @saved
|
||||
head :created, :location => note_url(@note), :text => "new note with id #{@note.id}"
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ class ProjectsController < ApplicationController
|
|||
|
||||
def index
|
||||
@source_view = params['_source_view'] || 'project_list'
|
||||
@new_project = current_user.projects.build
|
||||
if params[:projects_and_actions]
|
||||
projects_and_actions
|
||||
else
|
||||
@contexts = current_user.contexts.all
|
||||
@contexts = current_user.contexts
|
||||
init_not_done_counts(['project'])
|
||||
init_project_hidden_todo_counts(['project'])
|
||||
if params[:only_active_with_no_next_actions]
|
||||
|
|
@ -21,19 +20,47 @@ class ProjectsController < ApplicationController
|
|||
else
|
||||
@projects = current_user.projects.all
|
||||
end
|
||||
@new_project = current_user.projects.build
|
||||
@active_projects = current_user.projects.active
|
||||
@hidden_projects = current_user.projects.hidden
|
||||
respond_to do |format|
|
||||
format.html &render_projects_html
|
||||
format.m &render_projects_mobile
|
||||
format.xml { render :xml => @projects.to_xml( :except => :user_id ) }
|
||||
format.rss &render_rss_feed
|
||||
format.atom &render_atom_feed
|
||||
format.text &render_text_feed
|
||||
format.autocomplete { render :text => for_autocomplete(current_user.projects.uncompleted, params[:term]) }
|
||||
format.html do
|
||||
@page_title = t('projects.list_projects')
|
||||
@count = current_user.projects.count
|
||||
@completed_projects = current_user.projects.completed.limit(10)
|
||||
@completed_count = current_user.projects.completed.count
|
||||
@no_projects = current_user.projects.empty?
|
||||
current_user.projects.cache_note_counts
|
||||
@new_project = current_user.projects.build
|
||||
end
|
||||
format.m do
|
||||
@completed_projects = current_user.projects.completed
|
||||
@down_count = @active_projects.size + @hidden_projects.size + @completed_projects.size
|
||||
cookies[:mobile_url]= {:value => request.fullpath, :secure => SITE_CONFIG['secure_cookies']}
|
||||
end
|
||||
format.xml { render :xml => @projects.all.to_xml( :except => :user_id ) }
|
||||
format.rss do
|
||||
@feed_title = I18n.t('models.project.feed_title')
|
||||
@feed_description = I18n.t('models.project.feed_description', :username => current_user.display_name)
|
||||
end
|
||||
format.atom do
|
||||
@feed_title = I18n.t('models.project.feed_title')
|
||||
@feed_description = I18n.t('models.project.feed_description', :username => current_user.display_name)
|
||||
end
|
||||
format.text do
|
||||
# somehow passing Mime::TEXT using content_type to render does not work
|
||||
headers['Content-Type']=Mime::TEXT.to_s
|
||||
end
|
||||
format.autocomplete do
|
||||
projects = current_user.projects.active + current_user.projects.hidden
|
||||
render :text => for_autocomplete(projects, params[:term])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def review
|
||||
@source_view = params['_source_view'] || 'review'
|
||||
@page_title = t('projects.list_reviews')
|
||||
@projects = current_user.projects.all
|
||||
@contexts = current_user.contexts.all
|
||||
|
|
@ -73,13 +100,25 @@ class ProjectsController < ApplicationController
|
|||
def set_reviewed
|
||||
@project.last_reviewed = Time.zone.now
|
||||
@project.save
|
||||
redirect_to :action => 'show'
|
||||
|
||||
case @source_view
|
||||
when "project"
|
||||
redirect_to :action => 'show'
|
||||
when "project_list"
|
||||
redirect_to :action => 'index'
|
||||
when "review"
|
||||
redirect_to :action => 'review'
|
||||
else
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
end
|
||||
|
||||
def projects_and_actions
|
||||
@projects = current_user.projects.active
|
||||
respond_to do |format|
|
||||
format.text {
|
||||
# somehow passing Mime::TEXT using content_type to render does not work
|
||||
headers['Content-Type']=Mime::TEXT.to_s
|
||||
render :action => 'index_text_projects_and_actions', :layout => false, :content_type => Mime::TEXT
|
||||
}
|
||||
end
|
||||
|
|
@ -90,11 +129,15 @@ class ProjectsController < ApplicationController
|
|||
init_data_for_sidebar unless mobile?
|
||||
@page_title = t('projects.page_title', :project => @project.name)
|
||||
|
||||
@not_done = @project.todos.active_or_hidden(:include => Todo::DEFAULT_INCLUDES)
|
||||
@deferred = @project.todos.deferred(:include => Todo::DEFAULT_INCLUDES)
|
||||
@pending = @project.todos.pending(:include => Todo::DEFAULT_INCLUDES)
|
||||
@done = @project.todos.find_in_state(:all, :completed,
|
||||
:order => "todos.completed_at DESC", :limit => current_user.prefs.show_number_completed, :include => Todo::DEFAULT_INCLUDES)
|
||||
@not_done = @project.todos.active_or_hidden.includes(Todo::DEFAULT_INCLUDES)
|
||||
@deferred = @project.todos.deferred.includes(Todo::DEFAULT_INCLUDES)
|
||||
@pending = @project.todos.pending.includes(Todo::DEFAULT_INCLUDES)
|
||||
|
||||
@done = {}
|
||||
@done = @project.todos.completed.
|
||||
reorder("todos.completed_at DESC").
|
||||
limit(current_user.prefs.show_number_completed).
|
||||
includes(Todo::DEFAULT_INCLUDES) unless current_user.prefs.show_number_completed == 0
|
||||
|
||||
@count = @not_done.size
|
||||
@down_count = @count + @deferred.size + @pending.size
|
||||
|
|
@ -106,39 +149,34 @@ class ProjectsController < ApplicationController
|
|||
@contexts = current_user.contexts
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.m &render_project_mobile
|
||||
format.xml {
|
||||
format.m do
|
||||
if @project.default_context.nil?
|
||||
@project_default_context = t('projects.no_default_context')
|
||||
else
|
||||
@project_default_context = t('projects.default_context', :context => @project.default_context.name)
|
||||
end
|
||||
cookies[:mobile_url]= {:value => request.fullpath, :secure => SITE_CONFIG['secure_cookies']}
|
||||
@mobile_from_project = @project.id
|
||||
end
|
||||
format.xml do
|
||||
render :xml => @project.to_xml(:except => :user_id) { |xml|
|
||||
xml.not_done { @not_done.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
|
||||
xml.deferred { @deferred.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
|
||||
xml.pending { @pending.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
|
||||
xml.done { @done.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Example XML usage: curl -H 'Accept: application/xml' -H 'Content-Type:
|
||||
# application/xml'
|
||||
# -u username:password
|
||||
# -d '<request><project><name>new project_name</name></project></request>'
|
||||
# http://our.tracks.host/projects
|
||||
#
|
||||
def create
|
||||
if params[:format] == 'application/xml' && params['exception']
|
||||
render_failure "Expected post format is valid xml like so: <request><project><name>project name</name></project></request>."
|
||||
render_failure "Expected post format is valid xml like so: <project><name>project name</name></project>.", 400
|
||||
return
|
||||
end
|
||||
|
||||
@project = current_user.projects.build
|
||||
params_are_invalid = true
|
||||
if (params['project'] || (params['request'] && params['request']['project']))
|
||||
@project.attributes = params['project'] || params['request']['project']
|
||||
params_are_invalid = false
|
||||
end
|
||||
@project = current_user.projects.build(params['project'])
|
||||
@go_to_project = params['go_to_project']
|
||||
@saved = @project.save
|
||||
|
||||
@project_not_done_counts = { @project.id => 0 }
|
||||
@active_projects_count = current_user.projects.active.count
|
||||
@contexts = current_user.contexts
|
||||
|
|
@ -146,10 +184,8 @@ class ProjectsController < ApplicationController
|
|||
respond_to do |format|
|
||||
format.js { @down_count = current_user.projects.size }
|
||||
format.xml do
|
||||
if @project.new_record? && params_are_invalid
|
||||
render_failure "Expected post format is valid xml like so: <request><project><name>project name</name></project></request>."
|
||||
elsif @project.new_record?
|
||||
render_failure @project.errors.full_messages.join(', ')
|
||||
if @project.new_record?
|
||||
render_failure @project.errors.to_xml.html_safe, 409
|
||||
else
|
||||
head :created, :location => project_url(@project), :text => @project.id
|
||||
end
|
||||
|
|
@ -179,38 +215,30 @@ class ProjectsController < ApplicationController
|
|||
if @saved
|
||||
@project.transition_to(@new_state) if @state_changed
|
||||
if boolean_param('wants_render')
|
||||
if (@project.hidden?)
|
||||
@project_project_hidden_todo_counts = Hash.new
|
||||
@project_project_hidden_todo_counts[@project.id] = @project.reload().todos.active_or_hidden.count
|
||||
else
|
||||
@project_not_done_counts = Hash.new
|
||||
@project_not_done_counts[@project.id] = @project.reload().todos.active_or_hidden.count
|
||||
end
|
||||
@contexts = current_user.contexts
|
||||
update_state_counts
|
||||
init_data_for_sidebar
|
||||
|
||||
template = 'projects/update.js.erb'
|
||||
|
||||
# TODO: are these params ever set? or is this dead code?
|
||||
template = 'projects/update'
|
||||
|
||||
# TODO: are these params ever set? or is this dead code?
|
||||
elsif boolean_param('update_status')
|
||||
template = 'projects/update_status.js.rjs'
|
||||
template = 'projects/update_status'
|
||||
elsif boolean_param('update_default_context')
|
||||
@initial_context_name = @project.default_context.name
|
||||
template = 'projects/update_default_context.js.rjs'
|
||||
template = 'projects/update_default_context'
|
||||
elsif boolean_param('update_default_tags')
|
||||
template = 'projects/update_default_tags.js.rjs'
|
||||
template = 'projects/update_default_tags'
|
||||
elsif boolean_param('update_project_name')
|
||||
@projects = current_user.projects
|
||||
template = 'projects/update_project_name.js.rjs'
|
||||
template = 'projects/update_project_name'
|
||||
else
|
||||
render :text => success_text || 'Success'
|
||||
return
|
||||
end
|
||||
else
|
||||
init_data_for_sidebar
|
||||
template = 'projects/update.js.erb'
|
||||
template = 'projects/update'
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
|
|
@ -304,73 +332,7 @@ class ProjectsController < ApplicationController
|
|||
@show_hidden_projects = @hidden_projects_count > 0
|
||||
@show_completed_projects = @completed_projects_count > 0
|
||||
end
|
||||
|
||||
def render_projects_html
|
||||
lambda do
|
||||
@page_title = t('projects.list_projects')
|
||||
@count = current_user.projects.count
|
||||
@active_projects = current_user.projects.active
|
||||
@hidden_projects = current_user.projects.hidden
|
||||
@completed_projects = current_user.projects.completed.find(:all, :limit => 10)
|
||||
@completed_count = current_user.projects.completed.count
|
||||
@no_projects = current_user.projects.empty?
|
||||
current_user.projects.cache_note_counts
|
||||
@new_project = current_user.projects.build
|
||||
render
|
||||
end
|
||||
end
|
||||
|
||||
def render_projects_mobile
|
||||
lambda do
|
||||
@active_projects = current_user.projects.active
|
||||
@hidden_projects = current_user.projects.hidden
|
||||
@completed_projects = current_user.projects.completed
|
||||
@down_count = @active_projects.size + @hidden_projects.size + @completed_projects.size
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
|
||||
render :action => 'index_mobile'
|
||||
end
|
||||
end
|
||||
|
||||
def render_project_mobile
|
||||
lambda do
|
||||
if @project.default_context.nil?
|
||||
@project_default_context = t('projects.no_default_context')
|
||||
else
|
||||
@project_default_context = t('projects.default_context', :context => @project.default_context.name)
|
||||
end
|
||||
cookies[:mobile_url]= {:value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
|
||||
@mobile_from_project = @project.id
|
||||
render :action => 'project_mobile'
|
||||
end
|
||||
end
|
||||
|
||||
def render_rss_feed
|
||||
lambda do
|
||||
render_rss_feed_for @projects, :feed => feed_options,
|
||||
:title => :name,
|
||||
:item => { :description => lambda { |p| @template.summary(p) } }
|
||||
end
|
||||
end
|
||||
|
||||
def render_atom_feed
|
||||
lambda do
|
||||
render_atom_feed_for @projects, :feed => feed_options,
|
||||
:item => { :description => lambda { |p| @template.summary(p) },
|
||||
:title => :name,
|
||||
:author => lambda { |p| nil } }
|
||||
end
|
||||
end
|
||||
|
||||
def feed_options
|
||||
Project.feed_options(current_user)
|
||||
end
|
||||
|
||||
def render_text_feed
|
||||
lambda do
|
||||
render :action => 'index', :layout => false, :content_type => Mime::TEXT
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def set_project_from_params
|
||||
@project = current_user.projects.find_by_params(params)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ class RecurringTodosController < ApplicationController
|
|||
@page_title = t('todos.recurring_actions_title')
|
||||
@source_view = params['_source_view'] || 'recurring_todo'
|
||||
find_and_inactivate
|
||||
@recurring_todos = current_user.recurring_todos.active.find(:all, :include => [:tags, :taggings])
|
||||
@completed_recurring_todos = current_user.recurring_todos.completed.find(:all, :limit => 10, :include => [:tags, :taggings])
|
||||
@recurring_todos = current_user.recurring_todos.active.includes(:tags, :taggings)
|
||||
@completed_recurring_todos = current_user.recurring_todos.completed.limit(10).includes(:tags, :taggings)
|
||||
|
||||
@no_recurring_todos = @recurring_todos.size == 0
|
||||
@no_completed_recurring_todos = @completed_recurring_todos.size == 0
|
||||
@count = @recurring_todos.size
|
||||
@no_recurring_todos = @recurring_todos.count == 0
|
||||
@no_completed_recurring_todos = @completed_recurring_todos.count == 0
|
||||
@count = @recurring_todos.count
|
||||
|
||||
@new_recurring_todo = RecurringTodo.new
|
||||
end
|
||||
|
|
@ -161,10 +161,10 @@ class RecurringTodosController < ApplicationController
|
|||
|
||||
format.html do
|
||||
if @saved
|
||||
notify :notice, t('todos.recurring_deleted_success'), 2.0
|
||||
notify :notice, t('todos.recurring_deleted_success')
|
||||
redirect_to :action => 'index'
|
||||
else
|
||||
notify :error, t('todos.error_deleting_recurring', :description => @recurring_todo.description), 2.0
|
||||
notify :error, t('todos.error_deleting_recurring', :description => @recurring_todo.description)
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
end
|
||||
|
|
@ -271,8 +271,8 @@ class RecurringTodosController < ApplicationController
|
|||
end
|
||||
|
||||
@xth_day = [[t('common.first'),1],[t('common.second'),2],[t('common.third'),3],[t('common.fourth'),4],[t('common.last'),5]]
|
||||
@projects = current_user.projects.find(:all, :include => [:default_context])
|
||||
@contexts = current_user.contexts.find(:all)
|
||||
@projects = current_user.projects.includes(:default_context)
|
||||
@contexts = current_user.contexts
|
||||
end
|
||||
|
||||
def get_recurring_todo_from_param
|
||||
|
|
@ -282,10 +282,11 @@ class RecurringTodosController < ApplicationController
|
|||
def find_and_inactivate
|
||||
# find active recurring todos without active todos and inactivate them
|
||||
|
||||
current_user.recurring_todos.active.all(
|
||||
:select => "recurring_todos.id, recurring_todos.state",
|
||||
:joins => "LEFT JOIN todos fai_todos ON (recurring_todos.id = fai_todos.recurring_todo_id) AND (NOT fai_todos.state='completed')",
|
||||
:conditions => "fai_todos.id IS NULL").each { |rt| current_user.recurring_todos.find(rt.id).toggle_completion! }
|
||||
current_user.recurring_todos.active.
|
||||
select("recurring_todos.id, recurring_todos.state").
|
||||
joins("LEFT JOIN todos fai_todos ON (recurring_todos.id = fai_todos.recurring_todo_id) AND (NOT fai_todos.state='completed')").
|
||||
where("fai_todos.id IS NULL").
|
||||
each { |rt| current_user.recurring_todos.find(rt.id).toggle_completion! }
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,25 +1,30 @@
|
|||
class SearchController < ApplicationController
|
||||
|
||||
helper :todos, :application, :notes, :projects
|
||||
|
||||
helper :todos, :application, :notes, :projects, :contexts
|
||||
|
||||
def results
|
||||
@source_view = params['_source_view'] || 'search'
|
||||
@page_title = "TRACKS::Search Results for #{params[:search]}"
|
||||
terms = '%' + params[:search] + '%'
|
||||
terms = "%#{params[:search]}%"
|
||||
|
||||
@found_not_complete_todos = current_user.todos.find(:all,
|
||||
:conditions => ["(todos.description LIKE ? OR todos.notes LIKE ?) AND todos.completed_at IS NULL", terms, terms],
|
||||
:include => Todo::DEFAULT_INCLUDES,
|
||||
:order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC")
|
||||
@found_complete_todos = current_user.todos.find(:all,
|
||||
:conditions => ["(todos.description LIKE ? OR todos.notes LIKE ?) AND NOT (todos.completed_at IS NULL)", terms, terms],
|
||||
:include => Todo::DEFAULT_INCLUDES,
|
||||
:order => "todos.completed_at DESC")
|
||||
@found_not_complete_todos = current_user.todos.
|
||||
where("(todos.description LIKE ? OR todos.notes LIKE ?) AND todos.completed_at IS NULL", terms, terms).
|
||||
includes(Todo::DEFAULT_INCLUDES).
|
||||
reorder("todos.due IS NULL, todos.due ASC, todos.created_at ASC").
|
||||
all
|
||||
|
||||
@found_complete_todos = current_user.todos.
|
||||
where("(todos.description LIKE ? OR todos.notes LIKE ?) AND NOT (todos.completed_at IS NULL)", terms, terms).
|
||||
includes(Todo::DEFAULT_INCLUDES).
|
||||
reorder("todos.completed_at DESC").
|
||||
all
|
||||
|
||||
@found_todos = @found_not_complete_todos + @found_complete_todos
|
||||
|
||||
@found_projects = current_user.projects.find(:all, :conditions => ["name LIKE ? OR description LIKE ?", terms, terms])
|
||||
@found_notes = current_user.notes.find(:all, :conditions => ["body LIKE ?", terms])
|
||||
@found_contexts = current_user.contexts.find(:all, :conditions => ["name LIKE ?", terms])
|
||||
@found_projects = current_user.projects.where("name LIKE ? OR description LIKE ?", terms, terms).all
|
||||
@found_notes = current_user.notes.where("body LIKE ?", terms).all
|
||||
@found_contexts = current_user.contexts.where("name LIKE ?", terms).all
|
||||
|
||||
# TODO: limit search to tags on todos
|
||||
@found_tags = Tagging.find_by_sql([
|
||||
"SELECT DISTINCT tags.name as name "+
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
class UsersController < ApplicationController
|
||||
|
||||
before_filter :admin_login_required, :only => [ :index, :show, :destroy ]
|
||||
skip_before_filter :login_required, :only => [ :new, :create ]
|
||||
skip_before_filter :check_for_deprecated_password_hash,
|
||||
|
|
@ -17,7 +18,7 @@ class UsersController < ApplicationController
|
|||
store_location
|
||||
end
|
||||
format.xml do
|
||||
@users = User.find(:all, :order => 'login')
|
||||
@users = User.order('login').all
|
||||
render :xml => @users.to_xml(:except => [ :password ])
|
||||
end
|
||||
end
|
||||
|
|
@ -25,7 +26,7 @@ class UsersController < ApplicationController
|
|||
|
||||
# GET /users/id GET /users/id.xml
|
||||
def show
|
||||
@user = User.find_by_id(params[:id])
|
||||
@user = User.find(params[:id])
|
||||
render :xml => @user.to_xml(:except => [ :password ])
|
||||
end
|
||||
|
||||
|
|
@ -64,7 +65,7 @@ class UsersController < ApplicationController
|
|||
# POST /users POST /users.xml
|
||||
def create
|
||||
if params['exception']
|
||||
render_failure "Expected post format is valid xml like so: <request><login>username</login><password>abc123</password></request>."
|
||||
render_failure "Expected post format is valid xml like so: <user><login>username</login><password>abc123</password></user>."
|
||||
return
|
||||
end
|
||||
respond_to do |format|
|
||||
|
|
@ -82,7 +83,7 @@ class UsersController < ApplicationController
|
|||
user.auth_type == 'ldap' &&
|
||||
!SimpleLdapAuthenticator.valid?(user.login, params['user']['password'])
|
||||
notify :warning, "Incorrect password"
|
||||
redirect_to :action => 'new'
|
||||
redirect_to signup_path
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -111,23 +112,21 @@ class UsersController < ApplicationController
|
|||
return
|
||||
end
|
||||
format.xml do
|
||||
unless User.find_by_id_and_is_admin(session['user_id'], true)
|
||||
unless current_user && current_user.is_admin
|
||||
render :text => "401 Unauthorized: Only admin users are allowed access to this function.", :status => 401
|
||||
return
|
||||
end
|
||||
unless check_create_user_params
|
||||
render_failure "Expected post format is valid xml like so: <request><login>username</login><password>abc123</password></request>."
|
||||
render_failure "Expected post format is valid xml like so: <user><login>username</login><password>abc123</password></user>.", 400
|
||||
return
|
||||
end
|
||||
user = User.new(params[:request])
|
||||
if Tracks::Config.auth_schemes.include?('cas') && session[:cas_user]
|
||||
user.auth_type = "cas" #if they area cas user
|
||||
end
|
||||
user.password_confirmation = params[:request][:password]
|
||||
if user.save
|
||||
user = User.new(params[:user])
|
||||
user.password_confirmation = params[:user][:password]
|
||||
saved = user.save
|
||||
unless user.new_record?
|
||||
render :text => t('users.user_created'), :status => 200
|
||||
else
|
||||
render_failure user.errors.to_xml
|
||||
render_failure user.errors.to_xml, 409
|
||||
end
|
||||
return
|
||||
end
|
||||
|
|
@ -136,16 +135,16 @@ class UsersController < ApplicationController
|
|||
|
||||
# DELETE /users/id DELETE /users/id.xml
|
||||
def destroy
|
||||
@deleted_user = User.find_by_id(params[:id])
|
||||
@deleted_user = User.find(params[:id])
|
||||
@saved = @deleted_user.destroy
|
||||
@total_users = User.find(:all).size
|
||||
@total_users = User.all.size
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
if @saved
|
||||
notify :notice, t('users.successfully_deleted_user', :username => @deleted_user.login), 2.0
|
||||
notify :notice, t('users.successfully_deleted_user', :username => @deleted_user.login)
|
||||
else
|
||||
notify :error, t('users.failed_to_delete_user', :username => @deleted_user.login), 2.0
|
||||
notify :error, t('users.failed_to_delete_user', :username => @deleted_user.login)
|
||||
end
|
||||
redirect_to users_url
|
||||
end
|
||||
|
|
@ -154,19 +153,18 @@ class UsersController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
def change_password
|
||||
@page_title = t('users.change_password_title')
|
||||
end
|
||||
|
||||
def update_password
|
||||
# is used for focing password change after sha->bcrypt upgrade
|
||||
@user.change_password(params[:user][:password], params[:user][:password_confirmation])
|
||||
current_user.change_password(params[:user][:password], params[:user][:password_confirmation])
|
||||
notify :notice, t('users.password_updated')
|
||||
redirect_to preferences_path
|
||||
rescue Exception => error
|
||||
notify :error, error.message
|
||||
redirect_to :action => 'change_password'
|
||||
redirect_to change_password_user_path(current_user)
|
||||
end
|
||||
|
||||
def change_auth_type
|
||||
|
|
@ -174,40 +172,19 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
def update_auth_type
|
||||
if (params[:open_id_complete] || (params[:user][:auth_type] == 'open_id')) && openid_enabled?
|
||||
authenticate_with_open_id do |result, identity_url|
|
||||
if result.successful?
|
||||
# Success means that the transaction completed without error. If info
|
||||
# is nil, it means that the user cancelled the verification.
|
||||
@user.auth_type = 'open_id'
|
||||
@user.open_id_url = identity_url
|
||||
if @user.save
|
||||
notify :notice, t('users.openid_url_verified', :url => identity_url)
|
||||
else
|
||||
debugger
|
||||
notify :warning, t('users.openid_ok_pref_failed', :url => identity_url)
|
||||
end
|
||||
redirect_to preferences_path
|
||||
else
|
||||
notify :warning, result.message
|
||||
redirect_to :action => 'change_auth_type'
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
@user.auth_type = params[:user][:auth_type]
|
||||
if @user.save
|
||||
current_user.auth_type = params[:user][:auth_type]
|
||||
if current_user.save
|
||||
notify :notice, t('users.auth_type_updated')
|
||||
redirect_to preferences_path
|
||||
else
|
||||
notify :warning, t('users.auth_type_update_error', :error_messages => @user.errors.full_messages.join(', '))
|
||||
redirect_to :action => 'change_auth_type'
|
||||
notify :warning, t('users.auth_type_update_error', :error_messages => current_user.errors.full_messages.join(', '))
|
||||
redirect_to change_auth_type_user_path(current_user)
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_token
|
||||
@user.generate_token
|
||||
@user.save!
|
||||
current_user.generate_token
|
||||
current_user.save!
|
||||
notify :notice, t('users.new_token_generated')
|
||||
redirect_to preferences_path
|
||||
end
|
||||
|
|
@ -225,11 +202,11 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
def check_create_user_params
|
||||
return false unless params.has_key?(:request)
|
||||
return false unless params[:request].has_key?(:login)
|
||||
return false if params[:request][:login].empty?
|
||||
return false unless params[:request].has_key?(:password)
|
||||
return false if params[:request][:password].empty?
|
||||
return false unless params.has_key?(:user)
|
||||
return false unless params[:user].has_key?(:login)
|
||||
return false if params[:user][:login].empty?
|
||||
return false unless params[:user].has_key?(:password)
|
||||
return false if params[:user][:password].empty?
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,21 +6,23 @@ module ApplicationHelper
|
|||
# current page. If that matches the url, the link is marked id = "current"
|
||||
#
|
||||
def navigation_link(name, options = {}, html_options = nil, *parameters_for_method_reference)
|
||||
if html_options
|
||||
html_options = html_options.stringify_keys
|
||||
convert_options_to_javascript!(html_options)
|
||||
tag_options = tag_options(html_options)
|
||||
else
|
||||
tag_options = nil
|
||||
end
|
||||
url = options.is_a?(String) ? options : self.url_for(options, *parameters_for_method_reference)
|
||||
id_tag = (request.request_uri == url) ? " id=\"current\"" : ""
|
||||
|
||||
"<a href=\"#{url}\"#{tag_options}#{id_tag}>#{name || url}</a>"
|
||||
link_to name, options, html_options
|
||||
# TODO: check if this needs to be converted
|
||||
# if html_options
|
||||
# html_options = html_options.stringify_keys
|
||||
# convert_options_to_javascript!(html_options)
|
||||
# tag_options = tag_options(html_options)
|
||||
# else
|
||||
# tag_options = nil
|
||||
# end
|
||||
# url = options.is_a?(String) ? options : self.url_for(options, *parameters_for_method_reference)
|
||||
# id_tag = (request.request_uri == url) ? " id=\"current\"" : ""
|
||||
#
|
||||
# "<a href=\"#{url}\"#{tag_options}#{id_tag}>#{name || url}</a>"
|
||||
end
|
||||
|
||||
def days_from_today(date)
|
||||
date.in_time_zone.to_date - current_user.time.to_date
|
||||
Integer (date.in_time_zone.to_date - current_user.time.to_date)
|
||||
end
|
||||
|
||||
# Check due date in comparison to today's date Flag up date appropriately with
|
||||
|
|
@ -53,7 +55,7 @@ module ApplicationHelper
|
|||
else
|
||||
# overdue or due very soon! sound the alarm!
|
||||
if days == -1
|
||||
t('todos.next_actions_due_date.overdue_by', :days => days * -1)
|
||||
t('todos.next_actions_due_date.overdue_by', :days => days * -1)
|
||||
elsif days < -1
|
||||
t('todos.next_actions_due_date.overdue_by_plural', :days => days * -1)
|
||||
else
|
||||
|
|
@ -98,18 +100,18 @@ module ApplicationHelper
|
|||
# is count and a string descriptor, correctly pluralised if there are no
|
||||
# actions or multiple actions
|
||||
#
|
||||
def count_undone_todos_phrase(todos_parent, string="actions")
|
||||
@controller.count_undone_todos_phrase(todos_parent, string)
|
||||
def count_undone_todos_phrase(todos_parent)
|
||||
controller.count_undone_todos_phrase(todos_parent).html_safe
|
||||
end
|
||||
|
||||
def count_undone_todos_phrase_text(todos_parent, string="actions")
|
||||
count_undone_todos_phrase(todos_parent, string).gsub(" "," ")
|
||||
def count_undone_todos_phrase_text(todos_parent)
|
||||
count_undone_todos_phrase(todos_parent).gsub(" "," ").html_safe
|
||||
end
|
||||
|
||||
def count_undone_todos_and_notes_phrase(project, string="actions")
|
||||
s = count_undone_todos_phrase(project, string)
|
||||
s += ", #{pluralize(project.note_count, 'note')}" unless project.note_count == 0
|
||||
s
|
||||
def count_undone_todos_and_notes_phrase(project)
|
||||
s = count_undone_todos_phrase(project)
|
||||
s += ", #{t('common.note', :count => project.note_count)}" unless project.note_count == 0
|
||||
s.html_safe
|
||||
end
|
||||
|
||||
def link_to_context(context, descriptor = sanitize(context.name))
|
||||
|
|
@ -120,18 +122,6 @@ module ApplicationHelper
|
|||
link_to( descriptor, project, :title => "View project: #{project.name}" )
|
||||
end
|
||||
|
||||
def link_to_edit_project (project, descriptor = sanitize(project.name))
|
||||
link_to(descriptor,
|
||||
url_for({:controller => 'projects', :action => 'edit', :id => project.id}),
|
||||
{:id => "link_edit_#{dom_id(project)}", :class => "project_edit_settings"})
|
||||
end
|
||||
|
||||
def link_to_edit_context (context, descriptor = sanitize(context.name))
|
||||
link_to(descriptor,
|
||||
url_for({:controller => 'contexts', :action => 'edit', :id => context.id}),
|
||||
{:id => "link_edit_#{dom_id(context)}", :class => "context_edit_settings"})
|
||||
end
|
||||
|
||||
def link_to_edit_note (note, descriptor = sanitize(note.id.to_s))
|
||||
link_to(descriptor,
|
||||
url_for({:controller => 'notes', :action => 'edit', :id => note.id}),
|
||||
|
|
@ -155,7 +145,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def render_flash
|
||||
render :partial => 'shared/flash', :object => flash
|
||||
render :partial => 'shared/flash', :object => flash
|
||||
end
|
||||
|
||||
def recurrence_time_span(rt)
|
||||
|
|
@ -201,50 +191,18 @@ module ApplicationHelper
|
|||
end
|
||||
end
|
||||
|
||||
AUTO_LINK_MESSAGE_RE = %r{message://<[^>]+>} unless const_defined?(:AUTO_LINK_MESSAGE_RE)
|
||||
|
||||
# Converts message:// links to href. This URL scheme is used on Mac OS X
|
||||
# to link to a mail message in Mail.app.
|
||||
def auto_link_message(text)
|
||||
text.gsub(AUTO_LINK_MESSAGE_RE) do
|
||||
href = $&
|
||||
left, right = $`, $'
|
||||
# detect already linked URLs and URLs in the middle of a tag
|
||||
if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
|
||||
# do not change string; URL is alreay linked
|
||||
href
|
||||
else
|
||||
content = content_tag(:a, h(href), :href => h(href))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def format_note(note)
|
||||
note = auto_link_message(note)
|
||||
note = markdown(note)
|
||||
note = auto_link(note, :link => :urls)
|
||||
|
||||
# add onenote and message protocols
|
||||
Sanitize::Config::RELAXED[:protocols]['a']['href'] << 'onenote'
|
||||
Sanitize::Config::RELAXED[:protocols]['a']['href'] << 'message'
|
||||
|
||||
note = Sanitize.clean(note, Sanitize::Config::RELAXED)
|
||||
return note
|
||||
end
|
||||
|
||||
def sidebar_html_for_titled_list (list, title)
|
||||
return content_tag(:h3, title+" (#{list.length})") +
|
||||
content_tag(:ul, sidebar_html_for_list(list))
|
||||
return content_tag(:h3, title+" (#{list.size})") + content_tag(:ul, sidebar_html_for_list(list))
|
||||
end
|
||||
|
||||
def sidebar_html_for_list(list)
|
||||
if list.empty?
|
||||
return content_tag(:li, t('sidebar.list_empty'))
|
||||
return content_tag(:li, t('sidebar.list_empty')).html_safe
|
||||
else
|
||||
return list.inject("") do |html, item|
|
||||
link = (item.class == "Project") ? link_to_project( item ) : link_to_context(item)
|
||||
html << content_tag(:li, link + " (" + count_undone_todos_phrase(item,"actions")+")")
|
||||
end
|
||||
html << content_tag(:li, link + " (" + count_undone_todos_phrase(item)+")")
|
||||
end.html_safe
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -261,6 +219,7 @@ module ApplicationHelper
|
|||
contexts.show_form contexts.show_form_title
|
||||
contexts.new_context_pre contexts.new_context_post
|
||||
common.cancel common.ok
|
||||
common.update common.create
|
||||
common.ajaxError todos.unresolved_dependency
|
||||
}.each do |s|
|
||||
js << "i18n['#{s}'] = '#{ t(s).gsub(/'/, "\\\\'") }';\n"
|
||||
|
|
@ -277,7 +236,7 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def determine_done_path
|
||||
case @controller.controller_name
|
||||
case controller.controller_name
|
||||
when "contexts"
|
||||
done_todos_context_path(@context)
|
||||
when "projects"
|
||||
|
|
@ -287,14 +246,14 @@ module ApplicationHelper
|
|||
done_tag_path(@tag_name)
|
||||
else
|
||||
done_todos_path
|
||||
end
|
||||
end
|
||||
else
|
||||
done_todos_path
|
||||
end
|
||||
end
|
||||
|
||||
def determine_all_done_path
|
||||
case @controller.controller_name
|
||||
case controller.controller_name
|
||||
when "contexts"
|
||||
all_done_todos_context_path(@context)
|
||||
when "projects"
|
||||
|
|
@ -309,5 +268,13 @@ module ApplicationHelper
|
|||
all_done_todos_path
|
||||
end
|
||||
end
|
||||
|
||||
def get_list_of_error_messages_for(model)
|
||||
error_messages = ""
|
||||
if model.errors.any?
|
||||
list_of_messages = model.errors.full_messages.inject("") { |all, msg| all += content_tag(:li, msg) }
|
||||
error_messages = content_tag(:ul, list_of_messages)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,30 +1,36 @@
|
|||
module ContextsHelper
|
||||
|
||||
def get_listing_sortable_options
|
||||
{
|
||||
:tag => 'div',
|
||||
:handle => 'handle',
|
||||
:complete => visual_effect(:highlight, 'list-contexts'),
|
||||
:url => order_contexts_path
|
||||
}
|
||||
end
|
||||
|
||||
def link_to_delete_context(context, descriptor = sanitize(context.name))
|
||||
link_to(descriptor,
|
||||
context_path(context, :format => 'js'),
|
||||
{
|
||||
:id => "delete_context_#{context.id}",
|
||||
:class => "delete_context_button icon",
|
||||
:x_confirm_message => t('contexts.delete_context_confirmation', :name => context.name),
|
||||
:title => t('contexts.delete_context_title')
|
||||
})
|
||||
end
|
||||
|
||||
def get_listing_sortable_options
|
||||
{
|
||||
:tag => 'div',
|
||||
:handle => 'handle',
|
||||
:complete => visual_effect(:highlight, 'list-contexts'),
|
||||
:url => order_contexts_path
|
||||
}
|
||||
end
|
||||
|
||||
def link_to_delete_context(context, descriptor = sanitize(context.name))
|
||||
link_to(
|
||||
descriptor,
|
||||
context_path(context, :format => 'js'),
|
||||
{
|
||||
:id => "delete_context_#{context.id}",
|
||||
:class => "delete_context_button",
|
||||
:x_confirm_message => t('contexts.delete_context_confirmation', :name => context.name),
|
||||
:title => t('contexts.delete_context_title')
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def summary(context, undone_todo_count)
|
||||
content_tag(:p, "#{undone_todo_count}. Context is #{context.hidden? ? 'Hidden' : 'Active'}.")
|
||||
end
|
||||
def link_to_edit_context (context, descriptor = sanitize(context.name))
|
||||
link_to(descriptor,
|
||||
url_for({:controller => 'contexts', :action => 'edit', :id => context.id}),
|
||||
{
|
||||
:id => "link_edit_#{dom_id(context)}",
|
||||
:class => "context_edit_settings icon"
|
||||
})
|
||||
end
|
||||
|
||||
def context_summary(context, undone_todo_count)
|
||||
content_tag(:p, "#{undone_todo_count}. Context is #{context.hidden? ? 'Hidden' : 'Active'}.".html_safe)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ module FeedlistHelper
|
|||
|
||||
def text_formatted_link(options = {})
|
||||
linkoptions = merge_hashes( {:format => 'txt'}, user_token_hash, options)
|
||||
link_to('<span class="feed">TXT</span>', linkoptions, :title => "Plain text feed" )
|
||||
link_to(content_tag(:span, 'TXT', {:class => 'feed', :title => "Plain text feed"}), linkoptions)
|
||||
end
|
||||
|
||||
def ical_formatted_link(options = {})
|
||||
linkoptions = merge_hashes( {:format => 'ics'}, user_token_hash, options)
|
||||
link_to('<span class="feed">iCal</span>', linkoptions, :title => "iCal feed" )
|
||||
link_to(content_tag(:span, 'iCal', {:class=>"feed", :title => "iCal feed"}), linkoptions)
|
||||
end
|
||||
|
||||
def feed_links(feeds, link_options, title)
|
||||
|
|
@ -23,7 +23,7 @@ module FeedlistHelper
|
|||
html << text_formatted_link(link_options)+space if feeds.include?(:txt)
|
||||
html << ical_formatted_link(link_options)+space if feeds.include?(:ical)
|
||||
html << title
|
||||
return html
|
||||
return html.html_safe
|
||||
end
|
||||
|
||||
def all_feed_links_for_project(project)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
module PreferencesHelper
|
||||
|
||||
def pref(model, pref_name, &block)
|
||||
s = "<label for #{model+pref_name}>#{Preference.human_attribute_name(pref_name)}:</label><br/>"
|
||||
s = content_tag(:label, Preference.human_attribute_name(pref_name), :for => model+pref_name)
|
||||
s << "<br/>".html_safe
|
||||
s << yield
|
||||
s << "<br/><br/>"
|
||||
s << "<br/><br/>".html_safe
|
||||
s
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -18,31 +18,30 @@ module ProjectsHelper
|
|||
end
|
||||
|
||||
def project_next_prev
|
||||
html = ''
|
||||
unless @previous_project.nil?
|
||||
html = ""
|
||||
if @previous_project
|
||||
project_name = truncate(@previous_project.name, :length => 40, :omission => "...")
|
||||
html << link_to_project(@previous_project, "« #{project_name}")
|
||||
html << link_to_project(@previous_project, "« #{project_name}".html_safe)
|
||||
end
|
||||
html << ' | ' if @previous_project && @next_project
|
||||
unless @next_project.nil?
|
||||
html << " | " if @previous_project && @next_project
|
||||
if @next_project
|
||||
project_name = truncate(@next_project.name, :length => 40, :omission => "...")
|
||||
html << link_to_project(@next_project, "#{project_name} »")
|
||||
html << link_to_project(@next_project, "#{project_name} »".html_safe)
|
||||
end
|
||||
html
|
||||
html.html_safe
|
||||
end
|
||||
|
||||
def project_next_prev_mobile
|
||||
html = ''
|
||||
prev_project,next_project= "", ""
|
||||
unless @previous_project.nil?
|
||||
project_name = truncate(@previous_project.name, :length => 40, :omission => "...")
|
||||
html << link_to_project_mobile(@previous_project, "5", "« 5-#{project_name}")
|
||||
prev_project = content_tag(:li, link_to_project_mobile(@previous_project, "5", project_name), :class=>"prev")
|
||||
end
|
||||
html << ' | ' if @previous_project && @next_project
|
||||
unless @next_project.nil?
|
||||
project_name = truncate(@next_project.name, :length => 40, :omission => "...")
|
||||
html << link_to_project_mobile(@next_project, "6", "6-#{project_name} »")
|
||||
next_project = content_tag(:li, link_to_project_mobile(@next_project, "6", project_name), :class=>"next")
|
||||
end
|
||||
html
|
||||
return content_tag(:ul, "#{prev_project}#{next_project}".html_safe, :class=>"next-prev-project").html_safe
|
||||
end
|
||||
|
||||
def link_to_delete_project(project, descriptor = sanitize(project.name))
|
||||
|
|
@ -51,20 +50,29 @@ module ProjectsHelper
|
|||
project_path(project, :format => 'js'),
|
||||
{
|
||||
:id => "delete_project_#{project.id}",
|
||||
:class => "delete_project_button",
|
||||
:class => "delete_project_button icon",
|
||||
:x_confirm_message => t('projects.delete_project_confirmation', :name => project.name),
|
||||
:title => t('projects.delete_project_title')
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def summary(project)
|
||||
def link_to_edit_project (project, descriptor = sanitize(project.name))
|
||||
link_to(descriptor,
|
||||
url_for({:controller => 'projects', :action => 'edit', :id => project.id}),
|
||||
{
|
||||
:id => "link_edit_#{dom_id(project)}",
|
||||
:class => "project_edit_settings icon"
|
||||
})
|
||||
end
|
||||
|
||||
def project_summary(project)
|
||||
project_description = ''
|
||||
project_description += sanitize(markdown( project.description )) unless project.description.blank?
|
||||
project_description += Tracks::Utils.render_text( project.description ) unless project.description.blank?
|
||||
project_description += content_tag(:p,
|
||||
"#{count_undone_todos_phrase(p)}. " + t('projects.project_state', :state => project.state)
|
||||
"#{count_undone_todos_phrase(p)}. #{t('projects.project_state', :state => project.state)}".html_safe
|
||||
)
|
||||
project_description
|
||||
raw project_description
|
||||
end
|
||||
|
||||
def needsreview_class(item)
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@ module RecurringTodosHelper
|
|||
|
||||
def recurring_todo_tag_list
|
||||
tags_except_starred = @recurring_todo.tags.reject{|t| t.name == Todo::STARRED_TAG_NAME}
|
||||
tag_list = tags_except_starred.collect{|t| "<span class=\"tag #{t.name.gsub(' ','-')}\">" +
|
||||
link_to(t.name, :controller => "todos", :action => "tag", :id =>
|
||||
t.name) + #TODO: tag view for recurring_todos (yet?)
|
||||
"</span>"}.join('')
|
||||
"<span class='tags'>#{tag_list}</span>"
|
||||
tag_list = tags_except_starred.
|
||||
collect{|t| content_tag(:span,link_to(t.name, tag_path(t.name)), :class => "tag #{t.name.gsub(' ','-')}")}.
|
||||
join('')
|
||||
return content_tag :span, tag_list.html_safe, :class => "tags"
|
||||
end
|
||||
|
||||
def recurring_todo_remote_delete_icon
|
||||
|
|
@ -27,7 +26,7 @@ module RecurringTodosHelper
|
|||
edit_recurring_todo_path(@recurring_todo),
|
||||
:class => "icon edit_icon", :id => "link_edit_recurring_todo_#{@recurring_todo.id}")
|
||||
else
|
||||
str = '<a class="icon">' + image_tag("blank.png") + "</a> "
|
||||
str = content_tag(:a, image_tag("blank.png"), :class => "icon")
|
||||
end
|
||||
str
|
||||
end
|
||||
|
|
|
|||
|
|
@ -63,8 +63,14 @@ module TodosHelper
|
|||
end
|
||||
|
||||
def collapsed_notes_image(todo)
|
||||
link = link_to(image_tag( 'blank.png', :width=>'16', :height=>'16', :border=>'0' ), "#", {:class => 'show_notes', :title => 'Show notes'})
|
||||
notes = content_tag(:div, {:class => "todo_notes", :id => dom_id(todo, 'notes'), :style => "display:none"}) { format_note(todo.notes) }
|
||||
link = link_to(
|
||||
image_tag( 'blank.png', :width=>'16', :height=>'16', :border=>'0' ),
|
||||
"#",
|
||||
{:class => 'show_notes', :title => 'Show notes'})
|
||||
notes = content_tag(:div, {
|
||||
:class => "todo_notes",
|
||||
:id => dom_id(todo, 'notes'),
|
||||
:style => "display:none"}) { raw todo.rendered_notes }
|
||||
return link+notes
|
||||
end
|
||||
|
||||
|
|
@ -80,7 +86,7 @@ module TodosHelper
|
|||
def image_tag_for_recurring_todo(todo)
|
||||
return link_to(
|
||||
image_tag("recurring16x16.png"),
|
||||
{:controller => "recurring_todos", :action => "index"},
|
||||
recurring_todos_path,
|
||||
:class => "recurring_icon", :title => recurrence_pattern_as_text(todo.recurring_todo))
|
||||
end
|
||||
|
||||
|
|
@ -90,9 +96,9 @@ module TodosHelper
|
|||
end
|
||||
|
||||
def remote_mobile_checkbox(todo=@todo)
|
||||
form_tag mobile_done_todo_path(@todo, :format => 'm'), :method => :put, :class => "mobile-done", :name => "mobile_complete_#{@todo.id}" do
|
||||
form_tag toggle_check_todo_path(@todo, :format => 'm'), :method => :put, :class => "mobile-done", :name => "mobile_complete_#{@todo.id}" do
|
||||
check_box_tag('_source_view', 'todo', @todo && @todo.completed?, "onClick" => "document.mobile_complete_#{@todo.id}.submit()")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def date_span(todo=@todo)
|
||||
|
|
@ -110,7 +116,7 @@ module TodosHelper
|
|||
|
||||
def successors_span(todo=@todo)
|
||||
unless todo.pending_successors.empty?
|
||||
pending_count = todo.pending_successors.length
|
||||
pending_count = todo.pending_successors.count
|
||||
title = "#{t('todos.has_x_pending', :count => pending_count)}: #{todo.pending_successors.map(&:description).join(', ')}"
|
||||
image_tag( 'successor_off.png', :width=>'10', :height=>'16', :border=>'0', :title => title )
|
||||
end
|
||||
|
|
@ -129,25 +135,19 @@ module TodosHelper
|
|||
end
|
||||
|
||||
def tag_span (tag, mobile=false)
|
||||
content_tag(:span, :class => "tag #{tag.name.gsub(' ','-')}") { link_to(tag.name, (mobile ? mobile_tag_path(tag.name) : tag_path(tag.name))) }
|
||||
content_tag(:span, :class => "tag #{tag.name.gsub(' ','-')}") { link_to(tag.name, tag_path(tag.name, :format => mobile ? :m : :html)) }
|
||||
end
|
||||
|
||||
def tag_list(todo=@todo, mobile=false)
|
||||
content_tag(:span, :class => 'tags') { todo.tags.all_except_starred.collect{|tag| tag_span(tag, mobile)}.join('') }
|
||||
content_tag(:span, :class => 'tags') { todo.tags.all_except_starred.collect{|tag| tag_span(tag, mobile)}.join('').html_safe }
|
||||
end
|
||||
|
||||
def tag_list_mobile(todo=@todo)
|
||||
unless todo.tags.all_except_starred.empty?
|
||||
return tag_list(todo, true)
|
||||
else
|
||||
return ""
|
||||
end
|
||||
todo.tags.all_except_starred.empty? ? "" : tag_list(todo, true)
|
||||
end
|
||||
|
||||
def deferred_due_date(todo=@todo)
|
||||
if todo.deferred? && todo.due
|
||||
t('todos.action_due_on', :date => format_date(todo.due))
|
||||
end
|
||||
t('todos.action_due_on', :date => format_date(todo.due)) if todo.deferred? && todo.due
|
||||
end
|
||||
|
||||
def project_and_context_links(todo, parent_container_type, opts = {})
|
||||
|
|
@ -166,7 +166,7 @@ module TodosHelper
|
|||
str << item_link_to_project( todo )
|
||||
end
|
||||
end
|
||||
return str
|
||||
return str.html_safe
|
||||
end
|
||||
|
||||
# Uses the 'staleness_starts' value from settings.yml (in days) to colour the
|
||||
|
|
@ -272,12 +272,12 @@ module TodosHelper
|
|||
end
|
||||
|
||||
def default_contexts_for_autocomplete
|
||||
projects = current_user.uncompleted.projects.find(:all, :include => [:context], :conditions => ['default_context_id is not null'])
|
||||
projects = current_user.projects.uncompleted.includes(:default_context).where('NOT(default_context_id IS NULL)')
|
||||
Hash[*projects.map{ |p| [escape_javascript(p.name), escape_javascript(p.default_context.name)] }.flatten].to_json
|
||||
end
|
||||
|
||||
def default_tags_for_autocomplete
|
||||
projects = current_user.projects.uncompleted.find(:all, :conditions => ["default_tags != ''"])
|
||||
projects = current_user.projects.uncompleted.where("NOT(default_tags = '')")
|
||||
Hash[*projects.map{ |p| [escape_javascript(p.name), p.default_tags] }.flatten].to_json
|
||||
end
|
||||
|
||||
|
|
@ -311,7 +311,7 @@ module TodosHelper
|
|||
|
||||
def update_needs_to_remove_todo_from_container
|
||||
source_view do |page|
|
||||
page.context { return @context_changed || @todo.deferred? || @todo.pending? || @todo_should_be_hidden }
|
||||
page.context { return @context_changed || @todo_deferred_state_changed || @todo_pending_state_changed || @todo_should_be_hidden }
|
||||
page.project { return @todo_deferred_state_changed || @todo_pending_state_changed || @project_changed}
|
||||
page.deferred { return @context_changed || !(@todo.deferred? || @todo.pending?) }
|
||||
page.calendar { return @due_date_changed || !@todo.due }
|
||||
|
|
@ -339,7 +339,7 @@ module TodosHelper
|
|||
|
||||
def append_updated_todo
|
||||
source_view do |page|
|
||||
page.context { return false }
|
||||
page.context { return @todo_deferred_state_changed || @todo_pending_state_changed }
|
||||
page.project { return @todo_deferred_state_changed || @todo_pending_state_changed }
|
||||
page.deferred { return @context_changed && (@todo.deferred? || @todo.pending?) }
|
||||
page.calendar { return @due_date_changed && @todo.due }
|
||||
|
|
@ -370,7 +370,8 @@ module TodosHelper
|
|||
@todo_was_blocked_from_active_state ||
|
||||
@todo_was_destroyed_from_deferred_state ||
|
||||
@todo_was_created_deferred ||
|
||||
@todo_was_blocked_from_completed_state
|
||||
@todo_was_blocked_from_completed_state ||
|
||||
@todo_was_created_blocked
|
||||
return "p#{todo.project_id}empty-nd"
|
||||
}
|
||||
page.tag {
|
||||
|
|
@ -379,13 +380,31 @@ module TodosHelper
|
|||
@todo_was_blocked_from_active_state ||
|
||||
@todo_was_destroyed_from_deferred_state ||
|
||||
@todo_was_created_deferred ||
|
||||
@todo_was_blocked_from_completed_state
|
||||
@todo_was_blocked_from_completed_state ||
|
||||
@todo_was_created_blocked
|
||||
return "hidden-empty-nd" if @todo.hidden?
|
||||
return "c#{todo.context_id}empty-nd"
|
||||
}
|
||||
page.calendar {
|
||||
return "tickler-empty-nd" if
|
||||
@todo_was_deferred_from_active_state ||
|
||||
@todo_was_blocked_from_active_state ||
|
||||
@todo_was_destroyed_from_deferred_state ||
|
||||
@todo_was_created_deferred ||
|
||||
@todo_was_blocked_from_completed_state ||
|
||||
@todo_was_created_blocked
|
||||
return "empty_#{@new_due_id}"
|
||||
}
|
||||
page.context {
|
||||
return "tickler-empty-nd" if
|
||||
@todo_was_deferred_from_active_state ||
|
||||
@todo_was_blocked_from_active_state ||
|
||||
@todo_was_destroyed_from_deferred_state ||
|
||||
@todo_was_created_deferred ||
|
||||
@todo_was_blocked_from_completed_state ||
|
||||
@todo_was_created_blocked
|
||||
return "c#{todo.context_id}empty-nd"
|
||||
}
|
||||
end
|
||||
|
||||
return "c#{todo.context_id}empty-nd"
|
||||
|
|
@ -417,11 +436,12 @@ module TodosHelper
|
|||
}
|
||||
page.context {
|
||||
container_id = "c#{@original_item_context_id}empty-nd" if @remaining_in_context == 0
|
||||
container_id = "tickler-empty-nd" if todo_was_removed_from_deferred_or_blocked_container && @remaining_deferred_or_pending_count == 0
|
||||
container_id = "empty-d" if @completed_count && @completed_count == 0 && !@todo.completed?
|
||||
}
|
||||
page.todo { container_id = "c#{@original_item_context_id}empty-nd" if @remaining_in_context == 0 }
|
||||
end
|
||||
return container_id.blank? ? "" : "$(\"##{container_id}\").slideDown(100);"
|
||||
return container_id.blank? ? "" : "$(\"##{container_id}\").slideDown(100);".html_safe
|
||||
end
|
||||
|
||||
def render_animation(animation)
|
||||
|
|
@ -447,6 +467,17 @@ module TodosHelper
|
|||
return $tracks_tab_index
|
||||
end
|
||||
|
||||
def feed_content_for_todo(todo)
|
||||
item_notes = todo.notes ? todo.rendered_notes : ''
|
||||
due = todo.due ? content_tag(:div, t('todos.feeds.due', :date => format_date(todo.due))) : ''
|
||||
done = todo.completed? ? content_tag(:div, t('todos.feeds.completed', :date => format_date(todo.completed_at))) : ''
|
||||
context_link = link_to(context_url(todo.context), todo.context.name)
|
||||
project_link = todo.project.is_a?(NullProject) ? content_tag(:em, t('common.none')) : link_to(project_url(todo.project), todo.project.name)
|
||||
return "#{done} #{due} #{item_notes}\n" +
|
||||
content_tag(:div, "#{t('common.project')}: #{project_link}") + "\n" +
|
||||
content_tag(:div, "#{t('common.context')}: #{context_link}")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def image_tag_for_star(todo)
|
||||
|
|
|
|||