mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-26 12:08:47 +01:00
Merge branch 'master' of git://github.com/TracksApp/tracks into upstream
This commit is contained in:
commit
66f0859e63
256 changed files with 7258 additions and 20756 deletions
25
Gemfile
25
Gemfile
|
|
@ -1,6 +1,6 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rails'
|
||||
gem 'rails'
|
||||
|
||||
# Bundle edge Rails instead:
|
||||
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
||||
|
|
@ -24,19 +24,19 @@ 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'
|
||||
gem 'sass-rails'
|
||||
gem 'coffee-rails'
|
||||
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
# gem 'therubyracer', :platform => :ruby
|
||||
gem 'therubyracer', :platform => :ruby
|
||||
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
gem 'uglifier'
|
||||
end
|
||||
|
||||
gem 'jquery-rails'
|
||||
|
||||
# To use ActiveModel has_secure_password
|
||||
gem 'bcrypt-ruby', '~> 3.0.0'
|
||||
gem 'bcrypt-ruby'
|
||||
|
||||
# To use Jbuilder templates for JSON
|
||||
# gem 'jbuilder'
|
||||
|
|
@ -50,29 +50,22 @@ gem 'bcrypt-ruby', '~> 3.0.0'
|
|||
group :development do
|
||||
if RUBY_VERSION.to_f >= 1.9
|
||||
# gem "ruby-debug19", :require => 'ruby-debug'
|
||||
gem "mongrel", "1.2.0.pre2"
|
||||
gem "mongrel", ">=1.2.0.pre2"
|
||||
else
|
||||
gem "ruby-debug"
|
||||
gem "mongrel"
|
||||
end
|
||||
gem "yard"
|
||||
gem "tolk"
|
||||
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 '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"
|
||||
gem "therubyracer"
|
||||
|
||||
# uncomment to use the webkit option. This depends on Qt to be installed
|
||||
#gem "capybara-webkit"
|
||||
|
|
@ -80,4 +73,4 @@ group :test do
|
|||
# uncomment to be able to make screenshots from scenarios
|
||||
#gem "capybara-screenshot"
|
||||
#gem "launchy"
|
||||
end
|
||||
end
|
||||
60
Gemfile.lock
60
Gemfile.lock
|
|
@ -2,7 +2,7 @@ GEM
|
|||
remote: https://rubygems.org/
|
||||
specs:
|
||||
RedCloth (4.2.9)
|
||||
aasm (3.0.7)
|
||||
aasm (3.0.8)
|
||||
actionmailer (3.2.6)
|
||||
actionpack (= 3.2.6)
|
||||
mail (~> 2.4.4)
|
||||
|
|
@ -48,7 +48,7 @@ GEM
|
|||
rack-test (>= 0.5.4)
|
||||
selenium-webdriver (~> 2.0)
|
||||
xpath (~> 0.1.4)
|
||||
childprocess (0.3.2)
|
||||
childprocess (0.3.3)
|
||||
ffi (~> 1.0.6)
|
||||
coffee-rails (3.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
|
|
@ -72,10 +72,10 @@ GEM
|
|||
erubis (2.7.0)
|
||||
execjs (1.4.0)
|
||||
multi_json (~> 1.0)
|
||||
factory_girl (3.4.0)
|
||||
factory_girl (3.5.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (3.4.0)
|
||||
factory_girl (~> 3.4.0)
|
||||
factory_girl_rails (3.5.0)
|
||||
factory_girl (~> 3.5.0)
|
||||
railties (>= 3.0.0)
|
||||
ffi (1.0.11)
|
||||
formatize (1.1.0)
|
||||
|
|
@ -83,7 +83,7 @@ GEM
|
|||
actionpack (~> 3.0)
|
||||
bluecloth (~> 2.2)
|
||||
gem_plugin (0.2.3)
|
||||
gherkin (2.11.0)
|
||||
gherkin (2.11.1)
|
||||
json (>= 1.4.6)
|
||||
hike (1.2.1)
|
||||
htmlentities (4.3.1)
|
||||
|
|
@ -94,19 +94,19 @@ GEM
|
|||
thor (~> 0.14)
|
||||
json (1.7.3)
|
||||
libv8 (3.3.10.4)
|
||||
libwebsocket (0.1.3)
|
||||
libwebsocket (0.1.4)
|
||||
addressable
|
||||
mail (2.4.4)
|
||||
i18n (>= 0.4.0)
|
||||
mime-types (~> 1.16)
|
||||
treetop (~> 1.4.8)
|
||||
mime-types (1.18)
|
||||
mime-types (1.19)
|
||||
mongrel (1.2.0.pre2)
|
||||
daemons (~> 1.0.10)
|
||||
gem_plugin (~> 0.2.3)
|
||||
multi_json (1.3.6)
|
||||
mysql2 (0.3.11)
|
||||
nokogiri (1.5.4)
|
||||
nokogiri (1.5.5)
|
||||
polyglot (0.3.3)
|
||||
rack (1.4.1)
|
||||
rack-cache (1.2)
|
||||
|
|
@ -135,25 +135,24 @@ GEM
|
|||
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.1)
|
||||
rspec-expectations (2.10.0)
|
||||
rspec (2.11.0)
|
||||
rspec-core (~> 2.11.0)
|
||||
rspec-expectations (~> 2.11.0)
|
||||
rspec-mocks (~> 2.11.0)
|
||||
rspec-core (2.11.0)
|
||||
rspec-expectations (2.11.1)
|
||||
diff-lcs (~> 1.1.3)
|
||||
rspec-mocks (2.10.1)
|
||||
rubyzip (0.9.8)
|
||||
rspec-mocks (2.11.1)
|
||||
rubyzip (0.9.9)
|
||||
sanitize (2.0.3)
|
||||
nokogiri (>= 1.4.4, < 1.6)
|
||||
sass (3.1.19)
|
||||
sass (3.1.20)
|
||||
sass-rails (3.2.5)
|
||||
railties (~> 3.2.0)
|
||||
sass (>= 3.1.10)
|
||||
tilt (~> 1.3)
|
||||
selenium-webdriver (2.22.2)
|
||||
selenium-webdriver (2.24.0)
|
||||
childprocess (>= 0.2.5)
|
||||
ffi (~> 1.0)
|
||||
libwebsocket (~> 0.1.3)
|
||||
multi_json (~> 1.0)
|
||||
rubyzip
|
||||
|
|
@ -167,18 +166,22 @@ GEM
|
|||
rails (>= 3.1)
|
||||
therubyracer (0.10.1)
|
||||
libv8 (~> 3.3.10)
|
||||
thor (0.15.2)
|
||||
thor (0.15.4)
|
||||
tilt (1.3.3)
|
||||
tolk (1.3.1)
|
||||
will_paginate
|
||||
ya2yaml (~> 0.26)
|
||||
treetop (1.4.10)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
tzinfo (0.3.33)
|
||||
uglifier (1.2.4)
|
||||
uglifier (1.2.6)
|
||||
execjs (>= 0.3.0)
|
||||
multi_json (>= 1.0.2)
|
||||
multi_json (~> 1.3)
|
||||
will_paginate (3.0.3)
|
||||
xpath (0.1.4)
|
||||
nokogiri (~> 1.3)
|
||||
ya2yaml (0.31)
|
||||
yard (0.8.2.1)
|
||||
|
||||
PLATFORMS
|
||||
|
|
@ -189,25 +192,26 @@ DEPENDENCIES
|
|||
aasm
|
||||
acts_as_list
|
||||
aruba
|
||||
bcrypt-ruby (~> 3.0.0)
|
||||
bcrypt-ruby
|
||||
capybara
|
||||
coffee-rails (~> 3.2.1)
|
||||
coffee-rails
|
||||
cucumber-rails
|
||||
database_cleaner
|
||||
factory_girl_rails
|
||||
formatize
|
||||
htmlentities
|
||||
jquery-rails
|
||||
mongrel (= 1.2.0.pre2)
|
||||
mongrel (>= 1.2.0.pre2)
|
||||
mysql2
|
||||
rails
|
||||
rails_autolink
|
||||
sanitize
|
||||
sass-rails (~> 3.2.3)
|
||||
sass-rails
|
||||
selenium-webdriver
|
||||
sqlite3
|
||||
swf_fu
|
||||
therubyracer
|
||||
uglifier (>= 1.0.3)
|
||||
tolk
|
||||
uglifier
|
||||
will_paginate
|
||||
yard
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
//= require tracks.js
|
||||
|
||||
// Stuff in vendor/assets
|
||||
//= require jquery-ui-1.8.17.custom.min
|
||||
//= require jquery-ui-1.8.21.custom.min
|
||||
//= require jquery.ui.touch-punch.min
|
||||
//= require jquery.blockUI
|
||||
//= require jquery.cookie
|
||||
//= require jquery.form
|
||||
|
|
@ -27,4 +28,4 @@
|
|||
//= require niftycube
|
||||
//= require superfish
|
||||
//= require supersubs
|
||||
//= require swfobject
|
||||
//= require swf_fu
|
||||
|
|
@ -35,7 +35,7 @@ var TracksForm = {
|
|||
$('#project_name').html(name);
|
||||
},
|
||||
set_tag_list: function (name) {
|
||||
$('input#todo_tag_list').val(name);
|
||||
$('input#tag_list').val(name);
|
||||
},
|
||||
set_tag_list_for_multi_add: function (name) {
|
||||
$('#multi_tag_list').val(name);
|
||||
|
|
@ -252,7 +252,6 @@ var TracksPages = {
|
|||
ContextItems.setup_autocomplete_for_contexts('input[name=context_name]');
|
||||
ContextItems.setup_autocomplete_for_contexts('input[id="project_default_context_name"]');
|
||||
TracksPages.setup_autocomplete_for_tag_list('input[name=tag_list]'); // todo edit form
|
||||
TracksPages.setup_autocomplete_for_tag_list('input[name=todo_tag_list]'); // new todo form
|
||||
TracksPages.setup_autocomplete_for_tag_list('input[id="project_default_tags"]');
|
||||
TodoItems.setup_autocomplete_for_predecessor();
|
||||
},
|
||||
|
|
@ -519,8 +518,8 @@ var TodoItems = {
|
|||
ui.draggable.remove();
|
||||
$('.drop_target').hide();
|
||||
|
||||
ajax_options = default_ajax_options_for_scripts('POST', relative_to_root('todos/change_context'), target);
|
||||
ajax_options.data += "&todo[id]="+dragged_todo + "&todo[context_id]="+context_id
|
||||
ajax_options = default_ajax_options_for_scripts('POST', relative_to_root('todos/'+dragged_todo + '/change_context'), target);
|
||||
ajax_options.data += "&todo[context_id]="+context_id
|
||||
$.ajax(ajax_options);
|
||||
},
|
||||
setup_drag_and_drop: function() {
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ table.c {
|
|||
display:inline;
|
||||
}
|
||||
|
||||
input#todo_description, input#todo_tag_list, textarea#todo_notes, select#todo_project_id, select#todo_context_id {
|
||||
input#todo_description, input#tag_list, textarea#todo_notes, select#todo_project_id, select#todo_context_id {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
|
|
|
|||
0
backup.rails2.3/plugins/swf_fu/assets/swfs/expressInstall.swf → app/assets/swfs/expressInstall.swf
Executable file → Normal file
0
backup.rails2.3/plugins/swf_fu/assets/swfs/expressInstall.swf → app/assets/swfs/expressInstall.swf
Executable file → Normal file
|
|
@ -100,11 +100,7 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def count_deferred_todos(todos_parent)
|
||||
if todos_parent.nil?
|
||||
count = 0
|
||||
else
|
||||
count = todos_parent.todos.deferred.count
|
||||
end
|
||||
return todos_parent.nil? ? 0 : eval("@#{todos_parent.class.to_s.downcase}_deferred_counts[#{todos_parent.id}]") || 0
|
||||
end
|
||||
|
||||
# Convert a date object to the format specified in the user's preferences in
|
||||
|
|
@ -131,8 +127,8 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
# 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
|
||||
# 'mobile' version designed to be suitable for use from a phone or PDA. It
|
||||
# makes some sense that the 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
|
||||
|
|
@ -284,7 +280,8 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
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)")
|
||||
eval("@#{parent}_not_done_counts ||= current_user.todos.active.group('#{parent}_id').count")
|
||||
eval("@#{parent}_deferred_counts ||= current_user.todos.deferred.group('#{parent}_id').count")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,324 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -67,6 +67,7 @@ class ContextsController < ApplicationController
|
|||
respond_to do |format|
|
||||
format.js do
|
||||
@down_count = current_user.contexts.size
|
||||
init_not_done_counts
|
||||
end
|
||||
format.xml do
|
||||
if @context.new_record?
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ class IntegrationsController < ApplicationController
|
|||
require 'mail'
|
||||
|
||||
skip_before_filter :login_required, :only => [:cloudmailin, :search_plugin, :google_gadget]
|
||||
|
||||
|
||||
def index
|
||||
@page_title = 'TRACKS::Integrations'
|
||||
end
|
||||
|
|
@ -10,28 +10,22 @@ class IntegrationsController < ApplicationController
|
|||
def rest_api
|
||||
@page_title = 'TRACKS::REST API Documentation'
|
||||
end
|
||||
|
||||
|
||||
def get_quicksilver_applescript
|
||||
context = current_user.contexts.find params[:context_id]
|
||||
render :partial => 'quicksilver_applescript', :locals => { :context => context }
|
||||
get_applescript('quicksilver_applescript')
|
||||
end
|
||||
|
||||
def get_applescript1
|
||||
context = current_user.contexts.find params[:context_id]
|
||||
render :partial => 'applescript1', :locals => { :context => context }
|
||||
get_applescript('applescript1')
|
||||
end
|
||||
|
||||
def get_applescript2
|
||||
context = current_user.contexts.find params[:context_id]
|
||||
render :partial => 'applescript2', :locals => { :context => context }
|
||||
get_applescript('applescript2')
|
||||
end
|
||||
|
||||
def search_plugin
|
||||
# TODO: ASSET PATH!!
|
||||
@icon_data = [File.open(Rails.root + '/app/assets/images/done.png').read].
|
||||
@icon_data = [File.open(File.join(Rails.root, 'app', 'assets', 'images', 'done.png')).read].
|
||||
pack('m').gsub(/\n/, '')
|
||||
|
||||
render :layout => false
|
||||
end
|
||||
|
||||
def google_gadget
|
||||
|
|
@ -49,38 +43,18 @@ class IntegrationsController < ApplicationController
|
|||
return false
|
||||
end
|
||||
|
||||
# parse message
|
||||
message = Mail.new(params[:message])
|
||||
|
||||
# find user
|
||||
user = User.where("preferences.sms_email = ?", message.from).includes(:preference).first
|
||||
if user.nil?
|
||||
render :text => "No user found", :status => 404
|
||||
return false
|
||||
end
|
||||
|
||||
# load user settings
|
||||
context = user.prefs.sms_context
|
||||
|
||||
# prepare body
|
||||
if message.body.multipart?
|
||||
body = message.body.preamble
|
||||
if MessageGateway::receive(Mail.new(params[:message]))
|
||||
render :text => 'success', :status => 200
|
||||
else
|
||||
body = message.body.to_s
|
||||
render :text => "No user found or other error", :status => 404
|
||||
end
|
||||
|
||||
# parse mail
|
||||
if message.subject.to_s.empty?
|
||||
description = body
|
||||
notes = nil
|
||||
else
|
||||
description = message.subject.to_s
|
||||
notes = body
|
||||
end
|
||||
|
||||
# create todo
|
||||
todo = Todo.from_rich_message(user, context.id, description, notes)
|
||||
todo.save!
|
||||
render :text => 'success', :status => 200
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_applescript(partial_name)
|
||||
context = current_user.contexts.find params[:context_id]
|
||||
render :partial => partial_name, :locals => { :context => context }
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -31,14 +31,13 @@ class ProjectsController < ApplicationController
|
|||
@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.xml { render :xml => @projects.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)
|
||||
|
|
@ -182,7 +181,10 @@ class ProjectsController < ApplicationController
|
|||
@contexts = current_user.contexts
|
||||
|
||||
respond_to do |format|
|
||||
format.js { @down_count = current_user.projects.size }
|
||||
format.js do
|
||||
@down_count = current_user.projects.size
|
||||
init_not_done_counts
|
||||
end
|
||||
format.xml do
|
||||
if @project.new_record?
|
||||
render_failure @project.errors.to_xml.html_safe, 409
|
||||
|
|
@ -288,6 +290,7 @@ class ProjectsController < ApplicationController
|
|||
@projects = current_user.projects.alphabetize(:state => @state) if @state
|
||||
@contexts = current_user.contexts
|
||||
init_not_done_counts(['project'])
|
||||
init_project_hidden_todo_counts(['project']) if @state == 'hidden'
|
||||
end
|
||||
|
||||
def actionize
|
||||
|
|
@ -295,6 +298,7 @@ class ProjectsController < ApplicationController
|
|||
@projects = current_user.projects.actionize(:state => @state) if @state
|
||||
@contexts = current_user.contexts
|
||||
init_not_done_counts(['project'])
|
||||
init_project_hidden_todo_counts(['project']) if @state == 'hidden'
|
||||
end
|
||||
|
||||
def done_todos
|
||||
|
|
|
|||
|
|
@ -10,26 +10,6 @@ class TodosController < ApplicationController
|
|||
# :calendar, :auto_complete_for_predecessor, :remove_predecessor, :add_predecessor]
|
||||
|
||||
protect_from_forgery :except => :check_deferred
|
||||
|
||||
def with_parent_resource_scope(&block)
|
||||
@feed_title = t('common.actions')
|
||||
if (params[:context_id])
|
||||
@context = current_user.contexts.find_by_params(params)
|
||||
@feed_title = @feed_title + t('todos.feed_title_in_context', :context => @context.name)
|
||||
Todo.send :where, ['todos.context_id = ?', @context.id] do
|
||||
yield
|
||||
end
|
||||
elsif (params[:project_id])
|
||||
@project = current_user.projects.find_by_params(params)
|
||||
@feed_title = @feed_title + t('todos.feed_title_in_project', :project => @project.name)
|
||||
@project_feed = true
|
||||
Todo.send :where, ['todos.project_id = ?', @project.id] do
|
||||
yield
|
||||
end
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def index
|
||||
@source_view = params['_source_view'] || 'todo'
|
||||
|
|
@ -47,7 +27,11 @@ class TodosController < ApplicationController
|
|||
@not_done_todos = @not_done_todos.
|
||||
reorder("todos.due IS NULL, todos.due ASC, todos.created_at ASC").
|
||||
includes(Todo::DEFAULT_INCLUDES)
|
||||
@not_done_todos = @not_done_todos.limit(sanitize(params[:limit])) if params[:limit]
|
||||
|
||||
if params[:limit]
|
||||
@not_done_todos = @not_done_todos.limit(sanitize(params[:limit]))
|
||||
@todos = @todos.limit(sanitize(params[:limit]))
|
||||
end
|
||||
|
||||
if params[:due]
|
||||
due_within_when = Time.zone.now + params['due'].to_i.days
|
||||
|
|
@ -363,7 +347,7 @@ class TodosController < ApplicationController
|
|||
|
||||
def remove_predecessor
|
||||
@source_view = params['_source_view'] || 'todo'
|
||||
@todo = current_user.todos.find_by_id(params['id']).includes(Todo::DEFAULT_INCLUDES)
|
||||
@todo = current_user.todos.includes(Todo::DEFAULT_INCLUDES).find_by_id(params['id'])
|
||||
@predecessor = current_user.todos.find_by_id(params['predecessor'])
|
||||
@predecessors = @predecessor.predecessors
|
||||
@successor = @todo
|
||||
|
|
@ -469,10 +453,10 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
def change_context
|
||||
# TODO: is this method used?
|
||||
@todo = Todo.find_by_id(params[:todo][:id])
|
||||
# change context if you drag a todo to another context
|
||||
@todo = current_user.todos.find_by_id(params[:id])
|
||||
@original_item_context_id = @todo.context_id
|
||||
@context = Context.find_by_id(params[:todo][:context_id])
|
||||
@context = current_user.contexts.find_by_id(params[:todo][:context_id])
|
||||
@todo.context = @context
|
||||
@saved = @todo.save
|
||||
|
||||
|
|
@ -1245,7 +1229,7 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
def update_todo_state_if_project_changed
|
||||
if ( @project_changed ) then
|
||||
if @project_changed
|
||||
@todo.update_state_from_project
|
||||
@remaining_undone_in_project = current_user.projects.find_by_id(@original_item_project_id).todos.active.count if source_view_is :project
|
||||
end
|
||||
|
|
@ -1269,8 +1253,8 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
def update_tags
|
||||
if params[:todo_tag_list]
|
||||
@todo.tag_with(params[:todo_tag_list])
|
||||
if params[:tag_list]
|
||||
@todo.tag_with(params[:tag_list])
|
||||
@todo.tags(true) #force a reload for proper rendering
|
||||
end
|
||||
end
|
||||
|
|
@ -1423,7 +1407,7 @@ class TodosController < ApplicationController
|
|||
end
|
||||
|
||||
def tag_list
|
||||
@params['todo_tag_list']
|
||||
@params['tag_list']
|
||||
end
|
||||
|
||||
def predecessor_list
|
||||
|
|
|
|||
|
|
@ -2,27 +2,12 @@
|
|||
# application.
|
||||
module ApplicationHelper
|
||||
|
||||
# Replicates the link_to method but also checks request.request_uri to find
|
||||
# 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)
|
||||
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)
|
||||
Integer (date.in_time_zone.to_date - current_user.time.to_date)
|
||||
(date.in_time_zone.to_date - current_user.time.to_date).to_i
|
||||
end
|
||||
|
||||
# Check due date in comparison to today's date Flag up date appropriately with
|
||||
|
|
@ -123,8 +108,7 @@ module ApplicationHelper
|
|||
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}),
|
||||
link_to(descriptor, edit_note_path(note),
|
||||
{:id => "link_edit_#{dom_id(note)}", :class => "note_edit_settings"})
|
||||
end
|
||||
|
||||
|
|
@ -133,30 +117,30 @@ module ApplicationHelper
|
|||
end
|
||||
|
||||
def item_link_to_context(item)
|
||||
descriptor = "[C]"
|
||||
descriptor = "[#{item.context.name}]" if prefs.verbose_action_descriptors
|
||||
link_to_context( item.context, descriptor )
|
||||
link_to_context( item.context, prefs.verbose_action_descriptors ? "[#{item.context.name}]" : "[C]" )
|
||||
end
|
||||
|
||||
def item_link_to_project(item)
|
||||
descriptor = "[P]"
|
||||
descriptor = "[#{item.project.name}]" if prefs.verbose_action_descriptors
|
||||
link_to_project( item.project, descriptor )
|
||||
link_to_project( item.project, prefs.verbose_action_descriptors ? "[#{item.project.name}]" : "[P]" )
|
||||
end
|
||||
|
||||
def render_flash
|
||||
render :partial => 'shared/flash', :object => flash
|
||||
end
|
||||
|
||||
def time_span_text(date, i18n_text)
|
||||
return (date ? "#{i18n_text} #{format_date(date)}" : "").html_safe
|
||||
end
|
||||
|
||||
def recurrence_time_span(rt)
|
||||
case rt.ends_on
|
||||
when "no_end_date"
|
||||
return rt.start_from.nil? ? "" : I18n.t("todos.recurrence.pattern.from") + " " + format_date(rt.start_from)
|
||||
return time_span_text(rt.start_from, I18n.t("todos.recurrence.pattern.from"))
|
||||
when "ends_on_number_of_times"
|
||||
return I18n.t("todos.recurrence.pattern.times", :number => rt.number_of_occurences)
|
||||
when "ends_on_end_date"
|
||||
starts = rt.start_from.nil? ? "" : I18n.t("todos.recurrence.pattern.from") + " " + format_date(rt.start_from)
|
||||
ends = rt.end_date.nil? ? "" : " " + I18n.t("todos.recurrence.pattern.until") + " " + format_date(rt.end_date)
|
||||
starts = time_span_text(rt.start_from, I18n.t("todos.recurrence.pattern.from"))
|
||||
ends = time_span_text(rt.end_date, I18n.t("todos.recurrence.pattern.until"))
|
||||
return starts+ends
|
||||
else
|
||||
raise Exception.new, "unknown recurrence time span selection (#{rt.ends_on})"
|
||||
|
|
@ -166,17 +150,15 @@ module ApplicationHelper
|
|||
def recurrence_pattern_as_text(recurring_todo)
|
||||
rt = recurring_todo.recurring_target_as_text
|
||||
rp = recurring_todo.recurrence_pattern
|
||||
# only add space if recurrence_pattern has content
|
||||
rp = " " + rp if !rp.nil?
|
||||
rp = " " + rp unless rp.nil?
|
||||
rts = recurrence_time_span(recurring_todo)
|
||||
# only add space if recurrence_time_span has content
|
||||
rts = " " + rts if !(rts == "")
|
||||
rts = " " + rts unless rts == ""
|
||||
return rt+rp+rts
|
||||
end
|
||||
|
||||
def date_format_for_date_picker()
|
||||
standard_format = current_user.prefs.date_format
|
||||
translations = [
|
||||
[
|
||||
['%m', 'mm'],
|
||||
['%b', 'M'],
|
||||
['%B', 'MM'],
|
||||
|
|
@ -185,25 +167,24 @@ module ApplicationHelper
|
|||
['%A', 'DD'],
|
||||
['%y', 'y'],
|
||||
['%Y', 'yy']
|
||||
]
|
||||
translations.inject(standard_format) do |str, translation|
|
||||
str.gsub(*translation)
|
||||
end
|
||||
].inject(current_user.prefs.date_format) { |str, translation| str.gsub(*translation) }
|
||||
end
|
||||
|
||||
def sidebar_html_for_titled_list (list, title)
|
||||
return content_tag(:h3, title+" (#{list.size})") + content_tag(:ul, sidebar_html_for_list(list))
|
||||
end
|
||||
|
||||
def link_to_sidebar_item(item)
|
||||
item.is_a?(Project) ? link_to_project( item ) : link_to_context( item )
|
||||
end
|
||||
|
||||
def sidebar_html_for_item(item)
|
||||
content_tag(:li, link_to_sidebar_item(item) + " (" + count_undone_todos_phrase(item)+")")
|
||||
end
|
||||
|
||||
def sidebar_html_for_list(list)
|
||||
if 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)+")")
|
||||
end.html_safe
|
||||
end
|
||||
return content_tag(:li, t('sidebar.list_empty')).html_safe if list.empty?
|
||||
return list.inject("") { |html, item| html << sidebar_html_for_item(item) }.html_safe
|
||||
end
|
||||
|
||||
def generate_i18n_strings
|
||||
|
|
@ -230,7 +211,7 @@ module ApplicationHelper
|
|||
def javascript_tag_for_i18n_datepicker
|
||||
locale = I18n.locale
|
||||
# do not include en as locale since this the available by default
|
||||
if locale and locale != :en
|
||||
if locale && locale != :en
|
||||
javascript_include_tag("i18n/jquery.ui.datepicker-#{locale}.js")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,14 +1,5 @@
|
|||
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'),
|
||||
|
|
@ -21,8 +12,7 @@ module ContextsHelper
|
|||
end
|
||||
|
||||
def link_to_edit_context (context, descriptor = sanitize(context.name))
|
||||
link_to(descriptor,
|
||||
url_for({:controller => 'contexts', :action => 'edit', :id => context.id}),
|
||||
link_to(descriptor, edit_context_path(context),
|
||||
{
|
||||
:id => "link_edit_#{dom_id(context)}",
|
||||
:class => "context_edit_settings icon"
|
||||
|
|
|
|||
|
|
@ -1,25 +1,26 @@
|
|||
module FeedlistHelper
|
||||
|
||||
def linkoptions(format, options)
|
||||
merge_hashes( {:format => format}, options, user_token_hash)
|
||||
end
|
||||
|
||||
def rss_formatted_link(options = {})
|
||||
image_tag = image_tag("feed-icon.png", :size => "16X16", :border => 0, :class => "rss-icon")
|
||||
linkoptions = merge_hashes( {:format => 'rss'}, user_token_hash, options)
|
||||
link_to(image_tag, linkoptions, :title => "RSS feed")
|
||||
link_to(image_tag, linkoptions('rss', options), :title => "RSS feed")
|
||||
end
|
||||
|
||||
def text_formatted_link(options = {})
|
||||
linkoptions = merge_hashes( {:format => 'txt'}, user_token_hash, options)
|
||||
link_to(content_tag(:span, 'TXT', {:class => 'feed', :title => "Plain text feed"}), linkoptions)
|
||||
link_to(content_tag(:span, 'TXT', {:class => 'feed', :title => "Plain text feed"}), linkoptions('txt', options))
|
||||
end
|
||||
|
||||
def ical_formatted_link(options = {})
|
||||
linkoptions = merge_hashes( {:format => 'ics'}, user_token_hash, options)
|
||||
link_to(content_tag(:span, 'iCal', {:class=>"feed", :title => "iCal feed"}), linkoptions)
|
||||
link_to(content_tag(:span, 'iCal', {:class=>"feed", :title => "iCal feed"}), linkoptions('ics', options))
|
||||
end
|
||||
|
||||
def feed_links(feeds, link_options, title)
|
||||
space = " "
|
||||
html = ""
|
||||
html << rss_formatted_link(link_options)+space if feeds.include?(:rss)
|
||||
html << rss_formatted_link(link_options) +space if feeds.include?(:rss)
|
||||
html << text_formatted_link(link_options)+space if feeds.include?(:txt)
|
||||
html << ical_formatted_link(link_options)+space if feeds.include?(:ical)
|
||||
html << title
|
||||
|
|
|
|||
|
|
@ -1,47 +1,18 @@
|
|||
module ProjectsHelper
|
||||
|
||||
def get_listing_sortable_options(list_container_id)
|
||||
{
|
||||
:tag => 'div',
|
||||
:handle => 'handle',
|
||||
:complete => visual_effect(:highlight, list_container_id),
|
||||
:url => order_projects_path
|
||||
}
|
||||
end
|
||||
|
||||
def set_element_visible(id,test)
|
||||
if (test)
|
||||
page.show id
|
||||
else
|
||||
page.hide id
|
||||
end
|
||||
end
|
||||
|
||||
def project_next_prev
|
||||
html = ""
|
||||
if @previous_project
|
||||
project_name = truncate(@previous_project.name, :length => 40, :omission => "...")
|
||||
html << link_to_project(@previous_project, "« #{project_name}".html_safe)
|
||||
end
|
||||
html << link_to_project(@previous_project, "« #{@previous_project.shortened_name}".html_safe) if @previous_project
|
||||
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_safe)
|
||||
end
|
||||
html.html_safe
|
||||
html << link_to_project(@next_project, "#{@next_project.shortened_name} »".html_safe) if @next_project
|
||||
return html.html_safe
|
||||
end
|
||||
|
||||
def project_next_prev_mobile
|
||||
prev_project,next_project= "", ""
|
||||
unless @previous_project.nil?
|
||||
project_name = truncate(@previous_project.name, :length => 40, :omission => "...")
|
||||
prev_project = content_tag(:li, link_to_project_mobile(@previous_project, "5", project_name), :class=>"prev")
|
||||
end
|
||||
unless @next_project.nil?
|
||||
project_name = truncate(@next_project.name, :length => 40, :omission => "...")
|
||||
next_project = content_tag(:li, link_to_project_mobile(@next_project, "6", project_name), :class=>"next")
|
||||
end
|
||||
return content_tag(:ul, "#{prev_project}#{next_project}".html_safe, :class=>"next-prev-project").html_safe
|
||||
prev_project = content_tag(:li, link_to_project_mobile(@previous_project, "5", @previous_project.shortened_name), :class=>"prev") if @previous_project
|
||||
next_project = content_tag(:li, link_to_project_mobile(@next_project, "6", @next_project.shortened_name), :class=>"next") if @next_project
|
||||
return content_tag(:ul, "#{prev_project}#{next_project}".html_safe, :class=>"next-prev-project")
|
||||
end
|
||||
|
||||
def link_to_delete_project(project, descriptor = sanitize(project.name))
|
||||
|
|
@ -58,8 +29,7 @@ module ProjectsHelper
|
|||
end
|
||||
|
||||
def link_to_edit_project (project, descriptor = sanitize(project.name))
|
||||
link_to(descriptor,
|
||||
url_for({:controller => 'projects', :action => 'edit', :id => project.id}),
|
||||
link_to(descriptor, edit_project_path(project),
|
||||
{
|
||||
:id => "link_edit_#{dom_id(project)}",
|
||||
:class => "project_edit_settings icon"
|
||||
|
|
@ -72,7 +42,6 @@ module ProjectsHelper
|
|||
project_description += content_tag(:p,
|
||||
"#{count_undone_todos_phrase(p)}. #{t('projects.project_state', :state => project.state)}".html_safe
|
||||
)
|
||||
raw project_description
|
||||
end
|
||||
|
||||
def needsreview_class(item)
|
||||
|
|
|
|||
|
|
@ -16,8 +16,12 @@ module TodosHelper
|
|||
end
|
||||
|
||||
def remote_delete_menu_item(todo)
|
||||
# TODO: what is the current way to do mouseover with css?
|
||||
return link_to(
|
||||
image_tag("delete_off.png", :mouseover => "delete_on.png", :alt => t('todos.delete'), :align => "absmiddle")+" "+t('todos.delete'),
|
||||
image_tag("delete_off.png",
|
||||
:onmouseover => "this.src='#{path_to_image("delete_on.png")}'",
|
||||
:onmouseout => "this.src='#{path_to_image("delete_off.png")}'",
|
||||
:alt => t('todos.delete'), :align => "absmiddle")+" "+t('todos.delete'),
|
||||
{:controller => 'todos', :action => 'destroy', :id => todo.id},
|
||||
:class => "icon_delete_item",
|
||||
:id => "delete_#{dom_id(todo)}",
|
||||
|
|
@ -59,7 +63,11 @@ module TodosHelper
|
|||
end
|
||||
|
||||
def image_tag_for_defer(days)
|
||||
image_tag("defer_#{days}_off.png", :mouseover => "defer_#{days}.png", :alt => t('todos.defer_x_days', :count => days), :align => "absmiddle")+" "+t('todos.defer_x_days', :count => days)
|
||||
# TODO: what is the current way to do mouseover with css?
|
||||
image_tag("defer_#{days}_off.png",
|
||||
:onmouseover => "this.src='#{path_to_image("defer_#{days}.png")}'",
|
||||
:onmouseout => "this.src='#{path_to_image("defer_#{days}_off.png")}'",
|
||||
:alt => t('todos.defer_x_days', :count => days), :align => "absmiddle")+" "+t('todos.defer_x_days', :count => days)
|
||||
end
|
||||
|
||||
def collapsed_notes_image(todo)
|
||||
|
|
@ -135,7 +143,7 @@ module TodosHelper
|
|||
end
|
||||
|
||||
def tag_span (tag, mobile=false)
|
||||
content_tag(:span, :class => "tag #{tag.name.gsub(' ','-')}") { link_to(tag.name, tag_path(tag.name, :format => mobile ? :m : :html)) }
|
||||
content_tag(:span, :class => "tag #{tag.name.gsub(' ','-')}") { link_to(tag.name, tag_path(tag.name, :format => mobile ? :m : nil)) }
|
||||
end
|
||||
|
||||
def tag_list(todo=@todo, mobile=false)
|
||||
|
|
@ -162,7 +170,7 @@ module TodosHelper
|
|||
if (['project', 'tag', 'stats', 'search'].include?(parent_container_type))
|
||||
str << item_link_to_context( todo )
|
||||
end
|
||||
if (['context', 'tickler', 'tag', 'stats', 'search'].include?(parent_container_type)) && todo.project_id
|
||||
if (['context', 'tickler', 'tag', 'stats', 'search'].include?(parent_container_type)) && !todo.project_id.nil? && !todo.project.is_a?(NullProject)
|
||||
str << item_link_to_project( todo )
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ class MessageGateway < ActionMailer::Base
|
|||
|
||||
def receive(email)
|
||||
user = get_user_from_email_address(email)
|
||||
return if user.nil?
|
||||
return false if user.nil?
|
||||
|
||||
context = user.prefs.sms_context
|
||||
description = nil
|
||||
|
|
@ -23,11 +23,9 @@ class MessageGateway < ActionMailer::Base
|
|||
end
|
||||
end
|
||||
|
||||
# stupid T-Mobile often sends the same message multiple times
|
||||
return if user.todos.where(:description => description).first
|
||||
|
||||
todo = Todo.from_rich_message(user, context.id, description, notes)
|
||||
todo.save!
|
||||
Rails.logger.info "Saved email as todo for user #{user.login} in context #{context.name}"
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -36,14 +34,27 @@ class MessageGateway < ActionMailer::Base
|
|||
return SITE_CONFIG['email_dispatch'] == 'to' ? email.to[0] : email.from[0]
|
||||
end
|
||||
|
||||
def get_user_from_email_address(email)
|
||||
def get_user_from_env_setting
|
||||
Rails.logger.info "All received email goes to #{ENV['TRACKS_MAIL_RECEIVER']}"
|
||||
user = User.find_by_login(ENV['TRACKS_MAIL_RECEIVER'])
|
||||
Rails.logger.info "WARNING: Unknown user set for TRACKS_MAIL_RECEIVER (#{ENV['TRACKS_MAIL_RECEIVER']})" if user.nil?
|
||||
return user
|
||||
end
|
||||
|
||||
def get_user_from_mail_header(email)
|
||||
address = get_address(email)
|
||||
Rails.logger.info "Looking for user with email #{address}"
|
||||
user = User.where("preferences.sms_email" => address.strip).includes(:preference).first
|
||||
if user.nil?
|
||||
user = User.where("preferences.sms_email" => address.strip[1.100]).includes(:preference).first
|
||||
end
|
||||
Rails.logger.info(!user.nil? ? "Email belongs to #{user.login}" : "User unknown")
|
||||
return user
|
||||
end
|
||||
|
||||
def get_user_from_email_address(email)
|
||||
SITE_CONFIG['email_dispatch'] == 'single_user' ? get_user_from_env_setting : get_user_from_mail_header(email)
|
||||
end
|
||||
|
||||
def get_text_or_nil(text)
|
||||
return text ? sanitize(text.strip) : nil
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
class Project < ActiveRecord::Base
|
||||
has_many :todos, :dependent => :delete_all
|
||||
has_many :todos, :dependent => :delete_all, :order => 'todos.due IS NULL, todos.due ASC, todos.created_at ASC'
|
||||
has_many :notes, :dependent => :delete_all, :order => "created_at DESC"
|
||||
has_many :recurring_todos
|
||||
|
||||
|
|
@ -118,6 +118,9 @@ class Project < ActiveRecord::Base
|
|||
return self.todos.deferred_or_blocked.empty? && self.todos.not_deferred_or_blocked.empty?
|
||||
end
|
||||
|
||||
def shortened_name(length=40)
|
||||
name.truncate(length, :omission => "...").html_safe
|
||||
end
|
||||
|
||||
def name=(value)
|
||||
self[:name] = value.gsub(/\s{2,}/, " ").strip
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
class RecurringTodo < ActiveRecord::Base
|
||||
attr_protected :user
|
||||
|
||||
belongs_to :context
|
||||
belongs_to :project
|
||||
belongs_to :user
|
||||
|
||||
has_many :todos
|
||||
|
||||
include IsTaggable
|
||||
|
||||
scope :active, :conditions => { :state => 'active'}
|
||||
scope :completed, :conditions => { :state => 'completed'}
|
||||
|
||||
attr_protected :user
|
||||
include IsTaggable
|
||||
|
||||
include AASM
|
||||
aasm_column :state
|
||||
|
|
@ -50,31 +49,29 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def validate_daily
|
||||
if (!only_work_days) && (daily_every_x_days.nil? || daily_every_x_days.blank?)
|
||||
if (!only_work_days) && daily_every_x_days.blank?
|
||||
errors[:base] << "Every other nth day may not be empty for recurrence setting"
|
||||
end
|
||||
end
|
||||
|
||||
def validate_weekly
|
||||
if weekly_every_x_week.nil? || weekly_every_x_week.blank?
|
||||
if weekly_every_x_week.blank?
|
||||
errors[:base] << "Every other nth week may not be empty for recurrence setting"
|
||||
end
|
||||
something_set = false
|
||||
%w{sunday monday tuesday wednesday thursday friday saturday}.each do |day|
|
||||
something_set ||= self.send("on_#{day}")
|
||||
end
|
||||
errors[:base] << "You must specify at least one day on which the todo recurs" if !something_set
|
||||
%w{sunday monday tuesday wednesday thursday friday saturday}.each { |day| something_set ||= self.send("on_#{day}") }
|
||||
errors[:base] << "You must specify at least one day on which the todo recurs" unless something_set
|
||||
end
|
||||
|
||||
def validate_monthly
|
||||
case recurrence_selector
|
||||
when 0 # 'monthly_every_x_day'
|
||||
errors[:base] << "The day of the month may not be empty for recurrence setting" if monthly_every_x_day.nil? || monthly_every_x_day.blank?
|
||||
errors[:base] << "Every other nth month may not be empty for recurrence setting" if monthly_every_x_month.nil? || monthly_every_x_month.blank?
|
||||
errors[:base] << "The day of the month may not be empty for recurrence setting" if monthly_every_x_day.blank?
|
||||
errors[:base] << "Every other nth month may not be empty for recurrence setting" if monthly_every_x_month.blank?
|
||||
when 1 # 'monthly_every_xth_day'
|
||||
errors[:base] <<"Every other nth month may not be empty for recurrence setting" if monthly_every_x_month2.nil? || monthly_every_x_month2.blank?
|
||||
errors[:base] <<"The nth day of the month may not be empty for recurrence setting" if monthly_every_xth_day.nil? || monthly_every_xth_day.blank?
|
||||
errors[:base] <<"The day of the month may not be empty for recurrence setting" if monthly_day_of_week.nil? || monthly_day_of_week.blank?
|
||||
errors[:base] <<"Every other nth month may not be empty for recurrence setting" if monthly_every_x_month2.blank?
|
||||
errors[:base] <<"The nth day of the month may not be empty for recurrence setting" if monthly_every_xth_day.blank?
|
||||
errors[:base] <<"The day of the month may not be empty for recurrence setting" if monthly_day_of_week.blank?
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence selector '#{self.recurrence_selector}'"
|
||||
end
|
||||
|
|
@ -83,24 +80,24 @@ class RecurringTodo < ActiveRecord::Base
|
|||
def validate_yearly
|
||||
case recurrence_selector
|
||||
when 0 # 'yearly_every_x_day'
|
||||
errors[:base] << "The month of the year may not be empty for recurrence setting" if yearly_month_of_year.nil? || yearly_month_of_year.blank?
|
||||
errors[:base] << "The day of the month may not be empty for recurrence setting" if yearly_every_x_day.nil? || yearly_every_x_day.blank?
|
||||
errors[:base] << "The month of the year may not be empty for recurrence setting" if yearly_month_of_year.blank?
|
||||
errors[:base] << "The day of the month may not be empty for recurrence setting" if yearly_every_x_day.blank?
|
||||
when 1 # 'yearly_every_xth_day'
|
||||
errors[:base] << "The month of the year may not be empty for recurrence setting" if yearly_month_of_year2.nil? || yearly_month_of_year2.blank?
|
||||
errors[:base] << "The nth day of the month may not be empty for recurrence setting" if yearly_every_xth_day.nil? || yearly_every_xth_day.blank?
|
||||
errors[:base] << "The day of the week may not be empty for recurrence setting" if yearly_day_of_week.nil? || yearly_day_of_week.blank?
|
||||
errors[:base] << "The month of the year may not be empty for recurrence setting" if yearly_month_of_year2.blank?
|
||||
errors[:base] << "The nth day of the month may not be empty for recurrence setting" if yearly_every_xth_day.blank?
|
||||
errors[:base] << "The day of the week may not be empty for recurrence setting" if yearly_day_of_week.blank?
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence selector '#{self.recurrence_selector}'"
|
||||
end
|
||||
end
|
||||
|
||||
def starts_and_ends_on_validations
|
||||
errors[:base] << "The start date needs to be filled in" if start_from.nil? || start_from.blank?
|
||||
errors[:base] << "The start date needs to be filled in" if start_from.blank?
|
||||
case self.ends_on
|
||||
when 'ends_on_number_of_times'
|
||||
errors[:base] << "The number of recurrences needs to be filled in for 'Ends on'" if number_of_occurences.nil? || number_of_occurences.blank?
|
||||
errors[:base] << "The number of recurrences needs to be filled in for 'Ends on'" if number_of_occurences.blank?
|
||||
when "ends_on_end_date"
|
||||
errors[:base] << "The end date needs to be filled in for 'Ends on'" if end_date.nil? || end_date.blank?
|
||||
errors[:base] << "The end date needs to be filled in for 'Ends on'" if end_date.blank?
|
||||
else
|
||||
errors[:base] << "The end of the recurrence is not selected" unless ends_on == "no_end_date"
|
||||
end
|
||||
|
|
@ -114,7 +111,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
when 'due_date'
|
||||
errors[:base] << "Please select when to show the action" if show_always.nil?
|
||||
unless show_always
|
||||
errors[:base] << "Please fill in the number of days to show the todo before the due date" if show_from_delta.nil? || show_from_delta.blank?
|
||||
errors[:base] << "Please fill in the number of days to show the todo before the due date" if show_from_delta.blank?
|
||||
end
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target selector '#{self.recurrence_target}'"
|
||||
|
|
@ -157,9 +154,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def daily_every_x_days=(x)
|
||||
if recurring_period=='daily'
|
||||
self.every_other1 = x
|
||||
end
|
||||
self.every_other1 = x if recurring_period=='daily'
|
||||
end
|
||||
|
||||
def daily_every_x_days
|
||||
|
|
@ -177,9 +172,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def switch_week_day (day, position)
|
||||
if self.every_day.nil?
|
||||
self.every_day=' '
|
||||
end
|
||||
self.every_day = ' ' if self.every_day.nil?
|
||||
self.every_day = self.every_day[0,position] + day + self.every_day[position+1,self.every_day.length]
|
||||
end
|
||||
|
||||
|
|
@ -250,9 +243,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
# MONTHLY
|
||||
|
||||
def monthly_selector=(selector)
|
||||
if recurring_period=='monthly'
|
||||
self.recurrence_selector= (selector=='monthly_every_x_day')? 0 : 1
|
||||
end
|
||||
self.recurrence_selector = ( (selector=='monthly_every_x_day') ? 0 : 1) if recurring_period=='monthly'
|
||||
end
|
||||
|
||||
def monthly_every_x_day=(x)
|
||||
|
|
@ -280,11 +271,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
def monthly_every_x_month
|
||||
# in case monthly pattern is every day x, return every_other2 otherwise
|
||||
# return a default value
|
||||
if self.recurrence_selector == 0
|
||||
return self.every_other2
|
||||
else
|
||||
return 1
|
||||
end
|
||||
return self.recurrence_selector == 0 ? self.every_other2 : 1
|
||||
end
|
||||
|
||||
def monthly_every_x_month2=(x)
|
||||
|
|
@ -294,11 +281,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
def monthly_every_x_month2
|
||||
# in case monthly pattern is every xth day, return every_other2 otherwise
|
||||
# return a default value
|
||||
if self.recurrence_selector == 1
|
||||
return self.every_other2
|
||||
else
|
||||
return 1
|
||||
end
|
||||
return self.recurrence_selector == 1 ? self.every_other2 : 1
|
||||
end
|
||||
|
||||
def monthly_every_xth_day=(x)
|
||||
|
|
@ -321,9 +304,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
# YEARLY
|
||||
|
||||
def yearly_selector=(selector)
|
||||
if recurring_period=='yearly'
|
||||
self.recurrence_selector = (selector=='yearly_every_x_day') ? 0 : 1
|
||||
end
|
||||
self.recurrence_selector = ( (selector=='yearly_every_x_day') ? 0 : 1) if recurring_period=='yearly'
|
||||
end
|
||||
|
||||
def yearly_month_of_year=(moy)
|
||||
|
|
@ -333,11 +314,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
def yearly_month_of_year
|
||||
# if recurrence pattern is every x day in a month, return month otherwise
|
||||
# return a default value
|
||||
if self.recurrence_selector == 0
|
||||
return self.every_other2
|
||||
else
|
||||
return Time.zone.now.month
|
||||
end
|
||||
return self.recurrence_selector == 0 ? self.every_other2 : Time.zone.now.month
|
||||
end
|
||||
|
||||
def yearly_month_of_year2=(moy)
|
||||
|
|
@ -347,11 +324,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
def yearly_month_of_year2
|
||||
# if recurrence pattern is every xth day in a month, return month otherwise
|
||||
# return a default value
|
||||
if self.recurrence_selector == 1
|
||||
return self.every_other2
|
||||
else
|
||||
return Time.zone.now.month
|
||||
end
|
||||
return self.recurrence_selector == 1 ? self.every_other2 : Time.zone.now.month
|
||||
end
|
||||
|
||||
def yearly_every_x_day=(x)
|
||||
|
|
@ -403,51 +376,60 @@ class RecurringTodo < ActiveRecord::Base
|
|||
self.show_always=value
|
||||
end
|
||||
|
||||
def daily_recurrence_pattern
|
||||
return I18n.t("todos.recurrence.pattern.on_work_days") if only_work_days
|
||||
if every_other1 > 1
|
||||
return I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.days_midsentence.other")
|
||||
else
|
||||
return I18n.t("todos.recurrence.pattern.every_day")
|
||||
end
|
||||
end
|
||||
|
||||
def weekly_recurrence_pattern
|
||||
if every_other1 > 1
|
||||
return I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.weeks")
|
||||
else
|
||||
return I18n.t('todos.recurrence.pattern.weekly')
|
||||
end
|
||||
end
|
||||
|
||||
def monthly_recurrence_pattern
|
||||
return "invalid repeat pattern" if every_other2.nil?
|
||||
if self.recurrence_selector == 0
|
||||
on_day = " #{I18n.t('todos.recurrence.pattern.on_day_n', :n => self.every_other1)}"
|
||||
if self.every_other2>1
|
||||
return I18n.t("todos.recurrence.pattern.every_n", :n => self.every_other2) + " " + I18n.t('common.months') + on_day
|
||||
else
|
||||
return I18n.t("todos.recurrence.pattern.every_month") + on_day
|
||||
end
|
||||
else
|
||||
if self.every_other2>1
|
||||
n_months = "#{self.every_other2} #{I18n.t('common.months')}"
|
||||
else
|
||||
n_months = I18n.t('common.month')
|
||||
end
|
||||
return I18n.t('todos.recurrence.pattern.every_xth_day_of_every_n_months',
|
||||
:x => self.xth, :day => self.day_of_week, :n_months => n_months)
|
||||
end
|
||||
end
|
||||
|
||||
def yearly_recurrence_pattern
|
||||
if self.recurrence_selector == 0
|
||||
return I18n.t("todos.recurrence.pattern.every_year_on",
|
||||
:date => I18n.l(DateTime.new(Time.zone.now.year, self.every_other2, self.every_other1), :format => :month_day))
|
||||
else
|
||||
return I18n.t("todos.recurrence.pattern.every_year_on",
|
||||
:date => I18n.t("todos.recurrence.pattern.the_xth_day_of_month", :x => self.xth, :day => self.day_of_week, :month => self.month_of_year))
|
||||
end
|
||||
end
|
||||
|
||||
def recurrence_pattern
|
||||
return "invalid repeat pattern" if every_other1.nil?
|
||||
case recurring_period
|
||||
when 'daily'
|
||||
if only_work_days
|
||||
return I18n.t("todos.recurrence.pattern.on_work_days")
|
||||
else
|
||||
if every_other1 > 1
|
||||
return I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.days")
|
||||
else
|
||||
return I18n.t("todos.recurrence.pattern.every_day")
|
||||
end
|
||||
end
|
||||
when 'weekly'
|
||||
if every_other1 > 1
|
||||
return I18n.t("todos.recurrence.pattern.every_n", :n => every_other1) + " " + I18n.t("common.weeks")
|
||||
else
|
||||
return I18n.t('todos.recurrence.pattern.weekly')
|
||||
end
|
||||
when 'monthly'
|
||||
return "invalid repeat pattern" if every_other2.nil?
|
||||
if self.recurrence_selector == 0
|
||||
on_day = " " + I18n.t('todos.recurrence.pattern.on_day_n', :n => self.every_other1)
|
||||
if self.every_other2>1
|
||||
return I18n.t("todos.recurrence.pattern.every_n", :n => self.every_other2) + " " + I18n.t('common.months') + on_day
|
||||
else
|
||||
return I18n.t("todos.recurrence.pattern.every_month") + on_day
|
||||
end
|
||||
else
|
||||
if self.every_other2>1
|
||||
n_months = "#{self.every_other2} #{I18n.t('common.months')}"
|
||||
else
|
||||
n_months = I18n.t('common.month')
|
||||
end
|
||||
return I18n.t('todos.recurrence.pattern.every_xth_day_of_every_n_months',
|
||||
:x => self.xth, :day => self.day_of_week, :n_months => n_months)
|
||||
end
|
||||
when 'yearly'
|
||||
if self.recurrence_selector == 0
|
||||
return I18n.t("todos.recurrence.pattern.every_year_on",
|
||||
:date => I18n.l(DateTime.new(Time.zone.now.year, self.every_other2, self.every_other1), :format => :month_day))
|
||||
else
|
||||
return I18n.t("todos.recurrence.pattern.every_year_on",
|
||||
:date => I18n.t("todos.recurrence.pattern.the_xth_day_of_month", :x => self.xth, :day => self.day_of_week, :month => self.month_of_year))
|
||||
end
|
||||
when 'daily' then daily_recurrence_pattern
|
||||
when 'weekly' then weekly_recurrence_pattern
|
||||
when 'monthly' then monthly_recurrence_pattern
|
||||
when 'yearly' then yearly_recurrence_pattern
|
||||
else
|
||||
return 'unknown recurrence pattern: period unknown'
|
||||
end
|
||||
|
|
@ -461,7 +443,7 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def day_of_week
|
||||
return (self.every_count.nil? ? '??' : I18n.t('todos.recurrence.pattern.day_names')[self.every_count])
|
||||
return self.every_count.nil? ? '??' : I18n.t('todos.recurrence.pattern.day_names')[self.every_count]
|
||||
end
|
||||
|
||||
def month_of_year
|
||||
|
|
@ -472,10 +454,6 @@ class RecurringTodo < ActiveRecord::Base
|
|||
return has_tag?(Todo::STARRED_TAG_NAME)
|
||||
end
|
||||
|
||||
def has_tag?(tag_name)
|
||||
return self.tags.any? {|tag| tag.name == tag_name}
|
||||
end
|
||||
|
||||
def get_due_date(previous)
|
||||
case self.target
|
||||
when 'due_date'
|
||||
|
|
@ -492,14 +470,10 @@ class RecurringTodo < ActiveRecord::Base
|
|||
case self.target
|
||||
when 'due_date'
|
||||
# so set show from date relative to due date unless show_always is true or show_from_delta is nil
|
||||
if self.show_always? or self.show_from_delta.nil?
|
||||
nil
|
||||
else
|
||||
get_due_date(previous) - self.show_from_delta.days
|
||||
end
|
||||
(self.show_always? || self.show_from_delta.nil?) ? nil : get_due_date(previous) - self.show_from_delta.days
|
||||
when 'show_from_date'
|
||||
# Leave due date empty
|
||||
return get_next_date(previous)
|
||||
get_next_date(previous)
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||
end
|
||||
|
|
@ -507,14 +481,10 @@ class RecurringTodo < ActiveRecord::Base
|
|||
|
||||
def get_next_date(previous)
|
||||
case self.recurring_period
|
||||
when 'daily'
|
||||
return get_daily_date(previous)
|
||||
when 'weekly'
|
||||
return get_weekly_date(previous)
|
||||
when 'monthly'
|
||||
return get_monthly_date(previous)
|
||||
when 'yearly'
|
||||
return get_yearly_date(previous)
|
||||
when 'daily' then get_daily_date(previous)
|
||||
when 'weekly' then get_weekly_date(previous)
|
||||
when 'monthly' then get_monthly_date(previous)
|
||||
when 'yearly' then get_yearly_date(previous)
|
||||
else
|
||||
raise Exception.new, "unknown recurrence pattern: '#{self.recurring_period}'"
|
||||
end
|
||||
|
|
@ -689,21 +659,16 @@ class RecurringTodo < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def has_next_todo(previous)
|
||||
unless self.number_of_occurences.nil?
|
||||
return self.occurences_count < self.number_of_occurences
|
||||
return self.occurences_count < self.number_of_occurences unless self.number_of_occurences.nil?
|
||||
return true if self.end_date.nil? || self.ends_on == 'no_end_date'
|
||||
|
||||
case self.target
|
||||
when 'due_date'
|
||||
return get_due_date(previous) <= self.end_date
|
||||
when 'show_from_date'
|
||||
return get_show_from_date(previous) <= self.end_date
|
||||
else
|
||||
if self.end_date.nil? || self.ends_on == 'no_end_date'
|
||||
return true
|
||||
else
|
||||
case self.target
|
||||
when 'due_date'
|
||||
return get_due_date(previous) <= self.end_date
|
||||
when 'show_from_date'
|
||||
return get_show_from_date(previous) <= self.end_date
|
||||
else
|
||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||
end
|
||||
end
|
||||
raise Exception.new, "unexpected value of recurrence target '#{self.target}'"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -713,12 +678,11 @@ class RecurringTodo < ActiveRecord::Base
|
|||
|
||||
def toggle_star!
|
||||
if starred?
|
||||
_remove_tags Todo::STARRED_TAG_NAME
|
||||
tags.reload
|
||||
_remove_tags(Todo::STARRED_TAG_NAME)
|
||||
else
|
||||
_add_tags(Todo::STARRED_TAG_NAME)
|
||||
tags.reload
|
||||
end
|
||||
tags.reload
|
||||
starred?
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -26,12 +26,12 @@ class Todo < ActiveRecord::Base
|
|||
scope :active, :conditions => { :state => 'active' }
|
||||
scope :active_or_hidden, :conditions => ["todos.state = ? OR todos.state = ?", 'active', 'project_hidden']
|
||||
scope :not_completed, :conditions => ['NOT (todos.state = ?)', 'completed']
|
||||
scope :completed, :conditions => ["NOT (todos.completed_at IS NULL)"]
|
||||
scope :deferred, :conditions => ["todos.completed_at IS NULL AND NOT (todos.show_from IS NULL)"]
|
||||
scope :completed, :conditions => ["todos.state = ?", 'completed']
|
||||
scope :deferred, :conditions => ["todos.state = ?", 'deferred']
|
||||
scope :blocked, :conditions => ['todos.state = ?', 'pending']
|
||||
scope :pending, :conditions => ['todos.state = ?', 'pending']
|
||||
scope :deferred_or_blocked, :conditions => ["(todos.completed_at IS NULL AND NOT(todos.show_from IS NULL)) OR (todos.state = ?)", "pending"]
|
||||
scope :not_deferred_or_blocked, :conditions => ["(todos.completed_at IS NULL) AND (todos.show_from IS NULL) AND (NOT todos.state = ?)", "pending"]
|
||||
scope :deferred_or_blocked, :conditions => ["(todos.state = ?) OR (todos.state = ?)", "deferred", "pending"]
|
||||
scope :not_deferred_or_blocked, :conditions => ["(NOT todos.state=?) AND (NOT todos.state = ?)", "deferred", "pending"]
|
||||
scope :hidden,
|
||||
:joins => "INNER JOIN contexts c_hidden ON c_hidden.id = todos.context_id",
|
||||
:conditions => ["todos.state = ? OR (c_hidden.hide = ? AND (todos.state = ? OR todos.state = ? OR todos.state = ?))",
|
||||
|
|
@ -204,23 +204,19 @@ class Todo < ActiveRecord::Base
|
|||
return !pending_successors.empty?
|
||||
end
|
||||
|
||||
def has_tag?(tag_name)
|
||||
return self.tags.any? {|tag| tag.name == tag_name}
|
||||
end
|
||||
|
||||
def hidden?
|
||||
return self.state == 'project_hidden' || ( self.context.hidden? && (self.state == 'active' || self.state == 'deferred'))
|
||||
end
|
||||
|
||||
def update_state_from_project
|
||||
if self.state == 'project_hidden' and !self.project.hidden?
|
||||
if self.project_hidden? && (!self.project.hidden?)
|
||||
if self.uncompleted_predecessors.empty?
|
||||
self.state = 'active'
|
||||
self.activate!
|
||||
else
|
||||
self.state = 'pending'
|
||||
self.block!
|
||||
end
|
||||
elsif self.state == 'active' and self.project.hidden?
|
||||
self.state = 'project_hidden'
|
||||
elsif self.active? && self.project.hidden?
|
||||
self.hide!
|
||||
end
|
||||
self.save!
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,20 +17,20 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>YAML file containing all your actions, contexts, projects, tags and notes</td>
|
||||
<td><%= link_to "YAML file", :controller => 'data', :action => 'yaml_export' %></td>
|
||||
<td><%= link_to "YAML file", data_yaml_export_path %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CSV file containing all of your actions, with named contexts and projects</td>
|
||||
<td><%= link_to "CSV file (actions, contexts and projects)", :controller => 'data', :action => 'csv_actions' %></td>
|
||||
<td><%= link_to "CSV file (actions, contexts and projects)", data_csv_actions_path %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CSV file containing all your notes</td>
|
||||
<td><%= link_to "CSV file (notes only)", :controller => 'data', :action => 'csv_notes' %></td>
|
||||
<td><%= link_to "CSV file (notes only)", data_csv_notes_path %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>XML file containing all your actions, contexts, projects, tags and notes</td>
|
||||
<td><%= link_to "XML file (actions only)", :controller => 'data', :action => 'xml_export' %></td>
|
||||
<td><%= link_to "XML file (actions only)", data_xml_export_path %></td>
|
||||
</tr>
|
||||
</table>
|
||||
</table>
|
||||
</div><!-- End of feeds -->
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -100,18 +100,24 @@
|
|||
If Tracks is running on the same server as your mail server, you can use the integrated mail handler built into tracks. Steps to set it up:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Go to <%= link_to t('layouts.navigation.preferences'), preferences_url %>
|
||||
and set your "<%= Preference.human_attribute_name('sms_email') %>" and
|
||||
<li>Go to <%= link_to t('layouts.navigation.preferences'), preferences_url %> and
|
||||
set your "<%= Preference.human_attribute_name('sms_email') %>" and
|
||||
"<%= Preference.human_attribute_name('sms_context') %>" for todos sent in
|
||||
via email (which could come from an SMS message)</li>
|
||||
<li>In sendmail/qmail/postfix/whatever, set up an email address alias
|
||||
to pipe messages to <pre>/PATH/TO/RUBY/ruby /PATH/TO/TRACKS/script/runner -e production 'MessageGateway.receive(STDIN.read)'</pre></li>
|
||||
<li>In sendmail/qmail/postfix/whatever, set up an email address
|
||||
alias to pipe messages to <pre>/usr/bin/bundle exec /PATH/TO/TRACKS/script/rails r -e production 'MessageGateway.receive(STDIN.read)'</pre></li>
|
||||
<li>Send an email to your newly configured address!</li>
|
||||
</ul>
|
||||
<p>You can also use the Rich Todo API to send in tasks like "do laundry @ Home"
|
||||
or "Call Bill > project X". The subject of the message will fill description,
|
||||
context, and project, while the body will populate the tasks's note.
|
||||
</p>
|
||||
<p>
|
||||
You may need to configure your <tt>site.yml</tt> to tell the message gateway to look at the <tt>to:</tt> field or <tt>from:</tt> field to
|
||||
lookup Tracks' user from the email address in that field. </p>
|
||||
<p>You can also send all email to a specific Tracks user. Configure mail_dispatch in site.yml to <tt>single_user</tt> and pass the login of the user:
|
||||
<pre>TRACKS_MAIL_RECEIVER=<%=current_user.login%> usr/bin/bundle exec /PATH/TO/TRACKS/script/rails r -e production 'MessageGateway.receive(STDIN.read)'</pre>
|
||||
</p>
|
||||
|
||||
<a name="google_gadget"> </a>
|
||||
<h2>Add Tracks as a Google Gmail gadget</h2>
|
||||
|
|
|
|||
|
|
@ -105,13 +105,19 @@
|
|||
"<%= Preference.human_attribute_name('sms_context') %>" for todos sent in
|
||||
via email (which could come from an SMS message)</li>
|
||||
<li>In sendmail/qmail/postfix/whatever, set up an email address
|
||||
alias to pipe messages to <pre>/PATH/TO/RUBY/ruby /PATH/TO/TRACKS/script/runner -e production 'MessageGateway.receive(STDIN.read)'</pre></li>
|
||||
alias to pipe messages to <pre>/usr/bin/bundle exec /PATH/TO/TRACKS/script/rails r -e production 'MessageGateway.receive(STDIN.read)'</pre></li>
|
||||
<li>Send an email to your newly configured address!</li>
|
||||
</ul>
|
||||
<p>You can also use the Rich Todo API to send in tasks like "do laundry @ Home"
|
||||
or "Call Bill > project X". The subject of the message will fill description,
|
||||
context, and project, while the body will populate the tasks's note.
|
||||
</p>
|
||||
<p>
|
||||
You may need to configure your <tt>site.yml</tt> to tell the message gateway to look at the <tt>to:</tt> field or <tt>from:</tt> field to
|
||||
lookup Tracks' user from the email address in that field. </p>
|
||||
<p>You can also send all email to a specific Tracks user. Configure mail_dispatch in site.yml to <tt>single_user</tt> and pass the login of the user:
|
||||
<pre>TRACKS_MAIL_RECEIVER=<%=current_user.login%> usr/bin/bundle exec /PATH/TO/TRACKS/script/rails r -e production 'MessageGateway.receive(STDIN.read)'</pre>
|
||||
</p>
|
||||
|
||||
<a name="google_gadget"> </a>
|
||||
<h2>Add Tracks as a Google Gmail gadget</h2>
|
||||
|
|
|
|||
|
|
@ -109,13 +109,19 @@
|
|||
verzonden zijn via email (die bijv. komen via een SMS message)</li>
|
||||
<li>In sendmail/qmail/postfix/whatever, stel een email address
|
||||
alias in om berichten door te sturen naar
|
||||
<pre>/PATH/TO/RUBY/ruby /PATH/TO/TRACKS/script/runner -e production 'MessageGateway.receive(STDIN.read)'</pre></li>
|
||||
<pre>/usr/bin/bundle exec /PATH/TO/TRACKS/script/rails r -e production 'MessageGateway.receive(STDIN.read)'</pre></li>
|
||||
<li>Verstuur een email naar het net geconfigureerde e-mail adres!</li>
|
||||
</ul>
|
||||
<p>Je kan ook de Rich Todo API gebruiken om acties te maken zoals "do laundry @ Home"
|
||||
of "Call Bill > project X". Het onderwerp van het bericht zal de bijschrijving van de actie vullen,
|
||||
de context, en het project, terwijl de body van het bericht de notities van de actie zal vullen.
|
||||
</p>
|
||||
<p>
|
||||
Het kan nodig zijn om jouw <tt>site.yml</tt> te configureren om de message gateway te laten kijken naar het <tt>to:</tt> veld of het <tt>from:</tt> veld om
|
||||
de Tracks-gebruiker op te zoeken met het emailadres uit dat veld. </p>
|
||||
<p>Je kan ook alle email naar een specifieke Tracks gebruiker sturen. Stel mail_dispatch in site.yml in op <tt>single_user</tt> en geeft de login van de gebruiker door:
|
||||
<pre>TRACKS_MAIL_RECEIVER=<%=current_user.login%> usr/bin/bundle exec /PATH/TO/TRACKS/script/rails r -e production 'MessageGateway.receive(STDIN.read)'</pre>
|
||||
</p>
|
||||
|
||||
<a name="google_gadget"> </a>
|
||||
<h2>Voeg tracks toe als een Google Gmail gadget</h2>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<%= csrf_meta_tag %>
|
||||
<script type="text/javascript">
|
||||
var SOURCE_VIEW = '<%=@source_view%>';
|
||||
var AUTH_TOKEN = '<%= protect_against_forgery? ? form_authenticity_token.inspect : "" %>'
|
||||
var AUTH_TOKEN = '<%= raw(protect_against_forgery? ? form_authenticity_token.inspect : "") %>'
|
||||
var TAG_NAME = '<%= @tag_name ? @tag_name : "" %>'
|
||||
var defaultContexts = <%= default_contexts_for_autocomplete.html_safe rescue '{}' %>;
|
||||
var defaultTags = <%= default_tags_for_autocomplete.html_safe rescue '{}' %>;
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
</script>
|
||||
<link rel="shortcut icon" href="<%= image_path ('favicon.ico') %>" />
|
||||
<%= auto_discovery_link_tag(:rss, {:controller => "todos", :action => "index", :format => 'rss', :token => "#{current_user.token}"}, {:title => t('layouts.next_actions_rss_feed')}) %>
|
||||
<link rel="search" type="application/opensearchdescription+xml" title="Tracks" href="<%= search_plugin_path %>" />
|
||||
<link rel="search" type="application/opensearchdescription+xml" title="Tracks" href="<%= search_plugin_path(:format => :xml) %>" />
|
||||
<title><%= @page_title %></title>
|
||||
</head>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
t('preferences.generate_new_token'),
|
||||
refresh_token_user_path( current_user ),
|
||||
:method => :post,
|
||||
:confirm => t('preferences.generate_new_token_confirm'),
|
||||
:data => {:confirm => t('preferences.generate_new_token_confirm')},
|
||||
:id=>'prefs_new_token') %>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -35,17 +35,17 @@
|
|||
<br/>
|
||||
<label for="recurring_todo[ends_on]"><%= t('todos.recurrence.ends_on') %>:</label><br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'no_end_date', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.no_end_date') %><br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.ends_on_number_times', :number => text_field( :recurring_todo, :number_of_occurences, "size" => 3, "tabindex" => next_tab_index)) %> <br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.ends_on_date', :date => text_field(:recurring_todo, :end_date, "size" => 12, "class" => "Date", "tabindex" => next_tab_index, "autocomplete" => "off", "value" => "")) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_number_of_times', false, {:tabindex => next_tab_index})%> <%= raw t('todos.recurrence.ends_on_number_times', :number => text_field( :recurring_todo, :number_of_occurences, "size" => 3, "tabindex" => next_tab_index)) %> <br/>
|
||||
<%= radio_button_tag('recurring_todo[ends_on]', 'ends_on_end_date', false, {:tabindex => next_tab_index})%> <%= raw t('todos.recurrence.ends_on_date', :date => text_field(:recurring_todo, :end_date, "size" => 12, "class" => "Date", "tabindex" => next_tab_index, "autocomplete" => "off", "value" => "")) %><br/>
|
||||
</div></div>
|
||||
<div id="recurring_daily" style="display:block">
|
||||
<label><%= t('todos.recurrence.daily_options') %></label><br/>
|
||||
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.daily_every_number_day', :number=> text_field_tag( 'recurring_todo[daily_every_x_days]', "1", {"size" => 3, "tabindex" => next_tab_index})) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_x_day', true, {:tabindex => next_tab_index})%> <%= raw t('todos.recurrence.daily_every_number_day', :number=> text_field_tag( 'recurring_todo[daily_every_x_days]', "1", {"size" => 3, "tabindex" => next_tab_index})) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[daily_selector]', 'daily_every_work_day', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.every_work_day') %><br/>
|
||||
</div>
|
||||
<div id="recurring_weekly" style="display:none">
|
||||
<label><%= t('todos.recurrence.weekly_options') %></label><br/>
|
||||
<%= t('todos.recurrence.weekly_every_number_week', :number => text_field_tag('recurring_todo[weekly_every_x_week]', 1, {"size" => 3, "tabindex" => next_tab_index})) %><br/>
|
||||
<%= raw t('todos.recurrence.weekly_every_number_week', :number => text_field_tag('recurring_todo[weekly_every_x_week]', 1, {"size" => 3, "tabindex" => next_tab_index})) %><br/>
|
||||
<% week_day = Time.new.wday -%>
|
||||
<%# TODO: this should ideally use the 'week starts on' preferences setting, too? %>
|
||||
<%= check_box_tag('recurring_todo[weekly_return_monday]', 'm', week_day == 1 ? true : false, {:tabindex => next_tab_index}) %> <%= t('date.day_names')[1] %>
|
||||
|
|
@ -58,20 +58,20 @@
|
|||
</div>
|
||||
<div id="recurring_monthly" style="display:none">
|
||||
<label><%= t('todos.recurrence.monthly_options') %></label><br/>
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.day_x_on_every_x_month',
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_x_day', true, {:tabindex => next_tab_index})%> <%= raw t('todos.recurrence.day_x_on_every_x_month',
|
||||
:day => text_field_tag('recurring_todo[monthly_every_x_day]', Time.zone.now.mday, {"size" => 3, "tabindex" => next_tab_index}),
|
||||
:month => text_field_tag('recurring_todo[monthly_every_x_month]', 1, {"size" => 3, "tabindex" => 10})) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day')%> <%= t('todos.recurrence.monthly_every_xth_day',
|
||||
<%= radio_button_tag('recurring_todo[monthly_selector]', 'monthly_every_xth_day')%> <%= raw t('todos.recurrence.monthly_every_xth_day',
|
||||
:day => select_tag('recurring_todo[monthly_every_xth_day]', options_for_select(@xth_day), {}),
|
||||
:day_of_week => select_tag('recurring_todo[monthly_day_of_week]' , options_for_select(@days_of_week, Time.zone.now.wday), {}),
|
||||
:month => text_field_tag('recurring_todo[monthly_every_x_month2]', 1, {"size" => 3, "tabindex" => 11})) %><br/>
|
||||
</div>
|
||||
<div id="recurring_yearly" style="display:none">
|
||||
<label><%= t('todos.recurrence.yearly_options') %></label><br/>
|
||||
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.yearly_every_x_day',
|
||||
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_x_day', true, {:tabindex => next_tab_index})%> <%= raw t('todos.recurrence.yearly_every_x_day',
|
||||
:month => select_tag('recurring_todo[yearly_month_of_year]', options_for_select(@months_of_year, Time.zone.now.month), {:tabindex => next_tab_index}),
|
||||
:day => text_field_tag('recurring_todo[yearly_every_x_day]', Time.zone.now.day, "size" => 3, "tabindex" => next_tab_index)) %><br/>
|
||||
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.yearly_every_xth_day',
|
||||
<%= radio_button_tag('recurring_todo[yearly_selector]', 'yearly_every_xth_day', false, {:tabindex => next_tab_index})%> <%= raw t('todos.recurrence.yearly_every_xth_day',
|
||||
:day => select_tag('recurring_todo[yearly_every_xth_day]', options_for_select(@xth_day), {:tabindex => next_tab_index}),
|
||||
:day_of_week => select_tag('recurring_todo[yearly_day_of_week]', options_for_select(@days_of_week, Time.zone.now.wday), {:tabindex => next_tab_index}),
|
||||
:month => select_tag('recurring_todo[yearly_month_of_year2]', options_for_select(@months_of_year, Time.zone.now.month), {:tabindex => next_tab_index})) %><br/>
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
<%= radio_button_tag('recurring_todo[recurring_target]', 'due_date', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.recurrence_on_due_date') %>. <%= t('todos.recurrence.show_options') %>:
|
||||
<%= radio_button_tag('recurring_todo[recurring_show_always]', '1', true, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.show_option_always') %>
|
||||
<%= radio_button_tag('recurring_todo[recurring_show_always]', '0', false, {:tabindex => next_tab_index})%>
|
||||
<%= t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3, "tabindex" => next_tab_index})) %>
|
||||
<%= raw t('todos.recurrence.show_days_before', :days => text_field_tag( 'recurring_todo[recurring_show_days_before]', "0", {"size" => 3, "tabindex" => next_tab_index})) %>
|
||||
<br/>
|
||||
<%= radio_button_tag('recurring_todo[recurring_target]', 'show_from_date', false, {:tabindex => next_tab_index})%> <%= t('todos.recurrence.from_tickler') %><br/>
|
||||
<br/>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
<%= render :layout => 'show_results_collection', :object => @found_tags, :locals => { :collection_name => "found-tags", :collection_title => t('search.tags_matching_query')} do %>
|
||||
<span class="tags"><% @found_tags.each do |tag| -%>
|
||||
<span class="tag"><%= link_to tag.name, {:controller => "todos", :action => "tag", :id => tag.name} -%></span>
|
||||
<span class="tag"><%= link_to tag.name, tag_path(tag.name) -%></span>
|
||||
<% end %>
|
||||
</span><br/><br/>
|
||||
<% end -%>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<% @swf_count ||= 0 -%>
|
||||
<div class="open-flash-chart"><%= swf_tag "open-flash-chart.swf",
|
||||
<div class="open-flash-chart"><%= swf_tag asset_path("open-flash-chart.swf"),
|
||||
:flashvars => { 'width' => width, 'height' => height, 'data' => data},
|
||||
:parameters => { 'allowScriptAccess' => 'sameDomain', 'wmode' => 'transparent'},
|
||||
:div_id => "chart_#{@swf_count+=1}",
|
||||
|
|
|
|||
|
|
@ -7,10 +7,9 @@
|
|||
t('stats.no_tags_available')
|
||||
else
|
||||
@tags_for_cloud.each do |t| %>
|
||||
<%= link_to t.name,
|
||||
{:controller => "todos", :action => "tag", :id => t.name},
|
||||
{:style => "font-size: " + (9 + 2*(t.count.to_i-@tags_min)/@tags_divisor).to_s + "pt",
|
||||
:title => t.count.to_s+" #{t('common.actions_midsentence', :count => t.count)}"}
|
||||
<%= link_to t.name, tag_path(t.name), {
|
||||
:style => "font-size: " + (9 + 2*(t.count.to_i-@tags_min)/@tags_divisor).to_s + "pt",
|
||||
:title => t.count.to_s+" #{t('common.actions_midsentence', :count => t.count)}"}
|
||||
-%> <%
|
||||
end
|
||||
end-%>
|
||||
|
|
@ -25,10 +24,9 @@
|
|||
t('stats.no_tags_available')
|
||||
else
|
||||
@tags_for_cloud_90days.each do |t| %>
|
||||
<%= link_to t.name,
|
||||
{:controller => "todos", :action => "tag", :id => t.name},
|
||||
{:style => "font-size: " + (9 + 2*(t.count.to_i-@tags_min_90days)/@tags_divisor_90days).to_s + "pt",
|
||||
:title => t.count.to_s+" #{t('common.actions_midsentence', :count => t.count)}"}
|
||||
<%= link_to t.name, tag_path(t.name), {
|
||||
:style => "font-size: " + (9 + 2*(t.count.to_i-@tags_min_90days)/@tags_divisor_90days).to_s + "pt",
|
||||
:title => t.count.to_s+" #{t('common.actions_midsentence', :count => t.count)}"}
|
||||
-%> <%
|
||||
end
|
||||
end-%>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<% if collapsible %>
|
||||
<a href="#" class="container_toggle" id="toggle_completed"><%= image_tag("collapse.png") %></a>
|
||||
<% end %>
|
||||
<%= t('todos.completed_actions') %> <%= suffix %>
|
||||
<%= t('todos.completed_actions') %> <%= raw suffix %>
|
||||
</h2>
|
||||
<div id="completed_containeritems" class="items toggle_target">
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<% if collapsible %>
|
||||
<a href="#" class="container_toggle" id="toggle_deferred"><%= image_tag("collapse.png") %></a>
|
||||
<% end %>
|
||||
<%= t('todos.deferred_pending_actions') %> <%= append_descriptor ? append_descriptor : '' %>
|
||||
<%= t('todos.deferred_pending_actions') %> <%= raw(append_descriptor ? append_descriptor : '') %>
|
||||
</h2>
|
||||
|
||||
<div id="tickleritems" class="items toggle_target">
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@
|
|||
<input id="<%= dom_id(@todo, 'context_name') %>" name="context_name" autocomplete="off" tabindex="<%= next_tab_index%>" size="30" type="text" value="<%= h @todo.context.name %>" />
|
||||
</div>
|
||||
|
||||
<label class="tag_list_label" for="<%= dom_id(@todo, 'todo_tag_list') %>"><%= t('todos.tags') %></label>
|
||||
<%= text_field_tag 'todo_tag_list', tag_list_text, :id => dom_id(@todo, 'todo_tag_list'), :size => 30, :tabindex => next_tab_index %>
|
||||
<label class="tag_list_label" for="<%= dom_id(@todo, 'tag_list') %>"><%= t('todos.tags') %></label>
|
||||
<%= text_field_tag 'tag_list', tag_list_text, :id => dom_id(@todo, 'tag_list'), :size => 30, :tabindex => next_tab_index %>
|
||||
|
||||
<div class="due_input">
|
||||
<label for="<%= dom_id(@todo, 'due_label') %>"><%= Todo.human_attribute_name('due') %></label>
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
<% this_year = current_user.time.to_date.strftime("%Y").to_i -%>
|
||||
<h2><label for="todo_description"><%= t('common.description') %></label></h2>
|
||||
<%= text_field( "todo", "description", "tabindex" => 1, "maxlength" => 100, "size" => 50) %>
|
||||
<h2><label for="todo_tag_list"><%= t('todos.tags') %></label></h2>
|
||||
<%= text_field_tag "todo_tag_list", @tag_list_text, :size => 50, :tabindex => 2 %>
|
||||
<h2><label for="tag_list"><%= t('todos.tags') %></label></h2>
|
||||
<%= text_field_tag "tag_list", @tag_list_text, :size => 50, :tabindex => 2 %>
|
||||
<h2><label for="todo_context_id"><%= t('common.context') %></label></h2>
|
||||
<%= unless @mobile_from_context
|
||||
collection_select( "todo", "context_id", @contexts, "id", "name", {}, {"tabindex" => 3} )
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<% if collapsible %>
|
||||
<a href="#" class="container_toggle" id="toggle_deferred"><%= image_tag("collapse.png") %></a>
|
||||
<% end %>
|
||||
<%= t('todos.hidden_actions') %> <%= append_descriptor ? append_descriptor : '' %>
|
||||
<%= t('todos.hidden_actions') %> <%= raw(append_descriptor ? append_descriptor : '') %>
|
||||
</h2>
|
||||
|
||||
<div id="hiddenitems" class="items toggle_target">
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@
|
|||
<label for="todo_context_name"><%= Todo.human_attribute_name('context') %></label>
|
||||
<input id="todo_context_name" name="context_name" autocomplete="off" tabindex="<%= next_tab_index%>" size="30" type="text" value="<%= h(@initial_context_name) %>" />
|
||||
|
||||
<label for="todo_tag_list"><%= Todo.human_attribute_name('tags') + ' (' + t('shared.separate_tags_with_commas') + ')' %></label>
|
||||
<label for="tag_list"><%= Todo.human_attribute_name('tags') + ' (' + t('shared.separate_tags_with_commas') + ')' %></label>
|
||||
<%= hidden_field_tag "initial_tag_list", @initial_tags%>
|
||||
<%= text_field_tag "todo_tag_list", @initial_tags, :size => 30, :tabindex => next_tab_index %>
|
||||
<%= text_field_tag "tag_list", @initial_tags, :size => 30, :tabindex => next_tab_index %>
|
||||
<%= content_tag("div", "", :id => "tag_list_auto_complete", :class => "auto_complete") %>
|
||||
|
||||
<div class="due_input">
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ suppress_dependencies ||= false
|
|||
parameters = "_source_view=#{@source_view}"
|
||||
parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
|
||||
@z_index_counter = @z_index_counter - 1 # for IE z-index bug
|
||||
@rec_depth ||= 0
|
||||
%>
|
||||
<div id="<%= dom_id(successor, 'successor') %>" class="item-container">
|
||||
<div id="<%= dom_id(successor, 'successor_line') %>">
|
||||
|
|
@ -13,16 +14,21 @@ parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
|
|||
|
||||
<%= remote_delete_dependency(successor, predecessor) %>
|
||||
|
||||
<% unless successor.pending_successors.empty? %>
|
||||
<div class="todo_successors" id="<%= dom_id(successor, 'successors') %>">
|
||||
<%= render :partial => "todos/successor",
|
||||
:collection => successor.pending_successors,
|
||||
:locals => { :todo => successor,
|
||||
:parent_container_type => parent_container_type,
|
||||
:suppress_dependencies => true,
|
||||
:predecessor => successor }
|
||||
%>
|
||||
</div>
|
||||
<% unless successor.pending_successors.empty?
|
||||
if @rec_depth < 8
|
||||
@rec_depth+=1 %>
|
||||
<div class="todo_successors" id="<%= dom_id(successor, 'successors') %>">
|
||||
<%= render :partial => "todos/successor",
|
||||
:collection => successor.pending_successors,
|
||||
:locals => { :todo => successor,
|
||||
:parent_container_type => parent_container_type,
|
||||
:suppress_dependencies => true,
|
||||
:predecessor => successor }
|
||||
%>
|
||||
</div>
|
||||
<% else %>
|
||||
<a title="There are more dependencies that are not shown">[...]</a>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ suppress_edit_button ||= todo.completed?
|
|||
parameters = "_source_view=#{@source_view}"
|
||||
parameters += "&_tag_name=#{@tag_name}" if @source_view == 'tag'
|
||||
@z_index_counter = @z_index_counter - 1 # for IE z-index bug
|
||||
@rec_depth=0 # to prevent stack overflow on large dependency trees, see _successor.html.erb
|
||||
%>
|
||||
<div id="<%= dom_id(todo) %>" class="item-container">
|
||||
<div id="<%= dom_id(todo, 'line') %>" class="item-show">
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ xml.rss :version => "2.0" do
|
|||
xml.title h(todo.description)
|
||||
xml.description feed_content_for_todo(todo)
|
||||
xml.pubDate todo.created_at.to_s(:rfc822)
|
||||
xml.link todo.project ? project_url(todo.project) : context_url(todo.context)
|
||||
xml.link (todo.project && !todo.project.is_a?(NullProject)) ? project_url(todo.project) : context_url(todo.context)
|
||||
xml.guid todo_url(todo)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,39 +1,39 @@
|
|||
<h2>Description</h2>
|
||||
<h2><%= t('common.description') %></h2>
|
||||
<%= @todo.description %><br/>
|
||||
|
||||
<h2>Actions</h2>
|
||||
<h2><Actions><%= t('common.actions') %></h2>
|
||||
|
||||
<form method="get" action="<%= edit_todo_path(@todo, :format => :m)%>">
|
||||
<button>Edit this action</button>
|
||||
<button><%=t('todos.edit_action')%></button>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
</form>
|
||||
|
||||
<form method="post" action="<%=toggle_star_todo_path(@todo, :format=>:m)%>">
|
||||
<button>Toggle Star</button>
|
||||
<button><%=t('todos.star_action')%></button>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
</form>
|
||||
|
||||
<form method="post" action="<%=toggle_check_todo_path(@todo, :format=>:m)%>">
|
||||
<button>Mark complete</button>
|
||||
<button><%= t('todos.mark_complete')%></button>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
</form>
|
||||
|
||||
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 1)%>">
|
||||
<button>Defer 1 day</button>
|
||||
<button><%=t('todos.defer_x_days', :count => 1)%></button>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
</form>
|
||||
|
||||
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 2)%>">
|
||||
<button>Defer 2 days</button>
|
||||
<button><%=t('todos.defer_x_days', :count => 2)%></button>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
</form>
|
||||
|
||||
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 3)%>">
|
||||
<button>Defer 3 days</button>
|
||||
<button><%=t('todos.defer_x_days', :count => 3)%></button>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
</form>
|
||||
|
||||
<form method="post" action="<%=defer_todo_path(@todo, :format=>:m, :days => 7)%>">
|
||||
<button>Defer 7 days</button>
|
||||
<button><%=t('todos.defer_x_days', :count => 7)%></button>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<label for="user[password]"><%= t('users.new_password_label') %>:</label><br/>
|
||||
<%= password_field "user", "password", :size => 40 %><br/>
|
||||
<%= password_field "user", "password", :size => 40, :autocomplete => "off" %><br/>
|
||||
<label for="user[password_confirmation]"><%= t('users.password_confirmation_label') %>:</label><br/>
|
||||
<%= password_field "user", "password_confirmation", :size => 40 %><br/>
|
||||
<%= password_field "user", "password_confirmation", :size => 40, :autocomplete => "off" %><br/>
|
||||
|
|
|
|||
|
|
@ -1,65 +0,0 @@
|
|||
source :gemcutter
|
||||
source "http://gems.github.com/"
|
||||
|
||||
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"
|
||||
|
||||
# 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"
|
||||
|
||||
if RUBY_VERSION.to_f >= 1.9
|
||||
gem "soap4r-ruby1.9"
|
||||
else
|
||||
gem "soap4r", "~>1.5.8"
|
||||
end
|
||||
|
||||
group :development do
|
||||
if RUBY_VERSION.to_f >= 1.9
|
||||
gem "ruby-debug19"
|
||||
gem "mongrel", "1.2.0.pre2"
|
||||
else
|
||||
gem "ruby-debug"
|
||||
gem "mongrel"
|
||||
end
|
||||
gem "yard"
|
||||
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 "capybara", ">=0.3.5"
|
||||
gem "selenium-webdriver" # Note that > 2.14 has problems: https://code.google.com/p/selenium/issues/detail?id=3075
|
||||
gem "database_cleaner", ">=0.5.0"
|
||||
gem "cucumber-rails", "~>0.3.2"
|
||||
gem "aruba", "0.2.2", :path => "vendor/gems/aruba-0.2.2"
|
||||
|
||||
# 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
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rails', '3.2.3'
|
||||
|
||||
# Bundle edge Rails instead:
|
||||
# gem 'rails', :git => 'git://github.com/rails/rails.git'
|
||||
|
||||
gem 'sqlite3'
|
||||
gem 'mysql'
|
||||
|
||||
|
||||
# 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'
|
||||
|
||||
# To use debugger
|
||||
# gem 'ruby-debug19', :require => 'ruby-debug'
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
class TodoApi < ActionWebService::API::Base
|
||||
api_method :new_todo,
|
||||
:expects => [{:username => :string}, {:token => :string}, {:context_id => :int}, {:description => :string}, {:notes => :string}],
|
||||
:returns => [:int]
|
||||
|
||||
api_method :new_todo_for_project,
|
||||
:expects => [{:username => :string}, {:token => :string}, {:context_id => :int}, {:project_id => :int}, {:description => :string}, {:notes => :string}],
|
||||
:returns => [:int]
|
||||
|
||||
api_method :new_rich_todo,
|
||||
:expects => [{:username => :string}, {:token => :string}, {:default_context_id => :int}, {:description => :string}, {:notes => :string}],
|
||||
:returns => [:int]
|
||||
|
||||
api_method :list_contexts,
|
||||
:expects => [{:username => :string}, {:token => :string}],
|
||||
:returns => [[Context]]
|
||||
|
||||
api_method :list_projects,
|
||||
:expects => [{:username => :string}, {:token => :string}],
|
||||
:returns => [[Project]]
|
||||
|
||||
end
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
# The methods added to this helper will be available to all templates in the
|
||||
# application.
|
||||
module ApplicationHelper
|
||||
|
||||
# Replicates the link_to method but also checks request.request_uri to find
|
||||
# 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>"
|
||||
end
|
||||
|
||||
def days_from_today(date)
|
||||
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
|
||||
# a 'traffic light' colour code
|
||||
#
|
||||
def due_date(due)
|
||||
return "" if due.nil?
|
||||
|
||||
days = days_from_today(due)
|
||||
|
||||
colors = ['amber','amber','orange','orange','orange','orange','orange','orange']
|
||||
color = :red if days < 0
|
||||
color = :green if days > 7
|
||||
color = colors[days] if color.nil?
|
||||
|
||||
return content_tag(:a, {:title => format_date(due)}) {
|
||||
content_tag(:span, {:class => color}) {
|
||||
case days
|
||||
when 0
|
||||
t('todos.next_actions_due_date.due_today')
|
||||
when 1
|
||||
t('todos.next_actions_due_date.due_tomorrow')
|
||||
when 2..7
|
||||
if prefs.due_style == Preference.due_styles[:due_on]
|
||||
# TODO: internationalize strftime here
|
||||
t('models.preference.due_on', :date => due.strftime("%A"))
|
||||
else
|
||||
t('models.preference.due_in', :days => days)
|
||||
end
|
||||
else
|
||||
# overdue or due very soon! sound the alarm!
|
||||
if 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
|
||||
# more than a week away - relax
|
||||
t('models.preference.due_in', :days => days)
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# Check due date in comparison to today's date Flag up date appropriately with
|
||||
# a 'traffic light' colour code Modified method for mobile screen
|
||||
#
|
||||
def due_date_mobile(due)
|
||||
if due == nil
|
||||
return ""
|
||||
end
|
||||
|
||||
days = days_from_today(due)
|
||||
|
||||
case days
|
||||
when 0
|
||||
"<span class=\"amber\">"+ format_date(due) + "</span>"
|
||||
when 1
|
||||
"<span class=\"amber\">" + format_date(due) + "</span>"
|
||||
# due 2-7 days away
|
||||
when 2..7
|
||||
"<span class=\"orange\">" + format_date(due) + "</span>"
|
||||
else
|
||||
# overdue or due very soon! sound the alarm!
|
||||
if days < 0
|
||||
"<span class=\"red\">" + format_date(due) +"</span>"
|
||||
else
|
||||
# more than a week away - relax
|
||||
"<span class=\"green\">" + format_date(due) + "</span>"
|
||||
end
|
||||
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")
|
||||
@controller.count_undone_todos_phrase(todos_parent, string)
|
||||
end
|
||||
|
||||
def count_undone_todos_phrase_text(todos_parent, string="actions")
|
||||
count_undone_todos_phrase(todos_parent, string).gsub(" "," ")
|
||||
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
|
||||
end
|
||||
|
||||
def link_to_context(context, descriptor = sanitize(context.name))
|
||||
link_to( descriptor, context, :title => "View context: #{context.name}" )
|
||||
end
|
||||
|
||||
def link_to_project(project, descriptor = sanitize(project.name))
|
||||
link_to( descriptor, project, :title => "View project: #{project.name}" )
|
||||
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}),
|
||||
{:id => "link_edit_#{dom_id(note)}", :class => "note_edit_settings"})
|
||||
end
|
||||
|
||||
def link_to_project_mobile(project, accesskey, descriptor = sanitize(project.name))
|
||||
link_to( descriptor, project_path(project, :format => 'm'), {:title => "View project: #{project.name}", :accesskey => accesskey} )
|
||||
end
|
||||
|
||||
def item_link_to_context(item)
|
||||
descriptor = "[C]"
|
||||
descriptor = "[#{item.context.name}]" if prefs.verbose_action_descriptors
|
||||
link_to_context( item.context, descriptor )
|
||||
end
|
||||
|
||||
def item_link_to_project(item)
|
||||
descriptor = "[P]"
|
||||
descriptor = "[#{item.project.name}]" if prefs.verbose_action_descriptors
|
||||
link_to_project( item.project, descriptor )
|
||||
end
|
||||
|
||||
def render_flash
|
||||
render :partial => 'shared/flash', :object => flash
|
||||
end
|
||||
|
||||
def recurrence_time_span(rt)
|
||||
case rt.ends_on
|
||||
when "no_end_date"
|
||||
return rt.start_from.nil? ? "" : I18n.t("todos.recurrence.pattern.from") + " " + format_date(rt.start_from)
|
||||
when "ends_on_number_of_times"
|
||||
return I18n.t("todos.recurrence.pattern.times", :number => rt.number_of_occurences)
|
||||
when "ends_on_end_date"
|
||||
starts = rt.start_from.nil? ? "" : I18n.t("todos.recurrence.pattern.from") + " " + format_date(rt.start_from)
|
||||
ends = rt.end_date.nil? ? "" : " " + I18n.t("todos.recurrence.pattern.until") + " " + format_date(rt.end_date)
|
||||
return starts+ends
|
||||
else
|
||||
raise Exception.new, "unknown recurrence time span selection (#{rt.ends_on})"
|
||||
end
|
||||
end
|
||||
|
||||
def recurrence_pattern_as_text(recurring_todo)
|
||||
rt = recurring_todo.recurring_target_as_text
|
||||
rp = recurring_todo.recurrence_pattern
|
||||
# only add space if recurrence_pattern has content
|
||||
rp = " " + rp if !rp.nil?
|
||||
rts = recurrence_time_span(recurring_todo)
|
||||
# only add space if recurrence_time_span has content
|
||||
rts = " " + rts if !(rts == "")
|
||||
return rt+rp+rts
|
||||
end
|
||||
|
||||
def date_format_for_date_picker()
|
||||
standard_format = current_user.prefs.date_format
|
||||
translations = [
|
||||
['%m', 'mm'],
|
||||
['%b', 'M'],
|
||||
['%B', 'MM'],
|
||||
['%d', 'dd'],
|
||||
['%a', 'D'],
|
||||
['%A', 'DD'],
|
||||
['%y', 'y'],
|
||||
['%Y', 'yy']
|
||||
]
|
||||
translations.inject(standard_format) do |str, translation|
|
||||
str.gsub(*translation)
|
||||
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))
|
||||
end
|
||||
|
||||
def sidebar_html_for_list(list)
|
||||
if list.empty?
|
||||
return content_tag(:li, t('sidebar.list_empty'))
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
def generate_i18n_strings
|
||||
js = "i18n_locale='#{I18n.locale}';\n"
|
||||
js << "i18n = new Array();\n"
|
||||
%w{
|
||||
shared.toggle_multi shared.toggle_multi_title
|
||||
shared.hide_form shared.hide_action_form_title
|
||||
shared.toggle_single shared.toggle_single_title
|
||||
projects.hide_form projects.hide_form_title
|
||||
projects.show_form projects.show_form_title
|
||||
contexts.hide_form contexts.hide_form_title
|
||||
contexts.show_form contexts.show_form_title
|
||||
contexts.new_context_pre contexts.new_context_post
|
||||
common.cancel common.ok
|
||||
common.ajaxError todos.unresolved_dependency
|
||||
}.each do |s|
|
||||
js << "i18n['#{s}'] = '#{ t(s).gsub(/'/, "\\\\'") }';\n"
|
||||
end
|
||||
return js
|
||||
end
|
||||
|
||||
def javascript_tag_for_i18n_datepicker
|
||||
locale = I18n.locale
|
||||
# do not include en as locale since this the available by default
|
||||
if locale and locale != :en
|
||||
javascript_include_tag("i18n/jquery.ui.datepicker-#{locale}.js")
|
||||
end
|
||||
end
|
||||
|
||||
def determine_done_path
|
||||
case @controller.controller_name
|
||||
when "contexts"
|
||||
done_todos_context_path(@context)
|
||||
when "projects"
|
||||
done_todos_project_path(@project)
|
||||
when "todos"
|
||||
if source_view_is(:tag)
|
||||
done_tag_path(@tag_name)
|
||||
else
|
||||
done_todos_path
|
||||
end
|
||||
else
|
||||
done_todos_path
|
||||
end
|
||||
end
|
||||
|
||||
def determine_all_done_path
|
||||
case @controller.controller_name
|
||||
when "contexts"
|
||||
all_done_todos_context_path(@context)
|
||||
when "projects"
|
||||
all_done_todos_project_path(@project)
|
||||
when "todos"
|
||||
if source_view_is(:tag)
|
||||
all_done_tag_path(@tag_name)
|
||||
else
|
||||
all_done_todos_path
|
||||
end
|
||||
else
|
||||
all_done_todos_path
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
class CannotAccessContext < RuntimeError; end
|
||||
|
||||
class BackendController < ApplicationController
|
||||
acts_as_web_service
|
||||
wsdl_service_name 'Backend'
|
||||
web_service_api TodoApi
|
||||
web_service_scaffold :invoke
|
||||
skip_before_filter :login_required
|
||||
|
||||
|
||||
def new_todo(username, token, context_id, description, notes)
|
||||
check_token(username, token)
|
||||
check_context_belongs_to_user(context_id)
|
||||
item = create_todo(description, context_id, nil, notes)
|
||||
item.id
|
||||
end
|
||||
|
||||
def new_todo_for_project(username, token, context_id, project_id, description, notes)
|
||||
check_token(username, token)
|
||||
check_context_belongs_to_user(context_id)
|
||||
item = create_todo(description, context_id, project_id, notes)
|
||||
item.id
|
||||
end
|
||||
|
||||
def new_rich_todo(username, token, default_context_id, description, notes)
|
||||
check_token(username,token)
|
||||
item = Todo.from_rich_message(@user, default_context_id, description, notes)
|
||||
item.save
|
||||
raise item.errors.full_messages.to_s if item.new_record?
|
||||
item.id
|
||||
end
|
||||
|
||||
def list_contexts(username, token)
|
||||
check_token(username, token)
|
||||
|
||||
@user.contexts
|
||||
end
|
||||
|
||||
def list_projects(username, token)
|
||||
check_token(username, token)
|
||||
|
||||
@user.projects
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Check whether the token in the URL matches the token in the User's table
|
||||
def check_token(username, token)
|
||||
@user = User.find_by_login( username )
|
||||
unless (token == @user.token)
|
||||
raise(InvalidToken, "Sorry, you don't have permission to perform this action.")
|
||||
end
|
||||
end
|
||||
|
||||
def check_context_belongs_to_user(context_id)
|
||||
unless @user.contexts.exists? context_id
|
||||
raise(CannotAccessContext, "Cannot access a context that does not belong to this user.")
|
||||
end
|
||||
end
|
||||
|
||||
def create_todo(description, context_id, project_id = nil, notes="")
|
||||
item = @user.todos.build
|
||||
item.description = description
|
||||
item.notes = notes
|
||||
item.context_id = context_id
|
||||
item.project_id = project_id unless project_id.nil?
|
||||
item.save
|
||||
raise item.errors.full_messages.to_s if item.new_record?
|
||||
item
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidToken < RuntimeError; end
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# MySQL. Versions 4.1 and 5.0 are recommended.
|
||||
#
|
||||
#
|
||||
# Be sure to use new-style password hashing:
|
||||
# http://dev.mysql.com/doc/refman/5.0/en/old-client.html
|
||||
development:
|
||||
adapter: mysql
|
||||
database: tracks_trunk
|
||||
encoding: utf8
|
||||
host: localhost
|
||||
username: tracks
|
||||
password: 32tracks55
|
||||
|
||||
mdevelopment:
|
||||
adapter: sqlite3
|
||||
database: db/tracks-21-test.sqlite3.db
|
||||
|
||||
test: &TEST
|
||||
# adapter: sqlite3
|
||||
# database: ":memory:"
|
||||
# verbosity: quiet
|
||||
adapter: mysql
|
||||
database: tracks_test
|
||||
host: localhost
|
||||
username: tracks
|
||||
password: 32tracks55
|
||||
|
||||
production:
|
||||
adapter: mysql
|
||||
database: tracks_trunk
|
||||
encoding: utf8
|
||||
host: localhost
|
||||
username: tracks
|
||||
password: 32tracks55
|
||||
|
||||
cucumber:
|
||||
<<: *TEST
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
# In the development environment your application's code is reloaded on
|
||||
# every request. This slows down response time but is perfect for development
|
||||
# since you don't have to restart the webserver when you make code changes.
|
||||
config.cache_classes = false
|
||||
|
||||
# Log error messages when you accidentally call methods on nil.
|
||||
config.whiny_nils = true
|
||||
|
||||
# Show full error reports and disable caching
|
||||
config.action_controller.consider_all_requests_local = true
|
||||
config.action_controller.perform_caching = false
|
||||
|
||||
# Don't care if the mailer can't send
|
||||
config.action_mailer.raise_delivery_errors = false
|
||||
|
||||
# Unique cookies
|
||||
config.action_controller.session = { :key => 'TracksDev' }
|
||||
|
||||
NOTIFY_BAR="<div id=\"develop-notify-bar\"> </div>"
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
||||
# It is recommended to regenerate this file in the future when you upgrade to a
|
||||
# newer version of cucumber-rails. Consider adding your own code to a new file
|
||||
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
||||
# files.
|
||||
|
||||
ENV["RAILS_ENV"] ||= "cucumber"
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
|
||||
|
||||
require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support
|
||||
# require 'cucumber/rails/rspec'
|
||||
require 'cucumber/rails/world'
|
||||
require 'cucumber/rails/active_record'
|
||||
require 'cucumber/web/tableish'
|
||||
require 'aruba/cucumber'
|
||||
|
||||
require 'capybara/rails'
|
||||
require 'capybara/cucumber'
|
||||
require 'capybara/session'
|
||||
# BUG in this version of cucumber/capybara: require 'cucumber/rails/capybara_javascript_emulation' # Lets you click links with onclick javascript handlers without using @culerity or @javascript
|
||||
|
||||
Capybara.default_wait_time = 5
|
||||
Capybara.javascript_driver = ENV["JS_DRIVER"] ? ENV["JS_DRIVER"].to_sym : :selenium
|
||||
|
||||
if Capybara.javascript_driver == :webkit
|
||||
require 'capybara/webkit'
|
||||
end
|
||||
|
||||
# Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In
|
||||
# order to ease the transition to Capybara we set the default here. If you'd
|
||||
# prefer to use XPath just remove this line and adjust any selectors in your
|
||||
# steps to use the XPath syntax.
|
||||
Capybara.default_selector = :css
|
||||
|
||||
Capybara.prefer_visible_elements = true
|
||||
|
||||
# If you set this to false, any error raised from within your app will bubble
|
||||
# up to your step definition and out to cucumber unless you catch it somewhere
|
||||
# on the way. You can make Rails rescue errors and render error pages on a
|
||||
# per-scenario basis by tagging a scenario or feature with the @allow-rescue tag.
|
||||
#
|
||||
# If you set this to true, Rails will rescue all errors and render error
|
||||
# pages, more or less in the same way your application would behave in the
|
||||
# default production environment. It's not recommended to do this for all
|
||||
# of your scenarios, as this makes it hard to discover errors in your application.
|
||||
ActionController::Base.allow_rescue = false
|
||||
|
||||
# If you set this to true, each scenario will run in a database transaction.
|
||||
# You can still turn off transactions on a per-scenario basis, simply tagging
|
||||
# a feature or scenario with the @no-txn tag. If you are using Capybara,
|
||||
# tagging with @culerity or @javascript will also turn transactions off.
|
||||
#
|
||||
# If you set this to false, transactions will be off for all scenarios,
|
||||
# regardless of whether you use @no-txn or not.
|
||||
#
|
||||
# Beware that turning transactions off will leave data in your database
|
||||
# after each scenario, which can lead to hard-to-debug failures in
|
||||
# subsequent scenarios. If you do this, we recommend you create a Before
|
||||
# block that will explicitly put your database in a known state.
|
||||
Cucumber::Rails::World.use_transactional_fixtures = true
|
||||
|
||||
# How to clean your database when transactions are turned off. See
|
||||
# http://github.com/bmabey/database_cleaner for more info.
|
||||
if defined?(ActiveRecord::Base)
|
||||
begin
|
||||
require 'database_cleaner'
|
||||
DatabaseCleaner.strategy = :truncation
|
||||
rescue LoadError => ignore_if_database_cleaner_not_present
|
||||
end
|
||||
end
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
# Be sure to restart your webserver when you modify this file.
|
||||
# Uncomment below to force Rails into production mode
|
||||
|
||||
# (Use only when you can't set environment variables through your web/app server)
|
||||
# ENV['RAILS_ENV'] = 'production'
|
||||
|
||||
# Bootstrap the Rails environment, frameworks, and default configuration
|
||||
require File.join(File.dirname(__FILE__), 'boot')
|
||||
|
||||
require 'yaml'
|
||||
SITE_CONFIG = YAML.load_file(File.join(File.dirname(__FILE__), 'site.yml'))
|
||||
|
||||
class Rails::Configuration
|
||||
attr_accessor :action_web_service
|
||||
end
|
||||
|
||||
Encoding.default_external = Encoding::UTF_8 if RUBY_VERSION > "1.9"
|
||||
|
||||
Rails::Initializer.run do |config|
|
||||
# Skip frameworks you're not going to use
|
||||
# config.frameworks -= [ :action_web_service, :action_mailer ]
|
||||
config.autoload_paths += %W( #{RAILS_ROOT}/app/apis )
|
||||
|
||||
config.action_controller.use_accept_header = true
|
||||
|
||||
# Use the database for sessions instead of the file system
|
||||
# (create the session table with 'rake create_sessions_table')
|
||||
config.action_controller.session_store = :active_record_store
|
||||
|
||||
config.action_controller.session = {
|
||||
:key => '_tracks_session_id',
|
||||
:secret => SITE_CONFIG['salt'] * (30.0 / SITE_CONFIG['salt'].length).ceil #must be at least 30 characters
|
||||
}
|
||||
|
||||
config.action_controller.relative_url_root = SITE_CONFIG['subdir'] if SITE_CONFIG['subdir']
|
||||
|
||||
# Enable page/fragment caching by setting a file-based store
|
||||
# (remember to create the caching directory and make it readable to the application)
|
||||
# config.action_controller.fragment_cache_store = :file_store, "#{RAILS_ROOT}/cache"
|
||||
|
||||
# Activate observers that should always be running
|
||||
# config.active_record.observers = :cacher, :garbage_collector
|
||||
|
||||
# Make Active Record use UTC-base instead of local time
|
||||
config.active_record.default_timezone = :utc
|
||||
|
||||
# You''ll probably want to change this to the time zone of the computer where Tracks is running
|
||||
# run rake time:zones:local have Rails suggest time zone names on your system
|
||||
config.time_zone = SITE_CONFIG['time_zone']
|
||||
|
||||
# Use Active Record's schema dumper instead of SQL when creating the test database
|
||||
# (enables use of different database adapters for development and test environments)
|
||||
config.active_record.schema_format = :ruby
|
||||
|
||||
# allow other protocols in urls for sanitzer. Add to your liking, for example
|
||||
# config.action_view.sanitized_allowed_protocols = 'onenote', 'blah', 'proto'
|
||||
# to enable "link":onenote://... or "link":blah://... hyperlinks
|
||||
config.action_view.sanitized_allowed_protocols = 'onenote', 'message'
|
||||
|
||||
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
||||
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
||||
# config.i18n.default_locale = :de
|
||||
|
||||
end
|
||||
|
||||
# Add new inflection rules using the following format
|
||||
# (all these examples are active by default):
|
||||
# Inflector.inflections do |inflect|
|
||||
# inflect.plural /^(ox)$/i, '\1en'
|
||||
# inflect.singular /^(ox)en/i, '\1'
|
||||
# inflect.irregular 'person', 'people'
|
||||
# inflect.uncountable %w( fish sheep )
|
||||
# end
|
||||
|
||||
# Include your application configuration below
|
||||
|
||||
|
||||
require 'name_part_finder'
|
||||
require 'tracks/todo_list'
|
||||
require 'tracks/config'
|
||||
require 'tagging_extensions' # Needed for tagging-specific extensions
|
||||
require 'digest/sha1' #Needed to support 'rake db:fixtures:load' on some ruby installs: http://dev.rousette.org.uk/ticket/557
|
||||
|
||||
if ( SITE_CONFIG['authentication_schemes'].include? 'ldap')
|
||||
require 'net/ldap' #requires ruby-net-ldap gem be installed
|
||||
require 'simple_ldap_authenticator'
|
||||
ldap = SITE_CONFIG['ldap']
|
||||
SimpleLdapAuthenticator.ldap_library = ldap['library']
|
||||
SimpleLdapAuthenticator.servers = ldap['servers']
|
||||
SimpleLdapAuthenticator.use_ssl = ldap['ssl']
|
||||
SimpleLdapAuthenticator.login_format = ldap['login_format']
|
||||
end
|
||||
|
||||
if ( SITE_CONFIG['authentication_schemes'].include? 'open_id')
|
||||
#requires ruby-openid gem to be installed
|
||||
OpenID::Util.logger = RAILS_DEFAULT_LOGGER
|
||||
end
|
||||
|
||||
if ( SITE_CONFIG['authentication_schemes'].include? 'cas')
|
||||
#requires rubycas-client gem to be installed
|
||||
if defined? CASClient
|
||||
require 'casclient/frameworks/rails/filter'
|
||||
CASClient::Frameworks::Rails::Filter.configure(
|
||||
:cas_base_url => SITE_CONFIG['cas_server'] ,
|
||||
:cas_server_logout => SITE_CONFIG['cas_server_logout']
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# changed in development.rb to show under_construction bar
|
||||
NOTIFY_BAR = "" unless defined?(NOTIFY_BAR)
|
||||
|
||||
tracks_version='2.2devel'
|
||||
# comment out next two lines if you do not want (or can not) the date of the
|
||||
# last git commit in the footer
|
||||
info=`git log --pretty=format:"%ai" -1`
|
||||
tracks_version=tracks_version + ' ('+info+')'
|
||||
|
||||
TRACKS_VERSION=tracks_version
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
(function($){
|
||||
/* hoverIntent by Brian Cherne */
|
||||
$.fn.hoverIntent = function(f,g) {
|
||||
// default configuration options
|
||||
var cfg = {
|
||||
sensitivity: 7,
|
||||
interval: 100,
|
||||
timeout: 400
|
||||
};
|
||||
// override configuration options with user supplied object
|
||||
cfg = $.extend(cfg, g ? { over: f, out: g } : f );
|
||||
|
||||
// instantiate variables
|
||||
// cX, cY = current X and Y position of mouse, updated by mousemove event
|
||||
// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
|
||||
var cX, cY, pX, pY;
|
||||
|
||||
// A private function for getting mouse position
|
||||
var track = function(ev) {
|
||||
cX = ev.pageX;
|
||||
cY = ev.pageY;
|
||||
};
|
||||
|
||||
// A private function for comparing current and previous mouse position
|
||||
var compare = function(ev,ob) {
|
||||
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
|
||||
// compare mouse positions to see if they've crossed the threshold
|
||||
if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
|
||||
$(ob).unbind("mousemove",track);
|
||||
// set hoverIntent state to true (so mouseOut can be called)
|
||||
ob.hoverIntent_s = 1;
|
||||
return cfg.over.apply(ob,[ev]);
|
||||
} else {
|
||||
// set previous coordinates for next time
|
||||
pX = cX; pY = cY;
|
||||
// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
|
||||
ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
|
||||
}
|
||||
};
|
||||
|
||||
// A private function for delaying the mouseOut function
|
||||
var delay = function(ev,ob) {
|
||||
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
|
||||
ob.hoverIntent_s = 0;
|
||||
return cfg.out.apply(ob,[ev]);
|
||||
};
|
||||
|
||||
// A private function for handling mouse 'hovering'
|
||||
var handleHover = function(e) {
|
||||
// next three lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
|
||||
var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
|
||||
while ( p && p != this ) { try { p = p.parentNode; } catch(e) { p = this; } }
|
||||
if ( p == this ) { return false; }
|
||||
|
||||
// copy objects to be passed into t (required for event object to be passed in IE)
|
||||
var ev = jQuery.extend({},e);
|
||||
var ob = this;
|
||||
|
||||
// cancel hoverIntent timer if it exists
|
||||
if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
|
||||
|
||||
// else e.type == "onmouseover"
|
||||
if (e.type == "mouseover") {
|
||||
// set "previous" X and Y position based on initial entry point
|
||||
pX = ev.pageX; pY = ev.pageY;
|
||||
// update "current" X and Y position based on mousemove
|
||||
$(ob).bind("mousemove",track);
|
||||
// start polling interval (self-calling timeout) to compare mouse coordinates over time
|
||||
if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
|
||||
|
||||
// else e.type == "onmouseout"
|
||||
} else {
|
||||
// unbind expensive mousemove event
|
||||
$(ob).unbind("mousemove",track);
|
||||
// if hoverIntent state is true, then call the mouseOut function after the specified delay
|
||||
if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
|
||||
}
|
||||
};
|
||||
|
||||
// bind the function to the two event listeners
|
||||
return this.mouseover(handleHover).mouseout(handleHover);
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
module AuthenticatedTestHelper
|
||||
# Sets the current user in the session from the user fixtures.
|
||||
def login_as(user)
|
||||
@request.session['user_id'] = user ? users(user).id : nil
|
||||
end
|
||||
|
||||
def content_type(type)
|
||||
@request.env['Content-Type'] = type
|
||||
end
|
||||
|
||||
def accept(accept)
|
||||
@request.env["HTTP_ACCEPT"] = accept
|
||||
end
|
||||
|
||||
def authorize_as(user)
|
||||
if user
|
||||
@request.env["HTTP_AUTHORIZATION"] = "Basic #{Base64.encode64("#{users(user).login}:test")}"
|
||||
accept 'application/xml'
|
||||
content_type 'application/xml'
|
||||
else
|
||||
@request.env["HTTP_AUTHORIZATION"] = nil
|
||||
accept nil
|
||||
content_type nil
|
||||
end
|
||||
end
|
||||
|
||||
# http://project.ioni.st/post/217#post-217
|
||||
#
|
||||
# def test_new_publication
|
||||
# assert_difference(Publication, :count) do
|
||||
# post :create, :publication => {...}
|
||||
# # ...
|
||||
# end
|
||||
# end
|
||||
#
|
||||
def assert_difference(object, method = nil, difference = 1)
|
||||
initial_value = object.send(method)
|
||||
yield
|
||||
assert_equal initial_value + difference, object.send(method), "#{object}##{method}"
|
||||
end
|
||||
|
||||
def assert_no_difference(object, method, &block)
|
||||
assert_difference object, method, 0, &block
|
||||
end
|
||||
|
||||
# Assert the block redirects to the login
|
||||
#
|
||||
# assert_requires_login(:bob) { |c| c.get :edit, :id => 1 }
|
||||
#
|
||||
def assert_requires_login(login = nil)
|
||||
yield HttpLoginProxy.new(self, login)
|
||||
end
|
||||
|
||||
def assert_http_authentication_required(login = nil)
|
||||
yield XmlLoginProxy.new(self, login)
|
||||
end
|
||||
|
||||
def reset!(*instance_vars)
|
||||
instance_vars = [:controller, :request, :response] unless instance_vars.any?
|
||||
instance_vars.collect! { |v| "@#{v}".to_sym }
|
||||
instance_vars.each do |var|
|
||||
instance_variable_set(var, instance_variable_get(var).class.new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class BaseLoginProxy
|
||||
attr_reader :controller
|
||||
attr_reader :options
|
||||
def initialize(controller, login)
|
||||
@controller = controller
|
||||
@login = login
|
||||
end
|
||||
|
||||
private
|
||||
def authenticated
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def check
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def method_missing(method, *args)
|
||||
@controller.reset!
|
||||
authenticate
|
||||
@controller.send(method, *args)
|
||||
check
|
||||
end
|
||||
end
|
||||
|
||||
class HttpLoginProxy < BaseLoginProxy
|
||||
protected
|
||||
def authenticate
|
||||
@controller.login_as @login if @login
|
||||
end
|
||||
|
||||
def check
|
||||
@controller.assert_redirected_to :controller => 'account', :action => 'login'
|
||||
end
|
||||
end
|
||||
|
||||
class XmlLoginProxy < BaseLoginProxy
|
||||
protected
|
||||
def authenticate
|
||||
@controller.accept 'application/xml'
|
||||
@controller.authorize_as @login if @login
|
||||
end
|
||||
|
||||
def check
|
||||
@controller.assert_response 401
|
||||
end
|
||||
end
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
require_dependency "user"
|
||||
|
||||
module LoginSystem
|
||||
|
||||
def current_user
|
||||
get_current_user
|
||||
end
|
||||
|
||||
def prefs
|
||||
current_user.prefs unless current_user.nil?
|
||||
end
|
||||
|
||||
# Logout the {#current_user} and redirect to login page
|
||||
#
|
||||
# @param [String] message notification to display
|
||||
def logout_user message=t('login.logged_out')
|
||||
@user.forget_me if logged_in?
|
||||
cookies.delete :auth_token
|
||||
session['user_id'] = nil
|
||||
if ( SITE_CONFIG['authentication_schemes'].include? 'cas') && session[:cas_user]
|
||||
CASClient::Frameworks::Rails::Filter.logout(self)
|
||||
else
|
||||
reset_session
|
||||
notify :notice, message
|
||||
redirect_to_login
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# overwrite this if you want to restrict access to only a few actions
|
||||
# or if you want to check if the user has the correct rights
|
||||
# example:
|
||||
#
|
||||
# # only allow nonbobs
|
||||
# def authorize?(user)
|
||||
# user.login != "bob"
|
||||
# end
|
||||
def authorize?(user)
|
||||
true
|
||||
end
|
||||
|
||||
# overwrite this method if you only want to protect certain actions of the controller
|
||||
# example:
|
||||
#
|
||||
# # don't protect the login and the about method
|
||||
# def protect?(action)
|
||||
# if ['action', 'about'].include?(action)
|
||||
# return false
|
||||
# else
|
||||
# return true
|
||||
# end
|
||||
# end
|
||||
def protect?(action)
|
||||
true
|
||||
end
|
||||
|
||||
# When called with before_filter :login_from_cookie will check for an :auth_token
|
||||
# cookie and log the user back in if appropriate
|
||||
def login_from_cookie
|
||||
return unless cookies[:auth_token] && !logged_in?
|
||||
token = cookies[:auth_token]
|
||||
user = User.where(:remember_token => token)
|
||||
if user && user.remember_token?
|
||||
session['user_id'] = user.id
|
||||
set_current_user(user)
|
||||
current_user.remember_me
|
||||
cookies[:auth_token] = { :value => current_user.remember_token , :expires => current_user.remember_token_expires_at, :secure => SITE_CONFIG['secure_cookies'] }
|
||||
flash[:notice] = t('login.successful')
|
||||
end
|
||||
end
|
||||
|
||||
def login_or_feed_token_required
|
||||
if ['rss', 'atom', 'txt', 'ics'].include?(params[:format])
|
||||
if user = User.find_by_token(params[:token])
|
||||
set_current_user(user)
|
||||
return true
|
||||
end
|
||||
end
|
||||
login_required
|
||||
end
|
||||
|
||||
# login_required filter. add
|
||||
#
|
||||
# before_filter :login_required
|
||||
#
|
||||
# if the controller should be under any rights management.
|
||||
# for finer access control you can overwrite
|
||||
#
|
||||
# def authorize?(user)
|
||||
#
|
||||
def login_required
|
||||
|
||||
if not protect?(action_name)
|
||||
return true
|
||||
end
|
||||
|
||||
login_from_cookie
|
||||
|
||||
if session['user_id'] and authorize?(get_current_user)
|
||||
return true
|
||||
end
|
||||
|
||||
http_user, http_pass = get_basic_auth_data
|
||||
if user = User.authenticate(http_user, http_pass)
|
||||
session['user_id'] = user.id
|
||||
set_current_user(user)
|
||||
return true
|
||||
end
|
||||
|
||||
# store current location so that we can
|
||||
# come back after the user logged in
|
||||
store_location unless params[:format] == 'js'
|
||||
|
||||
# call overwriteable reaction to unauthorized access
|
||||
access_denied
|
||||
return false
|
||||
end
|
||||
|
||||
def login_optional
|
||||
|
||||
login_from_cookie
|
||||
|
||||
if session['user_id'] and authorize?(get_current_user)
|
||||
return true
|
||||
end
|
||||
|
||||
http_user, http_pass = get_basic_auth_data
|
||||
if user = User.authenticate(http_user, http_pass)
|
||||
session['user_id'] = user.id
|
||||
set_current_user(user)
|
||||
return true
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def logged_in?
|
||||
current_user != nil
|
||||
end
|
||||
|
||||
def get_current_user
|
||||
if @user.nil? && session['user_id']
|
||||
@user = User.find(session['user_id'])
|
||||
end
|
||||
@user
|
||||
end
|
||||
|
||||
def set_current_user(user)
|
||||
@user = user
|
||||
end
|
||||
|
||||
# overwrite if you want to have special behavior in case the user is not authorized
|
||||
# to access the current operation.
|
||||
# the default action is to redirect to the login screen
|
||||
# example use :
|
||||
# a popup window might just close itself for instance
|
||||
def access_denied
|
||||
respond_to do |format|
|
||||
format.html { redirect_to login_path }
|
||||
format.m { redirect_to formatted_login_path(:format => 'm') }
|
||||
format.js { render :partial => 'login/redirect_to_login' }
|
||||
format.xml { basic_auth_denied }
|
||||
format.rss { basic_auth_denied }
|
||||
format.atom { basic_auth_denied }
|
||||
format.text { basic_auth_denied }
|
||||
end
|
||||
end
|
||||
|
||||
# store current uri in the session.
|
||||
# we can return to this location by calling return_location
|
||||
def store_location
|
||||
session['return-to'] = request.request_uri
|
||||
end
|
||||
|
||||
# move to the last store_location call or to the passed default one
|
||||
def redirect_back_or_default(default)
|
||||
if session['return-to'].nil?
|
||||
redirect_to default
|
||||
else
|
||||
redirect_to session['return-to']
|
||||
session['return-to'] = nil
|
||||
end
|
||||
end
|
||||
|
||||
# HTTP Basic auth code adapted from Coda Hale's simple_http_auth plugin. Thanks, Coda!
|
||||
def get_basic_auth_data
|
||||
|
||||
auth_locations = ['REDIRECT_REDIRECT_X_HTTP_AUTHORIZATION',
|
||||
'REDIRECT_X_HTTP_AUTHORIZATION',
|
||||
'X-HTTP_AUTHORIZATION', 'HTTP_AUTHORIZATION']
|
||||
|
||||
authdata = nil
|
||||
for location in auth_locations
|
||||
if request.env.has_key?(location)
|
||||
authdata = request.env[location].to_s.split
|
||||
end
|
||||
end
|
||||
if authdata and authdata[0] == 'Basic'
|
||||
user, pass = Base64.decode64(authdata[1]).split(':')[0..1]
|
||||
else
|
||||
user, pass = ['', '']
|
||||
end
|
||||
return user, pass
|
||||
end
|
||||
|
||||
def basic_auth_denied
|
||||
response.headers["WWW-Authenticate"] = "Basic realm=\"'Tracks Login Required'\""
|
||||
render :text => t('login.unsuccessful'), :status => 401
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Redirect the user to the login page.
|
||||
def redirect_to_login
|
||||
respond_to do |format|
|
||||
format.html { redirect_to login_path }
|
||||
format.m { redirect_to login_path(:format => 'm') }
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module NamePartFinder
|
||||
def find_by_namepart(namepart)
|
||||
find_by_name(namepart) || find(:first, :conditions => ["name LIKE ?", namepart + '%'])
|
||||
end
|
||||
end
|
||||
|
|
@ -1,200 +0,0 @@
|
|||
class ActiveRecord::Base #:nodoc:
|
||||
|
||||
# These extensions make models taggable. This file is automatically generated and required by your app if you run the tagging generator included with has_many_polymorphs.
|
||||
module TaggingExtensions
|
||||
|
||||
# Add tags to <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
#
|
||||
# We need to avoid name conflicts with the built-in ActiveRecord association methods, thus the underscores.
|
||||
def _add_tags incoming
|
||||
taggable?(true)
|
||||
tag_cast_to_string(incoming).each do |tag_name|
|
||||
# added following check to prevent empty tags from being saved (which will fail)
|
||||
unless tag_name.blank?
|
||||
begin
|
||||
tag = Tag.find_or_create_by_name(tag_name)
|
||||
raise Tag::Error, "tag could not be saved: #{tag_name}" if tag.new_record?
|
||||
tags << tag
|
||||
rescue ActiveRecord::StatementInvalid => e
|
||||
raise unless e.to_s =~ /duplicate/i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Removes tags from <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
def _remove_tags outgoing
|
||||
taggable?(true)
|
||||
outgoing = tag_cast_to_string(outgoing)
|
||||
tags.delete(*(tags.select do |tag|
|
||||
outgoing.include? tag.name
|
||||
end))
|
||||
end
|
||||
|
||||
# Returns the tags on <tt>self</tt> as a string.
|
||||
def tag_list
|
||||
# Redefined later to avoid an RDoc parse error.
|
||||
end
|
||||
|
||||
# Replace the existing tags on <tt>self</tt>. Accepts a string of tagnames, an array of tagnames, an array of ids, or an array of Tags.
|
||||
def tag_with list
|
||||
#:stopdoc:
|
||||
taggable?(true)
|
||||
list = tag_cast_to_string(list)
|
||||
|
||||
# Transactions may not be ideal for you here; be aware.
|
||||
Tag.transaction do
|
||||
current = tags.map(&:name)
|
||||
_add_tags(list - current)
|
||||
_remove_tags(current - list)
|
||||
end
|
||||
|
||||
self
|
||||
#:startdoc:
|
||||
end
|
||||
|
||||
# Returns the tags on <tt>self</tt> as a string.
|
||||
def tag_list #:nodoc:
|
||||
#:stopdoc:
|
||||
taggable?(true)
|
||||
tags.reload
|
||||
tags.to_s
|
||||
#:startdoc:
|
||||
end
|
||||
|
||||
def tag_list=(value)
|
||||
tag_with(value)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tag_cast_to_string obj #:nodoc:
|
||||
case obj
|
||||
when Array
|
||||
obj.map! do |item|
|
||||
case item
|
||||
# removed next line as it prevents using numbers as tags
|
||||
# when /^\d+$/, Fixnum then Tag.find(item).name # This will be slow if you use ids a lot.
|
||||
when Tag then item.name
|
||||
when String then item
|
||||
else
|
||||
raise "Invalid type"
|
||||
end
|
||||
end
|
||||
when String
|
||||
obj = obj.split(Tag::DELIMITER).map do |tag_name|
|
||||
tag_name.strip.squeeze(" ")
|
||||
end
|
||||
else
|
||||
raise "Invalid object of class #{obj.class} as tagging method parameter"
|
||||
end.flatten.compact.map(&:downcase).uniq
|
||||
end
|
||||
|
||||
# Check if a model is in the :taggables target list. The alternative to this check is to explicitly include a TaggingMethods module (which you would create) in each target model.
|
||||
def taggable?(should_raise = false) #:nodoc:
|
||||
unless flag = respond_to?(:tags)
|
||||
raise "#{self.class} is not a taggable model" if should_raise
|
||||
end
|
||||
flag
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module TaggingFinders
|
||||
# Find all the objects tagged with the supplied list of tags
|
||||
#
|
||||
# Usage : Model.tagged_with("ruby")
|
||||
# Model.tagged_with("hello", "world")
|
||||
# Model.tagged_with("hello", "world", :limit => 10)
|
||||
#
|
||||
# XXX This query strategy is not performant, and needs to be rewritten as an inverted join or a series of unions
|
||||
#
|
||||
def tagged_with(*tag_list)
|
||||
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
||||
tag_list = parse_tags(tag_list)
|
||||
|
||||
scope = scope(:find)
|
||||
options[:select] ||= "#{table_name}.*"
|
||||
options[:from] ||= "#{table_name}, tags, taggings"
|
||||
|
||||
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
||||
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
||||
|
||||
add_joins!(sql, options[:joins], scope)
|
||||
|
||||
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
||||
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
||||
sql << "AND taggings.tag_id = tags.id "
|
||||
|
||||
tag_list_condition = tag_list.map {|name| "'#{name}'"}.join(", ")
|
||||
|
||||
sql << "AND (tags.name IN (#{sanitize_sql(tag_list_condition)})) "
|
||||
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
||||
|
||||
columns = column_names.map do |column|
|
||||
"#{table_name}.#{column}"
|
||||
end.join(", ")
|
||||
|
||||
sql << "GROUP BY #{columns} "
|
||||
sql << "HAVING COUNT(taggings.tag_id) = #{tag_list.size}"
|
||||
|
||||
add_order!(sql, options[:order], scope)
|
||||
add_limit!(sql, options, scope)
|
||||
add_lock!(sql, options, scope)
|
||||
|
||||
find_by_sql(sql)
|
||||
end
|
||||
|
||||
def self.tagged_with_any(*tag_list)
|
||||
options = tag_list.last.is_a?(Hash) ? tag_list.pop : {}
|
||||
tag_list = parse_tags(tag_list)
|
||||
|
||||
scope = scope(:find)
|
||||
options[:select] ||= "#{table_name}.*"
|
||||
options[:from] ||= "#{table_name}, meta_tags, taggings"
|
||||
|
||||
sql = "SELECT #{(scope && scope[:select]) || options[:select]} "
|
||||
sql << "FROM #{(scope && scope[:from]) || options[:from]} "
|
||||
|
||||
add_joins!(sql, options, scope)
|
||||
|
||||
sql << "WHERE #{table_name}.#{primary_key} = taggings.taggable_id "
|
||||
sql << "AND taggings.taggable_type = '#{ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s}' "
|
||||
sql << "AND taggings.meta_tag_id = meta_tags.id "
|
||||
|
||||
sql << "AND ("
|
||||
or_options = []
|
||||
tag_list.each do |name|
|
||||
or_options << "meta_tags.name = '#{name}'"
|
||||
end
|
||||
or_options_joined = or_options.join(" OR ")
|
||||
sql << "#{or_options_joined}) "
|
||||
|
||||
|
||||
sql << "AND #{sanitize_sql(options[:conditions])} " if options[:conditions]
|
||||
|
||||
columns = column_names.map do |column|
|
||||
"#{table_name}.#{column}"
|
||||
end.join(", ")
|
||||
|
||||
sql << "GROUP BY #{columns} "
|
||||
|
||||
add_order!(sql, options[:order], scope)
|
||||
add_limit!(sql, options, scope)
|
||||
add_lock!(sql, options, scope)
|
||||
|
||||
find_by_sql(sql)
|
||||
end
|
||||
|
||||
def parse_tags(tags)
|
||||
return [] if tags.blank?
|
||||
tags = Array(tags).first
|
||||
tags = tags.respond_to?(:flatten) ? tags.flatten : tags.split(Tag::DELIMITER)
|
||||
tags.map { |tag| tag.strip.squeeze(" ") }.flatten.compact.map(&:downcase).uniq
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
include TaggingExtensions
|
||||
extend TaggingFinders
|
||||
end
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
||||
# It is recommended to regenerate this file in the future when you upgrade to a
|
||||
# newer version of cucumber-rails. Consider adding your own code to a new file
|
||||
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
||||
# files.
|
||||
|
||||
vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
|
||||
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
|
||||
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
|
||||
namespace :cucumber do
|
||||
Cucumber::Rake::Task.new({:selenium => :env_to_selenium}, 'Run features that require selenium') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'selenium'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new({:selenium_wip => :env_to_selenium}, 'Run unfinished features that require selenium') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'selenium_wip'
|
||||
end
|
||||
|
||||
task :env_to_selenium => 'db:test:prepare' do
|
||||
ENV['RAILS_ENV'] = 'selenium'
|
||||
end
|
||||
|
||||
desc 'Run all features'
|
||||
task :all => [:ok, :wip, :selenium, :selenium_wip]
|
||||
end
|
||||
rescue LoadError
|
||||
desc 'cucumber rake task not available (cucumber not installed)'
|
||||
task :cucumber do
|
||||
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
||||
end
|
||||
end
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
|
||||
# It is recommended to regenerate this file in the future when you upgrade to a
|
||||
# newer version of cucumber-rails. Consider adding your own code to a new file
|
||||
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
||||
# files.
|
||||
|
||||
|
||||
unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks
|
||||
|
||||
vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
|
||||
$LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
|
||||
|
||||
begin
|
||||
require 'cucumber/rake/task'
|
||||
|
||||
namespace :cucumber do
|
||||
Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
|
||||
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'default'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'wip'
|
||||
end
|
||||
|
||||
Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t|
|
||||
t.binary = vendored_cucumber_bin
|
||||
t.fork = true # You may get faster startup if you set this to false
|
||||
t.profile = 'rerun'
|
||||
end
|
||||
|
||||
desc 'Run all features'
|
||||
task :all => [:ok, :wip]
|
||||
end
|
||||
desc 'Alias for cucumber:ok'
|
||||
task :cucumber => 'cucumber:ok'
|
||||
|
||||
task :default => :cucumber
|
||||
|
||||
task :features => :cucumber do
|
||||
STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
|
||||
end
|
||||
rescue LoadError
|
||||
desc 'cucumber rake task not available (cucumber not installed)'
|
||||
task :cucumber do
|
||||
abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
require 'rake'
|
||||
|
||||
namespace :db do
|
||||
desc "Dump the current SQLite3 or MySQL database to a sql file"
|
||||
task :dump_sql do
|
||||
load 'config/environment.rb'
|
||||
abcs = ActiveRecord::Base.configurations
|
||||
case abcs[RAILS_ENV]["adapter"]
|
||||
when 'mysql'
|
||||
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
|
||||
File.open("db/#{RAILS_ENV}_data.sql", "w+") do |f|
|
||||
if abcs[RAILS_ENV]["password"].blank?
|
||||
f << `mysqldump -h #{abcs[RAILS_ENV]["host"]} -u #{abcs[RAILS_ENV]["username"]} #{abcs[RAILS_ENV]["database"]}`
|
||||
else
|
||||
f << `mysqldump -h #{abcs[RAILS_ENV]["host"]} -u #{abcs[RAILS_ENV]["username"]} -p#{abcs[RAILS_ENV]["password"]} #{abcs[RAILS_ENV]["database"]}`
|
||||
end
|
||||
end
|
||||
when 'sqlite3'
|
||||
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
|
||||
File.open("db/#{RAILS_ENV}_data.sql", "w+") do |f|
|
||||
f << `sqlite3 #{abcs[RAILS_ENV]["database"]} .dump`
|
||||
end
|
||||
else
|
||||
raise "Task not supported by '#{abcs[RAILS_ENV]['adapter']}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
desc ' Create YAML test fixtures from data in an existing database.
|
||||
Defaults to development database. Set RAILS_ENV to override (taken from Rails Recipes book).'
|
||||
task :extract_fixtures => :environment do
|
||||
sql = "SELECT * FROM %s"
|
||||
skip_tables = ["schema_info", "sessions", "users"]
|
||||
ActiveRecord::Base.establish_connection
|
||||
(ActiveRecord::Base.connection.tables - skip_tables).each do |table_name|
|
||||
i = "000"
|
||||
File.open("#{RAILS_ROOT}/db/exported_fixtures/#{table_name}.yml", 'w' ) do |file|
|
||||
data = ActiveRecord::Base.connection.select_all(sql % table_name)
|
||||
file.write data.inject({}) { |hash, record|
|
||||
hash["#{table_name}_#{i.succ!}"] = record
|
||||
hash
|
||||
}.to_yaml
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
desc "Copy third-party gems into ./lib"
|
||||
task :freeze_other_gems do
|
||||
# TODO Get this list from parsing environment.rb
|
||||
libraries = %w(redcloth)
|
||||
require 'rubygems'
|
||||
require 'find'
|
||||
|
||||
libraries.each do |library|
|
||||
library_gem = Gem.cache.search(library).sort_by { |g| g.version }.last
|
||||
puts "Freezing #{library} for #{library_gem.version}..."
|
||||
|
||||
# TODO Add dependencies to list of libraries to freeze
|
||||
#library_gem.dependencies.each { |g| libraries << g }
|
||||
|
||||
folder_for_library = "#{library_gem.name}-#{library_gem.version}"
|
||||
system "cd vendor; gem unpack -v '#{library_gem.version}' #{library_gem.name};"
|
||||
|
||||
# Copy files recursively to ./lib
|
||||
folder_for_library_with_lib = "vendor/#{folder_for_library}/lib/"
|
||||
Find.find(folder_for_library_with_lib) do |original_file|
|
||||
destination_file = "./lib/" + original_file.gsub(folder_for_library_with_lib, '')
|
||||
|
||||
if File.directory?(original_file)
|
||||
if !File.exist?(destination_file)
|
||||
Dir.mkdir destination_file
|
||||
end
|
||||
else
|
||||
File.copy original_file, destination_file
|
||||
end
|
||||
end
|
||||
|
||||
system "rm -r vendor/#{folder_for_library}"
|
||||
end
|
||||
end
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
desc "Load exported fixtures (in db/exported_fixtures) into the current environment's database"
|
||||
task :load_exported_fixtures => :environment do
|
||||
require 'active_record/fixtures'
|
||||
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
||||
Dir.glob(File.join(RAILS_ROOT, 'db', 'exported_fixtures', '*.{yml,csv}')).each do |fixture_file|
|
||||
Fixtures.create_fixtures('db/exported_fixtures', File.basename(fixture_file, '.*'))
|
||||
end
|
||||
end
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
namespace :query_trace do
|
||||
desc "Enables the query_trace plugin. Must restart server to take effect."
|
||||
task :on => :environment do
|
||||
unless File.exist?("#{RAILS_ROOT}/vendor/query_trace.tar.gz")
|
||||
Dir.chdir("#{RAILS_ROOT}/vendor") do
|
||||
url = "https://terralien.devguard.com/svn/projects/plugins/query_trace"
|
||||
puts "Loading query_trace from #{url}..."
|
||||
system "svn co #{url} query_trace"
|
||||
system "tar zcf query_trace.tar.gz --exclude=.svn query_trace"
|
||||
FileUtils.rm_rf("query_trace")
|
||||
end
|
||||
end
|
||||
Dir.chdir("#{RAILS_ROOT}/vendor/plugins") do
|
||||
system "tar zxf ../query_trace.tar.gz query_trace"
|
||||
end
|
||||
puts "QueryTrace plugin enabled. Must restart server to take effect."
|
||||
end
|
||||
|
||||
desc "Disables the query_trace plugin. Must restart server to take effect."
|
||||
task :off => :environment do
|
||||
FileUtils.rm_rf("#{RAILS_ROOT}/vendor/plugins/query_trace")
|
||||
puts "QueryTrace plugin disabled. Must restart server to take effect."
|
||||
end
|
||||
end
|
||||
|
||||
namespace :query_analyzer do
|
||||
desc "Enables the query_analyzer plugin. Must restart server to take effect."
|
||||
task :on => :environment do
|
||||
unless File.exist?("#{RAILS_ROOT}/vendor/query_analyzer.tar.gz")
|
||||
Dir.chdir("#{RAILS_ROOT}/vendor") do
|
||||
url = "http://svn.nfectio.us/plugins/query_analyzer"
|
||||
puts "Loading query_analyzer from #{url}..."
|
||||
system "svn co #{url} query_analyzer"
|
||||
system "tar zcf query_analyzer.tar.gz --exclude=.svn query_analyzer"
|
||||
FileUtils.rm_rf("query_analyzer")
|
||||
end
|
||||
end
|
||||
Dir.chdir("#{RAILS_ROOT}/vendor/plugins") do
|
||||
system "tar zxf ../query_analyzer.tar.gz query_analyzer"
|
||||
end
|
||||
puts "QueryAnalyzer plugin enabled. Must restart server to take effect."
|
||||
end
|
||||
|
||||
desc "Disables the query_analyzer plugin. Must restart server to take effect."
|
||||
task :off => :environment do
|
||||
FileUtils.rm_rf("#{RAILS_ROOT}/vendor/plugins/query_analyzer")
|
||||
puts "QueryAnalyzer plugin disabled. Must restart server to take effect."
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
namespace :tracks do
|
||||
desc 'Replace the password of USER with a new one.'
|
||||
task :password => :environment do
|
||||
require "highline/import"
|
||||
|
||||
user = User.find_by_login(ENV['USER'])
|
||||
if user.nil?
|
||||
puts "Sorry, we couldn't find user '#{ENV['USER']}'. To specify a different user, pass USER=username to this task."
|
||||
exit 0
|
||||
end
|
||||
|
||||
puts "Changing Tracks password for #{ENV['USER']}."
|
||||
password = ask("New password: ") { |q| q.echo = false }
|
||||
password_confirmation = ask('Retype new password: ') { |q| q.echo = false }
|
||||
|
||||
begin
|
||||
user.change_password(password, password_confirmation)
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
puts "Sorry, we couldn't change #{ENV['USER']}'s password: "
|
||||
user.errors.each_full { |msg| puts "- #{msg}\n" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
gem 'test-unit', '1.2.3' if RUBY_VERSION.to_f >= 1.9
|
||||
rspec_gem_dir = nil
|
||||
Dir["#{RAILS_ROOT}/vendor/gems/*"].each do |subdir|
|
||||
rspec_gem_dir = subdir if subdir.gsub("#{RAILS_ROOT}/vendor/gems/","") =~ /^(\w+-)?rspec-(\d+)/ && File.exist?("#{subdir}/lib/spec/rake/spectask.rb")
|
||||
end
|
||||
rspec_plugin_dir = File.expand_path(File.dirname(__FILE__) + '/../../vendor/plugins/rspec')
|
||||
|
||||
if rspec_gem_dir && (test ?d, rspec_plugin_dir)
|
||||
raise "\n#{'*'*50}\nYou have rspec installed in both vendor/gems and vendor/plugins\nPlease pick one and dispose of the other.\n#{'*'*50}\n\n"
|
||||
end
|
||||
|
||||
if rspec_gem_dir
|
||||
$LOAD_PATH.unshift("#{rspec_gem_dir}/lib")
|
||||
elsif File.exist?(rspec_plugin_dir)
|
||||
$LOAD_PATH.unshift("#{rspec_plugin_dir}/lib")
|
||||
end
|
||||
|
||||
# Don't load rspec if running "rake gems:*"
|
||||
unless ARGV.any? {|a| a =~ /^gems/}
|
||||
|
||||
begin
|
||||
require 'spec/rake/spectask'
|
||||
rescue MissingSourceFile
|
||||
module Spec
|
||||
module Rake
|
||||
class SpecTask
|
||||
def initialize(name)
|
||||
task name do
|
||||
# if rspec-rails is a configured gem, this will output helpful material and exit ...
|
||||
require File.expand_path(File.join(File.dirname(__FILE__),"..","..","config","environment"))
|
||||
|
||||
# ... otherwise, do this:
|
||||
raise <<-MSG
|
||||
|
||||
#{"*" * 80}
|
||||
* You are trying to run an rspec rake task defined in
|
||||
* #{__FILE__},
|
||||
* but rspec can not be found in vendor/gems, vendor/plugins or system gems.
|
||||
#{"*" * 80}
|
||||
MSG
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Rake.application.instance_variable_get('@tasks').delete('default')
|
||||
|
||||
spec_prereq = File.exist?(File.join(RAILS_ROOT, 'config', 'database.yml')) ? "db:test:prepare" : :noop
|
||||
task :noop do
|
||||
end
|
||||
|
||||
task :default => :spec
|
||||
task :stats => "spec:statsetup"
|
||||
|
||||
desc "Run all specs in spec directory (excluding plugin specs)"
|
||||
Spec::Rake::SpecTask.new(:spec => spec_prereq) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList['spec/**/*_spec.rb']
|
||||
end
|
||||
|
||||
namespace :spec do
|
||||
desc "Run all specs in spec directory with RCov (excluding plugin specs)"
|
||||
Spec::Rake::SpecTask.new(:rcov) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList['spec/**/*_spec.rb']
|
||||
t.rcov = true
|
||||
t.rcov_opts = lambda do
|
||||
IO.readlines("#{RAILS_ROOT}/spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
|
||||
end
|
||||
end
|
||||
|
||||
desc "Print Specdoc for all specs (excluding plugin specs)"
|
||||
Spec::Rake::SpecTask.new(:doc) do |t|
|
||||
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
||||
t.spec_files = FileList['spec/**/*_spec.rb']
|
||||
end
|
||||
|
||||
desc "Print Specdoc for all plugin examples"
|
||||
Spec::Rake::SpecTask.new(:plugin_doc) do |t|
|
||||
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
||||
t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*')
|
||||
end
|
||||
|
||||
[:models, :controllers, :views, :helpers, :lib, :integration].each do |sub|
|
||||
desc "Run the code examples in spec/#{sub}"
|
||||
Spec::Rake::SpecTask.new(sub => spec_prereq) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
||||
end
|
||||
end
|
||||
|
||||
desc "Run the code examples in vendor/plugins (except RSpec's own)"
|
||||
Spec::Rake::SpecTask.new(:plugins => spec_prereq) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList['vendor/plugins/**/spec/**/*_spec.rb'].exclude('vendor/plugins/rspec/*').exclude("vendor/plugins/rspec-rails/*")
|
||||
end
|
||||
|
||||
namespace :plugins do
|
||||
desc "Runs the examples for rspec_on_rails"
|
||||
Spec::Rake::SpecTask.new(:rspec_on_rails) do |t|
|
||||
t.spec_opts = ['--options', "\"#{RAILS_ROOT}/spec/spec.opts\""]
|
||||
t.spec_files = FileList['vendor/plugins/rspec-rails/spec/**/*_spec.rb']
|
||||
end
|
||||
end
|
||||
|
||||
# Setup specs for stats
|
||||
task :statsetup do
|
||||
require 'code_statistics'
|
||||
::STATS_DIRECTORIES << %w(Model\ specs spec/models) if File.exist?('spec/models')
|
||||
::STATS_DIRECTORIES << %w(View\ specs spec/views) if File.exist?('spec/views')
|
||||
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers) if File.exist?('spec/controllers')
|
||||
::STATS_DIRECTORIES << %w(Helper\ specs spec/helpers) if File.exist?('spec/helpers')
|
||||
::STATS_DIRECTORIES << %w(Library\ specs spec/lib) if File.exist?('spec/lib')
|
||||
::STATS_DIRECTORIES << %w(Routing\ specs spec/routing) if File.exist?('spec/routing')
|
||||
::STATS_DIRECTORIES << %w(Integration\ specs spec/integration) if File.exist?('spec/integration')
|
||||
::CodeStatistics::TEST_TYPES << "Model specs" if File.exist?('spec/models')
|
||||
::CodeStatistics::TEST_TYPES << "View specs" if File.exist?('spec/views')
|
||||
::CodeStatistics::TEST_TYPES << "Controller specs" if File.exist?('spec/controllers')
|
||||
::CodeStatistics::TEST_TYPES << "Helper specs" if File.exist?('spec/helpers')
|
||||
::CodeStatistics::TEST_TYPES << "Library specs" if File.exist?('spec/lib')
|
||||
::CodeStatistics::TEST_TYPES << "Routing specs" if File.exist?('spec/routing')
|
||||
::CodeStatistics::TEST_TYPES << "Integration specs" if File.exist?('spec/integration')
|
||||
end
|
||||
|
||||
namespace :db do
|
||||
namespace :fixtures do
|
||||
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z."
|
||||
task :load => :environment do
|
||||
ActiveRecord::Base.establish_connection(Rails.env)
|
||||
base_dir = File.join(Rails.root, 'spec', 'fixtures')
|
||||
fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir
|
||||
|
||||
require 'active_record/fixtures'
|
||||
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/).map {|f| File.join(fixtures_dir, f) } : Dir.glob(File.join(fixtures_dir, '*.{yml,csv}'))).each do |fixture_file|
|
||||
Fixtures.create_fixtures(File.dirname(fixture_file), File.basename(fixture_file, '.*'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
desc "Initialises the installation, copy the *.tmpl files and directories to versions named without the .tmpl extension. It won't overwrite the files and directories if you've already copied them. You need to manually copy database.yml.tmpl -> database.yml and fill in the details before you run this task."
|
||||
task :setup_tracks => :environment do
|
||||
# Check the root directory for template files
|
||||
FileList["*.tmpl"].each do |template_file|
|
||||
f = File.basename(template_file) # with suffix
|
||||
f_only = File.basename(template_file,".tmpl") # without suffix
|
||||
if File.exists?(f_only)
|
||||
puts f_only + " already exists"
|
||||
else
|
||||
cp_r(f, f_only)
|
||||
puts f_only + " created"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
desc "Updates sqlite/sqlite3 databases created under Tracks 1.03 to the format required for Tracks 1.04. After this is done, you should be able to keep up to date with changes in the schema by running rake db:migrate."
|
||||
task :upgrade_sqlite_db => :environment do
|
||||
# Change the three lines below appropriately for your setup
|
||||
old_db = "tracks_103.db"
|
||||
new_db = "tracks_104.db"
|
||||
cmd = "sqlite3"
|
||||
replace_string = "update todos set done='f' where done=0;\nupdate todos set done='t' where done=1;\nupdate contexts set hide='f' where hide=0;\nupdate contexts set hide='t' where hide=1;\nupdate projects set done='f' where done=0;\nupdate projects set done='t' where done=1;\nCREATE TABLE 'schema_info' (\n 'version' INTEGER default NULL\n);\nINSERT INTO \"schema_info\" VALUES(1);\nCOMMIT;"
|
||||
|
||||
# cd to the db directory
|
||||
cd("db") do
|
||||
# Dump the old db into the temp file and replace the tinyints with booleans
|
||||
`#{cmd} #{old_db} .dump | sed "s/tinyint(4) NOT NULL default '0'/boolean default 'f'/" > temp.sql`
|
||||
# Create a second sqldump file for writing
|
||||
sqldump = File.open("temp2.sql", "w+")
|
||||
File.open("temp.sql") do |file|
|
||||
file.each_line do |line|
|
||||
# If COMMIT is on the line, insert the replace string
|
||||
# else just write the line back in
|
||||
# This effectively replaces COMMIT with the replace string
|
||||
if /COMMIT/ =~ line
|
||||
sqldump.write replace_string
|
||||
else
|
||||
sqldump.write line
|
||||
end
|
||||
end
|
||||
sqldump.close
|
||||
end
|
||||
|
||||
# Read the second dump back in to a new db
|
||||
system "#{cmd} #{new_db} < temp2.sql"
|
||||
puts "Created the a new database called #{new_db}."
|
||||
# Clean up the temp files
|
||||
rm("temp.sql")
|
||||
rm("temp2.sql")
|
||||
puts "Temporary files cleaned up."
|
||||
end
|
||||
|
||||
# rake db:migrate
|
||||
puts "Now check the database and run 'rake db:migrate' in the root of your Tracks installation."
|
||||
end
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
module Tracks
|
||||
class Config
|
||||
def self.salt
|
||||
SITE_CONFIG['salt']
|
||||
end
|
||||
|
||||
def self.auth_schemes
|
||||
SITE_CONFIG['authentication_schemes'] || []
|
||||
end
|
||||
|
||||
def self.openid_enabled?
|
||||
auth_schemes.include?('open_id')
|
||||
end
|
||||
|
||||
def self.cas_enabled?
|
||||
auth_schemes.include?('cas')
|
||||
end
|
||||
|
||||
def self.prefered_auth?
|
||||
if SITE_CONFIG['prefered_auth']
|
||||
SITE_CONFIG['prefered_auth']
|
||||
else
|
||||
auth_schemes.first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
# Inspiration from Bruce Williams [http://codefluency.com/articles/2006/07/01/rails-views-getting-in-context/]
|
||||
module Tracks
|
||||
|
||||
module SourceViewSwitching
|
||||
|
||||
class Responder
|
||||
|
||||
def initialize(source_view)
|
||||
@source_view = source_view.underscore.gsub(/\s+/,'_').to_sym rescue nil
|
||||
end
|
||||
|
||||
def nil?
|
||||
yield if @source_view.nil? && block_given?
|
||||
end
|
||||
|
||||
def context
|
||||
yield if :context == @source_view && block_given?
|
||||
end
|
||||
|
||||
def method_missing(check_source_view,*args)
|
||||
yield if check_source_view == @source_view && block_given?
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Controller
|
||||
|
||||
def self.included(base)
|
||||
base.send(:helper, Tracks::SourceViewSwitching::Helper)
|
||||
base.send(:helper_method, :source_view)
|
||||
end
|
||||
|
||||
def source_view_is( s )
|
||||
s == (params[:_source_view] || @source_view).to_sym
|
||||
end
|
||||
|
||||
def source_view_is_one_of( *s )
|
||||
s.include?(params[:_source_view].to_sym)
|
||||
end
|
||||
|
||||
def source_view
|
||||
responder = Tracks::SourceViewSwitching::Responder.new(params[:_source_view] || @source_view)
|
||||
block_given? ? yield(responder) : responder
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Helper
|
||||
|
||||
def source_view_tag(name)
|
||||
hidden_field_tag :_source_view, name.underscore.gsub(/\s+/,'_')
|
||||
end
|
||||
|
||||
def source_view_is( s )
|
||||
s == (params[:_source_view] || @source_view).to_sym
|
||||
end
|
||||
|
||||
def source_view_is_one_of( *s )
|
||||
s.include?(params[:_source_view].to_sym)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
ActionController::Base.send(:include, Tracks::SourceViewSwitching::Controller)
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
module Tracks
|
||||
module TodoList
|
||||
# TODO: this module should be deprecated. This could mostly (all?) be replaced by named scopes)
|
||||
|
||||
def not_done_todos(opts={})
|
||||
@not_done_todos ||= self.find_not_done_todos(opts)
|
||||
end
|
||||
|
||||
def done_todos
|
||||
@done_todos ||= self.find_done_todos
|
||||
end
|
||||
|
||||
def deferred_todos
|
||||
@deferred_todos ||= self.find_deferred_todos
|
||||
end
|
||||
|
||||
def find_not_done_todos(opts={})
|
||||
with_not_done_scope(opts) do
|
||||
self.todos.find(:all, :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC")
|
||||
end
|
||||
end
|
||||
|
||||
def find_deferred_todos(opts={})
|
||||
self.todos.find_in_state(:all, :deferred, :order => "todos.due IS NULL, todos.due ASC, todos.created_at ASC")
|
||||
end
|
||||
|
||||
def find_done_todos
|
||||
self.todos.completed.all(:order => "todos.completed_at DESC", :limit => self.user.prefs.show_number_completed)
|
||||
end
|
||||
|
||||
def not_done_todo_count(opts={})
|
||||
with_not_done_scope(opts) do
|
||||
self.todos.count
|
||||
end
|
||||
end
|
||||
|
||||
def with_not_done_scope(opts={})
|
||||
conditions = ["todos.state = ?", 'active']
|
||||
if opts.has_key?(:include_project_hidden_todos) && (opts[:include_project_hidden_todos] == true)
|
||||
conditions = ["(todos.state = ? OR todos.state = ?)", 'active', 'project_hidden']
|
||||
end
|
||||
if opts.has_key?(:tag)
|
||||
conditions = ["todos.state = ? AND taggings.tag_id = ?", 'active', opts[:tag]]
|
||||
end
|
||||
self.todos.send :with_scope, :find => {:conditions => conditions, :include => [:taggings]} do
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
def done_todo_count
|
||||
self.todos.count_in_state(:completed)
|
||||
end
|
||||
|
||||
def deferred_todo_count
|
||||
self.todos.count_in_state(:deferred)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
# adapted from https://gist.github.com/471663 and https://rails.lighthouseapp.com/projects/8994/tickets/4690-mongrel-doesnt-work-with-rails-238
|
||||
|
||||
def check_mongrel_around_115
|
||||
begin
|
||||
# Gem.available? is deprecated from rubygems 1.8.2
|
||||
Gem::Specification::find_by_name "mongrel", "~>1.1.5"
|
||||
rescue
|
||||
if RUBY_VERSION[2] == "9"
|
||||
false
|
||||
else
|
||||
Gem.available?('mongrel', '~>1.1.5')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
mongrel115 = check_mongrel_around_115
|
||||
|
||||
if Rails.version == '2.3.14' && mongrel115 && self.class.const_defined?(:Mongrel)
|
||||
|
||||
# Pulled right from latest rack. Old looked like this in 1.1.0 version.
|
||||
#
|
||||
# def [](k)
|
||||
# super(@names[k] ||= @names[k.downcase])
|
||||
# end
|
||||
#
|
||||
module Rack
|
||||
module Utils
|
||||
class HeaderHash < Hash
|
||||
def [](k)
|
||||
super(@names[k]) if @names[k]
|
||||
super(@names[k.downcase])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Code pulled from the ticket above.
|
||||
#
|
||||
class Mongrel::CGIWrapper
|
||||
def header_with_rails_fix(options = 'text/html')
|
||||
@head['cookie'] = options.delete('cookie').flatten.map { |v| v.sub(/^\n/,'') } if options.class != String and options['cookie']
|
||||
header_without_rails_fix(options)
|
||||
end
|
||||
alias_method_chain :header, :rails_fix
|
||||
end
|
||||
|
||||
# Pulled right from 2.3.10 ActionPack. Simple diff was
|
||||
#
|
||||
# if headers.include?('Set-Cookie')
|
||||
# headers['cookie'] = headers.delete('Set-Cookie').split("\n")
|
||||
# end
|
||||
#
|
||||
# to
|
||||
#
|
||||
# if headers['Set-Cookie']
|
||||
# headers['cookie'] = headers.delete('Set-Cookie').split("\n")
|
||||
# end
|
||||
#
|
||||
module ActionController
|
||||
class CGIHandler
|
||||
def self.dispatch_cgi(app, cgi, out = $stdout)
|
||||
env = cgi.__send__(:env_table)
|
||||
env.delete "HTTP_CONTENT_LENGTH"
|
||||
|
||||
cgi.stdinput.extend ProperStream
|
||||
|
||||
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
|
||||
|
||||
env.update({
|
||||
"rack.version" => [0,1],
|
||||
"rack.input" => cgi.stdinput,
|
||||
"rack.errors" => $stderr,
|
||||
"rack.multithread" => false,
|
||||
"rack.multiprocess" => true,
|
||||
"rack.run_once" => false,
|
||||
"rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
|
||||
})
|
||||
|
||||
env["QUERY_STRING"] ||= ""
|
||||
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
|
||||
env["REQUEST_PATH"] ||= "/"
|
||||
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
|
||||
|
||||
status, headers, body = app.call(env)
|
||||
begin
|
||||
out.binmode if out.respond_to?(:binmode)
|
||||
out.sync = false if out.respond_to?(:sync=)
|
||||
|
||||
headers['Status'] = status.to_s
|
||||
|
||||
if headers['Set-Cookie']
|
||||
headers['cookie'] = headers.delete('Set-Cookie').split("\n")
|
||||
end
|
||||
|
||||
out.write(cgi.header(headers))
|
||||
|
||||
body.each { |part|
|
||||
out.write part
|
||||
out.flush if out.respond_to?(:flush)
|
||||
}
|
||||
ensure
|
||||
body.close if body.respond_to?(:close)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# These settings change the behavior of Rails 2 apps and will be defaults
|
||||
# for Rails 3. You can remove this initializer when Rails 3 is released.
|
||||
|
||||
if defined?(ActiveRecord)
|
||||
# Include Active Record class name as root for JSON serialized output.
|
||||
ActiveRecord::Base.include_root_in_json = true
|
||||
|
||||
# Store the full class name (including module namespace) in STI type column.
|
||||
ActiveRecord::Base.store_full_sti_class = true
|
||||
end
|
||||
|
||||
ActionController::Routing.generate_best_match = false
|
||||
|
||||
# Use ISO 8601 format for JSON serialized times and dates.
|
||||
ActiveSupport.use_standard_json_time_format = true
|
||||
|
||||
# Don't escape HTML entities in JSON, leave that for the #json_escape helper.
|
||||
# if you're including raw json in an HTML page.
|
||||
ActiveSupport.escape_html_entities_in_json = false
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
require 'extra_validations'
|
||||
ActiveRecord::Base.extend ExtraValidations
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
module ExtraValidations
|
||||
|
||||
# Validates the value of the specified attribute by checking for a forbidden string
|
||||
#
|
||||
# class Person < ActiveRecord::Base
|
||||
# validates_does_not_contain :first_name, :string => ','
|
||||
# end
|
||||
#
|
||||
# A string must be provided or else an exception will be raised.
|
||||
#
|
||||
# Configuration options:
|
||||
# * <tt>message</tt> - A custom error message (default is: "is invalid")
|
||||
# * <tt>string</tt> - The string to verify is not included (note: must be supplied!)
|
||||
# * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
|
||||
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
||||
# occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
|
||||
# method, proc or string should return or evaluate to a true or false value.
|
||||
def validates_does_not_contain(*attr_names)
|
||||
configuration = { :message => I18n.translate('activerecord.errors.messages')[:invalid], :on => :save, :string => nil }
|
||||
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
||||
|
||||
raise(ArgumentError, "A string must be supplied as the :string option of the configuration hash") unless configuration[:string].is_a?(String)
|
||||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
record.errors.add(attr_name, configuration[:message]) if value.to_s =~ Regexp.new(Regexp.escape(configuration[:string]))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
* Fake HTTP method from OpenID server since they only support a GET. Eliminates the need to set an extra route to match the server's reply. [Josh Peek]
|
||||
|
||||
* OpenID 2.0 recommends that forms should use the field name "openid_identifier" rather than "openid_url" [Josh Peek]
|
||||
|
||||
* Return open_id_response.display_identifier to the application instead of .endpoints.claimed_id. [nbibler]
|
||||
|
||||
* Add Timeout protection [Rick]
|
||||
|
||||
* An invalid identity url passed through authenticate_with_open_id will no longer raise an InvalidOpenId exception. Instead it will return Result[:missing] to the completion block.
|
||||
|
||||
* Allow a return_to option to be used instead of the requested url [Josh Peek]
|
||||
|
||||
* Updated plugin to use Ruby OpenID 2.x.x [Josh Peek]
|
||||
|
||||
* Tied plugin to ruby-openid 1.1.4 gem until we can make it compatible with 2.x [DHH]
|
||||
|
||||
* Use URI instead of regexps to normalize the URL and gain free, better matching #8136 [dkubb]
|
||||
|
||||
* Allow -'s in #normalize_url [Rick]
|
||||
|
||||
* remove instance of mattr_accessor, it was breaking tests since they don't load ActiveSupport. Fix Timeout test [Rick]
|
||||
|
||||
* Throw a InvalidOpenId exception instead of just a RuntimeError when the URL can't be normalized [DHH]
|
||||
|
||||
* Just use the path for the return URL, so extra query parameters don't interfere [DHH]
|
||||
|
||||
* Added a new default database-backed store after experiencing trouble with the filestore on NFS. The file store is still available as an option [DHH]
|
||||
|
||||
* Added normalize_url and applied it to all operations going through the plugin [DHH]
|
||||
|
||||
* Removed open_id? as the idea of using the same input box for both OpenID and username has died -- use using_open_id? instead (which checks for the presence of params[:openid_url] by default) [DHH]
|
||||
|
||||
* Added OpenIdAuthentication::Result to make it easier to deal with default situations where you don't care to do something particular for each error state [DHH]
|
||||
|
||||
* Stop relying on root_url being defined, we can just grab the current url instead [DHH]
|
||||
|
|
@ -1,231 +0,0 @@
|
|||
OpenIdAuthentication
|
||||
====================
|
||||
|
||||
Provides a thin wrapper around the excellent ruby-openid gem from JanRan. Be sure to install that first:
|
||||
|
||||
gem install ruby-openid
|
||||
|
||||
To understand what OpenID is about and how it works, it helps to read the documentation for lib/openid/consumer.rb
|
||||
from that gem.
|
||||
|
||||
The specification used is http://openid.net/specs/openid-authentication-2_0.html.
|
||||
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
OpenID authentication uses the session, so be sure that you haven't turned that off. It also relies on a number of
|
||||
database tables to store the authentication keys. So you'll have to run the migration to create these before you get started:
|
||||
|
||||
rake open_id_authentication:db:create
|
||||
|
||||
Or, use the included generators to install or upgrade:
|
||||
|
||||
./script/generate open_id_authentication_tables MigrationName
|
||||
./script/generate upgrade_open_id_authentication_tables MigrationName
|
||||
|
||||
Alternatively, you can use the file-based store, which just relies on on tmp/openids being present in RAILS_ROOT. But be aware that this store only works if you have a single application server. And it's not safe to use across NFS. It's recommended that you use the database store if at all possible. To use the file-based store, you'll also have to add this line to your config/environment.rb:
|
||||
|
||||
OpenIdAuthentication.store = :file
|
||||
|
||||
This particular plugin also relies on the fact that the authentication action allows for both POST and GET operations.
|
||||
If you're using RESTful authentication, you'll need to explicitly allow for this in your routes.rb.
|
||||
|
||||
The plugin also expects to find a root_url method that points to the home page of your site. You can accomplish this by using a root route in config/routes.rb:
|
||||
|
||||
map.root :controller => 'articles'
|
||||
|
||||
This plugin relies on Rails Edge revision 6317 or newer.
|
||||
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
This example is just to meant to demonstrate how you could use OpenID authentication. You might well want to add
|
||||
salted hash logins instead of plain text passwords and other requirements on top of this. Treat it as a starting point,
|
||||
not a destination.
|
||||
|
||||
Note that the User model referenced in the simple example below has an 'identity_url' attribute. You will want to add the same or similar field to whatever
|
||||
model you are using for authentication.
|
||||
|
||||
Also of note is the following code block used in the example below:
|
||||
|
||||
authenticate_with_open_id do |result, identity_url|
|
||||
...
|
||||
end
|
||||
|
||||
In the above code block, 'identity_url' will need to match user.identity_url exactly. 'identity_url' will be a string in the form of 'http://example.com' -
|
||||
If you are storing just 'example.com' with your user, the lookup will fail.
|
||||
|
||||
There is a handy method in this plugin called 'normalize_url' that will help with validating OpenID URLs.
|
||||
|
||||
OpenIdAuthentication.normalize_url(user.identity_url)
|
||||
|
||||
The above will return a standardized version of the OpenID URL - the above called with 'example.com' will return 'http://example.com/'
|
||||
It will also raise an InvalidOpenId exception if the URL is determined to not be valid.
|
||||
Use the above code in your User model and validate OpenID URLs before saving them.
|
||||
|
||||
config/routes.rb
|
||||
|
||||
map.root :controller => 'articles'
|
||||
map.resource :session
|
||||
|
||||
|
||||
app/views/sessions/new.erb
|
||||
|
||||
<% form_tag(session_url) do %>
|
||||
<p>
|
||||
<label for="name">Username:</label>
|
||||
<%= text_field_tag "name" %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="password">Password:</label>
|
||||
<%= password_field_tag %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
...or use:
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label for="openid_identifier">OpenID:</label>
|
||||
<%= text_field_tag "openid_identifier" %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= submit_tag 'Sign in', :disable_with => "Signing in…" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
app/controllers/sessions_controller.rb
|
||||
class SessionsController < ApplicationController
|
||||
def create
|
||||
if using_open_id?
|
||||
open_id_authentication
|
||||
else
|
||||
password_authentication(params[:name], params[:password])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def password_authentication(name, password)
|
||||
if @current_user = @account.users.authenticate(params[:name], params[:password])
|
||||
successful_login
|
||||
else
|
||||
failed_login "Sorry, that username/password doesn't work"
|
||||
end
|
||||
end
|
||||
|
||||
def open_id_authentication
|
||||
authenticate_with_open_id do |result, identity_url|
|
||||
if result.successful?
|
||||
if @current_user = @account.users.find_by_identity_url(identity_url)
|
||||
successful_login
|
||||
else
|
||||
failed_login "Sorry, no user by that identity URL exists (#{identity_url})"
|
||||
end
|
||||
else
|
||||
failed_login result.message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def successful_login
|
||||
session[:user_id] = @current_user.id
|
||||
redirect_to(root_url)
|
||||
end
|
||||
|
||||
def failed_login(message)
|
||||
flash[:error] = message
|
||||
redirect_to(new_session_url)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
If you're fine with the result messages above and don't need individual logic on a per-failure basis,
|
||||
you can collapse the case into a mere boolean:
|
||||
|
||||
def open_id_authentication
|
||||
authenticate_with_open_id do |result, identity_url|
|
||||
if result.successful? && @current_user = @account.users.find_by_identity_url(identity_url)
|
||||
successful_login
|
||||
else
|
||||
failed_login(result.message || "Sorry, no user by that identity URL exists (#{identity_url})")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Simple Registration OpenID Extension
|
||||
====================================
|
||||
|
||||
Some OpenID Providers support this lightweight profile exchange protocol. See more: http://www.openidenabled.com/openid/simple-registration-extension
|
||||
|
||||
You can support it in your app by changing #open_id_authentication
|
||||
|
||||
def open_id_authentication(identity_url)
|
||||
# Pass optional :required and :optional keys to specify what sreg fields you want.
|
||||
# Be sure to yield registration, a third argument in the #authenticate_with_open_id block.
|
||||
authenticate_with_open_id(identity_url,
|
||||
:required => [ :nickname, :email ],
|
||||
:optional => :fullname) do |result, identity_url, registration|
|
||||
case result.status
|
||||
when :missing
|
||||
failed_login "Sorry, the OpenID server couldn't be found"
|
||||
when :invalid
|
||||
failed_login "Sorry, but this does not appear to be a valid OpenID"
|
||||
when :canceled
|
||||
failed_login "OpenID verification was canceled"
|
||||
when :failed
|
||||
failed_login "Sorry, the OpenID verification failed"
|
||||
when :successful
|
||||
if @current_user = @account.users.find_by_identity_url(identity_url)
|
||||
assign_registration_attributes!(registration)
|
||||
|
||||
if current_user.save
|
||||
successful_login
|
||||
else
|
||||
failed_login "Your OpenID profile registration failed: " +
|
||||
@current_user.errors.full_messages.to_sentence
|
||||
end
|
||||
else
|
||||
failed_login "Sorry, no user by that identity URL exists"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# registration is a hash containing the valid sreg keys given above
|
||||
# use this to map them to fields of your user model
|
||||
def assign_registration_attributes!(registration)
|
||||
model_to_registration_mapping.each do |model_attribute, registration_attribute|
|
||||
unless registration[registration_attribute].blank?
|
||||
@current_user.send("#{model_attribute}=", registration[registration_attribute])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def model_to_registration_mapping
|
||||
{ :login => 'nickname', :email => 'email', :display_name => 'fullname' }
|
||||
end
|
||||
|
||||
Attribute Exchange OpenID Extension
|
||||
===================================
|
||||
|
||||
Some OpenID providers also support the OpenID AX (attribute exchange) protocol for exchanging identity information between endpoints. See more: http://openid.net/specs/openid-attribute-exchange-1_0.html
|
||||
|
||||
Accessing AX data is very similar to the Simple Registration process, described above -- just add the URI identifier for the AX field to your :optional or :required parameters. For example:
|
||||
|
||||
authenticate_with_open_id(identity_url,
|
||||
:required => [ :email, 'http://schema.openid.net/birthDate' ]) do |result, identity_url, registration|
|
||||
|
||||
This would provide the sreg data for :email, and the AX data for 'http://schema.openid.net/birthDate'
|
||||
|
||||
|
||||
|
||||
Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the open_id_authentication plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the open_id_authentication plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'OpenIdAuthentication'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
class OpenIdAuthenticationTablesGenerator < Rails::Generator::NamedBase
|
||||
def initialize(runtime_args, runtime_options = {})
|
||||
super
|
||||
end
|
||||
|
||||
def manifest
|
||||
record do |m|
|
||||
m.migration_template 'migration.rb', 'db/migrate'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
class <%= class_name %> < ActiveRecord::Migration
|
||||
def self.up
|
||||
create_table :open_id_authentication_associations, :force => true do |t|
|
||||
t.integer :issued, :lifetime
|
||||
t.string :handle, :assoc_type
|
||||
t.binary :server_url, :secret
|
||||
end
|
||||
|
||||
create_table :open_id_authentication_nonces, :force => true do |t|
|
||||
t.integer :timestamp, :null => false
|
||||
t.string :server_url, :null => true
|
||||
t.string :salt, :null => false
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :open_id_authentication_associations
|
||||
drop_table :open_id_authentication_nonces
|
||||
end
|
||||
end
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
class <%= class_name %> < ActiveRecord::Migration
|
||||
def self.up
|
||||
drop_table :open_id_authentication_settings
|
||||
drop_table :open_id_authentication_nonces
|
||||
|
||||
create_table :open_id_authentication_nonces, :force => true do |t|
|
||||
t.integer :timestamp, :null => false
|
||||
t.string :server_url, :null => true
|
||||
t.string :salt, :null => false
|
||||
end
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :open_id_authentication_nonces
|
||||
|
||||
create_table :open_id_authentication_nonces, :force => true do |t|
|
||||
t.integer :created
|
||||
t.string :nonce
|
||||
end
|
||||
|
||||
create_table :open_id_authentication_settings, :force => true do |t|
|
||||
t.string :setting
|
||||
t.binary :value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
class UpgradeOpenIdAuthenticationTablesGenerator < Rails::Generator::NamedBase
|
||||
def initialize(runtime_args, runtime_options = {})
|
||||
super
|
||||
end
|
||||
|
||||
def manifest
|
||||
record do |m|
|
||||
m.migration_template 'migration.rb', 'db/migrate'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
if config.respond_to?(:gems)
|
||||
config.gem 'ruby-openid', :lib => 'openid', :version => '>=2.0.4'
|
||||
else
|
||||
begin
|
||||
require 'openid'
|
||||
rescue LoadError
|
||||
begin
|
||||
gem 'ruby-openid', '>=2.0.4'
|
||||
rescue Gem::LoadError
|
||||
puts "Install the ruby-openid gem to enable OpenID support"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
config.to_prepare do
|
||||
OpenID::Util.logger = Rails.logger
|
||||
ActionController::Base.send :include, OpenIdAuthentication
|
||||
end
|
||||
|
|
@ -1,240 +0,0 @@
|
|||
require 'uri'
|
||||
require 'openid/extensions/sreg'
|
||||
require 'openid/extensions/ax'
|
||||
require 'openid/store/filesystem'
|
||||
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/association'
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/nonce'
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/db_store'
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/request'
|
||||
require File.dirname(__FILE__) + '/open_id_authentication/timeout_fixes' if OpenID::VERSION == "2.0.4"
|
||||
|
||||
module OpenIdAuthentication
|
||||
OPEN_ID_AUTHENTICATION_DIR = RAILS_ROOT + "/tmp/openids"
|
||||
|
||||
def self.store
|
||||
@@store
|
||||
end
|
||||
|
||||
def self.store=(*store_option)
|
||||
store, *parameters = *([ store_option ].flatten)
|
||||
|
||||
@@store = case store
|
||||
when :db
|
||||
OpenIdAuthentication::DbStore.new
|
||||
when :file
|
||||
OpenID::Store::Filesystem.new(OPEN_ID_AUTHENTICATION_DIR)
|
||||
else
|
||||
store
|
||||
end
|
||||
end
|
||||
|
||||
self.store = :db
|
||||
|
||||
class InvalidOpenId < StandardError
|
||||
end
|
||||
|
||||
class Result
|
||||
ERROR_MESSAGES = {
|
||||
:missing => "Sorry, the OpenID server couldn't be found",
|
||||
:invalid => "Sorry, but this does not appear to be a valid OpenID",
|
||||
:canceled => "OpenID verification was canceled",
|
||||
:failed => "OpenID verification failed",
|
||||
:setup_needed => "OpenID verification needs setup"
|
||||
}
|
||||
|
||||
def self.[](code)
|
||||
new(code)
|
||||
end
|
||||
|
||||
def initialize(code)
|
||||
@code = code
|
||||
end
|
||||
|
||||
def status
|
||||
@code
|
||||
end
|
||||
|
||||
ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } }
|
||||
|
||||
def successful?
|
||||
@code == :successful
|
||||
end
|
||||
|
||||
def unsuccessful?
|
||||
ERROR_MESSAGES.keys.include?(@code)
|
||||
end
|
||||
|
||||
def message
|
||||
ERROR_MESSAGES[@code]
|
||||
end
|
||||
end
|
||||
|
||||
# normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
|
||||
def self.normalize_identifier(identifier)
|
||||
# clean up whitespace
|
||||
identifier = identifier.to_s.strip
|
||||
|
||||
# if an XRI has a prefix, strip it.
|
||||
identifier.gsub!(/xri:\/\//i, '')
|
||||
|
||||
# dodge XRIs -- TODO: validate, don't just skip.
|
||||
unless ['=', '@', '+', '$', '!', '('].include?(identifier.at(0))
|
||||
# does it begin with http? if not, add it.
|
||||
identifier = "http://#{identifier}" unless identifier =~ /^http/i
|
||||
|
||||
# strip any fragments
|
||||
identifier.gsub!(/\#(.*)$/, '')
|
||||
|
||||
begin
|
||||
uri = URI.parse(identifier)
|
||||
uri.scheme = uri.scheme.downcase # URI should do this
|
||||
identifier = uri.normalize.to_s
|
||||
rescue URI::InvalidURIError
|
||||
raise InvalidOpenId.new("#{identifier} is not an OpenID identifier")
|
||||
end
|
||||
end
|
||||
|
||||
return identifier
|
||||
end
|
||||
|
||||
# deprecated for OpenID 2.0, where not all OpenIDs are URLs
|
||||
def self.normalize_url(url)
|
||||
ActiveSupport::Deprecation.warn "normalize_url has been deprecated, use normalize_identifier instead"
|
||||
self.normalize_identifier(url)
|
||||
end
|
||||
|
||||
protected
|
||||
def normalize_url(url)
|
||||
OpenIdAuthentication.normalize_url(url)
|
||||
end
|
||||
|
||||
def normalize_identifier(url)
|
||||
OpenIdAuthentication.normalize_identifier(url)
|
||||
end
|
||||
|
||||
# The parameter name of "openid_identifier" is used rather than the Rails convention "open_id_identifier"
|
||||
# because that's what the specification dictates in order to get browser auto-complete working across sites
|
||||
def using_open_id?(identity_url = nil) #:doc:
|
||||
identity_url ||= params[:openid_identifier] || params[:openid_url]
|
||||
!identity_url.blank? || params[:open_id_complete]
|
||||
end
|
||||
|
||||
def authenticate_with_open_id(identity_url = nil, options = {}, &block) #:doc:
|
||||
identity_url ||= params[:openid_identifier] || params[:openid_url]
|
||||
|
||||
if params[:open_id_complete].nil?
|
||||
begin_open_id_authentication(identity_url, options, &block)
|
||||
else
|
||||
complete_open_id_authentication(&block)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def begin_open_id_authentication(identity_url, options = {})
|
||||
identity_url = normalize_identifier(identity_url)
|
||||
return_to = options.delete(:return_to)
|
||||
method = options.delete(:method)
|
||||
|
||||
options[:required] ||= [] # reduces validation later
|
||||
options[:optional] ||= []
|
||||
|
||||
open_id_request = open_id_consumer.begin(identity_url)
|
||||
add_simple_registration_fields(open_id_request, options)
|
||||
add_ax_fields(open_id_request, options)
|
||||
redirect_to(open_id_redirect_url(open_id_request, return_to, method))
|
||||
rescue OpenIdAuthentication::InvalidOpenId => e
|
||||
yield Result[:invalid], identity_url, nil
|
||||
rescue OpenID::OpenIDError, Timeout::Error => e
|
||||
logger.error("[OPENID] #{e}")
|
||||
yield Result[:missing], identity_url, nil
|
||||
end
|
||||
|
||||
def complete_open_id_authentication
|
||||
params_with_path = params.reject { |key, value| request.path_parameters[key] }
|
||||
params_with_path.delete(:format)
|
||||
open_id_response = timeout_protection_from_identity_server { open_id_consumer.complete(params_with_path, requested_url) }
|
||||
identity_url = normalize_identifier(open_id_response.display_identifier) if open_id_response.display_identifier
|
||||
|
||||
case open_id_response.status
|
||||
when OpenID::Consumer::SUCCESS
|
||||
profile_data = {}
|
||||
|
||||
# merge the SReg data and the AX data into a single hash of profile data
|
||||
[ OpenID::SReg::Response, OpenID::AX::FetchResponse ].each do |data_response|
|
||||
if data_response.from_success_response( open_id_response )
|
||||
profile_data.merge! data_response.from_success_response( open_id_response ).data
|
||||
end
|
||||
end
|
||||
|
||||
yield Result[:successful], identity_url, profile_data
|
||||
when OpenID::Consumer::CANCEL
|
||||
yield Result[:canceled], identity_url, nil
|
||||
when OpenID::Consumer::FAILURE
|
||||
yield Result[:failed], identity_url, nil
|
||||
when OpenID::Consumer::SETUP_NEEDED
|
||||
yield Result[:setup_needed], open_id_response.setup_url, nil
|
||||
end
|
||||
end
|
||||
|
||||
def open_id_consumer
|
||||
OpenID::Consumer.new(session, OpenIdAuthentication.store)
|
||||
end
|
||||
|
||||
def add_simple_registration_fields(open_id_request, fields)
|
||||
sreg_request = OpenID::SReg::Request.new
|
||||
|
||||
# filter out AX identifiers (URIs)
|
||||
required_fields = fields[:required].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
|
||||
optional_fields = fields[:optional].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
|
||||
|
||||
sreg_request.request_fields(required_fields, true) unless required_fields.blank?
|
||||
sreg_request.request_fields(optional_fields, false) unless optional_fields.blank?
|
||||
sreg_request.policy_url = fields[:policy_url] if fields[:policy_url]
|
||||
open_id_request.add_extension(sreg_request)
|
||||
end
|
||||
|
||||
def add_ax_fields( open_id_request, fields )
|
||||
ax_request = OpenID::AX::FetchRequest.new
|
||||
|
||||
# look through the :required and :optional fields for URIs (AX identifiers)
|
||||
fields[:required].each do |f|
|
||||
next unless f =~ /^https?:\/\//
|
||||
ax_request.add( OpenID::AX::AttrInfo.new( f, nil, true ) )
|
||||
end
|
||||
|
||||
fields[:optional].each do |f|
|
||||
next unless f =~ /^https?:\/\//
|
||||
ax_request.add( OpenID::AX::AttrInfo.new( f, nil, false ) )
|
||||
end
|
||||
|
||||
open_id_request.add_extension( ax_request )
|
||||
end
|
||||
|
||||
def open_id_redirect_url(open_id_request, return_to = nil, method = nil)
|
||||
open_id_request.return_to_args['_method'] = (method || request.method).to_s
|
||||
open_id_request.return_to_args['open_id_complete'] = '1'
|
||||
open_id_request.redirect_url(root_url, return_to || requested_url)
|
||||
end
|
||||
|
||||
def requested_url
|
||||
relative_url_root = self.class.respond_to?(:relative_url_root) ?
|
||||
self.class.relative_url_root.to_s :
|
||||
request.relative_url_root
|
||||
"#{request.protocol}#{request.host_with_port}#{ActionController::Base.relative_url_root}#{request.path}"
|
||||
end
|
||||
|
||||
def timeout_protection_from_identity_server
|
||||
yield
|
||||
rescue Timeout::Error
|
||||
Class.new do
|
||||
def status
|
||||
OpenID::FAILURE
|
||||
end
|
||||
|
||||
def msg
|
||||
"Identity server timed out"
|
||||
end
|
||||
end.new
|
||||
end
|
||||
end
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
module OpenIdAuthentication
|
||||
class Association < ActiveRecord::Base
|
||||
set_table_name :open_id_authentication_associations
|
||||
|
||||
def from_record
|
||||
OpenID::Association.new(handle, secret, issued, lifetime, assoc_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
require 'openid/store/interface'
|
||||
|
||||
module OpenIdAuthentication
|
||||
class DbStore < OpenID::Store::Interface
|
||||
def self.cleanup_nonces
|
||||
now = Time.now.to_i
|
||||
Nonce.delete_all(["timestamp > ? OR timestamp < ?", now + OpenID::Nonce.skew, now - OpenID::Nonce.skew])
|
||||
end
|
||||
|
||||
def self.cleanup_associations
|
||||
now = Time.now.to_i
|
||||
Association.delete_all(['issued + lifetime > ?',now])
|
||||
end
|
||||
|
||||
def store_association(server_url, assoc)
|
||||
remove_association(server_url, assoc.handle)
|
||||
Association.create(:server_url => server_url,
|
||||
:handle => assoc.handle,
|
||||
:secret => assoc.secret,
|
||||
:issued => assoc.issued,
|
||||
:lifetime => assoc.lifetime,
|
||||
:assoc_type => assoc.assoc_type)
|
||||
end
|
||||
|
||||
def get_association(server_url, handle = nil)
|
||||
assocs = if handle.blank?
|
||||
Association.find_all_by_server_url(server_url)
|
||||
else
|
||||
Association.find_all_by_server_url_and_handle(server_url, handle)
|
||||
end
|
||||
|
||||
assocs.reverse.each do |assoc|
|
||||
a = assoc.from_record
|
||||
if a.expires_in == 0
|
||||
assoc.destroy
|
||||
else
|
||||
return a
|
||||
end
|
||||
end if assocs.any?
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
def remove_association(server_url, handle)
|
||||
Association.delete_all(['server_url = ? AND handle = ?', server_url, handle]) > 0
|
||||
end
|
||||
|
||||
def use_nonce(server_url, timestamp, salt)
|
||||
return false if Nonce.find_by_server_url_and_timestamp_and_salt(server_url, timestamp, salt)
|
||||
return false if (timestamp - Time.now.to_i).abs > OpenID::Nonce.skew
|
||||
Nonce.create(:server_url => server_url, :timestamp => timestamp, :salt => salt)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
module OpenIdAuthentication
|
||||
class Nonce < ActiveRecord::Base
|
||||
set_table_name :open_id_authentication_nonces
|
||||
end
|
||||
end
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
module OpenIdAuthentication
|
||||
module Request
|
||||
def self.included(base)
|
||||
base.alias_method_chain :request_method, :openid
|
||||
end
|
||||
|
||||
def request_method_with_openid
|
||||
if !parameters[:_method].blank? && parameters[:open_id_complete] == '1'
|
||||
parameters[:_method].to_sym
|
||||
else
|
||||
request_method_without_openid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# In Rails 2.3, the request object has been renamed
|
||||
# from AbstractRequest to Request
|
||||
if defined? ActionController::Request
|
||||
ActionController::Request.send :include, OpenIdAuthentication::Request
|
||||
else
|
||||
ActionController::AbstractRequest.send :include, OpenIdAuthentication::Request
|
||||
end
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# http://trac.openidenabled.com/trac/ticket/156
|
||||
module OpenID
|
||||
@@timeout_threshold = 20
|
||||
|
||||
def self.timeout_threshold
|
||||
@@timeout_threshold
|
||||
end
|
||||
|
||||
def self.timeout_threshold=(value)
|
||||
@@timeout_threshold = value
|
||||
end
|
||||
|
||||
class StandardFetcher
|
||||
def make_http(uri)
|
||||
http = @proxy.new(uri.host, uri.port)
|
||||
http.read_timeout = http.open_timeout = OpenID.timeout_threshold
|
||||
http
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
namespace :open_id_authentication do
|
||||
namespace :db do
|
||||
desc "Creates authentication tables for use with OpenIdAuthentication"
|
||||
task :create => :environment do
|
||||
generate_migration(["open_id_authentication_tables", "add_open_id_authentication_tables"])
|
||||
end
|
||||
|
||||
desc "Upgrade authentication tables from ruby-openid 1.x.x to 2.x.x"
|
||||
task :upgrade => :environment do
|
||||
generate_migration(["upgrade_open_id_authentication_tables", "upgrade_open_id_authentication_tables"])
|
||||
end
|
||||
|
||||
def generate_migration(args)
|
||||
require 'rails_generator'
|
||||
require 'rails_generator/scripts/generate'
|
||||
|
||||
if ActiveRecord::Base.connection.supports_migrations?
|
||||
Rails::Generator::Scripts::Generate.new.run(args)
|
||||
else
|
||||
raise "Task unavailable to this database (no migration support)"
|
||||
end
|
||||
end
|
||||
|
||||
desc "Clear the authentication tables"
|
||||
task :clear => :environment do
|
||||
OpenIdAuthentication::DbStore.cleanup_nonces
|
||||
OpenIdAuthentication::DbStore.cleanup_associations
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/test_helper'
|
||||
|
||||
class NormalizeTest < Test::Unit::TestCase
|
||||
include OpenIdAuthentication
|
||||
|
||||
NORMALIZATIONS = {
|
||||
"openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
|
||||
"http://openid.aol.com/nextangler" => "http://openid.aol.com/nextangler",
|
||||
"https://openid.aol.com/nextangler" => "https://openid.aol.com/nextangler",
|
||||
"HTTP://OPENID.AOL.COM/NEXTANGLER" => "http://openid.aol.com/NEXTANGLER",
|
||||
"HTTPS://OPENID.AOL.COM/NEXTANGLER" => "https://openid.aol.com/NEXTANGLER",
|
||||
"loudthinking.com" => "http://loudthinking.com/",
|
||||
"http://loudthinking.com" => "http://loudthinking.com/",
|
||||
"http://loudthinking.com:80" => "http://loudthinking.com/",
|
||||
"https://loudthinking.com:443" => "https://loudthinking.com/",
|
||||
"http://loudthinking.com:8080" => "http://loudthinking.com:8080/",
|
||||
"techno-weenie.net" => "http://techno-weenie.net/",
|
||||
"http://techno-weenie.net" => "http://techno-weenie.net/",
|
||||
"http://techno-weenie.net " => "http://techno-weenie.net/",
|
||||
"=name" => "=name"
|
||||
}
|
||||
|
||||
def test_normalizations
|
||||
NORMALIZATIONS.each do |from, to|
|
||||
assert_equal to, normalize_identifier(from)
|
||||
end
|
||||
end
|
||||
|
||||
def test_broken_open_id
|
||||
assert_raises(InvalidOpenId) { normalize_identifier(nil) }
|
||||
end
|
||||
end
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/test_helper'
|
||||
|
||||
class OpenIdAuthenticationTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@controller = Class.new do
|
||||
include OpenIdAuthentication
|
||||
def params() {} end
|
||||
end.new
|
||||
end
|
||||
|
||||
def test_authentication_should_fail_when_the_identity_server_is_missing
|
||||
open_id_consumer = mock()
|
||||
open_id_consumer.expects(:begin).raises(OpenID::OpenIDError)
|
||||
@controller.expects(:open_id_consumer).returns(open_id_consumer)
|
||||
@controller.expects(:logger).returns(mock(:error => true))
|
||||
|
||||
@controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
|
||||
assert result.missing?
|
||||
assert_equal "Sorry, the OpenID server couldn't be found", result.message
|
||||
end
|
||||
end
|
||||
|
||||
def test_authentication_should_be_invalid_when_the_identity_url_is_invalid
|
||||
@controller.send(:authenticate_with_open_id, "!") do |result, identity_url|
|
||||
assert result.invalid?, "Result expected to be invalid but was not"
|
||||
assert_equal "Sorry, but this does not appear to be a valid OpenID", result.message
|
||||
end
|
||||
end
|
||||
|
||||
def test_authentication_should_fail_when_the_identity_server_times_out
|
||||
open_id_consumer = mock()
|
||||
open_id_consumer.expects(:begin).raises(Timeout::Error, "Identity Server took too long.")
|
||||
@controller.expects(:open_id_consumer).returns(open_id_consumer)
|
||||
@controller.expects(:logger).returns(mock(:error => true))
|
||||
|
||||
@controller.send(:authenticate_with_open_id, "http://someone.example.com") do |result, identity_url|
|
||||
assert result.missing?
|
||||
assert_equal "Sorry, the OpenID server couldn't be found", result.message
|
||||
end
|
||||
end
|
||||
|
||||
def test_authentication_should_begin_when_the_identity_server_is_present
|
||||
@controller.expects(:begin_open_id_authentication)
|
||||
@controller.send(:authenticate_with_open_id, "http://someone.example.com")
|
||||
end
|
||||
end
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
require File.dirname(__FILE__) + '/test_helper'
|
||||
|
||||
class StatusTest < Test::Unit::TestCase
|
||||
include OpenIdAuthentication
|
||||
|
||||
def test_state_conditional
|
||||
assert Result[:missing].missing?
|
||||
assert Result[:missing].unsuccessful?
|
||||
assert !Result[:missing].successful?
|
||||
|
||||
assert Result[:successful].successful?
|
||||
assert !Result[:successful].unsuccessful?
|
||||
end
|
||||
end
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
require 'test/unit'
|
||||
require 'rubygems'
|
||||
|
||||
gem 'activesupport'
|
||||
require 'active_support'
|
||||
|
||||
gem 'actionpack'
|
||||
require 'action_controller'
|
||||
|
||||
gem 'mocha'
|
||||
require 'mocha'
|
||||
|
||||
gem 'ruby-openid'
|
||||
require 'openid'
|
||||
|
||||
RAILS_ROOT = File.dirname(__FILE__) unless defined? RAILS_ROOT
|
||||
require File.dirname(__FILE__) + "/../lib/open_id_authentication"
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
ResourceFeeder
|
||||
==============
|
||||
|
||||
Simple feeds for resources
|
||||
|
||||
NOTE: This plugin depends on the latest version of simply_helpful, available here:
|
||||
http://dev.rubyonrails.org/svn/rails/plugins/simply_helpful/
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
|
||||
desc 'Default: run unit tests.'
|
||||
task :default => :test
|
||||
|
||||
desc 'Test the resource_feed plugin.'
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << 'lib'
|
||||
t.pattern = 'test/**/*_test.rb'
|
||||
t.verbose = true
|
||||
end
|
||||
|
||||
desc 'Generate documentation for the resource_feed plugin.'
|
||||
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = 'ResourceFeed'
|
||||
rdoc.options << '--line-numbers' << '--inline-source'
|
||||
rdoc.rdoc_files.include('README')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
require 'resource_feeder'
|
||||
ActionController::Base.send(:include, ResourceFeeder::Rss, ResourceFeeder::Atom)
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue