backlog 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. data/History.txt +19 -0
  2. data/app/controllers/periods_controller.rb +0 -14
  3. data/app/controllers/tasks_controller.rb +15 -4
  4. data/app/controllers/user_controller.rb +15 -1
  5. data/app/controllers/work_accounts_controller.rb +1 -1
  6. data/app/controllers/work_locks_controller.rb +88 -0
  7. data/app/controllers/works_controller.rb +29 -5
  8. data/app/helpers/work_locks_helper.rb +2 -0
  9. data/app/models/task.rb +1 -1
  10. data/app/models/user.rb +1 -0
  11. data/app/models/user_notify.rb +15 -0
  12. data/app/models/work.rb +28 -6
  13. data/app/models/work_account.rb +2 -0
  14. data/app/models/work_lock.rb +3 -0
  15. data/app/models/work_lock_notify.rb +27 -0
  16. data/app/models/work_lock_subscription.rb +3 -0
  17. data/app/views/display_notice.rjs +1 -0
  18. data/app/views/layouts/_left_top.rhtml +1 -0
  19. data/app/views/tasks/_task.rhtml +2 -2
  20. data/app/views/user/edit.rhtml +15 -2
  21. data/app/views/user/toggle_work_lock_monitoring.rjs +2 -0
  22. data/app/views/user_notify/monitoring_en.rhtml +3 -0
  23. data/app/views/user_notify/monitoring_no.rhtml +3 -0
  24. data/app/views/work_lock_notify/lock_en.rhtml +7 -0
  25. data/app/views/work_lock_notify/lock_no.rhtml +7 -0
  26. data/app/views/work_locks/_form.rhtml +10 -0
  27. data/app/views/work_locks/edit.rhtml +9 -0
  28. data/app/views/work_locks/list.rhtml +27 -0
  29. data/app/views/work_locks/new.rhtml +8 -0
  30. data/app/views/work_locks/show.rhtml +8 -0
  31. data/app/views/works/_buttons.rhtml +5 -0
  32. data/app/views/works/_form.rhtml +4 -1
  33. data/app/views/works/_row.rhtml +14 -17
  34. data/app/views/works/_row_field.rhtml +1 -1
  35. data/app/views/works/daily_work_sheet.rhtml +16 -25
  36. data/app/views/works/timeliste.rhtml +12 -12
  37. data/app/views/works/update_row.rjs +2 -4
  38. data/app/views/works/weekly_work_sheet.rhtml +17 -17
  39. data/app/views/works/weekly_work_sheet_by_work_account.rhtml +57 -0
  40. data/db/migrate/027_create_work_locks.rb +25 -0
  41. data/db/schema.rb +25 -1
  42. data/public/images/delete.png +0 -0
  43. data/public/images/email.png +0 -0
  44. data/public/images/email_grey.png +0 -0
  45. data/public/images/refresh.png +0 -0
  46. data/test/fixtures/work_lock_subscriptions.yml +7 -0
  47. data/test/fixtures/work_locks.yml +11 -0
  48. data/test/functional/estimates_controller_test.rb +1 -1
  49. data/test/functional/groups_controller_test.rb +1 -1
  50. data/test/functional/user_controller_test.rb +2 -2
  51. data/test/functional/work_locks_controller_test.rb +93 -0
  52. data/test/integration/user_system_test.rb +1 -1
  53. data/test/test_helper.rb +1 -1
  54. data/test/unit/estimate_test.rb +1 -1
  55. data/test/unit/group_test.rb +1 -1
  56. data/test/unit/period_test.rb +1 -1
  57. data/test/unit/task_test.rb +1 -1
  58. data/test/unit/user_test.rb +1 -1
  59. data/test/unit/work_account_test.rb +1 -1
  60. data/test/unit/work_lock_subscription_test.rb +10 -0
  61. data/test/unit/work_lock_test.rb +10 -0
  62. data/test/unit/work_test.rb +1 -1
  63. metadata +31 -2
data/History.txt CHANGED
@@ -1,3 +1,21 @@
1
+ == 0.19.0 2008-01-23
2
+
3
+ === Features
4
+
5
+ * Added button to recalculate the hours when changing start/stop times in the create/edit work record view.
6
+ * Added locking of work records, and notification.
7
+ * Added work accounts list link to navigation top.
8
+ * Changed weekly work sheet to show totals instead of individual work records.
9
+ Added links in the weekly work sheet to each day for details.
10
+ * Improved daily work sheet. Maybe usable now.
11
+
12
+ === Fixes
13
+
14
+ * Fixed bug when grabbing/releasing a task and then starting and stopping work.
15
+ This would render javascript in cleartext.
16
+ * Changed redirect after login from displaying backlog to welcome view.
17
+ * Fixed Excel export.
18
+
1
19
  == 0.18.0 2008-01-21
