knjtasks 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +18 -0
  4. data/Gemfile.lock +66 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +19 -0
  7. data/Rakefile +49 -0
  8. data/VERSION +1 -0
  9. data/files/database_schema.rb +174 -0
  10. data/lib/knjtasks.rb +172 -0
  11. data/locales/da_DK/LC_MESSAGES/default.mo +0 -0
  12. data/locales/da_DK/LC_MESSAGES/default.po +1153 -0
  13. data/locales/da_DK/title.txt +1 -0
  14. data/locales/en_GB/title.txt +1 -0
  15. data/models/class_comment.rb +44 -0
  16. data/models/class_customer.rb +13 -0
  17. data/models/class_email_check.rb +7 -0
  18. data/models/class_project.rb +22 -0
  19. data/models/class_task.rb +163 -0
  20. data/models/class_task_assigned_user.rb +62 -0
  21. data/models/class_task_check.rb +26 -0
  22. data/models/class_timelog.rb +82 -0
  23. data/models/class_user.rb +125 -0
  24. data/models/class_user_project_link.rb +66 -0
  25. data/models/class_user_rank.rb +3 -0
  26. data/models/class_user_rank_link.rb +17 -0
  27. data/models/class_user_task_list_link.rb +17 -0
  28. data/pages/admin.rhtml +7 -0
  29. data/pages/comment_edit.rhtml +121 -0
  30. data/pages/comment_update_id_per_obj.rhtml +41 -0
  31. data/pages/customer_edit.rhtml +69 -0
  32. data/pages/customer_search.rhtml +80 -0
  33. data/pages/customer_show.rhtml +50 -0
  34. data/pages/frontpage.rhtml +198 -0
  35. data/pages/project_edit.rhtml +129 -0
  36. data/pages/project_search.rhtml +82 -0
  37. data/pages/project_show.rhtml +203 -0
  38. data/pages/task_check_edit.rhtml +98 -0
  39. data/pages/task_edit.rhtml +168 -0
  40. data/pages/task_search.rhtml +131 -0
  41. data/pages/task_show.rhtml +454 -0
  42. data/pages/timelog_edit.rhtml +134 -0
  43. data/pages/timelog_search.rhtml +318 -0
  44. data/pages/user_edit.rhtml +223 -0
  45. data/pages/user_login.rhtml +83 -0
  46. data/pages/user_profile.rhtml +89 -0
  47. data/pages/user_rank_search.rhtml +95 -0
  48. data/pages/user_search.rhtml +136 -0
  49. data/pages/user_show.rhtml +87 -0
  50. data/pages/workstatus.rhtml +320 -0
  51. data/scripts/fckeditor_validate_login.rb +23 -0
  52. data/spec/knjtasks_spec.rb +115 -0
  53. data/spec/spec_helper.rb +12 -0
  54. data/threads/thread_mail_task_comments.rb +114 -0
  55. data/www/api/task.rhtml +9 -0
  56. data/www/api/user.rhtml +20 -0
  57. data/www/clean.rhtml +14 -0
  58. data/www/css/default.css +186 -0
  59. data/www/gfx/body_bg.jpg +0 -0
  60. data/www/gfx/button_bg.png +0 -0
  61. data/www/gfx/main_box_design.png +0 -0
  62. data/www/gfx/main_box_left.png +0 -0
  63. data/www/gfx/main_box_right.png +0 -0
  64. data/www/gfx/main_box_top.png +0 -0
  65. data/www/gfx/main_box_top_left.png +0 -0
  66. data/www/gfx/main_box_top_right.png +0 -0
  67. data/www/index.rhtml +154 -0
  68. data/www/js/default.js +112 -0
  69. metadata +208 -0
