backlog 0.18.0 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
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? %>