Author: matevz
Date: Sat Mar 30 16:22:41 2013
New Revision: 1462792

URL: http://svn.apache.org/r1462792
Log:
Ref. #470, #430 - implemented admin pages for linking repositories to products

Added:
    
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/templates/repository_links.html
Modified:
    
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/product_admin.py
    
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/versioncontrol.py

Modified: 
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/product_admin.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/product_admin.py?rev=1462792&r1=1462791&r2=1462792&view=diff
==============================================================================
--- 
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/product_admin.py
 (original)
+++ 
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/product_admin.py
 Sat Mar 30 16:22:41 2013
@@ -36,6 +36,11 @@ from multiproduct.env import ProductEnvi
 from multiproduct.model import Product
 from multiproduct.perm import sudo
 
+import multiproduct.versioncontrol
+import trac.versioncontrol.admin
+from trac.versioncontrol import DbRepositoryProvider, RepositoryManager
+from multiproduct.util import ReplacementComponent
+
 #--------------------------
 # Product admin panel
 #--------------------------
@@ -364,3 +369,63 @@ class DefaultProductAdminWhitelist(Compo
         #yield 'accounts', 'users'
         yield 'ticket', '*'
         yield 'versioncontrol', 'repository'
+
+
+class ProductRepositoryAdminPanel(ReplacementComponent, 
trac.versioncontrol.admin.RepositoryAdminPanel):
+    """Web admin panel for repository administration, product-aware."""
+
+    implements(trac.admin.IAdminPanelProvider)
+
+    # IAdminPanelProvider methods
+
+    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'))
+
+    def render_admin_panel(self, req, category, page, path_info):
+        if not isinstance(self.env, ProductEnvironment):
+            return super(ProductRepositoryAdminPanel, self).render_admin_panel(
+                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 = { k: all_repos[k] for k in sorted(
+            set(all_repos) - set(all_product_repos)) }
+
+        data = {'types': types, 'default_type': rm_product.repository_type,
+                'repositories': repositories,
+                'unlinked_repositories': unlinked_repositories}
+        return 'repository_links.html', data
+
+trac.versioncontrol.admin.RepositoryAdminPanel = ProductRepositoryAdminPanel
+
+

Added: 
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/templates/repository_links.html
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/templates/repository_links.html?rev=1462792&view=auto
==============================================================================
--- 
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/templates/repository_links.html
 (added)
+++ 
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/templates/repository_links.html
 Sat Mar 30 16:22:41 2013
@@ -0,0 +1,93 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
+<html xmlns="http://www.w3.org/1999/xhtml";
+      xmlns:xi="http://www.w3.org/2001/XInclude";
+      xmlns:py="http://genshi.edgewall.org/";
+      xmlns:i18n="http://genshi.edgewall.org/i18n";>
+  <xi:include href="admin.html" />
+  <head>
+    <title>Repository Links</title>
+  </head>
+  <body>
+    <h2>Manage repository links</h2>
+    <div class="row">
+      <div class="span8">
+        <form id="trac-repository_table" method="post" action=""
+         py:with="sorted_repos = sorted(repositories.iteritems(), key=lambda 
item: item[0].lower())">
+          <table py:if="sorted_repos" id="trac-reposlist"
+           class="table table-striped table-condensed table-bordered">
+            <thead>
+              <tr>
+                <th class="sel"><i class="icon-check"></i></th>
+                <th>Name</th>
+                <th>Type</th>
+                <th>Directory</th>
+                <th>Revision</th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr py:for="(reponame, info) in sorted_repos">
+                <td class="sel"><input py:if="info.editable" type="checkbox" 
name="sel" value="$info.name"/></td>
+                <td class="name">
+                  <a href="${panel_href(info.name or 
'(default)')}">${info.name or _('(default)')}</a>
+                </td>
+                <td>${(info.type or _('(default)')) if not info.alias else 
None}</td>
+                <td py:choose="">
+                  <py:when test="info.dir">$info.prettydir</py:when>
+                  <em py:otherwise="" i18n:msg="repo">Alias of ${info.alias or 
_('(default)')}</em>
+                </td>
+                <td><a py:if="info.rev" href="${href.changeset(info.rev, 
reponame) or None}">[$info.display_rev]</a></td>
+              </tr>
+            </tbody>
+          </table>
+          
+          <div py:if="not sorted_repos" class="alert alert-info">No linked 
repositories.</div>
+
+          <div class="buttons">
+            <input class="btn" type="submit" name="refresh" 
value="${_('Refresh')}"/>
+            <input py:if="sorted_repos" class="btn" type="submit"
+             name="remove" value="${_('Remove selected items')}"/>
+          </div>
+        </form>
+      </div>
+
+      <div class="span8">
+        <h4>Add repository link</h4>
+        <py:choose test="">
+          <py:when test="unlinked_repositories">
+            <form class="form-inline" id="repository-link" method="post" 
action="">
+              <select name="repository">
+                <option py:for="r,v in unlinked_repositories.iteritems()" 
value="$r">$v.name</option>
+              </select>
+              <button type="submit" name="addlink" class="btn">Add</button>
+            </form>
+          </py:when>
+          <py:otherwise>
+            <div class="alert alert-info">No repositories to add.</div>
+          </py:otherwise>
+        </py:choose>
+      </div>
+    </div>
+  </body>
+</html>
+

