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
@@ -0,0 +1,83 @@
|
|
1
|
+
## git-sync ditz plugin
|
2
|
+
##
|
3
|
+
## This plugin is useful for when you want synchronized, non-distributed issue
|
4
|
+
## coordination with other developers, and you're using git. It allows you to
|
5
|
+
## synchronize issue updates with other developers by using the 'ditz sync'
|
6
|
+
## command, which does all the git work of sending and receiving issue change
|
7
|
+
## for you. However, you have to set things up in a very specific way for this
|
8
|
+
## to work:
|
9
|
+
##
|
10
|
+
## 1. Your ditz state must be on a separate branch. I recommend calling it
|
11
|
+
## 'bugs'. Create this branch, do a ditz init, and push it to the remote
|
12
|
+
## repo. (This means you won't be able to mingle issue change and code
|
13
|
+
## change in the same commits. If you care.)
|
14
|
+
## 2. Make a checkout of the bugs branch in a separate directory, but NOT in
|
15
|
+
## your code checkout. If you're developing in a directory called "project",
|
16
|
+
## I recommend making a ../project-bugs/ directory, cloning the repo there
|
17
|
+
## as well, and keeping that directory checked out to the 'bugs' branch.
|
18
|
+
## (There are various complicated things you can do to make that directory
|
19
|
+
## share git objects with your code directory, but I wouldn't bother unless
|
20
|
+
## you really care about disk space. Just make it an independent clone.)
|
21
|
+
## 3. Set that directory as your issue-dir in your .ditz-config file in your
|
22
|
+
## code checkout directory. (This file should be in .gitignore, btw.)
|
23
|
+
## 4. Run 'ditz reconfigure' and fill in the local branch name, remote
|
24
|
+
## branch name, and remote repo for the issue tracking branch.
|
25
|
+
##
|
26
|
+
## Once that's set up, 'ditz sync' will change to the bugs checkout dir, bundle
|
27
|
+
## up any changes you've made to issue status, push them to the remote repo,
|
28
|
+
## and pull any new changes in too. All ditz commands will read from your bugs
|
29
|
+
## directory, so you should be able to use ditz without caring about where
|
30
|
+
## things are anymore.
|
31
|
+
##
|
32
|
+
## This complicated setup is necessary to avoid accidentally mingling code
|
33
|
+
## change and issue change. With this setup, issue change is synchronized,
|
34
|
+
## but how you synchronize code is still up to you.
|
35
|
+
##
|
36
|
+
## Usage:
|
37
|
+
## 0. read all the above text very carefully
|
38
|
+
## 1. add a line "- git-sync" to the .ditz-plugins file in the project
|
39
|
+
## root
|
40
|
+
## 2. run 'ditz reconfigure' and answer its questions
|
41
|
+
## 3. run 'ditz sync' with abandon
|
42
|
+
|
43
|
+
module Ditz
|
44
|
+
|
45
|
+
class Config
|
46
|
+
field :git_sync_local_branch, :prompt => "Local bugs branch name for ditz sync", :default => "bugs"
|
47
|
+
field :git_sync_remote_branch, :prompt => "Remote bugs branch name for ditz sync", :default => "bugs"
|
48
|
+
field :git_sync_remote_repo, :prompt => "Remote bugs repo name for ditz sync", :default => "origin"
|
49
|
+
end
|
50
|
+
|
51
|
+
class Operator
|
52
|
+
operation :sync, "Sync the repo containing ditz via git" do
|
53
|
+
opt :dry_run, "Dry run: print the commants, but don't execute them", :short => "n"
|
54
|
+
end
|
55
|
+
def sync project, config, opts
|
56
|
+
unless config.git_sync_local_branch
|
57
|
+
$stderr.puts "Please run ditz reconfigure and set the local and remote branch names"
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
Dir.chdir $project_root
|
62
|
+
puts "[in #{$project_root}]"
|
63
|
+
sh "git add *.yaml", :force => true, :fake => opts[:dry_run]
|
64
|
+
sh "git commit -m 'issue updates'", :force => true, :fake => opts[:dry_run]
|
65
|
+
sh "git pull", :fake => opts[:dry_run]
|
66
|
+
sh "git push #{config.git_sync_remote_repo} #{config.git_sync_local_branch}:#{config.git_sync_remote_branch}", :fake => opts[:dry_run]
|
67
|
+
puts
|
68
|
+
puts "Ditz issue state synchronized."
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def sh s, opts={}
|
74
|
+
puts "+ #{s}"
|
75
|
+
return if opts[:fake]
|
76
|
+
unless system(s) || opts[:force]
|
77
|
+
$stderr.puts "non-zero (#{$?.exitstatus}) exit code: #{s}"
|
78
|
+
exit(-1)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
data/lib/plugins/git.rb
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
## git ditz plugin
|
2
|
+
##
|
3
|
+
## This plugin allows issues to be associated with git commits and git
|
4
|
+
## branches. Git commits can be easily tagged with a ditz issue with the 'ditz
|
5
|
+
## commit' command, and both 'ditz show' and the ditz HTML output will then
|
6
|
+
## contain a list of associated commits for each issue.
|
7
|
+
##
|
8
|
+
## Issues can also be assigned a single git feature branch. In this case, all
|
9
|
+
## commits on that branch will listed as commits for that issue. This
|
10
|
+
## particular feature is fairly rudimentary, however---it assumes the reference
|
11
|
+
## point is the 'master' branch, and once the feature branch is merged back
|
12
|
+
## into master, the list of commits disappears.
|
13
|
+
##
|
14
|
+
## Two configuration variables are added, which, when specified, are used to
|
15
|
+
## construct HTML links for the git commit id and branch names in the generated
|
16
|
+
## HTML output.
|
17
|
+
##
|
18
|
+
## Commands added:
|
19
|
+
## ditz set-branch: set the git branch of an issue
|
20
|
+
## ditz commit: run git-commit, and insert the issue id into the commit
|
21
|
+
## message.
|
22
|
+
##
|
23
|
+
## Usage:
|
24
|
+
## 1. add a line "- git" to the .ditz-plugins file in the project root
|
25
|
+
## 2. run ditz reconfigure, and enter the URL prefixes, if any, from
|
26
|
+
## which to create commit and branch links.
|
27
|
+
## 3. use 'ditz commit' with abandon.
|
28
|
+
|
1
29
|
require 'time'
|
2
30
|
|
3
31
|
module Ditz
|
@@ -20,8 +48,8 @@ class Issue
|
|
20
48
|
end
|
21
49
|
|
22
50
|
class Config
|
23
|
-
field :git_commit_url_prefix, :prompt => "URL prefix
|
24
|
-
field :git_branch_url_prefix, :prompt => "URL prefix
|
51
|
+
field :git_commit_url_prefix, :prompt => "URL prefix to link git commits to", :default => ""
|
52
|
+
field :git_branch_url_prefix, :prompt => "URL prefix to link git branches to", :default => ""
|
25
53
|
end
|
26
54
|
|
27
55
|
class ScreenView
|
@@ -44,7 +72,7 @@ class HtmlView
|
|
44
72
|
[<<EOS, { :issue => issue, :url_prefix => config.git_branch_url_prefix }]
|
45
73
|
<tr>
|
46
74
|
<td class='attrname'>Git branch:</td>
|
47
|
-
<td class='attrval'><%= url_prefix ? link_to([url_prefix, issue.git_branch].join, issue.git_branch) : h(issue.git_branch) %></td>
|
75
|
+
<td class='attrval'><%= url_prefix && !url_prefix.blank? ? link_to([url_prefix, issue.git_branch].join, issue.git_branch) : h(issue.git_branch) %></td>
|
48
76
|
</tr>
|
49
77
|
EOS
|
50
78
|
end
|
@@ -55,17 +83,14 @@ EOS
|
|
55
83
|
|
56
84
|
[<<EOS, { :commits => commits, :url_prefix => config.git_commit_url_prefix }]
|
57
85
|
<h2>Commits for this issue</h2>
|
58
|
-
<table>
|
86
|
+
<table class="log">
|
59
87
|
<% commits.each_with_index do |(time, who, hash, msg), i| %>
|
60
|
-
|
61
|
-
<
|
62
|
-
|
63
|
-
<
|
64
|
-
<% end %>
|
65
|
-
<td class="logtime"><%=t time %></td>
|
66
|
-
<td class="logwho"><%=obscured_email who %></td>
|
67
|
-
<td class="logwhat"><%=h msg %> [<%= url_prefix ? link_to([url_prefix, hash].join, hash) : hash %>]</td>
|
88
|
+
<tr class="<%= i % 2 == 0 ? "even-row" : "odd-row" %>">
|
89
|
+
<td class="time"><%=t time %></td>
|
90
|
+
<td class="person"><%=obscured_email who %></td>
|
91
|
+
<td class="message"><%=h msg %> [<%= url_prefix && !url_prefix.blank? ? link_to([url_prefix, hash].join, hash) : hash %>]</td>
|
68
92
|
</tr>
|
93
|
+
<tr><td></td></tr>
|
69
94
|
<% end %>
|
70
95
|
</table>
|
71
96
|
EOS
|
@@ -92,9 +117,36 @@ class Operator
|
|
92
117
|
issue.git_branch = branch
|
93
118
|
end
|
94
119
|
|
95
|
-
operation :commit, "Runs git-commit and auto-fills the issue name in the commit message", :issue
|
96
|
-
|
97
|
-
|
120
|
+
operation :commit, "Runs git-commit and auto-fills the issue name in the commit message", :issue do
|
121
|
+
opt :all, "commit all changed files", :short => "-a", :default => false
|
122
|
+
opt :verbose, "show diff between HEAD and what would be committed", \
|
123
|
+
:short => "-v", :default => false
|
124
|
+
opt :message, "Use the given <s> as the commit message.", \
|
125
|
+
:short => "-m", :type => :string
|
126
|
+
opt :edit, "Further edit the message, even if --message is given.", :short => "-e", :default => false
|
127
|
+
end
|
128
|
+
|
129
|
+
def commit project, config, opts, issue
|
130
|
+
opts[:edit] = true if opts[:message].nil?
|
131
|
+
|
132
|
+
args = {
|
133
|
+
:verbose => "--verbose",
|
134
|
+
:all => "--all",
|
135
|
+
:edit => "--edit",
|
136
|
+
}.map { |k, v| opts[k] ? v : "" }.join(" ")
|
137
|
+
|
138
|
+
comment = "# #{issue.name}: #{issue.title}"
|
139
|
+
tag = "Ditz-issue: #{issue.id}"
|
140
|
+
message = if opts[:message] && !opts[:edit]
|
141
|
+
"#{opts[:message]}\n\n#{tag}"
|
142
|
+
elsif opts[:message] && opts[:edit]
|
143
|
+
"#{opts[:message]}\n\n#{comment}\n#{tag}"
|
144
|
+
else
|
145
|
+
"#{comment}\n#{tag}"
|
146
|
+
end
|
147
|
+
|
148
|
+
message = message.gsub("\"", "\\\"")
|
149
|
+
exec "git commit #{args} --message=\"#{message}\""
|
98
150
|
end
|
99
151
|
end
|
100
152
|
|
@@ -0,0 +1,174 @@
|
|
1
|
+
## issue-claiming ditz plugin
|
2
|
+
##
|
3
|
+
## This plugin allows people to claim issues. This is useful for avoiding
|
4
|
+
## duplication of work---you can check to see if someone's claimed an
|
5
|
+
## issue before starting to work on it, and you can let people know what
|
6
|
+
## you're working on.
|
7
|
+
##
|
8
|
+
## Commands added:
|
9
|
+
## ditz claim: claim an issue for yourself
|
10
|
+
## ditz unclaim: unclaim a claimed issue
|
11
|
+
## ditz mine: show all issues claimed by you
|
12
|
+
## ditz claimed: show all claimed issues, by developer
|
13
|
+
## ditz unclaimed: show all unclaimed issues
|
14
|
+
##
|
15
|
+
## Usage:
|
16
|
+
## 1. add a line "- issue-claiming" to the .ditz-plugins file in the project
|
17
|
+
## root
|
18
|
+
## 2. use the above commands to abandon
|
19
|
+
|
20
|
+
module Ditz
|
21
|
+
class Issue
|
22
|
+
field :claimer, :ask => false
|
23
|
+
|
24
|
+
def claim who, comment, force=false
|
25
|
+
raise Error, "already claimed by #{claimer}" if claimer && !force
|
26
|
+
log "issue claimed", who, comment
|
27
|
+
self.claimer = who
|
28
|
+
end
|
29
|
+
|
30
|
+
def unclaim who, comment, force=false
|
31
|
+
raise Error, "not claimed" unless claimer
|
32
|
+
raise Error, "can only be unclaimed by #{claimer}" unless claimer == who || force
|
33
|
+
if claimer == who
|
34
|
+
log "issue unclaimed", who, comment
|
35
|
+
else
|
36
|
+
log "unassigned from #{claimer}", who, comment
|
37
|
+
end
|
38
|
+
self.claimer = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def claimed?; claimer end
|
42
|
+
def unclaimed?; !claimed? end
|
43
|
+
end
|
44
|
+
|
45
|
+
class ScreenView
|
46
|
+
add_to_view :issue_summary do |issue, config|
|
47
|
+
" Claimed by: #{issue.claimer || 'none'}\n"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class HtmlView
|
52
|
+
add_to_view :issue_summary do |issue, config|
|
53
|
+
next unless issue.claimer
|
54
|
+
[<<EOS, { :issue => issue }]
|
55
|
+
<tr>
|
56
|
+
<td class='attrname'>Claimed by:</td>
|
57
|
+
<td class='attrval'><%= h(issue.claimer) %></td>
|
58
|
+
</tr>
|
59
|
+
EOS
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Operator
|
64
|
+
alias :__issue_claiming_start :start
|
65
|
+
def start project, config, opts, issue
|
66
|
+
if issue.claimed? && issue.claimer != config.user
|
67
|
+
raise Error, "issue #{issue.name} claimed by #{issue.claimer}"
|
68
|
+
else
|
69
|
+
__issue_claiming_start project, config, opts, issue
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
alias :__issue_claiming_stop :stop
|
74
|
+
def stop project, config, opts, issue
|
75
|
+
if issue.claimed? && issue.claimer != config.user
|
76
|
+
raise Error, "issue #{issue.name} claimed by #{issue.claimer}"
|
77
|
+
else
|
78
|
+
__issue_claiming_stop project, config, opts, issue
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
alias :__issue_claiming_close :close
|
83
|
+
def close project, config, opts, issue
|
84
|
+
if issue.claimed? && issue.claimer != config.user
|
85
|
+
raise Error, "issue #{issue.name} claimed by #{issue.claimer}"
|
86
|
+
else
|
87
|
+
__issue_claiming_close project, config, opts, issue
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
operation :claim, "Claim an issue for yourself", :issue do
|
92
|
+
opt :force, "Claim this issue even if someone else has claimed it", :default => false
|
93
|
+
end
|
94
|
+
def claim project, config, opts, issue
|
95
|
+
puts "Claiming issue #{issue.name}: #{issue.title}."
|
96
|
+
comment = ask_multiline "Comments" unless $opts[:no_comment]
|
97
|
+
issue.claim config.user, comment, opts[:force]
|
98
|
+
puts "Issue #{issue.name} marked as claimed by #{config.user}"
|
99
|
+
end
|
100
|
+
|
101
|
+
operation :unclaim, "Unclaim a claimed issue", :issue do
|
102
|
+
opt :force, "Unclaim this issue even if it's claimed by someone else", :default => false
|
103
|
+
end
|
104
|
+
def unclaim project, config, opts, issue
|
105
|
+
puts "Unclaiming issue #{issue.name}: #{issue.title}."
|
106
|
+
comment = ask_multiline "Comments" unless $opts[:no_comment]
|
107
|
+
issue.unclaim config.user, comment, opts[:force]
|
108
|
+
puts "Issue #{issue.name} marked as unclaimed."
|
109
|
+
end
|
110
|
+
|
111
|
+
operation :mine, "Show all issues claimed by you", :maybe_release do
|
112
|
+
opt :all, "Show all issues, not just open ones"
|
113
|
+
end
|
114
|
+
def mine project, config, opts, releases
|
115
|
+
releases ||= project.unreleased_releases + [:unassigned]
|
116
|
+
releases = [*releases]
|
117
|
+
|
118
|
+
issues = project.issues.select do |i|
|
119
|
+
r = project.release_for(i.release) || :unassigned
|
120
|
+
releases.member?(r) && i.claimer == config.user && (opts[:all] || i.open?)
|
121
|
+
end
|
122
|
+
if issues.empty?
|
123
|
+
puts "No issues."
|
124
|
+
else
|
125
|
+
print_todo_list_by_release_for project, issues
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
operation :claimed, "Show claimed issues by claimer", :maybe_release do
|
130
|
+
opt :all, "Show all issues, not just open ones"
|
131
|
+
end
|
132
|
+
def claimed project, config, opts, releases
|
133
|
+
releases ||= project.unreleased_releases + [:unassigned]
|
134
|
+
releases = [*releases]
|
135
|
+
|
136
|
+
issues = project.issues.inject({}) do |h, i|
|
137
|
+
r = project.release_for(i.release) || :unassigned
|
138
|
+
if i.claimed? && (opts[:all] || i.open?) && releases.member?(r)
|
139
|
+
(h[i.claimer] ||= []) << i
|
140
|
+
end
|
141
|
+
h
|
142
|
+
end
|
143
|
+
|
144
|
+
if issues.empty?
|
145
|
+
puts "No issues."
|
146
|
+
else
|
147
|
+
issues.keys.sort.each do |c|
|
148
|
+
puts "#{c}:"
|
149
|
+
puts todo_list_for(issues[c], :show_release => true)
|
150
|
+
puts
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
operation :unclaimed, "Show all unclaimed issues", :maybe_release do
|
156
|
+
opt :all, "Show all issues, not just open ones"
|
157
|
+
end
|
158
|
+
def unclaimed project, config, opts, releases
|
159
|
+
releases ||= project.unreleased_releases + [:unassigned]
|
160
|
+
releases = [*releases]
|
161
|
+
|
162
|
+
issues = project.issues.select do |i|
|
163
|
+
r = project.release_for(i.release) || :unassigned
|
164
|
+
releases.member?(r) && i.claimer.nil? && (opts[:all] || i.open?)
|
165
|
+
end
|
166
|
+
if issues.empty?
|
167
|
+
puts "No issues."
|
168
|
+
else
|
169
|
+
print_todo_list_by_release_for project, issues
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
data/lib/red-check.png
ADDED
Binary file
|
data/lib/release.rhtml
CHANGED
@@ -1,37 +1,48 @@
|
|
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 %> release <%= release.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
|
<body>
|
8
12
|
|
9
|
-
|
13
|
+
<div class="main">
|
10
14
|
|
11
15
|
<h1><%= project.name %> release <%= release.name %></h1>
|
16
|
+
<div class="backptr"><%= link_to "index", "« #{project.name} project page" %></div>
|
12
17
|
|
13
|
-
<p>
|
14
18
|
<table>
|
15
|
-
<
|
16
|
-
<td class="attrname">Status:</td>
|
17
|
-
<td class="attrval"><%= release.status %></td>
|
18
|
-
</tr>
|
19
|
-
<% if release.released? %>
|
19
|
+
<tbody>
|
20
20
|
<tr>
|
21
|
-
<td class="attrname">
|
22
|
-
<td class="attrval"><%= release.
|
21
|
+
<td class="attrname">Status:</td>
|
22
|
+
<td class="attrval"><%= release.status %></td>
|
23
23
|
</tr>
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
%>
|
30
|
-
<
|
31
|
-
|
32
|
-
|
24
|
+
<% if release.released? %>
|
25
|
+
<tr>
|
26
|
+
<td class="attrname">Release time:</td>
|
27
|
+
<td class="attrval"><%=t release.release_time %></td>
|
28
|
+
</tr>
|
29
|
+
<% end %>
|
30
|
+
<tr>
|
31
|
+
<td class="attrname">Completion:</td>
|
32
|
+
<td>
|
33
|
+
<%
|
34
|
+
num_done = issues.count_of { |i| i.closed? }
|
35
|
+
pct_done = issues.size == 0 ? 1.0 : (num_done.to_f / issues.size.to_f)
|
36
|
+
%>
|
37
|
+
<%= progress_meter pct_done %>
|
38
|
+
<%= sprintf "%.0f%%", pct_done * 100.0 %>
|
39
|
+
</td>
|
40
|
+
</tr>
|
41
|
+
<tr><td></td><td class="attrval">
|
42
|
+
<%= num_done %> / <%= issues.size %> issues
|
43
|
+
</td></tr>
|
44
|
+
</tbody>
|
33
45
|
</table>
|
34
|
-
</p>
|
35
46
|
|
36
47
|
<h2>Issues</h2>
|
37
48
|
<% if issues.empty? %>
|
@@ -40,28 +51,48 @@
|
|
40
51
|
<%= render "issue_table", :show_component => false, :show_release => false %>
|
41
52
|
<% end %>
|
42
53
|
|
54
|
+
<h2>Recent activity for this release</h2>
|
55
|
+
<table class="log">
|
56
|
+
<tbody>
|
57
|
+
<% issues.map { |i| i.log_events.map { |e| [e, i] } }.
|
58
|
+
flatten_one_level.
|
59
|
+
sort_by { |e| e.first.first }.
|
60
|
+
reverse[0 ... 10].
|
61
|
+
each_with_index do |((date, who, what, comment), i), idx| %>
|
62
|
+
<tr class="<%= idx % 2 == 0 ? "even-row" : "odd-row" %>">
|
63
|
+
<td class="date"><%= date.pretty_date %></td>
|
64
|
+
<td class="issuename">
|
65
|
+
<%= issue_link_for i, :status_image => true %>
|
66
|
+
</td>
|
67
|
+
<td> <%= what %> </td>
|
68
|
+
</tr>
|
69
|
+
<tr><td></td></tr>
|
70
|
+
<% end %>
|
71
|
+
</tbody>
|
72
|
+
</table>
|
73
|
+
|
43
74
|
<h2>Release log</h2>
|
44
|
-
<table>
|
45
|
-
|
46
|
-
<%
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
<% end %>
|
60
|
-
</td></tr>
|
75
|
+
<table class="log">
|
76
|
+
<tbody>
|
77
|
+
<% release.log_events.reverse.each_with_index do |(time, who, what, comment), i| %>
|
78
|
+
<tr class="<%= i % 2 == 0 ? "even-row" : "odd-row" %>">
|
79
|
+
<td class="date"><%=h time %></td>
|
80
|
+
<td class="person"><%=obscured_email who %></td>
|
81
|
+
<td class="message"><%=h what %></td>
|
82
|
+
</tr>
|
83
|
+
<tr><td colspan="3" class="logcomment">
|
84
|
+
<% if comment.empty? %>
|
85
|
+
<% else %>
|
86
|
+
<%= link_issue_names project, comment %>
|
87
|
+
<% end %>
|
88
|
+
</td></tr>
|
89
|
+
<tr><td></td></tr>
|
61
90
|
<% end %>
|
91
|
+
</tbody>
|
62
92
|
</table>
|
63
93
|
|
64
|
-
|
94
|
+
</div>
|
95
|
+
<div class="footer">Generated by <a href="http://ditz.rubyforge.org/">ditz</a>.</div>
|
65
96
|
|
66
97
|
</body>
|
67
98
|
</html>
|