bloodhound-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Joachim Dreimann <joachim.dreim...@wandisco.com>
Subject Re: First installation... my humble review
Date Tue, 12 Nov 2013 18:02:22 GMT


> On 12 Nov 2013, at 16:58, Gary Martin <gary.martin@wandisco.com> wrote:
> 
> Hi,
> 
> Just wondering how people felt about Olivier's contribution so far and the idea in general.
Obviously the code does need a bit more work as we probably need to have product repositories
isolated from the global environment.
> 
> While I think we definitely should maintain global repositories that products can link
to, the ability to keep a repository private to a specific product, even in the admin, makes
a lot of sense to me.
> 

I agree.

- Joe

> Cheers,
>    Gary
> 
>> On 07/11/13 16:40, Olivier Mauras wrote:
>> 
>> Please find attached two patches.
>> First one grants product owner admin rights to his products, and change the repository
management part to the one used by global one. This makes the product owner to create/delete/alias
repositories.
>> For the moment it's global, it would require some changes at DB level to make the
repositories isolated per product. I think a new table - "product_repositories" - with a new
"DbRepositoryProvider" class would be the less intrusive...
>> 
>> The second small patch modifies the theme to get "Source" tab to point to product/<id>/browser
instead of getting the wiki.
>> This indeed gives a "No node /" error as i haven't yet found my way in the code that
would point the product browser to the repository.
>> 
>> Sorry for the nasty two patches, i worked on installed app instead of the code...
This is all based on the latest stable 0.7.0.
>> 
>> Hope this will help.
>> 
>> Olivier
>> 
>> 
>> bh_p1.patch
>> 
>> 
>> diff --git a/product_admin.py b/product_admin.py
>> index 9c0fc42..df69e04 100644
>> --- a/product_admin.py
>> +++ b/product_admin.py
>> @@ -18,6 +18,8 @@
>>    """Admin panels for product management"""
>>  +from genshi.builder import tag
>> +
>>  from trac.admin.api import IAdminCommandProvider, AdminCommandError,\
>>      AdminCommandManager
>>  from trac.admin.console import TracAdmin, TRAC_VERSION
>> @@ -27,9 +29,9 @@ from trac.config import *
>>  from trac.perm import PermissionSystem
>>  from trac.resource import ResourceNotFound
>>  from trac.ticket.admin import TicketAdminPanel, _save_config
>> -from trac.util import lazy
>> -from trac.util.text import print_table, to_unicode, printerr, printout
>> -from trac.util.translation import _, N_, gettext, ngettext
>> +from trac.util import lazy, as_bool
>> +from trac.util.text import print_table, to_unicode, printerr, printout, breakable_path,
normalize_whitespace
>> +from trac.util.translation import _, N_, gettext, ngettext, tag_
>>  from trac.web.api import HTTPNotFound, IRequestFilter, IRequestHandler
>>  from trac.web.chrome import Chrome, add_notice, add_warning
>>  @@ -37,9 +39,10 @@ from multiproduct.env import ProductEnvironment
>>  from multiproduct.model import Product
>>  from multiproduct.perm import sudo
>>  -import multiproduct.versioncontrol
>> +#import multiproduct.versioncontrol
>>  import trac.versioncontrol.admin
>> -from trac.versioncontrol import DbRepositoryProvider, RepositoryManager
>> +from trac.versioncontrol import DbRepositoryProvider, RepositoryManager, \
>> +                                is_default
>>  from multiproduct.util import ReplacementComponent
>>    #--------------------------
>> @@ -335,8 +338,7 @@ class ProductAdminModule(Component):
>>          """
>>          if isinstance(self.env, ProductEnvironment) and \
>>                  handler is AdminModule(self.env) and \
>> -                not req.perm.has_permission('TRAC_ADMIN') and \
>> -                req.perm.has_permission('PRODUCT_ADMIN'):
>> +                not req.perm.has_permission('PRODUCT_ADMIN'):
>>              # Intercept admin request
>>              return self
>>          return handler
>> @@ -410,9 +412,27 @@ class ProductRepositoryAdminPanel(ReplacementComponent, trac.versioncontrol.admi
>>        def get_admin_panels(self, req):
>>          if 'VERSIONCONTROL_ADMIN' in req.perm:
>> -            yield ('versioncontrol', _('Version Control'), 'repository',
>> -                   _('Repository Links') if isinstance(self.env, ProductEnvironment)
>> -                    else _('Repositories'))
>> +            yield ('versioncontrol', _('Version Control'), 'repository',
>> +                _('Repositories'))
>> +
>> +    def _extend_info(self, reponame, info, editable):
>> +        """Extend repository info for rendering."""
>> +        info['name'] = reponame
>> +        if info.get('dir') is not None:
>> +            info['prettydir'] = breakable_path(info['dir']) or ''
>> +        info['hidden'] = as_bool(info.get('hidden'))
>> +        #info['editable'] = editable
>> +        info['editable'] = True
>> +        if not info.get('alias'):
>> +            try:
>> +                self.log.debug(reponame)
>> +                repos = RepositoryManager(self.env.parent).get_repository(reponame)
>> +                youngest_rev = repos.get_youngest_rev()
>> +                info['rev'] = youngest_rev
>> +                info['display_rev'] = repos.display_rev(youngest_rev)
>> +            except Exception:
>> +                pass
>> +        return info
>>        def render_admin_panel(self, req, category, page, path_info):
>>          if not isinstance(self.env, ProductEnvironment):
>> @@ -420,41 +440,143 @@ class ProductRepositoryAdminPanel(ReplacementComponent, trac.versioncontrol.admi
>>                  req, category, page, path_info)
>>            req.perm.require('VERSIONCONTROL_ADMIN')
>> -        db_provider = self.env[DbRepositoryProvider]
>> -
>> -        if req.method == 'POST' and db_provider:
>> -            if req.args.get('remove'):
>> -                repolist = req.args.get('sel')
>> -                if repolist:
>> -                    if isinstance(repolist, basestring):
>> -                        repolist = [repolist, ]
>> -                    for reponame in repolist:
>> -                        db_provider.unlink_product(reponame)
>> -            elif req.args.get('addlink') is not None and db_provider:
>> -                reponame = req.args.get('repository')
>> -                db_provider.link_product(reponame)
>> -            req.redirect(req.href.admin(category, page))
>> -
>> -        # Retrieve info for all product repositories
>> -        rm_product = RepositoryManager(self.env)
>> -        rm_product.reload_repositories()
>> -        all_product_repos = rm_product.get_all_repositories()
>> -        repositories = dict((reponame, self._extend_info(
>> -                                reponame, info.copy(), True))
>> -                            for (reponame, info) in
>> -                                all_product_repos.iteritems())
>> -        types = sorted([''] + rm_product.get_supported_types())
>> -
>> -        # construct a list of all repositores not linked to this product
>>          rm = RepositoryManager(self.env.parent)
>>          all_repos = rm.get_all_repositories()
>> -        unlinked_repositories = dict([(k, all_repos[k]) for k in
>> -            sorted(set(all_repos) - set(all_product_repos))])
>> +        db_provider = self.env[DbRepositoryProvider]
>> +
>> +        if path_info:
>> +            # Detail view
>> +            self.log.debug(path_info)
>> +            reponame = path_info if not is_default(path_info) else ''
>> +            info = all_repos.get(reponame)
>> +            if info is None:
>> +                raise TracError(_("Repository '%(repo)s' not found",
>> +                                  repo=path_info))
>> +            if req.method == 'POST':
>> +                if req.args.get('cancel'):
>> +                    req.redirect(req.href.admin(category, page))
>> +
>> +                elif db_provider and req.args.get('save'):
>> +                    # Modify repository
>> +                    changes = {}
>> +                    for field in db_provider.repository_attrs:
>> +                        value = normalize_whitespace(req.args.get(field))
>> +                        if (value is not None or field == 'hidden') \
>> +                                and value != info.get(field):
>> +                            changes[field] = value
>> +                    if 'dir' in changes \
>> +                            and not self._check_dir(req, changes['dir']):
>> +                        changes = {}
>> +                    if changes:
>> +                        db_provider.modify_repository(reponame, changes)
>> +                        add_notice(req, _('Your changes have been saved.'))
>> +                    name = req.args.get('name')
>> +                    resync = tag.tt('trac-admin $ENV repository resync "%s"'
>> +                                    % (name or '(default)'))
>> +                    if 'dir' in changes:
>> +                        msg = tag_('You should now run %(resync)s to '
>> +                                   'synchronize Trac with the repository.',
>> +                                   resync=resync)
>> +                        add_notice(req, msg)
>> +                    elif 'type' in changes:
>> +                        msg = tag_('You may have to run %(resync)s to '
>> +                                   'synchronize Trac with the repository.',
>> +                                   resync=resync)
>> +                        add_notice(req, msg)
>> +                    if name and name != path_info and not 'alias' in info:
>> +                        cset_added = tag.tt('trac-admin $ENV changeset '
>> +                                            'added "%s" $REV'
>> +                                            % (name or '(default)'))
>> +                        msg = tag_('You will need to update your post-commit '
>> +                                   'hook to call %(cset_added)s with the new '
>> +                                   'repository name.', cset_added=cset_added)
>> +                        add_notice(req, msg)
>> +                    if changes:
>> +                        req.redirect(req.href.admin(category, page))
>> +
>> +            Chrome(self.env).add_wiki_toolbars(req)
>> +            data = {'view': 'detail', 'reponame': reponame}
>>  -        data = {'types': types, 'default_type': rm_product.repository_type,
>> -                'repositories': repositories,
>> -                'unlinked_repositories': unlinked_repositories}
>> -        return 'repository_links.html', data
>> +        else:
>> +            if req.method == 'POST':
>> +                # Add a repository
>> +                if db_provider and req.args.get('add_repos'):
>> +                    name = req.args.get('name')
>> +                    type_ = req.args.get('type')
>> +                    # Avoid errors when copy/pasting paths
>> +                    dir = normalize_whitespace(req.args.get('dir', ''))
>> +                    if name is None or type_ is None or not dir:
>> +                        add_warning(req, _('Missing arguments to add a '
>> +                                            'repository.'))
>> +                    elif self._check_dir(req, dir):
>> +                        db_provider.add_repository(name, dir, type_)
>> +                        name = name or '(default)'
>> +                        add_notice(req, _('The repository "%(name)s" has been '
>> +                                          'added.', name=name))
>> +                        resync = tag.tt('trac-admin $ENV repository resync '
>> +                                        '"%s"' % name)
>> +                        msg = tag_('You should now run %(resync)s to '
>> +                                   'synchronize Trac with the repository.',
>> +                                    resync=resync)
>> +                        add_notice(req, msg)
>> +                        cset_added = tag.tt('trac-admin $ENV changeset '
>> +                                            'added "%s" $REV' % name)
>> +                        msg = tag_('You should also set up a post-commit hook '
>> +                                   'on the repository to call %(cset_added)s '
>> +                                   'for each committed changeset.',
>> +                                   cset_added=cset_added)
>> +                        add_notice(req, msg)
>> +                        req.redirect(req.href.admin(category, page))
>> +
>> +                # Add a repository alias
>> +                elif db_provider and req.args.get('add_alias'):
>> +                    name = req.args.get('name')
>> +                    alias = req.args.get('alias')
>> +                    if name is not None and alias is not None:
>> +                        db_provider.add_alias(name, alias)
>> +                        add_notice(req, _('The alias "%(name)s" has been '
>> +                                          'added.', name=name or '(default)'))
>> +                        req.redirect(req.href.admin(category, page))
>> +                    add_warning(req, _('Missing arguments to add an '
>> +                                       'alias.'))
>> +
>> +                # Refresh the list of repositories
>> +                elif req.args.get('refresh'):
>> +                    req.redirect(req.href.admin(category, page))
>> +
>> +                # Remove repositories
>> +                elif db_provider and req.args.get('remove'):
>> +                    sel = req.args.getlist('sel')
>> +                    if sel:
>> +                        for name in sel:
>> +                            db_provider.remove_repository(name)
>> +                        add_notice(req, _('The selected repositories have '
>> +                                          'been removed.'))
>> +                        req.redirect(req.href.admin(category, page))
>> +                    add_warning(req, _('No repositories were selected.'))
>> +
>> +            data = {'view': 'list'}
>> +
>> +        # Force repo refresh - should already happen :/
>> +        rm.reload_repositories()
>> +
>> +        #self.log.debug(all_repos)
>> +
>> +        db_repos = {}
>> +        if db_provider is not None:
>> +            db_repos = dict(db_provider.get_repositories())
>> +
>> +        # Prepare common rendering data
>> +        repositories = dict((reponame, self._extend_info(reponame, info.copy(),
>> +                                                         reponame in db_repos))
>> +                            for (reponame, info) in all_repos.iteritems())
>> +
>> +        #self.log.debug(repositories)
>> +        types = sorted([''] + rm.get_supported_types())
>> +        data.update({'types': types, 'default_type': rm.repository_type,
>> +                     'repositories': repositories})
>> +
>> +        return 'admin_repositories.html', data
>>    trac.versioncontrol.admin.RepositoryAdminPanel = ProductRepositoryAdminPanel
>>  
>> 
>> bh_p2.patch
>> 
>> 
>> diff --git a/theme.py b/theme.py
>> index 6c7f81b..8686a26 100644
>> --- a/theme.py
>> +++ b/theme.py
>> @@ -443,8 +443,7 @@ class BloodhoundTheme(ThemeBase):
>>              bm = self.env[BrowserModule]
>>              if bm and not list(bm.get_navigation_items(req)):
>>                  yield ('mainnav', 'browser',
>> -                       tag.a(_('Browse Source'),
>> -                             href=req.href.wiki('TracRepositoryAdmin')))
>> +                       tag.a(_('Browse Source'), href=req.href.browser()))
>>      class QuickCreateTicketDialog(Component):
> 

Mime
View raw message