2
20
 
3
21
  === Features
@@ -11,6 +29,7 @@
11
29
  === Fixes
12
30
 
13
31
  * Updated graphs in README.txt
32
+ * Sorted work account list by name.
14
33
 
15
34
  == 0.17.6 2008-01-19
16
35
 
@@ -147,20 +147,6 @@ class PeriodsController < ApplicationController
147
147
  move_task_to_period
148
148
  end
149
149
 
150
- def grab_task
151
- @task = Task.find(params[:id])
152
- @task.grab
153
- load_tasks(@task.period)
154
- render :template => '/tasks/_update.rjs'
155
- end
156
-
157
- def release_task
158
- @task = Task.find(params[:id])
159
- @task.release
160
- load_tasks(@task.period)
161
- render :template => '/tasks/_update.rjs'
162
- end
163
-
164
150
  def finish_task
165
151
  @task = Task.find(params[:id])
166
152
  @task.finish(Task::COMPLETED, true)
@@ -196,10 +196,21 @@ class TasksController < ApplicationController
196
196
  redirect_to :controller => 'periods', :action => :show_no_layout, :id => task.period_id, :task => task.id
197
197
  end
198
198
 
199
- # def order
200
- # params[:list].each_with_index { |id,idx| Model.update(id, :position => idx) }
201
- # render :text => 'Updated sort order'
202
- # end
199
+ def grab
200
+ @task = Task.find(params[:id])
201
+ @task.grab
202
+ detour = pop_detour
203
+ params.update(detour) if detour
204
+ render :template => '/tasks/_update.rjs'
205
+ end
206
+
207
+ def release
208
+ @task = Task.find(params[:id])
209
+ @task.release
210
+ detour = pop_detour
211
+ params.update(detour) if detour
212
+ render :template => '/tasks/_update.rjs'
213
+ end
203
214
 
204
215
  def start_work
205
216
  @task = Task.find(params[:id])
@@ -14,7 +14,7 @@ class UserController < ApplicationController
14
14
  cookies[:autologin] = {:value => user.id.to_s, :expires =>90.days.from_now}
15
15
  cookies[:token] = {:value => user.security_token, :expires =>90.days.from_now}
16
16
  end
17
- back_or_redirect_to :controller => 'backlogs', :action => :index
17
+ back_or_redirect_to :controller => 'welcome', :action => :index
18
18
  else
19
19
  @login = params['user']['login']
20
20
  flash[:notice] = 'Login failed'
@@ -155,6 +155,20 @@ class UserController < ApplicationController
155
155
  @users = User.find(:all)
156
156
  end
157
157
 
158
+ def toggle_work_lock_monitoring
159
+ @user = User.find(params[:id])
160
+ already_monitoring = @user.work_lock_subscribers.include? current_user
161
+ if already_monitoring
162
+ @user.work_lock_subscribers.delete current_user
163
+ action = 'stopped'
164
+ else
165
+ @user.work_lock_subscribers << current_user
166
+ action = 'started'
167
+ end
168
+ flash[:notice] = "Monitoring #{action}"
169
+ UserNotify.deliver_monitoring(@user, current_user, action)
170
+ end
171
+
158
172
  protected
159
173
 
160
174
  def protect?(action)
@@ -9,7 +9,7 @@ class WorkAccountsController < ApplicationController
9
9
  :redirect_to => { :action => :list }
10
10
 
11
11
  def list
12
- @work_accounts = WorkAccount.paginate :page => params[:page]
12
+ @work_accounts = WorkAccount.paginate :page => params[:page], :order => 'name'
13
13
  end
14
14
 
15
15
  def show
