Edgewall Software

NewWorkflow: patch-newworkflow-r1034.diff

File patch-newworkflow-r1034.diff, 67.1 KB (added by pkou <pkou at ua.fm>, 7 years ago)

Patch for the changes

  • htdocs/css/timeline.css

     
    3030/* Apply icon background-image twice to avoid hover-flicker in IE/Win */ 
    3131dt.changeset, dt.changeset a { background-image: url(../changeset.png) !important } 
    3232dt.newticket, dt.newticket a { background-image: url(../newticket.png) !important } 
     33dt.resolvedticket, dt.resolvedticket a { background-image: url(../resolvedticket.png) !important } 
     34dt.reopenedticket, dt.reopenedticket a { background-image: url(../reopenedticket.png) !important } 
    3335dt.closedticket, dt.closedticket a { background-image: url(../closedticket.png) !important } 
    3436dt.wiki, dt.wiki a { background-image: url(../wiki.png) !important } 
    3537dt.milestone, dt.milestone a { background-image: url(../milestone.png) !important } 
  • wiki-default/TracIni

     
    2222See also: TracLogging 
    2323 
    2424== [ticket] == 
     25|| workflow_version  || Workflow version: 1 for simple, 2 for advanced || 
    2526|| default_version   || Default version for newly created tickets || 
    2627|| default_severity  || Default severity for newly created tickets || 
    2728|| default_priority  || Default priority for newly created tickets || 
  • wiki-default/TracAdmin

     
    2323permission add <user> <action>      -- Add a new permission rule 
    2424permission remove <user> <action>   -- Remove permission rule 
    2525component list                      -- Show available components 
    26 component add <name> <owner>        -- Add a new component 
     26component add <name> <owner> [<qaowner>]   -- Add a new component 
    2727component rename <name> <newname>   -- Rename a component 
    2828component remove <name>             -- Remove/uninstall component 
    29 component chown <name> <owner>     -- Change component ownership 
     29component chown <name> <owner> [<qaowner>] -- Change component ownership 
    3030priority list                       -- Show possible ticket priorities 
    3131priority add <value>                -- Add a priority value option 
    3232priority change <value> <newvalue>  -- Change a priority value 
     
    4141version time <name> <time>          -- Set version date/time 
    4242version remove <name>               -- Remove version 
    4343milestone list                      -- Show milestones 
    44 milestone add <name> [time]        -- Add milestone 
     44milestone add <name> [<owner> [time]] -- Add milestone 
    4545milestone rename <name> <newname>   -- Rename milestone 
    4646milestone time <name> <time>        -- Set milestone date/time 
     47milestone chown <name> <owner>      -- Change milestone ownership 
    4748milestone remove <name>             -- Remove milestone 
    4849}}} 
    4950 
  • scripts/trac-admin

     
    290290     
    291291#    ## Component 
    292292    _help_component = [('component list', 'Show available components'), 
    293                        ('component add <name> <owner>', 'Add a new component'), 
     293                       ('component add <name> <owner> [<qaowner>]', 'Add a new component'), 
    294294                       ('component rename <name> <newname>', 'Rename a component'), 
    295295                       ('component remove <name>', 'Remove/uninstall component'), 
    296                        ('component chown <name> <owner>', 'Change component ownership')] 
     296                       ('component chown <name> <owner> [<qaowner>]', 'Change component ownership')] 
    297297 
    298298    def complete_component (self, text, line, begidx, endidx): 
    299299        if begidx in [16,17]: 
     
    309309        try: 
    310310            if arg[0]  == 'list': 
    311311                self._do_component_list() 
    312             elif arg[0] == 'add' and len(arg)==3: 
     312            elif arg[0] == 'add' and len(arg) in [3,4]: 
    313313                name = arg[1] 
    314314                owner = arg[2] 
    315                 self._do_component_add(name, owner) 
     315                if len(arg) == 4: 
     316                    qaowner = arg[3] 
     317                else: 
     318                    qaowner = owner 
     319                self._do_component_add(name, owner, qaowner) 
    316320            elif arg[0] == 'rename' and len(arg)==3: 
    317321                name = arg[1] 
    318322                newname = arg[2] 
     
    320324            elif arg[0] == 'remove'  and len(arg)==2: 
    321325                name = arg[1] 
    322326                self._do_component_remove(name) 
    323             elif arg[0] == 'chown' and len(arg)==3: 
     327            elif arg[0] == 'chown' and len(arg) in [3,4]: 
    324328                name = arg[1] 
    325329                owner = arg[2] 
    326                 self._do_component_set_owner(name, owner) 
     330                if len(arg) == 4: 
     331                    qaowner = arg[3] 
     332                else: 
     333                    qaowner = owner 
     334                self._do_component_set_owner(name, owner, qaowner) 
    327335            else:     
    328336                self.do_help ('component') 
    329337        except Exception, e: 
    330338            print 'Component %s failed:' % arg[0], e 
    331339 
    332340    def _do_component_list(self): 
    333         data = self.db_execsql('SELECT name, owner FROM component')  
    334         self.print_listing(['Name', 'Owner'], data) 
     341        data = self.db_execsql('SELECT name, owner, qaowner FROM component')  
     342        self.print_listing(['Name', 'Owner', 'QA Owner'], data) 
    335343 
    336     def _do_component_add(self, name, owner): 
    337         data = self.db_execsql("INSERT INTO component VALUES('%s', '%s')" 
    338                                % (name, owner)) 
     344    def _do_component_add(self, name, owner, qaowner): 
     345        data = self.db_execsql("INSERT INTO component VALUES('%s', '%s', '%s')" 
     346                               % (name,owner,qaowner)) 
    339347 
    340348    def _do_component_rename(self, name, newname): 
    341349        cnx = self.db_open() 
     
    360368        data = self.db_execsql("DELETE FROM component WHERE name='%s'" 
    361369                               % (name)) 
    362370 
    363     def _do_component_set_owner(self, name, owner): 
     371    def _do_component_set_owner(self, name, owner, qaowner): 
    364372        cnx = self.db_open() 
    365373        cursor = cnx.cursor () 
    366374        cursor.execute('SELECT name FROM component WHERE name=%s', name) 
    367375        data = cursor.fetchone() 
    368376        if not data: 
    369377            raise Exception("No such component '%s'" % name) 
    370         data = self.db_execsql("UPDATE component SET owner='%s' WHERE name='%s'" 
    371                                % (owner,name)) 
     378        data = self.db_execsql("UPDATE component SET owner='%s', qaowner='%s' WHERE name='%s'" 
     379                               % (owner,qaowner,name)) 
    372380 
    373381 
    374382    ## Permission 
     
    795803 
    796804    ## Milestone 
    797805    _help_milestone = [('milestone list', 'Show milestones'), 
    798                        ('milestone add <name> [time]', 'Add milestone'), 
     806                       ('milestone add <name> [<owner> [time]]', 'Add milestone'), 
    799807                       ('milestone rename <name> <newname>', 
    800808                        'Rename milestone'), 
     809                       ('milestone chown <name> <newowner>', 'Change milestone owner'), 
    801810                       ('milestone time <name> <time>', 'Set milestone date (Format: "Jun 3, 2003")'), 
    802811                       ('milestone remove <name>', 'Remove milestone')] 
    803812 
     
    805814 
    806815        if begidx in [15,17]: 
    807816            comp = self.get_milestone_list () 
     817        elif begidx > 15 and line.startswith('milestone chown '): 
     818            comp = self.get_user_list() 
    808819        elif begidx < 15: 
    809             comp = ['list','add','rename','time','remove'] 
     820            comp = ['list','add','rename','chown','time','remove'] 
    810821        return self.word_complete(text, comp) 
    811822 
    812823    def do_milestone(self, line): 
    813         self._do_mile_ver('milestone', line) 
     824        type = 'milestone' 
     825        arg = self.arg_tokenize(line) 
     826        try: 
     827            if arg[0]  == 'list': 
     828                self._do_milestone_list() 
     829            elif arg[0] == 'add' and len(arg) in [2,3,4]: 
     830                name = arg[1] 
     831                self._do_mile_ver_add(type, name) 
     832                if len(arg) >= 3: 
     833                    owner = arg[2] 
     834                    self._do_mile_ver_chown(type, name, owner) 
     835                if len(arg) >= 4: 
     836                    time = arg[3] 
     837                    self._do_mile_ver_time(type, name, time) 
     838            elif arg[0] == 'rename' and len(arg)==3: 
     839                name = arg[1] 
     840                newname = arg[2] 
     841                self._do_mile_ver_rename(type, name, newname) 
     842            elif arg[0] == 'chown' and len(arg)==3: 
     843                name = arg[1] 
     844                owner = arg[2] 
     845                self._do_mile_ver_chown(type, name, owner) 
     846            elif arg[0] == 'time' and len(arg)==3: 
     847                name = arg[1] 
     848                time = arg[2] 
     849                self._do_mile_ver_time(type, name, time) 
     850            elif arg[0] == 'remove' and len(arg)==2: 
     851                name = arg[1] 
     852                self._do_mile_ver_remove(type, name) 
     853            else: 
     854                self.do_help (type) 
     855        except Exception, e: 
     856            print 'Command %s failed:' % arg[0], e 
    814857 
     858    def _do_milestone_list(self): 
     859        data = self.db_execsql("SELECT name,owner,time FROM milestone ORDER BY time,name") 
     860        data = map(lambda x: (x[0], x[1], x[2] and time.strftime('%c', time.localtime(x[2]))), data) 
     861        #print data 
     862        self.print_listing(['Name', 'Owner', 'Time'], data) 
    815863 
     864 
    816865    ## Version 
    817866    _help_version = [('version list', 'Show versions'), 
    818867                       ('version add <name> [time]', 'Add version'), 
     
    832881    def do_version(self, line): 
    833882        self._do_mile_ver('version', line) 
    834883 
    835     # Milestone and Version are identical,  methods 
     884    # Milestone and Version are almost identical,  methods 
    836885 
    837886    def _do_mile_ver(self, type, line): 
    838887        arg = self.arg_tokenize(line) 
     
    923972        else: 
    924973            print >> sys.stderr, 'Unknown time format' 
    925974 
     975    def _do_mile_ver_chown(self, type, name, owner): 
     976        data = self.db_execsql("UPDATE %s SET owner='%s' WHERE name='%s'" 
     977                               % (type, owner, name)); 
     978 
    926979    _help_upgrade = [('upgrade', 'Upgrade database to current version.')] 
    927980    def do_upgrade(self, line): 
    928981        arg = self.arg_tokenize(line) 
  • trac/db_default.py

     
    2121 
    2222 
    2323# Database version identifier. Used for automatic upgrades. 
    24 db_version = 7 
     24db_version = 8 
     25# Current workflow version identifier 
     26current_workflow_version = 2 
    2527 
    2628def __mkreports(reps): 
    2729    """Utility function used to create report data in same syntax as the 
     
    125127); 
    126128CREATE TABLE component ( 
    127129         name            text PRIMARY KEY, 
    128          owner           text 
     130         owner           text, 
     131         qaowner         text 
    129132); 
    130133CREATE TABLE milestone ( 
    131134         id              integer PRIMARY KEY, 
    132135         name            text, 
    133136         time            integer, 
     137         owner           text, 
    134138         descr           text, 
    135139         UNIQUE(name) 
    136140); 
     
    209213""", 
    210214""" 
    211215SELECT p.value AS __color__, 
    212    version AS __group__, 
    213    id AS ticket, summary, component, version, severity,  
     216   (CASE WHEN IFNULL(version, '') = '' THEN 'Not Specified' ELSE 'Version ' || version END) AS __group__, 
     217   id AS ticket, summary, component, milestone, severity,  
    214218   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
    215219   time AS created, 
    216220   changetime AS _changetime, description AS _description, 
     
    218222  FROM ticket t, enum p 
    219223  WHERE status IN ('new', 'assigned', 'reopened')  
    220224AND p.name = t.priority AND p.type = 'priority' 
    221   ORDER BY (version IS NULL),version, p.value, severity, time 
     225  ORDER BY (IFNULL(version, '') = '') DESC,version, p.value, severity, time 
    222226"""), 
    223227#---------------------------------------------------------------------------- 
    224 ('All Tickets by Milestone', 
     228('Active Tickets by Milestone', 
    225229""" 
    226230This report shows how to color results by priority, 
    227231while grouping results by milestone. 
     
    231235""", 
    232236""" 
    233237SELECT p.value AS __color__, 
    234    milestone||' Release' AS __group__, 
     238   (CASE WHEN IFNULL(milestone, '') = '' THEN 'Not Assigned' ELSE milestone||' Release' END) AS __group__, 
    235239   id AS ticket, summary, component, version, severity,  
    236240   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
    237241   time AS created, 
     
    240244  FROM ticket t, enum p 
    241245  WHERE status IN ('new', 'assigned', 'reopened')  
    242246AND p.name = t.priority AND p.type = 'priority' 
    243   ORDER BY (milestone IS NULL),milestone, p.value, severity, time 
     247  ORDER BY (IFNULL(milestone, '') = '') DESC,milestone, p.value, severity, time 
    244248"""), 
    245249#---------------------------------------------------------------------------- 
    246250('Assigned, Active Tickets by Owner', 
     
    248252List assigned tickets, group by ticket owner, sorted by priority. 
    249253""", 
    250254""" 
    251  
    252255SELECT p.value AS __color__, 
    253    owner AS __group__, 
    254    id AS ticket, summary, component, milestone, severity, time AS created, 
     256   (CASE WHEN IFNULL(owner, '') = '' THEN 'Not Assigned' ELSE owner END) AS __group__, 
     257   id AS ticket, summary, component, version, milestone, severity, time AS created, 
    255258   changetime AS _changetime, description AS _description, 
    256259   reporter AS _reporter 
    257260  FROM ticket t,enum p 
    258261  WHERE status = 'assigned' 
    259262AND p.name=t.priority AND p.type='priority' 
    260   ORDER BY owner, p.value, severity, time 
     263  ORDER BY (IFNULL(owner, '') = '') DESC, owner, p.value, severity, time 
    261264"""), 
    262265#---------------------------------------------------------------------------- 
    263266('Assigned, Active Tickets by Owner (Full Description)', 
     
    268271""" 
    269272SELECT p.value AS __color__, 
    270273   owner AS __group__, 
    271    id AS ticket, summary, component, milestone, severity, time AS created, 
     274   id AS ticket, summary, component, version, milestone, severity, time AS created, 
    272275   description AS _description_, 
    273276   changetime AS _changetime, reporter AS _reporter 
    274277  FROM ticket t, enum p 
     
    283286""", 
    284287""" 
    285288SELECT p.value AS __color__, 
    286    t.milestone AS __group__, 
     289   (CASE WHEN IFNULL(t.milestone, '') = '' THEN 'Not Assigned' ELSE t.milestone || ' Release' END) AS __group__, 
    287290   (CASE status  
    288291      WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;' 
    289292      ELSE  
     
    295298   time AS _time,reporter AS _reporter 
    296299  FROM ticket t,enum p 
    297300  WHERE p.name=t.priority AND p.type='priority' 
    298   ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'),  
     301  ORDER BY (IFNULL(milestone, '') = '') DESC, milestone DESC, (status = 'closed'),  
    299302        (CASE status WHEN 'closed' THEN modified ELSE -p.value END) DESC 
    300303"""), 
    301304#---------------------------------------------------------------------------- 
     
    308311""" 
    309312SELECT p.value AS __color__, 
    310313   (CASE status WHEN 'assigned' THEN 'Assigned' ELSE 'Owned' END) AS __group__, 
    311    id AS ticket, summary, component, version, milestone, 
     314   id AS ticket, summary, component, status, version, milestone, 
    312315   severity, priority, time AS created, 
    313316   changetime AS _changetime, description AS _description, 
    314317   reporter AS _reporter 
    315318  FROM ticket t, enum p 
    316   WHERE t.status IN ('new', 'assigned', 'reopened')  
     319  WHERE t.status <> 'closed'  
    317320AND p.name = t.priority AND p.type = 'priority' AND owner = '$USER' 
    318321  ORDER BY (status = 'assigned') DESC, p.value, milestone, severity, time 
    319322"""), 
     
    338341  WHERE status IN ('new', 'assigned', 'reopened')  
    339342AND p.name = t.priority AND p.type = 'priority' 
    340343  ORDER BY (owner = '$USER') DESC, p.value, milestone, severity, time 
     344"""), 
     345#---------------------------------------------------------------------------- 
     346('Open Tickets, Mine first', 
     347""" 
     348 * List all not closed tickets by priority. 
     349 * Show all tickets owned by the logged in user in a group first. 
     350""", 
     351""" 
     352SELECT p.value AS __color__, 
     353   (CASE owner  
     354     WHEN '$USER' THEN 'My Tickets'  
     355     ELSE 'Open Tickets'  
     356    END) AS __group__, 
     357   id AS ticket, summary, component, status, version, milestone, severity,  
     358   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
     359   time AS created, 
     360   changetime AS _changetime, description AS _description, 
     361   reporter AS _reporter 
     362  FROM ticket t, enum p 
     363  WHERE status <> 'closed'  
     364AND p.name = t.priority AND p.type = 'priority' 
     365  ORDER BY (owner = '$USER') DESC, p.value, milestone, severity, time 
     366"""), 
     367#---------------------------------------------------------------------------- 
     368('Open Tickets by Version', 
     369""" 
     370 * List all not closed tickets by priority. 
     371 * Group results by version. 
     372""", 
     373""" 
     374SELECT p.value AS __color__, 
     375   (CASE WHEN IFNULL(version, '') = '' THEN 'Not Specified' ELSE 'Version ' || version END) AS __group__, 
     376   id AS ticket, summary, component, status, milestone, severity,  
     377   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
     378   time AS created, 
     379   changetime AS _changetime, description AS _description, 
     380   reporter AS _reporter 
     381  FROM ticket t, enum p 
     382  WHERE status <> 'closed' 
     383AND p.name = t.priority AND p.type = 'priority' 
     384  ORDER BY (IFNULL(version, '') = '') desc,version, p.value, severity, time 
     385"""), 
     386#---------------------------------------------------------------------------- 
     387('Open Tickets by Milestone', 
     388""" 
     389 * List all not closed tickets by priority. 
     390 * Group results by milestone. 
     391""", 
     392""" 
     393SELECT p.value AS __color__, 
     394   (CASE WHEN IFNULL(milestone, '') = '' THEN 'Not Assigned' ELSE milestone||' Release' END) AS __group__, 
     395   id AS ticket, summary, component, status, version, severity,  
     396   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
     397   time AS created, 
     398   changetime AS _changetime, description AS _description, 
     399   reporter AS _reporter 
     400  FROM ticket t, enum p 
     401  WHERE status <> 'closed'  
     402AND p.name = t.priority AND p.type = 'priority' 
     403  ORDER BY (IFNULL(milestone, '') = '') DESC,milestone, p.value, severity, time 
     404"""), 
     405#---------------------------------------------------------------------------- 
     406('Open Tickets by Owner', 
     407""" 
     408List not closed tickets, group by ticket owner, sorted by priority. 
     409""", 
     410""" 
     411SELECT p.value AS __color__, 
     412   (CASE WHEN IFNULL(owner, '') = '' THEN 'Not Assigned' ELSE owner END) AS __group__, 
     413   id AS ticket, summary, component, status, version, milestone, severity, 
     414   time AS created, changetime AS _changetime, description AS _description, 
     415   reporter AS _reporter 
     416  FROM ticket t,enum p 
     417  WHERE status <> 'closed' 
     418AND p.name=t.priority AND p.type='priority' 
     419  ORDER BY (IFNULL(owner, '') = '') DESC, owner, p.value, severity, time 
     420"""), 
     421#---------------------------------------------------------------------------- 
     422('Open Tickets by Status', 
     423""" 
     424 * List all not closed tickets by priority. 
     425 * Group results by status. 
     426""", 
     427""" 
     428SELECT p.value AS __color__, 
     429   status AS __group__, 
     430   id AS ticket, summary, component, version, milestone, severity, owner, 
     431   time AS created, 
     432   changetime AS _changetime, description AS _description, 
     433   reporter AS _reporter 
     434  FROM ticket t, enum q, enum p 
     435  WHERE status <> 'closed'  
     436AND q.name = t.status AND q.type = 'status' 
     437AND p.name = t.priority AND p.type = 'priority' 
     438  ORDER BY q.value, p.value, severity, time 
     439"""), 
     440#---------------------------------------------------------------------------- 
     441('Resolved Tickets, Mine first', 
     442""" 
     443 * List all resolved tickets by priority. 
     444 * Show all tickets owned by the logged in user in a group first. 
     445""", 
     446""" 
     447SELECT p.value AS __color__, 
     448   (CASE owner  
     449     WHEN '$USER' THEN 'My Tickets'  
     450     ELSE 'Active Tickets'  
     451    END) AS __group__, 
     452   id AS ticket, summary, component, version, milestone, severity,  
     453   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
     454   time AS created, 
     455   changetime AS _changetime, description AS _description, 
     456   reporter AS _reporter 
     457  FROM ticket t, enum p 
     458  WHERE status = 'resolved'  
     459AND p.name = t.priority AND p.type = 'priority' 
     460  ORDER BY (owner = '$USER') DESC, p.value, milestone, severity, time 
     461"""), 
     462#---------------------------------------------------------------------------- 
     463('Resolved Tickets by Milestone', 
     464""" 
     465List resolved tickets, sorted by priority, grouped by milestone 
     466""", 
     467""" 
     468SELECT p.value AS __color__, 
     469   (CASE WHEN IFNULL(milestone, '') = '' THEN 'Not Assigned' ELSE milestone||' Release' END) AS __group__, 
     470   id AS ticket, summary, component, version, severity,  
     471   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
     472   time AS created, 
     473   changetime AS _changetime, description AS _description, 
     474   reporter AS _reporter 
     475  FROM ticket t, enum p 
     476  WHERE status = 'resolved'  
     477AND p.name = t.priority AND p.type = 'priority' 
     478  ORDER BY (IFNULL(milestone, '') = '') DESC,milestone, p.value, severity, time 
     479"""), 
     480#---------------------------------------------------------------------------- 
     481('Resolved Tickets by Owner', 
     482""" 
     483List resolved tickets, group by ticket owner, sorted by priority. 
     484""", 
     485""" 
     486SELECT p.value AS __color__, 
     487   (CASE WHEN IFNULL(owner, '') = '' THEN 'Not Assigned' ELSE owner END) AS __group__, 
     488   id AS ticket, summary, component, version, milestone, severity, time AS created, 
     489   changetime AS _changetime, description AS _description, 
     490   reporter AS _reporter 
     491  FROM ticket t,enum p 
     492  WHERE status = 'resolved' 
     493AND p.name=t.priority AND p.type='priority' 
     494  ORDER BY (IFNULL(owner, '') = '') DESC, owner, p.value, severity, time 
     495"""), 
     496#---------------------------------------------------------------------------- 
     497('Verified Tickets by Milestone (Full Description)', 
     498""" 
     499Release Notes: List verified and closed tickets, group by milestone, include description. 
     500""", 
     501""" 
     502SELECT p.value AS __color__, 
     503   (CASE WHEN IFNULL(milestone, '') = '' THEN 'Not Assigned' ELSE milestone||' Release' END) AS __group__, 
     504   id AS ticket, summary, component, status, version, severity, time AS created, 
     505   description AS _description_, 
     506   changetime AS _changetime, reporter AS _reporter 
     507  FROM ticket t, enum p 
     508  WHERE status IN ('verified', 'closed') 
     509AND p.name = t.priority AND p.type = 'priority' 
     510  ORDER BY (IFNULL(milestone, '') = '') DESC,milestone, p.value, severity, time 
    341511""")) 
    342512 
    343513 
     
    347517 
    348518# (table, (column1, column2), ((row1col1, row1col2), (row2col1, row2col2))) 
    349519data = (('component', 
    350              ('name', 'owner'), 
    351                (('component1', 'somebody'), 
    352                 ('component2', 'somebody'))), 
     520             ('name', 'owner', 'qaowner'), 
     521               (('component1', 'somebody', 'qasomebody'), 
     522                ('component2', 'somebody', 'qasomebody'))), 
    353523           ('milestone', 
    354524             ('name', 'time'), 
    355525               (('', 0),  
     
    367537               (('status', 'new', 1), 
    368538                ('status', 'assigned', 2), 
    369539                ('status', 'reopened', 3), 
    370                 ('status', 'closed', 4), 
     540                ('status', 'resolved', 4), 
     541                ('status', 'verified', 5), 
     542                ('status', 'closed', 6), 
    371543                ('resolution', 'fixed', 1), 
    372544                ('resolution', 'invalid', 2), 
    373545                ('resolution', 'wontfix', 3), 
    374546                ('resolution', 'duplicate', 4), 
    375547                ('resolution', 'worksforme', 5), 
     548                ('resolution', 'moved', 6), 
    376549                ('severity', 'blocker', 1), 
    377550                ('severity', 'critical', 2), 
    378551                ('severity', 'major', 3), 
     
    426599  ('project', 'footer', 
    427600   ' Visit the Trac open source project at<br />' 
    428601   '<a href="http://trac.edgewall.com/">http://trac.edgewall.com/</a>'), 
     602  ('ticket', 'workflow_version', str(current_workflow_version)), 
    429603  ('ticket', 'default_version', ''), 
    430604  ('ticket', 'default_severity', 'normal'), 
    431605  ('ticket', 'default_priority', 'normal'), 
  • trac/Milestone.py

     
    6060    if not group: 
    6161        queries['all_tickets'] = env.href.query({'milestone': milestone}) 
    6262        queries['active_tickets'] = env.href.query({ 
    63             'milestone': milestone, 'status': ['new', 'assigned', 'reopened'] 
     63            'milestone': milestone, 'status': ['new', 'assigned', 'reopened', 'resolved'] 
    6464        }) 
    6565        queries['closed_tickets'] = env.href.query({ 
    66             'milestone': milestone, 'status': 'closed' 
     66            'milestone': milestone, 'status': ['closed', 'verified'] 
    6767        }) 
    6868    else: 
    6969        queries['all_tickets'] = env.href.query({ 
     
    7171        }) 
    7272        queries['active_tickets'] = env.href.query({ 
    7373            'milestone': milestone, grouped_by: group, 
    74             'status': ['new', 'assigned', 'reopened'] 
     74            'status': ['new', 'assigned', 'reopened', 'resolved'] 
    7575        }) 
    7676        queries['closed_tickets'] = env.href.query({ 
    7777            'milestone': milestone, grouped_by: group, 
    78             'status': 'closed' 
     78            'status': ['closed', 'verified'] 
    7979        }) 
    8080    return queries 
    8181 
    8282def calc_ticket_stats(tickets): 
    8383    total_cnt = len(tickets) 
    84     active = [ticket for ticket in tickets if ticket['status'] != 'closed'] 
     84    active = [ticket for ticket in tickets if ticket['status'] != 'closed' and ticket['status'] != 'verified'] 
    8585    active_cnt = len(active) 
    8686    closed_cnt = total_cnt - active_cnt 
    8787 
     
    116116                if datestr: 
    117117                    date = self.parse_date(datestr) 
    118118            descr = self.args.get('descr', '') 
     119            owner = self.args.get('owner', '') 
    119120            if not id: 
    120                 self.create_milestone(name, date, descr) 
     121                self.create_milestone(name, date, descr, owner) 
    121122            else: 
    122                 self.update_milestone(id, name, date, descr) 
     123                self.update_milestone(id, name, date, descr, owner) 
    123124        elif id: 
    124125            self.req.redirect(self.env.href.milestone(id)) 
    125126        else: 
     
    141142                            'Invalid Date Format') 
    142143        return seconds 
    143144 
    144     def create_milestone(self, name, date=0, descr=''): 
     145    def create_milestone(self, name, date=0, descr='', owner=''): 
    145146        self.perm.assert_permission(perm.MILESTONE_CREATE) 
    146147        if not name: 
    147148            raise TracError('You must provide a name for the milestone.', 
    148149                            'Required Field Missing') 
    149150        cursor = self.db.cursor() 
    150151        self.env.log.debug("Creating new milestone '%s'" % name) 
    151         cursor.execute("INSERT INTO milestone (id, name, time, descr) " 
    152                        "VALUES (NULL, %s, %d, %s)", name, date, descr) 
     152        cursor.execute("INSERT INTO milestone (id, name, time, descr, owner) " 
     153                       "VALUES (NULL, %s, %d, %s, %s)", name, date, descr, owner) 
    153154        self.db.commit() 
    154155        self.req.redirect(self.env.href.milestone(name)) 
    155156 
     
    179180        else: 
    180181            self.req.redirect(self.env.href.milestone(id)) 
    181182 
    182     def update_milestone(self, id, name, date, descr): 
     183    def update_milestone(self, id, name, date, descr, owner): 
    183184        self.perm.assert_permission(perm.MILESTONE_MODIFY) 
    184185        cursor = self.db.cursor() 
    185186        self.env.log.debug("Updating milestone '%s'" % id) 
     
    189190            cursor.execute('UPDATE ticket SET milestone = %s ' 
    190191                            'WHERE milestone = %s', name, id) 
    191192            cursor.execute("UPDATE milestone SET name = %s, time = %d, " 
    192                            "descr = %s WHERE name = %s", 
    193                            name, date, descr, id) 
     193                           "descr = %s, owner = %s WHERE name = %s", 
     194                           name, date, descr, owner, id) 
    194195            self.db.commit() 
     196 
    195197            self.req.redirect(self.env.href.milestone(name)) 
    196198        else: 
    197199            self.req.redirect(self.env.href.milestone(id)) 
     
    223225 
    224226    def get_milestone(self, name): 
    225227        cursor = self.db.cursor() 
    226         cursor.execute("SELECT name, time, descr FROM milestone " 
     228        cursor.execute("SELECT name, time, descr, owner FROM milestone " 
    227229                       "WHERE name = %s ORDER BY time, name", name) 
    228230        row = cursor.fetchone() 
    229231        cursor.close() 
     
    238240        t = row['time'] and int(row['time']) 
    239241        if t > 0: 
    240242            milestone['date'] = time.strftime('%x', time.localtime(t)) 
     243        owner = row['owner'] 
     244        if not owner: owner = '' 
     245        milestone['owner'] = owner 
    241246        return milestone 
    242247 
    243248    def render(self): 
  • trac/Query.py

     
    112112                del constraints[field] 
    113113 
    114114        cursor = self.db.cursor() 
    115         add_options('status', constraints, 'query.options.', cursor, 
    116                     "SELECT name FROM enum WHERE type='status' ORDER BY value") 
     115        if self.env.get_workflow_version() <= 1: 
     116            add_options('status', constraints, 'query.options.', cursor, 
     117                        "SELECT name FROM enum" \ 
     118                        " WHERE type='status' AND name IN" \ 
     119                        "       ('new', 'assigned', 'reopened', 'closed')" \ 
     120                        " ORDER BY value") 
     121        else: 
     122            add_options('status', constraints, 'query.options.', cursor, 
     123                        "SELECT name FROM enum WHERE type='status' ORDER BY value") 
    117124        add_options('resolution', constraints, 'query.options.', cursor, 
    118125                    "SELECT name FROM enum WHERE type='resolution' ORDER BY value") 
    119126        add_options('component', constraints, 'query.options.', cursor, 
  • trac/Timeline.py

     
    5252        REOPENED_TICKET = 4 
    5353        WIKI = 5 
    5454        MILESTONE = 6 
     55        VERIFIED_TICKET = 7 
     56        RESOLVED_TICKET = 8 
     57        RETESTED_TICKET = 9 
    5558 
    5659        q = [] 
    5760        if changeset: 
     
    6972                     "FROM ticket_change WHERE field='status' " 
    7073                     "AND newvalue='reopened' AND time>=%s AND time<=%s" % 
    7174                     (start, stop)) 
    72             q.append("SELECT t1.time AS time, t1.ticket AS idata," 
    73                      "       t2.newvalue AS tdata, 3 AS type," 
    74                      "       t3.newvalue AS message, t1.author AS author" 
    75                      " FROM ticket_change t1" 
    76                      "   INNER JOIN ticket_change t2 ON t1.ticket = t2.ticket" 
    77                      "     AND t1.time = t2.time" 
    78                      "   LEFT OUTER JOIN ticket_change t3 ON t1.time = t3.time" 
    79                      "     AND t1.ticket = t3.ticket AND t3.field = 'comment'" 
    80                      " WHERE t1.field = 'status' AND t1.newvalue = 'closed'" 
    81                      "   AND t2.field = 'resolution'" 
    82                      "   AND t1.time >= %s AND t1.time <= %s" % (start,stop)) 
     75            if self.env.get_workflow_version() <= 1: 
     76                q.append("SELECT t1.time AS time, t1.ticket AS idata," 
     77                         "       t2.newvalue AS tdata, 3 AS type," 
     78                         "       t3.newvalue AS message, t1.author AS author" 
     79                         " FROM ticket_change t1" 
     80                         "   INNER JOIN ticket_change t2 ON t1.ticket = t2.ticket" 
     81                         "     AND t1.time = t2.time" 
     82                         "   LEFT OUTER JOIN ticket_change t3 ON t1.time = t3.time" 
     83                         "     AND t1.ticket = t3.ticket AND t3.field = 'comment'" 
     84                         " WHERE t1.field = 'status' AND t1.newvalue = 'closed'" 
     85                         "   AND t2.field = 'resolution'" 
     86                         "   AND t1.time >= %s AND t1.time <= %s" % (start,stop)) 
     87            else: 
     88                q.append("SELECT time, ticket AS idata, '' AS tdata, 3 AS type, " 
     89                         "'' AS message, author " 
     90                         "FROM ticket_change WHERE field='status' " 
     91                         "AND newvalue='closed' AND time>=%s AND time<=%s" % 
     92                         (start, stop)) 
     93                q.append("SELECT time, ticket AS idata, '' AS tdata, 7 AS type, " 
     94                         "'' AS message, author " 
     95                         "FROM ticket_change WHERE field='status' " 
     96                         "AND newvalue='verified' AND oldvalue<>'closed' AND time>=%s AND time<=%s" % 
     97                         (start, stop)) 
     98                q.append("SELECT t1.time AS time, t1.ticket AS idata," 
     99                         "       t2.newvalue AS tdata, 8 AS type," 
     100                         "       t3.newvalue AS message, t1.author AS author" 
     101                         " FROM ticket_change t1" 
     102                         "   INNER JOIN ticket_change t2 ON t1.ticket = t2.ticket" 
     103                         "     AND t1.time = t2.time" 
     104                         "   LEFT OUTER JOIN ticket_change t3 ON t1.time = t3.time" 
     105                         "     AND t1.ticket = t3.ticket AND t3.field = 'comment'" 
     106                         " WHERE t1.field = 'status' AND t1.newvalue = 'resolved' AND t1.oldvalue NOT IN ('verified', 'closed')" 
     107                         "   AND t2.field = 'resolution'" 
     108                         "   AND t1.time >= %s AND t1.time <= %s" % (start,stop)) 
     109                q.append("SELECT time, ticket AS idata, '' AS tdata, 9 AS type, " 
     110                         "'' AS message, author " 
     111                         "FROM ticket_change WHERE field='status' " 
     112                         "AND newvalue='resolved' AND oldvalue IN ('closed', 'verified') AND time>=%s AND time<=%s" % 
     113                         (start, stop)) 
    83114        if wiki: 
    84115            q.append("SELECT time, -1 AS idata, name AS tdata, 5 AS type, " 
    85116                     "comment AS message, author " 
     
    87118                     (start, stop)) 
    88119        if milestone: 
    89120            q.append("SELECT time, -1 AS idata, '' AS tdata, 6 AS type, " 
    90                      "name AS message, '' AS author "  
     121                     "name AS message, owner AS author "  
    91122                     "FROM milestone WHERE time>=%s AND time<=%s" % 
    92123                     (start, stop)) 
    93124 
  • trac/Roadmap.py

     
    4848            icalhref += '&show=all' 
    4949            self.req.hdf.setValue('roadmap.href.list', 
    5050                                   self.env.href.roadmap()) 
    51             query = "SELECT name, time, descr FROM milestone " \ 
     51            query = "SELECT name, time, descr, owner FROM milestone " \ 
    5252                    "WHERE name != '' " \ 
    5353                    "ORDER BY (IFNULL(time, 0) = 0) ASC, time ASC, name" 
    5454        else: 
    5555            self.req.hdf.setValue('roadmap.showall', '1') 
    5656            self.req.hdf.setValue('roadmap.href.list', 
    5757                                   self.env.href.roadmap('all')) 
    58             query = "SELECT name, time, descr FROM milestone " \ 
     58            query = "SELECT name, time, descr, owner FROM milestone " \ 
    5959                    "WHERE name != '' " \ 
    6060                    "AND (time IS NULL OR time = 0 OR time > %d) " \ 
    6161                    "ORDER BY (IFNULL(time, 0) = 0) ASC, time ASC, name" % time() 
     
    8181                milestone['descr'] = wiki_to_html(descr, self.req.hdf, 
    8282                                                  self.env, self.db) 
    8383                milestone['descr_text'] = descr 
     84            owner = row['owner'] 
     85            if not owner: owner = '' 
     86            milestone['owner'] = owner 
    8487            if milestone['time'] > 0: 
    8588                milestone['date'] = strftime('%x', localtime(milestone['time'])) 
    8689            self.milestones.append(milestone) 
     
    113116            status = ticket['status'] 
    114117            if status == 'new' or status == 'reopened' and not ticket['owner']: 
    115118                return 'NEEDS-ACTION' 
    116             elif status == 'assigned' or status == 'reopened': 
     119            elif status != 'closed': 
    117120                return 'IN-PROCESS' 
    118121            elif status == 'closed': 
    119122                if ticket['resolution'] == 'fixed': return 'COMPLETED' 
  • trac/upgrades/db8.py

     
     1sql = """ 
     2-- Add statuses 'resolved' and 'verified' 
     3UPDATE enum SET value = 6 WHERE type = 'status' AND name = 'closed'; 
     4INSERT INTO enum (type, name, value) VALUES ('status', 'resolved', 4); 
     5INSERT INTO enum (type, name, value) VALUES ('status', 'verified', 5); 
     6 
     7-- Add resolution 'moved' 
     8INSERT INTO enum (type, name, value) VALUES ('resolution', 'moved', 6); 
     9 
     10-- Add QA Contact to 'component' 
     11CREATE TEMPORARY TABLE component_backup AS SELECT * FROM component; 
     12DROP TABLE component; 
     13CREATE TABLE component ( 
     14         name            text PRIMARY KEY, 
     15         owner           text, 
     16         qaowner         text 
     17); 
     18INSERT INTO component SELECT name, owner, owner AS qaowner FROM component_backup; 
     19DROP TABLE component_backup; 
     20 
     21-- Add Release Manager Contact to 'milestone' 
     22CREATE TEMPORARY TABLE milestone_backup AS SELECT * FROM milestone; 
     23DROP TABLE milestone; 
     24CREATE TABLE milestone ( 
     25         id              integer PRIMARY KEY, 
     26         name            text, 
     27         time            integer, 
     28         owner           text, 
     29         descr           text, 
     30         UNIQUE(name) 
     31); 
     32INSERT INTO milestone SELECT id, name, time, '' AS owner, descr FROM milestone_backup; 
     33DROP TABLE milestone_backup; 
     34 
     35-- Modify 'All Tickets by Version' report 
     36UPDATE report SET sql = ' 
     37SELECT p.value AS __color__, 
     38   (CASE WHEN IFNULL(version, '''') = '''' THEN ''Not Specified'' ELSE ''Version '' || version END) AS __group__, 
     39   id AS ticket, summary, component, milestone, severity,  
     40   (CASE status WHEN ''assigned'' THEN owner||'' *'' ELSE owner END) AS owner, 
     41   time AS created, 
     42   changetime AS _changetime, description AS _description, 
     43   reporter AS _reporter 
     44  FROM ticket t, enum p 
     45  WHERE status IN (''new'', ''assigned'', ''reopened'')  
     46AND p.name = t.priority AND p.type = ''priority'' 
     47  ORDER BY (IFNULL(version, '''') = '''') DESC,version, p.value, severity, time 
     48' WHERE title = 'Active Tickets by Version'; 
     49 
     50-- Modify 'All Tickets by Milestone' report 
     51UPDATE report SET sql = ' 
     52SELECT p.value AS __color__, 
     53   (CASE WHEN IFNULL(milestone, '''') = '''' THEN ''Not Assigned'' ELSE milestone||'' Release'' END) AS __group__, 
     54   id AS ticket, summary, component, version, severity,  
     55   (CASE status WHEN ''assigned'' THEN owner||'' *'' ELSE owner END) AS owner, 
     56   time AS created, 
     57   changetime AS _changetime, description AS _description, 
     58   reporter AS _reporter 
     59  FROM ticket t, enum p 
     60  WHERE status IN (''new'', ''assigned'', ''reopened'')  
     61AND p.name = t.priority AND p.type = ''priority'' 
     62  ORDER BY (IFNULL(milestone, '''') = '''') DESC,milestone, p.value, severity, time 
     63', title = 'Active Tickets by Milestone' 
     64WHERE title = 'All Tickets by Milestone'; 
     65 
     66-- Modify 'Assigned, Active Tickets by Owner' report 
     67UPDATE report SET sql = ' 
     68SELECT p.value AS __color__, 
     69   (CASE WHEN IFNULL(owner, '''') = '''' THEN ''Not Assigned'' ELSE owner END) AS __group__, 
     70   id AS ticket, summary, component, version, milestone, severity, time AS created, 
     71   changetime AS _changetime, description AS _description, 
     72   reporter AS _reporter 
     73  FROM ticket t,enum p 
     74  WHERE status = ''assigned'' 
     75AND p.name=t.priority AND p.type=''priority'' 
     76  ORDER BY (IFNULL(owner, '''') = '''') DESC, owner, p.value, severity, time 
     77' WHERE title = 'Assigned, Active Tickets by Owner'; 
     78 
     79-- Modify 'Assigned, Active Tickets by Owner (Full Description)' report 
     80UPDATE report SET sql = ' 
     81SELECT p.value AS __color__, 
     82   owner AS __group__, 
     83   id AS ticket, summary, component, version, milestone, severity, time AS created, 
     84   description AS _description_, 
     85   changetime AS _changetime, reporter AS _reporter 
     86  FROM ticket t, enum p 
     87  WHERE status = ''assigned'' 
     88AND p.name = t.priority AND p.type = ''priority'' 
     89  ORDER BY owner, p.value, severity, time 
     90' WHERE title = 'Assigned, Active Tickets by Owner (Full Description)'; 
     91 
     92-- Modify 'All Tickets By Milestone  (Including closed)' report 
     93UPDATE report SET sql = ' 
     94SELECT p.value AS __color__, 
     95   (CASE WHEN IFNULL(t.milestone, '''') = '''' THEN ''Not Assigned'' ELSE t.milestone || '' Release'' END) AS __group__, 
     96   (CASE status  
     97      WHEN ''closed'' THEN ''color: #777; background: #ddd; border-color: #ccc;'' 
     98      ELSE  
     99        (CASE owner WHEN ''$USER'' THEN ''font-weight: bold'' END) 
     100    END) AS __style__, 
     101   id AS ticket, summary, component, status,  
     102   resolution,version, severity, priority, owner, 
     103   changetime AS modified, 
     104   time AS _time,reporter AS _reporter 
     105  FROM ticket t,enum p 
     106  WHERE p.name=t.priority AND p.type=''priority'' 
     107  ORDER BY (IFNULL(milestone, '''') = '''') DESC, milestone DESC, (status = ''closed''),  
     108        (CASE status WHEN ''closed'' THEN modified ELSE -p.value END) DESC 
     109' WHERE title = 'All Tickets By Milestone  (Including closed)'; 
     110 
     111-- Modify 'My Tickets' report 
     112UPDATE report SET sql = ' 
     113SELECT p.value AS __color__, 
     114   (CASE status WHEN ''assigned'' THEN ''Assigned'' ELSE ''Owned'' END) AS __group__, 
     115   id AS ticket, summary, component, status, version, milestone, 
     116   severity, priority, time AS created, 
     117   changetime AS _changetime, description AS _description, 
     118   reporter AS _reporter 
     119  FROM ticket t, enum p 
     120  WHERE t.status <> ''closed''  
     121AND p.name = t.priority AND p.type = ''priority'' AND owner = ''$USER'' 
     122  ORDER BY (status = ''assigned'') DESC, p.value, milestone, severity, time 
     123' WHERE title = 'My Tickets'; 
     124 
     125-- New reports 
     126 
     127INSERT INTO report VALUES(NULL,NULL,'Open Tickets, Mine first',' 
     128SELECT p.value AS __color__, 
     129   (CASE owner  
     130     WHEN ''$USER'' THEN ''My Tickets''  
     131     ELSE ''Open Tickets''  
     132    END) AS __group__, 
     133   id AS ticket, summary, component, status, version, milestone, severity,  
     134   (CASE status WHEN ''assigned'' THEN owner||'' *'' ELSE owner END) AS owner, 
     135   time AS created, 
     136   changetime AS _changetime, description AS _description, 
     137   reporter AS _reporter 
     138  FROM ticket t, enum p 
     139  WHERE status <> ''closed''  
     140AND p.name = t.priority AND p.type = ''priority'' 
     141  ORDER BY (owner = ''$USER'') DESC, p.value, milestone, severity, time 
     142',' 
     143 * List all not closed tickets by priority. 
     144 * Show all tickets owned by the logged in user in a group first. 
     145'); 
     146 
     147INSERT INTO report VALUES(NULL,NULL,'Open Tickets by Version',' 
     148SELECT p.value AS __color__, 
     149   (CASE WHEN IFNULL(version, '''') = '''' THEN ''Not Specified'' ELSE ''Version '' || version END) AS __group__, 
     150   id AS ticket, summary, component, status, milestone, severity,  
     151   (CASE status WHEN ''assigned'' THEN owner||'' *'' ELSE owner END) AS owner, 
     152   time AS created, 
     153   changetime AS _changetime, description AS _description, 
     154   reporter AS _reporter 
     155  FROM ticket t, enum p 
     156  WHERE status <> ''closed'' 
     157AND p.name = t.priority AND p.type = ''priority'' 
     158  ORDER BY (IFNULL(version, '''') = '''') desc,version, p.value, severity, time 
     159',' 
     160 * List all not closed tickets by priority. 
     161 * Group results by version. 
     162'); 
     163 
     164INSERT INTO report VALUES(NULL,NULL,'Open Tickets by Milestone',' 
     165SELECT p.value AS __color__, 
     166   (CASE WHEN IFNULL(milestone, '''') = '''' THEN ''Not Assigned'' ELSE milestone||'' Release'' END) AS __group__, 
     167   id AS ticket, summary, component, status, version, severity,  
     168   (CASE status WHEN ''assigned'' THEN owner||'' *'' ELSE owner END) AS owner, 
     169   time AS created, 
     170   changetime AS _changetime, description AS _description, 
     171   reporter AS _reporter 
     172  FROM ticket t, enum p 
     173  WHERE status <> ''closed''  
     174AND p.name = t.priority AND p.type = ''priority'' 
     175  ORDER BY (IFNULL(milestone, '''') = '''') DESC,milestone, p.value, severity, time 
     176',' 
     177 * List all not closed tickets by priority. 
     178 * Group results by milestone. 
     179'); 
     180 
     181INSERT INTO report VALUES(NULL,NULL,'Open Tickets by Owner',' 
     182SELECT p.value AS __color__, 
     183   (CASE WHEN IFNULL(owner, '''') = '''' THEN ''Not Assigned'' ELSE owner END) AS __group__, 
     184   id AS ticket, summary, component, status, version, milestone, severity, 
     185   time AS created, changetime AS _changetime, description AS _description, 
     186   reporter AS _reporter 
     187  FROM ticket t,enum p 
     188  WHERE status <> ''closed'' 
     189AND p.name=t.priority AND p.type=''priority'' 
     190  ORDER BY (IFNULL(owner, '''') = '''') DESC, owner, p.value, severity, time 
     191',' 
     192List not closed tickets, group by ticket owner, sorted by priority. 
     193'); 
     194 
     195INSERT INTO report VALUES(NULL,NULL,'Open Tickets by Status',' 
     196SELECT p.value AS __color__, 
     197   status AS __group__, 
     198   id AS ticket, summary, component, version, milestone, severity, owner, 
     199   time AS created, 
     200   changetime AS _changetime, description AS _description, 
     201   reporter AS _reporter 
     202  FROM ticket t, enum q, enum p 
     203  WHERE status <> ''closed''  
     204AND q.name = t.status AND q.type = ''status'' 
     205AND p.name = t.priority AND p.type = ''priority'' 
     206  ORDER BY q.value, p.value, severity, time 
     207',' 
     208 * List all not closed tickets by priority. 
     209 * Group results by status. 
     210'); 
     211 
     212INSERT INTO report VALUES(NULL,NULL,'Resolved Tickets, Mine first',' 
     213SELECT p.value AS __color__, 
     214   (CASE owner  
     215     WHEN ''$USER'' THEN ''My Tickets''  
     216     ELSE ''Active Tickets''  
     217    END) AS __group__, 
     218   id AS ticket, summary, component, version, milestone, severity,  
     219   (CASE status WHEN ''assigned'' THEN owner||'' *'' ELSE owner END) AS owner, 
     220   time AS created, 
     221   changetime AS _changetime, description AS _description, 
     222   reporter AS _reporter 
     223  FROM ticket t, enum p 
     224  WHERE status = ''resolved''  
     225AND p.name = t.priority AND p.type = ''priority'' 
     226  ORDER BY (owner = ''$USER'') DESC, p.value, milestone, severity, time 
     227',' 
     228 * List all resolved tickets by priority. 
     229 * Show all tickets owned by the logged in user in a group first. 
     230'); 
     231 
     232INSERT INTO report VALUES(NULL,NULL,'Resolved Tickets by Milestone',' 
     233SELECT p.value AS __color__, 
     234   (CASE WHEN IFNULL(milestone, '''') = '''' THEN ''Not Assigned'' ELSE milestone||'' Release'' END) AS __group__, 
     235   id AS ticket, summary, component, version, severity,  
     236   (CASE status WHEN ''assigned'' THEN owner||'' *'' ELSE owner END) AS owner, 
     237   time AS created, 
     238   changetime AS _changetime, description AS _description, 
     239   reporter AS _reporter 
     240  FROM ticket t, enum p 
     241  WHERE status = ''resolved''  
     242AND p.name = t.priority AND p.type = ''priority'' 
     243  ORDER BY (IFNULL(milestone, '''') = '''') DESC,milestone, p.value, severity, time 
     244',' 
     245List resolved tickets, sorted by priority, grouped by milestone 
     246'); 
     247 
     248INSERT INTO report VALUES(NULL,NULL,'Resolved Tickets by Owner',' 
     249SELECT p.value AS __color__, 
     250   (CASE WHEN IFNULL(owner, '''') = '''' THEN ''Not Assigned'' ELSE owner END) AS __group__, 
     251   id AS ticket, summary, component, version, milestone, severity, time AS created, 
     252   changetime AS _changetime, description AS _description, 
     253   reporter AS _reporter 
     254  FROM ticket t,enum p 
     255  WHERE status = ''resolved'' 
     256AND p.name=t.priority AND p.type=''priority'' 
     257  ORDER BY (IFNULL(owner, '''') = '''') DESC, owner, p.value, severity, time 
     258',' 
     259List resolved tickets, group by ticket owner, sorted by priority. 
     260'); 
     261 
     262INSERT INTO report VALUES(NULL,NULL,'Verified Tickets by Milestone (Full Description)',' 
     263SELECT p.value AS __color__, 
     264   (CASE WHEN IFNULL(milestone, '''') = '''' THEN ''Not Assigned'' ELSE milestone||'' Release'' END) AS __group__, 
     265   id AS ticket, summary, component, status, version, severity, time AS created, 
     266   description AS _description_, 
     267   changetime AS _changetime, reporter AS _reporter 
     268  FROM ticket t, enum p 
     269  WHERE status IN (''verified'', ''closed'') 
     270AND p.name = t.priority AND p.type = ''priority'' 
     271  ORDER BY (IFNULL(milestone, '''') = '''') DESC,milestone, p.value, severity, time 
     272',' 
     273Release Notes: List verified and closed tickets, group by milestone, include description. 
     274'); 
     275""" 
     276 
     277def do_upgrade(env, ver, cursor): 
     278    cursor.execute(sql) 
     279    env.set_config('ticket', 'workflow_version', '1') 
     280    env.save_config() 
  • trac/Environment.py

     
    163163        row = cursor.fetchone() 
    164164        return row and int(row[0]) 
    165165 
     166    def get_workflow_version(self): 
     167        return int(self.get_config('ticket', 'workflow_version', str(db_default.current_workflow_version))) 
     168 
    166169    def load_config(self): 
    167170        self.cfg = ConfigParser.ConfigParser() 
    168171        self.cfg.read(os.path.join(self.path, 'conf', 'trac.ini')) 
  • trac/Ticket.py

     
    9393            if not dict.has_key(name): 
    9494                self[name] = '0' 
    9595 
     96    def calc_owner(self, db): 
     97        """Calculate owner field if the owner is not specified or empty. 
     98           It depends on ticket.status, component.owner, component.qaowner, and 
     99           milestone.owner""" 
     100        cursor = db.cursor() 
     101        status = self.get('status', 'new') 
     102        component = self.get('component') 
     103        milestone = self.get('milestone') 
     104 
     105        # 1. The owner field defaults to the component owner for active tickets 
     106        if self.get('owner', '') == '' and component and status in ['new', 'reopened']: 
     107            cursor.execute('SELECT owner FROM component WHERE name=%s', component) 
     108            newowner = cursor.fetchone()[0] 
     109            if newowner: self['owner'] = newowner 
     110        # 2. The owner field defaults to component QA owner for testing tickets 
     111        if self.get('owner', '') == '' and component and status == 'resolved': 
     112            cursor.execute('SELECT qaowner FROM component WHERE name=%s', component) 
     113            newowner = cursor.fetchone()[0] 
     114            if newowner: self['owner'] = newowner 
     115        # 3. The owner field defaults to milestone owner for open tickets 
     116        if self.get('owner', '') == '' and milestone and status != 'closed': 
     117            cursor.execute('SELECT owner FROM milestone WHERE name=%s', milestone) 
     118            newowner = cursor.fetchone()[0] 
     119            if newowner: self['owner'] = newowner 
     120        # 4. The owner field defaults to reporter for verified tickets 
     121        if self.get('owner', '') == '' and status == 'verified': 
     122            reporter = self.get('reporter', '') 
     123            if reporter: self['owner'] = reporter 
     124 
    96125    def insert(self, db): 
    97126        """Add ticket to database""" 
    98127        cursor = db.cursor() 
     
    132161 
    133162        # If the component is changed on a 'new' ticket then owner field 
    134163        # is updated accordingly. (#623). 
     164        owner_updated = 0 
    135165        if self['status'] == 'new' and self._old.has_key('component') and \ 
    136166               not self._old.has_key('owner'): 
    137167            cursor.execute('SELECT owner FROM component ' 
     
    140170            if self['owner'] == old_owner: 
    141171                cursor.execute('SELECT owner FROM component ' 
    142172                               'WHERE name=%s', self['component']) 
    143                 self['owner'] = cursor.fetchone()[0] 
    144             
     173                newowner = cursor.fetchone()[0] 
     174                if newowner: 
     175                    self['owner'] = newowner 
     176                    owner_updated = 1 
     177        if owner_updated == 0 and self['status'] == 'new' and \ 
     178               self._old.has_key('milestone') and not self._old.has_key('owner'): 
     179            cursor.execute('SELECT owner FROM milestone ' 
     180                           'WHERE name=%s', self._old['milestone']) 
     181            old_owner = cursor.fetchone()[0] 
     182            if self['owner'] == old_owner: 
     183                cursor.execute('SELECT owner FROM milestone ' 
     184                               'WHERE name=%s', self['milestone']) 
     185                newowner = cursor.fetchone()[0] 
     186                if newowner: 
     187                    self['owner'] = newowner 
     188                    owner_updated = 1 
    145189 
    146190        for name in self._old.keys(): 
    147191            if name[:7] == 'custom_': 
     
    261305        ticket = Ticket() 
    262306        ticket.populate(self.args) 
    263307        ticket.setdefault('reporter',self.req.authname) 
     308        ticket.calc_owner(self.db) 
    264309 
    265         # The owner field defaults to the component owner 
    266         cursor = self.db.cursor() 
    267         if ticket.get('component') and ticket.get('owner', '') == '': 
    268             cursor.execute('SELECT owner FROM component ' 
    269                            'WHERE name=%s', ticket['component']) 
    270             owner = cursor.fetchone()[0] 
    271             ticket['owner'] = owner 
    272  
    273310        tktid = ticket.insert(self.db) 
    274311 
    275312        # Notify 
     
    304341                                               self.req.hdf, self.env, self.db)) 
    305342 
    306343        self.req.hdf.setValue('title', 'New Ticket') 
     344        self.req.hdf.setValue('workflow_version', str(self.env.get_workflow_version())) 
    307345        evals = util.mydict(zip(ticket.keys(), 
    308346                                map(lambda x: util.escape(x), ticket.values()))) 
    309347        util.add_to_hdf(evals, self.req.hdf, 'newticket') 
     
    336374 
    337375        # TODO: this should not be hard-coded like this 
    338376        action = self.args.get('action', None) 
    339         if action == 'accept': 
    340             ticket['status'] =  'assigned' 
    341             ticket['owner'] = self.req.authname 
    342         if action == 'resolve': 
     377        wv = self.env.get_workflow_version() 
     378        if wv == 2 and action == 'verify2': 
     379            ticket['status'] = 'verified' 
     380            ticket['owner'] = '' 
     381        elif wv == 2 and action == 'close2': 
    343382            ticket['status'] = 'closed' 
     383        elif wv == 2 and action == 'retest2': 
     384            ticket['status'] = 'resolved' 
     385            ticket['owner'] = '' 
     386        elif wv == 2 and action == 'resolve': 
    344387            ticket['resolution'] = self.args.get('resolve_resolution') 
     388            ticket['status'] = 'resolved' 
     389            ticket['owner'] = '' 
     390        elif wv == 2 and action == 'reassign': 
     391            newowner = self.args.get('reassign_owner') 
     392            if ticket['owner'] != newowner: 
     393                ticket['owner'] = newowner 
     394                if ticket['status'] == 'assigned': ticket['status'] = 'new' 
     395        elif wv == 2 and action == 'reopen': 
     396            ticket['status'] = 'reopened' 
     397            ticket['resolution'] = '' 
     398            ticket['owner'] = '' 
     399        elif wv in [1,2] and action == 'accept': 
     400            if self.req.authname: 
     401                ticket['status'] =  'assigned' 
     402                ticket['owner'] = self.req.authname 
     403        elif action == 'resolve': 
     404            ticket['status'] = 'closed' 
     405            ticket['resolution'] = self.args.get('resolve_resolution') 
    345406        elif action == 'reassign': 
    346407            ticket['owner'] = self.args.get('reassign_owner') 
    347408            ticket['status'] = 'new' 
     
    350411            ticket['resolution'] = '' 
    351412 
    352413        ticket.populate(self.args) 
     414        ticket.calc_owner(self.db) 
    353415 
    354416        now = int(time.time()) 
    355417 
     
    386448 
    387449        self.req.hdf.setValue('ticket.reporter_id', util.escape(reporter_id)) 
    388450        self.req.hdf.setValue('title', '#%d (%s)' % (id,ticket['summary'])) 
     451        self.req.hdf.setValue('workflow_version', str(self.env.get_workflow_version())) 
    389452        self.req.hdf.setValue('ticket.description.formatted', 
    390453                              wiki_to_html(ticket['description'], self.req.hdf, 
    391454                                           self.env, self.db)) 
     
    427490        id = int(self.args.get('id')) 
    428491 
    429492        if not preview \ 
    430                and action in ['leave', 'accept', 'reopen', 'resolve', 'reassign']: 
     493               and (action in ['leave', 'accept', 'reopen', 'resolve', 'reassign'] \ 
     494                    or self.env.get_workflow_version() >= 2 \ 
     495                       and action in ['verify2', 'close2', 'retest2']): 
    431496            self.save_changes (id) 
    432497 
    433498        ticket = Ticket(self.db, id) 
  • trac/WikiFormatter.py

     
    125125            elif row[1] == 'closed': 
    126126                return '<a href="%s" title="CLOSED : %s"><del>#%d</del></a>' % (self._href.ticket(number), summary, number) 
    127127            else: 
    128                 return '<a href="%s" title="%s">#%d</a>' % (self._href.ticket(number), summary, number) 
     128                return '<a href="%s" title="%s : %s">#%d</a>' % (self._href.ticket(number), row[1].upper(), summary, number) 
    129129 
    130130    def _changesethref_formatter(self, match, fullmatch): 
    131131        number = int(match[1:-1]) 
     
    158158                elif row[1] == 'closed': 
    159159                    return self._href.ticket(args), '<del>%s:%s</del>' % (module, args), 0, 'CLOSED: ' + summary 
    160160                else: 
    161                     return self._href.ticket(args), '%s:%s' % (module, args), 0, summary 
     161                    return self._href.ticket(args), '%s:%s' % (module, args), 0, row[1].upper() + ': ' + summary 
    162162            else: 
    163163                return self._href.ticket(args), '%s:%s' % (module, args), 1, '' 
    164164        elif module == 'wiki': 
  • templates/ticket.cs

     
    3333<div id="ticket"> 
    3434 <div class="date"><?cs var:ticket.opened ?></div> 
    3535 <h1>Ticket #<?cs var:ticket.id ?> <?cs 
    36  if:ticket.status == 'closed' ?>(Closed: <?cs var:ticket.resolution ?>)<?cs 
     36 if:ticket.status == 'closed' || ticket.status == 'verified' || ticket.status == 'resolved' ?> 
     37  (<?cs var:ticket.status ?>: <?cs var:ticket.resolution ?>)<?cs 
    3738 elif:ticket.status != 'new' ?>(<?cs var:ticket.status ?>)<?cs 
    3839 /if ?></h1> 
    3940 <h2><?cs var:ticket.summary ?></h2> 
     
    219220  /def ?> 
    220221  <?cs call:action_radio('leave') ?> 
    221222  <label for="leave">leave as <?cs var:ticket.status ?></label><br /><?cs 
    222   if $ticket.status == "new" ?> 
     223  if $ticket.status == "new" || $workflow_version == "2" && $ticket.status == "reopened" ?> 
    223224   <?cs call:action_radio('accept') ?> 
    224225   <label for="accept">accept ticket</label><br /><?cs 
    225226  /if ?><?cs 
    226   if $ticket.status == "closed" ?> 
    227    <?cs call:action_radio('reopen') ?> 
    228    <label for="reopen">reopen ticket</label><br /><?cs 
    229   /if ?><?cs 
    230227  if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
    231228   <?cs call:action_radio('resolve') ?> 
    232229   <label for="resolve">resolve</label> 
    233230   <label for="resolve_resolution">as:</label> 
    234231   <?cs call:hdf_select(enums.resolution, "resolve_resolution", args.resolve_resolution) ?><br /> 
     232  <?cs /if ?> 
     233 
     234  <?cs if $workflow_version == "2" && $ticket.status == "resolved" ?> 
     235   <?cs call:action_radio('verify2') ?> 
     236   <label for="verify2">verify ticket</label><br /><?cs 
     237  /if ?> 
     238 
     239  <?cs if $workflow_version == "2" && ($ticket.status == "resolved" || $ticket.status == "verified") ?> 
     240   <?cs call:action_radio('close2') ?> 
     241   <label for="close2">close ticket</label><br /><?cs 
     242  /if ?> 
     243 
     244  <?cs if $ticket.status == "closed" || $workflow_version == "2" && ($ticket.status == "verified" || $ticket.status == "resolved") ?> 
     245   <?cs call:action_radio('reopen') ?> 
     246   <label for="reopen">reopen ticket</label><br /><?cs 
     247  /if ?> 
     248 
     249  <?cs if $workflow_version == "2" && ($ticket.status == "verified" || $ticket.status == "closed") ?> 
     250   <?cs call:action_radio('retest2') ?> 
     251   <label for="retest2">retest ticket</label><br /><?cs 
     252  /if ?> 
     253 
     254  <?cs 
     255  if $ticket.status != "closed" ?> 
    235256   <?cs call:action_radio('reassign') ?> 
    236257   <label for="reassign">reassign</label> 
    237258   <label for="reassign_owner">to:</label> 
     
    239260     if:args.reassign_to ?><?cs var:args.reassign_to ?><?cs 
    240261     else ?><?cs var:trac.authname ?><?cs /if ?>" /><?cs 
    241262  /if ?><?cs 
    242   if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
     263  if $ticket.status != "closed" ?> 
    243264   <script type="text/javascript"> 
    244      var resolve = document.getElementById("resolve"); 
     265     <?cs if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
     266       var resolve = document.getElementById("resolve");<?cs /if ?> 
    245267     var reassign = document.getElementById("reassign"); 
    246268     var updateActionFields = function() { 
    247        enableControl('resolve_resolution', resolve.checked); 
     269       <?cs if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
     270         enableControl('resolve_resolution', resolve.checked);<?cs /if ?> 
    248271       enableControl('reassign_owner', reassign.checked); 
    249272     }; 
    250273     addEvent(window, 'load', updateActionFields); 
    251      addEvent(document.getElementById("leave"), 'click', updateActionFields);<?cs 
    252     if $ticket.status == "new" ?> 
    253      addEvent(document.getElementById("accept"), 'click', updateActionFields);<?cs 
    254     /if ?> 
    255     addEvent(resolve, 'click', updateActionFields); 
    256     addEvent(reassign, 'click', updateActionFields); 
     274     addEvent(document.getElementById("leave"), 'click', updateActionFields); 
     275     <?cs if $ticket.status == "new" || $workflow_version == "2" && $ticket.status == "reopened" ?> 
     276       addEvent(document.getElementById("accept"), 'click', updateActionFields);<?cs /if ?> 
     277     <?cs if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
     278       addEvent(resolve, 'click', updateActionFields);<?cs /if ?> 
     279     <?cs if $workflow_version == "2" && $ticket.status == "resolved" ?> 
     280       addEvent(document.getElementById("verify2"), 'click', updateActionFields);<?cs /if ?> 
     281     <?cs if $workflow_version == "2" && ($ticket.status == "resolved" || $ticket.status == "verified") ?> 
     282       addEvent(document.getElementById("close2"), 'click', updateActionFields);<?cs /if ?> 
     283     <?cs if $ticket.status == "closed" || $workflow_version == "2" && ($ticket.status == "verified" || $ticket.status == "resolved") ?> 
     284       addEvent(document.getElementById("reopen"), 'click', updateActionFields);<?cs /if ?> 
     285     <?cs if $workflow_version == "2" && ($ticket.status == "verified" || $ticket.status == "closed") ?> 
     286       addEvent(document.getElementById("retest2"), 'click', updateActionFields);<?cs /if ?> 
     287     addEvent(reassign, 'click', updateActionFields); 
    257288   </script><?cs 
    258289  /if ?> 
    259290 </fieldset> 
  • templates/roadmap.cs

     
    2222      var:milestone.name ?></em></a></h2> 
    2323    <p class="date"><?cs if:milestone.date ?> 
    2424     <?cs var:milestone.date ?><?cs else ?>No date set<?cs /if ?> 
     25     <?cs if:milestone.owner ?>&nbsp;(<?cs var:milestone.owner ?>)<?cs /if ?> 
    2526    </p> 
    2627    <?cs with:stats = milestone.stats ?> 
    2728     <?cs if:#stats.total_tickets > #0 ?> 
  • templates/timeline_rss.cs

     
    4343                             $item.href, $item.msg_escwiki)  
    4444        ?><?cs elif:item.type == #3 
    4545        ?><!-- Closed ticket --> <?cs call:rss_item('Ticket', 
    46                              'Ticket #'+$item.idata+' resolved: '+$item.shortmsg, 
     46                             'Ticket #'+$item.idata+' closed: '+$item.shortmsg, 
    4747                             $item.href, $item.msg_escwiki)  
    4848        ?><?cs elif:item.type == #4  
    4949        ?><!-- Reopened ticket --><?cs call:rss_item('Ticket', 
    5050                             '#'+$item.idata+' reopened: '+$item.shortmsg, 
    5151                             $item.href, $item.msg_escwiki)  
     52        ?><?cs elif:item.type == #7  
     53        ?><!-- Verified ticket --><?cs call:rss_item('Ticket', 
     54                             '#'+$item.idata+' verified: '+$item.shortmsg, 
     55                             $item.href, $item.msg_escwiki)  
     56        ?><?cs elif:item.type == #8 
     57        ?><!-- Resolved ticket --> <?cs call:rss_item('Ticket', 
     58                             'Ticket #'+$item.idata+' resolved: '+$item.shortmsg, 
     59                             $item.href, $item.msg_escwiki)  
     60        ?><?cs elif:item.type == #9  
     61        ?><!-- Retested ticket --><?cs call:rss_item('Ticket', 
     62                             '#'+$item.idata+' retested: '+$item.shortmsg, 
     63                             $item.href, $item.msg_escwiki)  
    5264        ?><?cs elif:item.type == #5  
    5365        ?><!-- Wiki change --><?cs call:rss_item('Wiki', 
    5466                             $item.tdata+" page edited.", 
  • templates/milestone.cs

     
    5656      var:milestone.name ?>" /> 
    5757   </div> 
    5858   <div class="field"> 
     59    <label for="owner">Owner of the milestone (Release Manager):</label><br /> 
     60    <input type="text" id="owner" name="owner" size="32" value="<?cs 
     61      var:milestone.owner ?>" /> 
     62   </div> 
     63   <div class="field"> 
    5964    <label for="datemode">Completion date:</label><br /> 
    6065    <select name="datemode" id="datemode" 
    6166        onchange="enableControl('date',this.value=='manual'); 
     
    108113 <?cs else ?> 
    109114  <em class="date"><?cs if:milestone.date ?> 
    110115   <?cs var:milestone.date ?><?cs else ?>No date set<?cs /if ?> 
     116   <?cs if:milestone.owner ?>&nbsp;(<?cs var:milestone.owner ?>)<?cs /if ?> 
    111117  </em> 
    112118  <div class="descr"><?cs var:milestone.descr ?></div> 
    113119 <?cs /if ?> 
     
    120126  <thead><tr> 
    121127   <th class="name" rowspan="2"><?cs var:milestone.stats.grouped_by ?></th> 
    122128   <th class="tickets" scope="col" colspan="2">Tickets</th> 
    123    <th class="progress" rowspan="2">Percent Resolved</th> 
     129   <th class="progress" rowspan="2">Percent Completed</th> 
    124130  </tr><tr> 
    125131   <th class="open" scope="col">Active</th> 
    126132   <th class="closed" scope="col">Closed</th> 
  • templates/timeline.cs

     
    7373   <?cs set:imessage = '' ?> 
    7474  <?cs /if ?> 
    7575  <?cs call:tlitem(item.href, 'closedticket', 
    76     'Ticket <em>#'+$item.idata+'</em> resolved by '+$item.author,  
     76    'Ticket <em>#'+$item.idata+'</em> closed by '+$item.author,  
    7777    $item.tdata+$imessage) ?> 
    7878 <?cs elif:item.type == #4 ?><!-- Reopened ticket --> 
    79   <?cs call:tlitem(item.href, 'newticket', 
     79  <?cs call:tlitem(item.href, 'reopenedticket', 
    8080    'Ticket <em>#'+$item.idata+'</em> reopened by '+$item.author, '') ?> 
     81 <?cs elif:item.type == #7 ?><!-- Verified ticket --> 
     82  <?cs call:tlitem(item.href, 'closedticket', 
     83    'Ticket <em>#'+$item.idata+'</em> verified by '+$item.author, '') ?> 
     84 <?cs elif:item.type == #8 ?><!-- Resolved ticket --> 
     85  <?cs if:item.message ?> 
     86   <?cs set:imessage = ' - ' + $item.message ?> 
     87  <?cs else ?> 
     88   <?cs set:imessage = '' ?> 
     89  <?cs /if ?> 
     90  <?cs call:tlitem(item.href, 'resolvedticket', 
     91    'Ticket <em>#'+$item.idata+'</em> resolved by '+$item.author,  
     92    $item.tdata+$imessage) ?> 
     93 <?cs elif:item.type == #9 ?><!-- Retested ticket --> 
     94  <?cs call:tlitem(item.href, 'reopenedticket', 
     95    'Ticket <em>#'+$item.idata+'</em> retested by '+$item.author, '') ?> 
    8196 <?cs elif:item.type == #5 ?><!-- Wiki change --> 
    8297  <?cs call:tlitem(item.href, 'wiki', 
    8398    '<em>'+$item.tdata+'</em> edited by '+$item.author, item.message) ?>