41 properties += config.items(opts['filter']) |
41 properties += config.items(opts['filter']) |
42 |
42 |
43 properties += _get_properties(opts['property']) |
43 properties += _get_properties(opts['property']) |
44 |
44 |
45 for issue in issues: |
45 for issue in issues: |
46 mbox = mailbox.mbox(issue) |
46 mbox = mailbox.Maildir(issue, factory=mailbox.MaildirMessage) |
|
47 root = _find_root_key(mbox) |
47 property_match = True |
48 property_match = True |
48 for property,value in properties: |
49 for property,value in properties: |
49 property_match = property_match and (mbox[0][property] == value) |
50 property_match = property_match and (mbox[root][property] == value) |
50 if not show_all and (not properties or not property_match) and (properties or mbox[0]['State'].upper() == state['fixed'].upper()): continue |
51 if not show_all and (not properties or not property_match) and (properties or mbox[root]['State'].upper() == state['fixed'].upper()): continue |
51 |
52 |
52 |
53 |
53 if match_date and not date_match(util.parsedate(mbox[0]['date'])[0]): continue |
54 if match_date and not date_match(util.parsedate(mbox[root]['date'])[0]): continue |
54 ui.write("%s (%3d) [%s]: %s\n" % (issue[len(issues_path)+1:], # +1 for trailing / |
55 ui.write("%s (%3d) [%s]: %s\n" % (issue[len(issues_path)+1:], # +1 for trailing / |
55 len(mbox)-1, # number of replies (-1 for self) |
56 len(mbox)-1, # number of replies (-1 for self) |
56 mbox[0]['State'], |
57 mbox[root]['State'], |
57 mbox[0]['Subject'])) |
58 mbox[root]['Subject'])) |
58 |
59 |
59 |
60 |
60 def iadd(ui, repo, id = None, comment = 0): |
61 def iadd(ui, repo, id = None, comment = 0): |
61 """Adds a new issue, or comment to an existing issue ID or its comment COMMENT""" |
62 """Adds a new issue, or comment to an existing issue ID or its comment COMMENT""" |
62 |
63 |
87 if issue.strip() == default_issue_text: |
88 if issue.strip() == default_issue_text: |
88 ui.warn('Unchanged issue text, ignoring\n') |
89 ui.warn('Unchanged issue text, ignoring\n') |
89 return |
90 return |
90 |
91 |
91 # Create the message |
92 # Create the message |
92 msg = mailbox.mboxMessage(issue) |
93 msg = mailbox.MaildirMessage(issue) |
93 msg.set_from('artemis', True) |
94 #msg.set_from('artemis', True) |
94 |
95 |
95 # Pick random filename |
96 # Pick random filename |
96 if not id: |
97 if not id: |
97 issue_fn = issues_path |
98 issue_fn = issues_path |
98 while os.path.exists(issue_fn): |
99 while os.path.exists(issue_fn): |
99 issue_id = _random_id() |
100 issue_id = _random_id() |
100 issue_fn = os.path.join(issues_path, issue_id) |
101 issue_fn = os.path.join(issues_path, issue_id) |
101 # else: issue_fn already set |
102 # else: issue_fn already set |
102 |
103 |
103 # Add message to the mailbox |
104 # Add message to the mailbox |
104 mbox = mailbox.mbox(issue_fn) |
105 mbox = mailbox.Maildir(issue_fn) |
105 if id and comment not in mbox: |
106 keys = _order_keys_date(mbox) |
|
107 mbox.lock() |
|
108 if id and comment >= len(mbox): |
106 ui.warn('No such comment number in mailbox, commenting on the issue itself\n') |
109 ui.warn('No such comment number in mailbox, commenting on the issue itself\n') |
|
110 |
107 if not id: |
111 if not id: |
108 msg.add_header('Message-Id', "<%s-0-artemis@%s>" % (issue_id, socket.gethostname())) |
112 msg.add_header('Message-Id', "<%s-0-artemis@%s>" % (issue_id, socket.gethostname())) |
109 else: |
113 else: |
|
114 root = keys[0] |
110 msg.add_header('Message-Id', "<%s-%s-artemis@%s>" % (issue_id, _random_id(), socket.gethostname())) |
115 msg.add_header('Message-Id', "<%s-%s-artemis@%s>" % (issue_id, _random_id(), socket.gethostname())) |
111 msg.add_header('References', mbox[(comment < len(mbox) and comment) or 0]['Message-Id']) |
116 msg.add_header('References', mbox[(comment < len(mbox) and keys[comment]) or root]['Message-Id']) |
112 msg.add_header('In-Reply-To', mbox[(comment < len(mbox) and comment) or 0]['Message-Id']) |
117 msg.add_header('In-Reply-To', mbox[(comment < len(mbox) and keys[comment]) or root]['Message-Id']) |
113 mbox.add(msg) |
118 repo.add([issue_fn[(len(repo.root)+1):] + '/new/' + mbox.add(msg)]) # +1 for the trailing / |
114 mbox.close() |
119 mbox.close() |
115 |
120 |
116 # If adding issue, add the new mailbox to the repository |
121 # If adding issue, add the new mailbox to the repository |
117 if not id: |
122 if not id: |
118 repo.add([issue_fn[(len(repo.root)+1):]]) # +1 for the trailing / |
|
119 ui.status('Added new issue %s\n' % issue_id) |
123 ui.status('Added new issue %s\n' % issue_id) |
120 |
124 |
121 |
125 |
122 def ishow(ui, repo, id, comment = 0, **opts): |
126 def ishow(ui, repo, id, comment = 0, **opts): |
123 """Shows issue ID, or possibly its comment COMMENT""" |
127 """Shows issue ID, or possibly its comment COMMENT""" |
124 |
128 |
125 comment = int(comment) |
129 comment = int(comment) |
126 issue, id = _find_issue(ui, repo, id) |
130 issue, id = _find_issue(ui, repo, id) |
127 if not issue: return |
131 if not issue: return |
128 mbox = mailbox.mbox(issue) |
132 mbox = mailbox.Maildir(issue, factory=mailbox.MaildirMessage) |
129 |
133 |
130 if opts['all']: |
134 if opts['all']: |
131 ui.write('='*70 + '\n') |
135 ui.write('='*70 + '\n') |
132 for i in xrange(len(mbox)): |
136 i = 0 |
133 _write_message(ui, mbox[i], i) |
137 keys = _order_keys_date(mbox) |
|
138 for k in keys: |
|
139 _write_message(ui, mbox[k], i) |
134 ui.write('-'*70 + '\n') |
140 ui.write('-'*70 + '\n') |
|
141 i += 1 |
135 return |
142 return |
136 |
143 |
137 _show_mbox(ui, mbox, comment) |
144 _show_mbox(ui, mbox, comment) |
138 |
145 |
139 |
146 |
144 if not issue: return |
151 if not issue: return |
145 |
152 |
146 properties = _get_properties(opts['property']) |
153 properties = _get_properties(opts['property']) |
147 |
154 |
148 # Read the issue |
155 # Read the issue |
149 mbox = mailbox.mbox(issue) |
156 mbox = mailbox.Maildir(issue, factory=mailbox.MaildirMessage) |
150 msg = mbox[0] |
157 root = _find_root_key(mbox) |
|
158 msg = mbox[root] |
151 |
159 |
152 # Fix the properties |
160 # Fix the properties |
153 properties_text = '' |
161 properties_text = '' |
154 for property, value in properties: |
162 for property, value in properties: |
155 if property in msg: |
163 if property in msg: |
156 msg.replace_header(property, value) |
164 msg.replace_header(property, value) |
157 else: |
165 else: |
158 msg.add_header(property, value) |
166 msg.add_header(property, value) |
159 properties_text += '%s=%s\n' % (property, value) |
167 properties_text += '%s=%s\n' % (property, value) |
160 mbox[0] = msg |
168 mbox.lock() |
|
169 mbox[root] = msg |
161 |
170 |
162 # Write down a comment about updated properties |
171 # Write down a comment about updated properties |
163 if properties and not opts['no_property_comment']: |
172 if properties and not opts['no_property_comment']: |
164 user = ui.username() |
173 user = ui.username() |
165 properties_text = "From: %s\nDate: %s\nSubject: properties changes (%s)\n\n%s" % \ |
174 properties_text = "From: %s\nDate: %s\nSubject: properties changes (%s)\n\n%s" % \ |
168 properties_text) |
177 properties_text) |
169 msg = mailbox.mboxMessage(properties_text) |
178 msg = mailbox.mboxMessage(properties_text) |
170 msg.add_header('Message-Id', "<%s-%s-artemis@%s>" % (id, _random_id(), socket.gethostname())) |
179 msg.add_header('Message-Id', "<%s-%s-artemis@%s>" % (id, _random_id(), socket.gethostname())) |
171 msg.add_header('References', mbox[0]['Message-Id']) |
180 msg.add_header('References', mbox[0]['Message-Id']) |
172 msg.add_header('In-Reply-To', mbox[0]['Message-Id']) |
181 msg.add_header('In-Reply-To', mbox[0]['Message-Id']) |
173 msg.set_from('artemis', True) |
182 #msg.set_from('artemis', True) |
174 mbox.add(msg) |
183 repo.add([issue_fn[(len(repo.root)+1):] + '/new/' + mbox.add(msg)]) # +1 for the trailing / |
175 mbox.flush() |
184 mbox.close() |
176 |
185 |
177 # Show updated message |
186 # Show updated message |
178 _show_mbox(ui, mbox, 0) |
187 _show_mbox(ui, mbox, 0) |
179 |
188 |
180 |
189 |
210 def _show_mbox(ui, mbox, comment): |
219 def _show_mbox(ui, mbox, comment): |
211 # Output the issue (or comment) |
220 # Output the issue (or comment) |
212 if comment >= len(mbox): |
221 if comment >= len(mbox): |
213 comment = 0 |
222 comment = 0 |
214 ui.warn('Comment out of range, showing the issue itself\n') |
223 ui.warn('Comment out of range, showing the issue itself\n') |
215 msg = mbox[comment] |
224 keys = _order_keys_date(mbox) |
|
225 root = keys[0] |
|
226 msg = mbox[keys[comment]] |
216 ui.write('='*70 + '\n') |
227 ui.write('='*70 + '\n') |
217 if comment: |
228 if comment: |
218 ui.write('Subject: %s\n' % mbox[0]['Subject']) |
229 ui.write('Subject: %s\n' % mbox[root]['Subject']) |
219 ui.write('State: %s\n' % mbox[0]['State']) |
230 ui.write('State: %s\n' % mbox[root]['State']) |
220 ui.write('-'*70 + '\n') |
231 ui.write('-'*70 + '\n') |
221 _write_message(ui, msg, comment) |
232 _write_message(ui, msg, comment) |
222 ui.write('-'*70 + '\n') |
233 ui.write('-'*70 + '\n') |
223 |
234 |
224 # Read the mailbox into the messages and children dictionaries |
235 # Read the mailbox into the messages and children dictionaries |
225 messages = {} |
236 messages = {} |
226 children = {} |
237 children = {} |
227 for i in xrange(len(mbox)): |
238 i = 0 |
228 m = mbox[i] |
239 for k in keys: |
|
240 m = mbox[k] |
229 messages[m['Message-Id']] = (i,m) |
241 messages[m['Message-Id']] = (i,m) |
230 children.setdefault(m['In-Reply-To'], []).append(m['Message-Id']) |
242 children.setdefault(m['In-Reply-To'], []).append(m['Message-Id']) |
|
243 i += 1 |
231 children[None] = [] # Safeguard against infinte loop on empty Message-Id |
244 children[None] = [] # Safeguard against infinte loop on empty Message-Id |
232 |
245 |
233 # Iterate over children |
246 # Iterate over children |
234 id = msg['Message-Id'] |
247 id = msg['Message-Id'] |
235 id_stack = (id in children and map(lambda x: (x, 1), reversed(children[id]))) or [] |
248 id_stack = (id in children and map(lambda x: (x, 1), reversed(children[id]))) or [] |
239 id,offset = id_stack.pop() |
252 id,offset = id_stack.pop() |
240 id_stack += (id in children and map(lambda x: (x, offset+1), reversed(children[id]))) or [] |
253 id_stack += (id in children and map(lambda x: (x, offset+1), reversed(children[id]))) or [] |
241 index, msg = messages[id] |
254 index, msg = messages[id] |
242 ui.write(' '*offset + ('%d: ' % index) + msg['Subject'] + '\n') |
255 ui.write(' '*offset + ('%d: ' % index) + msg['Subject'] + '\n') |
243 ui.write('-'*70 + '\n') |
256 ui.write('-'*70 + '\n') |
|
257 |
|
258 def _find_root_key(maildir): |
|
259 for k,m in maildir.iteritems(): |
|
260 if 'in-reply-to' not in m: |
|
261 return k |
|
262 |
|
263 def _order_keys_date(mbox): |
|
264 keys = mbox.keys() |
|
265 root = _find_root_key(mbox) |
|
266 keys.sort(lambda k1,k2: -(k1 == root) or cmp(util.parsedate(mbox[k1]['date']), util.parsedate(mbox[k2]['date']))) |
|
267 return keys |
244 |
268 |
245 def _pretty_list(lst): |
269 def _pretty_list(lst): |
246 s = '' |
270 s = '' |
247 for i in lst: |
271 for i in lst: |
248 s += i + ', ' |
272 s += i + ', ' |