Skip to content
This repository has been archived by the owner on Aug 14, 2023. It is now read-only.

Commit

Permalink
Merge pull request itota#16 from trac-hacks/more_config_opts
Browse files Browse the repository at this point in the history
Added more configuration options and made administration possible through the admin panel
  • Loading branch information
thenor57 authored Jan 11, 2017
2 parents aaa3fe7 + 8e06683 commit 2dde228
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 95 deletions.
5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

setup(
name = 'TracSubTicketsPlugin',
version = '0.4.1',
version = '0.5.0',
keywords = 'trac plugin ticket subticket',
author = 'Takashi Ito',
author_email = 'TakashiC.Ito@gmail.com',
Expand All @@ -43,10 +43,9 @@
long_description = """
This plugin for Trac 1.0 and later provides Sub-Tickets functionality.
The association is done by adding parent tickets number to a custom field.
The association is done by adding parent tickets' number to a custom field.
Checks ensure i.e. resolving of sub-tickets before closing the parent.
Babel is required to display localized texts.
Currently only translation for de_DE is provided.
""",
license = 'BSD',

Expand Down
125 changes: 70 additions & 55 deletions tracsubtickets/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import pkg_resources

from trac.config import Option, BoolOption, IntOption, ChoiceOption, ListOption
from trac.core import *
from trac.env import IEnvironmentSetupParticipant
from trac.db import DatabaseManager
Expand Down Expand Up @@ -59,6 +60,18 @@ class SubTicketsSystem(Component):
ITicketChangeListener,
ITicketManipulator)

opt_no_modif_w_p_c = BoolOption \
('subtickets', 'no_modif_when_parent_closed', default='false',
doc = _("""
If `True`, any modification of a child whose parent is `closed`
will be blocked. If `False`, status changes will be blocked as
controlled by the setting of `skip_closure_validation`.
For compatibility with plugin versions prior to 0.5 that blocked
any modification unconditionally.
""")
)

def __init__(self):
self._version = None
self.ui = None
Expand Down Expand Up @@ -191,62 +204,64 @@ def prepare_ticket(self, req, ticket, fields, actions):
pass

def validate_ticket(self, req, ticket):
with self.env.db_query as db:
cursor = db.cursor()

try:
invalid_ids = set()
_ids = set(NUMBERS_RE.findall(ticket['parents'] or ''))
myid = str(ticket.id)
for id in _ids:
if id == myid:
try:
invalid_ids = set()
_ids = set(NUMBERS_RE.findall(ticket['parents'] or ''))
myid = str(ticket.id)
for id in _ids:
if id == myid:
invalid_ids.add(id)
yield 'parents', _("A ticket cannot be a parent of itself")
else:
# check if the id exists
tkt_id = self.env.db_query("""
SELECT id FROM ticket WHERE id=%s
""", (id, ))
if not tkt_id:
invalid_ids.add(id)
yield 'parents', _("A ticket cannot be a parent of itself")
yield 'parents', _("Ticket #%(id)s does not exist",
id=id)

# circularity check function
def _check_parents(id, all_parents):
all_parents = all_parents + [id]
errors = []
parents = self.env.db_query("""
SELECT parent FROM subtickets WHERE child=%s
""", (id, ))
for x in [int(x[0]) for x in parents]:
if x in all_parents:
invalid_ids.add(x)
error = ' > '.join(['#%s' % n for n in all_parents+[x]])
errors.append(('parents', _('Circularity error: %(e)s',
e=error)))
else:
# check if the id exists
cursor.execute("""
SELECT id FROM ticket WHERE id=%s
""", (id, ))
row = cursor.fetchone()
if row is None:
invalid_ids.add(id)
yield 'parents', _("Ticket #%(id)s does not exist", id=id)

# circularity check function
def _check_parents(id, all_parents):
all_parents = all_parents + [id]
errors = []
cursor.execute("""
SELECT parent FROM subtickets WHERE child=%s
""", (id, ))
for x in [int(x[0]) for x in cursor]:
if x in all_parents:
invalid_ids.add(x)
error = ' > '.join(['#%s' % n for n in all_parents + [x]])
errors.append(('parents', _('Circularity error: %(e)s', e=error)))
else:
errors += _check_parents(x, all_parents)
return errors

for x in [i for i in _ids if i not in invalid_ids]:
# Refuse modification if parent closed or if parentship is circular
try:
parent = Ticket(self.env, x)
if parent and parent['status'] == 'closed' :
invalid_ids.add(x)
yield None, _("Cannot modify ticket because parent ticket #%(id)s is closed. Comments allowed, though.", id=x)
else:
# check circularity
all_parents = ticket.id and [ticket.id] or []
for error in _check_parents(int(x), all_parents):
yield error
except ResourceNotFound, e:
errors += _check_parents(x, all_parents)
return errors

for x in [i for i in _ids if i not in invalid_ids]:
# Refuse modification if parent closed
# or if parentship is to be made circular
try:
parent = Ticket(self.env, x)
if parent and parent['status'] == 'closed' \
and self.opt_no_modif_w_p_c:
invalid_ids.add(x)

valid_ids = _ids.difference(invalid_ids)
ticket['parents'] = valid_ids and ', '.join(sorted(valid_ids, key=lambda x: int(x))) or ''
except Exception, e:
import traceback
self.log.error(traceback.format_exc())
yield 'parents', _('Not a valid list of ticket IDs.')
yield None, _("""Cannot modify ticket because
parent ticket #%(id)s is closed.
Comments allowed, though.""",
id=x)
# check circularity
all_parents = ticket.id and [ticket.id] or []
for error in _check_parents(int(x), all_parents):
yield error
except ResourceNotFound, e:
invalid_ids.add(x)

valid_ids = _ids.difference(invalid_ids)
ticket['parents'] = valid_ids and ', '.join(sorted(valid_ids, key=lambda x: int(x))) or ''
except Exception, e:
import traceback
self.log.error(traceback.format_exc())
yield 'parents', _('Not a valid list of ticket IDs.')

Loading

0 comments on commit 2dde228

Please sign in to comment.