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,66 @@
|
|
1
|
+
class Knjtasks::User_project_link < Knj::Datarow
|
2
|
+
has_one [
|
3
|
+
:User,
|
4
|
+
:Project
|
5
|
+
]
|
6
|
+
|
7
|
+
def self.list(d)
|
8
|
+
if d.args["orderby"] == "user_name"
|
9
|
+
join_users = true
|
10
|
+
orderby = "User.name"
|
11
|
+
d.args.delete("orderby")
|
12
|
+
end
|
13
|
+
|
14
|
+
sql = "SELECT User_project_link.* FROM User_project_link"
|
15
|
+
|
16
|
+
if join_users
|
17
|
+
sql += "
|
18
|
+
LEFT JOIN User ON
|
19
|
+
User.id = User_project_link.user_id
|
20
|
+
"
|
21
|
+
end
|
22
|
+
|
23
|
+
sql += " WHERE 1=1"
|
24
|
+
|
25
|
+
ret = list_helper(d)
|
26
|
+
d.args.each do |key, val|
|
27
|
+
raise sprintf(_("Invalid key: '%s'."), key)
|
28
|
+
end
|
29
|
+
|
30
|
+
sql += ret[:sql_where]
|
31
|
+
|
32
|
+
sql += " GROUP BY User_project_link.id"
|
33
|
+
|
34
|
+
if orderby
|
35
|
+
sql += " ORDER BY #{orderby}"
|
36
|
+
else
|
37
|
+
sql += ret[:sql_order]
|
38
|
+
end
|
39
|
+
|
40
|
+
sql += ret[:sql_limit]
|
41
|
+
|
42
|
+
return d.ob.list_bysql(:User_project_link, sql)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.add(d)
|
46
|
+
user = d.ob.get(:User, d.data[:user_id])
|
47
|
+
project = d.ob.get(:Project, d.data[:project_id])
|
48
|
+
|
49
|
+
exists = d.ob.get_by(:User_project_link, {
|
50
|
+
"user" => user,
|
51
|
+
"project" => project
|
52
|
+
})
|
53
|
+
raise Errno::EEXIST, _("That user is already assigned to that project.") if exists
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_after(d)
|
57
|
+
if self.user.has_email?
|
58
|
+
subj = sprintf(_hb.gettext.gettext("Assigned to project: %s", self.user.locale), self.project.name)
|
59
|
+
|
60
|
+
html = ""
|
61
|
+
html += sprintf(_hb.gettext.gettext("You have been assigned to the project: '%s'.", self.user.locale), self.project.name)
|
62
|
+
|
63
|
+
_hb.mail(:to => self.user[:email], :subject => subj, :html => html)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Knjtasks::User_rank_link < Knj::Datarow
|
2
|
+
has_one [
|
3
|
+
:User,
|
4
|
+
{:class => :User_rank, :col => :rank_id, :method => :rank}
|
5
|
+
]
|
6
|
+
|
7
|
+
def self.add(d)
|
8
|
+
user = d.ob.get(:User, d.data[:user_id])
|
9
|
+
rank = d.ob.get(:User_rank, d.data[:rank_id])
|
10
|
+
|
11
|
+
link = d.ob.get_by(:User_rank_link, {
|
12
|
+
"user" => user,
|
13
|
+
"rank" => rank
|
14
|
+
})
|
15
|
+
raise _("That user already have that rank.") if link
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Knjtasks::User_task_list_link < Knj::Datarow
|
2
|
+
has_one [
|
3
|
+
:Task,
|
4
|
+
:User
|
5
|
+
]
|
6
|
+
|
7
|
+
def self.add(d)
|
8
|
+
task = d.ob.get(:Task, d.data[:task_id])
|
9
|
+
user = d.ob.get(:User, d.data[:user_id])
|
10
|
+
|
11
|
+
link = d.ob.get_by(:User_task_list_link, {
|
12
|
+
"task" => task,
|
13
|
+
"user" => user
|
14
|
+
})
|
15
|
+
raise _("That user has already listed that task.") if link
|
16
|
+
end
|
17
|
+
end
|
data/pages/admin.rhtml
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
<%
|
2
|
+
if _get["choice"] == "dosave"
|
3
|
+
save_hash = {
|
4
|
+
:comment => _post["texcomment"]
|
5
|
+
}
|
6
|
+
end
|
7
|
+
|
8
|
+
if _get["comment_id"].to_i > 0
|
9
|
+
comment = _ob.get(:Comment, _get["comment_id"])
|
10
|
+
url = "&comment_id=#{comment.id}"
|
11
|
+
|
12
|
+
if _get["choice"] == "dosave"
|
13
|
+
comment.update(save_hash)
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
|
17
|
+
if _get["choice"] == "dodelete"
|
18
|
+
_ob.delete(comment)
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
else
|
22
|
+
url = "&object_class=#{_get["object_class"]}&object_id=#{_get["object_id"]}"
|
23
|
+
|
24
|
+
if _get["choice"] == "dosave"
|
25
|
+
if _post.has_key?("seltaskstatus")
|
26
|
+
task = _ob.get(:Task, _get["object_id"])
|
27
|
+
last_comment = task.comments("orderby" => [["date_saved", "desc"]], "limit" => 1)
|
28
|
+
|
29
|
+
if last_comment.empty?
|
30
|
+
save_hash[:id_per_obj] = 1
|
31
|
+
else
|
32
|
+
save_hash[:id_per_obj] = last_comment.first[:id_per_obj].to_i + 1
|
33
|
+
end
|
34
|
+
|
35
|
+
if task[:status] != _post["seltaskstatus"]
|
36
|
+
task[:status] = _post["seltaskstatus"]
|
37
|
+
save_hash[:comment] = "#{_post["texcomment"]}<div style=\"padding-top: 15px;\">#{sprintf(_("Changed the task-status to '%s'."), task.status_str)}</div>"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
save_hash[:object_class] = _get["object_class"]
|
42
|
+
save_hash[:object_id] = _get["object_id"]
|
43
|
+
comment = _ob.add(:Comment, save_hash)
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
end
|
47
|
+
%>
|
48
|
+
|
49
|
+
<form id="form_comment" method="get" onsubmit="return do_save_comment();">
|
50
|
+
|
51
|
+
<%=_site.boxt(_("Enter details"))%>
|
52
|
+
<table class="form">
|
53
|
+
<%
|
54
|
+
print _hb.inputs({
|
55
|
+
:title => _("Comment"),
|
56
|
+
:name => :texcomment,
|
57
|
+
:value => [comment, :comment],
|
58
|
+
:type => :fckeditor,
|
59
|
+
:height => 380
|
60
|
+
})
|
61
|
+
|
62
|
+
if _get["object_class"] == "Task"
|
63
|
+
task = _ob.get(:Task, _get["object_id"])
|
64
|
+
|
65
|
+
print _hb.inputs({
|
66
|
+
:title => _("Task status"),
|
67
|
+
:name => :seltaskstatus,
|
68
|
+
:value => task[:status],
|
69
|
+
:opts => _ob.static(:Task, :status_opts),
|
70
|
+
:descr => _("If you want to change the status of the task as well, then you should choose the new status here.")
|
71
|
+
})
|
72
|
+
end
|
73
|
+
%>
|
74
|
+
<tr>
|
75
|
+
<td colspan="2" class="buttons">
|
76
|
+
<input type="submit" value="<%=_("Save")%>" />
|
77
|
+
</td>
|
78
|
+
</tr>
|
79
|
+
</table>
|
80
|
+
<%=_site.boxb%>
|
81
|
+
|
82
|
+
</form>
|
83
|
+
|
84
|
+
<script type="text/javascript">
|
85
|
+
function do_save_comment(){
|
86
|
+
comment_val = FCKeditorAPI.GetInstance("texcomment").GetHTML();
|
87
|
+
if (comment_val.length <= 0){
|
88
|
+
alert("<%=_('Please write a comment before saving.')%>");
|
89
|
+
return false;
|
90
|
+
}
|
91
|
+
|
92
|
+
$.ajax({
|
93
|
+
type: "POST",
|
94
|
+
url: "clean.rhtml?show=comment_edit&choice=dosave<%=url%>",
|
95
|
+
data: forms_inputs_read($("#form_comment")),
|
96
|
+
complete: function(data){
|
97
|
+
if (data.responseText.length > 0){
|
98
|
+
alert(data.responseText);
|
99
|
+
}else{
|
100
|
+
events.call("on_comment_added");
|
101
|
+
events.call("do_comments_update");
|
102
|
+
modal_close();
|
103
|
+
}
|
104
|
+
}
|
105
|
+
});
|
106
|
+
return false;
|
107
|
+
}
|
108
|
+
|
109
|
+
modal_on_opened(function(){
|
110
|
+
try{
|
111
|
+
fckeditor_set_focus();
|
112
|
+
}catch(err){
|
113
|
+
setTimeout("fckeditor_set_focus", 100);
|
114
|
+
}
|
115
|
+
});
|
116
|
+
|
117
|
+
function fckeditor_set_focus(){
|
118
|
+
oFCKeditor = FCKeditorAPI.GetInstance("texcomment");
|
119
|
+
oFCKeditor.Focus();
|
120
|
+
}
|
121
|
+
</script>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<%
|
2
|
+
_hb.alert(_("You do not have permission to view this page.")).back if !_site.has_rank?("admin")
|
3
|
+
|
4
|
+
if _get["choice"] == "doupdate"
|
5
|
+
objs = {}
|
6
|
+
_db.q("SELECT * FROM Comment WHERE id_per_obj = 0") do |comment_data|
|
7
|
+
objs[comment_data[:object_class]] = {} if !objs.has_key?(comment_data[:object_class])
|
8
|
+
objs[comment_data[:object_class]][comment_data[:object_id]] = true if !objs[comment_data[:object_class]].has_key?(comment_data[:object_id])
|
9
|
+
end
|
10
|
+
|
11
|
+
objs.each do |class_name, class_ids|
|
12
|
+
class_ids.each do |class_id, temp_val|
|
13
|
+
_db.q("SELECT * FROM Task") do |task_data|
|
14
|
+
count = 0
|
15
|
+
_db.q("SELECT * FROM Comment WHERE object_class = 'Task' AND object_id = '#{task_data[:id]}' ORDER BY date_saved") do |comment_data|
|
16
|
+
count += 1
|
17
|
+
count = comment_data[:id_per_obj].to_i if comment_data[:id_per_obj].to_i > count
|
18
|
+
_db.update(:Comment, {:id_per_obj => count}, {:id => comment_data[:id]}) if comment_data[:id_per_obj].to_i == 0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
_hb.alert(_("Done")).redirect("?show=comment_update_id_per_obj")
|
25
|
+
end
|
26
|
+
|
27
|
+
print _site.header(_("Update comment IDs"))
|
28
|
+
%>
|
29
|
+
|
30
|
+
<%=_site.boxt(_("Actions"), 350)%>
|
31
|
+
<table class="form">
|
32
|
+
<%
|
33
|
+
|
34
|
+
%>
|
35
|
+
<tr>
|
36
|
+
<td colspan="2" class="buttons">
|
37
|
+
<input type="button" value="<%=_"Update"%>" onclick="location.href='?show=comment_update_id_per_obj&choice=doupdate';" />
|
38
|
+
</td>
|
39
|
+
</tr>
|
40
|
+
</table>
|
41
|
+
<%=_site.boxb%>
|
@@ -0,0 +1,69 @@
|
|
1
|
+
<%
|
2
|
+
if !_site.has_rank?("admin")
|
3
|
+
_hb.alert(_("You are not an administrator and cannot view this page.")).back
|
4
|
+
end
|
5
|
+
|
6
|
+
if _get["choice"] == "dosave"
|
7
|
+
save_hash = {
|
8
|
+
:name => _post["texname"]
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
if _get["customer_id"].to_i > 0
|
13
|
+
customer = _ob.get(:Customer, _get["customer_id"])
|
14
|
+
title = sprintf(_("Edit customer: %s"), customer.name.html)
|
15
|
+
|
16
|
+
if _get["choice"] == "dosave"
|
17
|
+
customer.update(save_hash)
|
18
|
+
_hb.redirect("?show=customer_edit&customer_id#{customer.id}")
|
19
|
+
end
|
20
|
+
|
21
|
+
if _get["choice"] == "dodelete"
|
22
|
+
_hb.on_error_go_back do
|
23
|
+
_ob.delete(customer)
|
24
|
+
_hb.redirect("?show=customer_search")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
else
|
28
|
+
title = _("Add new customer")
|
29
|
+
|
30
|
+
if _get["choice"] == "dosave"
|
31
|
+
_hb.on_error_go_back do
|
32
|
+
customer = _ob.add(:Customer, save_hash)
|
33
|
+
_hb.redirect("?show=customer_edit&customer_id=#{customer.id}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
print _site.header(title)
|
39
|
+
%>
|
40
|
+
|
41
|
+
<form method="post" action="?show=customer_edit&choice=dosave<%if customer; print "&customer_id=#{customer.id}"; end%>">
|
42
|
+
|
43
|
+
<%=_site.boxt(_("Enter details"), "350px")%>
|
44
|
+
<table class="form">
|
45
|
+
<%
|
46
|
+
print Knj::Web.inputs([{
|
47
|
+
:title => _("Name"),
|
48
|
+
:name => :texname,
|
49
|
+
:value => [customer, :name],
|
50
|
+
:descr => _("The name of the customer as it should appear in the system.")
|
51
|
+
}])
|
52
|
+
%>
|
53
|
+
<tr>
|
54
|
+
<td colspan="2" class="buttons">
|
55
|
+
<%if customer%>
|
56
|
+
<input type="button" value="<%=_("Show")%>" onclick="location.href='?show=customer_show&customer_id=<%=customer.id%>';" />
|
57
|
+
<input type="button" value="<%=_("Delete")%>" onclick="if (confirm('<%=_("Do you want to delete this customer?")%>')){location.href='?show=customer_edit&customer_id=<%=customer.id%>&choice=dodelete';}" />
|
58
|
+
<%end%>
|
59
|
+
<input type="submit" value="<%=_("Save")%>" />
|
60
|
+
</td>
|
61
|
+
</tr>
|
62
|
+
</table>
|
63
|
+
<%=_site.boxb%>
|
64
|
+
|
65
|
+
</form>
|
66
|
+
|
67
|
+
<script type="text/javascript">
|
68
|
+
$("#texname").focus();
|
69
|
+
</script>
|
@@ -0,0 +1,80 @@
|
|
1
|
+
<%
|
2
|
+
print _site.header(_("Search for customers"))
|
3
|
+
%>
|
4
|
+
|
5
|
+
<form method="get">
|
6
|
+
<%=Knj::Web.hiddens([{:name => :show, :value => :customer_search}, {:name => :choice, :value => :dosearch}])%>
|
7
|
+
|
8
|
+
<%=_site.boxt(_("Enter criteria"), "350px")%>
|
9
|
+
<table class="form">
|
10
|
+
<%
|
11
|
+
print Knj::Web.inputs([{
|
12
|
+
:title => _("Name"),
|
13
|
+
:name => :texname,
|
14
|
+
:value => _get["texname"],
|
15
|
+
:descr => _("A part of the customers name which you are looking for.")
|
16
|
+
}])
|
17
|
+
%>
|
18
|
+
<tr>
|
19
|
+
<td colspan="2" class="buttons">
|
20
|
+
<input type="button" value="<%=_("Add new customer")%>" onclick="location.href='?show=customer_edit';" />
|
21
|
+
<input type="submit" value="<%=_("Search")%>" />
|
22
|
+
</td>
|
23
|
+
</tr>
|
24
|
+
</table>
|
25
|
+
<%=_site.boxb%>
|
26
|
+
|
27
|
+
</form>
|
28
|
+
|
29
|
+
<script type="text/javascript">
|
30
|
+
$("#texname").focus();
|
31
|
+
</script>
|
32
|
+
|
33
|
+
<%
|
34
|
+
if _get["choice"] == "dosearch"
|
35
|
+
args = {"orderby" => "name"}
|
36
|
+
|
37
|
+
if _get["texname"].to_s.length > 0
|
38
|
+
args["name_search"] = _get["texname"]
|
39
|
+
end
|
40
|
+
|
41
|
+
customers = _ob.list(:Customer, args)
|
42
|
+
|
43
|
+
%>
|
44
|
+
<br />
|
45
|
+
|
46
|
+
<%=_site.boxt(_("Results"), "350px")%>
|
47
|
+
<table class="list">
|
48
|
+
<thead>
|
49
|
+
<tr>
|
50
|
+
<th><%=_("Customer")%></th>
|
51
|
+
</tr>
|
52
|
+
</thead>
|
53
|
+
<tbody>
|
54
|
+
<%
|
55
|
+
customers.each do |customer|
|
56
|
+
%>
|
57
|
+
<tr>
|
58
|
+
<td>
|
59
|
+
<%=customer.html%>
|
60
|
+
</td>
|
61
|
+
</tr>
|
62
|
+
<%
|
63
|
+
end
|
64
|
+
|
65
|
+
if customers.empty?
|
66
|
+
%>
|
67
|
+
<tr>
|
68
|
+
<td colspan="1" class="error">
|
69
|
+
<%=_("No customers were found.")%>
|
70
|
+
</td>
|
71
|
+
</tr>
|
72
|
+
<%
|
73
|
+
end
|
74
|
+
%>
|
75
|
+
</tbody>
|
76
|
+
</table>
|
77
|
+
<%=_site.boxb%>
|
78
|
+
<%
|
79
|
+
end
|
80
|
+
%>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<%
|
2
|
+
begin
|
3
|
+
customer = _ob.get(:Customer, _get["customer_id"])
|
4
|
+
rescue
|
5
|
+
_hb.alert(_("That customer do not exist.")).back
|
6
|
+
end
|
7
|
+
|
8
|
+
print _site.header(sprintf(_("Show customer: %s"), customer.name.html))
|
9
|
+
%>
|
10
|
+
|
11
|
+
<%=_site.boxt(_("Actions"), "350px")%>
|
12
|
+
<input type="button" value="<%=_("Edit customer")%>" onclick="location.href='?show=customer_edit&customer_id=<%=customer.id%>';" />
|
13
|
+
<%=_site.boxb%>
|
14
|
+
|
15
|
+
<br />
|
16
|
+
|
17
|
+
<%=_site.boxt(_("Projects"), "350px")%>
|
18
|
+
<table class="list">
|
19
|
+
<thead>
|
20
|
+
<tr>
|
21
|
+
<th><%=_("Project")%></th>
|
22
|
+
</tr>
|
23
|
+
</thead>
|
24
|
+
<tbody>
|
25
|
+
<%
|
26
|
+
projects = customer.projects
|
27
|
+
|
28
|
+
projects.each do |project|
|
29
|
+
%>
|
30
|
+
<tr>
|
31
|
+
<td>
|
32
|
+
<%=project.html%>
|
33
|
+
</td>
|
34
|
+
</tr>
|
35
|
+
<%
|
36
|
+
end
|
37
|
+
|
38
|
+
if projects.empty?
|
39
|
+
%>
|
40
|
+
<tr>
|
41
|
+
<td colspan="1" class="error">
|
42
|
+
<%=_("No projects has been added for this customer.")%>
|
43
|
+
</td>
|
44
|
+
</tr>
|
45
|
+
<%
|
46
|
+
end
|
47
|
+
%>
|
48
|
+
</tbody>
|
49
|
+
</table>
|
50
|
+
<%=_site.boxb%>
|