Edgewall Software

Ticket #869: patch-customworkflow-r1064.diff

File patch-customworkflow-r1064.diff, 24.3 KB (added by pkou <pkou at ua.fm>, 7 years ago)

Support customized workflows in Trac

  • wiki-default/TracIni

     
    2222See also: TracLogging 
    2323 
    2424== [ticket] == 
     25|| workflow || Ticket workflow class.  If not specified, it is ''trac.workflows.SimpleWorkflow'' || 
    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 || 
  • setup.py

     
    198198      author_email="info@edgewall.com", 
    199199      license=LICENSE, 
    200200      url=URL, 
    201       packages=['trac', 'trac.upgrades', 'trac.wikimacros', 'trac.mimeviewers'], 
     201      packages=['trac', 'trac.upgrades', 'trac.wikimacros', 'trac.mimeviewers', 
     202                'trac.workflows'], 
    202203      data_files=[(_p('share/trac/templates'), glob('templates/*')), 
    203204                  (_p('share/trac/htdocs'), glob(_p('htdocs/*.*')) + [_p('htdocs/README')]), 
    204205                  (_p('share/trac/htdocs/css'), glob(_p('htdocs/css/*'))), 
  • trac/workflows/Base.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: Pavel Kourochka <pkou@ua.fm> 
     21# 
     22# Abstract workflow definition 
     23 
     24class WorkflowBase: 
     25    """ 
     26    Generic workflow class for Trac. 
     27    """ 
     28 
     29    def __init__(self, env, db, user): 
     30        """ 
     31        Constructor for workflow class. 
     32        """ 
     33        self.env = env 
     34        self.db = db 
     35        self.user = user 
     36 
     37    def get_actions(self, ticket): 
     38        """ 
     39        For existing tickets only. 
     40        Return the list of available actions for specified ticket. 
     41        """ 
     42        raise Exception, "WorkflowBase::get_actions not implemented" 
     43 
     44    def do_action(self, ticket, action, args): 
     45        """ 
     46        For new and existing tickets. 
     47        Perform action on a ticket.  For new tickets, action name is 'create'. 
     48        """ 
     49        raise Exception, "WorkflowBase::do_action not implemented" 
     50 
     51    def get_actions_template(self, ticket): 
     52        """ 
     53        For new and existing tickets. 
     54        Return the name of ClearSilver template file for the workflow. 
     55        Return None if no additional template is required. 
     56        """ 
     57        return None 
     58 
     59    def init_template(self, ticket, hdf): 
     60        """ 
     61        For new and existing tickets. 
     62        Initialize ClearSilver variables for actions template. 
     63        Called if get_actions_template() returns file name only. 
     64        """ 
     65        pass 
     66 
     67    def validate(self, ticket): 
     68        """ 
     69        For new and existing tickets. 
     70        Validate ticket. 
     71        Return list of Wiki strings that describe errors in the ticket. 
     72        """ 
     73        return [] 
     74 
     75    def on_insert(self, ticket): 
     76        """ 
     77        For new tickets only. 
     78        Update ticket fields just before inserting the ticket into database. 
     79        """ 
     80        pass 
     81 
     82    def on_update(self, ticket): 
     83        """ 
     84        For existing tickets only. 
     85        Update ticket fields just before saving the ticket into database. 
     86        """ 
     87        pass 
  • trac/workflows/__init__.py

     
     1__all__ = ['Base', 'SimpleWorkflow'] 
  • trac/workflows/SimpleWorkflow.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: Pavel Kourochka <pkou@ua.fm> 
     21# 
     22# Simple workflow definition (as in Trac 0.8) 
     23 
     24from trac.workflows.Base import WorkflowBase 
     25 
     26class SimpleWorkflow(WorkflowBase): 
     27 
     28    def get_actions(self, ticket): 
     29        actions = { 
     30            'new':      ['leave', 'resolve', 'reassign', 'accept'], 
     31            'assigned': ['leave', 'resolve', 'reassign'          ], 
     32            'reopened': ['leave', 'resolve', 'reassign'          ], 
     33            'closed':   ['leave',                        'reopen'] 
     34        } 
     35        return actions.get(ticket['status'], ['leave']) 
     36 
     37    def do_action(self, ticket, action, args): 
     38        if action == 'accept': 
     39            ticket['status'] = 'assigned' 
     40            ticket['owner'] = self.user 
     41        elif action == 'resolve': 
     42            ticket['status'] = 'closed' 
     43            ticket['resolution'] = args.get('resolve_resolution') 
     44        elif action == 'reassign': 
     45            ticket['owner'] = args.get('reassign_owner') 
     46            ticket['status'] = 'new' 
     47        elif action == 'reopen': 
     48            ticket['status'] = 'reopened' 
     49            ticket['resolution'] = '' 
     50 
     51    def get_actions_template(self, ticket): 
     52        if ticket.has_key('id'): 
     53            return 'ticket_workflow_simple.cs' 
     54        else: 
     55            return None 
     56 
     57    def init_template(self, ticket, hdf): 
     58        WorkflowBase.init_template(self, ticket, hdf) 
     59        if ticket.has_key('id'): 
     60            for a in self.get_actions(ticket): 
     61                hdf.setValue('ticket.workflow.action.' + a, '1') 
     62 
     63    def validate(self, ticket): 
     64        err = WorkflowBase.validate(self, ticket) 
     65        if not ticket.get('summary'): 
     66            err.append("The ticket must contain '''Summary''' field.") 
     67        return err 
     68 
     69    def on_insert(self, ticket): 
     70        WorkflowBase.on_insert(self, ticket) 
     71 
     72        # The owner field defaults to the component owner 
     73        cursor = self.db.cursor() 
     74        if ticket.get('owner', '') == '': 
     75            cursor.execute('SELECT owner FROM component ' 
     76                           'WHERE name=%s', ticket.get('component', '')) 
     77            ticket['owner'] = cursor.fetchone()[0] or '' 
     78 
     79    def on_update(self, ticket): 
     80        WorkflowBase.on_update(self, ticket) 
     81        if not ticket._old: return # Not modified 
     82 
     83        # If the component is changed on a 'new' ticket then owner field 
     84        # is updated accordingly. (#623). 
     85        cursor = self.db.cursor() 
     86        if ticket['status'] == 'new' and ticket._old.has_key('component') and \ 
     87               not ticket._old.has_key('owner'): 
     88            cursor.execute('SELECT owner FROM component ' 
     89                           'WHERE name=%s', ticket._old['component']) 
     90            old_owner = cursor.fetchone()[0] 
     91            if ticket['owner'] == old_owner: 
     92                cursor.execute('SELECT owner FROM component ' 
     93                               'WHERE name=%s', ticket['component']) 
     94                ticket['owner'] = cursor.fetchone()[0] or '' 
  • trac/Ticket.py

     
    130130 
    131131        if not self._old and not comment: return # Not modified 
    132132 
    133         # If the component is changed on a 'new' ticket then owner field 
    134         # is updated accordingly. (#623). 
    135         if self['status'] == 'new' and self._old.has_key('component') and \ 
    136                not self._old.has_key('owner'): 
    137             cursor.execute('SELECT owner FROM component ' 
    138                            'WHERE name=%s', self._old['component']) 
    139             old_owner = cursor.fetchone()[0] 
    140             if self['owner'] == old_owner: 
    141                 cursor.execute('SELECT owner FROM component ' 
    142                                'WHERE name=%s', self['component']) 
    143                 self['owner'] = cursor.fetchone()[0] 
    144             
    145  
    146133        for name in self._old.keys(): 
    147134            if name[:7] == 'custom_': 
    148135                fname = name[7:] 
     
    264251        i += 1 
    265252 
    266253 
     254def get_workflow(env, db, user): 
     255#    from trac.workflows.Simple import SimpleWorkflow 
     256#    return SimpleWorkflow(env, db, user) 
     257    modulename = env.get_config('ticket', 'workflow', \ 
     258                                'trac.workflows.SimpleWorkflow') 
     259    i = modulename.rfind('.') 
     260    if i == -1: 
     261        classname = modulename 
     262    else: 
     263        classname = modulename[i+1:] 
     264 
     265    module = __import__(modulename, globals(), locals(), [classname]) 
     266    constructor = getattr(module, classname) 
     267    workflow = constructor(env, db, user) 
     268 
     269    from workflows.Base import WorkflowBase 
     270    if not isinstance(workflow, WorkflowBase): 
     271        raise EnvironmentError, "Workflow class %s from %s must be " \ 
     272                                "descendant of class WorkflowBase from " \ 
     273                                "trac.workflows.base" \ 
     274                                % (classname, modulename) 
     275 
     276    return workflow 
     277 
     278 
    267279class NewticketModule(Module): 
    268280    template_name = 'newticket.cs' 
    269281 
    270     def create_ticket(self): 
    271         if not self.args.get('summary'): 
    272             raise util.TracError('Tickets must contain Summary.') 
    273  
    274         ticket = Ticket() 
    275         ticket.populate(self.args) 
     282    def create_ticket(self, ticket, workflow): 
    276283        ticket.setdefault('reporter',self.req.authname) 
    277284 
    278         # The owner field defaults to the component owner 
    279         cursor = self.db.cursor() 
    280         if ticket.get('component') and ticket.get('owner', '') == '': 
    281             cursor.execute('SELECT owner FROM component ' 
    282                            'WHERE name=%s', ticket['component']) 
    283             owner = cursor.fetchone()[0] 
    284             ticket['owner'] = owner 
    285  
     285        workflow.on_insert(ticket) 
    286286        tktid = ticket.insert(self.db) 
    287287 
    288288        # Notify 
     
    294294    def render (self): 
    295295        self.perm.assert_permission(perm.TICKET_CREATE) 
    296296 
    297         if self.args.has_key('create'): 
    298             self.create_ticket() 
     297        ticket = Ticket() 
    299298 
    300         ticket = Ticket() 
     299        preview = self.args.has_key('preview') 
     300        do_create = self.args.has_key('create') 
    301301        ticket.populate(self.args) 
     302 
     303        workflow = get_workflow(self.env, self.db, self.req.authname) 
     304 
     305        # Validate the ticket 
     306        err = [] 
     307        if preview or do_create: 
     308            err.extend(workflow.validate(ticket)) 
     309        if len(err) != 0: preview = 1 
     310 
     311        # Create the ticket if not in preview mode 
     312        if not preview and do_create: 
     313            workflow.do_action(ticket, 'create', self.args) 
     314            self.create_ticket(ticket, workflow) 
     315 
    302316        ticket.setdefault('component', 
    303317                          self.env.get_config('ticket', 'default_component')) 
    304318        ticket.setdefault('milestone', 
     
    320334        evals = util.mydict(zip(ticket.keys(), 
    321335                                map(lambda x: util.escape(x), ticket.values()))) 
    322336        util.add_to_hdf(evals, self.req.hdf, 'newticket') 
     337        if len(err) != 0: 
     338            self.req.hdf.setValue('newticket.workflow.error', 
     339                              wiki_to_html(' * ' + '\n * '.join(err), 
     340                                           self.req.hdf, self.env, self.db)) 
     341        tpl = workflow.get_actions_template(ticket) 
     342        if tpl: 
     343            self.req.hdf.setValue('newticket.workflow.template', tpl) 
     344            workflow.init_template(ticket, self.req.hdf) 
    323345 
    324346        util.sql_to_hdf(self.db, 'SELECT name FROM component ORDER BY name', 
    325347                        self.req.hdf, 'newticket.components') 
     
    334356class TicketModule (Module): 
    335357    template_name = 'ticket.cs' 
    336358 
    337     def save_changes (self, id): 
     359    def save_changes (self, ticket, workflow): 
    338360        self.perm.assert_permission (perm.TICKET_MODIFY) 
    339         ticket = Ticket(self.db, id) 
    340361 
    341         if not self.args.get('summary'): 
    342             raise util.TracError('Tickets must contain Summary.') 
    343  
    344362        if self.args.has_key('description'): 
    345363            self.perm.assert_permission (perm.TICKET_ADMIN) 
    346364 
    347365        if self.args.has_key('reporter'): 
    348366            self.perm.assert_permission (perm.TICKET_ADMIN) 
    349367 
    350         # TODO: this should not be hard-coded like this 
    351         action = self.args.get('action', None) 
    352         if action == 'accept': 
    353             ticket['status'] =  'assigned' 
    354             ticket['owner'] = self.req.authname 
    355         if action == 'resolve': 
    356             ticket['status'] = 'closed' 
    357             ticket['resolution'] = self.args.get('resolve_resolution') 
    358         elif action == 'reassign': 
    359             ticket['owner'] = self.args.get('reassign_owner') 
    360             ticket['status'] = 'new' 
    361         elif action == 'reopen': 
    362             ticket['status'] = 'reopened' 
    363             ticket['resolution'] = '' 
    364  
    365         ticket.populate(self.args) 
    366  
    367368        now = int(time.time()) 
    368369 
     370        workflow.on_update(ticket) 
    369371        ticket.save_changes(self.db, 
    370372                            self.args.get('author', self.req.authname), 
    371373                            self.args.get('comment'), 
     
    373375 
    374376        tn = TicketNotifyEmail(self.env) 
    375377        tn.notify(ticket, newticket=0, modtime=now) 
    376         self.req.redirect(self.env.href.ticket(id)) 
     378        self.req.redirect(self.env.href.ticket(ticket['id'])) 
    377379 
    378380    def insert_ticket_data(self, hdf, id, ticket, reporter_id): 
    379381        """Insert ticket data into the hdf""" 
     
    431433    def render (self): 
    432434        self.perm.assert_permission (perm.TICKET_VIEW) 
    433435 
    434         action = self.args.get('action', 'view') 
    435         preview = self.args.has_key('preview') 
    436  
    437436        if not self.args.has_key('id'): 
    438437            self.req.redirect(self.env.href.wiki()) 
    439438 
    440439        id = int(self.args.get('id')) 
     440        ticket = Ticket(self.db, id) 
    441441 
    442         if not preview \ 
    443                and action in ['leave', 'accept', 'reopen', 'resolve', 'reassign']: 
    444             self.save_changes (id) 
     442        action = self.args.get('action', None) 
     443        preview = self.args.has_key('preview') 
     444        if action or preview: 
     445            ticket.populate(self.args) 
    445446 
    446         ticket = Ticket(self.db, id) 
     447        workflow = get_workflow(self.env, self.db, self.req.authname) 
     448 
     449        # Validate ticket 
     450        err = [] 
     451        if action or preview: 
     452            actions = workflow.get_actions(ticket) 
     453            if action not in actions: 
     454                err.append("Invalid action '''%s''' is performed on the ticket. " \ 
     455                           "Allowed actions are <''%s''>." % \ 
     456                           (action, ', '.join(actions))) 
     457            err.extend(workflow.validate(ticket)) 
     458        if len(err) != 0: preview = 1 
     459 
     460        # Save changes if not in preview mode 
     461        if not preview and action: 
     462            workflow.do_action(ticket, action, self.args) 
     463            self.save_changes(ticket, workflow) 
     464 
    447465        reporter_id = util.get_reporter_id(self.req) 
    448466 
    449467        if preview: 
    450             # Use user supplied values 
    451             for field in Ticket.std_fields: 
    452                 if self.args.has_key(field) and field != 'reporter': 
    453                     ticket[field] = self.args.get(field) 
    454             self.req.hdf.setValue('ticket.action', action) 
     468            if action: self.req.hdf.setValue('ticket.action', action) 
    455469            reporter_id = self.args.get('author') 
    456470            comment = self.args.get('comment') 
    457471            if comment: 
     
    462476                                               self.req.hdf, self.env, self.db)) 
    463477 
    464478        self.insert_ticket_data(self.req.hdf, id, ticket, reporter_id) 
     479        if len(err) != 0: 
     480            self.req.hdf.setValue('ticket.workflow.error', 
     481                              wiki_to_html(' * ' + '\n * '.join(err), 
     482                                           self.req.hdf, self.env, self.db)) 
     483        tpl = workflow.get_actions_template(ticket) 
     484        if tpl: 
     485            self.req.hdf.setValue('ticket.workflow.template', tpl) 
     486            workflow.init_template(ticket, self.req.hdf) 
    465487 
    466488        cursor = self.db.cursor() 
    467489        cursor.execute("SELECT max(id) FROM ticket") 
  • templates/ticket.cs

     
    208208  </div><?cs /if ?> 
    209209 </fieldset> 
    210210 
    211  <fieldset id="action"> 
    212   <legend>Action</legend><?cs 
    213   if:!ticket.action ?><?cs set:ticket.action = 'leave' ?><?cs 
    214   /if ?><?cs 
    215   def:action_radio(id) ?> 
    216    <input type="radio" id="<?cs var:id ?>" name="action" value="<?cs 
    217      var:id ?>"<?cs if:$ticket.action == $id ?> checked="checked"<?cs 
    218      /if ?> /><?cs 
    219   /def ?> 
    220   <?cs call:action_radio('leave') ?> 
    221   <label for="leave">leave as <?cs var:ticket.status ?></label><br /><?cs 
    222   if $ticket.status == "new" ?> 
    223    <?cs call:action_radio('accept') ?> 
    224    <label for="accept">accept ticket</label><br /><?cs 
    225   /if ?><?cs 
    226   if $ticket.status == "closed" ?> 
    227    <?cs call:action_radio('reopen') ?> 
    228    <label for="reopen">reopen ticket</label><br /><?cs 
    229   /if ?><?cs 
    230   if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
    231    <?cs call:action_radio('resolve') ?> 
    232    <label for="resolve">resolve</label> 
    233    <label for="resolve_resolution">as:</label> 
    234    <?cs call:hdf_select(enums.resolution, "resolve_resolution", args.resolve_resolution) ?><br /> 
    235    <?cs call:action_radio('reassign') ?> 
    236    <label for="reassign">reassign</label> 
    237    <label for="reassign_owner">to:</label> 
    238    <input type="text" id="reassign_owner" name="reassign_owner" size="40" value="<?cs 
    239      if:args.reassign_to ?><?cs var:args.reassign_to ?><?cs 
    240      else ?><?cs var:trac.authname ?><?cs /if ?>" /><?cs 
    241   /if ?><?cs 
    242   if $ticket.status == "new" || $ticket.status == "assigned" || $ticket.status == "reopened" ?> 
    243    <script type="text/javascript"> 
    244      var resolve = document.getElementById("resolve"); 
    245      var reassign = document.getElementById("reassign"); 
    246      var updateActionFields = function() { 
    247        enableControl('resolve_resolution', resolve.checked); 
    248        enableControl('reassign_owner', reassign.checked); 
    249      }; 
    250      addEvent(window, 'load', updateActionFields); 
    251      addEvent(document.getElementById("leave"), 'click', updateActionFields);<?cs 
    252     if $ticket.status == "new" ?> 
    253      addEvent(document.getElementById("accept"), 'click', updateActionFields);<?cs 
    254     /if ?> 
    255     addEvent(resolve, 'click', updateActionFields); 
    256     addEvent(reassign, 'click', updateActionFields); 
    257    </script><?cs 
    258   /if ?> 
    259  </fieldset> 
     211 <?cs if ticket.workflow.template ?> 
     212  <fieldset id="action"> 
     213   <legend>Action</legend> 
     214   <?cs include ticket.workflow.template ?> 
     215  </fieldset> 
     216 <?cs /if ?> 
    260217 
     218 <?cs if ticket.workflow.error ?> 
     219   <div class="system-message"> 
     220     <h2>Ticket Error</h2> 
     221     <p class="message"><?cs var ticket.workflow.error ?></p> 
     222     <strong>The ticket will not be saved.</strong> 
     223   </div> 
     224 <?cs /if ?> 
     225 
    261226 <div class="buttons"> 
    262227  <input type="reset" value="Reset" />&nbsp; 
    263228  <input type="submit" name="preview" value="Preview" />&nbsp; 
  • templates/ticket_workflow_simple.cs

     
     1<?cs 
     2if !ticket.action ?><?cs 
     3  set:ticket.action = 'leave' ?><?cs 
     4/if ?><?cs 
     5def action_radio(id) ?> 
     6  <input type="radio" id="<?cs var id ?>" name="action" value="<?cs var id ?>" 
     7    <?cs if $ticket.action == $id ?> checked="checked"<?cs /if ?> /><?cs 
     8/def ?> 
     9 
     10<?cs 
     11if ticket.workflow.action.leave ?><?cs 
     12  call:action_radio('leave') ?> 
     13  <label for="leave">leave as <?cs var:ticket.status ?></label><br /><?cs 
     14/if ?><?cs 
     15if ticket.workflow.action.accept ?><?cs 
     16  call action_radio('accept') ?> 
     17  <label for="accept">accept ticket</label><br /><?cs 
     18/if ?><?cs 
     19if ticket.workflow.action.resolve ?><?cs 
     20  call:action_radio('resolve') ?> 
     21  <label for="resolve">resolve</label> 
     22  <label for="resolve_resolution">as:</label><?cs 
     23  call:hdf_select(enums.resolution, "resolve_resolution", 
     24                  args.resolve_resolution) ?><br /><?cs 
     25/if ?><?cs 
     26if ticket.workflow.action.reopen ?><?cs 
     27  call:action_radio('reopen') ?> 
     28  <label for="reopen">reopen ticket</label><br /><?cs 
     29/if ?><?cs 
     30if ticket.workflow.action.reassign ?><?cs 
     31  call:action_radio('reassign') ?> 
     32  <label for="reassign">reassign</label> 
     33  <label for="reassign_owner">to:</label> 
     34  <input type="text" id="reassign_owner" name="reassign_owner" size="40" 
     35    value=<?cs if args.reassign_to ?>"<?cs var:args.reassign_to ?>" 
     36          <?cs else ?>"<?cs var:trac.authname ?>" 
     37          <?cs /if ?> /><?cs 
     38/if ?> 
     39 
     40<?cs 
     41if ticket.workflow.action.resolve || ticket.workflow.action.reassign ?> 
     42  <script type="text/javascript"><?cs 
     43  if ticket.workflow.action.resolve ?> 
     44    var resolve = document.getElementById("resolve");<?cs 
     45  /if ?><?cs 
     46  if ticket.workflow.action.reassign ?> 
     47    var reassign = document.getElementById("reassign");<?cs 
     48  /if ?> 
     49    var updateActionFields = function() {<?cs 
     50  if ticket.workflow.action.resolve ?> 
     51      enableControl('resolve_resolution', resolve.checked);<?cs 
     52  /if ?><?cs 
     53  if ticket.workflow.action.reassign ?> 
     54      enableControl('reassign_owner', reassign.checked);<?cs 
     55  /if ?> 
     56    }; 
     57    addEvent(window, 'load', updateActionFields);<?cs 
     58  if ticket.workflow.action.leave ?> 
     59    addEvent(document.getElementById("leave"), 'click', updateActionFields);<?cs 
     60  /if ?><?cs 
     61  if ticket.workflow.action.accept ?> 
     62    addEvent(document.getElementById("accept"), 'click', updateActionFields);<?cs 
     63  /if ?><?cs 
     64  if ticket.workflow.action.resolve ?> 
     65    addEvent(resolve, 'click', updateActionFields);<?cs 
     66  /if ?><?cs 
     67  if ticket.workflow.action.reopen ?> 
     68    addEvent(document.getElementById("reopen"), 'click', updateActionFields);<?cs 
     69  /if ?><?cs 
     70  if ticket.workflow.action.reassign ?> 
     71    addEvent(reassign, 'click', updateActionFields);<?cs 
     72  /if ?> 
     73  </script> 
     74<?cs /if ?> 
  • templates/newticket.cs

     
    6969  </div><?cs /if ?> 
    7070 </fieldset> 
    7171 
     72 <?cs if newticket.workflow.template ?> 
     73  <fieldset id="action"> 
     74   <legend>Action</legend> 
     75   <?cs include newticket.workflow.template ?> 
     76  </fieldset> 
     77 <?cs /if ?> 
     78 
     79 <?cs if newticket.workflow.error ?> 
     80   <div class="system-message"> 
     81     <h2>Ticket Error</h2> 
     82     <p class="message"><?cs var newticket.workflow.error ?></p> 
     83     <strong>The ticket will not be created.</strong> 
     84   </div> 
     85 <?cs /if ?> 
     86 
    7287 <div class="buttons"> 
    73   <input type="submit" value="Preview" />&nbsp; 
     88  <input type="submit" name="preview" value="Preview" />&nbsp; 
    7489  <input type="submit" name="create" value="Submit ticket" /> 
    7590 </div> 
    7691</form>