@@ -0,0 +1,88 @@
1
+ class WorkLocksController < ApplicationController
2
+ def index
3
+ list
4
+ render :action => 'list'
5
+ end
6
+
7
+ # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
8
+ verify :method => :post, :only => [ :destroy, :create, :update ],
9
+ :redirect_to => { :action => :list }
10
+
11
+ def list
12
+ @work_lock_pages, @work_locks = paginate :work_locks, :per_page => 10
13
+ end
14
+
15
+ def show
16
+ @work_lock = WorkLock.find(params[:id])
17
+ end
18
+
19
+ def new
20
+ @work_lock = WorkLock.new
21
+ end
22
+
23
+ def create
24
+ @work_lock = WorkLock.new(params[:work_lock])
25
+ if @work_lock.save
26
+ flash[:notice] = 'WorkLock was successfully created.'
27
+ redirect_to :action => 'list'
28
+ else
29
+ render :action => 'new'
30
+ end
31
+ end
32
+
33
+ def edit
34
+ @work_lock = WorkLock.find(params[:id])
35
+ end
36
+
37
+ def update
38
+ @work_lock = WorkLock.find(params[:id])
39
+ if @work_lock.update_attributes(params[:work_lock])
40
+ flash[:notice] = 'WorkLock was successfully updated.'
41
+ redirect_to :action => 'show', :id => @work_lock
42
+ else
43
+ render :action => 'edit'
44
+ end
45
+ end
46
+
47
+ def destroy
48
+ WorkLock.find(params[:id]).destroy
49
+ redirect_to :action => 'list'
50
+ end
51
+
52
+ def lock
53
+ @year = (params[:year] && params[:year].to_i) || Date.today.year
54
+ @week = (params[:week] && params[:week].to_i) || Date.today.cweek
55
+ first_date = Date.commercial(@year, @week, 1)
56
+ last_date = first_date + 6
57
+ @lock = WorkLock.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, first_date, last_date])
58
+ raise "Already locked" if @lock
59
+ WorkLock.create! :user_id => current_user.id, :start_on => first_date, :end_on => last_date
60
+ work_account_subscribers = Work.works_for_week(@year, @week).flatten.compact.map {|w| w.work_account}.uniq.map {|wa| wa.work_lock_subscribers}.flatten.uniq
61
+ user_subscribers = current_user.work_lock_subscribers
62
+ notify_users = (work_account_subscribers + user_subscribers).uniq
63
+ notify_users.each do |user|
64
+ week_url = url_for :controller => 'works', :action => :weekly_work_sheet_by_work_account, :year => @year, :week => @week
65
+ spreadsheet_url = url_for :controller => 'works', :action => :timeliste, :year => @year, :week => @week
66
+ WorkLockNotify.deliver_lock(user, current_user, @week, week_url, spreadsheet_url)
67
+ end
68
+ back_or_redirect_to :controller => 'works', :action => :weekly_work_sheet, :year => @year, :week => @week
69
+ end
70
+
71
+ def unlock
72
+ get_lock_from_params
73
+ raise "Not locked" unless @lock
74
+ raise "Unable to destroy" unless @lock.destroy
75
+ back_or_redirect_to :controller => 'works', :action => :weekly_work_sheet, :year => @year, :week => @week
76
+ end
77
+
78
+ private
79
+
80
+ def get_lock_from_params
81
+ @year = (params[:year] && params[:year].to_i) || Date.today.year
82
+ @week = (params[:week] && params[:week].to_i) || Date.today.cweek
83
+ @first_date = Date.commercial(@year, @week, 1)
84
+ @last_date = @first_date + 6
85
+ @lock = WorkLock.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, @first_date, @last_date])
86
+ end
87
+
88
+ end
@@ -3,7 +3,7 @@ class WorksController < ApplicationController
3
3
  in_place_edit_for :work, :invoice
4
4
  in_place_edit_for :work, :started_at_time
5
5
  in_place_edit_for :work, :completed_at_time
6
- skip_before_filter :populate_layout, :except => [:create, :destroy, :edit, :index, :list, :new, :show, :update]
6
+ skip_before_filter :populate_layout, :except => [:create, :destroy, :edit, :index, :list, :new, :show, :update, :daily_work_sheet, :weekly_work_sheet_by_work_account]
7
7
  auto_complete_for :work, :description
8
8
 
9
9
  def index
@@ -73,7 +73,7 @@ class WorksController < ApplicationController
73
73
  @work.attributes = params[:work]
74
74
  @estimate = Estimate.new(params[:estimate])
75
75
  @work_accounts = WorkAccount.find(:all)
76
- @tasks = Task.find_open
76
+ @tasks = [@work.task] + Task.find_open
77
77
  @users = User.find(:all)
