示例#1
0
    def screenshot_deleted(self, req, screenshot):
        # Create temporary resource.
        resource = Resource('screenshots', to_unicode(screenshot['id']))

        # Delete tags of screenshot.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(req, resource)
示例#2
0
    def expand_macro(self, formatter, name, args):
        req = formatter.req
        add_stylesheet(req, 'hacks/css/trachacks.css')

        tag_system = TagSystem(self.env)
        releases = natural_sort([r.id for r, _ in
                                 tag_system.query(req, 'realm:wiki release')])

        def link(resource):
            return render_resource_link(self.env, formatter.context,
                                        resource, 'compact')

        dl = builder.dl(class_='tracreleasesmacro')
        for release in releases:
            page = WikiPage(self.env, release)
            match = self.title_extract.search(page.text)
            if match:
                rel_title = '%s' % match.group(1).strip()
            else:
                rel_title = '%s' % release

            dl(builder.dt(link(Resource('wiki', release))))
            dl(builder.dd(wiki_to_html(rel_title, self.env, req)))

        return dl
示例#3
0
    def expand_macro(self, formatter, name, args):
        req = formatter.req
        add_stylesheet(req, 'hacks/css/trachacks.css')

        tag_system = TagSystem(self.env)

        categories = natural_sort([r.id for r, _ in
                                 tag_system.query(req, 'realm:wiki type')])

        def link(resource):
            return render_resource_link(self.env, formatter.context,
                                        resource, 'compact')

        dl = builder.dl(class_='hacktypesmacro')
        for category in categories:
            page = WikiPage(self.env, category)
            match = self.title_extract.search(page.text)
            if match:
                cat_title = '%s' % match.group(1).strip()
                cat_body = self.title_extract.sub('', page.text, 1)
            else:
                cat_title = '%s' % category
                cat_body = page.text
            cat_body = self.self_extract.sub('', cat_body).strip()
            dl(builder.dt(link(Resource('wiki', category))))
            dl(builder.dd(wiki_to_html(cat_body, self.env, req)))

        return dl
示例#4
0
    def render_admin_panel(self, req, cat, page, version):
        req.perm.require("TAGS_ADMIN")
        data = {}
        tag_system = TagSystem(self.env)
        if req.method == "POST":
            # Replace Tag
            allow_delete = req.args.get("allow_delete")
            new_tag = req.args.get("tag_new_name").strip()
            new_tag = not new_tag == u"" and new_tag or None
            if not (allow_delete or new_tag):
                data["error"] = _(
                    """Selected current tag(s) and either
                                  new tag or delete approval are required"""
                )
            else:
                comment = req.args.get("comment", u"")
                old_tags = req.args.get("tag_name")
                if old_tags:
                    # Provide list regardless of single or multiple selection.
                    old_tags = isinstance(old_tags, list) and old_tags or [old_tags]
                    tag_system.replace_tag(req, old_tags, new_tag, comment, allow_delete)
                data["selected"] = new_tag

        all_tags = sorted(tag_system.get_all_tags(req, "-dummy"))
        data["tags"] = all_tags
        try:
            Chrome(self.env).add_textarea_grips(req)
        except AttributeError:
            # Element modifiers unavailable before Trac 0.12, skip gracefully.
            pass
        return "admin_tag_change.html", data
示例#5
0
    def _page_tags(self, req):
        pagename = req.args.get('page', 'WikiStart')

        tag_system = TagSystem(self.env)
        resource = Resource('wiki', pagename)
        tags = sorted(tag_system.get_tags(req, resource))
        return tags
示例#6
0
    def process_request(self, req):
        """Process the request. For ClearSilver, return a (template_name,
        content_type) tuple, where `template` is the ClearSilver template to use
        (either a `neo_cs.CS` object, or the file name of the template), and
        `content_type` is the MIME type of the content. For Genshi, return a
        (template_name, data, content_type) tuple, where `data` is a dictionary
        of substitutions for the template.

        For both templating systems, "text/html" is assumed if `content_type` is
        `None`.

        Note that if template processing should not occur, this method can
        simply send the response itself and not return anything.
        """
        query = req.args.get('q', '').lower()
        tagsystem = TagSystem(self.env)
        alltags = tagsystem.query(req)
        tags = {}
        for resource, _tags in alltags:
            for tag in _tags:
                if query in tag.lower():
                    tags[tag] = tags.setdefault(tag, 0) + 1

        tags = sorted(tags.items(), key=lambda x: x[1], reverse=True)
        writeOut=str('\n'.join(['%s|%d' % (name, number) for name, number in tags]))
        req.send_header('Content-length', str(len(writeOut)))
        req.end_headers() 
        req.write(writeOut)
示例#7
0
    def _update_tags(self, req, page):
        tag_system = TagSystem(self.env)
        newtags = tag_system.split_into_tags(req.args.get('tags', ''))
        oldtags = tag_system.get_tags(req, page.resource)

        if oldtags != newtags:
            tag_system.set_tags(req, page.resource, newtags)
            return True
        return False
def Main(opts):
    """ Cross your fingers and pray """
    env = Environment(opts.envpath)
    from tractags.api import TagSystem

    tlist = opts.tags or split_tags(env.config.get('blog', 'default_tag', 
                                                   'blog'))
    tags = TagSystem(env)
    req = Mock(perm=MockPerm())
    blog = tags.query(req, ' '.join(tlist + ['realm:wiki']))
                   
    cnx = env.get_db_cnx()
    for resource, page_tags in list(blog):
        try:
            page = WikiPage(env, version=1, name=resource.id)
            _, publish_time, author, _, _ =  page.get_history().next()
            if opts.deleteonly:
                page.delete()
                continue
            categories = ' '.join([t for t in page_tags if t not in tlist])
            page = WikiPage(env, name=resource.id)
            for version, version_time, version_author, version_comment, \
                _ in page.get_history():
                # Currently the basename of the post url is used due to 
                # http://trac-hacks.org/ticket/2956
                #name = resource.id.replace('/', '_')
                name = resource.id
                # extract title from text:
                fulltext = page.text
                match = _title_split_match(fulltext)
                if match:
                    title = match.group(1)
                    fulltext = match.group(2)
                else: 
                    title = name
                body = fulltext
                print "Adding post %s, v%s: %s" % (name, version, title)
                insert_blog_post(cnx, name, version, title, body,
                                 publish_time, version_time, 
                                 version_comment, version_author, author,
                                 categories)
                reparent_blog_attachments(env, resource.id, name)
                continue
            cnx.commit()
            if opts.delete:
                page.delete()
                continue
        except:
            env.log.debug("Error loading wiki page %s" % resource.id, 
                          exc_info=True)
            print "Failed to add post %s, v%s: %s" % (name, version, title)
            cnx.rollback()
            cnx.close()
            return 1
    cnx.close()
    return 0
示例#9
0
    def __call__(self, context, type_):
        if not (type_ and isinstance(type_, basestring) and len(type_)):
            raise ValidationError('No type selected?!')

        tags = TagSystem(self.env)
        req = FakeRequest(self.env)
        types = [r.id for r, _ in tags.query(req, 'realm:wiki type')]
        if type_ not in types:
            raise ValidationError('Selected type "%s" invalid?!' % str(type_))
        return type_
示例#10
0
    def screenshot_created(self, req, screenshot):
        # Create temporary resource.
        resource = Resource('screenshots', to_unicode(screenshot['id']))

        # Delete tags of screenshot with same ID for sure.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(req, resource)

        # Add tags of new screenshot.
        new_tags = self._get_tags(screenshot)
        tag_system.add_tags(req, resource, new_tags)
示例#11
0
    def download_deleted(self, req, download):
        # Check proper permissions to modify tags.
        if not req.perm.has_permission('TAGS_MODIFY'):
            return

        # Create temporary resource.
        resource = Resource('downloads', download['id'])

        # Delete tags of download.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(req, resource)
示例#12
0
    def user_created(self, user, password):
        req = FakeRequest(self.env, user)
        resource = Resource('wiki', user)
        tag_system = TagSystem(self.env)
        tag_system.add_tags(req, resource, ['user',])

        page = WikiPage(self.env, user)
        page.text = '''= %(user)s =\n\n[[ListTagged(%(user)s)]]\n''' % {'user' : user}
        page.save(user, 'New user %s registered' % user, None)

        self.env.log.debug("New user %s registered" % user)
示例#13
0
 def wiki_page_renamed(self, page, old_name):
     """Called when a page has been renamed (since Trac 0.12)."""
     new_resource = Resource('wiki', page.name)
     old_resource = Resource('wiki', old_name)
     self.log.debug("Moving tags from %s to %s",
                    old_resource.id, new_resource.id)
     tag_system = TagSystem(self.env)
     # XXX Ugh. Hopefully this will be sufficient to fool any endpoints.
     from trac.test import Mock, MockPerm
     req = Mock(authname='anonymous', perm=MockPerm())
     tag_system.reparent_tags(req, old_resource, new_resource)
示例#14
0
    def _update_tags(self, req, resource, new_tags):
        # Get old tags of the resource.
        tag_system = TagSystem(self.env)
        old_tags = self._get_stored_tags(req, resource)

        self.log.debug("setting tags: %s" % (new_tags,))

        # Replace with new tags if different.
        if old_tags != new_tags:
            tag_system.set_tags(req, resource, new_tags)
            return True
        return False
示例#15
0
    def screenshot_changed(self, req, screenshot, old_screenshot):
        # Update old screenshot with new values.
        old_screenshot.update(screenshot)

        # Create temporary resource.
        resource = Resource('screenshots', to_unicode(old_screenshot['id']))

        # Delete old tags.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(req, resource)

        # Add new ones.
        new_tags = self._get_tags(old_screenshot)
        tag_system.add_tags(req, resource, new_tags)
示例#16
0
    def post_process_request(self, req, template, data, content_type):
        if data and req.path_info == '/timeline' and \
                'TAGS_VIEW' in req.perm(Resource('tags')):

            def realm_handler(_, node, context):
                return query.match(node, [context.realm])

            query_str = req.args.get(self.key)
            if query_str is None and req.args.get('format') != 'rss':
                query_str = req.session.get('timeline.%s' % self.key)
            else:
                query_str = (query_str or '').strip()
                # Record tag query expression between visits.
                req.session['timeline.%s' % self.key] = query_str

            if data.get('events') and query_str:
                tag_system = TagSystem(self.env)
                try:
                    query = Query(query_str,
                                  attribute_handlers=dict(realm=realm_handler)
                            )
                except InvalidQuery, e:
                    add_warning(req, _("Tag query syntax error: %s"
                                       % to_unicode(e)))
                else:
                    all_realms = tag_system.get_taggable_realms(req.perm)
                    query_realms = set()
                    for m in REALM_RE.finditer(query.as_string()):
                        query_realms.add(m.group(1))
                    # Don't care about resources from non-taggable realms.
                    realms = not query_realms and all_realms or \
                             query_realms.intersection(all_realms)
                    events = []
                    self.log.debug("Filtering timeline events by tags '%s'",
                                   query_str)
                    for event in data['events']:
                        resource = event['data'][0]
                        if resource.realm in realms:
                            # Shortcut view permission checks here.
                            tags = tag_system.get_tags(None, resource)
                            if query(tags, context=resource):
                                events.append(event)
                    # Overwrite with filtered list.
                    data['events'] = events
            if query_str:
                # Add current value for next form rendering.
                data[self.key] = query_str
            elif self.key in req.session:
                del req.session[self.key]
