knjtasks 0.0.3

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 (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%>