78
78
  end
79
79
 
@@ -91,6 +91,7 @@ class WorksController < ApplicationController
91
91
  update_work
92
92
  flash.discard
93
93
  @field = params[:field] || 'started_at_time'
94
+ @work_accounts = WorkAccount.find(:all, :order => :name)
94
95
  end
95
96
 
96
97
  def update_time
@@ -133,6 +134,7 @@ class WorksController < ApplicationController
133
134
  @works = Work.find_work_for_day @date
134
135
  @started_works = Task.find_started
135
136
  @work = Work.new(:started_at => Time.now, :completed_at => Time.now)
137
+ @work_accounts = WorkAccount.find(:all, :order => :name)
136
138
  render :layout => 'wide'
137
139
  end
138
140
 
@@ -140,12 +142,26 @@ class WorksController < ApplicationController
140
142
  @year = (params[:year] && params[:year].to_i) || Date.today.year
141
143
  @week = (params[:week] && params[:week].to_i) || Date.today.cweek
142
144
  @rows = Work.works_for_week(@year, @week)
145
+ @first_date = Date.commercial(@year, @week, 1)
146
+ @last_date = @first_date + 6
147
+ @lock = WorkLock.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, @first_date, @last_date])
148
+ render :layout => 'wide'
149
+ end
150
+
151
+ def weekly_work_sheet_by_work_account
152
+ @year = (params[:year] && params[:year].to_i) || Date.today.year
153
+ @week = (params[:week] && params[:week].to_i) || Date.today.cweek
154
+ @work_accounts = Work.works_for_week_by_work_account(@year, @week)
155
+ @first_date = Date.commercial(@year, @week, 1)
156
+ @last_date = @first_date + 6
157
+ @lock = WorkLock.find(:first, :conditions => ['user_id = ? AND start_on <= ? AND end_on >= ?', current_user.id, @first_date, @last_date])
143
158
  render :layout => 'wide'
144
159
  end
145
160
 
146
161
  def timeliste
147
- @week = (params[:id] && params[:id].to_i) || Date.today.cweek
148
- @backlogs = Work.work_totals_for_week(@week)
162
+ @year = (params[:year] && params[:year].to_i) || Date.today.year
163
+ @week = (params[:week] && params[:week].to_i) || Date.today.cweek
164
+ @work_totals_per_work_account = Work.work_totals_for_week(@year, @week)
149
165
  headers["Content-Type"] = "application/vnd.ms-excel"
150
166
  headers["Content-Disposition"] = 'attachment; filename="export.xml"'
151
167
  render :layout => false
@@ -183,6 +199,12 @@ class WorksController < ApplicationController
183
199
 
184
200
  render :partial => 'description_list'
185
201
  end
202
+
203
+ def calculate_hours
204
+ calculated_duration = (Time.parse(params[:completed_at]) - Time.parse(params[:started_at]))
205
+ new_value = '%2d:%02d' % [calculated_duration / 3600, (calculated_duration % 3600) / 60]
206
+ render :update do |page| page['work_hours_time'].value = new_value end
207
+ end
186
208
 
187
209
  private
188
210
 
@@ -194,7 +216,9 @@ class WorksController < ApplicationController
194
216
  else
195
217
  raise "Unknown time format: '#{params[:work][:hours_time]}'"
196
218
  end
197
- if @work && @work.started?
219
+ if params[:work][:completed_at]
220
+ t = Time.parse params[:work][:completed_at]
221
+ elsif @work && @work.started?
198
222
  t = @work.started_at
199
223
  else
200
224
  t = Time.now
@@ -0,0 +1,2 @@
1
+ module WorkLocksHelper
2
+ end
data/app/models/task.rb CHANGED
@@ -54,7 +54,7 @@ class Task < ActiveRecord::Base
54
54
  user_clause = " OR user_id = #{current_user.id}"
55
55
  end
56
56
  conditions = "completed_at IS NULL AND (user_id IS NULL#{user_clause})"
57
- Work.find(:all, :conditions => conditions).map {|work| work.task}.sort_by do |t|
57
+ Work.find(:all, :conditions => conditions).map {|work| work.task}.compact.sort_by do |t|
58
58
  [t.root_task.backlog.name, t.root_task.period.end_on, t.position || 0]
59
59
  end
60
60
  end