Modified: 
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/versioncontrol.py
URL: 
http://svn.apache.org/viewvc/bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/versioncontrol.py?rev=1462792&r1=1462791&r2=1462792&view=diff
==============================================================================
--- 
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/versioncontrol.py
 (original)
+++ 
bloodhound/branches/bep_0003_multiproduct/bloodhound_multiproduct/multiproduct/versioncontrol.py
 Sat Mar 30 16:22:41 2013
@@ -22,10 +22,12 @@ import os.path
 from trac.util.concurrency import threading
 from trac.core import implements
 import trac.versioncontrol.api
+from trac.versioncontrol import RepositoryManager
 import trac.admin
 import trac.web.api
 import trac.resource
 from multiproduct.util import ReplacementComponent
+from multiproduct.env import ProductEnvironment
 
 class DbRepositoryProvider(ReplacementComponent, 
trac.versioncontrol.api.DbRepositoryProvider):
     """Inplace replacement for trac.versioncontrol.api.DbRepositoryProvider. 
Filters
@@ -52,7 +54,7 @@ class DbRepositoryProvider(ReplacementCo
                     repos.setdefault(id, {})[name] = value
             reponames = {}
             for id, info in repos.iteritems():
-                if 'product' in info and \
+                if not 'product' in info or \
                    not self.env.product.prefix in info['product'].split(','):
                     # skip repository if not soft linked from the current
                     # product environment
@@ -66,6 +68,25 @@ class DbRepositoryProvider(ReplacementCo
         else:
             return super(DbRepositoryProvider, self).get_repositories()
 
+    def link_product(self, reponame):
+        if not isinstance(self.env, ProductEnvironment):
+            return
+        rm = RepositoryManager(self.env.parent)
+        repoid = rm.get_repository_id(reponame)
+        with self.env.db_direct_transaction as db:
+            db("""INSERT INTO repository (id, name, value)
+                    VALUES(%s, 'product', '%s')""" %
+                    (repoid, self.env.product.prefix))
+
+    def unlink_product(self, reponame):
+        if not isinstance(self.env, ProductEnvironment):
+            return
+        rm = RepositoryManager(self.env.parent)
+        repoid = rm.get_repository_id(reponame)
+        with self.env.db_direct_transaction as db:
+            db("""DELETE FROM repository WHERE id=%s AND name='product'
+                    AND value='%s'""" % (repoid, self.env.product.prefix))
+
 trac.versioncontrol.api.DbRepositoryProvider = DbRepositoryProvider
 trac.versioncontrol.DbRepositoryProvider = DbRepositoryProvider
 


Reply via email to