bloodhound-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Gary Martin <gary.mar...@wandisco.com>
Subject Re: First installation... my humble review
Date Tue, 12 Nov 2013 16:58:03 GMT
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.

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