data/app/models/user.rb CHANGED
@@ -8,6 +8,7 @@ class User < Party
8
8
  # TODO (uwe): We need to specify :join_table, :foreign_key and :association_foreign_key
9
9
  # even if they follow the defaults since ClassTableInheritanceInRails breaks it.
10
10
  has_and_belongs_to_many :groups, :join_table => "groups_users", :foreign_key => "user_id", :association_foreign_key => 'group_id'
11
+ has_and_belongs_to_many :work_lock_subscribers, :class_name => 'User', :join_table => "user_work_lock_subscriptions", :foreign_key => "user_id", :association_foreign_key => 'subscriber_user_id'
11
12
 
12
13
  attr_accessor :password_needs_confirmation
13
14
 
@@ -1,4 +1,6 @@
1
1
  class UserNotify < ActionMailer::Base
2
+ include Localization
3
+
2
4
  def signup(user, password, url=nil)
3
5
  setup_email(user)
4
6
 
@@ -40,6 +42,19 @@ class UserNotify < ActionMailer::Base
40
42
  @body["app_name"] = UserSystem::CONFIG[:app_name].to_s
41
43
  end
42
44
 
45
+ def monitoring(user, monitoring_user, action)
46
+ setup_email(user)
47
+
48
+ @subject += "#{monitoring_user.name} has #{l(action)} monitoring your time sheets."
49
+
50
+ @body["app_name"] = UserSystem::CONFIG[:app_name].to_s
51
+ @body["user"] = user
52
+ @body["monitoring_user"] = monitoring_user
53
+ @body["action"] = action
54
+ end
55
+
56
+ private
57
+
43
58
  def setup_email(user)
44
59
  @recipients = "#{user.email}"
45
60
  @from = UserSystem::CONFIG[:email_from].to_s
data/app/models/work.rb CHANGED
@@ -44,6 +44,28 @@ class Work < ActiveRecord::Base
44
44
  works_by_row = works_per_day.transpose
45
45
  end
46
46
 
47
+ # Return a hash with work accounts as keys an array of work hours totals per day as values:
48
+ # {
49
+ # <#WorkAccount#1> => [ 8, 7, 9, 12, 4, nil, nil],
50
+ # <#WorkAccount#2> => [nil, 1, nil, nil, 4, nil, nil],
51
+ # <#WorkAccount#3> => [nil, nil, nil, nil, nil, 4, 3],
52
+ # ]
53
+ def self.works_for_week_by_work_account(year, week_no, user = current_user)
54
+ first_date = Date.commercial(year, week_no, 1)
55
+ last_date = first_date + 6
56
+ works = find(:all, :conditions => "completed_at IS NOT NULL AND completed_at BETWEEN '#{first_date.to_time.iso8601}' AND '#{(last_date+1).to_time.iso8601}'", :order => 'completed_at, started_at')
57
+ result = {}
58
+ works.each do |work|
59
+ day_of_week = work.completed_at.to_date.cwday - 1
60
+ result[work.work_account] ||= []
61
+ result[work.work_account][day_of_week] ||= BigDecimal('0')
62
+ result[work.work_account][day_of_week] += work.hours
63
+ end
64
+ result.values.each {|work_account_totals| work_account_totals[6] ||= nil}
65
+ result.values.each {|work_account_totals| (0..6).each {|i| work_account_totals[i] = nil if work_account_totals[i] == 0}}
66
+ result
67
+ end
68
+
47
69
  # Return a hash with an array of work totals per day:
48
70
  # {
49
71
  # backlog1.id => [[m, t, w, t, f, s, s], [m, t, w, t, f, s, s]],
@@ -52,12 +74,12 @@ class Work < ActiveRecord::Base
52
74
  def self.work_totals_for_week(year, week_no, user = current_user)
53
75
  first = Date.commercial(year, week_no, 1)
54
76
  last = first + 7
55
- works = find(:all, :conditions => "completed_at IS NOT NULL AND started_at BETWEEN '#{first.to_time.iso8601}' AND '#{last.to_time.iso8601}'", :order => 'started_at')
77
+ works = find(:all, :conditions => "completed_at IS NOT NULL AND completed_at BETWEEN '#{first.to_time.iso8601}' AND '#{last.to_time.iso8601}'", :order => 'completed_at, started_at')
56
78
  totals_per_work_account = {}
57
- WorkAccount.find(:all).each do |work_account|
79
+ works.map{|w| w.work_account}.uniq.each do |work_account|
58
80
  totals_per_work_account[work_account.id] = [[], []]
