Edgewall Software

TracReleaselist: releaselist.patch

File releaselist.patch, 32.7 KB (added by jamesm@…, 8 years ago)

Patch which implements basic release management

  • htdocs/css/releaselist.css

     
     1/* Styles for the releaselist view */ 
     2ul.releases { margin: 2em 0 0; padding: 0 } 
     3li.releases{ list-style: none; margin-bottom: 4em } 
     4li.release .info { white-space: nowrap } 
     5li.release .info h2 { 
     6 background: #f7f7f7; 
     7 border-bottom: 1px solid #d7d7d7; 
     8 margin: 0; 
     9} 
     10li.release .info h2 :link, li.release .info h2 :visited { 
     11 color: #000; 
     12 display: block; 
     13 border-bottom: none; 
     14} 
     15li.release .info h2 :link:hover, li.release .info h2 :visited:hover { 
     16 color: #000; 
     17} 
     18li.release .info h2 em { color: #b00; font-style: normal } 
     19li.release .info .date { 
     20 color: #888; 
     21 font-size: 11px; 
     22 font-style: italic; 
     23 margin: 0; 
     24} 
     25li.release .info .progress { margin: 1em 1em 0; width: 40em; max-width: 80% } 
     26li.release .info dl { 
     27 font-size: 10px; 
     28 font-style: italic; 
     29 margin: 0 1em 2em; 
     30 white-space: nowrap; 
     31} 
     32li.release .info dt { display: inline; margin-left: .5em } 
     33li.release .info dd { display: inline; margin: 0 1em 0 .5em } 
     34li.release .descr { margin-left: 1em } 
     35 
     36/* Styles for the milestone view */ 
     37.release .date { color: #888; font-style: italic } 
     38.release .descr { margin: 1em 0 2em } 
     39 
     40/* Release view preferences */ 
     41#prefs fieldset { margin: 1em .5em .5em; padding: .5em 1em 0 } 
     42 
     43 
     44/* Styles for the release edit form */ 
     45#edit fieldset { margin: 1em 0 } 
     46#edit em { color: #888; font-size: smaller } 
     47#edit .disabled em { color: #d7d7d7 } 
     48#edit .field { margin-top: 1.3em } 
     49#edit label { padding-left: .2em } 
     50#edit textarea#descr { width: 97% } 
  • htdocs/css/release.css

     
     1@import url(diff.css); 
     2 
     3/* Release overview */ 
     4#overview .files { padding-top: 1em } 
     5#overview .files ul { margin: 0; padding: 0 } 
     6#overview .files li { list-style-type: none } 
     7#overview .files li .comment { display: none } 
     8#overview .files li div { 
     9 border: 1px solid #999; 
     10 float: left; 
     11 margin: .2em .5em 0 0; 
     12 overflow: hidden; 
     13 width: .8em; height: .8em; 
     14} 
     15#overview .message { padding: 1em 0 1px } 
     16#overview dd.message p, #overview dd.message ul, #overview dd.message ol { 
     17 margin-bottom: 1em; 
     18 margin-top: 0; 
     19} 
     20#overview .files { padding: 1px 0 } 
  • htdocs/css/timeline.css

     
    3838dt.closedticket, dt.closedticket a { background-image: url(../closedticket.png) !important } 
    3939dt.wiki, dt.wiki a { background-image: url(../wiki.png) !important } 
    4040dt.milestone, dt.milestone a { background-image: url(../milestone.png) !important } 
     41dt.release, dt.release a { background-image: url(../release.png) !important } 
    4142 
    4243.diff-unmod { color: #000 } 
    4344.diff-rem { color: #e00 } 
  • trac/core.py

    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
    
    Property changes on: htdocs/release.png
    ___________________________________________________________________
    Name: svn:mime-type
       + application/octet-stream
    
     
    5959    'attachment'  : ('File', 'Attachment', 0), 
    6060    'roadmap'     : ('Roadmap', 'Roadmap', 0), 
    6161    'settings'    : ('Settings', 'Settings', 0), 
    62     'milestone'   : ('Milestone', 'Milestone', 0) 
     62    'milestone'   : ('Milestone', 'Milestone', 0), 
     63    'releaselist' : ('Releaselist', 'Releaselist', 0), 
     64    'release'     : ('Release', 'Release', 0) 
    6365    } 
    6466 
    6567class TracFieldStorage(cgi.FieldStorage): 
     
    9395        if match.group(2): 
    9496            set_if_missing(args, 'page', match.group(2)) 
    9597        return args 
    96     match = re.search('^/(newticket|timeline|search|roadmap|settings|query)/?', path_info) 
     98    match = re.search('^/(newticket|timeline|releaselist|search|roadmap|settings|query)/?', path_info) 
    9799    if match: 
    98100        set_if_missing(args, 'mode', match.group(1)) 
    99101        return args 
     
    127129        if match.group(1): 
    128130            set_if_missing(args, 'id', urllib.unquote_plus(match.group(1))) 
    129131        return args 
     132    match = re.search('^/release(?:/([^\?]+))?(?:/(.*)/?)?', path_info) 
     133    if match: 
     134        set_if_missing(args, 'mode', 'release') 
     135        if match.group(1): 
     136            set_if_missing(args, 'rev', urllib.unquote_plus(match.group(1))) 
     137        return args 
    130138    return args 
    131139 
    132140def parse_args(command, path_info, query_string, 
     
    221229    hdf.setValue('trac.href.browser', env.href.browser('/')) 
    222230    hdf.setValue('trac.href.timeline', env.href.timeline()) 
    223231    hdf.setValue('trac.href.roadmap', env.href.roadmap()) 
     232    hdf.setValue('trac.href.releaselist', env.href.releaselist()) 
    224233    hdf.setValue('trac.href.report', env.href.report()) 
    225234    hdf.setValue('trac.href.query', env.href.query()) 
    226235    hdf.setValue('trac.href.newticket', env.href.newticket()) 
  • trac/db_default.py

     
    2121 
    2222 
    2323# Database version identifier. Used for automatic upgrades. 
    24 db_version = 7 
     24db_version = 8 
    2525 
    2626def __mkreports(reps): 
    2727    """Utility function used to create report data in same syntax as the 
     
    4040## 
    4141 
    4242schema = """ 
     43CREATE TABLE release ( 
     44        rev             integer PRIMARY KEY, 
     45        name            text, 
     46        changesetby     text, 
     47        modifiedby      text, 
     48        time            integer, 
     49        timemodified    integer, 
     50        descr           text         
     51); 
    4352CREATE TABLE revision ( 
    4453        rev             integer PRIMARY KEY, 
    4554        time            integer, 
     
    401410                ('anonymous', 'BROWSER_VIEW'), 
    402411                ('anonymous', 'TIMELINE_VIEW'), 
    403412                ('anonymous', 'CHANGESET_VIEW'), 
     413                ('anonymous', 'RELEASELIST_VIEW'), 
     414                ('anonymous', 'RELEASE_VIEW'), 
    404415                ('anonymous', 'ROADMAP_VIEW'), 
    405416                ('anonymous', 'MILESTONE_VIEW'))), 
    406417           ('system', 
  • trac/perm.py

     
    3030CHANGESET_VIEW = 'CHANGESET_VIEW' 
    3131BROWSER_VIEW   = 'BROWSER_VIEW' 
    3232ROADMAP_VIEW   = 'ROADMAP_VIEW' 
     33RELEASELIST_VIEW   = 'RELEASELIST_VIEW' 
    3334 
    3435TICKET_VIEW    = 'TICKET_VIEW' 
    3536TICKET_CREATE  = 'TICKET_CREATE' 
     
    5152MILESTONE_MODIFY = 'MILESTONE_MODIFY' 
    5253MILESTONE_DELETE = 'MILESTONE_DELETE' 
    5354 
     55RELEASE_VIEW = 'RELEASE_VIEW' 
     56RELEASE_CREATE = 'RELEASE_CREATE' 
     57RELEASE_MODIFY = 'RELEASE_MODIFY' 
     58RELEASE_DELETE = 'RELEASE_DELETE' 
     59 
    5460AUTHZSVN_VIEW = 'AUTHZSVN_VIEW' 
    5561AUTHZSVN_MODIFY = 'AUTHZSVN_MODIFY' 
    5662 
     
    5965REPORT_ADMIN = 'REPORT_ADMIN' 
    6066WIKI_ADMIN = 'WIKI_ADMIN' 
    6167ROADMAP_ADMIN = 'ROADMAP_ADMIN' 
     68RELEASE_ADMIN = 'RELEASE_ADMIN' 
    6269AUTHZSVN_ADMIN = 'AUTHZSVN_ADMIN' 
    6370 
    6471meta_permission = { 
    65     TRAC_ADMIN: [TICKET_ADMIN, REPORT_ADMIN, WIKI_ADMIN, ROADMAP_ADMIN, 
     72    TRAC_ADMIN: [TICKET_ADMIN, REPORT_ADMIN, WIKI_ADMIN, RELEASE_ADMIN, ROADMAP_ADMIN, 
    6673                 TIMELINE_VIEW, SEARCH_VIEW, CONFIG_VIEW, LOG_VIEW, 
    6774                 FILE_VIEW, CHANGESET_VIEW, BROWSER_VIEW], 
    6875    TICKET_ADMIN: [TICKET_VIEW, TICKET_CREATE, TICKET_MODIFY], 
    6976    REPORT_ADMIN: [REPORT_VIEW, REPORT_SQL_VIEW, REPORT_CREATE, REPORT_MODIFY, 
    7077                   REPORT_DELETE], 
    7178    WIKI_ADMIN: [WIKI_VIEW, WIKI_CREATE, WIKI_MODIFY, WIKI_DELETE], 
     79    RELEASE_ADMIN: [RELEASELIST_VIEW, RELEASE_VIEW, RELEASE_CREATE, RELEASE_MODIFY, RELEASE_DELETE], 
    7280    ROADMAP_ADMIN: [ROADMAP_VIEW, MILESTONE_VIEW, MILESTONE_CREATE, 
    7381                    MILESTONE_MODIFY, MILESTONE_DELETE], 
    7482    AUTHZSVN_ADMIN: [AUTHZSVN_VIEW, AUTHZSVN_MODIFY]} 
    7583 
    76  
    7784class PermissionError (StandardError): 
    7885    """Insufficient permissions to complete the operation""" 
    7986    def __init__ (self, action): 
  • trac/Timeline.py

     
    3535    template_rss_name = 'timeline_rss.cs' 
    3636 
    3737    def get_info (self, start, stop, maxrows, tickets, 
    38                   changeset, wiki, milestone): 
     38                  changeset, wiki, milestone, release): 
    3939        cursor = self.db.cursor () 
    4040 
    4141        tickets = tickets and self.perm.has_permission(perm.TICKET_VIEW) 
    4242        changeset = changeset and self.perm.has_permission(perm.CHANGESET_VIEW) 
    4343        wiki = wiki and self.perm.has_permission(perm.WIKI_VIEW) 
    4444        milestone = milestone and self.perm.has_permission(perm.MILESTONE_VIEW) 
     45        release = release and self.perm.has_permission(perm.RELEASE_VIEW) 
    4546 
    46         if tickets == changeset == wiki == milestone == 0: 
     47        if tickets == changeset == wiki == milestone == release == 0: 
    4748            return [] 
    4849 
    4950        CHANGESET = 1 
     
    5253        REOPENED_TICKET = 4 
    5354        WIKI = 5 
    5455        MILESTONE = 6 
     56        RELEASE = 7 
    5557 
    5658        q = [] 
    5759        if changeset: 
     
    9092                     "name AS message, '' AS author "  
    9193                     "FROM milestone WHERE time>=%s AND time<=%s" % 
    9294                     (start, stop)) 
     95        if release: 
     96            q.append("SELECT time, rev AS idata, '' AS tdata, 7 AS type, " 
     97                     " name AS message, '' AS author " 
     98                     "FROM release WHERE time>=%s AND time<=%s" % 
     99                     (start, stop)) 
    93100 
    94101        q_str = string.join(q, ' UNION ALL ') 
    95102        q_str += ' ORDER BY time DESC' 
     
    168175            elif item['type'] == MILESTONE: 
    169176                item['href'] = self.env.href.milestone(item['message']) 
    170177                item['message'] = util.escape(item['message']) 
     178            elif item['type'] == RELEASE: 
     179                item['href'] = self.env.href.release(item['idata']) 
     180                item['message'] = util.escape(item['message']) 
    171181            else:               # TICKET 
    172182                item['href'] = self.env.href.ticket(item['idata']) 
    173183                msg = item['message'] 
     
    223233        ticket = self.args.has_key('ticket') 
    224234        changeset = self.args.has_key('changeset') 
    225235        milestone = self.args.has_key('milestone') 
    226         if not (wiki or ticket or changeset or milestone): 
    227             wiki = ticket = changeset = milestone = 1 
     236        release = self.args.has_key('release') 
     237        if not (wiki or ticket or changeset or milestone or release): 
     238            wiki = ticket = changeset = milestone = release = 1 
    228239 
    229240        if wiki: 
    230241            self.req.hdf.setValue('timeline.wiki', 'checked') 
     
    234245            self.req.hdf.setValue('timeline.changeset', 'checked') 
    235246        if milestone: 
    236247            self.req.hdf.setValue('timeline.milestone', 'checked') 
     248        if release: 
     249            self.req.hdf.setValue('timeline.release', 'checked') 
    237250 
    238251        info = self.get_info (start, stop, maxrows, ticket, 
    239                               changeset, wiki, milestone) 
     252                              changeset, wiki, milestone, release) 
    240253        util.add_dictlist_to_hdf(info, self.req.hdf, 'timeline.items') 
    241254        self.req.hdf.setValue('title', 'Timeline') 
    242255 
  • trac/Release.py

     
     1# -*- coding: iso8859-1 -*- 
     2# 
     3# Copyright (C) 2003, 2004 Edgewall Software 
     4# Copyright (C) 2003, 2004 Jonas Borgström <jonas@edgewall.com> 
     5# 
     6# Trac is free software; you can redistribute it and/or 
     7# modify it under the terms of the GNU General Public License as 
     8# published by the Free Software Foundation; either version 2 of the 
     9# License, or (at your option) any later version. 
     10# 
     11# Trac is distributed in the hope that it will be useful, 
     12# but WITHOUT ANY WARRANTY; without even the implied warranty of 
     13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
     14# General Public License for more details. 
     15# 
     16# You should have received a copy of the GNU General Public License 
     17# along with this program; if not, write to the Free Software 
     18# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
     19# 
     20# Author: James Moger <james.moger@transonic.com> 
     21# Based on milestone.py by: 
     22#          Christopher Lenz <cmlenz@gmx.de> 
     23 
     24import time 
     25 
     26from Module import Module 
     27from util import add_to_hdf, TracError 
     28from WikiFormatter import wiki_to_html 
     29import perm 
     30 
     31class Release(Module): 
     32    template_name = 'release.cs' 
     33 
     34    def save_release(self, rev): 
     35        self.perm.assert_permission(perm.RELEASE_MODIFY) 
     36        if self.args.has_key('save'): 
     37            name = self.args.get('name', '') 
     38            if not name: 
     39                raise TracError('You must provide a name for the release.', 
     40                                'Required Field Missing') 
     41            cursor = self.db.cursor() 
     42            cursor.execute("SELECT time, author FROM revision " 
     43                "WHERE rev = %d", rev) 
     44            row = cursor.fetchone() 
     45             
     46            date = row['time'] + 1 
     47            descr = self.args.get('descr', '') 
     48            changesetby = row['author'] 
     49            modifiedby = self.req.authname 
     50 
     51            cursor.execute("SELECT name, time, descr FROM release " 
     52                "WHERE rev = %d ORDER BY time, name", rev) 
     53            row = cursor.fetchone() 
     54            cursor.close() 
     55            if not row: 
     56                self.create_release(rev, name, changesetby, modifiedby, date, descr) 
     57            else: 
     58                self.update_release(rev, name, modifiedby, descr) 
     59        elif rev: 
     60            self.req.redirect(self.env.href.release(rev)) 
     61        else: 
     62            self.req.redirect(self.env.href.releaselist()) 
     63 
     64    def create_release(self, rev, name, changesetby, modifiedby, date=0, descr=''): 
     65        self.perm.assert_permission(perm.RELEASE_CREATE) 
     66        if not name: 
     67                name = 'unknown @ %d', rev 
     68        when = int(time.time()) 
     69         
     70        cursor = self.db.cursor() 
     71        self.log.debug("Creating new release '%s'" % name) 
     72        cursor.execute("INSERT INTO release (rev, name, changesetby, modifiedby, time, timemodified, descr) " 
     73                       "VALUES (%d, %s, %s, %s, %d, %d, %s)", rev, name, changesetby, modifiedby, date, when, descr) 
     74        self.db.commit() 
     75        self.req.redirect(self.env.href.release(rev)) 
     76 
     77    def delete_release(self, rev): 
     78        self.perm.assert_permission(perm.RELEASE_DELETE) 
     79        release = self.get_release(rev) 
     80        if self.args.has_key('delete'): 
     81            cursor = self.db.cursor() 
     82            self.log.info('Deleting release %d' % rev) 
     83            cursor.execute("DELETE FROM release WHERE rev = %d", rev) 
     84            self.db.commit() 
     85            self.req.redirect(self.env.href.releaselist()) 
     86        else: 
     87            self.req.redirect(self.env.href.release(rev)) 
     88 
     89    def update_release(self, rev, name, modifiedby, descr): 
     90        self.perm.assert_permission(perm.RELEASE_MODIFY) 
     91        cursor = self.db.cursor() 
     92        self.log.info("Updating release '%d'" % rev) 
     93         
     94        when = int(time.time()) 
     95         
     96        if self.args.has_key('save'): 
     97            cursor.execute("UPDATE release SET name = %s, timemodified = %d," 
     98                           "descr = %s, modifiedby = %s WHERE rev = %d", 
     99                           name, when, descr, modifiedby, rev) 
     100            self.db.commit() 
     101            self.req.redirect(self.env.href.release(rev)) 
     102        else: 
     103            self.req.redirect(self.env.href.release(rev)) 
     104 
     105 
     106    def get_release(self, rev): 
     107        cursor = self.db.cursor() 
     108        cursor.execute("SELECT name, time, timemodified, changesetby, modifiedby, descr FROM release " 
     109                       "WHERE rev = %d ORDER BY time, name", rev) 
     110        row = cursor.fetchone() 
     111        cursor.close() 
     112        if not row: 
     113            raise TracError('Release %d does not exist.' % rev, 
     114                            'Invalid Release Number') 
     115        release = { 'name': row['name'] } 
     116        self.req.hdf.setValue('release.revision', str(self.rev)) 
     117        if self.perm.has_permission(perm.CHANGESET_VIEW): 
     118            self.req.hdf.setValue('release.href.changeset', 
     119                self.env.href.changeset(rev)) 
     120            self.req.hdf.setValue('release.changesetby', row['changesetby']) 
     121        self.req.hdf.setValue('release.modifiedby', row['modifiedby']) 
     122        t = row['timemodified'] and int(row['timemodified']) 
     123        if t > 0: 
     124            self.req.hdf.setValue('release.timemodified', time.strftime('%x %X', time.localtime(t))) 
     125        descr = row['descr'] 
     126        if descr: 
     127            release['descr_source'] = descr 
     128            release['descr'] = wiki_to_html(descr, self.req.hdf, self.env, self.db) 
     129        t = row['time'] and int(row['time']) 
     130        if t > 0: 
     131            release['date'] = time.strftime('%x %X', time.localtime(t)) 
     132        return release 
     133 
     134    def render(self): 
     135        self.perm.assert_permission(perm.RELEASE_VIEW) 
     136 
     137        self.add_link('up', self.env.href.releaselist(), 'Releases') 
     138 
     139        action = self.args.get('action', 'view') 
     140        if self.args.has_key('rev'): 
     141            self.rev = int(self.args.get('rev')) 
     142        else: 
     143            self.req.redirect(self.env.href.releaselist()) 
     144 
     145        if action == 'new': 
     146            self.perm.assert_permission(perm.RELEASE_CREATE) 
     147            self.render_neweditor(self.rev) 
     148        elif action == 'edit': 
     149            self.perm.assert_permission(perm.RELEASE_MODIFY) 
     150            self.render_editor(self.rev) 
     151        elif action == 'delete': 
     152            self.perm.assert_permission(perm.RELEASE_DELETE) 
     153            self.render_confirm(self.rev) 
     154        elif action == 'commit_changes': 
     155            self.save_release(self.rev) 
     156        elif action == 'confirm_delete': 
     157            self.delete_release(self.rev) 
     158        else: 
     159            self.render_view(self.rev) 
     160 
     161    def render_confirm(self, rev): 
     162        release = self.get_release(rev) 
     163        self.req.hdf.setValue('title', 'Release %s' % release['name']) 
     164        self.req.hdf.setValue('release.mode', 'delete')         
     165        add_to_hdf(release, self.req.hdf, 'release') 
     166 
     167        cursor = self.db.cursor() 
     168        cursor.execute("SELECT name FROM release " 
     169                       "WHERE name != '' ORDER BY name") 
     170        releases = [] 
     171        release_no = 0 
     172        while 1: 
     173            row = cursor.fetchone() 
     174            if not row: 
     175                break 
     176            self.req.hdf.setValue('releases.%d' % release_no, row['name']) 
     177            release_no += 1 
     178        cursor.close() 
     179 
     180    def render_editor(self, rev): 
     181        release = self.get_release(rev) 
     182        self.req.hdf.setValue('title', 'Release %s' % release['name']) 
     183        self.req.hdf.setValue('release.mode', 'edit') 
     184        add_to_hdf(release, self.req.hdf, 'release') 
     185 
     186    def render_neweditor(self, rev): 
     187        release = { 'revision': rev, 'name': '', 'descr': '' } 
     188        self.req.hdf.setValue('title', 'New Release') 
     189        self.req.hdf.setValue('release.mode', 'new') 
     190        add_to_hdf(release, self.req.hdf, 'release') 
     191         
     192    def render_view(self, rev): 
     193        if self.perm.has_permission(perm.RELEASE_DELETE): 
     194            self.req.hdf.setValue('release.href.delete', 
     195                                   self.env.href.release(rev, 'delete')) 
     196        if self.perm.has_permission(perm.RELEASE_MODIFY): 
     197            self.req.hdf.setValue('release.href.edit', 
     198                                   self.env.href.release(rev, 'edit')) 
     199 
     200        release = self.get_release(rev) 
     201        self.req.hdf.setValue('title', 'Release %s' % release['name']) 
     202        self.req.hdf.setValue('release.mode', 'view') 
     203        add_to_hdf(release, self.req.hdf, 'release') 
     204 
  • trac/upgrades/db8.py

     
     1sql = """ 
     2-- Create release table and add default release view permissions 
     3CREATE TABLE release ( 
     4        rev             integer PRIMARY KEY, 
     5        name            text, 
     6        changesetby     text, 
     7        modifiedby      text, 
     8        time            integer, 
     9        timemodified    integer, 
     10        descr           text         
     11); 
     12INSERT INTO permission (username, action) VALUES ('anonymous', 'RELEASELIST_VIEW'); 
     13INSERT INTO permission (username, action) VALUES ('anonymous', 'RELEASE_VIEW'); 
     14""" 
     15 
     16def do_upgrade(env, ver, cursor): 
     17    cursor.execute(sql) 
  • trac/Href.py

     
    116116            href += '?' + urllib.urlencode(params) 
    117117        return href 
    118118 
     119    def releaselist(self): 
     120        href = href_join(self.base, 'releaselist') 
     121        return href 
     122 
     123    def release(self, rev, action=None): 
     124        params = [] 
     125        if rev: 
     126             href = href_join(self.base, 'release', str(rev)) 
     127        else: 
     128            href = href_join(self.base, 'release') 
     129        if action: 
     130            params.append(('action', action)) 
     131        if params: 
     132            href += '?' + urllib.urlencode(params) 
     133        return href 
     134 
    119135    def settings(self): 
    120136        return href_join(self.base, 'settings') 
    121137 
  • trac/Changeset.py

     
    153153    fs_ptr = None 
    154154    pool = None 
    155155 
     156    def get_release_info (self, rev): 
     157        cursor = self.db.cursor () 
     158        cursor.execute ('SELECT time, name, descr FROM release ' + 
     159                        'WHERE rev=%d', rev) 
     160        row = cursor.fetchone() 
     161        return row 
     162 
    156163    def get_changeset_info (self, rev): 
    157164        cursor = self.db.cursor () 
    158165        cursor.execute ('SELECT time, author, message FROM revision ' + 
     
    194201        if self.args.has_key('update'): 
    195202            self.req.redirect(self.env.href.changeset(self.rev)) 
    196203 
     204        if self.perm.has_permission(perm.RELEASE_VIEW): 
     205           release_info = self.get_release_info(self.rev) 
     206           if release_info > 1: 
     207              self.add_link('existingrelease', self.env.href.release(self.rev), 
     208                'Show Tagged Release: %s' % release_info['name']) 
     209           else: 
     210              if self.perm.has_permission(perm.RELEASE_CREATE): 
     211                 self.add_link('newrelease', self.env.href.release(self.rev, 'new'), 
     212                    'Tag Changeset %d as New Release' % self.rev) 
     213         
    197214        change_info = self.get_change_info (self.rev) 
    198215        changeset_info = self.get_changeset_info (self.rev) 
    199216 
  • trac/Releaselist.py

     
     1# -*- coding: iso8859-1 -*- 
     2# 
     3# Copyright (C) 2004 Edgewall Software 
     4# Copyright (C) 2004 Christopher Lenz <cmlenz@gmx.de> 
     5# 
     6# Trac is free software; you can redistribute it and/or 
     7# modify it under the terms of the GNU General Public License as 
     8# published by the Free Software Foundation; either version 2 of the 
     9# License, or (at your option) any later version. 
     10# 
     11# Trac is distributed in the hope that it will be useful, 
     12# but WITHOUT ANY WARRANTY; without even the implied warranty of 
     13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
     14# General Public License for more details. 
     15# 
     16# You should have received a copy of the GNU General Public License 
     17# along with this program; if not, write to the Free Software 
     18# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
     19# 
     20# Author: James Moger <james.moger@transonic.com> 
     21# Based on roadmap.py by: 
     22#          Christopher Lenz <cmlenz@gmx.de> 
     23 
     24import re 
     25from time import localtime, strftime, time 
     26 
     27from __init__ import __version__ 
     28import perm 
     29from util import add_to_hdf, CRLF, TracError 
     30import util 
     31from Module import Module 
     32from Wiki import wiki_to_html, wiki_to_oneliner 
     33 
     34 
     35class Releaselist(Module): 
     36    template_name = 'releaselist.cs' 
     37 
     38    def render(self): 
     39        self.perm.assert_permission(perm.RELEASELIST_VIEW) 
     40        self.req.hdf.setValue('title', 'Releases') 
     41 
     42        query = "SELECT name, time, descr, rev FROM release WHERE name != '' ORDER BY name ASC, time DESC" 
     43 
     44        cursor = self.db.cursor() 
     45        cursor.execute(query) 
     46        self.releases = [] 
     47        while 1: 
     48            row = cursor.fetchone() 
     49            if not row: 
     50                break 
     51            release = { 
     52                'name': row['name'], 
     53                'href': self.env.href.release(row['rev']), 
     54                'time': row['time'] and int(row['time']) 
     55            } 
     56            descr = row['descr'] 
     57 
     58            if descr: 
     59                shorttip = util.shorten_line(util.wiki_escape_newline(descr)) 
     60                shortertip = wiki_to_oneliner(util.wiki_escape_newline(shorttip), self.req.hdf, 
     61                                                  self.env, self.db) 
     62                release['tooltip'] = util.escape(shortertip) 
     63            if release['time'] > 0: 
     64                release['date'] = strftime('%x %X', localtime(release['time'])) 
     65            self.releases.append(release) 
     66        cursor.close() 
     67        add_to_hdf(self.releases, self.req.hdf, 'releaselist.releases') 
     68 
     69 
  • templates/releaselist.cs

     
     1<?cs set:html.stylesheet = 'css/releaselist.css' ?> 
     2<?cs include "header.cs"?> 
     3<?cs include "macros.cs"?> 
     4 
     5<div id="ctxtnav" class="nav"> 
     6</div> 
     7 
     8<div id="content" class="releaselist"> 
     9<h1>Releases</h1> 
     10 
     11 <ul class="releaselist"><?cs each:release = releaselist.releases ?> 
     12  <li class="release"> 
     13   <div class="info"> 
     14    <h2><a href="<?cs var:release.href ?>" title="<?cs var:release.tooltip ?>"><em><?cs 
     15      var:release.name ?></em></a></h2> 
     16    <p class="date"><?cs if:release.date ?> 
     17     <?cs var:release.date ?><?cs else ?>No date set<?cs /if ?> 
     18    </p> 
     19   </div> 
     20  </li> 
     21 <?cs /each ?></ul> 
     22 <div id="help"> 
     23  <strong>Note:</strong> See <a href="<?cs 
     24    var:trac.href.wiki ?>/TracReleaselist">TracReleaselist</a> for help on using the release list. 
     25 </div> 
     26</div> 
     27<?cs include "footer.cs"?> 
  • templates/release.cs

     
     1<?cs set:html.stylesheet = 'css/releaselist.css' ?> 
     2<?cs include:"header.cs"?> 
     3<?cs include:"macros.cs"?> 
     4 
     5<div id="ctxtnav" class="nav"> 
     6 <ul> 
     7  <?cs if:release.href.edit ?><li class="first"><a href="<?cs 
     8    var:release.href.edit ?>">Edit Release Info</a></li><?cs /if ?> 
     9  <?cs if:release.href.delete ?><li class="last"><a href="<?cs 
     10    var:release.href.delete ?>">Delete Release</a></li><?cs /if ?> 
     11 </ul> 
     12</div> 
     13 
     14<div id="content" class="release"> 
     15 <?cs if:release.mode == "new" ?> 
     16 <h1>New Release</h1> 
     17 <?cs elif:release.mode == "edit" ?> 
     18 <h1>Edit Release <?cs var:release.name ?></h1> 
     19 <?cs elif:release.mode == "delete" ?> 
     20 <h1>Delete Release <?cs var:release.name ?></h1> 
     21 <?cs else ?> 
     22 <h1>Release <?cs var:release.name ?></h1> 
     23 <?cs /if ?> 
     24 
     25 <?cs if:release.mode == "edit" || release.mode == "new" ?> 
     26  <script type="text/javascript"> 
     27    addEvent(window, 'load', function() { 
     28      document.getElementById('name').focus() } 
     29    ); 
     30  </script> 
     31  <form id="edit" action="<?cs var:cgi_location ?>" method="post"> 
     32   <input type="hidden" name="mode" value="release" /> 
     33   <input type="hidden" name="rev" value="<?cs var:release.revision ?>" /> 
     34   <input type="hidden" name="action" value="commit_changes" /> 
     35   <div class="field"> 
     36    <label for="name">Name of the release:</label><br /> 
     37    <input type="text" id="name" name="name" size="32" value="<?cs 
     38      var:release.name ?>" /> 
     39   </div> 
     40   <div class="field"> 
     41    <fieldset class="iefix"> 
     42     <label for="descr">Description (you may use <a tabindex="42" href="<?cs 
     43       var:trac.href.wiki ?>/WikiFormatting">WikiFormatting</a> here):</label> 
     44     <p><textarea id="descr" name="descr" rows="12" cols="80"><?cs 
     45       var:release.descr_source ?></textarea></p> 
     46     <?cs call:wiki_toolbar('descr') ?> 
     47    </fieldset> 
     48   </div> 
     49   <div class="buttons"> 
     50    <?cs if:release.mode == "new" 
     51     ?><input type="submit" name="save" value="Add Release" /><?cs 
     52    else 
     53     ?><input type="submit" name="save" value="Save Changes" /><?cs 
     54    /if ?> 
     55    <input type="submit" name="cancel" value="Cancel" /> 
     56   </div> 
     57  </form> 
     58 <?cs elif:release.mode == "delete" ?> 
     59  <form action="<?cs var:cgi_location ?>" method="post"> 
     60   <input type="hidden" name="mode" value="release" /> 
     61   <input type="hidden" name="rev" value="<?cs var:release.revision ?>" /> 
     62   <input type="hidden" name="action" value="confirm_delete" /> 
     63   <p><strong>Are you sure you want to delete this release?</strong></p> 
     64   <div class="buttons"> 
     65    <input type="submit" name="cancel" value="Cancel" /> 
     66    <input type="submit" name="delete" value="Delete Release" /> 
     67   </div> 
     68  </form> 
     69 <?cs else ?> 
     70  <em class="date"><?cs if:release.date ?> 
     71   <?cs var:release.date ?><?cs else ?>No date specified<?cs /if ?> 
     72  </em> 
     73  <?cs if:release.href.changeset ?> 
     74  <dt class="date">Based on changeset <a class="prev" href="<?cs var:release.href.changeset ?>" title="Changeset <?cs var:release.revision ?>"><?cs var:release.revision ?></a> by <?cs var:release.changesetby ?></dt> 
     75  <?cs /if ?> 
     76  <dt class="date">Last changed by <?cs var:release.modifiedby ?> @ <?cs var:release.timemodified ?></dt> 
     77  <div class="descr"><?cs var:release.descr ?></div> 
     78 <?cs /if ?> 
     79 
     80 <?cs if:release.mode == "view" ?> 
     81<?cs /if ?> 
     82 
     83 <div id="help"> 
     84  <strong>Note:</strong> See <a href="<?cs 
     85    var:trac.href.wiki ?>/TracReleaselist">TracReleaselist</a> for help on using the release list. 
     86 </div> 
     87</div> 
     88<?cs include:"footer.cs"?> 
     89 
  • templates/header.cs

     
    100100  set:$roadmap_view="roadmap" ?><?cs  
    101101 /if ?> 
    102102 
     103 
    103104<div id="mainnav" class="nav"> 
    104105 <ul> 
    105106  <?cs call:navlink("Wiki", $trac.href.wiki, $wiki_view, 
    106107                    $trac.acl.WIKI_VIEW, "1") ?> 
     108  <?cs call:navlink("Releases", $trac.href.releaselist, "releaselist", 
     109                    $trac.acl.RELEASELIST_VIEW, "") ?> 
    107110  <?cs call:navlink("Timeline", $trac.href.timeline, "timeline", 
    108111                    $trac.acl.TIMELINE_VIEW, "2") ?> 
    109112  <?cs call:navlink("Roadmap", trac.href.roadmap, $roadmap_view, 
  • templates/changeset.cs

     
    55<div id="ctxtnav" class="nav"> 
    66 <h2>Changeset Navigation</h2> 
    77 <ul><?cs 
     8  if:len(links.newrelease) ?> 
     9  <li> 
     10   <a class="next" href="<?cs var:links.newrelease.0.href ?>" title="<?cs 
     11    var:links.newrelease.0.title ?>">Tag as New Release</a> 
     12   </li><?cs 
     13  /if ?><?cs 
     14  if:len(links.existingrelease) ?> 
     15  <li> 
     16   <a class="next" href="<?cs var:links.existingrelease.0.href ?>" title="<?cs 
     17    var:links.existingrelease.0.title ?>">Show Tagged Release</a> 
     18   </li><?cs 
     19  /if ?><?cs 
    820  if:len(links.prev) ?> 
    921   <li class="first<?cs if:!len(links.next) ?> last<?cs /if ?>"> 
    1022    <a class="prev" href="<?cs var:links.prev.0.href ?>" title="<?cs 
  • templates/timeline.cs

     
    3535      if:timeline.milestone ?>checked="checked"<?cs /if ?> /> 
    3636    <label for="milestone">Milestones</label> 
    3737   </div><?cs /if ?> 
     38   <?cs if:trac.acl.RELEASE_VIEW ?><div class="field"> 
     39    <input type="checkbox" id="release" name="release" <?cs 
     40      if:timeline.release ?>checked="checked"<?cs /if ?> /> 
     41    <label for="release">Releases</label> 
     42   </div><?cs /if ?> 
    3843  </fieldset> 
    3944  <div class="buttons"> 
    4045   <input type="submit" value="Update" /> 
     
    8489 <?cs elif:item.type == #6 ?><!-- milestone --> 
    8590  <?cs call:tlitem(item.href, 'milestone', 
    8691    '<em>Milestone '+$item.message+'</em> reached', '') ?> 
     92 <?cs elif:item.type == #7 ?><!-- release --> 
     93  <?cs call:tlitem(item.href, 'release', 
     94    'Release <em>'+$item.message +'</em>', '') ?> 
    8795 <?cs /if ?> 
    8896<?cs /each ?> 
    8997<?cs if:len(timeline.items) ?></dl><?cs /if ?>