示例#17
0
    def _get_keywords_string(self, req):
        keywords = set(self.sticky_tags_opt) # prevent duplicates
        if self.tags_enabled:
            # Use TagsPlugin >= 0.7 performance-enhanced API.
            tags = TagSystem(self.env).get_all_tags(req)
            keywords.update(tags.keys())

        if keywords:
            keywords = sorted(keywords)
            keywords = ','.join(("'%s'" % javascript_quote(_keyword)
                                 for _keyword in keywords))
        else:
            keywords = ''

        return keywords
示例#18
0
 def __call__(self, context, selected):
     if selected:
         tags = TagSystem(self.env)
         req = FakeRequest(self.env)
         releases = [r.id for r, _ in tags.query(req, 'realm:wiki release')]
         if isinstance(selected, (basestring, unicode)):
             selected = [ selected ]
         for s in selected:
             if s not in releases:
                 hack = context.data.get('name', '') + \
                        context.data.get('type', '').title()
                 self.env.log.error(
                     "Invalid release %s selected for new hack %s" % (s, hack)
                 )
                 raise ValidationError('Selected release "%s" invalid?!' % str(s))
     return selected
示例#19
0
    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        self.perms = PermissionSystem(self.env)

        self.db = self.env.get_db_cnx()
        setup = TagSetup(self.env)
        # Current tractags schema is setup with enabled component anyway.
        #   Revert these changes for getting default permissions inserted.
        self._revert_tractags_schema_init()
        setup.upgrade_environment(self.db)

        self.provider = TicketTagProvider(self.env)
        self.realm = 'ticket'
        self.tag_sys = TagSystem(self.env)
        self.tags = ['tag1']

        cursor = self.db.cursor()
        # Populate table with initial test data, not synced with tickets yet.
        cursor.execute("""
            INSERT INTO tags
                   (tagspace, name, tag)
            VALUES ('ticket', '1', 'deleted')""")
        self.realm = 'ticket'
        self._create_ticket(self.tags)

        self.req = Mock()
        # Mock an anonymous request.
        self.req.perm = PermissionCache(self.env)
示例#20
0
    def download_created(self, context, download):
        # Check proper permissions to modify tags.
        if not context.req.perm.has_permission('TAGS_MODIFY'):
            return

        # Create temporary resource.
        resource = Resource(self.realm, download['id'])

        # Delete tags of download with same ID for sure.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(context.req, resource)

        # Add tags of new download.
        new_tags = self._get_tags(download)
        self.log.debug('tags: %s' % (new_tags,))
        tag_system.add_tags(context.req, resource, new_tags)
示例#21
0
    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        self.db = self.env.get_db_cnx()
        setup = TagSetup(self.env)
        # Current tractags schema is partially setup with enabled component.
        #   Revert these changes for getting a clean setup.
        self._revert_tractags_schema_init()
        setup.upgrade_environment(self.db)

        self.perms = PermissionSystem(self.env)
        self.tag_s = TagSystem(self.env)
        self.tag_wp = WikiTagProvider(self.env)

        cursor = self.db.cursor()
        # Populate table with initial test data.
        cursor.execute("""
            INSERT INTO tags
                   (tagspace, name, tag)
            VALUES ('wiki', 'WikiStart', 'tag1')
        """)

        self.req = Mock(authname='editor')
        # Mock an anonymous request.
        self.req.perm = PermissionCache(self.env)
        self.realm = 'wiki'
        self.tags = ['tag1']
示例#22
0
    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        self.perms = PermissionSystem(self.env)

        self.db = self.env.get_db_cnx()
        setup = TagSetup(self.env)
        # Current tractags schema is setup with enabled component anyway.
        #   Revert these changes for getting default permissions inserted.
        self._revert_tractags_schema_init()
        setup.upgrade_environment(self.db)

        self.provider = TicketTagProvider(self.env)
        self.realm = 'ticket'
        self.tag_sys = TagSystem(self.env)
        self.tags = ['tag1', 'tag2']

        cursor = self.db.cursor()
        # Populate tables with initial test data.
        self._create_ticket(self.tags)

        # Mock an anonymous request.
        self.anon_req = Mock()
        self.anon_req.perm = PermissionCache(self.env)

        self.req = Mock(authname='editor')
        self.req.authname = 'editor'
        self.req.perm = PermissionCache(self.env, username='******')
示例#23
0
    def download_created(self, req, download):
        # Check proper permissions to modify tags.
        if not req.perm.has_permission('TAGS_MODIFY'):
            return

        # Create temporary resource.
        resource = Resource()
        resource.realm = 'downloads'
        resource.id = download['id']

        # Delete tags of download with same ID for sure.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(req, resource)

        # Add tags of new download.
        new_tags = self._get_tags(download)
        tag_system.add_tags(req, resource, new_tags)
示例#24
0
    def render_admin_panel(self, req, cat, page, version):
        req.perm.require('TAGS_ADMIN')

        tag_system = TagSystem(self.env)
        all_realms = tag_system.get_taggable_realms(req.perm)
        # Check request for enabled filters, or use default.
        if not [r for r in all_realms if r in req.args]:
            for realm in all_realms:
                req.args[realm] = 'on'
        checked_realms = [r for r in all_realms if r in req.args]
        data = dict(checked_realms=checked_realms,
                    tag_realms=list(dict(name=realm,
                                         checked=realm in checked_realms)
                                    for realm in all_realms))

        if req.method == 'POST':
            # Replace Tag
            allow_delete = req.args.get('allow_delete')
            new_tag = req.args.get('tag_new_name').strip()
            new_tag = not new_tag == u'' and new_tag or None
            if not (allow_delete or new_tag):
                data['error'] = _("Selected current tag(s) and either "
                                  "new tag or delete approval are required")
            else:
                comment = req.args.get('comment', u'')
                old_tags = req.args.get('tag_name')
                if old_tags:
                    # Provide list regardless of single or multiple selection.
                    old_tags = isinstance(old_tags, list) and old_tags or \
                               [old_tags]
                    tag_system.replace_tag(req, old_tags, new_tag, comment,
                                           allow_delete, filter=checked_realms)
                data['selected'] = new_tag

        query = ' or '.join(['realm:%s' % r for r in checked_realms])
        all_tags = sorted(tag_system.get_all_tags(req, query))
        data['tags'] = all_tags
        try:
            Chrome(self.env).add_textarea_grips(req)
        except AttributeError:
            # Element modifiers unavailable before Trac 0.12, skip gracefully.
            pass
        return 'admin_tag_change.html', data
示例#25
0
 def post_process_request(self, req, template, data, content_type):
     if req.method == 'GET' and req.path_info.startswith('/wiki/') and \
             req.args.get('action') == 'edit' and \
             req.args.get('template') and 'tags' not in req.args:
         # Retrieve template resource to be queried for tags.
         template_page = WikiPage(self.env,''.join(
                                  [WikiModule.PAGE_TEMPLATES_PREFIX,
                                   req.args.get('template')]))
         if template_page and template_page.exists and \
                 'TAGS_VIEW' in req.perm(template_page.resource):
             ts = TagSystem(self.env)
             tags = sorted(ts.get_tags(req, template_page.resource))
             # Prepare tags as content for the editor field.
             tags_str = ' '.join(tags)
             self.env.log.debug("Tags retrieved from template: '%s'" \
                                % unicode(tags_str).encode('utf-8'))
             # DEVEL: More arguments need to be propagated here?
             req.redirect(req.href(req.path_info,
                                   action='edit', tags=tags_str,
                                   template=req.args.get('template')))
     return (template, data, content_type)
示例#26
0
    def process_request(self, req):
        data = {}
        tag_system = TagSystem(self.env)

        #match = self.path_match.match(req.path_info)
        #view = 'cloud'
        #if match.group(1):
        #    view = match.group(1)

        # Hack types and their description
        types = []
        for category in sorted([r.id for r, _ in
                             tag_system.query(req, 'realm:wiki type')]):
            page = WikiPage(self.env, category)
            match = self.title_extract.search(page.text)
            if match:
                title = '%s' % match.group(1).strip()
            else:
                title = '%s' % category
            types.append((category, title))

        # Trac releases
        releases = natural_sort([r.id for r, _ in
                                 tag_system.query(req, 'realm:wiki release')])

        data['types'] = types
        data['releases'] = releases

        selected_releases = req.args.get('release', set(['0.10', '0.11', 'anyrelease']))
        data['selected_releases'] = selected_releases

        hacks = self.fetch_hacks(req, data, [ t[0] for t in types ], selected_releases)

        add_stylesheet(req, 'tags/css/tractags.css')
        add_stylesheet(req, 'hacks/css/trachacks.css')
        add_script(req, 'hacks/js/trachacks.js')

        if req.path_info == '/newhack':
            return self.render_new(req, data, hacks)
示例#27
0
    def download_changed(self, req, download, old_download):
        # Check proper permissions to modify tags.
        if not req.perm.has_permission('TAGS_MODIFY'):
            return

        # Check if tags has to be updated.
        if not self._has_tags_changed(download):
            return

        # Update old download with new values.
        old_download.update(download)

        # Create temporary resource.
        resource = Resource('downloads', old_download['id'])

        # Delete old tags.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(req, resource)

        # Add new ones.
        new_tags = self._get_tags(old_download)
        tag_system.add_tags(req, resource, new_tags)
示例#28
0
文件: api.py 项目: nyuhuhuu/trachacks
    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        self.perms = PermissionSystem(self.env)
        self.req = Mock()

        self.actions = ['TAGS_ADMIN', 'TAGS_MODIFY', 'TAGS_VIEW']
        self.tag_s = TagSystem(self.env)
        self.db = self.env.get_db_cnx()
        setup = TagSetup(self.env)
        # Current tractags schema is setup with enabled component anyway.
        #   Revert these changes for getting default permissions inserted.
        self._revert_tractags_schema_init()
        setup.upgrade_environment(self.db)
示例#29
0
    def __init__(self, env):
        if self._initialized:
            self._log('already initialized')
            return
        else:
            env.log.debug('HacksCache %x: initializing' % id(self.__dict__))

        self._lock.acquire()

        self._env = env
        self._tag_system = TagSystem(env)
        self._req = FakeRequest(env)
        self.id = id(self.__dict__)

        self._rebuild_cache()

        self._initialized = True
        self._lock.release()
        self._log('initialization finished')
示例#30
0
    def expand_macro(self, formatter, name, content):
        req = formatter.req
        query_result = TagSystem(self.env).query(req, content)

        def link(resource):
            return render_resource_link(self.env, formatter.context, resource,
                                        'compact')

        ul = builder.ul(class_='taglist')
        # Beware: Resources can be Unicode strings.
        for resource, tags in sorted(query_result, key=lambda r: r[0].id):
            tags = sorted(tags)
            if tags:
                rendered_tags = [link(resource('tag', tag)) for tag in tags]
                li = builder.li(link(resource), ' (', rendered_tags[0],
                                [(' ', tag) for tag in rendered_tags[1:]], ')')
            else:
                li = builder.li(link(resource))
            ul(li, '\n')
        return ul