59
81
  (0..6).each do |day|
60
- works_for_day = works.select {|work| (work.work_account == work_account) && (work.started_at.to_date == (first + day)) && (work.user_id.nil? || (user && work.user_id == user.id)) }
82
+ works_for_day = works.select {|work| (work.work_account == work_account) && (work.completed_at.to_date == (first + day)) && (work.user_id.nil? || (user && work.user_id == user.id)) }
61
83
  invoice_works_for_day = works_for_day.select {|work| work.invoice? }
62
84
  internal_works_for_day = works_for_day.select {|work| !work.invoice? }
63
85
 
@@ -74,8 +96,8 @@ class Work < ActiveRecord::Base
74
96
  end
75
97
 
76
98
  def self.find_work_for_day date
77
- Work.find(:all, :conditions => "started_at < '#{date+1}' AND completed_at >= '#{date}' AND user_id = #{current_user.id}",
78
- :order => 'completed_at')
99
+ Work.find(:all, :conditions => "(completed_at IS NULL OR completed_at BETWEEN '#{date}' AND '#{date+1}') AND user_id = #{current_user.id}",
100
+ :order => 'completed_at, started_at')
79
101
  end
80
102
 
81
103
  def started?
@@ -94,7 +116,7 @@ class Work < ActiveRecord::Base
94
116
  end
95
117
 
96
118
  def completed_at_time
97
- completed_at.strftime('%H:%M')
119
+ completed_at && completed_at.strftime('%H:%M')
98
120
  end
99
121
 
100
122
  def completed_at_time=(new_value)
@@ -1,9 +1,11 @@
1
1
  class WorkAccount < ActiveRecord::Base
2
2
  has_many :backlogs, :dependent => :nullify
3
3
  has_many :works, :dependent => :destroy
4
+ has_and_belongs_to_many :work_lock_subscribers, :class_name => 'User', :join_table => "work_lock_subscriptions", :foreign_key => "work_account_id", :association_foreign_key => 'subscriber_user_id'
4
5
 
5
6
  validates_inclusion_of :track_times, :in => [true, false], :allow_nil => true, :message => ActiveRecord::Errors.default_error_messages[:blank]
6
7
  validates_length_of :invoice_code, :allow_nil => true, :maximum => 255
8
+ validates_uniqueness_of :name
7
9
 
8
10
  def enable_invoicing?
9
11
  invoice_code && invoice_code.length > 0
@@ -0,0 +1,3 @@
1
+ class WorkLock < ActiveRecord::Base
2
+ belongs_to :user
3
+ end
@@ -0,0 +1,27 @@
1
+ class WorkLockNotify < ActionMailer::Base
2
+ def lock(user, lock_user, week, week_url, spreadsheet_url)
3
+ setup_email(user)
4
+
5
+ # Email header info
6
+ @subject += "#{lock_user.name} has marked week #{week} as locked."
7
+
8
+ # Email body substitutions
9
+ @body["app_name"] = UserSystem::CONFIG[:app_name].to_s
10
+ @body["app_url"] = UserSystem::CONFIG[:app_url].to_s
11
+ @body["name"] = "#{user.first_name} #{user.last_name}"
12
+ @body["login"] = user.login
13
+ @body["lock_user"] = lock_user
14
+ @body["week_url"] = week_url
15
+ @body["spreadsheet_url"] = spreadsheet_url
16
+ end
17
+
18
+ private
19
+
20
+ def setup_email(user)
21
+ @recipients = "#{user.email}"
22
+ @from = UserSystem::CONFIG[:email_from].to_s
23
+ @subject = "[#{UserSystem::CONFIG[:app_name]}] "
24
+ @sent_on = Time.now
25
+ @headers['Content-Type'] = "text/plain; charset=#{UserSystem::CONFIG[:mail_charset]}; format=flowed"
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ class WorkLockSubscription < ActiveRecord::Base
2
+ belongs_to :work_account
3
+ end
@@ -0,0 +1 @@
1
+ display_notice(page)
@@ -6,6 +6,7 @@
6
6
 
7
7
  <% if user? %>
8
8
  | <%= link_to l(:backlogs), :controller => 'backlogs', :action => :list %>
9
+ | <%= link_to l(:work_accounts), :controller => 'work_accounts', :action => :list %>
9
10
  | <%= link_to l(:log_out), :controller => 'user', :action => :logout %>
