source: watchlistplugin/0.12/tracwatchlist/ticket.py

Last change on this file was 15264, checked in by Ryan J Ollos, 8 years ago

Remove unnecessary svn:mime-type on py files

svn:mime-type was set to "plain" for many files.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Id Date Author Rev URL
File size: 14.2 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3= Watchlist Plugin for Trac =
4Plugin Website:  http://trac-hacks.org/wiki/WatchlistPlugin
5Trac website:    http://trac.edgewall.org/
6
7Copyright (c) 2008-2010 by Martin Scharrer <martin@scharrer-online.de>
8All rights reserved.
9
10The i18n support was added by Steffen Hoffmann <hoff.st@web.de>.
11
12This program is free software: you can redistribute it and/or modify
13it under the terms of the GNU General Public License as published by
14the Free Software Foundation, either version 3 of the License, or
15(at your option) any later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20GNU General Public License for more details.
21
22For a copy of the GNU General Public License see
23<http://www.gnu.org/licenses/>.
24
25$Id: ticket.py 15264 2016-02-11 04:22:34Z rjollos $
26"""
27
28__url__      = ur"$URL: //trac-hacks.org/svn/watchlistplugin/0.12/tracwatchlist/ticket.py $"[6:-2]
29__author__   = ur"$Author: rjollos $"[9:-2]
30__revision__ = int("0" + ur"$Rev: 15264 $"[6:-2].strip('M'))
31__date__     = ur"$Date: 2016-02-11 04:22:34 +0000 (Thu, 11 Feb 2016) $"[7:-2]
32
33from  trac.core              import  *
34from  genshi.builder         import  tag
35from  trac.ticket.model      import  Ticket
36from  trac.ticket.api        import  TicketSystem
37from  trac.util.datefmt      import  pretty_timedelta, \
38                                     datetime, utc, to_timestamp
39from  trac.util.text         import  to_unicode, obfuscate_email_address
40from  trac.wiki.formatter    import  format_to_oneliner
41from  trac.mimeview.api      import  Context
42from  trac.web.chrome        import  Chrome
43from  trac.resource          import  Resource
44from  trac.attachment        import  Attachment
45
46from  trac.util.datefmt      import  format_datetime as trac_format_datetime
47
48from  tracwatchlist.api      import  BasicWatchlist
49from  tracwatchlist.translation import  add_domain, _, N_, T_, t_, tag_, ngettext
50from  tracwatchlist.render   import  render_property_diff
51from  tracwatchlist.util     import  moreless, format_datetime, LC_TIME,\
52                                     decode_range_sql
53
54
55class TicketWatchlist(BasicWatchlist):
56    """Watchlist entry for tickets."""
57    realms = ['ticket']
58    fields = {'ticket':{
59        'author'    : T_("Author"),
60        'changes'   : N_("Changes"),
61        # TRANSLATOR: '#' stands for 'number'.
62        # This is the header label for a column showing the number
63        # of the latest comment.
64        'commentnum': N_("Comment #"),
65        'unwatch'   : N_("U"),
66        'notify'    : N_("Notify"),
67        'comment'   : T_("Comment"),
68        'attachment': T_("Attachments"),
69        # Plus further pairs imported at __init__.
70    }}
71
72    default_fields = {'ticket':[
73        'id', 'changetime', 'author', 'changes', 'commentnum',
74        'unwatch', 'notify', 'comment',
75    ]}
76    sort_key = {'ticket':int}
77
78    tagsystem = None
79
80    def __init__(self):
81        try: # Only works for Trac 0.12, but is not needed for Trac 0.11 anyway
82            self.fields['ticket'].update( self.env[TicketSystem].get_ticket_field_labels() )
83        except (KeyError, AttributeError):
84            pass
85        self.fields['ticket']['id'] = self.get_realm_label('ticket')
86
87        try: # Try to support the Tags Plugin
88            from tractags.api import TagSystem
89            self.tagsystem = self.env[TagSystem]
90        except ImportError, e:
91            pass
92        else:
93            if self.tagsystem:
94                self.fields['ticket']['tags'] = _("Tags")
95
96
97    def get_realm_label(self, realm, n_plural=1, astitle=False):
98        if astitle:
99            # TRANSLATOR: 'ticket(s)' as title
100            return ngettext("Ticket", "Tickets", n_plural)
101        else:
102            # TRANSLATOR: 'ticket(s)' inside a sentence
103            return ngettext("ticket", "tickets", n_plural)
104
105
106    def _get_sql(self, resids, fuzzy, var='id'):
107        if isinstance(resids,basestring):
108            sql = decode_range_sql( resids ) % {'var':var}
109            args = []
110        else:
111            args = resids
112            if (len(resids) == 1):
113                sql = ' ' + var + '=%s '
114            else:
115                sql = ' ' + var + ' IN (' + ','.join(('%s',) * len(resids)) + ') '
116        return sql, args
117
118
119    def resources_exists(self, realm, resids, fuzzy=0):
120        if not resids:
121            return []
122        sql, args = self._get_sql(resids, fuzzy)
123        if not sql:
124            return []
125        db = self.env.get_db_cnx()
126        cursor = db.cursor()
127        cursor.execute("""
128            SELECT id
129            FROM ticket
130            WHERE
131        """ + sql, args)
132        return [ unicode(v[0]) for v in cursor.fetchall() ]
133
134
135    def watched_resources(self, realm, resids, user, wl, fuzzy=0):
136        if not resids:
137            return []
138        sql, args = self._get_sql(resids, fuzzy, 'CAST(resid AS decimal)')
139        if not sql:
140            return []
141        db = self.env.get_db_cnx()
142        cursor = db.cursor()
143        cursor.log = self.log
144        cursor.execute("""
145            SELECT resid
146            FROM watchlist
147            WHERE wluser=%s AND realm='ticket' AND (
148        """ + sql + " )", [user] + args)
149        return [ unicode(v[0]) for v in cursor.fetchall() ]
150
151
152    def unwatched_resources(self, realm, resids, user, wl, fuzzy=0):
153        if not resids:
154            return []
155        sql, args = self._get_sql(resids, fuzzy)
156        if not sql:
157            return []
158        db = self.env.get_db_cnx()
159        cursor = db.cursor()
160        cursor.log = self.log
161        cursor.execute("""
162            SELECT id
163            FROM ticket
164            WHERE id NOT in (
165                SELECT CAST(resid as decimal)
166                FROM watchlist
167                WHERE wluser=%s AND realm='ticket'
168            ) AND (
169        """ + sql + " )", [user] + args)
170        return [ unicode(v[0]) for v in cursor.fetchall() ]
171
172
173    def get_list(self, realm, wl, req, fields=None):
174        db = self.env.get_db_cnx()
175        cursor = db.cursor()
176        context = Context.from_request(req)
177        locale = getattr(req, 'locale', None) or LC_TIME
178
179        ticketlist = []
180        extradict = {}
181        if not fields:
182            fields = set(self.default_fields['ticket'])
183        else:
184            fields = set(fields)
185
186        if 'changetime' in fields:
187            max_changetime = datetime(1970,1,1,tzinfo=utc)
188            min_changetime = datetime.now(utc)
189        if 'time' in fields:
190            max_time = datetime(1970,1,1,tzinfo=utc)
191            min_time = datetime.now(utc)
192
193
194        for sid,last_visit in wl.get_watched_resources( 'ticket', req.authname ):
195            ticketdict = {}
196            try:
197                ticket = Ticket(self.env, sid, db)
198                exists = ticket.exists
199            except:
200                exists = False
201
202            if not exists:
203                ticketdict['deleted'] = True
204                if 'id' in fields:
205                    ticketdict['id'] = sid
206                    ticketdict['ID'] = '#' + sid
207                if 'author' in fields:
208                    ticketdict['author'] = '?'
209                if 'changetime' in fields:
210                    ticketdict['changedsincelastvisit'] = 1
211                    ticketdict['changetime'] = '?'
212                    ticketdict['ichangetime'] = 0
213                if 'time' in fields:
214                    ticketdict['time'] = '?'
215                    ticketdict['itime'] = 0
216                if 'comment' in fields:
217                    ticketdict['comment'] = tag.strong(t_("deleted"), class_='deleted')
218                if 'notify' in fields:
219                    ticketdict['notify'] =  wl.is_notify(req, 'ticket', sid)
220                if 'description' in fields:
221                    ticketdict['description'] = ''
222                if 'owner' in fields:
223                    ticketdict['owner'] = ''
224                if 'reporter' in fields:
225                    ticketdict['reporter'] = ''
226                ticketlist.append(ticketdict)
227                continue
228
229            render_elt = lambda x: x
230            if not (Chrome(self.env).show_email_addresses or \
231                    'EMAIL_VIEW' in req.perm(ticket.resource)):
232                render_elt = obfuscate_email_address
233
234            # Copy all requested fields from ticket
235            if fields:
236                for f in fields:
237                    ticketdict[f] = ticket.values.get(f,u'')
238            else:
239                ticketdict = ticket.values.copy()
240
241            changetime = ticket.time_changed
242            if wl.options['attachment_changes']:
243                for attachment in Attachment.select(self.env, 'ticket', sid, db):
244                    if attachment.date > changetime:
245                        changetime = attachment.date
246            if 'attachment' in fields:
247                attachments = []
248                for attachment in Attachment.select(self.env, 'ticket', sid, db):
249                    wikitext = u'[attachment:"' + u':'.join([attachment.filename,'ticket',sid]) + u'" ' + attachment.filename  + u']'
250                    attachments.extend([tag(', '), format_to_oneliner(self.env, context, wikitext, shorten=False)])
251                if attachments:
252                    attachments.reverse()
253                    attachments.pop()
254                ticketdict['attachment'] = moreless(attachments, 5)
255
256            # Changes are special. Comment, commentnum and last author are included in them.
257            if 'changes' in fields or 'author' in fields or 'comment' in fields or 'commentnum' in fields:
258                changes = []
259                # If there are now changes the reporter is the last author
260                author  = ticket.values['reporter']
261                commentnum = u"0"
262                comment = u""
263                want_changes = 'changes' in fields
264                for date,cauthor,field,oldvalue,newvalue,permanent in ticket.get_changelog(changetime,db):
265                    author = cauthor
266                    if field == 'comment':
267                        if 'commentnum' in fields:
268                            ticketdict['commentnum'] = to_unicode(oldvalue)
269                        if 'comment' in fields:
270                            comment = to_unicode(newvalue)
271                            comment = moreless(comment, 200)
272                            ticketdict['comment'] = comment
273                        if not want_changes:
274                            break
275                    else:
276                        if want_changes:
277                            label = self.fields['ticket'].get(field,u'')
278                            if label:
279                                changes.extend(
280                                    [ tag(tag.strong(label), ' ',
281                                        render_property_diff(self.env, req, ticket, field, oldvalue, newvalue)
282                                        ), tag('; ') ])
283                if want_changes:
284                    # Remove the last tag('; '):
285                    if changes:
286                        changes.pop()
287                    changes = moreless(changes, 5)
288                    ticketdict['changes'] = tag(changes)
289
290            if 'id' in fields:
291                ticketdict['id'] = sid
292                ticketdict['ID'] = format_to_oneliner(self.env, context, '#' + sid, shorten=True)
293            if 'cc' in fields:
294                if render_elt == obfuscate_email_address:
295                    ticketdict['cc'] = ', '.join([ render_elt(c) for c in ticketdict['cc'].split(', ') ])
296            if 'author' in fields:
297                ticketdict['author'] = render_elt(author)
298            if 'changetime' in fields:
299                ichangetime = to_timestamp( changetime )
300                ticketdict.update(
301                    changetime       = format_datetime( changetime, locale=locale, tzinfo=req.tz ),
302                    ichangetime      = ichangetime,
303                    changedsincelastvisit = (last_visit < ichangetime and 1 or 0),
304                    changetime_delta = pretty_timedelta( changetime ),
305                    changetime_link  = req.href.timeline(precision='seconds',
306                                       from_=trac_format_datetime ( changetime, 'iso8601', tzinfo=req.tz)))
307                if changetime > max_changetime:
308                    max_changetime = changetime
309                if changetime < min_changetime:
310                    min_changetime = changetime
311            if 'time' in fields:
312                time = ticket.time_created
313                ticketdict.update(
314                    time             = format_datetime( time, locale=locale, tzinfo=req.tz ),
315                    itime            = to_timestamp( time ),
316                    time_delta       = pretty_timedelta( time ),
317                    time_link        = req.href.timeline(precision='seconds',
318                                       from_=trac_format_datetime ( time, 'iso8601', tzinfo=req.tz )))
319                if time > max_time:
320                    max_time = time
321                if time < min_time:
322                    min_time = time
323            if 'description' in fields:
324                description = ticket.values['description']
325                description = moreless(description, 200)
326                ticketdict['description'] = description
327            if 'notify' in fields:
328                ticketdict['notify'] = wl.is_notify(req, 'ticket', sid)
329            if 'owner' in fields:
330                ticketdict['owner'] = render_elt(ticket.values['owner'])
331            if 'reporter' in fields:
332                ticketdict['reporter'] = render_elt(ticket.values['reporter'])
333            if 'tags' in fields and self.tagsystem:
334                tags = []
335                for t in self.tagsystem.get_tags(req, Resource('ticket', sid)):
336                    tags.extend([tag.a(t,href=req.href('tags',q=t)), tag(', ')])
337                if tags:
338                    tags.pop()
339                ticketdict['tags'] = moreless(tags, 10)
340
341            ticketlist.append(ticketdict)
342
343        if 'changetime' in fields:
344            extradict['max_changetime'] = format_datetime( max_changetime, locale=locale, tzinfo=req.tz )
345            extradict['min_changetime'] = format_datetime( min_changetime, locale=locale, tzinfo=req.tz )
346        if 'time' in fields:
347            extradict['max_time'] = format_datetime( max_time, locale=locale, tzinfo=req.tz )
348            extradict['min_time'] = format_datetime( min_time, locale=locale, tzinfo=req.tz )
349
350        return ticketlist, extradict
351
352_EXTRA_STRINGS = [ _("%(value)s added") ]
353
354# EOF
Note: See TracBrowser for help on using the repository browser.