Ticket #226: ticket_dependency.patch
| File ticket_dependency.patch, 17.2 kB (added by trac@…, 4 years ago) |
|---|
-
trac/db_default.py
21 21 22 22 23 23 # Database version identifier. Used for automatic upgrades. 24 db_version = 724 db_version = 8 25 25 26 26 def __mkreports(reps): 27 27 """Utility function used to create report data in same syntax as the … … 88 88 reporter text, 89 89 cc text, -- email addresses to notify 90 90 url text, -- url related to this ticket 91 version text, -- 92 milestone text, -- 91 version text, -- 92 milestone text, -- 93 93 status text, 94 94 resolution text, 95 95 summary text, -- one-line summary … … 119 119 description text 120 120 ); 121 121 CREATE TABLE permission ( 122 username text, -- 122 username text, -- 123 123 action text, -- allowable activity 124 124 UNIQUE(username,action) 125 125 ); … … 188 188 """, 189 189 """ 190 190 SELECT p.value AS __color__, 191 id AS ticket, summary, component, version, milestone, severity, 191 id AS ticket, summary, component, version, milestone, severity, 192 192 (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 193 193 time AS created, 194 194 changetime AS _changetime, description AS _description, 195 195 reporter AS _reporter 196 196 FROM ticket t, enum p 197 WHERE status IN ('new', 'assigned', 'reopened') 197 WHERE status IN ('new', 'assigned', 'reopened') 198 198 AND p.name = t.priority AND p.type = 'priority' 199 199 ORDER BY p.value, milestone, severity, time 200 200 """), … … 210 210 """ 211 211 SELECT p.value AS __color__, 212 212 version AS __group__, 213 id AS ticket, summary, component, version, severity, 213 id AS ticket, summary, component, version, severity, 214 214 (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 215 215 time AS created, 216 216 changetime AS _changetime, description AS _description, 217 217 reporter AS _reporter 218 218 FROM ticket t, enum p 219 WHERE status IN ('new', 'assigned', 'reopened') 219 WHERE status IN ('new', 'assigned', 'reopened') 220 220 AND p.name = t.priority AND p.type = 'priority' 221 221 ORDER BY (version IS NULL),version, p.value, severity, time 222 222 """), … … 232 232 """ 233 233 SELECT p.value AS __color__, 234 234 milestone||' Release' AS __group__, 235 id AS ticket, summary, component, version, severity, 235 id AS ticket, summary, component, version, severity, 236 236 (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 237 237 time AS created, 238 238 changetime AS _changetime, description AS _description, 239 239 reporter AS _reporter 240 240 FROM ticket t, enum p 241 WHERE status IN ('new', 'assigned', 'reopened') 241 WHERE status IN ('new', 'assigned', 'reopened') 242 242 AND p.name = t.priority AND p.type = 'priority' 243 243 ORDER BY (milestone IS NULL),milestone, p.value, severity, time 244 244 """), … … 284 284 """ 285 285 SELECT p.value AS __color__, 286 286 t.milestone AS __group__, 287 (CASE status 287 (CASE status 288 288 WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;' 289 ELSE 289 ELSE 290 290 (CASE owner WHEN '$USER' THEN 'font-weight: bold' END) 291 291 END) AS __style__, 292 id AS ticket, summary, component, status, 292 id AS ticket, summary, component, status, 293 293 resolution,version, severity, priority, owner, 294 294 changetime AS modified, 295 295 time AS _time,reporter AS _reporter 296 296 FROM ticket t,enum p 297 297 WHERE p.name=t.priority AND p.type='priority' 298 ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'), 298 ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'), 299 299 (CASE status WHEN 'closed' THEN modified ELSE -p.value END) DESC 300 300 """), 301 301 #---------------------------------------------------------------------------- 302 302 ('My Tickets', 303 303 """ 304 This report demonstrates the use of the automatically set 304 This report demonstrates the use of the automatically set 305 305 $USER dynamic variable, replaced with the username of the 306 306 logged in user when executed. 307 307 """, … … 313 313 changetime AS _changetime, description AS _description, 314 314 reporter AS _reporter 315 315 FROM ticket t, enum p 316 WHERE t.status IN ('new', 'assigned', 'reopened') 316 WHERE t.status IN ('new', 'assigned', 'reopened') 317 317 AND p.name = t.priority AND p.type = 'priority' AND owner = '$USER' 318 318 ORDER BY (status = 'assigned') DESC, p.value, milestone, severity, time 319 319 """), … … 325 325 """, 326 326 """ 327 327 SELECT p.value AS __color__, 328 (CASE owner 329 WHEN '$USER' THEN 'My Tickets' 330 ELSE 'Active Tickets' 328 (CASE owner 329 WHEN '$USER' THEN 'My Tickets' 330 ELSE 'Active Tickets' 331 331 END) AS __group__, 332 id AS ticket, summary, component, version, milestone, severity, 332 id AS ticket, summary, component, version, milestone, severity, 333 333 (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 334 334 time AS created, 335 335 changetime AS _changetime, description AS _description, 336 336 reporter AS _reporter 337 337 FROM ticket t, enum p 338 WHERE status IN ('new', 'assigned', 'reopened') 338 WHERE status IN ('new', 'assigned', 'reopened') 339 339 AND p.name = t.priority AND p.type = 'priority' 340 340 ORDER BY (owner = '$USER') DESC, p.value, milestone, severity, time 341 341 """)) … … 352 352 ('component2', 'somebody'))), 353 353 ('milestone', 354 354 ('name', 'time'), 355 (('', 0), 355 (('', 0), 356 356 ('milestone1', 0), 357 357 ('milestone2', 0), 358 358 ('milestone3', 0), … … 446 446 ('notification', 'smtp_replyto', 'trac@localhost'), 447 447 ('timeline', 'changeset_show_files', 'false'), 448 448 ('timeline', 'changeset_files_count', 3)) 449 449 -
trac/Ticket.py
29 29 import util 30 30 from Module import Module 31 31 from WikiFormatter import wiki_to_html 32 from Wiki import wiki_to_oneliner 32 33 from Notify import TicketNotifyEmail 33 34 34 35 __all__ = ['Ticket', 'NewticketModule', 'TicketModule'] … … 37 38 class Ticket(UserDict): 38 39 std_fields = ['time', 'component', 'severity', 'priority', 'milestone', 39 40 'reporter', 'owner', 'cc', 'url', 'version', 'status', 'resolution', 40 'keywords', 'summary', 'description' , 'reporter']41 'keywords', 'summary', 'description'] 41 42 42 43 def __init__(self, *args): 43 44 UserDict.__init__(self) … … 78 79 if rows: 79 80 for r in rows: 80 81 self['custom_' + r[0]] = r[1] 82 83 self['dependson'] = self.get_dependson_list(db, id) 84 self['blocks'] = self.get_blocks_list(db, id) 85 86 81 87 self._forget_changes() 82 88 83 89 def populate(self, dict): … … 87 93 for name in names: 88 94 self[name] = dict.get(name, '') 89 95 96 self['blocks'] = dict.get('blocks',''); 97 self['dependson'] = dict.get('dependson',''); 98 90 99 # We have to do an extra trick to catch unchecked checkboxes 91 100 checkboxes = filter(lambda n: n[:9] == 'checkbox_', dict.keys()) 92 101 for name in ['custom_' + n[9:] for n in checkboxes]: … … 103 112 self['time'] = now 104 113 self['changetime'] = now 105 114 106 std_fields = filter(lambda n: n[:7] != 'custom_' , self.keys())115 std_fields = filter(lambda n: n[:7] != 'custom_' and n != 'dependson' and n != 'blocks' , self.keys()) 107 116 custom_fields = filter(lambda n: n[:7] == 'custom_', self.keys()) 108 117 std_values = map(lambda n, self=self: self[n], std_fields) 109 118 nstr = string.join(std_fields, ',') … … 114 123 for name in custom_fields: 115 124 cursor.execute('INSERT INTO ticket_custom(ticket,name,value)' 116 125 ' VALUES(%d, %s, %s)', id, name[7:], self[name]) 126 # Dependencies 127 cursor.execute('DELETE FROM dependencies WHERE dependson=%s',id) 128 cursor.executemany ('INSERT INTO dependencies (dependson,blocks) ' 129 "VALUES (" + str(id) + ",%s)", 130 self['blocks']) 131 cursor.execute('DELETE FROM dependencies WHERE blocks=%s',id) 132 cursor.executemany ('INSERT INTO dependencies (blocks,dependson) ' 133 "VALUES (" + str(id) + ",%s)", 134 self['dependson']) 117 135 db.commit() 118 136 self['id'] = id 119 137 self._forget_changes() … … 141 159 cursor.execute('SELECT owner FROM component ' 142 160 'WHERE name=%s', self['component']) 143 161 self['owner'] = cursor.fetchone()[0] 144 145 162 163 146 164 for name in self._old.keys(): 147 165 if name[:7] == 'custom_': 148 166 fname = name[7:] 149 167 cursor.execute('REPLACE INTO ticket_custom(ticket,name,value)' 150 168 ' VALUES(%s, %s, %s)', id, fname, self[name]) 169 elif name == 'dependson': 170 fname = name 171 cursor.execute('DELETE FROM dependencies WHERE blocks=%s',id) 172 if len(self[name]) > 0: 173 cursor.executemany ('INSERT INTO dependencies (blocks,dependson) ' 174 "VALUES (" + str(id) + ",%s)", 175 string.split(self[name],',')) 176 elif name == 'blocks': 177 fname = name 178 cursor.execute('DELETE FROM dependencies WHERE dependson=%s',id) 179 if len(self[name]) > 0: 180 cursor.executemany ('INSERT INTO dependencies (dependson,blocks) ' 181 "VALUES (" + str(id) + ",%s)", 182 string.split(self[name],',')) 151 183 else: 152 184 fname = name 153 185 cursor.execute ('UPDATE ticket SET %s=%s WHERE id=%s', 154 186 fname, self[name], id) 155 187 156 cursor.execute ('INSERT INTO ticket_change ' 157 '(ticket, time, author, field, oldvalue, newvalue) ' 158 'VALUES (%s, %s, %s, %s, %s, %s)', 159 id, when, author, fname, self._old[name], self[name]) 188 # Weasle out on this one. 189 if fname != 'dependson' and fname != 'blocks': 190 cursor.execute ('INSERT INTO ticket_change ' 191 '(ticket, time, author, field, oldvalue, newvalue) ' 192 'VALUES (%s, %s, %s, %s, %s, %s)', 193 id, when, author, fname, self._old[name], self[name]) 160 194 if comment: 161 195 cursor.execute ('INSERT INTO ticket_change ' 162 196 '(ticket,time,author,field,oldvalue,newvalue) ' … … 195 229 log.append((int(row[0]), row[1], row[2], row[3] or '', row[4] or '')) 196 230 return log 197 231 232 def get_blocks_list(self, db, id): 233 # Determine Tickets this Ticket blocks 234 cursor = db.cursor () 235 cursor.execute( 'SELECT blocks FROM dependencies WHERE dependson=%s', id ) 198 236 237 blocks = [] # List of Tickets the current Ticket blocks 238 while 1: 239 row = cursor.fetchone() 240 if not row: 241 break 242 blocks.append(row['blocks']); 243 244 cursor.close () 245 246 return blocks 247 248 def get_dependson_list(self, db, id): 249 # Determine Tickets this Ticket depends on 250 cursor = db.cursor () 251 cursor.execute( 'SELECT dependson FROM dependencies WHERE blocks=%s', id ) 252 253 dependson = [] # List of Tickets the current Ticket deepnds on 254 while 1: 255 row = cursor.fetchone() 256 if not row: 257 break 258 dependson.append(row['dependson']); 259 260 cursor.close () 261 262 return dependson 263 199 264 def get_custom_fields(env): 200 265 cfg = env.get_config_items('ticket-custom') 201 266 if not cfg: … … 387 452 self.env, self.db)) 388 453 self.req.hdf.setValue('ticket.opened', time.strftime('%c', time.localtime(int(ticket['time'])))) 389 454 455 # Dependencies 456 dependson = [] 457 dependson_displ = [] 458 for i in range(len(ticket['dependson'])): 459 dependson_displ.append( '#' + str(ticket['dependson'][i]) ) 460 dependson.append( str(ticket['dependson'][i]) ) 461 self.req.hdf.setValue('ticket.dependson_displ', 462 wiki_to_oneliner(string.join(dependson_displ, ', '), 463 self.req.hdf, self.env, self.db)) 464 self.req.hdf.setValue('ticket.dependson', 465 string.join(dependson, ',')) 466 blocks = [] 467 blocks_displ = [] 468 for i in range(len(ticket['blocks'])): 469 blocks_displ.append( '#' + str(ticket['blocks'][i]) ) 470 blocks.append( str(ticket['blocks'][i]) ) 471 self.req.hdf.setValue('ticket.blocks_displ', 472 wiki_to_oneliner(string.join(blocks_displ, ', '), 473 self.req.hdf, self.env, self.db)) 474 self.req.hdf.setValue('ticket.blocks', 475 string.join(blocks, ',')) 476 390 477 changelog = ticket.get_changelog(self.db) 391 478 curr_author = None 392 479 curr_date = 0 … … 437 524 self.req.hdf.setValue('ticket.action', action) 438 525 reporter_id = self.args.get('author') 439 526 comment = self.args.get('comment') 527 if self.args.has_key('dependson'): 528 ticket['dependson'] = string.split(self.args.get('dependson'),',') 529 else: 530 ticket['dependson'] = [] 531 532 if self.args.has_key('blocks'): 533 ticket['blocks'] = string.split(self.args.get('blocks'),',') 534 else: 535 ticket['blocks'] = [] 536 440 537 if comment: 441 538 self.req.hdf.setValue('ticket.comment', comment) 442 539 # Wiki format a preview of comment -
templates/ticket.cs
41 41 call:ticketprop("Version", "version", ticket.version, 0) ?><?cs 42 42 call:ticketprop("Resolution", "resolution", ticket.resolution, 0) ?><?cs 43 43 call:ticketprop("Milestone", "milestone", ticket.milestone, 0) ?><?cs 44 call:ticketprop("Keywords", "keywords", ticket.keywords, 0) ?><?cs 45 call:ticketprop("Depends On", "dependson", ticket.dependson_displ, 0) ?><?cs 44 46 set:last_prop = #1 ?><?cs 45 call:ticketprop(" Keywords", "keywords", ticket.keywords, 0) ?><?cs47 call:ticketprop("Blocks", "blocks", ticket.blocks_displ, 0) ?><?cs 46 48 set:last_prop = #0 ?> 47 49 </tr></table><?cs if ticket.custom.0.name ?> 48 50 <hr /> … … 178 180 <label for="keywords">Keywords:</label> 179 181 <input type="text" id="keywords" name="keywords" size="20" 180 182 value="<?cs var:ticket.keywords ?>" /> 183 <br /> 184 <label for="dependson">Depends On:</label> 185 <input type="text" id="dependson" name="dependson" size="20" 186 value="<?cs var:ticket.dependson ?>" /> 181 187 </div> 182 188 <div class="col2"> 183 189 <label for="priority">Priority:</label><?cs … … 189 195 var:ticket.owner ?>" disabled="disabled" /><br /> 190 196 <label for="cc">Cc:</label> 191 197 <input type="text" id="cc" name="cc" size="30" value="<?cs var:ticket.cc ?>" /> 198 <br /> 199 <label for="blocks">Blocks:</label> 200 <input type="text" id="blocks" name="blocks" size="20" 201 value="<?cs var:ticket.blocks ?>" /> 192 202 </div> 193 203 <div class="custom"> 194 204 <?cs call:ticket_custom_props(ticket) ?> … … 233 243 <div class="buttons"> 234 244 <input type="reset" value="Reset" /> 235 245 <input type="submit" name="preview" value="Preview" /> 236 <input type="submit" value="Submit changes" /> 246 <input type="submit" value="Submit changes" /> 237 247 </div> 238 248 </form> 239 249 <?cs /if ?> -
templates/newticket.cs
2 2 <?cs include "header.cs" ?> 3 3 <?cs include "macros.cs" ?> 4 4 <script type="text/javascript"> 5 addEvent(window, 'load', function() { document.getElementById('summary').focus()}); 5 addEvent(window, 'load', function() { document.getElementById('summary').focus()}); 6 6 </script> 7 7 8 8 <div id="ctxtnav" class="nav"></div> … … 52 52 <label for="keywords">Keywords:</label> 53 53 <input type="text" id="keywords" name="keywords" size="20" 54 54 value="<?cs var:ticket.keywords ?>" /> 55 <br /> 56 <label for="dependson">Depends On:</label> 57 <input type="text" id="dependson" name="dependson" size="20" 58 value="<?cs var:ticket.dependson ?>" /> 55 59 </div> 56 60 <div class="col2"> 57 61 <label for="priority">Priority:</label><?cs … … 63 67 var:newticket.owner ?>" /><br /> 64 68 <label for="cc">Cc:</label> 65 69 <input type="text" id="cc" name="cc" size="30" value="<?cs var:newticket.cc ?>" /> 70 <br /> 71 <label for="blocks">Blocks:</label> 72 <input type="text" id="blocks" name="blocks" size="20" 73 value="<?cs var:ticket.blocks ?>" /> 66 74 </div> 67 75 <div class="custom"> 68 76 <?cs call:ticket_custom_props(ticket) ?>
