def render_tag_changes(old_tags, new_tags): old_tags = split_into_tags(old_tags or '') new_tags = split_into_tags(new_tags or '') added = sorted(new_tags - old_tags) added = added and \ tagn_("%(tags)s added", "%(tags)s added", len(added), tags=builder.em(', '.join(added))) removed = sorted(old_tags - new_tags) removed = removed and \ tagn_("%(tags)s removed", "%(tags)s removed", len(removed), tags=builder.em(', '.join(removed))) # TRANSLATOR: How to delimit added and removed tags. delim = added and removed and _("; ") return builder(builder.strong(_("Tags")), ' ', added, delim, removed)
def splitIntoTags(self, req, tag_str): """Returns a list of tags from a string. Comma, whitespace and combinations of these characters are recognized as delimiter, that get stripped from the output. """ return split_into_tags(tag_str)
def set_resource_tags(self, req, ticket_or_resource, tags, comment=u'', when=None): try: resource = ticket_or_resource.resource except AttributeError: resource = ticket_or_resource assert resource.realm == self.realm if not self._check_permission(req, resource, 'modify'): raise PermissionError(resource=resource, env=self.env) tag_set = set(tags) # Processing a call from TracTags, try to alter the ticket. tkt = Ticket(self.env, resource.id) all = self._ticket_tags(tkt) # Avoid unnecessary ticket changes, considering comments below. if tag_set != all: # Will only alter tags in 'keywords' ticket field. keywords = split_into_tags(tkt['keywords']) # Assume, that duplication is depreciated and consolitation # wanted to primarily affect 'keywords' ticket field. # Consequently updating ticket tags and reducing (tag) # 'ticket_fields' afterwards may result in undesired tag loss. tag_set.difference_update(all.difference(keywords)) tkt['keywords'] = u' '.join(sorted(map(to_unicode, tag_set))) tkt.save_changes(get_reporter_id(req), comment) else: # Processing a change listener event. tags = self._ticket_tags(ticket_or_resource) super(TicketTagProvider, self).set_resource_tags(req, resource, tags)
def resource_tags(env, resource, db=None, when=None): """Return all tags for a Trac resource by realm and ID.""" db = _get_db(env, db) cursor = db.cursor() id = to_unicode(resource.id) if when is None: cursor.execute(""" SELECT tag FROM tags WHERE tagspace=%s AND name=%s """, (resource.realm, id)) for row in cursor: yield row[0] else: if isinstance(when, datetime): when = to_utimestamp(when) cursor.execute("SELECT newtags FROM tags_change " "WHERE tagspace=%s AND name=%s AND time<=%s " "ORDER BY time DESC LIMIT 1", (resource.realm, id, when)) row = cursor.fetchone() if row and row[0]: for tag in split_into_tags(row[0]): yield tag
def resource_tags(env, resource, db=None, when=None): """Return all tags for a Trac resource by realm and ID.""" db = _get_db(env, db) cursor = db.cursor() id = to_unicode(resource.id) if when is None: cursor.execute( """ SELECT tag FROM tags WHERE tagspace=%s AND name=%s """, (resource.realm, id)) for row in cursor: yield row[0] else: if isinstance(when, datetime): when = to_utimestamp(when) cursor.execute( "SELECT newtags FROM tags_change " "WHERE tagspace=%s AND name=%s AND time<=%s " "ORDER BY time DESC LIMIT 1", (resource.realm, id, when)) row = cursor.fetchone() if row and row[0]: for tag in split_into_tags(row[0]): yield tag
def _update_tags(self, req, page, when=None): newtags = split_into_tags(req.args.get('tags', '')) oldtags = self.tag_system.get_tags(req, page.resource) if oldtags != newtags: self.tag_system.set_tags(req, page.resource, newtags, when=when) return True return False
def _post_process_request_history(self, req, data): history = [] page_histories = data.get('history', []) resource = data['resource'] tags_histories = tag_changes(self.env, resource) for page_history in page_histories: while tags_histories and \ tags_histories[0][0] >= page_history['date']: tags_history = tags_histories.pop(0) date = tags_history[0] author = tags_history[1] old_tags = split_into_tags(tags_history[2] or '') new_tags = split_into_tags(tags_history[3] or '') added = sorted(new_tags - old_tags) added = added and \ tagn_("%(tags)s added", "%(tags)s added", len(added), tags=tag.em(', '.join(added))) removed = sorted(old_tags - new_tags) removed = removed and \ tagn_("%(tags)s removed", "%(tags)s removed", len(removed), tags=tag.em(', '.join(removed))) # TRANSLATOR: How to delimit added and removed tags. delim = added and removed and _("; ") comment = tag(tag.strong(_("Tags")), ' ', added, delim, removed) url = req.href(resource.realm, resource.id, version=page_history['version'], tags_version=to_utimestamp(date)) history.append({'version': '*', 'url': url, 'date': date, 'author': author, 'comment': comment, 'ipnr': ''}) history.append(page_history) data.update(dict(history=history, wiki_to_oneliner=self._wiki_to_oneliner))
def _fetch_tkt_tags(self, db): """Transfer all relevant ticket attributes to tags db table.""" # Initial sync is done by forced, stupid one-way mirroring. # Data aquisition for this utilizes the known ticket tags query. fields = ["COALESCE(%s, '')" % f for f in self.fields] ignore = '' if self.ignore_closed_tickets: ignore = " AND status != 'closed'" sql = """ SELECT * FROM (SELECT id, %s, %s AS std_fields FROM ticket AS tkt WHERE NOT EXISTS (SELECT * FROM tags WHERE tagspace=%%s AND name=%s) %s) AS s WHERE std_fields != '' ORDER BY id """ % (','.join(self.fields), db.concat(*fields), db.cast('tkt.id', 'text'), ignore) self.env.log.debug(sql) # Obtain cursors for reading tickets and altering tags db table. # DEVEL: Use appropriate cursor typs from Trac 1.0 db API. ro_cursor = db.cursor() rw_cursor = db.cursor() # Delete tags for non-existent ticket rw_cursor.execute( """ DELETE FROM tags WHERE tagspace=%%s AND NOT EXISTS (SELECT * FROM ticket AS tkt WHERE tkt.id=%s%s) """ % (db.cast('tags.name', 'int'), ignore), (self.realm, )) self.log.debug('ENTER_TAG_DB_CHECKOUT') ro_cursor.execute(sql, (self.realm, )) self.log.debug('EXIT_TAG_DB_CHECKOUT') self.log.debug('ENTER_TAG_SYNC') for row in ro_cursor: tkt_id, ttags = row[0], ' '.join([f for f in row[1:-1] if f]) ticket_tags = split_into_tags(ttags) rw_cursor.executemany( """ INSERT INTO tags (tagspace, name, tag) VALUES (%s, %s, %s) """, [(self.realm, str(tkt_id), tag) for tag in ticket_tags]) self.log.debug('EXIT_TAG_SYNC')
def _fetch_tkt_tags(self, db): """Transfer all relevant ticket attributes to tags db table.""" # Initial sync is done by forced, stupid one-way mirroring. # Data aquisition for this utilizes the known ticket tags query. fields = ["COALESCE(%s, '')" % f for f in self.fields] ignore = '' if self.ignore_closed_tickets: ignore = " AND status != 'closed'" sql = """ SELECT * FROM (SELECT id, %s, %s AS std_fields FROM ticket AS tkt WHERE NOT EXISTS (SELECT * FROM tags WHERE tagspace=%%s AND name=%s) %s) AS s WHERE std_fields != '' ORDER BY id """ % (','.join(self.fields), db.concat(*fields), db.cast('tkt.id', 'text'), ignore) # Obtain cursors for reading tickets and altering tags db table. # DEVEL: Use appropriate cursor typs from Trac 1.0 db API. ro_cursor = db.cursor() rw_cursor = db.cursor() # Delete tags for non-existent ticket rw_cursor.execute(""" DELETE FROM tags WHERE tagspace=%%s AND NOT EXISTS (SELECT * FROM ticket AS tkt WHERE tkt.id=%s%s) """ % (db.cast('tags.name', 'int'), ignore), (self.realm,)) self.log.debug('ENTER_TAG_DB_CHECKOUT') ro_cursor.execute(sql, (self.realm,)) self.log.debug('EXIT_TAG_DB_CHECKOUT') self.log.debug('ENTER_TAG_SYNC') for row in ro_cursor: tkt_id, ttags = row[0], ' '.join([f for f in row[1:-1] if f]) ticket_tags = split_into_tags(ttags) rw_cursor.executemany(""" INSERT INTO tags (tagspace, name, tag) VALUES (%s, %s, %s) """, [(self.realm, str(tkt_id), tag) for tag in ticket_tags]) self.log.debug('EXIT_TAG_SYNC')
def resource_tags(env, resource, when=None): """Return all tags for a Trac resource by realm and ID.""" id = to_unicode(resource.id) if when is None: for tag, in env.db_query(""" SELECT tag FROM tags WHERE tagspace=%s AND name=%s """, (resource.realm, id)): yield tag else: if isinstance(when, datetime): when = to_utimestamp(when) for newtags, in env.db_query(""" SELECT newtags FROM tags_change WHERE tagspace=%s AND name=%s AND time<=%s ORDER BY time DESC LIMIT 1 """, (resource.realm, id, when)): for tag in split_into_tags(newtags): yield tag
def _ticket_tags(self, ticket): return split_into_tags(' '.join( filter(None, [ticket[f] for f in self.fields])))
def _ticket_tags(self, ticket): return split_into_tags( ' '.join(filter(None, [ticket[f] for f in self.fields])))