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.
- 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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
data/www/api/task.rhtml
ADDED
data/www/api/user.rhtml
ADDED
@@ -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
|
+
%>
|
data/www/clean.rhtml
ADDED
data/www/css/default.css
ADDED
@@ -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
|
+
}
|