示例#31
0
    def download_created(self, context, download):
        # Check proper permissions to modify tags.
        if not context.req.perm.has_permission('TAGS_MODIFY'):
            return

        # Create temporary resource.
        resource = Resource(self.realm, download['id'])

        # Delete tags of download with same ID for sure.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(context.req, resource)

        # Add tags of new download.
        new_tags = self._get_tags(download)
        self.log.debug('tags: %s' % (new_tags, ))
        tag_system.add_tags(context.req, resource, new_tags)
示例#32
0
    def _fetch_tkt_tags(self):
        """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.
        db = self.env.get_db_cnx()
        fields = ["COALESCE(%s, '')" % f for f in self.fields]
        ignore = ''
        if self.ignore_closed_tickets:
            ignore = " WHERE status != 'closed'"
        sql = """
            SELECT *
              FROM (SELECT id, %s, %s AS std_fields
                      FROM ticket%s) s
            """ % (','.join(self.fields), db.concat(*fields), ignore)
        sql += " WHERE std_fields != '' ORDER BY id"
        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 all previous entries for 'ticket' tagspace.
        rw_cursor.execute('DELETE FROM tags WHERE tagspace=%s', (self.realm, ))

        self.log.debug('ENTER_TAG_DB_CHECKOUT')
        ro_cursor.execute(sql)
        self.log.debug('EXIT_TAG_DB_CHECKOUT')
        split_into_tags = TagSystem(self.env).split_into_tags
        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])
        db.commit()
        self.log.debug('EXIT_TAG_SYNC')
示例#33
0
    def render_admin_panel(self, req, cat, page, version):
        req.perm.require('TAGS_ADMIN')

        tag_system = TagSystem(self.env)
        all_realms = tag_system.get_taggable_realms(req.perm)
        # Check request for enabled filters, or use default.
        if [r for r in all_realms if r in req.args] == []:
            for realm in all_realms:
                req.args[realm] = 'on'
        checked_realms = [r for r in all_realms if r in req.args]
        data = dict(checked_realms=checked_realms,
                    tag_realms=list(
                        dict(name=realm, checked=realm in checked_realms)
                        for realm in all_realms))

        if req.method == 'POST':
            # Replace Tag
            allow_delete = req.args.get('allow_delete')
            new_tag = req.args.get('tag_new_name').strip()
            new_tag = not new_tag == u'' and new_tag or None
            if not (allow_delete or new_tag):
                data['error'] = _("Selected current tag(s) and either "
                                  "new tag or delete approval are required")
            else:
                comment = req.args.get('comment', u'')
                old_tags = req.args.get('tag_name')
                if old_tags:
                    # Provide list regardless of single or multiple selection.
                    old_tags = isinstance(old_tags, list) and old_tags or \
                               [old_tags]
                    tag_system.replace_tag(req,
                                           old_tags,
                                           new_tag,
                                           comment,
                                           allow_delete,
                                           filter=checked_realms)
                data['selected'] = new_tag

        query = ' or '.join(['realm:%s' % r for r in checked_realms])
        all_tags = sorted(tag_system.get_all_tags(req, query))
        data['tags'] = all_tags
        try:
            Chrome(self.env).add_textarea_grips(req)
        except AttributeError:
            # Element modifiers unavailable before Trac 0.12, skip gracefully.
            pass
        return 'admin_tag_change.html', data
示例#34
0
    def render_admin_panel(self, req, cat, page, version):
        req.perm.require('TAGS_ADMIN')

        tag_system = TagSystem(self.env)
        all_realms = tag_system.get_taggable_realms(req.perm)
        # Check request for enabled filters, or use default.
        if not [r for r in all_realms if r in req.args]:
            for realm in all_realms:
                req.args[realm] = 'on'
        checked_realms = [r for r in all_realms if r in req.args]
        data = dict(checked_realms=checked_realms,
                    tag_realms=list(
                        dict(name=realm, checked=realm in checked_realms)
                        for realm in all_realms))

        if req.method == 'POST':
            # Replace Tag
            allow_delete = req.args.get('allow_delete')
            new_tag = req.args.get('tag_new_name').strip()
            new_tag = not new_tag == u'' and new_tag or None
            if not (allow_delete or new_tag):
                add_warning(
                    req,
                    _("Selected current tag(s) and either "
                      "new tag or delete approval are required"))
            else:
                comment = req.args.get('comment', u'')
                old_tags = req.args.getlist('tag_name')
                if old_tags:
                    tag_system.replace_tag(req,
                                           old_tags,
                                           new_tag,
                                           comment,
                                           allow_delete,
                                           filter=checked_realms)
                data['selected'] = new_tag
            req.redirect(req.href.admin('tags', 'replace'))

        query = ' or '.join('realm:%s' % r for r in checked_realms)
        all_tags = sorted(tag_system.get_all_tags(req, query))
        data['tags'] = all_tags
        chrome = Chrome(self.env)
        chrome.add_textarea_grips(req)
        if hasattr(chrome, 'jenv'):
            return 'admin_tag_change.html', data, None
        else:
            return 'admin_tag_change.html', data
示例#35
0
    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        setup = TagSetup(self.env)
        # Current tractags schema is partially setup with enabled component.
        #   Revert these changes for getting a clean setup.
        self._revert_tractags_schema_init()
        setup.upgrade_environment()

        self.perms = PermissionSystem(self.env)
        self.tag_s = TagSystem(self.env)

        # Populate table with initial test data.
        self.env.db_transaction("""
            INSERT INTO tags (tagspace, name, tag)
            VALUES ('wiki', 'WikiStart', 'tag1')
            """)

        self.req = Mock(authname='editor')
        # Mock an anonymous request.
        self.req.perm = PermissionCache(self.env)
示例#36
0
    def setUp(self):
        self.env = EnvironmentStub(enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        setup = TagSetup(self.env)
        # Current tractags schema is setup with enabled component anyway.
        #   Revert these changes for getting a clean setup.
        self._revert_tractags_schema_init()
        setup.upgrade_environment()

        self.tag_s = TagSystem(self.env)
        self.tag_rh = TagRequestHandler(self.env)

        perms = PermissionSystem(self.env)
        # Revoke default permissions, because more diversity is required here.
        perms.revoke_permission('anonymous', 'TAGS_VIEW')
        perms.revoke_permission('authenticated', 'TAGS_MODIFY')
        perms.grant_permission('reader', 'TAGS_VIEW')
        perms.grant_permission('writer', 'TAGS_MODIFY')
        perms.grant_permission('admin', 'TAGS_ADMIN')

        self.href = Href('/trac')
        self.abs_href = Href('http://example.org/trac')
示例#37
0
    def download_created(self, req, download):
        # Check proper permissions to modify tags.
        if not req.perm.has_permission('TAGS_MODIFY'):
           return

        # Create temporary resource.
        resource = Resource()
        resource.realm = 'downloads'
        resource.id = download['id']

        # Delete tags of download with same ID for sure.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(req, resource)

        # Add tags of new download.
        new_tags = self._get_tags(download)
        tag_system.add_tags(req, resource, new_tags)
示例#38
0
    def get_tagged_resources(self, req, tags):
        if 'TICKET_VIEW' not in req.perm:
            return

        split_into_tags = TagSystem(self.env).split_into_tags
        db = self.env.get_db_cnx()
        cursor = db.cursor()
        args = []
        ignore = ''
        if self.ignore_closed_tickets:
            ignore = " WHERE status != 'closed'"
        sql = "SELECT * FROM (SELECT id, %s, %s AS fields FROM ticket%s) s" % (
            ','.join(self.fields), '||'.join(
                ["COALESCE(%s, '')" % f for f in self.fields]), ignore)
        constraints = []
        if tags:
            constraints.append("(" +
                               ' OR '.join(["fields LIKE %s"
                                            for t in tags]) + ")")
            args += ['%' + t + '%' for t in tags]
        else:
            constraints.append("fields != ''")

        if constraints:
            sql += " WHERE " + " AND ".join(constraints)
        sql += " ORDER BY id"
        self.env.log.debug(sql)
        cursor.execute(sql, args)
        for row in cursor:
            id, ttags = row[0], ' '.join([f for f in row[1:-1] if f])
            perm = req.perm('ticket', id)
            if 'TICKET_VIEW' not in perm or 'TAGS_VIEW' not in perm:
                continue
            ticket_tags = split_into_tags(ttags)
            tags = set([to_unicode(x) for x in tags])
            if (not tags or ticket_tags.intersection(tags)):
                yield Resource('ticket', id), ticket_tags
示例#39
0
    def check_permission(self, action, username, resource, perm):
        if resource is None or action.split('_')[0] != resource.realm.upper():
            return None

        from tractags.api import TagSystem

        class FakeRequest(object):
            def __init__(self, perm):
                self.perm = perm

        permission = action.lower().split('_')[1]
        req = FakeRequest(perm)
        tags = TagSystem(self.env).get_tags(None, resource)

        # Explicitly denied?
        if ':-'.join((username, permission)) in tags:
            return False

        # Find all granted permissions for the requesting user from
        # tagged permissions by expanding any meta action as well.
        if action in set(PermissionSystem(self.env).expand_actions(
                         ['_'.join([resource.realm, t.split(':')[1]]).upper()
                          for t in tags if t.split(':')[0] == username])):
            return True
示例#40
0
    def download_changed(self, req, download, old_download):
        # Check proper permissions to modify tags.
        if not req.perm.has_permission('TAGS_MODIFY'):
           return

        # Check if tags has to be updated.
        if not self._has_tags_changed(download):
           return

        # Update old download with new values.
        old_download.update(download)

        # Create temporary resource.
        resource = Resource('downloads', old_download['id'])

        # Delete old tags.
        tag_system = TagSystem(self.env)
        tag_system.delete_tags(req, resource)

        # Add new ones.
        new_tags = self._get_tags(old_download)
        tag_system.add_tags(req, resource, new_tags)
示例#41
0
class TicketTagProviderTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        self.perms = PermissionSystem(self.env)

        setup = TagSetup(self.env)
        # Current tractags schema is setup with enabled component anyway.
        #   Revert these changes for getting default permissions inserted.
        self._revert_tractags_schema_init()
        setup.upgrade_environment()

        self.provider = TicketTagProvider(self.env)
        self.realm = 'ticket'
        self.tag_sys = TagSystem(self.env)
        self.tags = ['tag1', 'tag2']

        # Populate tables with initial test data.
        self._create_ticket(self.tags)

    def tearDown(self):
        self.env.shutdown()
        shutil.rmtree(self.env.path)

    # Helpers

    def _create_ticket(self, tags, **kwargs):
        ticket = Ticket(self.env)
        ticket['keywords'] = u' '.join(sorted(map(to_unicode, tags)))
        ticket['summary'] = 'summary'
        ticket['reporter'] = 'admin'
        for name, value in kwargs.iteritems():
            ticket[name] = value
        ticket.insert()
        return ticket

    def _revert_tractags_schema_init(self):
        with self.env.db_transaction as db:
            db("DROP TABLE IF EXISTS tags")
            db("DROP TABLE IF EXISTS tags_change")
            db("DELETE FROM system WHERE name='tags_version'")
            db("DELETE FROM permission WHERE action %s" % db.like(),
               ('TAGS_%', ))

    def _tags(self):
        tags = {}
        for name, tag in self.env.db_query("""
                SELECT name,tag FROM tags
                """):
            if name in tags:
                tags[name].add(tag)
            else:
                tags[name] = set([tag])
        return tags

    # Tests

    def test_get_tagged_resources(self):
        # No tags, no restrictions, all resources.
        req = MockRequest(self.env, authname='editor')
        self.assertEquals(
            [r for r in self.provider.get_tagged_resources(req, None)][0][1],
            set(self.tags))
        # Force fine-grained perm-check check for all tags, not just the one
        # from query.
        self.provider.fast_permcheck = False
        self.assertEquals([
            r for r in self.provider.get_tagged_resources(
                req, set(self.tags[:1]))
        ][0][1], set(self.tags))

    def test_get_tags(self):
        req = MockRequest(self.env, authname='editor')
        resource = Resource('ticket', 2)
        self.assertRaises(ResourceNotFound, self.provider.get_resource_tags,
                          req, resource)
        self._create_ticket(self.tags)
        self.assertEquals(
            [tag for tag in self.provider.get_resource_tags(req, resource)],
            self.tags)

    def test_set_tags(self):
        req = MockRequest(self.env, authname='editor')
        tags = ['tag3']
        ticket = Ticket(self.env, 1)
        ticket['keywords'] = tags[0]
        # Tags get updated by TicketChangeListener method.
        ticket.save_changes(req.authname)
        self.assertEquals(self.tag_sys.get_all_tags(req).keys(), tags)

    def test_remove_tags(self):
        req = MockRequest(self.env, authname='editor')
        anon_req = MockRequest(self.env, authname='anonymous')
        resource = Resource('ticket', 1)
        # Anonymous lacks required permissions.
        self.assertRaises(PermissionError, self.provider.remove_resource_tags,
                          anon_req, resource)
        # Shouldn't raise an error with appropriate permission.
        self.provider.remove_resource_tags(req, resource, 'comment')
        ticket = Ticket(self.env, 1)
        self.assertEquals(ticket['keywords'], '')

    def test_describe_tagged_resource(self):
        req = MockRequest(self.env, authname='editor')
        resource = Resource('ticket', 1)
        self.assertEquals(
            self.provider.describe_tagged_resource(req, resource),
            'defect: summary')

    def test_create_ticket_by_anonymous(self):
        req = MockRequest(self.env, authname='editor')
        ticket = self._create_ticket(self.tags, reporter='anonymous')
        tags = self.provider.get_resource_tags(req, ticket.resource)
        self.assertEquals(tags, set(self.tags))

    def test_update_ticket_by_anonymous(self):
        req = MockRequest(self.env, authname='editor')
        ticket = self._create_ticket([])
        tags = self.provider.get_resource_tags(req, ticket.resource)
        self.assertEquals(tags, set([]))

        ticket['keywords'] = ', '.join(self.tags)
        ticket.save_changes('anonymous', comment='Adding keywords')
        tags = self.provider.get_resource_tags(req, ticket.resource)
        self.assertEquals(tags, set(self.tags))
示例#42
0
class HacksCache(Borg):
    _lock = threading.RLock()
    _initialized = False

    _hacks = dict()
    _types = dict()
    _releases = dict()

    def __init__(self, env):
        if self._initialized:
            self._log('already initialized')
            return
        else:
            env.log.debug('HacksCache %x: initializing' % id(self.__dict__))

        self._lock.acquire()

        self._env = env
        self._tag_system = TagSystem(env)
        self._req = FakeRequest(env)
        self.id = id(self.__dict__)

        self._rebuild_cache()

        self._initialized = True
        self._lock.release()
        self._log('initialization finished')

    def __del__(self):
        self._log('Aiiieeeee')

    def _rebuild_cache(self):
        """ Rebuild the cache. Caller MUST hold the lock! """
        self._log('rebuilding cache (initialized=%s)' % self._initialized)
        self._hacks = dict()
        self._types = dict()
        self._releases = dict()

        self._update_types()
        self._update_releases()

        query  = 'realm:wiki (%s) (%s)' % \
            (' or '.join(self._types), ' or '.join(self._releases))
        for h in self._query_tags(query):
            self._update_hack(h, False, False)

    def _log(self, message):
        self._env.log.debug('HacksCache %x: %s' % (self.id, message))

    def _query_tags(self, query):
        """ Helper to perform queries on cached tags system """
        return [r.id for r, _ in self._tag_system.query(self._req, query)]

    def _update_types(self):
        """ Get/update list of hack types """
        types = self._query_tags('realm:wiki type')
        keys = self._types.keys()

        # add types that have been added since last update
        for t in types:
            if t not in keys:
                self._types[t] = Tag(t, self._env, self._req, self._tag_system)

        # remove types that no longer exist
        for k in keys:
            if k not in types:
                del self._types[k]

    def _update_releases(self):
        """ Get/update list of Trac releases """
        releases = self._query_tags('realm:wiki release')
        keys = self._releases.keys()

        # add releases that have been added since last update
        for r in releases:
            if r not in keys:
                self._releases[r] = Tag(r, self._env, self._req,
                                        self._tag_system)

        # remove releases that no longer exist
        for k in keys:
            if k not in releases:
                del self._releases[k]

    def _update_hack(self, name, full_update=True, check_delete=True):
        """ Get/update/delete hack properties """
        if full_update:
            self._update_types()
            self._update_releases()

        delete = False
        if check_delete:
            page = WikiPage(self._env, name)
            if page.exists:
                tags = set(self._tag_system.get_tags(self._req, page.resource))
                if not (tags.intersection(self._types)
                        and tags.intersection(self._releases)):
                    delete = True
            else:
                delete = True

        if delete:
            try:
                del self._hacks[name]
                self._log('deleted hack %s' % name)
            except:
                self._log('%s is no hack, skipped' % name)
        else:
            try:
                self._hacks[name].update()
                self._log('updated hack %s' % name)
            except:
                self._hacks[name] = Hack(name, self._env, self._req,
                                         self._tag_system)
                self._log('learned hack %s' % name)

    def _get(self, where, what):
        try:
            return where[what]
        except:
            return None

    def _get_all(self, where, sorted=False):
        v = where.values()
        if sorted:
            return natural_sort(v)
        else:
            return v

    # Public API
    def update(self, hack=None):
        """ Update cache for all or just the given hack """
        if hack:
            self._update_hack(hack, True, True)
        else:
            self._lock.acquire()
            self._rebuild_cache()
            self._lock.release()

    def get_type(self, name):
        return self._get(self._types, name)

    def get_all_types(self, sorted=False):
        return self._get_all(self._types, sorted)

    def get_release(self, name):
        return self._get(self._releases, name)

    def get_all_releases(self, sorted=False):
        return self._get_all(self._releases, sorted)

    def get_hack(self, name):
        return self._get(self._hacks, name)

    def get_all_hacks(self, sorted=False):
        return self._get_all(self._hacks, sorted)
示例#43
0
 def _get_stored_tags(self, req, resource):
     # Return tags associated to resource.
     tag_system = TagSystem(self.env)
     tags = tag_system.get_tags(req, resource)
     return sorted(tags)
示例#44
0
class WikiTagProviderTestCase(unittest.TestCase):

    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   path=tempfile.mkdtemp(),
                                   enable=['trac.*', 'tractags.*'])
        setup = TagSetup(self.env)
        # Current tractags schema is partially setup with enabled component.
        #   Revert these changes for getting a clean setup.
        self._revert_tractags_schema_init()
        setup.upgrade_environment()

        self.perms = PermissionSystem(self.env)
        self.tag_s = TagSystem(self.env)
        self.tag_wp = WikiTagProvider(self.env)

        # Populate table with initial test data.
        self.env.db_transaction("""
            INSERT INTO tags (tagspace, name, tag)
            VALUES ('wiki', 'WikiStart', 'tag1')
            """)

        self.realm = 'wiki'
        self.tags = ['tag1']

    def tearDown(self):
        self.env.shutdown()
        shutil.rmtree(self.env.path)

    # Helpers

    def _revert_tractags_schema_init(self):
        _revert_tractags_schema_init(self.env)

    # Tests

    def test_get_tags(self):
        resource = Resource('wiki', 'WikiStart')
        req = MockRequest(self.env, authname='editor')
        self.assertEquals([tag for tag in
                           self.tag_wp.get_resource_tags(req, resource)],
                          self.tags)

    def test_exclude_template_tags(self):
        # Populate table with more test data.
        req = MockRequest(self.env, authname='editor')
        self.env.db_transaction("""
            INSERT INTO tags (tagspace, name, tag)
            VALUES ('wiki', 'PageTemplates/Template', 'tag2')
            """)
        tags = ['tag1', 'tag2']
        self.assertEquals(self.tag_s.get_all_tags(req).keys(), self.tags)
        self.env.config.set('tags', 'query_exclude_wiki_templates', False)
        self.assertEquals(self.tag_s.get_all_tags(req).keys(), tags)

    def test_set_tags_no_perms(self):
        resource = Resource('wiki', 'TaggedPage')
        req = MockRequest(self.env, authname='anonymous')
        self.assertRaises(PermissionError, self.tag_wp.set_resource_tags,
                          req, resource, self.tags)

    def test_set_tags(self):
        resource = Resource('wiki', 'TaggedPage')
        req = MockRequest(self.env, authname='editor')
        # Shouldn't raise an error with appropriate permission.
        self.tag_wp.set_resource_tags(req, resource, self.tags)
        self.tag_wp.set_resource_tags(req, resource, ['tag2'])
        # Check change records.
        rows = self.env.db_query("""
            SELECT author,oldtags,newtags FROM tags_change
            WHERE tagspace=%s AND name=%s
            ORDER by time DESC
            """, ('wiki', 'TaggedPage'))
        self.assertEqual(rows[0], ('editor', 'tag1', 'tag2'))
        self.assertEqual(rows[1], ('editor', '', 'tag1'))
示例#45
0
class TicketTagProviderTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        self.perms = PermissionSystem(self.env)

        self.db = self.env.get_db_cnx()
        setup = TagSetup(self.env)
        # Current tractags schema is setup with enabled component anyway.
        #   Revert these changes for getting default permissions inserted.
        self._revert_tractags_schema_init()
        setup.upgrade_environment(self.db)

        self.provider = TicketTagProvider(self.env)
        self.realm = 'ticket'
        self.tag_sys = TagSystem(self.env)
        self.tags = ['tag1']

        cursor = self.db.cursor()
        # Populate table with initial test data, not synced with tickets yet.
        cursor.execute("""
            INSERT INTO tags
                   (tagspace, name, tag)
            VALUES ('ticket', '1', 'deleted')""")
        self.realm = 'ticket'
        self._create_ticket(self.tags)

        self.req = Mock()
        # Mock an anonymous request.
        self.req.perm = PermissionCache(self.env)

    def tearDown(self):
        self.db.close()
        # Really close db connections.
        self.env.shutdown()
        shutil.rmtree(self.env.path)

    # Helpers

    def _create_ticket(self, tags):
        ticket = Ticket(self.env)
        ticket['keywords'] = u' '.join(sorted(map(to_unicode, tags)))
        ticket['summary'] = 'summary'
        ticket.insert()

    def _revert_tractags_schema_init(self):
        cursor = self.db.cursor()
        cursor.execute("DROP TABLE IF EXISTS tags")
        cursor.execute("DELETE FROM system WHERE name='tags_version'")
        cursor.execute(
            "DELETE FROM permission WHERE action %s" % self.db.like(),
            ('TAGS_%', ))

    def _tags(self):
        tags = {}
        cursor = self.db.cursor()
        cursor.execute("SELECT name,tag FROM tags")
        for name, tag in cursor.fetchall():
            if name in tags:
                tags[name].add(tag)
            else:
                tags[name] = set([tag])
        return tags

    # Tests

    def test_get_tags(self):
        resource = Resource('ticket', 2)
        self.assertRaises(ResourceNotFound, self.provider.get_resource_tags,
                          self.req, resource)
        self._create_ticket(self.tags)
        self.assertEquals([
            tag for tag in self.provider.get_resource_tags(self.req, resource)
        ], self.tags)
        #ignore_closed_tickets

    def test_set_tags(self):
        resource = Resource('ticket', 1)
        self.tags = ['tag2']
        # Anonymous lacks required permissions.
        self.assertRaises(PermissionError, self.provider.set_resource_tags,
                          self.req, resource, self.tags)
        self.req.authname = 'user'
        self.req.perm = PermissionCache(self.env, username='******')
        # Shouldn't raise an error with appropriate permission.
        self.provider.set_resource_tags(self.req, resource, self.tags)
        self.assertEquals(
            self.tag_sys.get_all_tags(self.req, '').keys(), self.tags)

    def test_remove_tags(self):
        resource = Resource('ticket', 1)
        # Anonymous lacks required permissions.
        self.assertRaises(PermissionError, self.provider.remove_resource_tags,
                          self.req, resource)
        self.req.authname = 'user'
        self.req.perm = PermissionCache(self.env, username='******')
        # Shouldn't raise an error with appropriate permission.
        self.provider.remove_resource_tags(self.req, resource, 'comment')
        tkt = Ticket(self.env, 1)
        self.assertEquals(tkt['keywords'], '')

    def test_describe_tagged_resource(self):
        resource = Resource('ticket', 1)
        self.assertEquals(
            self.provider.describe_tagged_resource(self.req, resource),
            'defect: summary')
示例#46
0
    def expand_macro(self, formatter, name, args):
        req = formatter.req
        tag_system = TagSystem(self.env)

        all_releases = natural_sort([r.id for r, _ in
                                     tag_system.query(req, 'realm:wiki release')])
        all_categories = sorted([r.id for r, _ in
                                 tag_system.query(req, 'realm:wiki type')])

        hide_release_picker = False
        hide_fieldset_legend = False
        hide_fieldset_description = False
        other = []
        if args:
            categories = []
            releases = []
            for arg in args.split():
                if arg in all_releases:
                    hide_release_picker = True
                    releases.append(arg)
                elif arg in all_categories:
                    categories.append(arg)
                else:
                    other.append(arg)

            if len(categories) or len(releases):
                hide_fieldset_description = True

            if not len(categories):
                categories = all_categories
            elif len(categories) == 1:
                hide_fieldset_legend = True

            if not len(releases):
                releases = all_releases
        else:
            categories = all_categories
            releases = all_releases

        if 'update_th_filter' in req.args:
            show_releases = req.args.get('release', ['0.11'])
            if isinstance(show_releases, basestring):
                show_releases = [show_releases]
            req.session['th_release_filter'] = ','.join(show_releases)
        else:
            show_releases = req.session.get('th_release_filter', '0.11').split(',')

        output = ""
        if not hide_release_picker:
            style = "text-align:right; padding-top:1em; margin-right:5em;"
            form = builder.form('\n', style=style, method="get")

            style = "font-size:xx-small;"
            span = builder.span("Show hacks for releases:", style=style)

            for version in releases:
                inp = builder.input(version, type_="checkbox", name="release",
                                    value=version)
                if version in show_releases:
                    inp(checked="checked")
                span(inp, '\n')

            style = "font-size:xx-small; padding:0; border:solid 1px black;"
            span(builder.input(name="update_th_filter", type_="submit",
                               style=style, value="Update"), '\n')
            form('\n', span, '\n')
            output = "%s%s\n" % (output, form)


        def link(resource):
            return render_resource_link(self.env, formatter.context,
                                        resource, 'compact')

        for category in categories:
            page = WikiPage(self.env, category)
            match = self.title_extract.search(page.text)
            if match:
                cat_title = '%s' % match.group(1).strip()
                cat_body = self.title_extract.sub('', page.text, 1)
            else:
                cat_title = '%s' % category
                cat_body = page.text
            cat_body = self.self_extract.sub('', cat_body).strip()

            style = "padding:1em; margin:0em 5em 2em 5em; border:1px solid #999;"
            fieldset = builder.fieldset('\n', style=style)
            if not hide_fieldset_legend:
                legend = builder.legend(style="color: #999;")
                legend(builder.a(cat_title, href=self.env.href.wiki(category)))
                fieldset(legend, '\n')
            if not hide_fieldset_description:
                fieldset(builder.p(wiki_to_html(cat_body, self.env, req)))

            ul = builder.ul('\n', class_="listtagged")
            query = 'realm:wiki (%s) %s %s' % \
                (' or '.join(show_releases), category, ' '.join(other))

            lines = 0
            for resource, tags in tag_system.query(req, query):
                # filter out the page used to make important tags
                # persistent
                if resource.id == "tags/persistent":
                    continue

                lines += 1
                li = builder.li(link(resource), ': ')

                page = WikiPage(self.env, resource)
                match = self.title_extract.search(page.text)
                description = "''no description available''"
                if match:
                    if match.group(1):
                        description = match.group(1).strip()

                li(wiki_to_oneliner(description, self.env, req=req))
                if tags:
                    if hide_fieldset_legend == False and category in tags:
                        tags.remove(category)
                        self.log.debug("hide %s: no legend" % category)
                    for o in other:
                        if o in tags: tags.remove(o)
                    rendered_tags = [ link(resource('tag', tag))
                                      for tag in natural_sort(tags) ]

                    span = builder.span(style="font-size:xx-small;")
                    span(' (tags: ', rendered_tags[0],
                       [(', ', tag) for tag in rendered_tags[1:]], ')')
                    li(span)
                ul(li, '\n')

            if lines:
                fieldset(ul, '\n')
            else:
                message = "No results for %s." % \
                    (hide_release_picker and "this version" or "your selection")
                fieldset(builder.p(builder.em(message)), '\n')
            output = "%s%s\n" % (output, fieldset)

        return output
示例#47
0
    def expand_macro(self, formatter, name, content):
        env = self.env
        req = formatter.req
        args, kw = parse_args(content)

        # Use macro arguments (most likely wiki macro calls).
        realms = 'realm' in kw and kw['realm'].split('|') or []
        tag_system = TagSystem(env)
        all_realms = [p.get_taggable_realm() for p in tag_system.tag_providers]
        self.all_realms = all_realms
        self.realms = realms

        if name == 'TagCloud':
            args.append(' or '.join(['realm:%s' % r for r in realms]))
            all_tags = tag_system.get_all_tags(req, ' '.join(args))
            mincount = 'mincount' in kw and kw['mincount'] or None
            return self.render_cloud(req,
                                     all_tags,
                                     caseless_sort=self.caseless_sort,
                                     mincount=mincount)
        elif name == 'ListTagged':
            if _OBSOLETE_ARGS_RE.search(content):
                data = {'warning': 'obsolete_args'}
            else:
                data = {'warning': None}
            context = formatter.context
            # Use TagsQuery arguments (most likely wiki macro calls).
            cols = 'cols' in kw and kw['cols'] or self.default_cols
            format = 'format' in kw and kw['format'] or self.default_format
            query = args and args[0].strip() or None
            if query and not realms:
                # First read query arguments (most likely a web-UI call).
                for realm in all_realms:
                    if re.search('(^|\W)realm:%s(\W|$)' % (realm), query):
                        realms = realms and realms.append(realm) or [realm]
            if not realms:
                # Apply ListTagged defaults to macro call w/o realm.
                realms = list(set(all_realms) - set(self.exclude_realms))
            if not realms:
                return ''
            else:
                self.query = query
                self.realms = realms
            query = '(%s) (%s)' % (query or '', ' or '.join(
                ['realm:%s' % (r) for r in realms]))
            env.log.debug('LISTTAGGED_QUERY: ' + query)
            query_result = tag_system.query(req, query)
            if not query_result:
                return ''

            def _link(resource):
                if resource.realm == 'tag':
                    # Keep realm selection in tag links.
                    return builder.a(resource.id,
                                     href=self.get_href(req, tag=resource))
                elif resource.realm == 'ticket':
                    # Return resource link including ticket status dependend
                    #   class to allow for common Trac ticket link style.
                    ticket = Ticket(env, resource.id)
                    return builder.a('#%s' % ticket.id,
                                     class_=ticket['status'],
                                     href=formatter.href.ticket(ticket.id),
                                     title=shorten_line(ticket['summary']))
                return render_resource_link(env, context, resource, 'compact')

            if format == 'table':
                cols = [
                    col for col in cols.split('|')
                    if col in self.supported_cols
                ]
                # Use available translations from Trac core.
                try:
                    labels = TicketSystem(env).get_ticket_field_labels()
                    labels['id'] = _('Id')
                except AttributeError:
                    # Trac 0.11 neither has the attribute nor uses i18n.
                    labels = {'id': 'Id', 'description': 'Description'}
                labels['realm'] = _('Realm')
                labels['tags'] = _('Tags')
                headers = [{'label': labels.get(col)} for col in cols]
                data.update({'cols': cols, 'headers': headers})

            results = sorted(query_result, key=lambda r: \
                             embedded_numbers(to_unicode(r[0].id)))
            results = self._paginate(req, results)
            rows = []
            for resource, tags in results:
                desc = tag_system.describe_tagged_resource(req, resource)
                tags = sorted(tags)
                if tags:
                    rendered_tags = [
                        _link(Resource('tag', tag)) for tag in tags
                    ]
                    if 'oldlist' == format:
                        resource_link = _link(resource)
                    else:
                        desc = desc or \
                               get_resource_description(env, resource,
                                                        context=context)
                        resource_link = builder.a(desc,
                                                  href=get_resource_url(
                                                      env, resource,
                                                      context.href))
                        if 'table' == format:
                            cells = []
                            for col in cols:
                                if col == 'id':
                                    cells.append(_link(resource))
                                # Don't duplicate links to resource in both.
                                elif col == 'description' and 'id' in cols:
                                    cells.append(desc)
                                elif col == 'description':
                                    cells.append(resource_link)
                                elif col == 'realm':
                                    cells.append(resource.realm)
                                elif col == 'tags':
                                    cells.append(
                                        builder([(tag, ' ')
                                                 for tag in rendered_tags]))
                            rows.append({'cells': cells})
                            continue
                rows.append({
                    'desc': desc,
                    'rendered_tags': None,
                    'resource_link': _link(resource)
                })
            data.update({
                'format': format,
                'paginator': results,
                'results': rows,
                'tags_url': req.href('tags')
            })

            # Work around a bug in trac/templates/layout.html, that causes a
            # TypeError for the wiki macro call, if we use add_link() alone.
            add_stylesheet(req, 'common/css/search.css')

            return Chrome(env).render_template(req, 'listtagged_results.html',
                                               data, 'text/html', True)
示例#48
0
 def _get_stored_tags(self, req, download_id):
     tag_system = TagSystem(self.env)
     resource = Resource('downloads', download_id)
     tags = tag_system.get_tags(req, resource)
     return sorted(tags)
示例#49
0
    def expand_macro(self, formatter, name, content, realms=[]):
        """Evaluate macro call and render results.

        Calls from web-UI come with pre-processed realm selection.
        """
        env = self.env
        req = formatter.req
        tag_system = TagSystem(env)

        all_realms = tag_system.get_taggable_realms()
        if not all_realms:
            # Tag providers are required, no result without at least one.
            return ''
        args, kw = parse_args(content)

        query = args and args[0].strip() or None
        if not realms:
            # Check macro arguments for realms (typical wiki macro call).
            realms = 'realm' in kw and kw['realm'].split('|') or []
        if query:
            # Add realms from query expression.
            realms.extend(query_realms(query, all_realms))
            # Remove redundant realm selection for performance.
            if set(realms) == all_realms:
                query = re.sub('(^|\W)realm:\S+(\W|$)', ' ', query).strip()
        if name == 'TagCloud':
            # Set implicit 'all tagged realms' as default.
            if not realms:
                realms = all_realms
            if query:
                all_tags = Counter()
                # Require per resource query including view permission checks.
                for resource, tags in tag_system.query(req, query):
                    all_tags.update(tags)
            else:
                # Allow faster per tag query, side steps permission checks.
                all_tags = tag_system.get_all_tags(req, realms=realms)
            mincount = 'mincount' in kw and kw['mincount'] or None
            return self.render_cloud(req,
                                     all_tags,
                                     caseless_sort=self.caseless_sort,
                                     mincount=mincount,
                                     realms=realms)
        elif name == 'ListTagged':
            if content and _OBSOLETE_ARGS_RE.search(content):
                data = {'warning': 'obsolete_args'}
            else:
                data = {'warning': None}
            context = formatter.context
            # Use TagsQuery arguments (most likely wiki macro calls).
            cols = 'cols' in kw and kw['cols'] or self.default_cols
            format = 'format' in kw and kw['format'] or self.default_format
            if not realms:
                # Apply ListTagged defaults to macro call w/o realm.
                realms = list(set(all_realms) - set(self.exclude_realms))
                if not realms:
                    return ''
            query = '(%s) (%s)' % (query or '', ' or '.join(
                ['realm:%s' % (r) for r in realms]))
            query_result = tag_system.query(req, query)
            excludes = [
                exc.strip() for exc in kw.get('exclude', '').split(':')
                if exc.strip()
            ]
            if excludes and query_result:
                filtered_result = [(resource, tags)
                                   for resource, tags in query_result
                                   if not any(
                                       fnmatchcase(resource.id, exc)
                                       for exc in excludes)]
                query_result = filtered_result
            if not query_result:
                return ''

            def _link(resource):
                if resource.realm == 'tag':
                    # Keep realm selection in tag links.
                    return builder.a(resource.id,
                                     href=self.get_href(req,
                                                        realms,
                                                        tag=resource))
                elif resource.realm == 'ticket':
                    # Return resource link including ticket status dependend
                    #   class to allow for common Trac ticket link style.
                    ticket = Ticket(env, resource.id)
                    return builder.a('#%s' % ticket.id,
                                     class_=ticket['status'],
                                     href=formatter.href.ticket(ticket.id),
                                     title=shorten_line(ticket['summary']))
                return render_resource_link(env, context, resource, 'compact')

            if format == 'table':
                cols = [
                    col for col in cols.split('|')
                    if col in self.supported_cols
                ]
                # Use available translations from Trac core.
                try:
                    labels = TicketSystem(env).get_ticket_field_labels()
                    labels['id'] = _('Id')
                except AttributeError:
                    # Trac 0.11 neither has the attribute nor uses i18n.
                    labels = {'id': 'Id', 'description': 'Description'}
                labels['realm'] = _('Realm')
                labels['tags'] = _('Tags')
                headers = [{'label': labels.get(col)} for col in cols]
                data.update({'cols': cols, 'headers': headers})

            try:
                results = sorted(
                    query_result,
                    key=lambda r: embedded_numbers(to_unicode(r[0].id)))
            except (InvalidQuery, InvalidTagRealm), e:
                return system_message(_("ListTagged macro error"), e)
            results = self._paginate(req, results, realms)
            rows = []
            for resource, tags in results:
                desc = tag_system.describe_tagged_resource(req, resource)
                tags = sorted(tags)
                wiki_desc = format_to_oneliner(env, context, desc)
                if tags:
                    rendered_tags = [
                        _link(Resource('tag', tag)) for tag in tags
                    ]
                    if 'oldlist' == format:
                        resource_link = _link(resource)
                    else:
                        resource_link = builder.a(wiki_desc,
                                                  href=get_resource_url(
                                                      env, resource,
                                                      context.href))
                        if 'table' == format:
                            cells = []
                            for col in cols:
                                if col == 'id':
                                    cells.append(_link(resource))
                                # Don't duplicate links to resource in both.
                                elif col == 'description' and 'id' in cols:
                                    cells.append(wiki_desc)
                                elif col == 'description':
                                    cells.append(resource_link)
                                elif col == 'realm':
                                    cells.append(resource.realm)
                                elif col == 'tags':
                                    cells.append(
                                        builder([(tag, ' ')
                                                 for tag in rendered_tags]))
                            rows.append({'cells': cells})
                            continue
                rows.append({
                    'desc': wiki_desc,
                    'rendered_tags': None,
                    'resource_link': _link(resource)
                })
            data.update({
                'format': format,
                'paginator': results,
                'results': rows,
                'tags_url': req.href('tags')
            })

            # Work around a bug in trac/templates/layout.html, that causes a
            # TypeError for the wiki macro call, if we use add_link() alone.
            add_stylesheet(req, 'common/css/search.css')

            return Chrome(env).render_template(req, 'listtagged_results.html',
                                               data, 'text/html', True)
示例#50
0
class TagWikiSyntaxProvider(Component):
    """[opt] Provides tag:<expr> links.

    This extends TracLinks via WikiFormatting to point at tag queries or
    at specific tags.
    """

    implements(IWikiSyntaxProvider)

    def __init__(self):
        self.tag_system = TagSystem(self.env)

    # IWikiSyntaxProvider methods

    def get_wiki_syntax(self):
        """Additional syntax for quoted tags or tag expression."""
        tag_expr = (r"(%s)" % (WikiParser.QUOTED_STRING))

        # Simple (tag|tagged):link syntax
        yield (
            r'''(?P<qualifier>tag(?:ged)?):(?P<tag_expr>%s)''' % tag_expr,
            lambda f, ns, match: self._format_tagged(
                f, match.group('qualifier'), match.group('tag_expr'), '%s:%s' %
                (match.group('qualifier'), match.group('tag_expr'))))

        # [(tag|tagged):link with label]
        yield (
            r'''\[tag(?:ged)?:'''
            r'''(?P<ltag_expr>%s)\s*(?P<tag_title>[^\]]+)?\]''' % tag_expr,
            lambda f, ns, match: self._format_tagged(
                f, 'tag', match.group('ltag_expr'), match.group('tag_title')))

    def get_link_resolvers(self):
        return [('tag', self._format_tagged), ('tagged', self._format_tagged)]

    def _format_tagged(self, formatter, ns, target, label, fullmatch=None):
        """Tag and tag query expression link formatter."""
        def unquote(text):
            """Strip all matching pairs of outer quotes from string."""
            while re.match(WikiParser.QUOTED_STRING, text):
                # Remove outer whitespace after stripped quotation too.
                text = text[1:-1].strip()
            return text

        label = label and unquote(label.strip()) or ''
        target = unquote(target.strip())

        query = target
        # Pop realms from query expression.
        all_realms = self.tag_system.get_taggable_realms(formatter.perm)
        realms = query_realms(target, all_realms)
        if realms:
            kwargs = dict((realm, 'on') for realm in realms)
            target = re.sub('(^|\W)realm:\S+(\W|$)', ' ', target).strip()
        else:
            kwargs = {}

        tag_res = Resource('tag', target)
        if 'TAGS_VIEW' not in formatter.perm(tag_res):
            return tag.span(label,
                            class_='forbidden tags',
                            title=_("no permission to view tags"))

        context = formatter.context
        href = self.tag_system.get_resource_url(tag_res, context.href, kwargs)
        if all_realms and (target in self.tag_system.get_all_tags(
                formatter.req) or not iter_is_empty(
                    self.tag_system.query(formatter.req, query))):
            # At least one tag provider is available and tag exists or
            # tags query yields at least one match.
            if label:
                return tag.a(label, href=href)
            return render_resource_link(self.env, context, tag_res)

        return tag.a(label + '?',
                     href=href,
                     class_='missing tags',
                     rel='nofollow')
示例#51
0
class WikiTagProviderTestCase(unittest.TestCase):
    def setUp(self):
        self.env = EnvironmentStub(default_data=True,
                                   enable=['trac.*', 'tractags.*'])
        self.env.path = tempfile.mkdtemp()
        self.perms = PermissionSystem(self.env)

        self.tag_s = TagSystem(self.env)
        self.tag_wp = WikiTagProvider(self.env)

        self.db = self.env.get_db_cnx()
        setup = TagSetup(self.env)
        # Current tractags schema is partially setup with enabled component.
        #   Revert these changes for getting a clean setup.
        self._revert_tractags_schema_init()
        setup.upgrade_environment(self.db)

        cursor = self.db.cursor()
        # Populate table with initial test data.
        cursor.execute("""
            INSERT INTO tags
                   (tagspace, name, tag)
            VALUES ('wiki', 'WikiStart', 'tag1')
        """)

        self.req = Mock()
        # Mock an anonymous request.
        self.req.perm = PermissionCache(self.env)
        self.realm = 'wiki'
        self.tags = ['tag1']

    def tearDown(self):
        self.db.close()
        # Really close db connections.
        self.env.shutdown()
        shutil.rmtree(self.env.path)

    # Helpers

    def _revert_tractags_schema_init(self):
        cursor = self.db.cursor()
        cursor.execute("DROP TABLE IF EXISTS tags")
        cursor.execute("DELETE FROM system WHERE name='tags_version'")
        cursor.execute(
            "DELETE FROM permission WHERE action %s" % self.db.like(),
            ('TAGS_%', ))

    # Tests

    def test_get_tags(self):
        resource = Resource('wiki', 'WikiStart')
        self.assertEquals(
            [tag for tag in self.tag_wp.get_resource_tags(self.req, resource)],
            self.tags)

    def test_exclude_template_tags(self):
        cursor = self.db.cursor()
        # Populate table with more test data.
        cursor.execute("""
            INSERT INTO tags
                   (tagspace, name, tag)
            VALUES ('wiki', 'PageTemplates/Template', 'tag2')
        """)
        tags = ['tag1', 'tag2']
        # Previous workaround for regression in query parser still works,
        #   but the query without argument works again as well.
        self.assertEquals(
            self.tag_s.get_all_tags(self.req, '-invalid').keys(), self.tags)
        self.assertEquals(
            self.tag_s.get_all_tags(self.req, '').keys(), self.tags)
        self.env.config.set('tags', 'query_exclude_wiki_templates', False)
        self.assertEquals(
            self.tag_s.get_all_tags(self.req, '-invalid').keys(), tags)
        self.assertEquals(self.tag_s.get_all_tags(self.req, '').keys(), tags)

    def test_set_tags_no_perms(self):
        resource = Resource('wiki', 'TaggedPage')
        self.assertRaises(PermissionError, self.tag_wp.set_resource_tags,
                          self.req, resource, self.tags)

    def test_set_tags(self):
        resource = Resource('wiki', 'TaggedPage')
        self.req.perm = PermissionCache(self.env, username='******')
        # Shouldn't raise an error with appropriate permission.
        self.tag_wp.set_resource_tags(self.req, resource, self.tags)
示例#52
0
class WikiTagInterface(TagTemplateProvider):
    """[main] Implements the user interface for tagging Wiki pages."""

    implements(IRequestFilter, ITemplateStreamFilter, IWikiChangeListener,
               IWikiPageManipulator)

    def __init__(self):
        self.tag_system = TagSystem(self.env)

    # IRequestFilter methods

    def pre_process_request(self, req, handler):
        return handler

    def post_process_request(self, req, template, data, content_type):
        if template is not None:
            if req.method == 'GET' and req.path_info.startswith('/wiki/'):
                if req.args.get('action') == 'edit' and \
                        req.args.get('template') and 'tags' not in req.args:
                    self._post_process_request_edit(req)
                if req.args.get('action') == 'history' and \
                        data and 'history' in data:
                    self._post_process_request_history(req, data)
            elif req.method == 'POST' and \
                    req.path_info.startswith('/wiki/') and \
                    'save' in req.args:
                requests.reset()
        return template, data, content_type

    # ITemplateStreamFilter methods
    def filter_stream(self, req, method, filename, stream, data):
        page_name = req.args.get('page', 'WikiStart')
        resource = Resource('wiki', page_name)
        if filename == 'wiki_view.html' and 'TAGS_VIEW' in req.perm(resource):
            return self._wiki_view(req, stream)
        elif filename == 'wiki_edit.html' and \
                         'TAGS_MODIFY' in req.perm(resource):
            return self._wiki_edit(req, stream)
        elif filename == 'history_view.html' and \
                         'TAGS_VIEW' in req.perm(resource):
            return self._wiki_history(req, stream)
        return stream

    # IWikiPageManipulator methods
    def prepare_wiki_page(self, req, page, fields):
        pass

    def validate_wiki_page(self, req, page):
        # If we're saving the wiki page, and can modify tags, do so.
        if req and 'TAGS_MODIFY' in req.perm(page.resource) \
                and req.path_info.startswith('/wiki') and 'save' in req.args:
            page_modified = req.args.get('text') != page.old_text or \
                    page.readonly != int('readonly' in req.args)
            if page_modified:
                requests.set(req)
                req.add_redirect_listener(self._redirect_listener)
            elif page.version > 0:
                # If the page hasn't been otherwise modified, save tags and
                # redirect to avoid the "page has not been modified" warning.
                if self._update_tags(req, page):
                    req.redirect(
                        get_resource_url(self.env,
                                         page.resource,
                                         req.href,
                                         version=None))
        return []

    # IWikiChangeListener methods
    def wiki_page_added(self, page):
        req = requests.get()
        if req:
            self._update_tags(req, page, page.time)

    def wiki_page_changed(self, page, version, t, comment, author, ipnr):
        req = requests.get()
        if req:
            self._update_tags(req, page, page.time)

    def wiki_page_renamed(self, page, old_name):
        """Called when a page has been renamed (since Trac 0.12)."""
        self.log.debug("Moving wiki page tags from %s to %s", old_name,
                       page.name)
        req = MockReq()
        self.tag_system.reparent_tags(req, Resource('wiki', page.name),
                                      old_name)

    def wiki_page_deleted(self, page):
        # Page gone, so remove all records on it.
        delete_tags(self.env, page.resource, purge=True)

    def wiki_page_version_deleted(self, page):
        pass

    # Internal methods
    def _page_tags(self, req):
        pagename = req.args.get('page', 'WikiStart')
        version = req.args.get('version')
        tags_version = req.args.get('tags_version')

        page = WikiPage(self.env, pagename, version=version)
        resource = page.resource
        if version and not tags_version:
            tags_version = page.time
        tags = sorted(
            self.tag_system.get_tags(req, resource, when=tags_version))
        return tags

    def _redirect_listener(self, req, url, permanent):
        requests.reset()

    def _post_process_request_edit(self, req):
        # Retrieve template resource to be queried for tags.
        template_pagename = ''.join(
            [WikiModule.PAGE_TEMPLATES_PREFIX,
             req.args.get('template')])
        template_page = WikiPage(self.env, template_pagename)
        if template_page.exists and \
                'TAGS_VIEW' in req.perm(template_page.resource):
            tags = sorted(self.tag_system.get_tags(req,
                                                   template_page.resource))
            # Prepare tags as content for the editor field.
            tags_str = ' '.join(tags)
            self.env.log.debug("Tags retrieved from template: '%s'",
                               unicode(tags_str).encode('utf-8'))
            # DEVEL: More arguments need to be propagated here?
            req.redirect(
                req.href(req.path_info,
                         action='edit',
                         tags=tags_str,
                         template=req.args.get('template')))

    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]
                comment = render_tag_changes(tags_history[2], tags_history[3])
                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 _wiki_view(self, req, stream):
        add_stylesheet(req, 'tags/css/tractags.css')
        tags = self._page_tags(req)
        if not tags:
            return stream
        li = []
        for tag_ in tags:
            resource = Resource('tag', tag_)
            anchor = render_resource_link(self.env, web_context(req, resource),
                                          resource)
            anchor = anchor(rel='tag')
            li.append(tag.li(anchor, ' '))

        # TRANSLATOR: Header label text for tag list at wiki page bottom.
        insert = tag.ul(class_='tags')(tag.li(_("Tags"), class_='header'), li)
        return stream | (
            Transformer('//div[contains(@class,"wikipage")]').after(insert))

    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 _wiki_edit(self, req, stream):
        # TRANSLATOR: Label text for link to '/tags'.
        link = tag.a(_("view all tags"), href=req.href.tags())
        # TRANSLATOR: ... (view all tags)
        insert = tag(Markup(_("Tag under: (%(tags_link)s)", tags_link=link)))
        insert(
            tag.br(),
            tag.input(id='tags',
                      type='text',
                      name='tags',
                      size='50',
                      value=req.args.get('tags',
                                         ' '.join(self._page_tags(req)))))
        insert = tag.div(tag.label(insert), class_='field')
        return stream | Transformer('//div[@id="changeinfo1"]').append(insert)

    def _wiki_history(self, req, stream):
        xpath = '//input[@type="radio" and @value="*"]'
        stream = stream | Transformer(xpath).remove()
        # Remove invalid links to wiki page revisions (fix for Trac < 0.12).
        xpath = '//a[contains(@href,"%2A")]'
        return stream | Transformer(xpath).remove()

    def _wiki_to_oneliner(self, context, wiki, shorten=None):
        if isinstance(wiki, Fragment):
            return wiki
        return format_to_oneliner(self.env, context, wiki, shorten=shorten)
示例#53
0
class TagRPC(Component):
    """[extra] RPC interface for the tag system.

    Access Trac resource tagging system through methods provided by
    [https://trac-hacks.org/wiki/XmlRpcPlugin XmlRpcPlugin].
    """

    implements(IXMLRPCHandler)

    def __init__(self):
        self.tag_system = TagSystem(self.env)

    # IXMLRPCHandler methods

    def xmlrpc_namespace(self):
        return 'tags'

    def xmlrpc_methods(self):
        yield (None, ((list, str),), self.splitIntoTags)
        yield ('TAGS_VIEW', ((list,),), self.getTaggableRealms)
        yield ('TAGS_VIEW', ((dict,), (dict, list)), self.getAllTags)
        yield ('TAGS_VIEW', ((list, str, str),), self.getTags)
        yield ('TAGS_VIEW', ((list, str),), self.query)
        yield ('TAGS_MODIFY', ((list, str, str, list),
                               (list, str, str, list, str)), self.addTags)
        yield ('TAGS_MODIFY', ((list, str, str, list),
                               (list, str, str, list, str)), self.setTags)

    # Exported functions and TagSystem methods

    def addTags(self, req, realm, id, tags, comment=u''):
        """Add the supplied list of tags to a taggable Trac resource.

        Returns the updated list of resource tags.
        """
        resource = Resource(realm, id)
        # Replicate TagSystem.add_tags() method due to xmlrpclib issue.
        tags = set(tags)
        tags.update(self._get_tags(req, resource))
        self.tag_system.set_tags(req, resource, tags, comment)
        return self._get_tags(req, resource)

    def getAllTags(self, req, realms=[]):
        """Returns a dict of all tags as keys and occurrences as values.

        If a realm list is supplied, only tags from these taggable realms
        are shown.
        """
        # Type conversion needed for content transfer of Counter object.
        return dict(self.tag_system.get_all_tags(req, realms))

    def getTaggableRealms(self, req):
        """Returns the list of taggable Trac realms."""
        return list(self.tag_system.get_taggable_realms())

    def getTags(self, req, realm, id):
        """Returns the list of tags for a Trac resource."""
        return self._get_tags(req, Resource(realm, id))

    def query(self, req, query_str):
        """Returns a list of tagged Trac resources, whose tags match the
        supplied tag query expression.
        """
        # Type conversion needed for content transfer of Python set objects.
        return [(resource.realm, resource.id, list(tags))
                for resource, tags in self.tag_system.query(req, query_str)]

    def setTags(self, req, realm, id, tags, comment=u''):
        """Replace tags for a Trac resource with the supplied list of tags.

        Returns the updated list of resource tags.
        """
        resource = Resource(realm, id)
        self._get_tags(req, resource) # Trac resource exists?
        self.tag_system.set_tags(req, resource, tags, comment)
        return self._get_tags(req, resource)

    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)

    # Private methods

    def _get_tags(self, req, resource):
        if not resource_exists(self.env, resource):
            raise ResourceNotFound('Resource "%r" does not exists' % resource)
        # Workaround for ServiceException when calling TagSystem.get_tags().
        provider = [p for p in self.tag_system.tag_providers
                    if p.get_taggable_realm() == resource.realm][0]
        # Type conversion needed for content transfer of Python set objects.
        return list(provider.get_resource_tags(req, resource))
示例#54
0
 def __init__(self):
     self.tag_system = TagSystem(self.env)
示例#55
0
    def render_new(self, req, data, hacks):
        req.perm.require('HACK_CREATE')

        tag_system = TagSystem(self.env)

        hacks = list(hacks)
        hack_names = set(r[2].id for r in hacks)
        users = set(u.id for u, _ in tag_system.query(req, 'realm:wiki user'))
        exclude = hack_names.union(users).union(data['types']).union(data['releases'])

        cloud = {}

        for votes, rank, resource, tags, title in hacks:
            for tag in tags:
                if tag in exclude:
                    continue
                try:
                    cloud[tag] += 1
                except KeyError:
                    cloud[tag] = 1

        # Pick the top 25 tags + a random sample of 10 from the rest.
        cloud = sorted(cloud.items(), key=lambda i: -i[1])
        remainder = cloud[25:]
        cloud = dict(cloud[:25] +
                     random.sample(remainder, min(10, len(remainder))))

        # Render the cloud
        min_px = 8
        max_px = 20

        def cloud_renderer(tag, count, percent):
            self.env.log.debug("cloud: %s = %2.2f%%" % (tag, percent * 100))
            return builder.a(tag, href='#', style='font-size: %ipx' %
                             int(min_px + percent * (max_px - min_px)))

        data['cloud'] = render_cloud(self.env, req, cloud, cloud_renderer)

        add_script(req, 'common/js/wikitoolbar.js')
        add_script(req, 'common/js/folding.js')

        data['focus'] = 'name'

        # Populate data with form submission
        if req.method == 'POST' and 'create' in req.args or 'preview' in req.args:
            data.update(req.args)

            context = self.form.validate(data)
            data['form_context'] = context

            vars = {}
            vars['OWNER'] = req.authname
            vars['WIKINAME'] = get_page_name(data['name'], data['type'])
            vars['TYPE'] = data.setdefault('type', 'plugin')
            vars['TITLE'] = data.setdefault('title', 'No title available')
            vars['LCNAME'] = vars['WIKINAME'].lower()
            vars['SOURCEURL'] = '%s/%s' % (self.svnbase, vars['LCNAME'])
            vars['DESCRIPTION'] = data.setdefault('description',
                                                  'No description available')
            vars['EXAMPLE'] = data.setdefault('example',
                                              'No example available')

            if 'create' in req.args and not context.errors:
                success, message = self.create_hack(req, data, vars)
                if success:
                    target = '%s?%s' % \
                        (req.href.wiki(vars['WIKINAME']), 'hack=created')
                    req.redirect(target)
                    raise RequestDone
                else:
                    add_warning(req, message)
            elif 'preview' in req.args and not context.errors:
                page = WikiPage(self.env, self.template)
                if not page.exists:
                    raise TracError('New hack template %s does not exist.' % \
                                    self.template)
                template = Template(page.text).substitute(vars)
                template = re.sub(r'\[\[ChangeLog[^\]]*\]\]',
                                  'No changes yet', template)
                data['page_preview'] = wiki_to_html(template, self.env, req)
        else:
            data['form_context'] = None
            data['type'] = 'plugin'
            data['release'] = ['0.11']

        self.env.log.debug('MUPPETS AHOY')
        return 'hacks_new.html', data, None
示例#56
0
 def _delete_tags(self, req, resource):
     # Delete tags of the resource.
     tag_system = TagSystem(self.env)
     tag_system.delete_tags(req, resource)
示例#57
0
    def create_hack(self, req, data, vars):
        import fcntl

        messages = []
        created = False
        have_lock = False
        lockfile = open(self.lockfile, "w")
        try:
            rv = fcntl.flock(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
            if rv:
                raise TracError('Failed to acquire lock, error: %i' % rv)
            have_lock = True
        except IOError:
            messages.append(
                'A hack is currently being created by another user. '
                'Please wait a few seconds, then click the "Create hack" '
                'button again.'
            )

        if have_lock:
            steps_done = []
            try:
                # Step 1: create repository paths
                from os import popen

                svn_path = 'file://%s' % \
                    self.env.config.get('trac', 'repository_dir')
                svn_path = svn_path.rstrip('/')
                page_name = vars['WIKINAME']
                hack_path = vars['LCNAME']
                paths = [ '%s/%s' % (svn_path, hack_path) ]
                selected_releases = data['selected_releases']
                if isinstance(selected_releases, (basestring, unicode)):
                    selected_releases = [ selected_releases, ]
                for release in selected_releases:
                    if release == 'anyrelease': continue
                    paths.append("%s/%s/%s" % \
                        (svn_path, hack_path, release))

                cmd  = '/usr/bin/op create-hack %s ' % req.authname
                cmd += '"New hack %s, created by %s" ' % \
                        (page_name, req.authname)
                cmd += '%s 2>&1' % ' '.join(paths)
                output = popen(cmd).readlines()
                if output:
                    raise Exception(
                        "Failed to create Subversion paths:\n%s" % \
                            '\n'.join(output)
                        )
                steps_done.append('repository')

                # Step 2: Add permissions
                from svnauthz.model import User, Path, PathAcl

                authz_file = self.env.config.get('trac', 'authz_file')
                authz = AuthzFileReader().read(authz_file)

                svn_path_acl = PathAcl(User(req.authname), r=True, w=True)
                authz.add_path(Path("/%s" % hack_path, acls = [svn_path_acl,]))
                AuthzFileWriter().write(authz_file, authz)
                steps_done.append('permissions')

                # Step 3: Add component
                component = TicketComponent(self.env)
                component.name = page_name
                component.owner = req.authname
                component.insert()
                steps_done.append('component')

                # Step 4: Create wiki page
                template_page = WikiPage(self.env, self.template)
                page = WikiPage(self.env, page_name)
                page.text = Template(template_page.text).substitute(vars)
                page.save(req.authname, 'New hack %s, created by %s' % \
                          (page_name, req.authname), '0.0.0.0')
                steps_done.append('wiki')

                # Step 5: Tag the new wiki page
                res = Resource('wiki', page_name)
                tags = data['tags'].split() + selected_releases + [data['type']]
                TagSystem(self.env).set_tags(req, res, tags)
                steps_done.append('tags')

                rv = fcntl.flock(lockfile, fcntl.LOCK_UN)
                created = True
            except Exception, e:
                try:
                    if 'tags' in steps_done:
                        res = Resource('wiki', page_name)
                        tags = data['tags'].split() + selected_releases
                        TagSystem(self.env).delete_tags(req, res, tags)
                    if 'wiki' in steps_done:
                        WikiPage(self.env, page_name).delete()
                    if 'component' in steps_done:
                        TicketComponent(self.env, page_name).delete()
                    if 'permissions' in steps_done:
                        authz_file = self.env.config.get('trac', 'authz_file')
                        authz = AuthzFileReader().read(authz_file)
                        authz.del_path(Path("/%s" % hack_path))
                        AuthzFileWriter().write(authz_file, authz)
                    # TODO: rollback subversion path creation
                    rv = fcntl.flock(lockfile, fcntl.LOCK_UN)
                except:
                    self.env.log.error("Rollback failed")
                    rv = fcntl.flock(lockfile, fcntl.LOCK_UN)
                self.env.log.error(e, exc_info=True)
                raise TracError(str(e))
示例#58
0
    def process_request(self, req):
        req.perm.require('TAGS_VIEW')

        match = re.match(r'/tags/?(.*)', req.path_info)
        tag_id = match.group(1) and match.group(1) or None
        query = req.args.get('q', '')

        # Consider only providers, that are permitted for display.
        tag_system = TagSystem(self.env)
        all_realms = tag_system.get_taggable_realms(req.perm)
        if not (tag_id or query) or [r for r in all_realms if r in req.args
                                     ] == []:
            for realm in all_realms:
                if not realm in self.exclude_realms:
                    req.args[realm] = 'on'
        checked_realms = [r for r in all_realms if r in req.args]
        if query:
            # Add permitted realms from query expression.
            checked_realms.extend(query_realms(query, all_realms))
        realm_args = dict(
            zip([r for r in checked_realms], ['on' for r in checked_realms]))
        # Switch between single tag and tag query expression mode.
        if tag_id and not re.match(r"""(['"]?)(\S+)\1$""", tag_id, re.UNICODE):
            # Convert complex, invalid tag ID's --> query expression.
            req.redirect(req.href.tags(realm_args, q=tag_id))
        elif query:
            single_page = re.match(r"""(['"]?)(\S+)\1$""", query, re.UNICODE)
            if single_page:
                # Convert simple query --> single tag.
                req.redirect(req.href.tags(single_page.group(2), realm_args))

        data = dict(page_title=_("Tags"), checked_realms=checked_realms)
        # Populate the TagsQuery form field.
        data['tag_query'] = tag_id and tag_id or query
        data['tag_realms'] = list(
            dict(name=realm, checked=realm in checked_realms)
            for realm in all_realms)
        if tag_id:
            data['tag_page'] = WikiPage(self.env,
                                        tag_system.wiki_page_prefix + tag_id)
        if query or tag_id:
            macro = 'ListTagged'
            # TRANSLATOR: The meta-nav link label.
            add_ctxtnav(req, _("Back to Cloud"), req.href.tags())
            args = "%s,format=%s,cols=%s" % \
                   (tag_id and tag_id or query, self.default_format,
                    self.default_cols)
            data['mincount'] = None
        else:
            macro = 'TagCloud'
            mincount = as_int(req.args.get('mincount', None),
                              self.cloud_mincount)
            args = mincount and "mincount=%s" % mincount or None
            data['mincount'] = mincount
        formatter = Formatter(self.env,
                              Context.from_request(req, Resource('tag')))
        self.env.log.debug("%s macro arguments: %s" %
                           (macro, args and args or '(none)'))
        macros = TagWikiMacros(self.env)
        try:
            # Query string without realm throws 'NotImplementedError'.
            data['tag_body'] = checked_realms and \
                               macros.expand_macro(formatter, macro, args,
                                                   realms=checked_realms) \
                               or ''
        except InvalidQuery, e:
            data['tag_query_error'] = to_unicode(e)
            data['tag_body'] = macros.expand_macro(formatter, 'TagCloud', '')
示例#59
0
    def fetch_hacks(self, req, data, types, releases):
        """Return a list of hacks in the form

        [votes, rank, resource, tags, title]
        """
        tag_system = TagSystem(self.env)
        vote_system = VoteSystem(self.env)

        query = 'realm:wiki (%s) (%s)' % \
                (' or '.join(releases), ' or '.join(types))
        self.env.log.debug(query)
        tagged = tag_system.query(req, query)

        # Limit
        try:
            limit = int(req.args.get('limit', self.limit))
            data['limit_message'] = 'top %s' % limit
        except ValueError:
            data['limit_message'] = 'all'
            limit = 9999
        data['limit'] = limit

        # Query
        q = req.args.get('q', '')
        data['query'] = q
        query = Query(q.lower())

        # Build hacks list
        hacks = []
        for resource, tags in tagged:
            page = WikiPage(self.env, resource.id)
            if q:
                text = page.name.lower() + page.text.lower() + ' '.join(tags)
                if not query(text):
                    continue
            _, count, _ = vote_system.get_vote_counts(resource)
            match = self.title_extract.search(page.text)
            count_string = pluralise(count, 'vote')
            if match:
                title = '%s (%s)' % (match.group(1).strip(), count_string)
            else:
                title = '%s' % count_string
            hacks.append([count, None, resource, tags, title])

        # Rank
        total_hack_count = len(hacks)
        hacks = sorted(hacks, key=lambda i: -i[0])
        remainder = hacks[limit:]
        hacks = hacks[:limit] + random.sample(remainder,
                                              min(limit, len(remainder)))

        # Navigation
        #if len(hacks) >= limit:
        #    add_ctxtnav(req, builder.a('More', href='?action=more'))
        #    limit = len(hacks)
        #    data['limit'] = data['limit_message'] = limit
        #else:
        #    add_ctxtnav(req, 'More')
        #if q or limit != self.limit:
        #    add_ctxtnav(req, builder.a('Default', href='?action=default'))
        #else:
        #    add_ctxtnav(req, 'Default')
        #if total_hack_count > limit:
        #    add_ctxtnav(req, builder.a('All', href='?action=all'))
        #else:
        #    add_ctxtnav(req, 'All')
        #if limit > 10:
        #    limit = min(limit, len(hacks))
        #    add_ctxtnav(req, builder.a('Less', href='?action=less'))
        #else:
        #    add_ctxtnav(req, 'Less')
        #for i, hack in enumerate(hacks):
        #    hack[1] = i
        return hacks