backlog 0.36.2 → 0.37.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +16 -4
- data/Gemfile.lock +130 -0
- data/History.txt +17 -0
- data/README.txt +0 -2
- data/Rakefile +17 -7
- data/app/controllers/absences_controller.rb +1 -2
- data/app/controllers/application_controller.rb +13 -14
- data/app/controllers/application_controller.rb.rails2 +186 -0
- data/app/controllers/estimates_controller.rb +1 -1
- data/app/controllers/groups_controller.rb +3 -1
- data/app/controllers/periods_controller.rb +61 -50
- data/app/controllers/{backlogs_controller.rb → projects_controller.rb} +35 -35
- data/app/controllers/search_controller.rb +2 -2
- data/app/controllers/tasks_controller.rb +11 -11
- data/app/controllers/user_controller.rb +6 -4
- data/app/controllers/welcome_controller.rb +4 -4
- data/app/controllers/work_locks_controller.rb +2 -2
- data/app/controllers/works_controller.rb +31 -31
- data/app/helpers/application_helper.rb +2 -2
- data/app/helpers/application_helper.rb.rails2 +118 -0
- data/app/helpers/periods_helper.rb +3 -3
- data/app/helpers/{backlogs_helper.rb → projects_helper.rb} +5 -5
- data/app/helpers/user_helper.rb +2 -2
- data/app/{models → mailers}/user_notify.rb +1 -0
- data/app/models/absence.rb +2 -2
- data/{lib → app/models}/clock.rb +0 -0
- data/app/models/estimate.rb +2 -2
- data/app/models/party.rb +1 -2
- data/app/models/period.rb +17 -18
- data/app/models/{backlog.rb → project.rb} +2 -2
- data/app/models/sidebar.rb +2 -2
- data/app/models/task.rb +28 -28
- data/app/models/user.rb +5 -4
- data/app/models/work.rb +29 -23
- data/app/models/work_lock_nagger.rb +1 -1
- data/app/models/works_report_filter.rb +4 -4
- data/app/views/customers/_name_list.rhtml +1 -1
- data/app/views/layouts/_headers.rhtml +2 -1
- data/app/views/layouts/_left_top.rhtml +32 -29
- data/app/views/layouts/_shortcuts.rhtml +15 -9
- data/app/views/layouts/_shortcuts_js.rhtml +4 -4
- data/app/views/layouts/mwrt002.html.erb +44 -0
- data/app/views/periods/_burn_down_chart.rhtml +1 -1
- data/app/views/periods/_show_active.rhtml +3 -3
- data/app/views/periods/_title.rhtml +1 -1
- data/app/views/{backlogs → projects}/_buttons.rhtml +4 -4
- data/app/views/projects/_form.rhtml +44 -0
- data/app/views/projects/_name_list.rhtml +5 -0
- data/app/views/projects/edit.rhtml +14 -0
- data/app/views/{backlogs → projects}/finish_task.rjs +0 -0
- data/app/views/projects/list.rhtml +16 -0
- data/app/views/{backlogs → projects}/move_task_to_period.rjs +0 -0
- data/app/views/{backlogs → projects}/new.rhtml +1 -1
- data/app/views/{backlogs → projects}/reopen_task.rjs +0 -0
- data/app/views/{backlogs → projects}/show.rhtml +6 -6
- data/app/views/search/results.rhtml +3 -3
- data/app/views/tasks/_backlog_header.rhtml +4 -4
- data/app/views/tasks/_completed.rhtml +2 -2
- data/app/views/tasks/_form.rhtml +13 -13
- data/app/views/tasks/_task.rhtml +10 -10
- data/app/views/tasks/edit.rhtml +1 -1
- data/app/views/tasks/list.rhtml +3 -3
- data/app/views/tasks/list_started.rhtml +4 -4
- data/app/views/tasks/start_work.rjs +1 -1
- data/app/views/user/login.rhtml +1 -1
- data/app/views/user/signup.rhtml +1 -1
- data/app/views/user/welcome.rhtml +1 -1
- data/app/views/works/_description_list.rhtml +1 -1
- data/app/views/works/_form.rhtml +5 -5
- data/app/views/works/_new_row.rhtml +8 -8
- data/app/views/works/_row.rhtml +1 -1
- data/app/views/works/_task_id_list.rhtml +1 -1
- data/app/views/works/daily_work_sheet.rhtml +1 -1
- data/app/views/works/list.rhtml +5 -5
- data/app/views/works/list_excel.rhtml +2 -2
- data/app/views/works/timeliste.rhtml +14 -14
- data/app/views/works/weekly_work_sheet.rhtml +5 -5
- data/app/views/works/weekly_work_sheet_details.rhtml +5 -5
- data/backlog.gemspec +44 -0
- data/bin/backlog +5 -1
- data/config.ru +4 -0
- data/config/application.rb +10 -0
- data/config/boot.rb +12 -116
- data/config/database.yml +3 -6
- data/config/database.yml~ +17 -0
- data/config/environment.rb +28 -49
- data/config/environments/development.rb +24 -20
- data/config/environments/development.rb.rails2 +26 -0
- data/config/environments/production.rb +26 -22
- data/config/environments/test.rb +20 -15
- data/config/environments/user_environment.rb +1 -1
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/inflections.rb +10 -0
- data/config/initializers/jdbc.rb +1 -1
- data/config/initializers/mime_types.rb +5 -0
- data/config/initializers/secret_token.rb +7 -0
- data/config/initializers/session_store.rb +8 -0
- data/config/locales/en.yml +22 -11
- data/config/locales/no.yml +1 -0
- data/config/routes.rb +4 -5
- data/cruise_build.sh +6 -2
- data/db/migrate/004_add_period.rb +22 -22
- data/db/migrate/015_add_user_option.rb +5 -19
- data/db/migrate/017_increase_backlog_name_limit.rb +10 -0
- data/db/migrate/021_create_work_accounts.rb +0 -2
- data/db/migrate/20101006092700_rename_backlogs_to_projects.rb +22 -0
- data/db/migrate/20101006092700_rename_backlogs_to_projects.rb~ +22 -0
- data/db/schema.rb +27 -30
- data/db/seeds.rb +7 -0
- data/lib/array_helper.rb +0 -8
- data/lib/class_table_inheritance.rb +8 -7
- data/lib/tasks/backup.rake +3 -3
- data/lib/tasks/jdbc.rake +2 -2
- data/lib/version_from_history.rb +1 -1
- data/public/404.html +23 -7
- data/public/422.html +26 -0
- data/public/500.html +23 -6
- data/public/images/rails.png +0 -0
- data/public/javascripts/controls.js +5 -3
- data/public/javascripts/dragdrop.js +7 -6
- data/public/javascripts/effects.js +8 -13
- data/public/javascripts/prototype.js +3381 -1700
- data/public/javascripts/rails.js +175 -0
- data/public/robots.txt +5 -1
- data/script/rails +6 -0
- data/test/client/login.rb +0 -2
- data/test/client/login_test.rb +1 -1
- data/test/client/setup.rb +25 -24
- data/test/fixtures/{backlogs.yml → projects.yml} +2 -2
- data/test/fixtures/tasks.yml +9 -9
- data/test/fixtures/work_lock_subscriptions.yml +2 -2
- data/test/fixtures/works.yml +7 -7
- data/test/functional/periods_controller_test.rb +1 -1
- data/test/functional/{backlogs_controller_test.rb → projects_controller_test.rb} +22 -21
- data/test/functional/search_controller_test.rb +1 -1
- data/test/functional/tasks_controller_test.rb +17 -17
- data/test/functional/user_controller_test.rb +16 -21
- data/test/functional/welcome_controller_test.rb +4 -3
- data/test/functional/works_controller_test.rb +5 -5
- data/test/integration/user_system_test.rb +1 -1
- data/test/mocks/test/clock.rb +1 -1
- data/test/performance/browsing_test.rb +9 -0
- data/test/performance/common.rb +1 -1
- data/test/test_helper.rb +23 -6
- data/test/test_helper.rb~ +121 -0
- data/test/unit/user_test.rb +3 -3
- data/test/unit/work_test.rb +7 -7
- data/vendor/plugins/assert_cookie/lib/assert_cookie.rb +0 -2
- data/vendor/plugins/{foreign_key_migrations → dynamic_form}/MIT-LICENSE +1 -1
- data/vendor/plugins/dynamic_form/README +13 -0
- data/vendor/plugins/dynamic_form/Rakefile +10 -0
- data/vendor/plugins/dynamic_form/dynamic_form.gemspec +12 -0
- data/vendor/plugins/dynamic_form/init.rb +1 -0
- data/vendor/plugins/dynamic_form/lib/action_view/helpers/dynamic_form.rb +300 -0
- data/vendor/plugins/dynamic_form/lib/action_view/locale/en.yml +8 -0
- data/vendor/plugins/dynamic_form/lib/dynamic_form.rb +5 -0
- data/vendor/plugins/dynamic_form/test/dynamic_form_i18n_test.rb +42 -0
- data/vendor/plugins/dynamic_form/test/dynamic_form_test.rb +370 -0
- data/vendor/plugins/dynamic_form/test/test_helper.rb +9 -0
- data/vendor/plugins/prototype_legacy_helper/lib/prototype_legacy_helper.rb +432 -0
- data/vendor/plugins/prototype_legacy_helper/test/test_prototype_helper.rb +297 -0
- data/vendor/plugins/rails_time/test/debug.log +1 -0
- data/vendor/plugins/{redhillonrails_core → verification}/MIT-LICENSE +1 -1
- data/vendor/plugins/verification/README +34 -0
- data/vendor/plugins/verification/Rakefile +22 -0
- data/vendor/plugins/verification/init.rb +3 -0
- data/vendor/plugins/verification/lib/action_controller/verification.rb +132 -0
- data/vendor/plugins/verification/test/test_helper.rb +18 -0
- data/vendor/plugins/verification/test/verification_test.rb +270 -0
- data/vendor/plugins/will_paginate/lib/will_paginate/collection.rb +1 -1
- metadata +115 -134
- data/Gemfile~ +0 -4
- data/History.txt~ +0 -961
- data/LICENSE_LOCALIZATION +0 -20
- data/README_LOCALIZATION +0 -61
- data/README_RAILS +0 -180
- data/app/views/backlogs/_form.rhtml +0 -44
- data/app/views/backlogs/_name_list.rhtml +0 -5
- data/app/views/backlogs/edit.rhtml +0 -14
- data/app/views/backlogs/list.rhtml +0 -16
- data/app/views/layouts/mwrt002.rhtml +0 -43
- data/config/initializers/mongrel.rb +0 -83
- data/config/preinitializer.rb +0 -20
- data/config/warble.rb~ +0 -84
- data/db/migrate/017_insert_datek_projects.rb +0 -98
- data/lib/change_column_null_migration_fix.rb +0 -15
- data/no_test.rb~ +0 -6
- data/public/dispatch.cgi +0 -10
- data/public/dispatch.fcgi +0 -24
- data/public/dispatch.rb +0 -10
- data/script/about +0 -3
- data/script/breakpointer +0 -3
- data/script/console +0 -3
- data/script/dbconsole +0 -3
- data/script/destroy +0 -3
- data/script/generate +0 -3
- data/script/performance/benchmarker +0 -3
- data/script/performance/profiler +0 -3
- data/script/plugin +0 -3
- data/script/process/inspector +0 -3
- data/script/process/reaper +0 -3
- data/script/process/spawner +0 -3
- data/script/runner +0 -3
- data/script/server +0 -3
- data/test/client/login.rb~ +0 -33
- data/test/mocks/test/user_notify.rb +0 -16
- data/vendor/plugins/foreign_key_migrations/CHANGELOG +0 -103
- data/vendor/plugins/foreign_key_migrations/README +0 -87
- data/vendor/plugins/foreign_key_migrations/about.yml +0 -5
- data/vendor/plugins/foreign_key_migrations/init.rb +0 -1
- data/vendor/plugins/foreign_key_migrations/install.rb +0 -1
- data/vendor/plugins/foreign_key_migrations/lib/foreign_key_migrations.rb +0 -3
- data/vendor/plugins/foreign_key_migrations/lib/red_hill_consulting/foreign_key_migrations/active_record/base.rb +0 -22
- data/vendor/plugins/foreign_key_migrations/lib/red_hill_consulting/foreign_key_migrations/active_record/connection_adapters/abstract_adapter.rb +0 -22
- data/vendor/plugins/foreign_key_migrations/lib/red_hill_consulting/foreign_key_migrations/active_record/connection_adapters/table_definition.rb +0 -28
- data/vendor/plugins/lightwindow_helper/README +0 -33
- data/vendor/plugins/lightwindow_helper/assets/images/ajax-loading.gif +0 -0
- data/vendor/plugins/lightwindow_helper/assets/images/arrow-down.gif +0 -0
- data/vendor/plugins/lightwindow_helper/assets/images/arrow-up.gif +0 -0
- data/vendor/plugins/lightwindow_helper/assets/images/black-70.png +0 -0
- data/vendor/plugins/lightwindow_helper/assets/images/black.png +0 -0
- data/vendor/plugins/lightwindow_helper/assets/images/nextlabel.gif +0 -0
- data/vendor/plugins/lightwindow_helper/assets/images/prevlabel.gif +0 -0
- data/vendor/plugins/lightwindow_helper/assets/javascripts/lightwindow.js +0 -1921
- data/vendor/plugins/lightwindow_helper/assets/stylesheets/lightwindow.css +0 -376
- data/vendor/plugins/lightwindow_helper/init.rb +0 -1
- data/vendor/plugins/lightwindow_helper/install.rb +0 -7
- data/vendor/plugins/lightwindow_helper/lib/lightwindow_helper.rb +0 -31
- data/vendor/plugins/redhillonrails_core/CHANGELOG +0 -150
- data/vendor/plugins/redhillonrails_core/README +0 -124
- data/vendor/plugins/redhillonrails_core/init.rb +0 -19
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/base.rb +0 -54
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/abstract_adapter.rb +0 -31
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/column.rb +0 -21
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/foreign_key_definition.rb +0 -26
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/index_definition.rb +0 -11
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/mysql_adapter.rb +0 -74
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/mysql_column.rb +0 -8
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/postgresql_adapter.rb +0 -99
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/schema_statements.rb +0 -16
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/sqlite3_adapter.rb +0 -9
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/connection_adapters/table_definition.rb +0 -27
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/schema.rb +0 -27
- data/vendor/plugins/redhillonrails_core/lib/red_hill_consulting/core/active_record/schema_dumper.rb +0 -47
- data/vendor/plugins/transactional_migrations/CHANGELOG +0 -9
- data/vendor/plugins/transactional_migrations/MIT-LICENSE +0 -20
- data/vendor/plugins/transactional_migrations/README +0 -15
- data/vendor/plugins/transactional_migrations/about.yml +0 -5
- data/vendor/plugins/transactional_migrations/init.rb +0 -1
- data/vendor/plugins/transactional_migrations/lib/red_hill_consulting/transactional_migrations/active_record/migration.rb +0 -19
@@ -21,7 +21,7 @@ module PeriodsHelper
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def add_active_task(page)
|
24
|
-
page.insert_html(:top, "active_tasks_#{@task.period.id}", render(:partial => "/tasks/task", :locals => { :task => @task, :i => 1, :active => true, :highlight_task => false, :update => :spotlight, :hidden => true, :
|
24
|
+
page.insert_html(:top, "active_tasks_#{@task.period.id}", render(:partial => "/tasks/task", :locals => { :task => @task, :i => 1, :active => true, :highlight_task => false, :update => :spotlight, :hidden => true, :show_project => false }))
|
25
25
|
|
26
26
|
page[:no_tasks_message].hide
|
27
27
|
|
@@ -43,9 +43,9 @@ module PeriodsHelper
|
|
43
43
|
period_tag_id = "completed_period_#{@task.period_id}"
|
44
44
|
page.insert_html :top, "completed_tasks", %Q{<div id="#{period_tag_id}" />}
|
45
45
|
page.insert_html :top, period_tag_id, %Q{<ul id="#{list_tag_id}" class="task_list"/>}
|
46
|
-
page.insert_html :top, period_tag_id, :partial => '/tasks/fields_header', :locals => {:
|
46
|
+
page.insert_html :top, period_tag_id, :partial => '/tasks/fields_header', :locals => {:project => @task.project, :active => false, :work_done => work_done}
|
47
47
|
end
|
48
|
-
page.insert_html :top, list_tag_id, :partial => '/tasks/task', :locals => {:active => false, :hidden => true, :highlight_task => false, :
|
48
|
+
page.insert_html :top, list_tag_id, :partial => '/tasks/task', :locals => {:active => false, :hidden => true, :highlight_task => false, :show_project => true}
|
49
49
|
page.visual_effect :appear, "task_#{@task.id}"
|
50
50
|
|
51
51
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module ProjectsHelper
|
2
2
|
def remove_active_task(page)
|
3
3
|
page.visual_effect :blind_up, "task_#{@task.id}"
|
4
4
|
last_in_period = @tasks.find {|t| t.period_id == @task.period_id}.nil?
|
@@ -23,11 +23,11 @@ module BacklogsHelper
|
|
23
23
|
period_tag_id = "completed_period_#{@task.period_id}"
|
24
24
|
page.insert_html :top, :completed_tasks, "<div id=\"#{period_tag_id}\ />"
|
25
25
|
page.insert_html :top, period_tag_id, "<ul id=\"#{list_tag_id}\" class=\"task_list\" />"
|
26
|
-
page.insert_html :top, period_tag_id, :partial => '/tasks/fields_header', :locals => {:
|
26
|
+
page.insert_html :top, period_tag_id, :partial => '/tasks/fields_header', :locals => {:project => @task.project, :active => false, :work_done => work_done}
|
27
27
|
page.insert_html :top, period_tag_id, :partial => '/tasks/period_header', :locals => {:period => @task.period}
|
28
28
|
end
|
29
29
|
|
30
|
-
page.insert_html :top, list_tag_id, :partial => '/tasks/task', :locals => {:active => false, :hidden => true, :highlight_task => false, :
|
30
|
+
page.insert_html :top, list_tag_id, :partial => '/tasks/task', :locals => {:task => @task, :active => false, :hidden => true, :highlight_task => false, :show_project => false}
|
31
31
|
page.visual_effect :appear, "task_#{@task.id}"
|
32
32
|
end
|
33
33
|
|
@@ -55,12 +55,12 @@ module BacklogsHelper
|
|
55
55
|
page.insert_html :after, "active_period_#{@task.period.higher_item.id}", period_tag
|
56
56
|
end
|
57
57
|
page.insert_html :top, period_tag_id, %Q{<ul id="#{list_tag_id}" class="task_list" style="width: 100%;"/>}
|
58
|
-
fields_header = render(:partial => '/tasks/fields_header', :locals => { :
|
58
|
+
fields_header = render(:partial => '/tasks/fields_header', :locals => { :project => @task.project, :active => true, :track_todo => @task.project.track_todo?, :track_done => @task.project.track_done?, :track_times => tasks_in_period.find {|t|t.period && t.period.active?} } )
|
59
59
|
page.insert_html :top, period_tag_id, fields_header
|
60
60
|
page.insert_html :top, period_tag_id, render(:partial => "/tasks/period_header", :locals => {:period => @task.period})
|
61
61
|
end
|
62
62
|
|
63
|
-
page.insert_html(:top, list_tag_id, render(:partial => "/tasks/task", :locals => { :task => @task, :i => 1, :active => true, :highlight_task => false, :update => :spotlight, :hidden => true, :
|
63
|
+
page.insert_html(:top, list_tag_id, render(:partial => "/tasks/task", :locals => { :task => @task, :i => 1, :active => true, :highlight_task => false, :update => :spotlight, :hidden => true, :show_project => false }))
|
64
64
|
record page, sortable_element_js(list_tag_id, :url => { :action => :order }, :containment => @tasks.map{|t| t.period_id}.uniq.map{|p| "active_tasks_#{p}"}, :constraint => false)
|
65
65
|
|
66
66
|
page[:no_tasks_message].hide
|
data/app/helpers/user_helper.rb
CHANGED
@@ -7,7 +7,7 @@ module UserHelper
|
|
7
7
|
}.freeze unless defined? DEFAULT_HEAD_OPTIONS
|
8
8
|
|
9
9
|
def title_helper
|
10
|
-
"#{controller.
|
10
|
+
"#{controller.class.name} #{controller.action_name}"
|
11
11
|
end
|
12
12
|
|
13
13
|
def head_helper(label, options = {})
|
@@ -35,7 +35,7 @@ module UserHelper
|
|
35
35
|
|
36
36
|
def start_form_tag_helper(options = {})
|
37
37
|
url = url_for(:action => "#{controller.action_name}")
|
38
|
-
"#{self.send(:form_tag, url, options)}"
|
38
|
+
"#{self.send(:form_tag, url, options)}".html_safe
|
39
39
|
end
|
40
40
|
|
41
41
|
def user_id
|
data/app/models/absence.rb
CHANGED
@@ -7,8 +7,8 @@ class Absence < ActiveRecord::Base
|
|
7
7
|
|
8
8
|
validates_uniqueness_of :on, :scope => :user_id
|
9
9
|
|
10
|
-
|
11
|
-
if PublicHoliday.exists? :on => on
|
10
|
+
validate do |a|
|
11
|
+
if PublicHoliday.exists? :on => a.on
|
12
12
|
errors.add :on, "You cannot mark absence on a public holiday."
|
13
13
|
end
|
14
14
|
end
|
data/{lib → app/models}/clock.rb
RENAMED
File without changes
|
data/app/models/estimate.rb
CHANGED
@@ -10,8 +10,8 @@ class Estimate < ActiveRecord::Base
|
|
10
10
|
validates_associated :task
|
11
11
|
validates_presence_of :todo
|
12
12
|
|
13
|
-
|
14
|
-
task.finish(Task::COMPLETED, false) if todo == 0
|
13
|
+
after_create do |e|
|
14
|
+
e.task.finish(Task::COMPLETED, false) if todo == 0
|
15
15
|
end
|
16
16
|
|
17
17
|
end
|
data/app/models/party.rb
CHANGED
data/app/models/period.rb
CHANGED
@@ -8,9 +8,7 @@ class Period < ActiveRecord::Base
|
|
8
8
|
validates_associated :party
|
9
9
|
validates_uniqueness_of :position, :scope => :party_id
|
10
10
|
|
11
|
-
|
12
|
-
errors.add "A period cannot end before it starts" unless end_on >= start_on
|
13
|
-
end
|
11
|
+
validate { |p| p.errors.add "A period cannot end before it starts" unless p.end_on >= p.start_on }
|
14
12
|
|
15
13
|
def self.find_active_or_future(check_tasks = false)
|
16
14
|
periods = find(:all, :conditions => "end_on >= '#{Date.today.strftime '%Y-%m-%d'}'")
|
@@ -36,15 +34,15 @@ class Period < ActiveRecord::Base
|
|
36
34
|
tasks.select {|task| task.active?}
|
37
35
|
end
|
38
36
|
|
39
|
-
def
|
40
|
-
|
41
|
-
return nil if
|
37
|
+
def most_frequent_project
|
38
|
+
all_projects = tasks.map {|t| t.project}.compact
|
39
|
+
return nil if all_projects.empty?
|
42
40
|
freq = {}
|
43
|
-
|
41
|
+
all_projects.each do |b|
|
44
42
|
freq[b.id] ||= 0
|
45
43
|
freq[b.id] += 1
|
46
44
|
end
|
47
|
-
freq.to_a.sort_by {|
|
45
|
+
freq.to_a.sort_by {|project, count| -count}.first[0]
|
48
46
|
end
|
49
47
|
|
50
48
|
def active?(check_tasks = false)
|
@@ -133,7 +131,7 @@ class Period < ActiveRecord::Base
|
|
133
131
|
last = first + 6
|
134
132
|
works = tasks.map {|t| t.works_with_children}.flatten
|
135
133
|
works.reject! {|work| work.hours == 0} unless with_empty_works
|
136
|
-
works.reject! {|work| work.user_id != user_id} if user_id &&
|
134
|
+
works.reject! {|work| work.user_id != user_id} if user_id && project.enable_users
|
137
135
|
by_day = (0..6).map do |i|
|
138
136
|
works.select {|w| w.completed_at && w.completed_at.to_date == first + i}.sort do |w1, w2|
|
139
137
|
if w1.completed_at != w2.completed_at
|
@@ -154,31 +152,32 @@ class Period < ActiveRecord::Base
|
|
154
152
|
end
|
155
153
|
|
156
154
|
def estimates?
|
157
|
-
not
|
155
|
+
not projects.find {|project| project.track_todo?}.nil?
|
158
156
|
end
|
159
157
|
|
160
158
|
def track_times?
|
161
|
-
not
|
159
|
+
not projects.find {|project| project.track_times?}.nil?
|
162
160
|
end
|
163
161
|
|
164
162
|
def enable_subtasks?
|
165
|
-
not
|
163
|
+
not projects.find {|project| project.enable_subtasks?}.nil?
|
166
164
|
end
|
167
165
|
|
168
166
|
def invoice?
|
169
|
-
not
|
167
|
+
not projects.find {|project| project.enable_invoicing?}.nil?
|
170
168
|
end
|
171
169
|
|
172
|
-
def
|
173
|
-
tasks.map {|task| task.
|
170
|
+
def projects
|
171
|
+
tasks.map {|task| task.project}.uniq
|
174
172
|
end
|
175
173
|
|
176
174
|
def burn_down_graph(size)
|
177
175
|
begin
|
178
176
|
require 'gruff'
|
179
|
-
|
180
|
-
|
181
|
-
return File.read(
|
177
|
+
rescue MissingSourceFile => e
|
178
|
+
matching_file_name = Rails.root.to_s + "/public/images/rmagick_#{size}.gif"
|
179
|
+
return File.read(matching_file_name) if File.exists? matching_file_name
|
180
|
+
return File.read(Rails.root.to_s + "/public/images/rmagick.gif")
|
182
181
|
end
|
183
182
|
g = Gruff::Line.new(size)
|
184
183
|
g.theme_37signals
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class
|
1
|
+
class Project < ActiveRecord::Base
|
2
2
|
validates_presence_of :name
|
3
3
|
validates_length_of :name, :allow_nil => false, :maximum => 64
|
4
4
|
validates_uniqueness_of :name
|
@@ -11,7 +11,7 @@ class Backlog < ActiveRecord::Base
|
|
11
11
|
has_many :tasks, :order => 'period_id, position', :dependent => :destroy
|
12
12
|
has_many :unplanned_tasks, :class_name => 'Task', :conditions => 'period_id IS NULL', :order => 'position'
|
13
13
|
has_many :works, :dependent => :destroy
|
14
|
-
has_and_belongs_to_many :work_lock_subscribers, :class_name => 'User', :join_table => "work_lock_subscriptions", :foreign_key => "
|
14
|
+
has_and_belongs_to_many :work_lock_subscribers, :class_name => 'User', :join_table => "work_lock_subscriptions", :foreign_key => "project_id", :association_foreign_key => 'subscriber_user_id'
|
15
15
|
|
16
16
|
validates_inclusion_of :track_times, :in => [true, false], :message => I18n.translate('activerecord.errors.messages')[:blank]
|
17
17
|
|
data/app/models/sidebar.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'action_view/helpers/url_helper'
|
2
2
|
|
3
3
|
class Sidebar
|
4
|
-
include
|
4
|
+
include Rails.application.routes.url_helpers
|
5
5
|
include ActionView::Helpers::TagHelper
|
6
6
|
include ERB::Util
|
7
7
|
|
@@ -28,7 +28,7 @@ class Sidebar
|
|
28
28
|
started_tasks = Task.find_started
|
29
29
|
if not started_tasks.empty?
|
30
30
|
links = started_tasks.map do |task|
|
31
|
-
"<li><a href=\"#{url_for :controller => 'tasks', :action =>
|
31
|
+
"<li><a href=\"#{url_for(:host => @controller.request.host_with_port, :controller => 'tasks', :action => 'list_started', :id => task.id)}\">#{task.description}</a></li>"
|
32
32
|
end
|
33
33
|
blocks.unshift({ :title => @controller.l(:started_tasks),
|
34
34
|
:options => {:controller => 'tasks', :action => :list_started},
|
data/app/models/task.rb
CHANGED
@@ -9,7 +9,7 @@ class Task < ActiveRecord::Base
|
|
9
9
|
MOVED = 'MOVED'
|
10
10
|
ABORTED = 'ABORTED'
|
11
11
|
|
12
|
-
belongs_to :
|
12
|
+
belongs_to :project
|
13
13
|
belongs_to :period
|
14
14
|
belongs_to :ancestor_task, :class_name => 'Task', :foreign_key => 'previous_task_id'
|
15
15
|
belongs_to :customer
|
@@ -21,33 +21,33 @@ class Task < ActiveRecord::Base
|
|
21
21
|
belongs_to :creator, :class_name => 'User', :foreign_key => 'created_by'
|
22
22
|
belongs_to :updater, :class_name => 'User', :foreign_key => 'updated_by'
|
23
23
|
|
24
|
-
acts_as_list :scope => '#{parent_id ? "parent_id = #{parent_id}" : period_id ? "period_id = #{period_id}" : "period_id IS NULL AND parent_id IS NULL AND
|
24
|
+
acts_as_list :scope => '#{parent_id ? "parent_id = #{parent_id}" : period_id ? "period_id = #{period_id}" : "period_id IS NULL AND parent_id IS NULL AND project_id = #{project_id}"} AND finished_at IS NULL'
|
25
25
|
acts_as_tree :order => 'position'
|
26
26
|
|
27
|
-
validates_presence_of :
|
28
|
-
validates_presence_of :parent_id, :if => Proc.new { |task| task.
|
27
|
+
validates_presence_of :project_id, :if => Proc.new { |task| task.parent_id.nil? }
|
28
|
+
validates_presence_of :parent_id, :if => Proc.new { |task| task.project_id.nil? }
|
29
29
|
validates_presence_of :position, :on => :update, :if => Proc.new { |task| task.finished_at.nil? }
|
30
30
|
validates_presence_of :finished_at, :on => :update, :if => Proc.new { |task| task.position.nil? }
|
31
31
|
validates_presence_of :resolution, :if => :finished_at
|
32
|
-
validates_presence_of :description, :if => :
|
32
|
+
validates_presence_of :description, :if => :project_id
|
33
33
|
|
34
34
|
validates_size_of :description, :maximum => 80, :if => :description
|
35
35
|
|
36
|
-
#validates_uniqueness_of :description, :scope => :
|
36
|
+
#validates_uniqueness_of :description, :scope => :project_id, :if => Proc.new {|task| task.project_id && task.previous_task_id.nil?}
|
37
37
|
validates_uniqueness_of :description, :scope => :period_id, :if => :period_id
|
38
38
|
validates_uniqueness_of :position, :scope => :parent_id, :if => :parent_id, :allow_nil => true
|
39
39
|
validates_uniqueness_of :position, :scope => :period_id, :if => :period_id, :allow_nil => true
|
40
|
-
validates_uniqueness_of :position, :scope => [:period_id, :parent_id, :
|
40
|
+
validates_uniqueness_of :position, :scope => [:period_id, :parent_id, :project_id], :if => Proc.new {|task| task.period_id.nil? && task.parent_id.nil?}, :allow_nil => true
|
41
41
|
|
42
42
|
before_create {|record| record.created_by = current_user.id}
|
43
43
|
before_save {|record| record.updated_by = current_user.id}
|
44
44
|
|
45
|
-
|
46
|
-
if
|
47
|
-
errors.add :parent_id, "A subtask may have neither period nor
|
45
|
+
validate do |r|
|
46
|
+
if r.parent_id && (r.period_id || r.project_id)
|
47
|
+
r.errors.add :parent_id, "A subtask may have neither period nor project set."
|
48
48
|
end
|
49
|
-
if new_record? &&
|
50
|
-
errors.add :period_id, "You may not add a task to a past period."
|
49
|
+
if r.new_record? && r.period && r.period.passed?
|
50
|
+
r.errors.add :period_id, "You may not add a task to a past period."
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -61,7 +61,7 @@ class Task < ActiveRecord::Base
|
|
61
61
|
end
|
62
62
|
conditions = "start_time IS NOT NULL AND completed_at IS NULL AND (user_id IS NULL#{user_clause})"
|
63
63
|
Work.find(:all, :conditions => conditions).map {|work| work.task}.compact.sort_by do |t|
|
64
|
-
[t.root_task.
|
64
|
+
[t.root_task.project.name, t.root_task.period ? t.root_task.period.end_on : 0, t.position || 0]
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -173,7 +173,7 @@ class Task < ActiveRecord::Base
|
|
173
173
|
end
|
174
174
|
existing_task = Task.find_descendant(new_period && new_period.id, ancestor_id)
|
175
175
|
if existing_task ||= Task.find_by_period_id_and_previous_task_id(new_period && new_period.id, ancestor_id)
|
176
|
-
raise "mismatch" unless existing_task.
|
176
|
+
raise "mismatch" unless existing_task.project == root_task.project
|
177
177
|
raise "Mismatch" unless existing_task.period == new_period
|
178
178
|
existing_task.open
|
179
179
|
existing_task.previous_task_id = self.previous_task_id || self.id
|
@@ -189,7 +189,7 @@ class Task < ActiveRecord::Base
|
|
189
189
|
new_parent = Task.find_descendant(new_period.id, parent.ancestor_id)
|
190
190
|
new_task.parent = new_parent
|
191
191
|
else
|
192
|
-
new_task.
|
192
|
+
new_task.project = root_task.project
|
193
193
|
new_task.period = new_period
|
194
194
|
end
|
195
195
|
new_task.description = self.description
|
@@ -279,35 +279,35 @@ class Task < ActiveRecord::Base
|
|
279
279
|
end
|
280
280
|
|
281
281
|
def invoicing?
|
282
|
-
root_task.
|
282
|
+
root_task.project.enable_invoicing?
|
283
283
|
end
|
284
284
|
|
285
285
|
def track_done?
|
286
|
-
|
286
|
+
project.track_done?
|
287
287
|
end
|
288
288
|
|
289
289
|
def track_times?
|
290
|
-
|
290
|
+
project.try :track_times?
|
291
291
|
end
|
292
292
|
|
293
293
|
def track_todo?
|
294
|
-
root_task.
|
294
|
+
root_task.project.track_todo?
|
295
295
|
end
|
296
296
|
|
297
297
|
def enable_subtasks?
|
298
|
-
root_task.
|
298
|
+
root_task.project.enable_subtasks?
|
299
299
|
end
|
300
300
|
|
301
301
|
def enable_users?
|
302
|
-
root_task.
|
302
|
+
root_task.project.enable_users?
|
303
303
|
end
|
304
304
|
|
305
305
|
def enable_customer?
|
306
|
-
root_task.
|
306
|
+
root_task.project.enable_customer?
|
307
307
|
end
|
308
308
|
|
309
309
|
def enable_invoicing?
|
310
|
-
root_task.
|
310
|
+
root_task.project.enable_invoicing?
|
311
311
|
end
|
312
312
|
|
313
313
|
alias_method :old_period, :period
|
@@ -315,9 +315,9 @@ class Task < ActiveRecord::Base
|
|
315
315
|
old_period || root_task.old_period
|
316
316
|
end
|
317
317
|
|
318
|
-
alias_method :
|
319
|
-
def
|
320
|
-
|
318
|
+
alias_method :old_project, :project
|
319
|
+
def project
|
320
|
+
old_project || root_task.old_project
|
321
321
|
end
|
322
322
|
|
323
323
|
def depth
|
@@ -380,7 +380,7 @@ class Task < ActiveRecord::Base
|
|
380
380
|
end
|
381
381
|
|
382
382
|
new_work.user = current_user
|
383
|
-
new_work.
|
383
|
+
new_work.project_id = project.id
|
384
384
|
new_work.save!
|
385
385
|
works << new_work
|
386
386
|
end
|
@@ -412,7 +412,7 @@ class Task < ActiveRecord::Base
|
|
412
412
|
|
413
413
|
def description_with_parents
|
414
414
|
if parent.nil?
|
415
|
-
"#{
|
415
|
+
"#{project.name}: #{description}"
|
416
416
|
else
|
417
417
|
"#{parent.description_with_parents} - #{description}"
|
418
418
|
end
|
data/app/models/user.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'digest/sha1'
|
2
|
+
require 'clock'
|
2
3
|
|
3
4
|
# this model expects a certain database layout and its based on the name/login pattern.
|
4
5
|
#class User < ActiveRecord::Base
|
@@ -124,15 +125,15 @@ class User < Party
|
|
124
125
|
|
125
126
|
def crypt_password
|
126
127
|
if @password_needs_confirmation
|
127
|
-
|
128
|
-
|
128
|
+
self[:salt] = self.class.hashed("salt-#{Clock.now}")
|
129
|
+
self[:salted_password] = self.class.salted_password(salt, self.class.hashed(@password))
|
129
130
|
end
|
130
131
|
end
|
131
132
|
|
132
133
|
def new_security_token
|
133
134
|
expiry = Time.at(Clock.now.to_i + self.class.token_lifetime)
|
134
|
-
|
135
|
-
|
135
|
+
self[:security_token] = self.class.hashed(self.salted_password + Clock.now.to_i.to_s + rand.to_s)
|
136
|
+
self[:token_expiry] = expiry
|
136
137
|
# update_without_callbacks
|
137
138
|
update
|
138
139
|
return self.security_token
|
data/app/models/work.rb
CHANGED
@@ -4,11 +4,11 @@ class Work < ActiveRecord::Base
|
|
4
4
|
|
5
5
|
belongs_to :task
|
6
6
|
belongs_to :user
|
7
|
-
belongs_to :
|
7
|
+
belongs_to :project
|
8
8
|
belongs_to :customer
|
9
9
|
|
10
10
|
validates_associated :task
|
11
|
-
validates_presence_of :
|
11
|
+
validates_presence_of :project
|
12
12
|
validates_presence_of :started_on
|
13
13
|
validates_presence_of :start_time, :if => :track_times?
|
14
14
|
|
@@ -18,7 +18,7 @@ class Work < ActiveRecord::Base
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def track_times?
|
21
|
-
|
21
|
+
project && project.track_times?
|
22
22
|
end
|
23
23
|
|
24
24
|
# Return an array with an array of works per day:
|
@@ -44,53 +44,53 @@ class Work < ActiveRecord::Base
|
|
44
44
|
|
45
45
|
# Return a hash with work accounts as keys an array of work hours totals per day as values:
|
46
46
|
# {
|
47
|
-
# <#
|
48
|
-
# <#
|
49
|
-
# <#
|
47
|
+
# <#Project#1> => [ 8, 7, 9, 12, 4, nil, nil],
|
48
|
+
# <#Project#2> => [nil, 1, nil, nil, 4, nil, nil],
|
49
|
+
# <#Project#3> => [nil, nil, nil, nil, nil, 4, 3],
|
50
50
|
# ]
|
51
|
-
def self.
|
51
|
+
def self.works_for_week_by_project(year, week_no, user = current_user)
|
52
52
|
first_date = Date.commercial(year, week_no, 1)
|
53
53
|
last_date = first_date + 6
|
54
54
|
works = find(:all, :conditions => "user_id #{user ? " = #{user.id}" : "IS NULL"} AND started_on BETWEEN '#{first_date}' AND '#{last_date}'", :order => 'completed_at, started_on, start_time')
|
55
55
|
result = {}
|
56
56
|
works.each do |work|
|
57
57
|
day_of_week = work.started_on.cwday - 1
|
58
|
-
result[work.
|
59
|
-
result[work.
|
60
|
-
result[work.
|
58
|
+
result[work.project] ||= []
|
59
|
+
result[work.project][day_of_week] ||= BigDecimal('0')
|
60
|
+
result[work.project][day_of_week] += work.hours
|
61
61
|
end
|
62
|
-
result.values.each {|
|
63
|
-
result.values.each {|
|
62
|
+
result.values.each {|project_totals| project_totals[6] ||= nil}
|
63
|
+
result.values.each {|project_totals| (0..6).each {|i| project_totals[i] = nil if project_totals[i] == 0}}
|
64
64
|
result
|
65
65
|
end
|
66
66
|
|
67
67
|
# Return a hash with an array of work totals per day:
|
68
68
|
# {
|
69
|
-
#
|
70
|
-
#
|
69
|
+
# project1.id => [[m, t, w, t, f, s, s], [m, t, w, t, f, s, s]],
|
70
|
+
# project2.id => [<work for monday>, 0, <work for wednesday>, <work for thursday>, <work for friday>, 0, <work for sunday>]
|
71
71
|
# }
|
72
72
|
def self.work_totals_for_week(year, week_no, user = current_user)
|
73
73
|
first = Date.commercial(year, week_no, 1)
|
74
74
|
last = first + 6
|
75
75
|
works = find(:all, :conditions => "completed_at IS NOT NULL AND started_on BETWEEN '#{first}' AND '#{last}'", :order => 'started_on, start_time, completed_at')
|
76
|
-
|
77
|
-
works.map{|w| w.
|
78
|
-
|
76
|
+
totals_per_project = {}
|
77
|
+
works.map{|w| w.project}.uniq.each do |project|
|
78
|
+
totals_per_project[project.id] = [[], []]
|
79
79
|
(0..6).each do |day|
|
80
|
-
works_for_day = works.select {|work| (work.
|
80
|
+
works_for_day = works.select {|work| (work.project == project) && (work.started_on == (first + day)) && ((user.nil? && work.user_id.nil?) || (user && work.user_id == user.id)) }
|
81
81
|
invoice_works_for_day = works_for_day.select {|work| work.invoice? }
|
82
82
|
internal_works_for_day = works_for_day.select {|work| !work.invoice? }
|
83
83
|
|
84
84
|
invoice_day_total = invoice_works_for_day.reduce(BigDecimal('0')){|total, work| total += work.hours}
|
85
85
|
internal_day_total = internal_works_for_day.reduce(BigDecimal('0')){|total, work| total += work.hours}
|
86
|
-
|
87
|
-
|
86
|
+
totals_per_project[project.id][0] << invoice_day_total
|
87
|
+
totals_per_project[project.id][1] << internal_day_total
|
88
88
|
end
|
89
89
|
end
|
90
|
-
|
90
|
+
totals_per_project.reject! do |project_id, day_totals|
|
91
91
|
!day_totals[0].find{|day_total| day_total > 0} && !day_totals[1].find{|day_total| day_total > 0}
|
92
92
|
end
|
93
|
-
|
93
|
+
totals_per_project
|
94
94
|
end
|
95
95
|
|
96
96
|
def self.find_work_for_day(date, user = current_user)
|
@@ -157,6 +157,12 @@ class Work < ActiveRecord::Base
|
|
157
157
|
self.started_on = Date.new(t.year, t.month, t.day)
|
158
158
|
self.start_time = TimeOfDay.new(t.hour, t.min)
|
159
159
|
end
|
160
|
+
|
161
|
+
def started_on=(new_value)
|
162
|
+
super
|
163
|
+
self.completed_at_time = completed_at_time.to_s if self.started_on_was.nil?
|
164
|
+
self.started_on
|
165
|
+
end
|
160
166
|
|
161
167
|
def completed_at_time
|
162
168
|
completed_at && completed_at.time_of_day
|
@@ -169,7 +175,7 @@ class Work < ActiveRecord::Base
|
|
169
175
|
return
|
170
176
|
end
|
171
177
|
time_of_day = TimeOfDay.parse(new_value)
|
172
|
-
date = started_on || completed_at.to_date || Date.today
|
178
|
+
date = started_on || completed_at.try(:to_date) || Date.today
|
173
179
|
self.completed_at = date.at time_of_day
|
174
180
|
end
|
175
181
|
|