knjtasks 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +66 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/files/database_schema.rb +174 -0
- data/lib/knjtasks.rb +172 -0
- data/locales/da_DK/LC_MESSAGES/default.mo +0 -0
- data/locales/da_DK/LC_MESSAGES/default.po +1153 -0
- data/locales/da_DK/title.txt +1 -0
- data/locales/en_GB/title.txt +1 -0
- data/models/class_comment.rb +44 -0
- data/models/class_customer.rb +13 -0
- data/models/class_email_check.rb +7 -0
- data/models/class_project.rb +22 -0
- data/models/class_task.rb +163 -0
- data/models/class_task_assigned_user.rb +62 -0
- data/models/class_task_check.rb +26 -0
- data/models/class_timelog.rb +82 -0
- data/models/class_user.rb +125 -0
- data/models/class_user_project_link.rb +66 -0
- data/models/class_user_rank.rb +3 -0
- data/models/class_user_rank_link.rb +17 -0
- data/models/class_user_task_list_link.rb +17 -0
- data/pages/admin.rhtml +7 -0
- data/pages/comment_edit.rhtml +121 -0
- data/pages/comment_update_id_per_obj.rhtml +41 -0
- data/pages/customer_edit.rhtml +69 -0
- data/pages/customer_search.rhtml +80 -0
- data/pages/customer_show.rhtml +50 -0
- data/pages/frontpage.rhtml +198 -0
- data/pages/project_edit.rhtml +129 -0
- data/pages/project_search.rhtml +82 -0
- data/pages/project_show.rhtml +203 -0
- data/pages/task_check_edit.rhtml +98 -0
- data/pages/task_edit.rhtml +168 -0
- data/pages/task_search.rhtml +131 -0
- data/pages/task_show.rhtml +454 -0
- data/pages/timelog_edit.rhtml +134 -0
- data/pages/timelog_search.rhtml +318 -0
- data/pages/user_edit.rhtml +223 -0
- data/pages/user_login.rhtml +83 -0
- data/pages/user_profile.rhtml +89 -0
- data/pages/user_rank_search.rhtml +95 -0
- data/pages/user_search.rhtml +136 -0
- data/pages/user_show.rhtml +87 -0
- data/pages/workstatus.rhtml +320 -0
- data/scripts/fckeditor_validate_login.rb +23 -0
- data/spec/knjtasks_spec.rb +115 -0
- data/spec/spec_helper.rb +12 -0
- data/threads/thread_mail_task_comments.rb +114 -0
- data/www/api/task.rhtml +9 -0
- data/www/api/user.rhtml +20 -0
- data/www/clean.rhtml +14 -0
- data/www/css/default.css +186 -0
- data/www/gfx/body_bg.jpg +0 -0
- data/www/gfx/button_bg.png +0 -0
- data/www/gfx/main_box_design.png +0 -0
- data/www/gfx/main_box_left.png +0 -0
- data/www/gfx/main_box_right.png +0 -0
- data/www/gfx/main_box_top.png +0 -0
- data/www/gfx/main_box_top_left.png +0 -0
- data/www/gfx/main_box_top_right.png +0 -0
- data/www/index.rhtml +154 -0
- data/www/js/default.js +112 -0
- metadata +208 -0
@@ -0,0 +1,134 @@
|
|
1
|
+
<%
|
2
|
+
task_id = _get["task_id"] if _get["task_id"].to_i > 0
|
3
|
+
|
4
|
+
if _get["choice"] == "dosave"
|
5
|
+
save_hash = {
|
6
|
+
:time => _post["textime"],
|
7
|
+
:time_transport => _post["textimetransport"],
|
8
|
+
:transport_length => _post["textransportlength"],
|
9
|
+
:date => Datet.in(_post["texdate"]),
|
10
|
+
:comment => _post["texcomment"],
|
11
|
+
:task_id => _post["seltask"],
|
12
|
+
:invoiced => Knj::Web.checkval(_post["cheinvoiced"], 1, 0)
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
if _get["timelog_id"].to_i > 0
|
17
|
+
timelog = _ob.get(:Timelog, _get["timelog_id"])
|
18
|
+
task_id = timelog.task.id
|
19
|
+
date_val = Datet.in(timelog[:date]).out(:time => false)
|
20
|
+
time_val = timelog[:time]
|
21
|
+
time_transport_val = timelog[:time_transport]
|
22
|
+
url = "&timelog_id=#{timelog.id}"
|
23
|
+
|
24
|
+
if _get["choice"] == "dosave"
|
25
|
+
timelog.update(save_hash)
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
|
29
|
+
if _get["choice"] == "dodelete"
|
30
|
+
_ob.delete(timelog)
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
else
|
34
|
+
date_val = Datet.new.out(:time => false)
|
35
|
+
time_val = "00:00:00"
|
36
|
+
time_transport_val = "00:00:00"
|
37
|
+
|
38
|
+
if _get["choice"] == "dosave"
|
39
|
+
begin
|
40
|
+
timelog = _ob.add(:Timelog, save_hash)
|
41
|
+
rescue RuntimeError => e
|
42
|
+
puts e.message
|
43
|
+
end
|
44
|
+
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
end
|
48
|
+
%>
|
49
|
+
|
50
|
+
<form method="get" onsubmit="return do_save_timelog();">
|
51
|
+
|
52
|
+
<%=_site.boxt(_("Enter details"))%>
|
53
|
+
<table class="form" id="formtimelogedit">
|
54
|
+
<%
|
55
|
+
opts = _ob.list_optshash(:Task, {:debug => false})
|
56
|
+
raise "Empty opts?" if opts.empty?
|
57
|
+
|
58
|
+
print _hb.inputs({
|
59
|
+
:title => _("Task"),
|
60
|
+
:name => :seltask,
|
61
|
+
:value => task_id,
|
62
|
+
:opts => opts,
|
63
|
+
:descr => _("On which task should this timelog be saved?")
|
64
|
+
},{
|
65
|
+
:title => _("Time"),
|
66
|
+
:name => :textime,
|
67
|
+
:value => time_val,
|
68
|
+
:descr => _("How much time should be logged?")
|
69
|
+
},{
|
70
|
+
:title => _("Time transportation"),
|
71
|
+
:name => :textimetransport,
|
72
|
+
:value => time_transport_val,
|
73
|
+
:descr => _("How much time was used on transportation?")
|
74
|
+
},{
|
75
|
+
:title => _("Transportation length"),
|
76
|
+
:name => :textransportlength,
|
77
|
+
:value => [timelog, :transport_length],
|
78
|
+
:descr => _("How many km was used on transportation?")
|
79
|
+
},{
|
80
|
+
:title => _("Date"),
|
81
|
+
:name => :texdate,
|
82
|
+
:value => date_val,
|
83
|
+
:descr => _("On which date was the time used?")
|
84
|
+
},{
|
85
|
+
:title => _("Invoiced"),
|
86
|
+
:name => :cheinvoiced,
|
87
|
+
:value => [timelog, :invoiced],
|
88
|
+
:descr => _("If this timelog has been invoiced or not.")
|
89
|
+
},{
|
90
|
+
:title => _("Comment"),
|
91
|
+
:name => :texcomment,
|
92
|
+
:value => [timelog, :comment],
|
93
|
+
:type => :textarea,
|
94
|
+
:descr => _("What whas done using the time."),
|
95
|
+
:height => 200
|
96
|
+
})
|
97
|
+
%>
|
98
|
+
<tr>
|
99
|
+
<td colspan="2" class="buttons">
|
100
|
+
<input type="submit" value="<%=_("Save")%>" />
|
101
|
+
</td>
|
102
|
+
</tr>
|
103
|
+
</table>
|
104
|
+
<%=_site.boxb%>
|
105
|
+
|
106
|
+
</form>
|
107
|
+
|
108
|
+
<script type="text/javascript">
|
109
|
+
modal_on_opened(function(){
|
110
|
+
$("#textime").focus();
|
111
|
+
});
|
112
|
+
|
113
|
+
function do_save_timelog(){
|
114
|
+
urlstr = "clean.rhtml?show=timelog_edit&choice=dosave<%=url%>";
|
115
|
+
formdata = forms_inputs_read($("#formtimelogedit"));
|
116
|
+
|
117
|
+
$.ajax({
|
118
|
+
type: "POST",
|
119
|
+
url: urlstr,
|
120
|
+
data: formdata,
|
121
|
+
complete: function(data){
|
122
|
+
if (data.responseText.length > 0){
|
123
|
+
alert(data.responseText);
|
124
|
+
}else{
|
125
|
+
modal_close();
|
126
|
+
events.call("on_timelog_added");
|
127
|
+
events.call("do_timelogs_update");
|
128
|
+
}
|
129
|
+
}
|
130
|
+
});
|
131
|
+
|
132
|
+
return false;
|
133
|
+
}
|
134
|
+
</script>
|
@@ -0,0 +1,318 @@
|
|
1
|
+
<%
|
2
|
+
_hb.alert(_("You do not have permission to view this page.")).back if !_site.has_rank?("admin")
|
3
|
+
|
4
|
+
if _get["choice"] == "dosearch"
|
5
|
+
begin
|
6
|
+
date_from = Datet.in(_get["texdatefrom"])
|
7
|
+
rescue
|
8
|
+
_hb.alert(_("Invalid date-from.")).back
|
9
|
+
end
|
10
|
+
|
11
|
+
begin
|
12
|
+
date_to = Datet.in(_get["texdateto"])
|
13
|
+
rescue
|
14
|
+
_hb.alert(_("Invalid date-to.")).back
|
15
|
+
end
|
16
|
+
else
|
17
|
+
date_from = Datet.new
|
18
|
+
date_from.day = 1
|
19
|
+
|
20
|
+
date_to = Datet.new
|
21
|
+
date_to.day = date_to.days_in_month
|
22
|
+
end
|
23
|
+
|
24
|
+
if _get["choice"] == "domarktimelogsasinvoiced"
|
25
|
+
_post["tlog_ids"].split(";").each do |tlog_id|
|
26
|
+
tlog = _ob.get(:Timelog, tlog_id)
|
27
|
+
tlog[:invoiced] = 1 if !tlog.invoiced?
|
28
|
+
end
|
29
|
+
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
print _site.header(_("Timelogs"))
|
34
|
+
%>
|
35
|
+
|
36
|
+
<form method="get" onsubmit="return do_submit_criterias();">
|
37
|
+
<%=Knj::Web.hiddens([{:name => :show, :value => :timelog_search}, {:name => :choice, :value => :dosearch}, {:name => :users, :value => ""}, {:name => :projects, :value => ""}])%>
|
38
|
+
|
39
|
+
<%=_site.boxt(_("Enter criteria"), "400px")%>
|
40
|
+
<table class="form">
|
41
|
+
<%
|
42
|
+
print _hb.inputs({
|
43
|
+
:title => _("Date from"),
|
44
|
+
:name => :texdatefrom,
|
45
|
+
:value => date_from.out(:time => false),
|
46
|
+
:descr => _("If you only want timelogs from a specific date and up.")
|
47
|
+
},{
|
48
|
+
:title => _("Date to"),
|
49
|
+
:name => :texdateto,
|
50
|
+
:value => date_to.out(:time => false),
|
51
|
+
:descr => _("If you only want timelogs up to a specific date.")
|
52
|
+
},{
|
53
|
+
:title => _("Comment"),
|
54
|
+
:name => :texcomment,
|
55
|
+
:value => _get["texcomment"],
|
56
|
+
:descr => _("A part of the comment in the timelog you are looking for.")
|
57
|
+
},{
|
58
|
+
:title => _("Users"),
|
59
|
+
:name => :seluser,
|
60
|
+
:opts => _ob.list_optshash(:User, :list_args => {"orderby" => "name"}),
|
61
|
+
:multiple => true,
|
62
|
+
:size => 5,
|
63
|
+
:values => _get["users"].to_s.split(";").map{|ele| ele.to_i},
|
64
|
+
:descr => _("If you want to find timelogs from specific users.")
|
65
|
+
},{
|
66
|
+
:title => _("Projects"),
|
67
|
+
:name => :selproject,
|
68
|
+
:opts => _ob.list_optshash(:Project, :list_args => {"orderby" => "name"}),
|
69
|
+
:multiple => true,
|
70
|
+
:size => 5,
|
71
|
+
:values => _get["projects"].to_s.split(";").map{|ele| ele.to_i},
|
72
|
+
:descr => _("If you want to find timelogs from specific projects.")
|
73
|
+
},{
|
74
|
+
:title => _("Invoiced"),
|
75
|
+
:name => :selinvoiced,
|
76
|
+
:value => _get["selinvoiced"],
|
77
|
+
:descr => _("If you only want to view invoiced, uninvoiced or all timelogs."),
|
78
|
+
:opts => {
|
79
|
+
"" => _("All"),
|
80
|
+
"1" => _("Only invoiced"),
|
81
|
+
"0" => _("Only un-invoiced")
|
82
|
+
}
|
83
|
+
})
|
84
|
+
%>
|
85
|
+
<tr>
|
86
|
+
<td colspan="2" class="buttons">
|
87
|
+
<input type="submit" value="<%=_("Search")%>" />
|
88
|
+
</td>
|
89
|
+
</tr>
|
90
|
+
</table>
|
91
|
+
<%=_site.boxb%>
|
92
|
+
|
93
|
+
</form>
|
94
|
+
|
95
|
+
<script type="text/javascript">
|
96
|
+
$("#texdatefrom").focus();
|
97
|
+
|
98
|
+
function do_submit_criterias(){
|
99
|
+
$("input[name=users]").val(select_values({sel: $("#seluser"), selected: true, expl: ";"}));
|
100
|
+
$("input[name=projects]").val(select_values({sel: $("#selproject"), selected: true, expl: ";"}));
|
101
|
+
$("#seluser").val(false);
|
102
|
+
return true;
|
103
|
+
}
|
104
|
+
|
105
|
+
function do_mark_invoiced(){
|
106
|
+
tlog_ids = []
|
107
|
+
$("input[type=hidden]", $("#table_customer_message")).each(function(){
|
108
|
+
id = $(this).attr("name").substring(9);
|
109
|
+
tlog_ids.push(id);
|
110
|
+
});
|
111
|
+
|
112
|
+
$.ajax({
|
113
|
+
type: "POST",
|
114
|
+
url: "/clean.rhtml?show=timelog_search&choice=domarktimelogsasinvoiced",
|
115
|
+
data: {tlog_ids: tlog_ids.join(";")},
|
116
|
+
async: true,
|
117
|
+
cache: false,
|
118
|
+
complete: function(data){
|
119
|
+
if (data.responseText.length > 0){
|
120
|
+
alert(data.responseText);
|
121
|
+
}else{
|
122
|
+
alert("<%=_'All timelogs was marked as invoiced.'%>");
|
123
|
+
}
|
124
|
+
}
|
125
|
+
});
|
126
|
+
}
|
127
|
+
</script>
|
128
|
+
|
129
|
+
<%
|
130
|
+
if _get["choice"] == "dosearch"
|
131
|
+
args = {"orderby" => "date"}
|
132
|
+
|
133
|
+
if _get["texdatefrom"].to_s.length > 0
|
134
|
+
args["date_from"] = date_from
|
135
|
+
end
|
136
|
+
|
137
|
+
if _get["texdateto"].to_s.length > 0
|
138
|
+
args["date_to"] = date_to
|
139
|
+
end
|
140
|
+
|
141
|
+
if _get["texcomment"].to_s.length > 0
|
142
|
+
args["comment_search"] = _get["texcomment"]
|
143
|
+
end
|
144
|
+
|
145
|
+
if _get["users"].to_s.length > 0
|
146
|
+
args["user_id"] = _get["users"].to_s.split(";")
|
147
|
+
end
|
148
|
+
|
149
|
+
if _get["projects"].to_s.length > 0
|
150
|
+
args["project_id"] = _get["projects"].to_s.split(";")
|
151
|
+
end
|
152
|
+
|
153
|
+
if _get["selinvoiced"].to_s == "1" or _get["selinvoiced"].to_s == "0"
|
154
|
+
args["invoiced"] = _get["selinvoiced"].to_s
|
155
|
+
end
|
156
|
+
|
157
|
+
args_count = args.merge(:count => true)
|
158
|
+
|
159
|
+
tlogs = _ob.list(:Timelog, args)
|
160
|
+
tlogs_count = _ob.list(:Timelog, args_count)
|
161
|
+
|
162
|
+
%>
|
163
|
+
<br />
|
164
|
+
|
165
|
+
<%=_site.boxt(_("Sum"), "350px")%>
|
166
|
+
<table class="form">
|
167
|
+
<%
|
168
|
+
print _hb.inputs({
|
169
|
+
:title => _("Total hours"),
|
170
|
+
:type => :info,
|
171
|
+
:value => Knj::Locales.number_out(tlogs_count[:sum_time].to_f / 3600, 1) + " / " + Knj::Locales.number_out(tlogs_count[:sum_time_transport].to_f / 3600, 1),
|
172
|
+
:descr => _("The total amount of hours of all the found time-logs (first hours used then time used on transport).")
|
173
|
+
},{
|
174
|
+
:title => _("Total transport"),
|
175
|
+
:type => :info,
|
176
|
+
:value => Knj::Locales.number_out(tlogs_count[:sum_transport_length], 1),
|
177
|
+
:descr => _("Total km's of transport used in all the timelogs found.")
|
178
|
+
})
|
179
|
+
%>
|
180
|
+
</table>
|
181
|
+
<%=_site.boxb%>
|
182
|
+
|
183
|
+
<br />
|
184
|
+
|
185
|
+
<%=_site.boxt(_("Results"))%>
|
186
|
+
<table class="form">
|
187
|
+
<thead>
|
188
|
+
<tr>
|
189
|
+
<th><%=_"Timelog"%></th>
|
190
|
+
<th><%=_"User"%></th>
|
191
|
+
<th><%=_"Date"%></th>
|
192
|
+
<th><%=_"Task"%></th>
|
193
|
+
<th><%=_"Invoiced"%></th>
|
194
|
+
<th><%=_"Time"%> / <%=_"Transport"%></th>
|
195
|
+
<th><%=_"Transport length"%></th>
|
196
|
+
</tr>
|
197
|
+
</thead>
|
198
|
+
<tbody>
|
199
|
+
<%
|
200
|
+
tlogs_sorted = {}
|
201
|
+
tlogs.each do |tlog|
|
202
|
+
dbt = Knj::Db::Dbtime.new(tlog[:time])
|
203
|
+
dbt_transport = Knj::Db::Dbtime.new(tlog[:time_transport])
|
204
|
+
tlogs_sorted[tlog[:task_id]] = [] if !tlogs_sorted.has_key?(tlog[:task_id])
|
205
|
+
tlogs_sorted[tlog[:task_id]] << tlog
|
206
|
+
|
207
|
+
%>
|
208
|
+
<tr>
|
209
|
+
<td>
|
210
|
+
<%=tlog.html%>
|
211
|
+
</td>
|
212
|
+
<td>
|
213
|
+
<%=tlog.user_html%>
|
214
|
+
</td>
|
215
|
+
<td>
|
216
|
+
<%=tlog.date_str(:time => false)%>
|
217
|
+
</td>
|
218
|
+
<td>
|
219
|
+
<%=tlog.task_html%>
|
220
|
+
</td>
|
221
|
+
<td>
|
222
|
+
<%=Knj::Strings.yn_str(tlog.invoiced?, _("Yes"), _("No"))%>
|
223
|
+
</td>
|
224
|
+
<td>
|
225
|
+
<%=Knj::Locales.number_out(dbt.hours_total, 1)%> / <%=Knj::Locales.number_out(dbt_transport.hours_total, 1)%>
|
226
|
+
</td>
|
227
|
+
<td>
|
228
|
+
<%=Knj::Locales.number_out(tlog[:transport_length], 0)%>
|
229
|
+
</td>
|
230
|
+
</tr>
|
231
|
+
<%
|
232
|
+
end
|
233
|
+
|
234
|
+
if tlogs.empty?
|
235
|
+
%>
|
236
|
+
<tr>
|
237
|
+
<td colspan="1" class="error">
|
238
|
+
<%=_("No timelogs were found.")%>
|
239
|
+
</td>
|
240
|
+
</tr>
|
241
|
+
<%
|
242
|
+
end
|
243
|
+
%>
|
244
|
+
</tbody>
|
245
|
+
</table>
|
246
|
+
<%=_site.boxb%>
|
247
|
+
|
248
|
+
<br />
|
249
|
+
|
250
|
+
<%=_site.boxt(_("Customer message"))%>
|
251
|
+
<table class="list" id="table_customer_message">
|
252
|
+
<thead>
|
253
|
+
<tr>
|
254
|
+
<th><%=_"Date"%>/<%=_"Hours"%></th>
|
255
|
+
<th><%=_"User"%></th>
|
256
|
+
<th><%=_"Description"%></th>
|
257
|
+
</tr>
|
258
|
+
</thead>
|
259
|
+
<tbody>
|
260
|
+
<%
|
261
|
+
tlogs_sorted.each do |task_id, tlogs_arr|
|
262
|
+
task = _ob.get(:Task, task_id)
|
263
|
+
|
264
|
+
%>
|
265
|
+
<tr>
|
266
|
+
<td colspan="3" class="tdt" style="padding-top: 10px;">
|
267
|
+
<%=task.name.html%>
|
268
|
+
</td>
|
269
|
+
</tr>
|
270
|
+
<%
|
271
|
+
|
272
|
+
first = true
|
273
|
+
tlogs_arr.each do |tlog|
|
274
|
+
if !first
|
275
|
+
%>
|
276
|
+
<tr>
|
277
|
+
<td colspan="3"><hr size="1" color="#cfcfcf" /></td>
|
278
|
+
</tr>
|
279
|
+
<%
|
280
|
+
end
|
281
|
+
|
282
|
+
first = false if first
|
283
|
+
|
284
|
+
%>
|
285
|
+
<tr>
|
286
|
+
<td style="text-align: center;">
|
287
|
+
<input type="hidden" name="timelogs_<%=tlog.id%>" value="1" />
|
288
|
+
|
289
|
+
<%=tlog.date_str(:time => false)%><br />
|
290
|
+
<%=Knj::Locales.number_out(tlog.time_dbt.hours_total, 2)%> / <%=Knj::Locales.number_out(tlog.time_transport_dbt.hours_total, 2)%>
|
291
|
+
</td>
|
292
|
+
<td class="nowrap">
|
293
|
+
<%=tlog.user.name.html%>
|
294
|
+
</td>
|
295
|
+
<td>
|
296
|
+
<%=tlog.comment_html%>
|
297
|
+
</td>
|
298
|
+
</tr>
|
299
|
+
<%
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
if !tlogs_sorted.empty?
|
304
|
+
%>
|
305
|
+
<tr>
|
306
|
+
<td colspan="3" style="padding-top: 15px; text-align: right;">
|
307
|
+
<input type="button" value="<%=_"Mark timelogs as invoiced"%>" onclick="if (confirm('<%=_"Are you sure you want to mark all the found timelogs as invoiced?"%>')){do_mark_invoiced();}" />
|
308
|
+
</td>
|
309
|
+
</tr>
|
310
|
+
<%
|
311
|
+
end
|
312
|
+
%>
|
313
|
+
</tbody>
|
314
|
+
</table>
|
315
|
+
<%=_site.boxb%>
|
316
|
+
<%
|
317
|
+
end
|
318
|
+
%>
|