10
11
  <% else %>
11
12
  | <%= detour_to l(:log_in), :controller => 'user', :action => :login %>
@@ -87,9 +87,9 @@
87
87
  <% unless @task.work_started? -%>
88
88
  <% if @task.backlog.enable_users? %>
89
89
  <% if @task.users.include?(current_user) %>
90
- <%=image_link_to_remote('grab.png', l(:grab_task), {:action => :release_task, :id => @task}, nil, true)%>
90
+ <%=image_link_to_remote('grab.png', l(:grab_task), with_detour({:controller => 'tasks', :action => :release, :id => @task}), nil, true)%>
91
91
  <% else %>
92
- <%=image_link_to_remote('grab_gray.png', l(:grab_task), {:action => :grab_task, :id => @task}, nil, true)%>
92
+ <%=image_link_to_remote('grab_gray.png', l(:grab_task), with_detour({:controller => 'tasks', :action => :grab, :id => @task}), nil, true)%>
93
93
  <% end %>
94
94
  <% end %>
95
95
  <%=image_link_to_remote('arrow_right.png', l(:move_to_next_period), {:action => :move_task_to_next_period, :id => @task}, nil, true) if @task.backlog.enable_periods? || @task.period_id%>
@@ -1,6 +1,19 @@
1
- <div id="spotlight" title="<%= title_helper %>" class="form">
2
- <% @page_title = "#{l :editing} #{l :user}" %>
1
+ <% @page_title = "#{l :editing} #{l :user}" %>
3
2
 
3
+ <div id="spotlight" title="<%= title_helper %>" class="form">
4
+ <% if @user == current_user %>
5
+ <% unless @user.work_lock_subscribers.empty? %>
6
+ <table align="right">
7
+ <tr><th>Subscribers</th></tr>
8
+ <% for subscriber in @user.work_lock_subscribers %>
9
+ <tr><td><%=detour_to subscriber.name, :action => :edit%></td></tr>
10
+ <% end %>
11
+ </table>
12
+ <% end %>
13
+ <% else %>
14
+ <% monitoring = @user.work_lock_subscribers.include? current_user %>
15
+ <%=image_link_to_remote "email#{'_grey' unless monitoring}.png", "#{l(monitoring ? :stop : :start)} #{l(:monitoring)}", {:action => :toggle_work_lock_monitoring, :id => @user.id}, {:id => :work_lock_monitor_icon, :style => 'float: right'} %>
16
+ <% end %>
4
17
  <%= start_form_tag_helper %>
5
18
  <%= render_partial 'edit', :user => @user, :submit => true %>
6
19
  </form>
