Edgewall Software

NewWorkflow: patch-newworkflow-r1003.diff

File patch-newworkflow-r1003.diff, 80.4 KB (added by pkou@…, 7 years ago)

Cumulative patch for new workflow in tickets

  • 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 } 
    3334dt.closedticket, dt.closedticket a { background-image: url(../closedticket.png) !important } 
    3435dt.wiki, dt.wiki a { background-image: url(../wiki.png) !important } 
    3536dt.milestone, dt.milestone a { background-image: url(../milestone.png) !important } 
  • wiki-default/TracIni

    Cannot display: file marked as a binary type.
    svn:mime-type = image/png
     
    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() 
     
    349357        self.db_execsql("UPDATE ticket SET component='%s' WHERE component='%s'" 
    350358                        % (newname,name), cursor) 
    351359        cnx.commit() 
     360        if self.__env.get_config('ticket', 'default_component') == name: 
     361            self.__env.set_config('ticket', 'default_component', newname) 
     362            self.__env.save_config() 
    352363 
    353364    def _do_component_remove(self, name): 
    354365        cnx = self.db_open() 
     
    359370            raise Exception("No such component '%s'" % name) 
    360371        data = self.db_execsql("DELETE FROM component WHERE name='%s'" 
    361372                               % (name)) 
     373        if self.__env.get_config('ticket', 'default_component') == name: 
     374            self.__env.set_config('ticket', 'default_component', '') 
     375            self.__env.save_config() 
    362376 
    363     def _do_component_set_owner(self, name, owner): 
     377    def _do_component_set_owner(self, name, owner, qaowner): 
    364378        cnx = self.db_open() 
    365379        cursor = cnx.cursor () 
    366380        cursor.execute('SELECT name FROM component WHERE name=%s', name) 
    367381        data = cursor.fetchone() 
    368382        if not data: 
    369383            raise Exception("No such component '%s'" % name) 
    370         data = self.db_execsql("UPDATE component SET owner='%s' WHERE name='%s'" 
    371                                % (owner,name)) 
     384        data = self.db_execsql("UPDATE component SET owner='%s', qaowner='%s' WHERE name='%s'" 
     385                               % (owner,qaowner,name)) 
    372386 
    373387 
    374388    ## Permission 
     
    783797            raise Exception, "No such value '%s'" % name 
    784798        data = self.db_execsql("UPDATE enum SET name='%(newname)s'"  
    785799                               " WHERE type='%(type)s' AND name='%(name)s'" % d) 
     800        if self.__env.get_config('ticket', 'default_' + type) == name: 
     801            self.__env.set_config('ticket', 'default_' + type, newname) 
     802            self.__env.save_config() 
     803        data = self.db_execsql("UPDATE ticket SET %(type)s='%(newname)s'" 
     804                               " WHERE %(type)s='%(name)s'" % d) 
    786805 
    787806    def _do_enum_remove(self, type, name): 
    788807        data = self.db_execsql("SELECT name FROM enum"  
     
    791810            raise Exception, "No such value '%s'" % name 
    792811        data = self.db_execsql("DELETE FROM enum WHERE type='%s' AND name='%s'" 
    793812                               % (type, name)) 
     813        if self.__env.get_config('ticket', 'default_' + type) == name: 
     814            self.__env.set_config('ticket', 'default_' + type, '') 
     815            self.__env.save_config() 
     816        newname = self.__env.get_config('ticket', 'default_' + type) 
     817        d = {'name':name, 'newname':newname, 'type':type} 
     818        data = self.db_execsql("UPDATE ticket SET %(type)s='%(newname)s'" 
     819                               " WHERE %(type)s='%(name)s'" % d) 
    794820 
    795821 
    796822    ## Milestone 
    797823    _help_milestone = [('milestone list', 'Show milestones'), 
    798                        ('milestone add <name> [time]', 'Add milestone'), 
     824                       ('milestone add <name> [<owner> [time]]', 'Add milestone'), 
    799825                       ('milestone rename <name> <newname>', 
    800826                        'Rename milestone'), 
     827                       ('milestone chown <name> <newowner>', 'Change milestone owner'), 
    801828                       ('milestone time <name> <time>', 'Set milestone date (Format: "Jun 3, 2003")'), 
    802829                       ('milestone remove <name>', 'Remove milestone')] 
    803830 
     
    805832 
    806833        if begidx in [15,17]: 
    807834            comp = self.get_milestone_list () 
     835        elif begidx > 15 and line.startswith('milestone chown '): 
     836            comp = self.get_user_list() 
    808837        elif begidx < 15: 
    809             comp = ['list','add','rename','time','remove'] 
     838            comp = ['list','add','rename','chown','time','remove'] 
    810839        return self.word_complete(text, comp) 
    811840 
    812841    def do_milestone(self, line): 
    813         self._do_mile_ver('milestone', line) 
     842        type = 'milestone' 
     843        arg = self.arg_tokenize(line) 
     844        try: 
     845            if arg[0]  == 'list': 
     846                self._do_milestone_list() 
     847            elif arg[0] == 'add' and len(arg) in [2,3,4]: 
     848                name = arg[1] 
     849                self._do_mile_ver_add(type, name) 
     850                if len(arg) >= 3: 
     851                    owner = arg[2] 
     852                    self._do_mile_ver_chown(type, name, owner) 
     853                if len(arg) >= 4: 
     854                    time = arg[3] 
     855                    self._do_mile_ver_time(type, name, time) 
     856            elif arg[0] == 'rename' and len(arg)==3: 
     857                name = arg[1] 
     858                newname = arg[2] 
     859                self._do_mile_ver_rename(type, name, newname) 
     860            elif arg[0] == 'chown' and len(arg)==3: 
     861                name = arg[1] 
     862                owner = arg[2] 
     863                self._do_mile_ver_chown(type, name, owner) 
     864            elif arg[0] == 'time' and len(arg)==3: 
     865                name = arg[1] 
     866                time = arg[2] 
     867                self._do_mile_ver_time(type, name, time) 
     868            elif arg[0] == 'remove' and len(arg)==2: 
     869                name = arg[1] 
     870                self._do_mile_ver_remove(type, name) 
     871            else: 
     872                self.do_help (type) 
     873        except Exception, e: 
     874            print 'Command %s failed:' % arg[0], e 
    814875 
     876    def _do_milestone_list(self): 
     877        data = self.db_execsql("SELECT name,owner,time FROM milestone ORDER BY time,name") 
     878        data = map(lambda x: (x[0], x[1], x[2] and time.strftime('%c', time.localtime(x[2]))), data) 
     879        #print data 
     880        self.print_listing(['Name', 'Owner', 'Time'], data) 
    815881 
     882 
    816883    ## Version 
    817884    _help_version = [('version list', 'Show versions'), 
    818885                       ('version add <name> [time]', 'Add version'), 
     
    832899    def do_version(self, line): 
    833900        self._do_mile_ver('version', line) 
    834901 
    835     # Milestone and Version are identical,  methods 
     902    # Milestone and Version are almost identical,  methods 
    836903 
    837904    def _do_mile_ver(self, type, line): 
    838905        arg = self.arg_tokenize(line) 
     
    876943            raise Exception, "No such %s '%s'" % (type, name) 
    877944        data = self.db_execsql("UPDATE %(type)s SET name='%(newname)s'"  
    878945                               " WHERE name='%(name)s'" % d) 
     946        if self.__env.get_config('ticket', 'default_' + type) == name: 
     947            self.__env.set_config('ticket', 'default_' + type, newname) 
     948            self.__env.save_config() 
     949        data = self.db_execsql("UPDATE ticket SET %(type)s='%(newname)s'" 
     950                               " WHERE %(type)s='%(name)s'" % d) 
    879951 
    880952    def _do_mile_ver_add(self, type, name): 
    881953        sql = ("INSERT INTO %(type)s('name', 'time') " 
     
    891963            raise Exception, "No such %s '%s'" % (type, name) 
    892964        data = self.db_execsql("DELETE FROM %(type)s"  
    893965                               " WHERE name='%(name)s'" % d) 
     966        if self.__env.get_config('ticket', 'default_' + type) == name: 
     967            self.__env.set_config('ticket', 'default_' + type, '') 
     968            self.__env.save_config() 
     969        data = self.db_execsql("UPDATE ticket SET %(type)s=''" 
     970                               " WHERE %(type)s='%(name)s'" % d) 
    894971 
    895972    def _do_mile_ver_time(self, type, name, t): 
    896973        d = {'name':name, 'type':type} 
     
    9231000        else: 
    9241001            print >> sys.stderr, 'Unknown time format' 
    9251002 
     1003    def _do_mile_ver_chown(self, type, name, owner): 
     1004        data = self.db_execsql("UPDATE %s SET owner='%s' WHERE name='%s'" 
     1005                               % (type, owner, name)); 
     1006 
    9261007    _help_upgrade = [('upgrade', 'Upgrade database to current version.')] 
    9271008    def do_upgrade(self, line): 
    9281009        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 
     
    166167                                      'milestone %s' % (id, target)) 
    167168                    cursor.execute ('UPDATE ticket SET milestone = %s ' 
    168169                                    'WHERE milestone = %s', target, id) 
     170                    if self.env.get_config('ticket', 'default_milestone') == id: 
     171                        self.env.set_config('ticket', 'default_milestone', target) 
     172                        self.env.save_config() 
    169173                else: 
    170174                    self.env.log.info('Resetting milestone field of all ' 
    171175                                      'tickets associated with milestone %s' 
    172176                                      % id) 
    173177                    cursor.execute ('UPDATE ticket SET milestone = NULL ' 
    174178                                    'WHERE milestone = %s', id) 
     179                    if self.env.get_config('ticket', 'default_milestone') == id: 
     180                        self.env.set_config('ticket', 'default_milestone', '') 
     181                        self.env.save_config() 
    175182            self.env.log.debug('Deleting milestone %s' % id) 
    176183            cursor.execute("DELETE FROM milestone WHERE name = %s", id) 
    177184            self.db.commit() 
     
    179186        else: 
    180187            self.req.redirect(self.env.href.milestone(id)) 
    181188 
    182     def update_milestone(self, id, name, date, descr): 
     189    def update_milestone(self, id, name, date, descr, owner): 
    183190        self.perm.assert_permission(perm.MILESTONE_MODIFY) 
    184191        cursor = self.db.cursor() 
    185192        self.env.log.debug("Updating milestone '%s'" % id) 
     
    189196            cursor.execute('UPDATE ticket SET milestone = %s ' 
    190197                            'WHERE milestone = %s', name, id) 
    191198            cursor.execute("UPDATE milestone SET name = %s, time = %d, " 
    192                            "descr = %s WHERE name = %s", 
    193                            name, date, descr, id) 
     199                           "descr = %s, owner = %s WHERE name = %s", 
     200                           name, date, descr, owner, id) 
    194201            self.db.commit() 
     202            if self.env.get_config('ticket', 'default_milestone') == id: 
     203                self.env.set_config('ticket', 'default_milestone', name) 
     204                self.env.save_config() 
     205 
    195206            self.req.redirect(self.env.href.milestone(name)) 
    196207        else: 
    197208            self.req.redirect(self.env.href.milestone(id)) 
     
    223234 
    224235    def get_milestone(self, name): 
    225236        cursor = self.db.cursor() 
    226         cursor.execute("SELECT name, time, descr FROM milestone " 
     237        cursor.execute("SELECT name, time, descr, owner FROM milestone " 
    227238                       "WHERE name = %s ORDER BY time, name", name) 
    228239        row = cursor.fetchone() 
    229240        cursor.close() 
     
    238249        t = row['time'] and int(row['time']) 
    239250        if t > 0: 
    240251            milestone['date'] = time.strftime('%x', time.localtime(t)) 
     252        owner = row['owner'] 
     253        if not owner: owner = '' 
     254        milestone['owner'] = owner 
    241255        return milestone 
    242256 
    243257    def render(self): 
  • trac/Query.py

     
    111111            if check: 
    112112                del constraints[field] 
    113113 
    114         def add_db_options(field, constraints, prefix, cursor, sql): 
     114        def add_db_options(field, constraints, prefix, cursor, sql, withnull=0): 
    115115            cursor.execute(sql) 
    116116            options = [] 
     117            if withnull: options.append({'name': '(empty)'}) 
    117118            while 1: 
    118119                row = cursor.fetchone() 
    119120                if not row: break 
    120121                if row[0]: options.append({'name': row[0]}) 
    121122            add_options(field, constraints, prefix, options) 
    122123 
    123         add_options('status', constraints, 'query.options.', 
    124                     [{'name': 'new'}, {'name': 'assigned'}, 
    125                      {'name': 'reopened'}, {'name': 'closed'}]) 
    126         add_options('resolution', constraints, 'query.options.', 
    127                     [{'name': 'fixed'}, {'name': 'invalid'}, {'name': 'wontfix'}, 
    128                      {'name': 'duplicate'}, {'name': 'worksforme'}]) 
    129124        cursor = self.db.cursor() 
     125        if self.env.get_workflow_version() <= 1: 
     126            add_options('status', constraints, 'query.options.', 
     127                        [{'name': 'new'}, {'name': 'assigned'}, 
     128                         {'name': 'reopened'}, {'name': 'closed'}]) 
     129        else: 
     130            add_db_options('status', constraints, 'query.options.', cursor, 
     131                           'SELECT name FROM enum WHERE type=\'status\' ORDER BY value') 
     132        add_db_options('resolution', constraints, 'query.options.', cursor, 
     133                       'SELECT name FROM enum WHERE type=\'resolution\' ORDER BY value') 
    130134        add_db_options('component', constraints, 'query.options.', cursor, 
    131                        'SELECT name FROM component ORDER BY name', ) 
     135                       'SELECT name FROM component ORDER BY name', 1) 
    132136        add_db_options('milestone', constraints, 'query.options.', cursor, 
    133                        'SELECT name FROM milestone ORDER BY name') 
     137                       'SELECT name FROM milestone ORDER BY name', 1) 
    134138        add_db_options('version', constraints, 'query.options.', cursor, 
    135                        'SELECT name FROM version ORDER BY name') 
     139                       'SELECT name FROM version ORDER BY name', 1) 
    136140        add_db_options('priority', constraints, 'query.options.', cursor, 
    137                        'SELECT name FROM enum WHERE type=\'priority\'') 
     141                       'SELECT name FROM enum WHERE type=\'priority\' ORDER BY value') 
    138142        add_db_options('severity', constraints, 'query.options.', cursor, 
    139                        'SELECT name FROM enum WHERE type=\'severity\'') 
     143                       'SELECT name FROM enum WHERE type=\'severity\' ORDER BY value') 
    140144 
    141145        custom_fields = get_custom_fields(self.env) 
    142146        for custom in custom_fields: 
     
    147151                    options[i] = {'name': options[i]} 
    148152                    if check and (options[i]['name'] in constraints[custom['name']]): 
    149153                        options[i]['selected'] = 1 
     154                # FIXME: Don't know how to define "empty" queries for custom fields 
     155                #        Below line can be uncommented when these queries work 
     156                # options.insert(0, {'name': '(empty)'}) 
    150157                custom['options'] = options 
    151158        util.add_to_hdf(custom_fields, self.req.hdf, 'query.custom') 
    152159 
     
    195202            col = k 
    196203            if not col in Ticket.std_fields: 
    197204                col = 'value' 
     205            # Build clause array 
     206            if len(v) != 0 and v[0] == '(empty)': 
     207                # FIXME: Don't know how to define "empty" queries for custom fields 
     208                clause.append("IFNULL(%s, '') = ''" % col) 
     209                v.pop(0) 
    198210            if len(v) > 1: 
    199211                inlist = ["'" + util.sql_escape(item) + "'" for item in v] 
    200212                clause.append("%s IN (%s)" % (col, ", ".join(inlist))) 
    201             elif k in ['keywords', 'cc']: 
    202                 clause.append("%s LIKE '%%%s%%'" % (col, util.sql_escape(v[0]))) 
    203             else: 
    204                 clause.append("%s = '%s'" % (col, util.sql_escape(v[0]))) 
     213            elif len(v) != 0: 
     214                if k in ['keywords', 'cc']: 
     215                    clause.append("%s LIKE '%%%s%%'" % (col, util.sql_escape(v[0]))) 
     216                else: 
     217                    clause.append("%s = '%s'" % (col, util.sql_escape(v[0]))) 
     218            # Combine clause into clauses array 
    205219            if not k in Ticket.std_fields: 
    206220                clauses.append("(name='%s' AND (" % k + " OR ".join(clause) + "))") 
    207221            else: 
  • trac/Timeline.py

     
    5252        REOPENED_TICKET = 4 
    5353        WIKI = 5 
    5454        MILESTONE = 6 
    55  
     55        VERIFIED_TICKET = 7 
     56        RESOLVED_TICKET = 8 
     57         
    5658        q = [] 
    5759        if changeset: 
    5860            q.append("SELECT time, rev AS idata, '' AS tdata, 1 AS type, " 
     
    6971                     "FROM ticket_change WHERE field='status' " 
    7072                     "AND newvalue='reopened' AND time>=%s AND time<=%s" % 
    7173                     (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)) 
     74            if self.env.get_workflow_version() <= 1: 
     75                q.append("SELECT t1.time AS time, t1.ticket AS idata," 
     76                         "       t2.newvalue AS tdata, 3 AS type," 
     77                         "       t3.newvalue AS message, t1.author AS author" 
     78                         " FROM ticket_change t1" 
     79                         "   INNER JOIN ticket_change t2 ON t1.ticket = t2.ticket" 
     80                         "     AND t1.time = t2.time" 
     81                         "   LEFT OUTER JOIN ticket_change t3 ON t1.time = t3.time" 
     82                         "     AND t1.ticket = t3.ticket AND t3.field = 'comment'" 
     83                         " WHERE t1.field = 'status' AND t1.newvalue = 'closed'" 
     84                         "   AND t2.field = 'resolution'" 
     85                         "   AND t1.time >= %s AND t1.time <= %s" % (start,stop)) 
     86            else: 
     87                q.append("SELECT time, ticket AS idata, '' AS tdata, 3 AS type, " 
     88                         "'' AS message, author " 
     89                         "FROM ticket_change WHERE field='status' " 
     90                         "AND newvalue='closed' AND time>=%s AND time<=%s" % 
     91                         (start, stop)) 
     92                q.append("SELECT time, ticket AS idata, '' AS tdata, 7 AS type, " 
     93                         "'' AS message, author " 
     94                         "FROM ticket_change WHERE field='status' " 
     95                         "AND newvalue='verified' AND oldvalue<>'closed' AND time>=%s AND time<=%s" % 
     96                         (start, stop)) 
     97                q.append("SELECT t1.time AS time, t1.ticket AS idata," 
     98                         "       t2.newvalue AS tdata, 8 AS type," 
     99                         "       t3.newvalue AS message, t1.author AS author" 
     100                         " FROM ticket_change t1" 
     101                         "   INNER JOIN ticket_change t2 ON t1.ticket = t2.ticket" 
     102                         "     AND t1.time = t2.time" 
     103                         "   LEFT OUTER JOIN ticket_change t3 ON t1.time = t3.time" 
     104                         "     AND t1.ticket = t3.ticket AND t3.field = 'comment'" 
     105                         " WHERE t1.field = 'status' AND t1.newvalue = 'resolved' AND t1.oldvalue NOT IN ('verified', 'closed')" 
     106                         "   AND t2.field = 'resolution'" 
     107                         "   AND t1.time >= %s AND t1.time <= %s" % (start,stop)) 
    83108        if wiki: 
    84109            q.append("SELECT time, -1 AS idata, name AS tdata, 5 AS type, " 
    85110                     "comment AS message, author " 
     
    87112                     (start, stop)) 
    88113        if milestone: 
    89114            q.append("SELECT time, -1 AS idata, '' AS tdata, 6 AS type, " 
    90                      "name AS message, '' AS author "  
     115                     "name AS message, owner AS author "  
    91116                     "FROM milestone WHERE time>=%s AND time<=%s" % 
    92117                     (start, stop)) 
    93118 
  • trac/Report.py

     
    196196 
    197197        if copy: 
    198198            title += ' copy' 
    199         self.req.hdf.setValue('title', 'Create New Report') 
     199 
     200        if action == 'commit': 
     201            self.req.hdf.setValue('title', 'Edit {%s} %s (report)' % (id, row['title'])) 
     202        else: 
     203            self.req.hdf.setValue('title', 'Create New Report') 
    200204        self.req.hdf.setValue('report.mode', 'editor') 
    201205        self.req.hdf.setValue('report.title', title) 
    202206        self.req.hdf.setValue('report.id', str(id)) 
  • 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 time DESC, 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 time DESC, name" % time() 
     
    7474            milestone = { 
    7575                'name': row['name'], 
    7676                'href': self.env.href.milestone(row['name']), 
    77                 'time': row['time'] and int(row['time']) 
     77                'time': row['time'] and int(row['time']), 
    7878            } 
    7979            descr = row['descr'] 
    8080            if descr: 
    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.insert(0, milestone) 
     
    115118            status = ticket['status'] 
    116119            if status == 'new' or status == 'reopened' and not ticket['owner']: 
    117120                return 'NEEDS-ACTION' 
    118             elif status == 'assigned' or status == 'reopened': 
     121            elif status != 'closed': 
    119122                return 'IN-PROCESS' 
    120123            elif status == 'closed': 
    121124                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/Notify.py

     
    176176        NotifyEmail.__init__(self, env, self.template_name) 
    177177 
    178178    def notify(self, ticket, newticket=1, modtime=0): 
     179        enabled = self.env.get_config('notification', 'smtp_enabled', '0') 
     180        if not enabled.lower() in TRUE: 
     181            return 
     182 
    179183        self.ticket = ticket 
    180184        self.modtime = modtime 
    181185        self.newticket = newticket 
  • trac/Ticket.py

     
    3737class Ticket(UserDict): 
    3838    std_fields = ['time', 'component', 'severity', 'priority', 'milestone', 
    3939                  'reporter', 'owner', 'cc', 'url', 'version', 'status', 'resolution', 
    40                   'keywords', 'summary', 'description', 'reporter'] 
     40                  'keywords', 'summary', 'description'] 
    4141 
    4242    def __init__(self, *args): 
    4343        UserDict.__init__(self) 
     
    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 
     
    374436                        self.req.hdf, 'ticket.milestones') 
    375437        util.sql_to_hdf(self.db, 'SELECT name FROM version ORDER BY name', 
    376438                        self.req.hdf, 'ticket.versions') 
     439        util.sql_to_hdf(self.db, "SELECT name FROM enum WHERE type='resolution' ORDER BY value", 
     440                        self.req.hdf, 'enums.resolution') 
    377441        util.hdf_add_if_missing(self.req.hdf, 'ticket.components', ticket['component']) 
    378442        util.hdf_add_if_missing(self.req.hdf, 'ticket.milestones', ticket['milestone']) 
    379443        util.hdf_add_if_missing(self.req.hdf, 'ticket.versions', ticket['version']) 
    380444        util.hdf_add_if_missing(self.req.hdf, 'enums.priority', ticket['priority']) 
    381445        util.hdf_add_if_missing(self.req.hdf, 'enums.severity', ticket['severity']) 
     446        util.hdf_add_if_missing(self.req.hdf, 'enums.resolution', 'fixed') 
    382447 
    383448        self.req.hdf.setValue('ticket.reporter_id', util.escape(reporter_id)) 
    384449        self.req.hdf.setValue('title', '#%d (%s)' % (id,ticket['summary'])) 
     450        self.req.hdf.setValue('workflow_version', str(self.env.get_workflow_version())) 
    385451        self.req.hdf.setValue('ticket.description.formatted', 
    386452                              wiki_to_html(ticket['description'], self.req.hdf, 
    387453                                           self.env, self.db)) 
     
    423489        id = int(self.args.get('id')) 
    424490 
    425491        if not preview \ 
    426                and action in ['leave', 'accept', 'reopen', 'resolve', 'reassign']: 
     492               and (action in ['leave', 'accept', 'reopen', 'resolve', 'reassign'] \ 
     493                    or self.env.get_workflow_version() >= 2 \ 
     494                       and action in ['verify2', 'close2', 'retest2']): 
    427495            self.save_changes (id) 
    428496 
    429497        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/report.cs

     
    175175 
    176176 <?cs elif report.mode == "delete" ?> 
    177177 
    178   <h1 id="report-hdr">Delete Report</h1>  
     178  <h1 id="report-hdr">Delete Report {<?cs var:report.id ?>}: <?cs var:report.title ?></h1>  
    179179  <form action="<?cs var:cgi_location ?>" method="post"> 
    180180   <input type="hidden" name="mode" value="report" /> 
    181181  <input type="hidden" name="id" value="<?cs var:report.id ?>" /> 
  • templates/ticket.cs

       <input type="hidden" name="action" value="confirm_delete" />
        <p><strong>Are you sure you want to delete this report?</strong></p>
    @@ -187,7 +187,11 @@
      
      <?cs elif report.mode == "editor" ?>
      
    -   <h1 id="report-hdr">Create New Report</h1>
    +   <h1 id="report-hdr">
    +     <?cs if report.action == "commit" ?>Edit Report {<?cs var:report.id ?>}: <?cs var:report.title ?>
    +     <?cs else ?>Create New Report
    +     <?cs /if ?>
    +   </h1>
        
        <form action="<?cs var:cgi_location ?>" method="post">
          <div>
     
    2222<div id="ticket"> 
    2323 <div class="date"><?cs var:ticket.opened ?></div> 
    2424 <h1>Ticket #<?cs var:ticket.id ?> <?cs 
    25  if:ticket.status == 'closed' ?>(Closed: <?cs var:ticket.resolution ?>)<?cs 
     25 if:ticket.status == 'closed' || ticket.status == 'verified' || ticket.status == 'resolved' ?> 
     26  (<?cs var:ticket.status ?>: <?cs var:ticket.resolution ?>)<?cs 
    2627 elif:ticket.status != 'new' ?>(<?cs var:ticket.status ?>)<?cs 
    2728 /if ?></h1> 
    2829 <h2><?cs var:ticket.summary ?></h2> 
     
    208209  /def ?> 
    209210  <?cs call:action_radio('leave') ?> 
    210211  <label for="leave">leave as <?cs var:ticket.status ?></label><br /><?cs 
    211   if $ticket.status == "new" ?> 
     212  if $ticket.status == "new" || $workflow_version == "2" && $ticket.status == "reopened" ?> 
    212213   <?cs call:action_radio('accept') ?> 
    213214   <label for="accept">accept ticket</label><br /><?cs 
    214215  /if ?><?cs 
    215   if $ticket.status == "closed" ?> 
    216    <?cs call:action_radio('reopen') ?> 
    217    <label for="reopen">reopen ticket</label><br /><?cs 
    218   /if ?><?cs 
    219216  if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
    220217   <?cs call:action_radio('resolve') ?> 
    221218   <label for="resolve">resolve</label> 
    222219   <label for="resolve_resolution">as:</label> 
    223    <select id="resolve_resolution" name="resolve_resolution"> 
    224     <option<?cs 
    225      if:args.resolve_resolution == 'fixed' ?> selected="selected"<?cs 
    226      /if ?>>fixed</option> 
    227     <option<?cs 
    228      if:args.resolve_resolution == 'invalid' ?> selected="selected"<?cs 
    229      /if ?>>invalid</option> 
    230     <option<?cs 
    231      if:args.resolve_resolution == 'wontfix' ?> selected="selected"<?cs 
    232      /if ?>>wontfix</option> 
    233     <option<?cs 
    234      if:args.resolve_resolution == 'duplicate' ?> selected="selected"<?cs 
    235      /if ?>>duplicate</option> 
    236     <option<?cs 
    237      if:args.resolve_resolution == 'worksforme' ?> selected="selected"<?cs 
    238      /if ?>>worksforme</option> 
    239    </select><br /> 
     220   <?cs call:hdf_select(enums.resolution, "resolve_resolution", args.resolve_resolution) ?><br /><?cs 
     221  /if ?> 
     222 
     223  <?cs if $workflow_version == "2" && $ticket.status == "resolved" ?> 
     224   <?cs call:action_radio('verify2') ?> 
     225   <label for="verify2">verify ticket</label><br /><?cs 
     226  /if ?> 
     227 
     228  <?cs if $workflow_version == "2" && ($ticket.status == "resolved" || $ticket.status == "verified") ?> 
     229   <?cs call:action_radio('close2') ?> 
     230   <label for="close2">close ticket</label><br /><?cs 
     231  /if ?> 
     232 
     233  <?cs if $ticket.status == "closed" || $workflow_version == "2" && ($ticket.status == "verified" || $ticket.status == "resolved") ?> 
     234   <?cs call:action_radio('reopen') ?> 
     235   <label for="reopen">reopen ticket</label><br /><?cs 
     236  /if ?> 
     237 
     238  <?cs if $workflow_version == "2" && ($ticket.status == "verified" || $ticket.status == "closed") ?> 
     239   <?cs call:action_radio('retest2') ?> 
     240   <label for="retest2">retest ticket</label><br /><?cs 
     241  /if ?> 
     242 
     243  <?cs 
     244  if $ticket.status != "closed" ?> 
    240245   <?cs call:action_radio('reassign') ?> 
    241246   <label for="reassign">reassign</label> 
    242247   <label for="reassign_owner">to:</label> 
     
    244249     if:args.reassign_to ?><?cs var:args.reassign_to ?><?cs 
    245250     else ?><?cs var:trac.authname ?><?cs /if ?>" /><?cs 
    246251  /if ?><?cs 
    247   if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
     252  if $ticket.status != "closed" ?> 
    248253   <script type="text/javascript"> 
    249      var resolve = document.getElementById("resolve"); 
     254     <?cs if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
     255       var resolve = document.getElementById("resolve");<?cs /if ?> 
    250256     var reassign = document.getElementById("reassign"); 
    251257     var updateActionFields = function() { 
    252        enableControl('resolve_resolution', resolve.checked); 
     258       <?cs if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
     259         enableControl('resolve_resolution', resolve.checked);<?cs /if ?> 
    253260       enableControl('reassign_owner', reassign.checked); 
    254261     }; 
    255262     addEvent(window, 'load', updateActionFields); 
    256      addEvent(document.getElementById("leave"), 'click', updateActionFields);<?cs 
    257     if $ticket.status == "new" ?> 
    258      addEvent(document.getElementById("accept"), 'click', updateActionFields);<?cs 
    259     /if ?> 
    260     addEvent(resolve, 'click', updateActionFields); 
    261     addEvent(reassign, 'click', updateActionFields); 
     263     addEvent(document.getElementById("leave"), 'click', updateActionFields); 
     264     <?cs if $ticket.status == "new" || $workflow_version == "2" && $ticket.status == "reopened" ?> 
     265       addEvent(document.getElementById("accept"), 'click', updateActionFields);<?cs /if ?> 
     266     <?cs if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
     267       addEvent(resolve, 'click', updateActionFields);<?cs /if ?> 
     268     <?cs if $workflow_version == "2" && $ticket.status == "resolved" ?> 
     269       addEvent(document.getElementById("verify2"), 'click', updateActionFields);<?cs /if ?> 
     270     <?cs if $workflow_version == "2" && ($ticket.status == "resolved" || $ticket.status == "verified") ?> 
     271       addEvent(document.getElementById("close2"), 'click', updateActionFields);<?cs /if ?> 
     272     <?cs if $ticket.status == "closed" || $workflow_version == "2" && ($ticket.status == "verified" || $ticket.status == "resolved") ?> 
     273       addEvent(document.getElementById("reopen"), 'click', updateActionFields);<?cs /if ?> 
     274     <?cs if $workflow_version == "2" && ($ticket.status == "verified" || $ticket.status == "closed") ?> 
     275       addEvent(document.getElementById("retest2"), 'click', updateActionFields);<?cs /if ?> 
     276     addEvent(reassign, 'click', updateActionFields); 
    262277   </script><?cs 
    263278  /if ?> 
    264279 </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 ?> 
     
    3334       <dt>Active tickets:</dt> 
    3435       <dd><a href="<?cs var:milestone.queries.active_tickets ?>"><?cs 
    3536         var:stats.active_tickets ?></a></dd> 
    36        <dt>Resolved tickets:</dt> 
     37       <dt>Completed tickets:</dt> 
    3738       <dd><a href="<?cs var:milestone.queries.closed_tickets ?>"><?cs 
    3839         var:stats.closed_tickets ?></a></dd> 
    3940      </dl> 
  • 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)  
    5260        ?><?cs elif:item.type == #5  
    5361        ?><!-- Wiki change --><?cs call:rss_item('Wiki', 
    5462                             $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> 
    126    <th class="closed" scope="col">Resolved</th> 
     132   <th class="closed" scope="col">Completed</th> 
    127133  </tr></thead> 
    128134  <?cs if:len(milestone.stats.groups) ?><tbody> 
    129135   <?cs each:group = milestone.stats.groups ?> 
  • templates/query.cs

     
    4848   <script type="text/javascript"> 
    4949     var status = document.getElementById("status"); 
    5050     var updateResolution = function() { 
    51        enableControl('resolution', status.selectedIndex == -1 || 
    52                                    status.options[3].selected); 
     51       var bEnable = true 
     52       if (status.selectedIndex != -1) { 
     53         for (i=0; i < status.options.length; ++i) { 
     54           if (status.options[i].selected && 
     55               (status.options[i].text == 'new'   || 
     56                status.options[i].text == 'assigned' || 
     57                status.options[i].text == 'reopened')) { 
     58             bEnable = false 
     59             break 
     60           } 
     61         } 
     62       } 
     63       enableControl('resolution', bEnable); 
    5364     }; 
    5465     addEvent(window, 'load', updateResolution); 
    5566     addEvent(status, 'change', updateResolution); 
  • templates/timeline.cs

     
    7878   <?cs set:imessage = '' ?> 
    7979  <?cs /if ?> 
    8080  <?cs call:tlitem(item.href, 'closedticket', 
    81     'Ticket <em>#'+$item.idata+'</em> resolved by '+$item.author,  
     81    'Ticket <em>#'+$item.idata+'</em> closed by '+$item.author,  
    8282    $item.tdata+$imessage) ?> 
    8383 <?cs elif:item.type == #4 ?><!-- Reopened ticket --> 
    8484  <?cs call:tlitem(item.href, 'newticket', 
    8585    'Ticket <em>#'+$item.idata+'</em> reopened by '+$item.author, '') ?> 
     86 <?cs elif:item.type == #7 ?><!-- Verified ticket --> 
     87  <?cs call:tlitem(item.href, 'closedticket', 
     88    'Ticket <em>#'+$item.idata+'</em> verified by '+$item.author, '') ?> 
     89 <?cs elif:item.type == #8 ?><!-- Resolved ticket --> 
     90  <?cs if:item.message ?> 
     91   <?cs set:imessage = ' - ' + $item.message ?> 
     92  <?cs else ?> 
     93   <?cs set:imessage = '' ?> 
     94  <?cs /if ?> 
     95  <?cs call:tlitem(item.href, 'resolvedticket', 
     96    'Ticket <em>#'+$item.idata+'</em> resolved by '+$item.author,  
     97    $item.tdata+$imessage) ?> 
    8698 <?cs elif:item.type == #5 ?><!-- Wiki change --> 
    8799  <?cs call:tlitem(item.href, 'wiki', 
    88100    '<em>'+$item.tdata+'</em> edited by '+$item.author, item.message) ?>