@@ -0,0 +1,136 @@
1
+ <%
2
+ print _site.header(_("Search for users")) if !_get["ajaxsearch"]
3
+
4
+ if !_get["ajaxsearch"] or (_get["ajaxsearch"] and _get["choice"] != "dosearch")
5
+ %>
6
+ <form method="get"<%if _get["ajaxsearch"]; print " onsubmit=\"return do_ajax_search();\""; end%>>
7
+ <%=Knj::Web.hiddens([{:name => :show, :value => :user_search}, {:name => :choice, :value => :dosearch}])%>
8
+
9
+ <%
10
+ if _get["not_in_task_id"]
11
+ print Knj::Web.hiddens([{:name => :not_in_task_id, :value => _get["not_in_task_id"]}])
12
+ end
13
+ %>
14
+
15
+ <%=_site.boxt(_("Enter criteria"), "350px")%>
16
+ <table class="form" id="formusersearch">
17
+ <%
18
+ print Knj::Web.inputs([{
19
+ :title => _("Name"),
20
+ :name => :texname,
21
+ :value => _get["texname"],
22
+ :descr => _("Enter a part of the name of the user you are looking for.")
23
+ }])
24
+ %>
25
+ <tr>
26
+ <td colspan="2" class="buttons">
27
+ <%if _site.has_rank?("admin")%>
28
+ <input type="button" value="<%=_("Add new")%>" onclick="location.href='?show=user_edit';" />
29
+ <%end%>
30
+ <input type="submit" value="<%=_("Search")%>" />
31
+ </td>
32
+ </tr>
33
+ </table>
34
+ <%=_site.boxb%>
35
+
36
+ </form>
37
+
38
+ <%
39
+ if _get["ajaxsearch"]
40
+ %>
41
+ <br />
42
+
43
+ <div id="divresults">
44
+ <div class="error"><%=_("Please search...")%></div>
45
+ </div>
46
+
47
+ <script type="text/javascript">
48
+ modal_on_opened(function(){
49
+ $("#texname").focus();
50
+ });
51
+ </script>
52
+ <%
53
+ end
54
+ %>
55
+
56
+ <script type="text/javascript">
57
+ $("#texname").focus();
58
+
59
+ function do_ajax_search(){
60
+ formdata = forms_inputs_read($("#formusersearch"));
61
+
62
+ urlstr = "clean.rhtml?show=user_search&ajaxsearch=true&choice=dosearch&jscallback=<%=_get['jscallback']%>&"
63
+ urlstr += $.param(formdata)
64
+
65
+ $.ajax({type: "GET", url: urlstr, complete: function(data){
66
+ $("#divresults").slideUp("fast", function(){
67
+ $("#divresults").html(data.responseText);
68
+ $("#divresults").slideDown("fast");
69
+ });
70
+ }});
71
+
72
+ return false;
73
+ }
74
+ </script>
75
+ <%
76
+ end
77
+
78
+ if _get["choice"] == "dosearch"
79
+ args = {"orderby" => ["name", "username"]}
80
+
81
+ if _get["texname"].to_s.length > 0
82
+ args["name_search"] = _get["texname"]
83
+ end
84
+
85
+ users = _ob.list(:User, args)
86
+
87
+ %>
88
+ <br />
89
+
90
+ <%=_site.boxt(_("Results"), "350px")%>
91
+ <table class="list">
92
+ <thead>
93
+ <tr>
94
+ <th><%=_("Name")%></th>
95
+ </tr>
96
+ </thead>
97
+ <tbody>
98
+ <%
99
+ count = 0
100
+ users.each do |user|
101
+ next if !user.has_view_access?(_site.user)
102
+ count += 1
103
+
104
+ %>
105
+ <tr>
106
+ <td>
107
+ <%
108
+ if _get["jscallback"]
109
+ %>
110
+ <a href="javascript: <%=_get["jscallback"]%>('<%=user.id%>');"><%=user.name.html%></a>
111
+ <%
112
+ else
113
+ print user.html
114
+ end
115
+ %>
116
+ </td>
117
+ </tr>
118
+ <%
119
+ end
120
+
121
+ if count <= 0
122
+ %>
123
+ <tr>
124
+ <td colspan="1" class="error">
125
+ <%=_("No users were found.")%>
126
+ </td>
127
+ </tr>
128
+ <%
129
+ end
130
+ %>
131
+ </tbody>
132
+ </table>
133
+ <%=_site.boxb%>
134
+ <%
135
+ end
136
+ %>
@@ -0,0 +1,87 @@
1
+ <%
2
+ user = _ob.get(:User, _get["user_id"])
3
+ _hb.alert(_("You do not have access to view this user.")) if !user.has_view_access?(_site.user)
4
+
5
+ print _site.header(sprintf(_("Show user: %s."), user.name.html))
6
+
7
+ if _site.has_rank?("admin")
8
+ %>
9
+ <%=_site.boxt(_("Actions"), 450)%>
10
+ <input type="button" value="<%=_("Edit")%>" onclick="location.href='?show=user_edit&amp;user_id=<%=user.id%>';" />
11
+ <%=_site.boxb%>
12
+
13
+ <br />
14
+ <%
15
+ end
16
+ %>
17
+
18
+ <%=_site.boxt(_("Information"), 450)%>
19
+ <table class="form">
20
+ <%
21
+ print Knj::Web.inputs([{
22
+ :title => _("Name"),
23
+ :type => :info,
24
+ :value => user.name.html,
25
+ :descr => _("The real name of the user.")
26
+ },{
27
+ :title => _("Email"),
28
+ :type => :info,
29
+ :value => "<a href=\"mailto:#{user[:email].html}\">#{user[:email].html}</a>",
30
+ :descr => _("The email of the user.")
31
+ }])
32
+ %>
33
+ </table>
34
+ <%=_site.boxb%>
35
+
36
+ <br />
37
+
38
+ <%=_site.boxt(_("Tasks"), 800)%>
39
+ <table class="list">
40
+ <thead>
41
+ <tr>
42
+ <th><%=_"Task"%></th>
43
+ <th><%=_"Status"%></th>
44
+ <th><%=_"Date"%></th>
45
+ <th><%=_"Project"%></th>
46
+ </tr>
47
+ </thead>
48
+ <tbody>
49
+ <%
50
+ tasks = user.tasks("orderby" => [["date_added", "desc"], ["id", "desc"]])
51
+
52
+ count = 0
53
+ tasks.each do |task|
54
+ next if !task.has_view_access?(_site.user)
55
+ count += 1
56
+
57
+ %>
58
+ <tr>
59
+ <td>
60
+ <%=task.html%>
61
+ </td>
62
+ <td>
63
+ <%=task.status_str.html%>
64
+ </td>
65
+ <td class="nowrap" style="width: 100px;">
66
+ <%=task.date_added_str(:time => false)%>
67
+ </td>
68
+ <td>
69
+ <%=task.project_html%>
70
+ </td>
71
+ </tr>
72
+ <%
73
+ end
74
+
75
+ if count <= 0
76
+ %>
77
+ <tr>
78
+ <td colspan="2" class="error">
79
+ <%=_("No tasks were found for this user.")%>
80
+ </td>
81
+ </tr>
82
+ <%
83
+ end
84
+ %>
85
+ </tbody>
86
+ </table>
87
+ <%=_site.boxb%>
@@ -0,0 +1,320 @@
1
+ <%
2
+ _hb.alert(_("You need to be logged in to view this page.")).back if !_site.user
3
+
4
+ cur_date = Datet.new
5
+
6
+ if _get["choice"] == "dofilter"
7
+ date_from = Datet.in(_get["texdatefrom"])
8
+ date_to = Datet.in(_get["texdateto"])
9
+ else
10
+ date_from = Datet.new
11
+ date_from.day = 1
12
+
13
+ date_to = Datet.new
14
+ date_to.day = date_to.days_in_month
15
+ end
16
+
17
+ stats = {
18
+ :date_logs => {},
19
+ :projects => {},
20
+ :totals => {:earned => 0, :earned_time => 0, :earned_transport => 0, :transport_length => 0}
21
+ }
22
+
23
+ sql = "
24
+ SELECT
25
+ SUM(TIME_TO_SEC(Timelog.time)) AS time,
26
+ SUM(TIME_TO_SEC(Timelog.time_transport)) AS time_transport,
27
+ Timelog.date,
28
+ Timelog.transport_length,
29
+ Project.id AS project_id
30
+
31
+ FROM
32
+ Timelog,
33
+ Task,
34
+ Project
35
+
36
+ WHERE
37
+ Task.id = Timelog.task_id AND
38
+ Project.id = Task.project_id AND
39
+ Timelog.date >= '#{_db.date_out(date_from, {:time => false})}' AND
40
+ Timelog.date <= '#{_db.date_out(date_to, {:time => false})}'
41
+ "
42
+
43
+ if _get["projects"].to_s.length > 0
44
+ project_ids = _get["projects"].to_s.split(";")
45
+ sql += " AND Task.project_id IN (#{Knj::ArrayExt.join(:arr => project_ids, :sep => ",", :surr => "'", :callback => [_db, :esc])})"
46
+ end
47
+
48
+ sql += "
49
+ GROUP BY
50
+ Timelog.id
51
+
52
+ ORDER BY
53
+ Timelog.date,
54
+ Timelog.id
55
+ "
56
+
57
+ _db.q(sql) do |data|
58
+ date = Datet.in(data[:date])
59
+ dj = date.day.to_i
60
+ dm = date.month.to_i
61
+ dy = date.year.to_i
62
+
63
+ hours = data[:time].to_f / 3600
64
+ hours_transport = data[:time_transport].to_f / 3600
65
+
66
+ earned_time = hours * 200
67
+ earned_transport = hours_transport * 200
68
+ earned_total = earned_time + earned_transport
69
+
70
+ stats[:date_logs][dy] = {} if !stats[:date_logs].has_key?(dy)
71
+ stats[:date_logs][dy][dm] = {} if !stats[:date_logs][dy].has_key?(dm)
72
+ stats[:date_logs][dy][dm][dj] = {:time => 0, :time_transport => 0, :transport_length => 0, :earned_total => 0, :earned_time => 0, :earned_transport => 0} if !stats[:date_logs][dy][dm].has_key?(dj)
73
+ stats[:date_logs][dy][dm][dj][:time] += data[:time].to_i
74
+ stats[:date_logs][dy][dm][dj][:time_transport] += data[:time_transport].to_i
75
+ stats[:date_logs][dy][dm][dj][:transport_length] += data[:transport_length].to_i
76
+ stats[:date_logs][dy][dm][dj][:earned_time] += earned_time
77
+ stats[:date_logs][dy][dm][dj][:earned_transport] += earned_transport
78
+ stats[:date_logs][dy][dm][dj][:earned_total] += earned_transport
79
+
80
+ stats[:projects][data[:project_id]] = {:time => 0, :time_transport => 0, :earned_total => 0} if !stats[:projects].has_key?(data[:project_id])
81
+ stats[:projects][data[:project_id]][:time] += data[:time].to_i
82
+ stats[:projects][data[:project_id]][:time_transport] += data[:time_transport].to_i
83
+ stats[:projects][data[:project_id]][:earned_total] += earned_total
84
+
85
+ stats[:totals][:earned] += earned_total
86
+ stats[:totals][:earned_time] += earned_time
87
+ stats[:totals][:earned_transport] += earned_transport
88
+ stats[:totals][:transport_length] += data[:transport_length].to_i
89
+ end
90
+
91
+ stats[:projects] = stats[:projects].sort do |a, b|
92
+ ap = _ob.get(:Project, a[0])
93
+ bp = _ob.get(:Project, b[0])
94
+
95
+ ap.name <=> bp.name
96
+ end
97
+
98
+ print _site.header(_("Work-status"))
99
+ %>
100
+
101
+ <form method="get" onsubmit="return do_filter();">
102
+ <%=Knj::Web.hiddens([{:name => :show, :value => :workstatus},{:name => :choice, :value => :dofilter},{:name => :projects}])%>
103
+
104
+ <%=_site.boxt(_("Enter criteria"), 350)%>
105
+ <table class="form">
106
+ <%
107
+ print Knj::Web.inputs([{
108
+ :title => _("Date from"),
109
+ :name => :texdatefrom,
110
+ :value => date_from.out(:time => false)
111
+ },{
112
+ :title => _("Date to"),
113
+ :name => :texdateto,
114
+ :value => date_to.out(:time => false)
115
+ },{
116
+ :title => _("Project"),
117
+ :name => :selprojects,
118
+ :value => nil,
119
+ :opts => _ob.list_optshash(:Project, {:list_args => {"orderby" => "name"}}),
120
+ :multiple => true,
121
+ :size => 15,
122
+ :values => _get["projects"].to_s.split(";")
123
+ }])
124
+ %>
125
+ <tr>
126
+ <td colspan="2" class="buttons">
127
+ <input type="submit" value="<%=_"Filter"%>" />
128
+ </td>
129
+ </tr>
130
+ </table>
131
+ <%=_site.boxb%>
132
+
133
+ </form>
134
+
135
+ <script type="text/javascript">
136
+ function do_filter(){
137
+ $("input[name=projects]").val(select_values({sel: $("#selprojects"), selected: true, expl: ";"}));
138
+ return true;
139
+ }
140
+ </script>
141
+
142
+ <br />
143
+
144
+ <%=_site.boxt(_("Results"), 700)%>
145
+ <table class="list">
146
+ <thead>
147
+ <tr>
148
+ <th><%=_"Date"%></th>
149
+ <th class="tdr"><%=_"Hours"%></th>
150
+ <th class="tdr"><%=_"Hours driving"%></th>
151
+ <th class="tdr"><%=_"Earned"%></th>
152
+ <th class="tdr"><%=_"Driving earned"%></th>
153
+ <th class="tdr"><%=_"Driving length"%></th>
154
+ </tr>
155
+ </thead>
156
+ <tbody>
157
+ <%
158
+ stats[:date_logs].each do |year, year_data|
159
+ if date_from.year != date_to.year
160
+ %>
161
+ <tr>
162
+ <td colspan="6">
163
+ <h2><%=year%></h2>
164
+ </td>
165
+ </tr>
166
+ <%
167
+ end
168
+
169
+ year_data.each do |month, month_data|
170
+ month_date = Datet.in("#{year}-#{month}-1")
171
+
172
+ if date_from.year != date_to.year or date_from.month != date_to.month
173
+ %>
174
+ <tr>
175
+ <td colspan="6">
176
+ <h3><%=month%></h3>
177
+ </td>
178
+ </tr>
179
+ <%
180
+ end
181
+
182
+ 1.upto(month_date.days_in_month) do |date|
183
+ date_data = month_data[date.to_i]
184
+
185
+ if date_data
186
+ hours = date_data[:time].to_f / 3600
187
+ hours_driving = date_data[:time_transport].to_f / 3600
188
+ earned = date_data[:earned_time]
189
+ earned_transport = date_data[:earned_transport]
190
+ transport_length = date_data[:transport_length]
191
+ else
192
+ hours = 0
193
+ hours_driving = 0
194
+ earned = 0
195
+ earned_transport = 0
196
+ transport_length = 0
197
+ end
198
+
199
+ cur_date_is = (cur_date.day.to_i == date.to_i and cur_date.year.to_i == year.to_i and cur_date.month.to_i == month.to_i)
200
+
201
+ %>
202
+ <tr<%if cur_date_is; print " style=\"font-weight: bold;\""; end%>>
203
+ <td>
204
+ <%=date%>
205
+ </td>
206
+ <td class="tdr">
207
+ <%=Knj::Locales.number_out(hours, 1)%>
208
+ </td>
209
+ <td class="tdr">
210
+ <%=Knj::Locales.number_out(hours_driving, 1)%>
211
+ </td>
212
+ <td class="tdr">
213
+ <%=Knj::Locales.number_out(earned, 0)%>
214
+ </td>
215
+ <td class="tdr">
216
+ <%=Knj::Locales.number_out(earned_transport, 0)%>
217
+ </td>
218
+ <td class="tdr">
219
+ <%=Knj::Locales.number_out(transport_length, 0)%>
220
+ </td>
221
+ </tr>
222
+ <%
223
+ end
224
+ end
225
+ end
226
+ %>
227
+ </tbody>
228
+ </table>
229
+ <%=_site.boxb%>
230
+
231
+ <br />
232
+
233
+ <%=_site.boxt(_("Overview"), 450)%>
234
+ <table class="form">
235
+ <%
236
+ tax_40 = stats[:totals][:earned] * 0.4
237
+
238
+ print Knj::Web.inputs([{
239
+ :title => _("Hours"),
240
+ :type => :info,
241
+ :align => :right,
242
+ :value => Knj::Locales.number_out(stats[:totals][:earned_time], 2)
243
+ },{
244
+ :title => _("Transport"),
245
+ :type => :info,
246
+ :align => :right,
247
+ :value => Knj::Locales.number_out(stats[:totals][:earned_transport], 2)
248
+ },{
249
+ :title => _("Total earned"),
250
+ :type => :info,
251
+ :align => :right,
252
+ :value => Knj::Locales.number_out(stats[:totals][:earned], 2)
253
+ },{
254
+ :title => _("Tax") + " (40%)",
255
+ :type => :info,
256
+ :align => :right,
257
+ :value => Knj::Locales.number_out(tax_40, 2)
258
+ },{
259
+ :title => _("Total after tax"),
260
+ :type => :info,
261
+ :align => :right,
262
+ :value => Knj::Locales.number_out(stats[:totals][:earned].to_f - tax_40.to_f, 2)
263
+ },{
264
+ :title => _("Total transport"),
265
+ :type => :info,
266
+ :align => :right,
267
+ :value => _hb.num(stats[:totals][:transport_length], 2)
268
+ }])
269
+ %>
270
+ </table>
271
+ <%=_site.boxb%>
272
+
273
+ <br />
274
+
275
+ <%=_site.boxt(_("Projects"), 450)%>
276
+ <table class="list">
277
+ <thead>
278
+ <tr>
279
+ <th><%=_"Project"%></th>
280
+ <th class="tdr"><%=_"Hours"%></th>
281
+ <th class="tdr"><%=_"Transport"%></th>
282
+ <th class="tdr"><%=_"Earned"%></th>
283
+ </tr>
284
+ </thead>
285
+ <tbody>
286
+ <%
287
+ stats[:projects].each do |project_id, project_data|
288
+ if project_id.to_i > 0
289
+ project = _ob.get(:Project, project_id)
290
+ else
291
+ project = nil
292
+ end
293
+
294
+ %>
295
+ <tr>
296
+ <td>
297
+ <%
298
+ if project
299
+ print project.html
300
+ else
301
+ print "[#{_("no project")}]"
302
+ end
303
+ %>
304
+ </td>
305
+ <td class="tdr">
306
+ <%=Knj::Locales.number_out(project_data[:time].to_f / 3600, 2)%>
307
+ </td>
308
+ <td class="tdr">
309
+ <%=Knj::Locales.number_out(project_data[:earned_transport].to_f, 2)%>
310
+ </td>
311
+ <td class="tdr">
312
+ <%=Knj::Locales.number_out(project_data[:earned_total], 2)%>
313
+ </td>
314
+ </tr>
315
+ <%
316
+ end
317
+ %>
318
+ </tbody>
319
+ </table>
320
+ <%=_site.boxb%>