Edgewall Software

Ticket #548: subcomponent.patch

File subcomponent.patch, 30.8 kB (added by endre@…, 8 months ago)
  • trac/admin/templates/admin_subcomponents.html

     
     1<!DOCTYPE html 
     2    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
     3    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
     4<html xmlns="http://www.w3.org/1999/xhtml" 
     5      xmlns:xi="http://www.w3.org/2001/XInclude" 
     6      xmlns:py="http://genshi.edgewall.org/"> 
     7  <xi:include href="admin.html" /> 
     8  <head> 
     9    <title>Sub-Components</title> 
     10  </head> 
     11 
     12  <body> 
     13    <h2>Manage Sub-Components</h2> 
     14 
     15    <py:def function="owner_field(default_owner='')"> 
     16      <div class="field"> 
     17        <label>Owner: <br /> 
     18          <py:choose> 
     19            <select py:when="owners" size="1" id="owner" name="owner"> 
     20              <option py:for="owner in owners" 
     21                      selected="${owner==default_owner or None}">$owner</option> 
     22              <option py:if="default_owner and default_owner not in owners" 
     23                      selected="selected">$default_owner</option> 
     24            </select> 
     25            <input py:otherwise="" type="text" name="owner" value="$default_owner" /> 
     26          </py:choose> 
     27        </label> 
     28      </div> 
     29    </py:def> 
     30 
     31    <py:choose test="view"> 
     32      <form py:when="'detail'" class="mod" id="submodcomp" method="post"> 
     33        <fieldset> 
     34          <legend>Modify Sub-Component:</legend> 
     35          <div class="field"> 
     36            <label>Component:<br /><input type="text" name="parent" value="${subcomponent.parent}" disabled="disabled" /></label> 
     37          </div> 
     38          <div class="field"> 
     39            <label>Name:<br /><input type="text" name="name" value="${subcomponent.name}" /></label> 
     40          </div> 
     41          ${owner_field(subcomponent.owner)} 
     42          <div class="field"> 
     43            <fieldset class="iefix"> 
     44              <label for="description"> 
     45                Description (you may use 
     46                <a tabindex="42" href="${href.wiki('WikiFormatting')}">WikiFormatting</a> 
     47                here): 
     48              </label> 
     49              <p> 
     50                <textarea id="description" name="description" class="wikitext" 
     51                          rows="6" cols="60"> 
     52$subcomponent.description</textarea> 
     53              </p> 
     54            </fieldset> 
     55          </div> 
     56          <div class="buttons"> 
     57            <input type="submit" name="cancel" value="Cancel" /> 
     58            <input type="submit" name="save" value="Save" /> 
     59          </div> 
     60        </fieldset> 
     61      </form> 
     62 
     63      <py:otherwise> 
     64        <form class="addnew" id="addsubcomp" method="post"> 
     65          <fieldset> 
     66            <legend>Add Subcomponent:</legend> 
     67            <input type="hidden" name="parent" value="${parent}" /> 
     68            <div class="field"> 
     69              <label>Name:<br /><input type="text" name="name" /></label> 
     70            </div> 
     71            ${owner_field()} 
     72            <div class="buttons"> 
     73              <input type="submit" name="add" value="Add"/> 
     74            </div> 
     75          </fieldset> 
     76        </form> 
     77 
     78        <py:choose> 
     79          <form py:when="subcomponents" method="POST"> 
     80            Component: <select id="parent" name="parent" onchange="this.form.submit()"> 
     81            <option py:for="component in components" 
     82            selected="${parent == component.name or None}">${component.name}</option> 
     83            </select> 
     84            <table class="listing" id="subcomplist"> 
     85              <thead> 
     86                <tr><th class="sel">&nbsp;</th> 
     87                  <th>Name</th><th>Owner</th><th>Default</th> 
     88                </tr> 
     89              </thead> 
     90              <tbody> 
     91                <tr py:for="scomp in subcomponents"> 
     92                  <td class="sel"><input type="checkbox" name="sel" value="$scomp.name" /></td> 
     93                  <td class="name"> 
     94                    <a href="${panel_href(parent + '/' + scomp.name)}">$scomp.name</a> 
     95                  </td> 
     96                  <td class="owner">$scomp.owner</td> 
     97                  <td class="default"> 
     98                    <input type="radio" name="default" value="$scomp.name" 
     99                           checked="${scomp.name==default or None}" /> 
     100                  </td> 
     101                </tr> 
     102              </tbody> 
     103            </table> 
     104            <div class="buttons"> 
     105              <input type="submit" name="remove" value="Remove selected items" /> 
     106              <input type="submit" name="apply" value="Apply changes" /> 
     107            </div> 
     108            <p class="help"> 
     109              You can remove all items from this list to completely hide this 
     110              field from the user interface. 
     111            </p> 
     112          </form> 
     113 
     114          <p py:otherwise="" class="help"> 
     115            As long as you don't add any items to the list, this field 
     116            will remain completely hidden from the user interface. 
     117          </p> 
     118        </py:choose> 
     119      </py:otherwise> 
     120    </py:choose> 
     121  </body> 
     122 
     123</html> 
  • trac/db_default.py

     
    1717from trac.db import Table, Column, Index 
    1818 
    1919# Database version identifier. Used for automatic upgrades. 
    20 db_version = 21 
     20db_version = 22 
    2121 
    2222def __mkreports(reports): 
    2323    """Utility function used to create report data in same syntax as the 
     
    104104        Column('time', type='int'), 
    105105        Column('changetime', type='int'), 
    106106        Column('component'), 
     107        Column('subcomponent'), 
    107108        Column('severity'), 
    108109        Column('priority'), 
    109110        Column('owner'), 
     
    139140        Column('name'), 
    140141        Column('owner'), 
    141142        Column('description')], 
     143    Table('subcomponent', key=('parent', 'name'))[ 
     144        Column('parent'), 
     145        Column('name'), 
     146        Column('owner'), 
     147        Column('description')], 
    142148    Table('milestone', key='name')[ 
    143149        Column('name'), 
    144150        Column('due', type='int'), 
     
    335341             ('name', 'owner'), 
    336342               (('component1', 'somebody'), 
    337343                ('component2', 'somebody'))), 
    338            ('milestone', 
     344             ('subcomponent', 
     345             ('parent', 'name', 'owner'), 
     346               (('component1', 'subcomponent1-1', 'somebody'), 
     347                ('component1', 'subcomponent1-2', 'somebody'), 
     348                ('component2', 'subcomponent2-1', 'somebody'), 
     349                ('component2', 'subcomponent2-2', 'somebody'))), 
     350             ('milestone', 
    339351             ('name', 'due', 'completed'), 
    340352               (('milestone1', 0, 0), 
    341353                ('milestone2', 0, 0), 
  • trac/htdocs/js/query.js

     
    157157      return e; 
    158158    } 
    159159 
     160    // Convenience function for creating a <select> dependent on other <select> 
     161    function createDepSelect(name, options, optional) { 
     162      var e = document.createElement("select"); 
     163      if (name) e.name = name; 
     164      if (optional) e.options[0] = new Option(); 
     165      if (options) { 
     166        for (var i = 0; i < options.length; i++) { 
     167          var option; 
     168          if (typeof(options[i]) == "object") { 
     169            option = new Option(options[i].text, options[i].value); 
     170          } else { 
     171            option = new Option(options[i], options[i]); 
     172          } 
     173          e.options[e.options.length] = option; 
     174        } 
     175      } 
     176      return e; 
     177    } 
    160178    var propertyName = select.options[select.selectedIndex].value; 
    161179    var property = properties[propertyName]; 
    162180    var table = document.getElementById("filters").getElementsByTagName("table")[0]; 
     
    219237      td.className = "filter"; 
    220238      if (property.type == "select") { 
    221239        var element = createSelect(propertyName, property.options, true); 
     240      } else if (property.type == "depselect") { 
     241        var element = createDepSelect(propertyName, property.options, true); 
    222242      } else if (property.type == "text") { 
    223243        var element = document.createElement("input"); 
    224244        element.type = "text"; 
  • trac/htdocs/js/sublist.js

     
     1/*  
     2Based on code found at: 
     3http://www.ajaxray.com/blog/2007/11/08/jquery-controlled-dependent-or-cascading-select-list-2/  
     4*/ 
     5function makeSublist(parent,child,isSubselectOptional,childVal) 
     6{ 
     7  $("body").append("<select style='display:none' id='" + parent + child + "'></select>"); 
     8  $('#'+parent+child).html($("#"+child+" option")); 
     9   
     10          var parentValue = $('#'+parent).attr('value'); 
     11          $('#'+child).html($("#"+parent+child+" .sub_"+parentValue).clone()); 
     12   
     13  childVal = (typeof childVal == "undefined")? "" : childVal ; 
     14  if(isSubselectOptional) $('#'+child).prepend("<option></option>"); 
     15  $("#"+child+' option[@value="'+ childVal +'"]').attr('selected','selected');  
     16  $('#'+parent).change(  
     17          function() 
     18          { 
     19                  var parentValue = $('#'+parent).attr('value'); 
     20                  $('#'+child).html($("#"+parent+child+" .sub_"+parentValue).clone()); 
     21                  if(isSubselectOptional) $('#'+child).prepend("<option></option>"); 
     22                  $('#'+child+' option:first').attr('selected', 'selected'); 
     23                  $('#'+child).trigger("change");  
     24                  $('#'+child).focus(); 
     25          } 
     26  ); 
     27} 
  • trac/ticket/admin.py

     
    1212# history and logs, available at http://trac.edgewall.org/. 
    1313 
    1414from datetime import datetime 
     15import re 
    1516 
    1617from trac.admin import IAdminPanelProvider 
    1718from trac.core import * 
     
    119120        return 'admin_components.html', data 
    120121 
    121122 
     123class SubcomponentAdminPage(TicketAdminPage): 
     124 
     125    _type = 'subcomponents' 
     126    _label = ('Subcomponent', 'Subcomponents') 
     127 
     128    # TicketAdminPage methods 
     129 
     130    def _render_admin_panel(self, req, cat, page, subcomponent): 
     131        match = None 
     132        # Detail view? 
     133        if subcomponent: 
     134            match = re.match('([^/]+)/(.*)$', subcomponent) # Looking for pattern <component>/<subcomponent> in url 
     135  
     136        if match: # Detail view 
     137            subcomp = model.Subcomponent(self.env, match.group(2), match.group(1)) 
     138            if req.method == 'POST': 
     139                if req.args.get('save'): 
     140                    subcomp.name = req.args.get('name') 
     141                    subcomp.owner = req.args.get('owner') 
     142                    subcomp.description = req.args.get('description') 
     143                    subcomp.update() 
     144                    req.redirect(req.href.admin(cat, page, match.group(1))) 
     145                elif req.args.get('cancel'): 
     146                    req.redirect(req.href.admin(cat, page, match.group(1))) 
     147 
     148            add_script(req, 'common/js/wikitoolbar.js') 
     149            data = {'view': 'detail', 'subcomponent': subcomp} 
     150 
     151        else: 
     152            if req.method == 'POST': 
     153                # Add Component 
     154                if req.args.get('add') and req.args.get('name') and req.args.get('parent'): 
     155                    subcomp = model.Subcomponent(self.env) 
     156                    subcomp.name = req.args.get('name') 
     157                    subcomp.parent = req.args.get('parent') 
     158                    if req.args.get('owner'): 
     159                        subcomp.owner = req.args.get('owner') 
     160                    subcomp.insert() 
     161                    req.redirect(req.href.admin(cat, page, subcomp.parent)) 
     162 
     163                # Remove components 
     164                elif req.args.get('remove') and req.args.get('sel'): 
     165                    sel = req.args.get('sel') 
     166                    sel = isinstance(sel, list) and sel or [sel] 
     167                    parent = req.args.get('parent') 
     168                    if not sel: 
     169                        raise TracError(_('No subcomponent selected')) 
     170                    db = self.env.get_db_cnx() 
     171                    for subcompname in sel: 
     172                        subcomp = model.Subcomponent(self.env, subcompname, parent, db=db) 
     173                        subcomp.delete(db=db) 
     174                    db.commit() 
     175                    req.redirect(req.href.admin(cat, page, req.args.get('parent'))) 
     176 
     177                # Set default component 
     178                elif req.args.get('apply'): 
     179                    if req.args.get('default'): 
     180                        name = req.args.get('default') 
     181                        self.log.info('Setting default subcomponent to %s', name) 
     182                        # Todo: One default for each parent? 
     183                        self.config.set('ticket', 'default_subcomponent', name) 
     184                        self.config.save() 
     185                        req.redirect(req.href.admin(cat, page, req.args.get('parent'))) 
     186                 
     187                # Changed selected parent 
     188                elif req.args.get('parent'): 
     189                    req.redirect(req.href.admin(cat, page, req.args.get('parent'))) 
     190 
     191            components = list(model.Component.select(self.env)) 
     192            if subcomponent: 
     193                parent = subcomponent # Catches redirects 
     194            else: 
     195                parent = components[0].name # Just use first in list as default 
     196 
     197            default = self.config.get('ticket', 'default_subcomponent') 
     198            data = {'view': 'list', 
     199                    'components': components, 
     200                    'subcomponents': model.Subcomponent.select(self.env, parent=parent), 
     201                    'parent': parent, 
     202                    'default': default} 
     203 
     204        if self.config.getbool('ticket', 'restrict_owner'): 
     205            perm = PermissionSystem(self.env) 
     206            def valid_owner(username): 
     207                return perm.get_user_permissions(username).get('TICKET_MODIFY') 
     208            data['owners'] = [username for username, name, email 
     209                              in self.env.get_known_users() 
     210                              if valid_owner(username)] 
     211        else: 
     212            data['owners'] = None 
     213 
     214        return 'admin_subcomponents.html', data 
     215 
     216 
    122217class MilestoneAdminPage(TicketAdminPage): 
    123218 
    124219    _type = 'milestones' 
  • trac/ticket/api.py

     
    227227            elif name in ('milestone', 'version'): 
    228228                field['optional'] = True 
    229229            fields.append(field) 
     230             
     231        # Dependent select fields 
     232        depselects = [('subcomponent', model.Subcomponent, 'component')] 
     233        for name, cls, parent in depselects: 
     234            options = [(val.name, val.parent) for val in cls.select(self.env, db=db)] 
     235            if not options: 
     236                continue 
     237            field = {'name': name, 'type': 'depselect', 'label': name.title(), 
     238                     'value': self.config.get('ticket', 'default_' + name), 
     239                     'options': options} 
     240            field['optional'] = True 
     241            fields.append(field) 
     242             
     243                 
    230244 
    231245        # Advanced text fields 
    232246        for name in ('keywords', 'cc', ): 
  • trac/ticket/model.py

     
    3131from trac.util.translation import _ 
    3232 
    3333__all__ = ['Ticket', 'Type', 'Status', 'Resolution', 'Priority', 'Severity', 
    34            'Component', 'Milestone', 'Version'] 
     34           'Component', 'Subcomponent', 'Milestone', 'Version'] 
    3535 
    3636 
    3737class Ticket(object): 
     
    149149 
    150150        cursor = db.cursor() 
    151151 
    152         # The owner field defaults to the component owner 
     152        # The owner field defaults to the sub-component owner or component owner if sub-component doesn't exist 
    153153        if self.values.get('component') and not self.values.get('owner'): 
    154             try: 
    155                 component = Component(self.env, self['component'], db=db) 
    156                 if component.owner: 
    157                     self['owner'] = component.owner 
    158             except TracError, e: 
    159                 # Assume that no such component exists 
    160                 pass 
     154            foundowner = False 
     155            # Try to get owner field from sub-component first 
     156            if self.values.get('subcomponent'): 
     157                try: 
     158                    subcomponent = Subcomponent(self.env, self['subcomponent'], self['component'], db=db) 
     159                    if subcomponent.owner: 
     160                        self['owner'] = subcomponent.owner 
     161                        foundowner = True 
     162                except TracError, e: 
     163                    pass 
     164            # If no owner found, try component 
     165            if not foundowner: 
     166                try: 
     167                    component = Component(self.env, self['component'], db=db) 
     168                    if component.owner: 
     169                        self['owner'] = component.owner 
     170                except TracError, e: 
     171                    # Assume that no such component exists 
     172                    pass 
    161173 
    162174        # Insert ticket record 
    163175        created = to_timestamp(self.time_created) 
     
    519531        cursor = db.cursor() 
    520532        self.env.log.info('Deleting component %s' % self.name) 
    521533        cursor.execute("DELETE FROM component WHERE name=%s", (self.name,)) 
     534        # Delete all subcomponents while we're at it 
     535        cursor.execute("DELETE FROM subcomponent WHERE parent=%s", (self.name,)) 
    522536 
    523537        self.name = self._old_name = None 
    524538 
     
    564578            # Update tickets 
    565579            cursor.execute("UPDATE ticket SET component=%s WHERE component=%s", 
    566580                           (self.name, self._old_name)) 
     581            # Update subcomponents 
     582            cursor.execute("UPDATE subcomponent SET parent=%s WHERE parent=%s", 
     583                           (self.name, self._old_name)) 
    567584            self._old_name = self.name 
    568585 
    569586        if handle_ta: 
     
    584601    select = classmethod(select) 
    585602 
    586603 
     604class Subcomponent(object): 
     605 
     606    def __init__(self, env, name=None, parent=None, db=None): 
     607        self.env = env 
     608        if name and parent: 
     609            if not db: 
     610                db = self.env.get_db_cnx() 
     611            cursor = db.cursor() 
     612            cursor.execute("SELECT owner,description FROM subcomponent " 
     613                           "WHERE name=%s AND parent=%s", (name,parent)) 
     614            row = cursor.fetchone() 
     615            if not row: 
     616                raise TracError(_('Subcomponent %(name) does not exist.', 
     617                                  name=name)) 
     618            self.name = self._old_name = name 
     619            self.parent = self._old_parent = parent 
     620            self.owner = row[0] or None 
     621            self.description = row[1] or '' 
     622        else: 
     623            self.name = self._old_name = None 
     624            self.owner = None 
     625            self.description = None 
     626            self.parent = self._old_parent = None 
     627 
     628    exists = property(fget=lambda self: self._old_name is not None) 
     629 
     630    def delete(self, db=None): 
     631        assert self.exists, 'Cannot deleting non-existent component' 
     632        if not db: 
     633            db = self.env.get_db_cnx() 
     634            handle_ta = True 
     635        else: 
     636            handle_ta = False 
     637 
     638        cursor = db.cursor() 
     639        self.env.log.info('Deleting subcomponent %s' % self.name) 
     640        cursor.execute("DELETE FROM subcomponent WHERE name=%s AND parent=%s", (self.name,self.parent)) 
     641 
     642        self.name = self._old_name = None 
     643        self.parent = self._old_parent = None 
     644 
     645        if handle_ta: 
     646            db.commit() 
     647 
     648    def insert(self, db=None): 
     649        assert not self.exists, 'Cannot insert existing subcomponent' 
     650        self.name = simplify_whitespace(self.name) 
     651        self.parent = simplify_whitespace(self.parent) 
     652        assert self.name, 'Cannot create component with no name' 
     653        assert self.parent, 'Cannot create subcomponent without a parent' 
     654        if not db: 
     655            db = self.env.get_db_cnx() 
     656            handle_ta = True 
     657        else: 
     658            handle_ta = False 
     659 
     660        cursor = db.cursor() 
     661        self.env.log.debug("Creating new subcomponent '%s'" % self.name) 
     662        cursor.execute("INSERT INTO subcomponent (name,owner,description,parent) " 
     663                       "VALUES (%s,%s,%s,%s)", 
     664                       (self.name, self.owner, self.description, self.parent)) 
     665 
     666        if handle_ta: 
     667            db.commit() 
     668 
     669    def update(self, db=None): 
     670        assert self.exists, 'Cannot update non-existent subcomponent' 
     671        self.name = simplify_whitespace(self.name) 
     672        self.parent = simplify_whitespace(self.parent) 
     673        assert self.name, 'Cannot update subcomponent with no name' 
     674        assert self.parent, 'Cannot create subcomponent without a parent' 
     675        if not db: 
     676            db = self.env.get_db_cnx() 
     677            handle_ta = True 
     678        else: 
     679            handle_ta = False 
     680 
     681        cursor = db.cursor() 
     682        self.env.log.info('Updating subcomponent "%s"' % self.name) 
     683        cursor.execute("UPDATE subcomponent SET name=%s,owner=%s,description=%s, parent=%s " 
     684                       "WHERE name=%s AND parent=%s", 
     685                       (self.name, self.owner, self.description, self.parent, self._old_name, self._old_parent)) 
     686        if self.name != self._old_name or self.parent != self._old_parent: 
     687            # Update tickets 
     688            cursor.execute("UPDATE ticket SET component=%s, subcomponent=%s WHERE component=%s AND subcomponent=%s", 
     689                           (self.parent, self.name, self._old_parent, self._old_name)) 
     690            self._old_name = self.name 
     691            self._old_parent = self.parent 
     692 
     693        if handle_ta: 
     694            db.commit() 
     695 
     696    def select(cls, env, db=None, parent=None): 
     697        if not db: 
     698            db = env.get_db_cnx() 
     699        cursor = db.cursor() 
     700        if parent: 
     701            cursor.execute("SELECT name,parent,owner,description FROM subcomponent " 
     702                           "WHERE parent=%s ORDER BY name", (parent,)) 
     703        else: 
     704            cursor.execute("SELECT name,parent,owner,description FROM subcomponent " 
     705                           "ORDER BY parent,name") 
     706        for name, parent, owner, description in cursor: 
     707            subcomponent = cls(env) 
     708            subcomponent.parent = parent 
     709            subcomponent.name = name 
     710            subcomponent.owner = owner or None 
     711            subcomponent.description = description or '' 
     712            yield subcomponent 
     713    select = classmethod(select) 
     714 
     715 
    587716class Milestone(object): 
    588717 
    589718    def __init__(self, env, name=None, db=None): 
  • trac/ticket/query.py

     
    511511            {'name': _("is"), 'value': ""}, 
    512512            {'name': _("is not"), 'value': "!"} 
    513513        ] 
     514        modes['depselect'] = [ 
     515            {'name': _("is"), 'value': ""}, 
     516            {'name': _("is not"), 'value': "!"} 
     517        ] 
    514518 
    515519        groups = {} 
    516520        groupsequence = [] 
  • trac/ticket/templates/query.html

     
    7575                          </select> 
    7676                        </py:when> 
    7777 
     78                        <py:when test="'depselect'"> 
     79                          <select name="${constraint_name}"> 
     80                            <option></option> 
     81                            <option py:for="option, parent in field.options" 
     82                              class="sub_$parent" 
     83                              selected="${option == constraint_value and 'selected' or None}">$option 
     84                            </option> 
     85                          </select> 
     86                        </py:when> 
     87 
    7888                        <py:when test="'radio'"> 
    7989                          <py:for each="option in field.options"> 
    8090                            <input type="checkbox" id="${field_name}_$option" name="${field_name}" 
     
    150160          <select name="group" id="group"> 
    151161            <option></option> 
    152162            <option py:for="field_name, field in fields.items()" 
    153                     py:if="field.type in ('select', 'radio') or field_name == 'owner'" 
     163                    py:if="field.type in ('select', 'depselect', 'radio') or field_name == 'owner'" 
    154164                    selected="${field_name == query.group or None}" 
    155165                    value="${field_name}">${field.label}</option> 
    156166          </select> 
     
    183193        <py:for each="(field_name, field), sep in separated(fields.iteritems())"> 
    184194          $field_name: { type: "$field.type", label: "$field.label" 
    185195          <py:if test="'options' in field">, options: [ 
    186             <py:for each="option, sep in separated(field.options)">"$option"$sep 
    187             </py:for>] 
     196            <py:choose test="field.type"> 
     197              <py:when test="'depselect'"> 
     198                <py:for each="option, sep in separated([o] for o, p in field.options)">"$option"$sep 
     199                </py:for>] 
     200              </py:when> 
     201              <py:otherwise> 
     202                <py:for each="option, sep in separated(field.options)">"$option"$sep 
     203                </py:for>] 
     204              </py:otherwise> 
     205            </py:choose> 
    188206          </py:if>}$sep 
    189207        </py:for> 
    190208        }; 
  • trac/ticket/templates/ticket.html

     
    1919      </py:choose> 
    2020    </title> 
    2121    <script type="text/javascript" src="${chrome.htdocs_location}js/wikitoolbar.js"></script> 
    22     <script type="text/javascript" py:choose=""> 
     22    <script type="text/javascript" src="${chrome.htdocs_location}js/sublist.js"></script> 
     23  <script type="text/javascript" py:choose=""> 
    2324      $(document).ready(function() { 
     25        makeSublist('field-component', 'field-subcomponent', true, '${ticket["subcomponent"]}');  
    2426        $("div.description").find("h1,h2,h3,h4,h5,h6").addAnchor("Link to this section"); 
    2527      <py:when test="ticket.exists"> 
    2628        $("#changelog h3.change").addAnchor("Link to this change"); 
     
    3941        updateActionFields(); 
    4042      </py:when> 
    4143      <py:otherwise> 
    42         $(document).ready(function() {$("#field-summary").get(0).focus()}); 
     44        $(document).ready(function() {makeSublist('parent', 'child', false, '1'); $("#field-summary").get(0).focus();}); 
    4345      </py:otherwise> 
    4446      }); 
    4547    </script> 
     
    311313                      <option py:if="field.optional"></option> 
    312314                      <option py:for="option in field.options" 
    313315                              selected="${ticket[field.name] == option or None}" 
     316                              value="$option" 
    314317                              py:content="option"></option> 
    315318                      <optgroup py:for="optgroup in field.optgroups" 
    316319                                label="${optgroup.label}"> 
     
    319322                                py:content="option"></option> 
    320323                      </optgroup> 
    321324                    </select> 
     325                    <select py:when="'depselect'" id="field-${field.name}" name="field_${field.name}"> 
     326                      <option py:for="(option, parent) in field.options" 
     327                              class="sub_${parent}" 
     328                              value="$option" 
     329                            &nb