@@ -0,0 +1,2 @@
1
+ display_notice(page)
2
+ page['work_lock_monitor_icon'].src = image_path("email#{'_grey' unless @user.work_lock_subscribers.include? current_user}.png")
@@ -0,0 +1,3 @@
1
+ Regarding your account at <%=@app_name %>, <%=@user.name %>.
2
+
3
+ <%=@monitoring_user.name%> has <%=@action%> monitoring your time sheets.
@@ -0,0 +1,3 @@
1
+ Angående din konto på <%=@app_name %>, <%=@user.name %>.
2
+
3
+ <%=@monitoring_user.name%> har <%=@action%> monitorering av dine timelister.
@@ -0,0 +1,7 @@
1
+ <%=@name %>
2
+
3
+ <%=@lock_user.name%> har godkjent timeliste for uke <%=@week%>.
4
+
5
+ Klikk på linken under for en ukeoversikt:
6
+
7
+ <%=@week_url%>
@@ -0,0 +1,7 @@
1
+ <%=@name %>
2
+
3
+ <%=@lock_user.name%> has marked week <%=@week%> as complete.
4
+
5
+ Please click on the following link to go to the week overview:
6
+
7
+ <%=@week_url%>
@@ -0,0 +1,10 @@
1
+ <%= error_messages_for 'work_lock' %>
2
+
3
+ <!--[form:work_lock]-->
4
+ <p><label for="work_lock_start_on">Start on</label><br/>
5
+ <%= date_select 'work_lock', 'start_on' %></p>
6
+
7
+ <p><label for="work_lock_end_on">End on</label><br/>
8
+ <%= date_select 'work_lock', 'end_on' %></p>
9
+ <!--[eoform:work_lock]-->
10
+
@@ -0,0 +1,9 @@
1
+ <h1>Editing work_lock</h1>
2
+
3
+ <% form_tag :action => 'update', :id => @work_lock do %>
4
+ <%= render :partial => 'form' %>
5
+ <%= submit_tag 'Edit' %>
6
+ <% end %>
7
+
8
+ <%= link_to 'Show', :action => 'show', :id => @work_lock %> |
9
+ <%= link_to 'Back', :action => 'list' %>
@@ -0,0 +1,27 @@
1
+ <h1>Listing work_locks</h1>
2
+
3
+ <table>
4
+ <tr>
5
+ <% for column in WorkLock.content_columns %>
6
+ <th><%= column.human_name %></th>
7
+ <% end %>
8
+ </tr>
9
+
10
+ <% for work_lock in @work_locks %>
11
+ <tr>
12
+ <% for column in WorkLock.content_columns %>
13
+ <td><%=h work_lock.send(column.name) %></td>
14
+ <% end %>
15
+ <td><%= link_to 'Show', :action => 'show', :id => work_lock %></td>
16
+ <td><%= link_to 'Edit', :action => 'edit', :id => work_lock %></td>
17
+ <td><%= link_to 'Destroy', { :action => 'destroy', :id => work_lock }, :confirm => 'Are you sure?', :method => :post %></td>
18
+ </tr>
19
+ <% end %>
20
+ </table>
21
+
22
+ <%= link_to 'Previous page', { :page => @work_lock_pages.current.previous } if @work_lock_pages.current.previous %>
23
+ <%= link_to 'Next page', { :page => @work_lock_pages.current.next } if @work_lock_pages.current.next %>
24
+
25
+ <br />
26
+
27
+ <%= link_to 'New work_lock', :action => 'new' %>
@@ -0,0 +1,8 @@
1
+ <h1>New work_lock</h1>
2
+
3
+ <% form_tag :action => 'create' do %>
4
+ <%= render :partial => 'form' %>
5
+ <%= submit_tag "Create" %>
6
+ <% end %>
7
+
8
+ <%= link_to 'Back', :action => 'list' %>
@@ -0,0 +1,8 @@
1
+ <% for column in WorkLock.content_columns %>
2
+ <p>
3
+ <b><%= column.human_name %>:</b> <%=h @work_lock.send(column.name) %>
4
+ </p>
5
+ <% end %>
6
+
7
+ <%= link_to 'Edit', :action => 'edit', :id => @work_lock %> |
8
+ <%= link_to 'Back', :action => 'list' %>
@@ -1,3 +1,8 @@
1
+ <%=image_detour_to('hammer.png', l(:weekly_work_sheet), :controller => 'works', :action => :weekly_work_sheet_by_work_account)%>
2
+ <!--
1
3
  <%=image_detour_to('hammer.png', l(:weekly_work_sheet), :controller => 'works', :action => :weekly_work_sheet)%>
4
+ -->
2
5
  <%=link_to(image_tag(url_for("hammer.png"), :alt => l(:daily_work_sheet), :title => l(:daily_work_sheet), :class => 'image-submit'), :controller => 'works', :action => :daily_work_sheet)%>
6
+ <!--
3
7
  <%=link_to(image_tag(url_for("hammer.png"), :alt => l(:edit_works), :title => l(:edit_works), :class => 'image-submit'), :controller => 'periods', :action => 'list_work', :id => (@period ? @period.id : (@backlog && @backlog.periods.first ? @backlog.periods.first.id : nil))) if @period || @backlog%>
8
+ -->
@@ -63,7 +63,10 @@
63
63
 
64
64
  <% if @work.task.nil? || @work.task.track_done?%>
65
65
  <p style="float: left;"><label for="work_hours"><%=l :hours%></label><br/>
66
- <%= text_field 'work', 'hours_time', :class => :task_hours, :value => t(@work.hours) %></p>
66
+ <%=text_field 'work', 'hours_time', :class => :task_hours, :value => t(@work.hours) %></p>
67
+
68
+ <p style="float: left;"><br/><%=image_link_to_remote 'refresh.png', l(:refresh), :action => :calculate_hours, :started_at => "' + $('work_started_at').value + '", :completed_at => "' + $('work_completed_at').value + '" %></p>
69
+
67
70
  <% end %>
68
71
 
69
72
  <% if @work.task.nil? || @work.task.enable_invoicing? %>