ditz 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +15 -0
- data/INSTALL +20 -0
- data/LICENSE +674 -0
- data/Manifest.txt +42 -0
- data/PLUGINS.txt +99 -0
- data/README.txt +50 -35
- data/Rakefile +34 -2
- data/ReleaseNotes +6 -0
- data/bin/ditz +45 -68
- data/contrib/completion/ditz.bash +7 -7
- data/lib/blue-check.png +0 -0
- data/lib/component.rhtml +10 -4
- data/lib/ditz.rb +15 -3
- data/lib/file-storage.rb +54 -0
- data/lib/green-bar.png +0 -0
- data/lib/green-check.png +0 -0
- data/lib/hook.rb +1 -1
- data/lib/html.rb +39 -4
- data/lib/index.rhtml +80 -59
- data/lib/issue.rhtml +91 -79
- data/lib/issue_table.rhtml +24 -29
- data/lib/lowline.rb +5 -6
- data/lib/model-objects.rb +50 -17
- data/lib/model.rb +84 -26
- data/lib/operator.rb +151 -70
- data/lib/plugins/git-sync.rb +83 -0
- data/lib/plugins/git.rb +67 -15
- data/lib/plugins/issue-claiming.rb +174 -0
- data/lib/red-check.png +0 -0
- data/lib/release.rhtml +69 -38
- data/lib/style.css +138 -39
- data/lib/trollop.rb +614 -0
- data/lib/unassigned.rhtml +11 -15
- data/lib/util.rb +4 -0
- data/lib/views.rb +3 -1
- data/lib/yellow-bar.png +0 -0
- data/man/ditz.1 +38 -0
- data/setup.rb +1585 -0
- data/www/index.html +152 -0
- data/www/main.css +45 -0
- metadata +26 -7
@@ -6,16 +6,16 @@
|
|
6
6
|
|
7
7
|
_ditz()
|
8
8
|
{
|
9
|
-
cur=${COMP_WORDS[COMP_CWORD]}
|
9
|
+
local cur=${COMP_WORDS[COMP_CWORD]}
|
10
10
|
if [ $COMP_CWORD -eq 1 ]; then
|
11
|
-
COMPREPLY=( $( compgen -W "$(ditz --commands)" $cur ) )
|
11
|
+
COMPREPLY=( $( compgen -W "$(ditz --commands)" -- $cur ) )
|
12
12
|
elif [ $COMP_CWORD -eq 2 ]; then
|
13
|
-
cmd=${COMP_WORDS[1]}
|
14
|
-
COMPREPLY=( $( compgen -W "$(ditz "$cmd" '<options>' 2>/dev/null)" $cur ) )
|
13
|
+
local cmd=${COMP_WORDS[1]}
|
14
|
+
COMPREPLY=( $( compgen -W "$(ditz "$cmd" '<options>' 2>/dev/null)" -- $cur ) )
|
15
15
|
elif [ $COMP_CWORD -eq 3 ]; then
|
16
|
-
cmd=${COMP_WORDS[1]}
|
17
|
-
parm1=${COMP_WORDS[2]}
|
18
|
-
COMPREPLY=( $( compgen -W "$(ditz "$cmd" "$parm1" '<options>' 2>/dev/null)" $cur ) )
|
16
|
+
local cmd=${COMP_WORDS[1]}
|
17
|
+
local parm1=${COMP_WORDS[2]}
|
18
|
+
COMPREPLY=( $( compgen -W "$(ditz "$cmd" "$parm1" '<options>' 2>/dev/null)" -- $cur ) )
|
19
19
|
fi
|
20
20
|
}
|
21
21
|
|
data/lib/blue-check.png
ADDED
Binary file
|
data/lib/component.rhtml
CHANGED
@@ -1,18 +1,24 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
4
|
+
|
1
5
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
2
6
|
<head>
|
3
7
|
<title>Component <%= component.name %></title>
|
4
|
-
<meta http-equiv="Content-Type" content="text/html; charset=
|
8
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
5
9
|
<link rel="stylesheet" href="style.css" type="text/css" />
|
6
10
|
</head>
|
7
11
|
|
8
12
|
<body>
|
9
13
|
|
10
|
-
|
14
|
+
<div class="main">
|
11
15
|
|
12
16
|
<h1><%= project.name %> component: <%= component.name %></h1>
|
17
|
+
<div class="backptr"><%= link_to "index", "« #{project.name} project page" %></div>
|
13
18
|
|
19
|
+
<h2>All issues</h2>
|
14
20
|
<%= render "issue_table", :show_component => false, :show_release => true %>
|
15
|
-
|
16
|
-
<
|
21
|
+
</div>
|
22
|
+
<div class="footer">Generated by <a href="http://ditz.rubyforge.org/">ditz</a>.</div>
|
17
23
|
</body>
|
18
24
|
</html>
|
data/lib/ditz.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Ditz
|
2
2
|
|
3
|
-
VERSION = "0.
|
3
|
+
VERSION = "0.5"
|
4
4
|
|
5
5
|
def debug s
|
6
|
-
puts "# #{s}" if $
|
6
|
+
puts "# #{s}" if $verbose
|
7
7
|
end
|
8
8
|
module_function :debug
|
9
9
|
|
@@ -46,11 +46,23 @@ def find_ditz_file fn
|
|
46
46
|
File.expand_path File.join(dir, fn)
|
47
47
|
end
|
48
48
|
|
49
|
-
|
49
|
+
def load_plugins fn
|
50
|
+
Ditz::debug "loading plugins from #{fn}"
|
51
|
+
plugins = YAML::load_file $opts[:plugins_file]
|
52
|
+
plugins.each do |p|
|
53
|
+
fn = Ditz::find_ditz_file "plugins/#{p}.rb"
|
54
|
+
Ditz::debug "loading plugin #{p.inspect} from #{fn}"
|
55
|
+
require File.expand_path(fn)
|
56
|
+
end
|
57
|
+
plugins
|
58
|
+
end
|
59
|
+
|
60
|
+
module_function :home_dir, :find_dir_containing, :find_ditz_file, :load_plugins
|
50
61
|
end
|
51
62
|
|
52
63
|
require 'model-objects'
|
53
64
|
require 'operator'
|
54
65
|
require 'views'
|
55
66
|
require 'hook'
|
67
|
+
require 'file-storage'
|
56
68
|
|
data/lib/file-storage.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Ditz
|
2
|
+
|
3
|
+
## stores ditz database on disk
|
4
|
+
class FileStorage
|
5
|
+
PROJECT_FN = "project.yaml"
|
6
|
+
ISSUE_FN_GLOB = "issue-*.yaml"
|
7
|
+
|
8
|
+
def ISSUE_TO_FN i; "issue-#{i.id}.yaml" end
|
9
|
+
|
10
|
+
def initialize base_dir
|
11
|
+
@base_dir = base_dir
|
12
|
+
@project_fn = File.join @base_dir, PROJECT_FN
|
13
|
+
end
|
14
|
+
|
15
|
+
def load
|
16
|
+
Ditz::debug "loading project from #{@project_fn}"
|
17
|
+
project = Project.from @project_fn
|
18
|
+
|
19
|
+
fn = File.join @base_dir, ISSUE_FN_GLOB
|
20
|
+
Ditz::debug "loading issues from #{fn}"
|
21
|
+
project.issues = Dir[fn].map { |fn| Issue.from fn }
|
22
|
+
Ditz::debug "found #{project.issues.size} issues"
|
23
|
+
|
24
|
+
project.issues.each { |i| i.project = project }
|
25
|
+
project
|
26
|
+
end
|
27
|
+
|
28
|
+
def save project
|
29
|
+
dirty = false
|
30
|
+
dirty = project.each_modelobject { |o| break true if o.changed? }
|
31
|
+
if dirty
|
32
|
+
Ditz::debug "project is dirty, saving #{@project_fn}"
|
33
|
+
project.save! @project_fn
|
34
|
+
end
|
35
|
+
|
36
|
+
changed_issues = project.issues.select { |i| i.changed? }
|
37
|
+
changed_issues.each do |i|
|
38
|
+
fn = filename_for_issue i
|
39
|
+
Ditz::debug "issue #{i.name} is dirty, saving #{fn}"
|
40
|
+
i.save! fn
|
41
|
+
end
|
42
|
+
|
43
|
+
project.deleted_issues.each do |i|
|
44
|
+
fn = filename_for_issue i
|
45
|
+
Ditz::debug "issue #{i.name} has been deleted, deleting #{fn}"
|
46
|
+
FileUtils.rm fn
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def filename_for_issue i; File.join @base_dir, ISSUE_TO_FN(i) end
|
51
|
+
def filename_for_project; @project_fn end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/green-bar.png
ADDED
Binary file
|
data/lib/green-check.png
ADDED
Binary file
|
data/lib/hook.rb
CHANGED
data/lib/html.rb
CHANGED
@@ -48,16 +48,51 @@ class ErbHtml
|
|
48
48
|
raise ArgumentError, "no link for #{o.inspect}" unless dest
|
49
49
|
"<a href=\"#{dest}\">#{name}</a>"
|
50
50
|
end
|
51
|
-
|
52
|
-
|
51
|
+
|
52
|
+
def issue_status_img_for i, opts={}
|
53
|
+
fn, title = if i.closed?
|
54
|
+
case i.disposition
|
55
|
+
when :fixed; ["green-check.png", "fixed"]
|
56
|
+
when :wontfix; ["red-check.png", "won't fix"]
|
57
|
+
when :reorg; ["blue-check.png", "reorganized"]
|
58
|
+
end
|
59
|
+
elsif i.in_progress?
|
60
|
+
["green-bar.png", "in progress"]
|
61
|
+
elsif i.paused?
|
62
|
+
["yellow-bar.png", "paused"]
|
63
|
+
end
|
64
|
+
|
65
|
+
return "" unless fn
|
66
|
+
|
67
|
+
args = {:src => fn, :alt => title, :title => title}
|
68
|
+
args[:class] = opts[:class] if opts[:class]
|
69
|
+
|
70
|
+
"<img " + args.map { |k, v| "#{k}=#{v.inspect}" }.join(" ") + "/>"
|
53
71
|
end
|
54
72
|
|
55
|
-
def
|
73
|
+
def issue_link_for i, opts={}
|
74
|
+
link = link_to i, "#{i.title}"
|
75
|
+
link = "<span class=\"inline-issue-link\">" + link + "</span>" if opts[:inline]
|
76
|
+
link = link + " " + issue_status_img_for(i, :class => "inline-status-image") if opts[:status_image]
|
77
|
+
link
|
78
|
+
end
|
79
|
+
|
80
|
+
def link_issue_names project, s, opts={}
|
56
81
|
project.issues.inject(s) do |s, i|
|
57
|
-
s.gsub(/\b#{i.name}\b/,
|
82
|
+
s.gsub(/\b#{i.name}\b/, issue_link_for(i, {:inline => true, :status_image => true}.merge(opts)))
|
58
83
|
end
|
59
84
|
end
|
60
85
|
|
86
|
+
def progress_meter p, size=50
|
87
|
+
done = (p * size).to_i
|
88
|
+
undone = [size - done, 0].max
|
89
|
+
"<span class='progress-meter'><span class='progress-meter-done'>" +
|
90
|
+
(" " * done) +
|
91
|
+
"</span><span class='progress-meter-undone'>" +
|
92
|
+
(" " * undone) +
|
93
|
+
"</span></span>"
|
94
|
+
end
|
95
|
+
|
61
96
|
## render a nested ERB
|
62
97
|
alias :render :render_template
|
63
98
|
|
data/lib/index.rhtml
CHANGED
@@ -1,51 +1,65 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
4
|
+
|
1
5
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
2
6
|
<head>
|
3
7
|
<title><%= project.name %> Issue Tracker</title>
|
4
|
-
<meta http-equiv="Content-Type" content="text/html; charset=
|
8
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
5
9
|
<link rel="stylesheet" href="style.css" type="text/css" />
|
6
10
|
</head>
|
7
11
|
<body>
|
8
12
|
|
13
|
+
<div class="main">
|
9
14
|
<h1><%= project.name %> Issue Tracker</h1>
|
10
15
|
|
11
16
|
<h2>Upcoming Releases</h2>
|
12
17
|
<% if upcoming_releases.empty? %>
|
13
18
|
<p>No upcoming releases.</p>
|
14
19
|
<% else %>
|
15
|
-
<
|
16
|
-
|
17
|
-
<%
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
20
|
+
<table>
|
21
|
+
<tbody>
|
22
|
+
<% upcoming_releases.each do |r| %>
|
23
|
+
<%
|
24
|
+
issues = project.issues_for_release r
|
25
|
+
num_done = issues.count_of { |i| i.closed? }
|
26
|
+
pct_done = issues.size == 0 ? 1.0 : (num_done.to_f / issues.size.to_f)
|
27
|
+
open_issues = issues.select { |i| i.open? }
|
28
|
+
%>
|
29
|
+
<tr><td>
|
30
|
+
<%= link_to r, "#{r.name}" %>
|
31
|
+
</td>
|
32
|
+
<td>
|
33
|
+
<% if issues.empty? %>
|
34
|
+
no issues
|
35
|
+
<% elsif open_issues.empty? %>
|
36
|
+
ready for release!
|
31
37
|
<% else %>
|
32
|
-
<%=
|
38
|
+
<%= progress_meter pct_done %>
|
39
|
+
<%= sprintf "%.0f%%", pct_done * 100.0 %> complete
|
40
|
+
</td>
|
41
|
+
</tr><tr><td></td><td>
|
42
|
+
<%= num_done %> / <%= issues.size %> issues.
|
43
|
+
<%= link_to r, "See issues »" %>
|
33
44
|
<% end %>
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
45
|
+
</td>
|
46
|
+
</tr>
|
47
|
+
<% end %>
|
48
|
+
</tbody>
|
49
|
+
</table>
|
38
50
|
<% end %>
|
39
51
|
|
40
52
|
<h2>Past Releases</h2>
|
41
53
|
<% if past_releases.empty? %>
|
42
54
|
<p>No past releases.</p>
|
43
55
|
<% else %>
|
44
|
-
<
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
56
|
+
<table>
|
57
|
+
<tbody>
|
58
|
+
<% past_releases.sort_by { |r| r.release_time }.reverse.each do |r| %>
|
59
|
+
<tr><td><%= link_to r, r.name %></td><td class="littledate">on <%= r.release_time.pretty_date %></td></tr>
|
60
|
+
<% end %>
|
61
|
+
</tbody>
|
62
|
+
</table>
|
49
63
|
<% end %>
|
50
64
|
|
51
65
|
<h2>Unassigned issues</h2>
|
@@ -57,53 +71,60 @@
|
|
57
71
|
<% if issues.empty? %>
|
58
72
|
No unassigned issues.
|
59
73
|
<% else %>
|
60
|
-
<%= link_to "unassigned", "unassigned issue".pluralize(issues.size).capitalize
|
74
|
+
<%= link_to "unassigned", "unassigned issue".pluralize(issues.size).capitalize %> (<%= open_issues.size.to_pretty_s %> open).
|
61
75
|
<% end %>
|
62
76
|
</p>
|
63
77
|
|
64
78
|
<% if components.size > 1 %>
|
65
79
|
<h2>Open Issues by component</h2>
|
66
|
-
<
|
67
|
-
|
68
|
-
<%
|
69
|
-
|
70
|
-
|
71
|
-
|
80
|
+
<table>
|
81
|
+
<tbody>
|
82
|
+
<% components.each do |c| %>
|
83
|
+
<%
|
84
|
+
issues = project.issues_for_component c
|
85
|
+
num_done = issues.count_of { |i| i.closed? }
|
86
|
+
pct_done = issues.size == 0 ? 1.0 : (num_done.to_f / issues.size.to_f)
|
87
|
+
open_issues = issues.select { |i| i.open? }
|
88
|
+
%>
|
72
89
|
<% if open_issues.empty? %>
|
73
|
-
<
|
74
|
-
<%= link_to c, c.name %>:
|
75
|
-
no open issues.
|
76
|
-
</span>
|
90
|
+
<tr class="dimmed">
|
77
91
|
<% else %>
|
78
|
-
|
79
|
-
<%= open_issues.map { |n,is| n.to_s.pluralize is.size }.join(' and ') %> open.
|
92
|
+
<tr>
|
80
93
|
<% end %>
|
81
|
-
|
82
|
-
|
83
|
-
|
94
|
+
<td>
|
95
|
+
<%= link_to c, c.name %>
|
96
|
+
</td><td>
|
97
|
+
<%= "open issue".pluralize(open_issues.size) %>
|
98
|
+
</td></tr>
|
99
|
+
<% end %>
|
100
|
+
</tbody>
|
101
|
+
</table>
|
84
102
|
<% end %>
|
85
103
|
|
86
104
|
<h2>Recent activity</h2>
|
87
105
|
|
88
|
-
<table>
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
<
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
106
|
+
<table class="log">
|
107
|
+
<tbody>
|
108
|
+
<% project.issues.map { |i| i.log_events.map { |e| [e, i] } }.
|
109
|
+
flatten_one_level.
|
110
|
+
sort_by { |e| e.first.first }.
|
111
|
+
reverse[0 ... 10].
|
112
|
+
each_with_index do |((date, who, what, comment), i), idx| %>
|
113
|
+
<tr class="<%= idx % 2 == 0 ? "even-row" : "odd-row" %>">
|
114
|
+
<td class="date"><%= date.pretty_date %></td>
|
115
|
+
<td class="issuename">
|
116
|
+
<%= issue_link_for i, :status_image => true %>
|
117
|
+
</td>
|
118
|
+
<td> <%= what %> </td>
|
119
|
+
</tr>
|
120
|
+
<tr><td></td></tr>
|
102
121
|
<% end %>
|
103
|
-
|
122
|
+
</tbody>
|
104
123
|
</table>
|
105
124
|
|
106
|
-
|
125
|
+
</div>
|
126
|
+
|
127
|
+
<div class="footer">Generated by <a href="http://ditz.rubyforge.org/">ditz</a>.</div>
|
107
128
|
|
108
129
|
</body>
|
109
130
|
</html>
|
data/lib/issue.rhtml
CHANGED
@@ -1,107 +1,119 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
4
|
+
|
1
5
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
2
6
|
<head>
|
3
7
|
<title><%= issue.title %></title>
|
4
|
-
<meta http-equiv="Content-Type" content="text/html; charset=
|
8
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
5
9
|
<link rel="stylesheet" href="style.css" type="text/css" />
|
6
10
|
</head>
|
7
11
|
|
8
12
|
<body>
|
9
13
|
|
10
|
-
|
14
|
+
<div class="main">
|
11
15
|
|
12
16
|
<h1><%= link_issue_names project, issue.title %></h1>
|
17
|
+
<div class="backptr"><%= link_to "index", "« #{project.name} project page" %></div>
|
13
18
|
|
14
|
-
|
19
|
+
<% if issue.desc && !issue.desc.empty? %>
|
20
|
+
<div class="description">
|
21
|
+
<%= link_issue_names project, p(issue.desc) %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
15
24
|
|
25
|
+
<h2>Details</h2>
|
16
26
|
<table>
|
17
|
-
<
|
18
|
-
<
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
<
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
<
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
<
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
<
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
27
|
+
<tbody>
|
28
|
+
<tr>
|
29
|
+
<td class="attrname">Id:</td>
|
30
|
+
<td class="attrval"><span class="id"><%= issue.id %></span></td>
|
31
|
+
</tr>
|
32
|
+
|
33
|
+
<tr>
|
34
|
+
<td class="attrname">Type:</td>
|
35
|
+
<td class="attrval"><%= issue.type %></td>
|
36
|
+
</tr>
|
37
|
+
|
38
|
+
<tr>
|
39
|
+
<td class="attrname">Creation time:</td>
|
40
|
+
<td class="attrval"><%=t issue.creation_time %></td>
|
41
|
+
</tr>
|
42
|
+
|
43
|
+
<tr>
|
44
|
+
<td class="attrname">Creator:</td>
|
45
|
+
<td class="attrval"><span class="person"><%=obscured_email issue.reporter %></span></td>
|
46
|
+
</tr>
|
47
|
+
|
48
|
+
<% unless issue.references.empty? %>
|
49
|
+
<tr>
|
50
|
+
<td class="attrname">References:</td>
|
51
|
+
<td class="attrval">
|
52
|
+
<% issue.references.each_with_index do |r, i| %>
|
53
|
+
[<%= i + 1 %>] <%= link_to r, r %><br/>
|
54
|
+
<% end %>
|
55
|
+
</td>
|
56
|
+
</tr>
|
46
57
|
|
47
|
-
|
58
|
+
<% end %>
|
48
59
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
60
|
+
<tr>
|
61
|
+
<td class="attrname">Release:</td>
|
62
|
+
<td class="attrval">
|
63
|
+
<% if release %>
|
64
|
+
<%= link_to release, release.name %>
|
65
|
+
<% if release.released? %>
|
66
|
+
(released <%= release.release_time.pretty_date %>)
|
67
|
+
<% else %>
|
68
|
+
(unreleased)
|
69
|
+
<% end %>
|
56
70
|
<% else %>
|
57
|
-
|
71
|
+
<%= link_to "unassigned", "unassigned" %>
|
58
72
|
<% end %>
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
<
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
<%= extra_summary_html %>
|
78
|
-
|
73
|
+
</td>
|
74
|
+
</tr>
|
75
|
+
|
76
|
+
<tr>
|
77
|
+
<td class="attrname">Component:</td>
|
78
|
+
<td class="attrval"><%= link_to component, component.name %></td>
|
79
|
+
</tr>
|
80
|
+
|
81
|
+
<tr>
|
82
|
+
<td class="attrname">Status:</td>
|
83
|
+
<td class="attrval">
|
84
|
+
<%= issue.status_string %><% if issue.closed? %>: <%= issue.disposition_string %><% end %>
|
85
|
+
<%= issue_status_img_for issue, :class => "inline-status-image" %>
|
86
|
+
</td>
|
87
|
+
</tr>
|
88
|
+
|
89
|
+
<%= extra_summary_html %>
|
90
|
+
</tbody>
|
79
91
|
</table>
|
80
92
|
|
81
93
|
<%= extra_details_html %>
|
82
94
|
|
83
95
|
<h2>Issue log</h2>
|
84
96
|
|
85
|
-
<table>
|
86
|
-
|
87
|
-
<%
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
<% if comment.empty? %>
|
98
|
-
<% else %>
|
99
|
-
<%= link_issue_names project, p(comment) %>
|
97
|
+
<table class="log">
|
98
|
+
<tbody>
|
99
|
+
<% issue.log_events.reverse.each_with_index do |(time, who, what, comment), i| %>
|
100
|
+
<tr class="<%= i % 2 == 0 ? "even-row" : "odd-row" %>">
|
101
|
+
<td class="date"><%=t time %></td>
|
102
|
+
<td class="person"><%=obscured_email who %></td>
|
103
|
+
<td class="message"><%=h what %></td>
|
104
|
+
</tr>
|
105
|
+
<% unless comment.empty? %>
|
106
|
+
<tr><td colspan="3" class="logcomment">
|
107
|
+
<%= link_issue_names project, comment %>
|
108
|
+
</td></tr>
|
100
109
|
<% end %>
|
101
|
-
|
102
|
-
<% end %>
|
110
|
+
<tr><td></td></tr>
|
111
|
+
<% end %>
|
112
|
+
</tbody>
|
103
113
|
</table>
|
104
114
|
|
105
|
-
|
115
|
+
</div>
|
116
|
+
<div class="footer">Generated by <a href="http://ditz.rubyforge.org/">ditz</a>.</div>
|
117
|
+
|
106
118
|
</body>
|
107
119
|
</html>
|