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,23 @@
1
+ args = {
2
+ "host" => $validate_login["_SERVER"]["HTTP_HOST"]
3
+ }
4
+
5
+ if $validate_login["_SERVER"]["HTTPS"] or $validate_login["_SERVER"]["HTTP_SSL_ENABLED"]
6
+ args["ssl"] = true
7
+ args["port"] = 443
8
+ args["validate"] = false
9
+ end
10
+
11
+ http = Knj::Http.new(args)
12
+ http.cookies["KnjappserverSession"] = $validate_login["_COOKIE"]["KnjappserverSession"]
13
+ data = http.get("/?show=users_login")
14
+
15
+ if data["data"].to_s.index("<form method=\"post\" action=\"?show=user_login") == nil
16
+ print JSON.generate(
17
+ "Enabled" => true
18
+ )
19
+ else
20
+ print JSON.generate(
21
+ "Enabled" => false
22
+ )
23
+ end
@@ -0,0 +1,115 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Knjtasks" do
4
+ it "should be able to require the needed frameworks and gems" do
5
+ require "rubygems"
6
+ require "sqlite3"
7
+ require "knjrbfw"
8
+ require "knjtasks"
9
+ Knj.gem_require(:Hayabusa)
10
+ Knj.gem_require(:Http2)
11
+ end
12
+
13
+ it "should be able to require all the model-framework-files." do
14
+ models_path = "#{File.dirname(__FILE__)}/../models"
15
+ Dir.new(models_path).each do |file|
16
+ next if file == "." or file == ".."
17
+ fp = "#{models_path}/#{file}"
18
+ require fp
19
+ end
20
+ end
21
+
22
+ it "should be able to generate a sample database and start a test-environment." do
23
+ db_path = "#{Knj::Os.tmpdir}/knjtasks_spec_database_sample.sqlite3"
24
+ File.unlink(db_path) if File.exists?(db_path)
25
+
26
+ db = Knj::Db.new(
27
+ :type => "sqlite3",
28
+ :path => db_path,
29
+ :return_keys => "symbols",
30
+ :index_append_table_name => true
31
+ )
32
+ tasks = Knjtasks.new(
33
+ :title => "rake_rspec",
34
+ :host => "0.0.0.0",
35
+ :port => 1515,
36
+ :db => db,
37
+ :knjjs_url => "http://www.kaspernj.org/js",
38
+ :email_admin => "k@spernj.org",
39
+ :email_robot => "k@spernj.org",
40
+ :smtp_args => {
41
+ "smtp_host" => "localhost",
42
+ "smtp_port" => 25
43
+ },
44
+ :db_args => false
45
+ )
46
+
47
+ tasks.start
48
+ $tasks = tasks
49
+
50
+ #The admin-user should exists.
51
+ user = tasks.ob.get_by(:User, "username" => "admin", "passwd" => Digest::MD5.hexdigest("admin"))
52
+ raise "Expected admin user to exist but it didnt." if !user
53
+
54
+ $http = Http2.new(:host => "localhost", :port => 1515, :follow_redirects => false)
55
+ end
56
+
57
+ it "should be able to log in" do
58
+ res = $http.post(:url => "?show=user_login&choice=dologin", :post => {
59
+ "texuser" => "admin",
60
+ "texpass_md5" => Digest::MD5.hexdigest("admin"),
61
+ "cheremember" => "on"
62
+ })
63
+
64
+ res = $http.get("?show=user_login")
65
+ raise "Expected to be logged in but wasnt according to HTML:\n#{res.body}" if res.body.index("You are logged in as") == nil
66
+ end
67
+
68
+ it "should be able to add customers" do
69
+ res = $http.post(:url => "?show=customer_edit&choice=dosave", :post => {
70
+ "texname" => "Test customer"
71
+ })
72
+
73
+ if match = res.body.match(/location\.href="\?show=customer_edit&customer_id=(\d+)"/)
74
+ $customer = $tasks.ob.get(:Customer, match[1])
75
+ else
76
+ raise "Expected to be redirected to the new customer but wasnt:\n#{res.body}"
77
+ end
78
+ end
79
+
80
+ it "should be able to add projects" do
81
+ res = $http.post(:url => "?show=project_edit&choice=dosave", :post => {
82
+ "texname" => "Test project",
83
+ "selcustomer" => $customer.id,
84
+ "texdescr" => "Test project"
85
+ })
86
+
87
+ if match = res.body.match(/location\.href="\?show=project_edit&project_id=(\d+)"/)
88
+ $project = $tasks.ob.get(:Project, match[1])
89
+ else
90
+ raise "Expected to be redirected to new project but wasnt:\n#{res.body}"
91
+ end
92
+ end
93
+
94
+ it "should be able to add tasks" do
95
+ res = $http.post(:url => "?show=task_edit&choice=dosave", :post => {
96
+ "texname" => "Test task",
97
+ "texdescr" => "Test description",
98
+ "selproject" => $project.id,
99
+ "type" => "feature"
100
+ })
101
+
102
+ if match = res.body.match(/location\.href="\?show=task_show&task_id=(\d+)"/)
103
+ $task = $tasks.ob.get(:Task, match[1])
104
+ else
105
+ raise "Expected to be redirected to new task but wasnt:\n#{res.body}"
106
+ end
107
+ end
108
+
109
+ it "should show special HTML for expired sessions" do
110
+ res = $http.get(:url => "?show=user_login&choice=dologout")
111
+ res = $http.get(:url => "clean.rhtml?show=task_show&task_id=#{$task.id}&choice=gettimelogs")
112
+
113
+ raise "Expected message about not logged in but got this instead: '#{res.body}'." if res.body != "You are not logged in. Log in and try to view this page again."
114
+ end
115
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'knjtasks'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
@@ -0,0 +1,114 @@
1
+ #This class checks the mail-account for mails and imports them as comments.
2
+ class Knjtasks::Thread_mail_task_comments
3
+ def initialize(args = {})
4
+ @args = args
5
+
6
+ require "rubygems"
7
+ require "mail"
8
+
9
+ if !@args[:args][:mail_args][:port]
10
+ @args[:args][:mail_args][:port] = 143 if @args[:args][:mail_args][:type] == "imap"
11
+ @args[:args][:mail_args][:port] = 100 if @args[:args][:mail_args][:type] == "pop3"
12
+ end
13
+ end
14
+
15
+ def run
16
+ STDOUT.print "Checking for mails.\n" if @args[:appsrv].debug
17
+ conn = Net::IMAP.new(@args[:args][:mail_args][:host], @args[:args][:mail_args][:port].to_i, @args[:args][:mail_args][:ssl])
18
+
19
+ if @args[:args][:mail_args][:user] and @args[:args][:mail_args][:pass]
20
+ conn.login(@args[:args][:mail_args][:user], @args[:args][:mail_args][:pass])
21
+ end
22
+
23
+ conn.select("INBOX")
24
+ emails = conn.search(["ALL"])
25
+ emails.each do |msg_id|
26
+ error = nil
27
+ from = nil
28
+ html = nil
29
+
30
+ begin
31
+ msg = conn.fetch(msg_id, "(ENVELOPE RFC822)")
32
+ mail = Mail.new(msg[0].attr["RFC822"])
33
+
34
+ env = msg[0].attr["ENVELOPE"]
35
+ from = "#{env.from.first.mailbox}@#{env.from.first.host}"
36
+ env_msg_id = env.message_id
37
+
38
+ if _ob.static(:Email_check, :checked_id?, env_msg_id)
39
+ next
40
+ else
41
+ _db.insert(:Email_check, {:email_id_str => env_msg_id})
42
+ end
43
+
44
+ user = @args[:ob].get_by(:User, {"email" => from})
45
+ raise "Could not find a user in this task-system with that email: '#{from}'." if !user
46
+
47
+ subj_match = mail.subject.to_s.match(/^([A-z]{1,3}):\s+(\S+)\s+#(\d+):\s+/)
48
+ raise "Could not figure out the task-ID from the email." if !subj_match
49
+ task_id = subj_match[3]
50
+
51
+ begin
52
+ task = @args[:ob].get(:Task, task_id)
53
+ rescue
54
+ raise "Could not find a task in this task-system with that task-ID: '#{task_id}'."
55
+ end
56
+
57
+ parts = {}
58
+ mail.parts.each do |part|
59
+ if part.content_type.match(/^text\/plain/)
60
+ parts[:plain] = part.body.to_s
61
+ elsif part.content_type.match(/^text\/html/) and match = part.body.match(/<body.*>([\s\S]+)<\/body>/)
62
+ parts[:html] = match[1]
63
+ end
64
+ end
65
+
66
+ if parts[:html]
67
+ html = parts[:html]
68
+ html.gsub!(/<blockquote([\s\S]+?)>([\s\S]+?)<\/blockquote>/i, "")
69
+ html.gsub!(/on\s+\d+.+?\s+wrote:/i, "")
70
+ html.gsub!(/<pre.+?class="moz-signature".*?>([\s\S]*?)<\/pre>/i, "")
71
+ html = Knj::Strings.strip(html, {
72
+ :strips => [" ", "\n", "\r", "<br>", "<br />", "<br/>"]
73
+ })
74
+ elsif parts[:plain]
75
+ html = parts[:plain]
76
+ html.gsub!(/on\s+\d+.+?\s+wrote:/i, "")
77
+ html.gsub!(/^>(.*?)\n/, "")
78
+ html.gsub!(/\n-- \n[\s\S]++/, "")
79
+ html = Knj::Strings.strip(html, {
80
+ :strips => ["\n", "\r", "<br>", "<br />", "<br/>"]
81
+ })
82
+ html.gsub!(/\n/, "<br>")
83
+ end
84
+
85
+ raise "Could not read any content in the email." if !html
86
+
87
+ comment = @args[:ob].add(:Comment, {
88
+ :object_class => :Task,
89
+ :object_id => task.id,
90
+ :comment => html,
91
+ :user_id => user.id
92
+ })
93
+
94
+ conn.store(msg_id, "+FLAGS", ["DELETED"])
95
+ conn.expunge
96
+ rescue => e
97
+ if !from
98
+ STDOUT.print "Could not respond to task-email."
99
+ STDOUT.puts e.inspect
100
+ STDOUT.puts e.backtrace
101
+ else
102
+ @args[:appsrv].mail(
103
+ :to => from,
104
+ :subject => "Your email to the task-system.",
105
+ :text => "Could not parse your email:\n\n#{e.inspect}\n#{e.backtrace.join("\n")}"
106
+ )
107
+ end
108
+ end
109
+ end
110
+
111
+ conn.logout
112
+ conn.disconnect
113
+ end
114
+ end
@@ -0,0 +1,9 @@
1
+ <%
2
+ arr = []
3
+
4
+ _ob.list(:Task) do |task|
5
+ arr << task.data
6
+ end
7
+
8
+ print JSON.generate(arr)
9
+ %>
@@ -0,0 +1,20 @@
1
+ <%
2
+ if _get["choice"] == "dologin"
3
+ user = _ob.get_by(:User, {
4
+ "username" => _post["username"],
5
+ "passwd" => _post["password"]
6
+ })
7
+ if !user
8
+ print JSON.generate(
9
+ "result" => false,
10
+ "msg" => "User not found."
11
+ )
12
+ else
13
+ print JSON.generate(
14
+ "result" => true,
15
+ "msg" => "Logged in as user #{user.id}.",
16
+ "user_data" => user.data
17
+ )
18
+ end
19
+ end
20
+ %>
@@ -0,0 +1,14 @@
1
+ <%
2
+ _site.load_request
3
+
4
+ show = _get["show"]
5
+ show = "frontpage" if !show
6
+ showp = "../pages/#{show}.rhtml"
7
+
8
+ if !_site.user
9
+ print _("You are not logged in. Log in and try to view this page again.")
10
+ exit
11
+ end
12
+
13
+ _hb.import(showp)
14
+ %>
@@ -0,0 +1,186 @@
1
+ html, body, input, textarea, select, td{
2
+ font-family: verdana, tahoma, arial;
3
+ font-size: 12px;
4
+ color: black;
5
+ }
6
+
7
+ html, body, html > body > center{
8
+ width: 100%;
9
+ height: 100%;
10
+ margin: 0px;
11
+ padding: 0px;
12
+ }
13
+
14
+ body{
15
+ background: url("/gfx/body_bg.jpg") 0 0 repeat;
16
+ }
17
+
18
+ a:link, a:visited{
19
+ color: #698295;
20
+ text-decoration: none;
21
+ }
22
+
23
+ a:active, a:hover{
24
+ color: #272727;
25
+ text-decoration: none;
26
+ }
27
+
28
+ table.main_table{
29
+ height: 100%;
30
+ width: 1200px;
31
+ border-top: none;
32
+ border-bottom: none;
33
+ text-align: left;
34
+ }
35
+
36
+ td.main_logo{
37
+ font-size: 24px;
38
+ padding: 15px;
39
+ padding-left: 28px;
40
+ height: 40px;
41
+ }
42
+
43
+ div.main_menu_outer{
44
+ position: relative;
45
+ height: 19px;
46
+ }
47
+
48
+ div.main_menu_inner{
49
+ position: absolute;
50
+ top: -15px;
51
+ font-size: 16px;
52
+ }
53
+
54
+ td.main_bottom{
55
+ text-align: center;
56
+ padding: 4px;
57
+ background-color: #ffffff;
58
+ height: 25px;
59
+ }
60
+
61
+ td.main_corner_top_left{
62
+ width: 41px;
63
+ height: 41px;
64
+ background: url("/gfx/main_box_top_left.png") 0 0 no-repeat;
65
+ }
66
+
67
+ td.main_corner_top_right{
68
+ width: 41px;
69
+ height: 41px;
70
+ background: url("/gfx/main_box_top_right.png") 0 0 no-repeat;
71
+ }
72
+
73
+ td.main_side_top{
74
+ height: 41px;
75
+ background: url("/gfx/main_box_top.png") 0 0 repeat-x;
76
+ }
77
+
78
+ td.main_side_left{
79
+ width: 41px;
80
+ background: url("/gfx/main_box_left.png") 0 0 repeat-y;
81
+ }
82
+
83
+ td.main_side_right{
84
+ width: 41px;
85
+ background: url("/gfx/main_box_right.png") 0 0 repeat-y;
86
+ }
87
+
88
+ td.main_body{
89
+ background-color: #ffffff;
90
+ vertical-align: top;
91
+ }
92
+
93
+ table.box{
94
+ border: none;
95
+ }
96
+
97
+ td.box_header{
98
+ font-weight: bold;
99
+ white-space: nowrap;
100
+ text-align: right;
101
+ background-color: #666666;
102
+ padding: 5px;
103
+ color: #d9d9d9;
104
+ }
105
+
106
+ td.box_header_spacer{
107
+ width: 27%;
108
+ }
109
+
110
+ td.box_content{
111
+ border: 1px solid #666666;
112
+ padding: 7px;
113
+ padding-right: 14px;
114
+ background-color: #ffffff;
115
+ }
116
+
117
+ .buttons{
118
+ text-align: right;
119
+ }
120
+
121
+ table.form, table.list{
122
+ width: 100%;
123
+ }
124
+
125
+ thead > tr > th{
126
+ font-size: 12px;
127
+ text-align: left;
128
+ white-space: nowrap;
129
+ }
130
+
131
+ td.tdt, td.tdcheck{
132
+ font-weight: bold;
133
+ white-space: nowrap;
134
+ }
135
+
136
+ td.tdc{
137
+ width: 100%;
138
+ }
139
+
140
+ td.tdd, div.tdd{
141
+ color: grey;
142
+ padding-top: 0px;
143
+ padding-bottom: 7px;
144
+ font-size: 10px;
145
+ }
146
+
147
+ td.tdr, th.tdr{
148
+ text-align: right;
149
+ }
150
+
151
+ .error{
152
+ font-style: italic;
153
+ }
154
+
155
+ td.error{
156
+ text-align: center;
157
+ }
158
+
159
+ input, select, textarea{
160
+ border: 1px solid #bbbbbb;
161
+ background-color: #ffffff;
162
+ color: #000000;
163
+ padding: 4px;
164
+ }
165
+
166
+ input[type=button], input[type=submit], input.button{
167
+ border: 1px solid #989898;
168
+ background: url("/gfx/button_bg.png") 0 0 repeat-x;
169
+ height: 20px;
170
+ font-size: 12px;
171
+ padding: 0px 9px 0px 9px;
172
+ margin: 0px 0px 0px 0px;
173
+ }
174
+
175
+ input[type=checkbox]{
176
+ border: none;
177
+ background-color: transparent;
178
+ }
179
+
180
+ input.input_text, input.input_password, select.input_select, textarea.input_textarea{
181
+ width: 100%;
182
+ }
183
+
184
+ .nowrap{
185
+ white-space: nowrap;
186
+ }