You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by tv...@apache.org on 2013/08/20 15:28:27 UTC

[01/25] git commit: [#6553] fix taskd error logging (again)

Updated Branches:
  refs/heads/db/6482 7ece9b7d5 -> 908217cf3 (forced update)


[#6553] fix taskd error logging (again)

* don't re-raise an error after we've logged it already
* commit [2b7892] was incorrect in saying that errormiddleware wasn't used for taskd,
only StatusCodeRedirect was bypassed.  Now we *do* bypass error middleware also


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/9a13632b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/9a13632b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/9a13632b

Branch: refs/heads/db/6482
Commit: 9a13632b5524702b4131fa33719c015cb4f2c58f
Parents: fea4b5f
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Mon Aug 12 19:33:17 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Tue Aug 13 21:15:25 2013 +0000

----------------------------------------------------------------------
 Allura/allura/config/middleware.py | 12 ++++++------
 Allura/allura/model/monq_model.py  |  1 -
 2 files changed, 6 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/9a13632b/Allura/allura/config/middleware.py
----------------------------------------------------------------------
diff --git a/Allura/allura/config/middleware.py b/Allura/allura/config/middleware.py
index bd73dce..1e9b0c7 100644
--- a/Allura/allura/config/middleware.py
+++ b/Allura/allura/config/middleware.py
@@ -100,7 +100,7 @@ def _make_core_app(root, global_conf, full_stack=True, **app_conf):
 
     # Configure EW variable provider
     ew.render.TemplateEngine.register_variable_provider(get_tg_vars)
-    
+
     # Set FormEncode language to english, as we don't support any other locales
     formencode.api.set_stdtranslation(domain='FormEncode', languages=['en'])
 
@@ -161,16 +161,16 @@ def _make_core_app(root, global_conf, full_stack=True, **app_conf):
     #    streaming=true ensures they won't be cleaned up till
     #    the WSGI application's iterator is exhausted
     app = RegistryManager(app, streaming=True)
-    # Converts exceptions to HTTP errors, shows traceback in debug mode
-    tg.error.footer_html = '<!-- %s %s -->'  # don't use TG footer with extra CSS & images that take time to load
-    app = tg.error.ErrorHandler(app, global_conf, **config['pylons.errorware'])
     # Make sure that the wsgi.scheme is set appropriately when we
     # have the funky HTTP_X_SFINC_SSL  environ var
     if asbool(app_conf.get('auth.method', 'local')=='sfx'):
         app = set_scheme_middleware(app)
-    # Redirect some status codes to /error/document
+    # "task" wsgi would get a 2nd request to /error/document if we used this middleware
     if config.get('override_root') != 'task':
-        # "task" wsgi would get a 2nd request to /error/document if we used this middleware
+        # Converts exceptions to HTTP errors, shows traceback in debug mode
+        tg.error.footer_html = '<!-- %s %s -->'  # don't use TG footer with extra CSS & images that take time to load
+        app = tg.error.ErrorHandler(app, global_conf, **config['pylons.errorware'])
+        # Redirect some status codes to /error/document
         if asbool(config['debug']):
             app = StatusCodeRedirect(app, base_config.handle_status_codes)
         else:

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/9a13632b/Allura/allura/model/monq_model.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/monq_model.py b/Allura/allura/model/monq_model.py
index 9fa0064..3163ea5 100644
--- a/Allura/allura/model/monq_model.py
+++ b/Allura/allura/model/monq_model.py
@@ -264,7 +264,6 @@ class MonQTask(MappedClass):
                 log.error(self.result)
             else:
                 self.result = traceback.format_exc()
-            raise
         finally:
             self.time_stop = datetime.utcnow()
             session(self).flush(self)


[14/25] git commit: [#6506] Added TracWikiImporter to requirements-sf.txt

Posted by tv...@apache.org.
[#6506] Added TracWikiImporter to requirements-sf.txt

Signed-off-by: Cory Johns <cj...@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/3e67aa66
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/3e67aa66
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/3e67aa66

Branch: refs/heads/db/6482
Commit: 3e67aa667a132c162719e394a5963261dfb1345d
Parents: 0875863
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Fri Aug 16 00:27:49 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Fri Aug 16 00:27:49 2013 +0000

----------------------------------------------------------------------
 requirements-sf.txt | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3e67aa66/requirements-sf.txt
----------------------------------------------------------------------
diff --git a/requirements-sf.txt b/requirements-sf.txt
index b774faf..6d006ec 100644
--- a/requirements-sf.txt
+++ b/requirements-sf.txt
@@ -20,6 +20,7 @@ wsgipreload==1.2
 pyzmq==2.1.7
 html2text==3.200.3dev-20121112
 PyMollom==0.1
+TracWikiImporter=0.1.0
 
 # use version built from https://github.com/johnsca/GitPython/commits/tv/6000
 # for unmerged fixes for [#5411], [#6000], and [#6078]


[04/25] git commit: [#4818] sort entry points for reliable ordering

Posted by tv...@apache.org.
[#4818] sort entry points for reliable ordering


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/c7006032
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/c7006032
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/c7006032

Branch: refs/heads/db/6482
Commit: c70060322432813497d338c502d734eafe808bd4
Parents: c0dbb7f
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Aug 9 21:47:00 2013 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Wed Aug 14 13:12:13 2013 +0000

----------------------------------------------------------------------
 Allura/allura/ext/admin/admin_main.py | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c7006032/Allura/allura/ext/admin/admin_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index 98a34c8..06aaec1 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -140,7 +140,8 @@ class AdminApp(Application):
             links.append(None)
             links.append(SitemapEntry('Help', nbhd_admin_url+ 'help/'))
 
-        for name, admin_extension in g.entry_points['admin'].iteritems():
+        for ep_name in sorted(g.entry_points['admin'].keys()):
+            admin_extension = g.entry_points['admin'][ep_name]
             admin_extension().update_project_sidebar_menu(links)
 
         return links
@@ -156,7 +157,8 @@ class AdminExtensionLookup(object):
 
     @expose()
     def _lookup(self, name, *remainder):
-        for ext_name, admin_extension in g.entry_points['admin'].iteritems():
+        for ep_name in sorted(g.entry_points['admin'].keys()):
+            admin_extension = g.entry_points['admin'][ep_name]
             controller = admin_extension().project_admin_controllers.get(name)
             if controller:
                 return controller(), remainder


[07/25] git commit: [#4818] add admin extensions

Posted by tv...@apache.org.
[#4818] add admin extensions


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/5f836baa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/5f836baa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/5f836baa

Branch: refs/heads/db/6482
Commit: 5f836baa5b741bb24175233b90276df90a7fa11b
Parents: dfafc62
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Aug 9 19:41:07 2013 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Wed Aug 14 13:12:13 2013 +0000

----------------------------------------------------------------------
 Allura/allura/ext/admin/admin_main.py        | 17 ++++++++++
 Allura/allura/lib/app_globals.py             |  1 +
 Allura/allura/lib/plugin.py                  | 32 +++++++++++++++++++
 Allura/allura/tests/functional/test_admin.py | 39 +++++++++++++++++++++++
 4 files changed, 89 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5f836baa/Allura/allura/ext/admin/admin_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index f8c8983..92efacb 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -139,6 +139,10 @@ class AdminApp(Application):
             links.append(SitemapEntry('Statistics', nbhd_admin_url+ 'stats/'))
             links.append(None)
             links.append(SitemapEntry('Help', nbhd_admin_url+ 'help/'))
+
+        for name, admin_extension in g.entry_points['admin'].iteritems():
+            admin_extension().update_project_sidebar_menu(links)
+
         return links
 
     def admin_menu(self):
@@ -147,6 +151,18 @@ class AdminApp(Application):
     def install(self, project):
         pass
 
+
+class AdminExtensionLookup(object):
+    
+    @expose()
+    def _lookup(self, name, *remainder):
+        for ext_name, admin_extension in g.entry_points['admin'].iteritems():
+            controller = admin_extension().project_admin_controllers.get(name)
+            if controller:
+                return controller(), remainder
+        raise exc.HTTPNotFound, name
+
+
 class ProjectAdminController(BaseController):
 
     def _check_security(self):
@@ -156,6 +172,7 @@ class ProjectAdminController(BaseController):
         self.permissions = PermissionsController()
         self.groups = GroupsController()
         self.audit = AuditController()
+        self.ext = AdminExtensionLookup()
 
     @with_trailing_slash
     @expose('jinja:allura.ext.admin:templates/project_admin.html')

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5f836baa/Allura/allura/lib/app_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index bdcacf7..7f45f69 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -197,6 +197,7 @@ class Globals(object):
             spam=_cache_eps('allura.spam'),
             stats=_cache_eps('allura.stats'),
             site_stats=_cache_eps('allura.site_stats'),
+            admin=_cache_eps('allura.admin'),
             )
 
         # Zarkov logger

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5f836baa/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 4afd9fc..f972c26 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -915,3 +915,35 @@ class LocalUserPreferencesProvider(UserPreferencesProvider):
         users = M.User.query.find(dict(
                 display_name=name_regex)).sort('username').all()
         return users
+
+
+class AdminExtension(object):
+    """
+    A base class for extending the admin areas in Allura.
+
+    After extending this, expose the app by adding an entry point in your
+    setup.py::
+
+        [allura.admin]
+        myadmin = foo.bar.baz:MyCustomAdmin
+
+    :ivar dict project_admin_controllers: Mapping of str (url component) to
+        Controllers.  Can be implemented as a ``@property`` function.  The str
+        url components will be mounted at /p/someproject/admin/ext/STR/ and will
+        invoke the Controller.
+    """
+
+    project_admin_controllers = {}
+
+    def update_project_sidebar_menu(self, sidebar_links):
+        """
+        Implement this function to modify the project sidebar.
+        Check `c.project` if you want to limit when this displays
+        (e.g. nbhd project, subproject, etc)
+
+        :param sidebar_links: project admin side bar links
+        :type sidebar_links: list of :class:`allura.app.SitemapEntry`
+
+        :rtype: ``None``
+        """
+        pass

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5f836baa/Allura/allura/tests/functional/test_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_admin.py b/Allura/allura/tests/functional/test_admin.py
index 7d630c9..adeb71b 100644
--- a/Allura/allura/tests/functional/test_admin.py
+++ b/Allura/allura/tests/functional/test_admin.py
@@ -24,6 +24,10 @@ from contextlib import contextmanager
 import PIL
 from nose.tools import assert_equals
 from ming.orm.ormsession import ThreadLocalORMSession
+from tg import expose
+from pylons import tmpl_context as c, app_globals as g
+import mock
+from BeautifulSoup import BeautifulSoup
 
 try:
     import sfx
@@ -33,6 +37,8 @@ except ImportError:
 from allura.tests import TestController
 from allura.tests import decorators as td
 from allura import model as M
+from allura.app import SitemapEntry
+from allura.lib.plugin import AdminExtension
 
 @contextmanager
 def audits(*messages):
@@ -726,3 +732,36 @@ class TestProjectAdmin(TestController):
         assert {u'text': u'Does not have permission create', u'has': u'no', u'name': u'create'} in r.json[admin_id]
         assert {u'text': u'Does not have permission create', u'has': u'no', u'name': u'create'} in r.json[mem_id]
         assert {u'text': u'Does not have permission create', u'has': u'no', u'name': u'create'} in r.json[anon_id]
+
+
+    def test_admin_extension_sidebar(self):
+
+        class FooSettingsController(object):
+            @expose()
+            def index(self, *a, **kw):
+                return 'here the foo settings go'
+
+
+        class FooSettingsExtension(AdminExtension):
+            def update_project_sidebar_menu(self, sidebar_links):
+                base_url = c.project.url()+'admin/ext/'
+                sidebar_links.append(SitemapEntry('Foo Settings', base_url+'foo'))
+
+            @property
+            def project_admin_controllers(self):
+                return {
+                    'foo': FooSettingsController,
+                }
+
+        eps = {
+            'admin': {
+                'foo-settings': FooSettingsExtension,
+            }
+        }
+
+        with mock.patch.dict(g.entry_points, eps):
+            main_page = self.app.get('/admin/')
+            foo_page = main_page.click(description='Foo Settings')
+            url = foo_page.environ['PATH_INFO']
+            assert url.endswith('/admin/ext/foo'), url
+            assert_equals('here the foo settings go', foo_page.body)


[03/25] git commit: [#5177] Fixed default values for source and target_branch when creating a merge request

Posted by tv...@apache.org.
[#5177] Fixed default values for source and target_branch when creating a merge request

Signed-off-by: Cory Johns <cj...@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/45732c0c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/45732c0c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/45732c0c

Branch: refs/heads/db/6482
Commit: 45732c0cd2952b6e2d156f4f2ec1a21f20f9b317
Parents: 9a13632
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Thu Aug 8 14:03:29 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Aug 13 21:28:33 2013 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/repository.py         | 14 +++++++++++---
 Allura/allura/lib/repository.py                 |  5 ++++-
 Allura/allura/templates/repo/request_merge.html |  2 +-
 3 files changed, 16 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/45732c0c/Allura/allura/controllers/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index aaf9f6a..36b4bdc 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -162,9 +162,16 @@ class RepoRootController(BaseController, FeedController):
     def request_merge(self, branch=None, **kw):
         security.require(security.has_access(c.app.repo, 'admin'))
         c.form = self.mr_widget
-        if branch is None:
-            source_branch=c.app.default_branch_name
-        return dict(source_branch=source_branch)
+        if branch in c.form.source_branches:
+            source_branch = branch
+        else:
+            source_branch = c.app.default_branch_name
+        with c.app.repo.push_upstream_context():
+            target_branch = c.app.default_branch_name
+        return {
+                'source_branch': source_branch,
+                'target_branch': target_branch,
+            }
 
     @expose()
     @require_post()
@@ -417,6 +424,7 @@ class CommitBrowser(BaseController):
     def __init__(self, revision):
         self._revision = revision
         self._commit = c.app.repo.commit(revision)
+        c.revision = revision
         if self._commit is None:
             raise exc.HTTPNotFound
         self.tree = self.TreeBrowserClass(self._commit, tree=self._commit.tree)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/45732c0c/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 83df6b2..65b68c8 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -127,7 +127,10 @@ class RepositoryApp(Application):
                     self.repo.upstream_repo.name)
                 ]
             if not c.app.repo.is_empty() and has_access(c.app.repo, 'admin'):
-                links.append(SitemapEntry('Request Merge', c.app.url + 'request_merge',
+                merge_url = c.app.url + 'request_merge'
+                if getattr(c, 'revision', None):
+                    merge_url = merge_url + '?branch=' + h.urlquote(c.revision)
+                links.append(SitemapEntry('Request Merge', merge_url,
                              ui_icon=g.icons['merge'],
                              ))
             pending_upstream_merges = self.repo.pending_upstream_merges()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/45732c0c/Allura/allura/templates/repo/request_merge.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/repo/request_merge.html b/Allura/allura/templates/repo/request_merge.html
index 02b9061..a55a67c 100644
--- a/Allura/allura/templates/repo/request_merge.html
+++ b/Allura/allura/templates/repo/request_merge.html
@@ -29,5 +29,5 @@
 {% block header %}Request merge of {{c.app.config.options.mount_label}} {% endblock %}
 
 {% block content %}
-  {{ c.form.display(action='do_request_merge', value=dict(source_branch=source_branch))}}
+  {{ c.form.display(action='do_request_merge', value=dict(source_branch=source_branch, target_branch=target_branch))}}
 {% endblock %}


[21/25] git commit: Fixed tab indents

Posted by tv...@apache.org.
Fixed tab indents


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/d9248352
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/d9248352
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/d9248352

Branch: refs/heads/db/6482
Commit: d9248352ac5e8dfe160a9d4d189b6be783c6e431
Parents: ba700e3
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Mon Aug 19 20:31:01 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Mon Aug 19 20:31:01 2013 +0000

----------------------------------------------------------------------
 Allura/allura/lib/utils.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/d9248352/Allura/allura/lib/utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index 4bd1e3c..abd3fd4 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -503,6 +503,6 @@ class ForgeHTMLSanitizer(_HTMLSanitizer):
         if 'iframe' in self.acceptable_elements:
             self.acceptable_elements.remove('iframe')
         if (tag == 'iframe') and (dict(attrs).get('src', '').startswith('http://www.youtube.com/embed/') or
-				  dict(attrs).get('src', '').startswith('https://www.gittip.com/')):
+                                  dict(attrs).get('src', '').startswith('https://www.gittip.com/')):
             self.acceptable_elements.append('iframe')
         _HTMLSanitizer.unknown_starttag(self, tag, attrs)


[17/25] git commit: [#6422] Added CHANGES file for ASF release

Posted by tv...@apache.org.
[#6422] Added CHANGES file for ASF release

Signed-off-by: Cory Johns <cj...@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/afb7bb4d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/afb7bb4d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/afb7bb4d

Branch: refs/heads/db/6482
Commit: afb7bb4d34141f22ac63be19986355065f7febd5
Parents: 1c62317
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Mon Aug 19 14:46:25 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Mon Aug 19 14:46:25 2013 +0000

----------------------------------------------------------------------
 CHANGES | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/afb7bb4d/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..a9ba287
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,3 @@
+Version 1.0.0  (August 2013)
+
+ * Initial ASF Incubation release


[19/25] git commit: Fixed syntax documentation of Gittip button for Wiki tool

Posted by tv...@apache.org.
Fixed syntax documentation of Gittip button for Wiki tool


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/ed2ca56a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/ed2ca56a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/ed2ca56a

Branch: refs/heads/db/6482
Commit: ed2ca56a4364ea336ec8f1d4762033e30dd66cd1
Parents: e81ffc0
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Mon Aug 19 19:59:34 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Mon Aug 19 19:59:34 2013 +0000

----------------------------------------------------------------------
 Allura/allura/templates/jinja_master/lib.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/ed2ca56a/Allura/allura/templates/jinja_master/lib.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/jinja_master/lib.html b/Allura/allura/templates/jinja_master/lib.html
index 9d8b3c1..4ca02e9 100644
--- a/Allura/allura/templates/jinja_master/lib.html
+++ b/Allura/allura/templates/jinja_master/lib.html
@@ -707,7 +707,7 @@ allowed, permitting basic styling and layout: &lt;div markdown style="float:left
 </pre></div>
 
 <h2 id="md_ex_gittip_button{{id}}">Gittip Button</h2>
-<p>You can display a Gittip button in your wiki. You will need to set your gittip handle in the Metadata section of the Admin tool for your project.</p>
+<p>You can display a Gittip button in your wiki. You will need to provide your gittip handle in the username parameter.</p>
 <div class="codehilite"><pre>
 [[gittip_button username=foo]]
 </pre></div>


[20/25] git commit: [#6532] ticket:412 ignore forks with non existing app_config_id

Posted by tv...@apache.org.
[#6532] ticket:412 ignore forks with non existing app_config_id


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/ba700e34
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/ba700e34
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/ba700e34

Branch: refs/heads/db/6482
Commit: ba700e34e495ad7dfb673cc875e6f048645cc02e
Parents: ed2ca56
Author: Anton Kasyanov <mi...@gmail.com>
Authored: Mon Aug 19 13:25:42 2013 +0300
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Mon Aug 19 20:16:30 2013 +0000

----------------------------------------------------------------------
 Allura/allura/model/repository.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/ba700e34/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index e29cd0a..9db3747 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -533,7 +533,8 @@ class Repository(Artifact, ActivityObject):
 
     @property
     def forks(self):
-        return self.query.find({'upstream_repo.name': self.url()}).all()
+        all_forks = self.query.find({'upstream_repo.name': self.url()}).all()
+        return filter(lambda fork: fork.app_config is not None, all_forks)
 
     def tarball(self, revision, path=None):
         if path:


[09/25] git commit: [#6480] Improvements to Trac importer

Posted by tv...@apache.org.
[#6480] Improvements to Trac importer

- Disable email notifications during import
- Retry time-out HTTP requests
- Add option to supply user-map file

Signed-off-by: Tim Van Steenburgh <tv...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/675495e3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/675495e3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/675495e3

Branch: refs/heads/db/6482
Commit: 675495e39723e593cba00cdcf84e3cb02d00e4b0
Parents: 6657808
Author: Tim Van Steenburgh <tv...@gmail.com>
Authored: Mon Aug 12 18:06:43 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Thu Aug 15 16:08:41 2013 +0000

----------------------------------------------------------------------
 Allura/allura/lib/decorators.py                 | 75 +++++++++++---------
 Allura/allura/lib/helpers.py                    | 71 +++++++++++++++++-
 Allura/allura/lib/import_api.py                 |  2 +-
 Allura/allura/lib/spam/__init__.py              |  2 +-
 Allura/allura/lib/validators.py                 | 30 ++++++++
 Allura/allura/scripts/trac_export.py            |  9 ++-
 Allura/allura/tests/test_decorators.py          | 39 ++++++++++
 Allura/allura/tests/test_helpers.py             | 42 +++++++++++
 Allura/allura/tests/test_validators.py          | 36 ++++++++++
 ForgeBlog/forgeblog/command/rssfeeds.py         |  2 +-
 ForgeImporters/forgeimporters/base.py           |  5 +-
 .../forgeimporters/templates/project_base.html  | 11 ++-
 ForgeImporters/forgeimporters/trac/project.py   |  2 +
 .../forgeimporters/trac/templates/project.html  | 14 +++-
 .../trac/templates/tickets/index.html           |  5 +-
 .../forgeimporters/trac/tests/test_tickets.py   | 12 +++-
 ForgeImporters/forgeimporters/trac/tickets.py   | 12 ++--
 ForgeTracker/forgetracker/import_support.py     | 24 +++++--
 ForgeTracker/forgetracker/tracker_main.py       | 23 +++---
 ForgeWiki/forgewiki/wiki_main.py                | 23 +++---
 20 files changed, 354 insertions(+), 85 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/Allura/allura/lib/decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/decorators.py b/Allura/allura/lib/decorators.py
index 36fb2de..137152e 100644
--- a/Allura/allura/lib/decorators.py
+++ b/Allura/allura/lib/decorators.py
@@ -28,16 +28,49 @@ from tg import request, redirect
 
 from webob import exc
 
-def task(func):
-    '''Decorator to add some methods to task functions'''
-    def post(*args, **kwargs):
-        from allura import model as M
-        delay = kwargs.pop('delay', 0)
-        return M.MonQTask.post(func, args, kwargs, delay=delay)
-    # if decorating a class, have to make it a staticmethod
-    # or it gets a spurious cls argument
-    func.post = staticmethod(post) if inspect.isclass(func) else post
-    return func
+from pylons import tmpl_context as c
+from allura.lib import helpers as h
+
+
+def _get_model():
+    from allura import model as M
+    return M
+
+def task(*args, **kw):
+    """Decorator that adds a ``.post()`` function to the decorated callable.
+
+    Calling <original_callable>.post(*args, **kw) queues the callable for
+    execution by a background worker process. All parameters must be
+    BSON-serializable.
+
+    Example usage:
+
+    @task
+    def myfunc():
+        pass
+
+    @task(notifications_disabled=True)
+    def myotherfunc():
+        # No email notifications will be sent for c.project during this task
+        pass
+
+    """
+    def task_(func):
+        def post(*args, **kwargs):
+            delay = kwargs.pop('delay', 0)
+            project = getattr(c, 'project', None)
+            cm = (h.notifications_disabled if project and
+                    kw.get('notifications_disabled') else h.null_contextmanager)
+            with cm(project):
+                M = _get_model()
+                return M.MonQTask.post(func, args, kwargs, delay=delay)
+        # if decorating a class, have to make it a staticmethod
+        # or it gets a spurious cls argument
+        func.post = staticmethod(post) if inspect.isclass(func) else post
+        return func
+    if len(args) == 1 and callable(args[0]):
+        return task_(args[0])
+    return task_
 
 class event_handler(object):
     '''Decorator to register event handlers'''
@@ -155,28 +188,6 @@ class log_action(object): # pragma no cover
         extra['referer_link'] = referer_link
         return extra
 
-class exceptionless(object):
-    '''Decorator making the decorated function return 'error_result' on any
-    exceptions rather than propagating exceptions up the stack
-    '''
-
-    def __init__(self, error_result, log=None):
-        self.error_result = error_result
-        self.log = log
-
-    def __call__(self, fun):
-        fname = 'exceptionless(%s)' % fun.__name__
-        def inner(*args, **kwargs):
-            try:
-                return fun(*args, **kwargs)
-            except Exception as e:
-                if self.log:
-                    self.log.exception('Error calling %s(args=%s, kwargs=%s): %s',
-                            fname, args, kwargs, str(e))
-                return self.error_result
-        inner.__name__ = fname
-        return inner
-
 def Property(function):
     '''Decorator to easily assign descriptors based on sub-function names
     See <http://code.activestate.com/recipes/410698-property-decorator-for-python-24/>

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/Allura/allura/lib/helpers.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index 1604aa9..0511dc7 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -22,6 +22,7 @@ import os
 import os.path
 import difflib
 import urllib
+import urllib2
 import re
 import json
 import logging
@@ -30,6 +31,7 @@ from hashlib import sha1
 from datetime import datetime, timedelta
 from collections import defaultdict
 import shlex
+import socket
 
 import tg
 import genshi.template
@@ -53,10 +55,10 @@ from webhelpers import date, feedgenerator, html, number, misc, text
 
 from allura.lib import exceptions as exc
 # Reimport to make available to templates
-from allura.lib.decorators import exceptionless
 from allura.lib import AsciiDammit
 from .security import has_access
 
+log = logging.getLogger(__name__)
 
 # validates project, subproject, and user names
 re_project_name = re.compile(r'^[a-z][-a-z0-9]{2,14}$')
@@ -857,3 +859,70 @@ def split_select_field_options(field_options):
         # so we're getting rid of those.
         field_options = [o.replace('"', '') for o in field_options]
     return field_options
+
+
+@contextmanager
+def notifications_disabled(project):
+    """Temporarily disable email notifications on a project.
+
+    """
+    orig = project.notifications_disabled
+    try:
+        project.notifications_disabled = True
+        yield
+    finally:
+        project.notifications_disabled = orig
+
+
+@contextmanager
+def null_contextmanager(*args, **kw):
+    """A no-op contextmanager.
+
+    """
+    yield
+
+
+class exceptionless(object):
+    '''Decorator making the decorated function return 'error_result' on any
+    exceptions rather than propagating exceptions up the stack
+    '''
+
+    def __init__(self, error_result, log=None):
+        self.error_result = error_result
+        self.log = log
+
+    def __call__(self, fun):
+        fname = 'exceptionless(%s)' % fun.__name__
+        def inner(*args, **kwargs):
+            try:
+                return fun(*args, **kwargs)
+            except Exception as e:
+                if self.log:
+                    self.log.exception('Error calling %s(args=%s, kwargs=%s): %s',
+                            fname, args, kwargs, str(e))
+                return self.error_result
+        inner.__name__ = fname
+        return inner
+
+
+def urlopen(url, retries=3, codes=(408,)):
+    """Open url, optionally retrying if an error is encountered.
+
+    Socket timeouts will always be retried if retries > 0.
+    HTTP errors are retried if the error code is passed in ``codes``.
+
+    :param retries: Number of time to retry.
+    :param codes: HTTP error codes that should be retried.
+
+    """
+    while True:
+        try:
+            return urllib2.urlopen(url)
+        except (urllib2.HTTPError, socket.timeout) as e:
+            if retries and (isinstance(e, socket.timeout) or
+                    e.code in codes):
+                retries -= 1
+                continue
+            else:
+                log.exception('Failed after %s retries: %s', retries, e)
+                raise

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/Allura/allura/lib/import_api.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/import_api.py b/Allura/allura/lib/import_api.py
index 1f3f98d..8eeb8a7 100644
--- a/Allura/allura/lib/import_api.py
+++ b/Allura/allura/lib/import_api.py
@@ -49,7 +49,7 @@ class AlluraImportApiClient(object):
         url = urlparse.urljoin(self.base_url, url)
         if self.verbose:
             print "Using URL '%s'" % (url)
-        
+
         params = self.sign(urlparse.urlparse(url).path, params.items())
 
         while True:

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/Allura/allura/lib/spam/__init__.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/spam/__init__.py b/Allura/allura/lib/spam/__init__.py
index 8e6607a..cfb5c41 100644
--- a/Allura/allura/lib/spam/__init__.py
+++ b/Allura/allura/lib/spam/__init__.py
@@ -17,7 +17,7 @@
 
 import logging
 
-from allura.lib.decorators import exceptionless
+from allura.lib.helpers import exceptionless
 
 log = logging.getLogger(__name__)
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/Allura/allura/lib/validators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/validators.py b/Allura/allura/lib/validators.py
index ae863e8..9b44d53 100644
--- a/Allura/allura/lib/validators.py
+++ b/Allura/allura/lib/validators.py
@@ -151,6 +151,36 @@ class JsonConverter(fev.FancyValidator):
             raise fe.Invalid('Invalid JSON: ' + str(e), value, state)
         return obj
 
+class JsonFile(fev.FieldStorageUploadConverter):
+    """Validates that a file is JSON and returns the deserialized Python object
+
+    """
+    def _to_python(self, value, state):
+        return JsonConverter.to_python(value.value)
+
+class UserMapJsonFile(JsonFile):
+    """Validates that a JSON file conforms to this format:
+
+    {str:str, ...}
+
+    and returns a deserialized or stringified copy of it.
+
+    """
+    def __init__(self, as_string=False):
+        self.as_string = as_string
+
+    def _to_python(self, value, state):
+        value = super(self.__class__, self)._to_python(value, state)
+        try:
+            for k, v in value.iteritems():
+                if not(isinstance(k, basestring) and isinstance(v, basestring)):
+                    raise
+            return json.dumps(value) if self.as_string else value
+        except:
+            raise fe.Invalid(
+                    'User map file must contain mapping of {str:str, ...}',
+                    value, state)
+
 class CreateTaskSchema(fe.Schema):
     task = TaskValidator(not_empty=True, strip=True)
     task_args = JsonConverter(if_missing=dict(args=[], kwargs={}))

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/Allura/allura/scripts/trac_export.py
----------------------------------------------------------------------
diff --git a/Allura/allura/scripts/trac_export.py b/Allura/allura/scripts/trac_export.py
index e45175b..d53afbc 100644
--- a/Allura/allura/scripts/trac_export.py
+++ b/Allura/allura/scripts/trac_export.py
@@ -18,6 +18,7 @@
 #       under the License.
 
 import logging
+import socket
 import sys
 import csv
 import urlparse
@@ -34,6 +35,8 @@ from BeautifulSoup import BeautifulSoup, NavigableString
 import dateutil.parser
 import pytz
 
+from allura.lib import helpers as h
+
 log = logging.getLogger(__name__)
 
 
@@ -121,7 +124,7 @@ class TracExport(object):
 
     def csvopen(self, url):
         self.log_url(url)
-        f = urllib2.urlopen(url)
+        f = h.urlopen(url)
         # Trac doesn't throw 403 error, just shows normal 200 HTML page
         # telling that access denied. So, we'll emulate 403 ourselves.
         # TODO: currently, any non-csv result treated as 403.
@@ -143,7 +146,7 @@ class TracExport(object):
         from html2text import html2text
         url = self.full_url(self.TICKET_URL % id, 'rss')
         self.log_url(url)
-        d = feedparser.parse(url)
+        d = feedparser.parse(h.urlopen(url))
         res = []
         for comment in d['entries']:
             c = {}
@@ -160,7 +163,7 @@ class TracExport(object):
         # Scrape HTML to get ticket attachments
         url = self.full_url(self.ATTACHMENT_LIST_URL % id)
         self.log_url(url)
-        f = urllib2.urlopen(url)
+        f = h.urlopen(url)
         soup = BeautifulSoup(f)
         attach = soup.find('div', id='attachments')
         list = []

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/Allura/allura/tests/test_decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_decorators.py b/Allura/allura/tests/test_decorators.py
new file mode 100644
index 0000000..893f327
--- /dev/null
+++ b/Allura/allura/tests/test_decorators.py
@@ -0,0 +1,39 @@
+from unittest import TestCase
+
+from mock import patch
+
+from allura.lib.decorators import task
+
+
+class TestTask(TestCase):
+
+    def test_no_params(self):
+        @task
+        def func():
+            pass
+        self.assertTrue(hasattr(func, 'post'))
+
+    def test_with_params(self):
+        @task(disable_notifications=True)
+        def func():
+            pass
+        self.assertTrue(hasattr(func, 'post'))
+
+    @patch('allura.lib.decorators.c')
+    @patch('allura.lib.decorators._get_model')
+    def test_post(self, c, _get_model):
+        @task(disable_notifications=True)
+        def func(s, foo=None, **kw):
+            pass
+        def mock_post(f, args, kw, delay=None):
+            self.assertTrue(c.project.notifications_disabled)
+            self.assertFalse('delay' in kw)
+            self.assertEqual(delay, 1)
+            self.assertEqual(kw, dict(foo=2))
+            self.assertEqual(args, ('test',))
+            self.assertEqual(f, func)
+
+        c.project.notifications_disabled = False
+        M = _get_model.return_value
+        M.MonQTask.post.side_effect = mock_post
+        func.post('test', foo=2, delay=1)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/Allura/allura/tests/test_helpers.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_helpers.py b/Allura/allura/tests/test_helpers.py
index 42fb963..4ec91df 100644
--- a/Allura/allura/tests/test_helpers.py
+++ b/Allura/allura/tests/test_helpers.py
@@ -254,3 +254,45 @@ def test_datetimeformat():
 def test_split_select_field_options():
     assert_equals(h.split_select_field_options('"test message" test2'), ['test message', 'test2'])
     assert_equals(h.split_select_field_options('"test message test2'), ['test', 'message', 'test2'])
+
+
+def test_notifications_disabled():
+    project = Mock(notifications_disabled=False)
+    with h.notifications_disabled(project):
+        assert_equals(project.notifications_disabled, True)
+    assert_equals(project.notifications_disabled, False)
+
+
+class TestUrlOpen(TestCase):
+    @patch('allura.lib.helpers.urllib2')
+    def test_no_error(self, urllib2):
+        r = h.urlopen('myurl')
+        self.assertEqual(r, urllib2.urlopen.return_value)
+        urllib2.urlopen.assert_called_once_with('myurl')
+
+    @patch('allura.lib.helpers.urllib2.urlopen')
+    def test_socket_timeout(self, urlopen):
+        import socket
+        def side_effect(url):
+            raise socket.timeout()
+        urlopen.side_effect = side_effect
+        self.assertRaises(socket.timeout, h.urlopen, 'myurl')
+        self.assertEqual(urlopen.call_count, 4)
+
+    @patch('allura.lib.helpers.urllib2.urlopen')
+    def test_handled_http_error(self, urlopen):
+        from urllib2 import HTTPError
+        def side_effect(url):
+            raise HTTPError('url', 408, 'timeout', None, None)
+        urlopen.side_effect = side_effect
+        self.assertRaises(HTTPError, h.urlopen, 'myurl')
+        self.assertEqual(urlopen.call_count, 4)
+
+    @patch('allura.lib.helpers.urllib2.urlopen')
+    def test_unhandled_http_error(self, urlopen):
+        from urllib2 import HTTPError
+        def side_effect(url):
+            raise HTTPError('url', 404, 'timeout', None, None)
+        urlopen.side_effect = side_effect
+        self.assertRaises(HTTPError, h.urlopen, 'myurl')
+        self.assertEqual(urlopen.call_count, 1)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/Allura/allura/tests/test_validators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_validators.py b/Allura/allura/tests/test_validators.py
index 8a40610..b0d165c 100644
--- a/Allura/allura/tests/test_validators.py
+++ b/Allura/allura/tests/test_validators.py
@@ -44,6 +44,42 @@ class TestJsonConverter(unittest.TestCase):
             self.val.to_python('{')
 
 
+class TestJsonFile(unittest.TestCase):
+    val = v.JsonFile
+
+    class FieldStorage(object):
+        def __init__(self, content):
+            self.value = content
+
+    def test_valid(self):
+        self.assertEqual({}, self.val.to_python(self.FieldStorage('{}')))
+
+    def test_invalid(self):
+        with self.assertRaises(fe.Invalid):
+            self.val.to_python(self.FieldStorage('{'))
+
+
+class TestUserMapFile(unittest.TestCase):
+    val = v.UserMapJsonFile()
+
+    class FieldStorage(object):
+        def __init__(self, content):
+            self.value = content
+
+    def test_valid(self):
+        self.assertEqual({"user_old": "user_new"}, self.val.to_python(
+            self.FieldStorage('{"user_old": "user_new"}')))
+
+    def test_invalid(self):
+        with self.assertRaises(fe.Invalid):
+            self.val.to_python(self.FieldStorage('{"user_old": 1}'))
+
+    def test_as_string(self):
+        val = v.UserMapJsonFile(as_string=True)
+        self.assertEqual('{"user_old": "user_new"}', val.to_python(
+            self.FieldStorage('{"user_old": "user_new"}')))
+
+
 class TestUserValidator(unittest.TestCase):
     val = v.UserValidator
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeBlog/forgeblog/command/rssfeeds.py
----------------------------------------------------------------------
diff --git a/ForgeBlog/forgeblog/command/rssfeeds.py b/ForgeBlog/forgeblog/command/rssfeeds.py
index 9f44fd4..305bc5a 100644
--- a/ForgeBlog/forgeblog/command/rssfeeds.py
+++ b/ForgeBlog/forgeblog/command/rssfeeds.py
@@ -33,7 +33,7 @@ from forgeblog import model as BM
 from forgeblog import version
 from forgeblog.main import ForgeBlogApp
 from allura.lib import exceptions
-from allura.lib.decorators import exceptionless
+from allura.lib.helpers import exceptionless
 
 ## Everything in this file depends on html2text,
 ## so import attempt is placed in global scope.

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index 8cc3b52..e1af4f7 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -50,7 +50,7 @@ class ProjectImportForm(schema.Schema):
     project_name = fev.UnicodeString(not_empty=True, max=40)
 
 
-@task
+@task(notifications_disabled=True)
 def import_tool(importer_name, project_name=None, mount_point=None, mount_label=None, **kw):
     importer = ToolImporter.by_name(importer_name)
     importer.import_tool(c.project, c.user, project_name=project_name,
@@ -61,8 +61,9 @@ class ProjectImporter(BaseController):
     """
     Base class for project importers.
 
-    Subclases are required to implement the :meth:`index()` and
+    Subclasses are required to implement the :meth:`index()` and
     :meth:`process()` views described below.
+
     """
     source = None
     process_validator = None

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeImporters/forgeimporters/templates/project_base.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/templates/project_base.html b/ForgeImporters/forgeimporters/templates/project_base.html
index 6f1b683..9453e65 100644
--- a/ForgeImporters/forgeimporters/templates/project_base.html
+++ b/ForgeImporters/forgeimporters/templates/project_base.html
@@ -53,11 +53,10 @@
         }
 
         function check_names() {
-            var data = {
-                'neighborhood': $('#neighborhood').val(),
-                'project_name': $('#project_name').val(),
-                'project_shortname': $('#project_shortname').val()
-            };
+            var data = {};
+            $('#project-import-form input').each(function() {
+              data[$(this).attr('name')] = $(this).val();
+            });
             $.getJSON('check_names', data, function(result) {
                 $('#project_name_error').addClass('hidden');
                 $('#project_shortname_error').addClass('hidden');
@@ -86,7 +85,7 @@
 {% endblock %}
 
 {% block content %}
-<form id="project-import-form" method="POST" action="process">
+<form id="project-import-form" method="POST" action="process" enctype="multipart/form-data">
     <input type="hidden" id="neighborhood" name="neighborhood" value="{{importer.neighborhood.name}}"/>
 
     <fieldset id="project-fields">

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeImporters/forgeimporters/trac/project.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/project.py b/ForgeImporters/forgeimporters/trac/project.py
index 66e8326..71ef5f4 100644
--- a/ForgeImporters/forgeimporters/trac/project.py
+++ b/ForgeImporters/forgeimporters/trac/project.py
@@ -23,6 +23,7 @@ from tg import expose, validate
 from tg.decorators import with_trailing_slash
 
 from allura.lib.decorators import require_post
+from allura.lib.validators import UserMapJsonFile
 
 from .. import base
 
@@ -32,6 +33,7 @@ log = logging.getLogger(__name__)
 
 class TracProjectForm(base.ProjectImportForm):
     trac_url = fev.URL(not_empty=True)
+    user_map = UserMapJsonFile(as_string=True)
 
 
 class TracProjectImporter(base.ProjectImporter):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeImporters/forgeimporters/trac/templates/project.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/templates/project.html b/ForgeImporters/forgeimporters/trac/templates/project.html
index 869e290..a9af734 100644
--- a/ForgeImporters/forgeimporters/trac/templates/project.html
+++ b/ForgeImporters/forgeimporters/trac/templates/project.html
@@ -24,9 +24,21 @@
     </div>
     <div class="grid-10">
         <input id="trac_url" name="trac_url" value="{{c.form_values['trac_url']}}" autofocus/>
-        <div id="trac_ur_errorl" class="error{% if not c.form_errors['trac_url'] %} hidden{% endif %}">
+        <div id="trac_url_error" class="error{% if not c.form_errors['trac_url'] %} hidden{% endif %}">
             {{c.form_errors['trac_url']}}
         </div>
     </div>
+
+    <div class="grid-6" style="clear:left">
+        <label>User Map (optional)</label>
+    </div>
+    <div class="grid-10">
+        <input id="user_map" name="user_map" value="{{c.form_values['user_map']}}" type="file"/>
+        <br><small>JSON file mapping Trac usernames to Allura usernames</small>
+        <div id="user_map_error" class="error{% if not c.form_errors['user_map'] %} hidden{% endif %}">
+            {{c.form_errors['user_map']}}
+        </div>
+    </div>
+
     {{ super() }}
 {% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeImporters/forgeimporters/trac/templates/tickets/index.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/templates/tickets/index.html b/ForgeImporters/forgeimporters/trac/templates/tickets/index.html
index eaf9aac..7c278b0 100644
--- a/ForgeImporters/forgeimporters/trac/templates/tickets/index.html
+++ b/ForgeImporters/forgeimporters/trac/templates/tickets/index.html
@@ -27,10 +27,13 @@ Import tickets from Trac
 {% endblock %}
 
 {% block content %}
-<form action="create" method="post" class="pad">
+<form action="create" method="post" enctype="multipart/form-data" class="pad">
   <label for="trac_url">URL of the Trac instance</label>
   <input name="trac_url" />
 
+  <label for="user_map">JSON file mapping Trac usernames to Allura usernames (optional)</label>
+  <input name="user_map" type="file" />
+
   <label for="mount_label">Label</label>
   <input name="mount_label" value="Source" />
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/tests/test_tickets.py b/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
index 5f88eef..2a539df 100644
--- a/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
+++ b/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
@@ -15,6 +15,7 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+import json
 from unittest import TestCase
 from mock import Mock, patch
 
@@ -39,7 +40,7 @@ class TestTracTicketImporter(TestCase):
         from datetime import datetime, timedelta
         now = datetime.utcnow()
         dt.utcnow.return_value = now
-
+        user_map = {"orig_user":"new_user"}
         importer = TracTicketImporter()
         app = Mock(name='ForgeTrackerApp')
         project = Mock(name='Project', shortname='myproject')
@@ -48,7 +49,9 @@ class TestTracTicketImporter(TestCase):
         res = importer.import_tool(project, user,
                 mount_point='bugs',
                 mount_label='Bugs',
-                trac_url='http://example.com/trac/url')
+                trac_url='http://example.com/trac/url',
+                user_map=json.dumps(user_map),
+                )
         self.assertEqual(res, app)
         project.install_app.assert_called_once_with(
                 'Tickets', mount_point='bugs', mount_label='Bugs')
@@ -60,7 +63,8 @@ class TestTracTicketImporter(TestCase):
                 expires=now + timedelta(minutes=60))
         api_client = ApiClient.return_value
         import_tracker.assert_called_once_with(
-                api_client, 'myproject', 'bugs', {}, '[]',
+                api_client, 'myproject', 'bugs',
+                {"user_map": user_map}, '[]',
                 validate=False)
         g.post_event.assert_called_once_with('project_updated')
 
@@ -91,10 +95,12 @@ class TestTracTicketImportController(TestController, TestCase):
                 mount_point='mymount',
                 )
         r = self.app.post('/p/test/admin/bugs/_importer/create', params,
+                upload_files=[('user_map', 'myfile', '{"orig_user": "new_user"}')],
                 status=302)
         project = M.Project.query.get(shortname='test')
         self.assertEqual(r.location, 'http://localhost/p/test/mymount')
         self.assertEqual(project._id, importer.import_tool.call_args[0][0]._id)
         self.assertEqual(u'mymount', importer.import_tool.call_args[1]['mount_point'])
         self.assertEqual(u'mylabel', importer.import_tool.call_args[1]['mount_label'])
+        self.assertEqual('{"orig_user": "new_user"}', importer.import_tool.call_args[1]['user_map'])
         self.assertEqual(u'http://example.com/trac/url', importer.import_tool.call_args[1]['trac_url'])

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeImporters/forgeimporters/trac/tickets.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/tickets.py b/ForgeImporters/forgeimporters/trac/tickets.py
index f7d50b4..00bca2a 100644
--- a/ForgeImporters/forgeimporters/trac/tickets.py
+++ b/ForgeImporters/forgeimporters/trac/tickets.py
@@ -41,6 +41,7 @@ from tg.decorators import (
 from allura.controllers import BaseController
 from allura.lib.decorators import require_post
 from allura.lib.import_api import AlluraImportApiClient
+from allura.lib.validators import UserMapJsonFile
 from allura.model import ApiTicket
 from allura.scripts.trac_export import (
         TracExport,
@@ -54,6 +55,7 @@ from forgetracker.scripts.import_tracker import import_tracker
 
 class TracTicketImportSchema(fe.Schema):
     trac_url = fev.URL(not_empty=True)
+    user_map = UserMapJsonFile(as_string=True)
     mount_point = fev.UnicodeString()
     mount_label = fev.UnicodeString()
 
@@ -68,11 +70,12 @@ class TracTicketImportController(BaseController):
     @expose()
     @require_post()
     @validate(TracTicketImportSchema(), error_handler=index)
-    def create(self, trac_url, mount_point, mount_label, **kw):
+    def create(self, trac_url, mount_point, mount_label, user_map=None, **kw):
         app = TracTicketImporter().import_tool(c.project, c.user,
                 mount_point=mount_point,
                 mount_label=mount_label,
-                trac_url=trac_url)
+                trac_url=trac_url,
+                user_map=user_map)
         redirect(app.url())
 
 
@@ -84,7 +87,7 @@ class TracTicketImporter(ToolImporter):
     tool_description = 'Import your tickets from Trac'
 
     def import_tool(self, project, user, project_name=None, mount_point=None,
-            mount_label=None, trac_url=None, **kw):
+            mount_label=None, trac_url=None, user_map=None, **kw):
         """ Import Trac tickets into a new Allura Tracker tool.
 
         """
@@ -105,7 +108,8 @@ class TracTicketImporter(ToolImporter):
         session(api_ticket).flush(api_ticket)
         cli = AlluraImportApiClient(config['base_url'], api_ticket.api_key,
                 api_ticket.secret_key, False)
-        import_tracker(cli, project.shortname, mount_point, {},
+        import_tracker(cli, project.shortname, mount_point,
+                {'user_map': json.loads(user_map) if user_map else {}},
                 export_string, validate=False)
         g.post_event('project_updated')
         return app

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeTracker/forgetracker/import_support.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/import_support.py b/ForgeTracker/forgetracker/import_support.py
index 26e182e..04d453a 100644
--- a/ForgeTracker/forgetracker/import_support.py
+++ b/ForgeTracker/forgetracker/import_support.py
@@ -136,11 +136,11 @@ class ImportSupport(object):
         return datetime.strptime(date_string, '%Y-%m-%dT%H:%M:%SZ')
 
     def get_user_id(self, username):
-        username = self.options['user_map'].get(username, username)
+        username = self.options['user_map'].get(username)
+        if not username:
+            return None
         u = M.User.by_username(username)
-        if u:
-            return u._id
-        return None
+        return u._id if u else None
 
     def check_custom_field(self, field, value):
         field = c.app.globals.get_custom_field(field)
@@ -193,7 +193,12 @@ class ImportSupport(object):
                 new_f, conv = transform
                 remapped[new_f] = conv(v)
 
-        remapped['description'] = self.link_processing(remapped['description'])
+        description = self.link_processing(remapped['description'])
+        if ticket_dict['submitter'] and not remapped['reported_by_id']:
+            description = 'Originally created by: {0}\n\n{1}'.format(
+                    ticket_dict['submitter'], description)
+        remapped['description'] = description
+
         ticket_num = ticket_dict['id']
         existing_ticket = TM.Ticket.query.get(app_config_id=c.app.config._id,
                                           ticket_num=ticket_num)
@@ -261,8 +266,13 @@ class ImportSupport(object):
 
     def make_comment(self, thread, comment_dict):
         ts = self.parse_date(comment_dict['date'])
-        comment = thread.post(text=self.link_processing(comment_dict['comment']), timestamp=ts)
-        comment.author_id = self.get_user_id(comment_dict['submitter'])
+        author_id = self.get_user_id(comment_dict['submitter'])
+        text = self.link_processing(comment_dict['comment'])
+        if not author_id and comment_dict['submitter']:
+            text = 'Originally posted by: {0}\n\n{1}'.format(
+                    comment_dict['submitter'], text)
+        comment = thread.post(text=text, timestamp=ts)
+        comment.author_id = author_id
         comment.import_id = c.api_token.api_key
 
     def make_attachment(self, org_ticket_id, ticket_id, att_dict):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index a1363dc..2b432df 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1608,18 +1608,19 @@ class RootRestController(BaseController):
 
     @expose('json:')
     def perform_import(self, doc=None, options=None, **post_data):
-        require_access(c.project, 'admin')
-        if c.api_token.get_capability('import') != [c.project.neighborhood.name, c.project.shortname]:
-            log.error('Import capability is not enabled for %s', c.project.shortname)
-            raise exc.HTTPForbidden(detail='Import is not allowed')
+        with h.notifications_disabled(c.project):
+            require_access(c.project, 'admin')
+            if c.api_token.get_capability('import') != [c.project.neighborhood.name, c.project.shortname]:
+                log.error('Import capability is not enabled for %s', c.project.shortname)
+                raise exc.HTTPForbidden(detail='Import is not allowed')
 
-        migrator = ImportSupport()
-        try:
-            status = migrator.perform_import(doc, options, **post_data)
-            return status
-        except Exception, e:
-            log.exception(e)
-            return dict(status=False, errors=[str(e)])
+            migrator = ImportSupport()
+            try:
+                status = migrator.perform_import(doc, options, **post_data)
+                return status
+            except Exception, e:
+                log.exception(e)
+                return dict(status=False, errors=[str(e)])
 
     @expose('json:')
     def search(self, q=None, limit=100, page=0, sort=None, **kw):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/675495e3/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index 816db78..e059038 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -730,17 +730,18 @@ class PageRestController(BaseController):
         return self.page.__json__()
 
     def _update_page(self, title, **post_data):
-        if not self.page:
-            require_access(c.app, 'create')
-            self.page = WM.Page.upsert(title)
-            self.page.viewable_by = ['all']
-        else:
-            require_access(self.page, 'edit')
-        self.page.text = post_data['text']
-        if 'labels' in post_data:
-            self.page.labels = post_data['labels'].split(',')
-        self.page.commit()
-        return {}
+        with h.notifications_disabled(c.project):
+            if not self.page:
+                require_access(c.app, 'create')
+                self.page = WM.Page.upsert(title)
+                self.page.viewable_by = ['all']
+            else:
+                require_access(self.page, 'edit')
+            self.page.text = post_data['text']
+            if 'labels' in post_data:
+                self.page.labels = post_data['labels'].split(',')
+            self.page.commit()
+            return {}
 
 
 class WikiAdminController(DefaultAdminController):


[12/25] git commit: [#6506] Shortened Trac Ticket Importer tool_label

Posted by tv...@apache.org.
[#6506] Shortened Trac Ticket Importer tool_label

Signed-off-by: Cory Johns <cj...@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/37aacca9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/37aacca9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/37aacca9

Branch: refs/heads/db/6482
Commit: 37aacca9a59e94c4cdfb440c5e96c0f6fe202314
Parents: 3f6bf33
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Thu Aug 15 20:22:44 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Thu Aug 15 20:22:44 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/trac/tickets.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/37aacca9/ForgeImporters/forgeimporters/trac/tickets.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/tickets.py b/ForgeImporters/forgeimporters/trac/tickets.py
index 00bca2a..ef099ca 100644
--- a/ForgeImporters/forgeimporters/trac/tickets.py
+++ b/ForgeImporters/forgeimporters/trac/tickets.py
@@ -83,7 +83,7 @@ class TracTicketImporter(ToolImporter):
     target_app = ForgeTrackerApp
     source = 'Trac'
     controller = TracTicketImportController
-    tool_label = 'Trac Ticket Importer'
+    tool_label = 'Tickets'
     tool_description = 'Import your tickets from Trac'
 
     def import_tool(self, project, user, project_name=None, mount_point=None,


[18/25] git commit: Added Gittip macro for Wiki tool

Posted by tv...@apache.org.
Added Gittip macro for Wiki tool


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/e81ffc0d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/e81ffc0d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/e81ffc0d

Branch: refs/heads/db/6482
Commit: e81ffc0d0a4ce5bdc7d5bef0c93122f8a6995514
Parents: afb7bb4
Author: Wayne Witzel III <wa...@pieceofpy.com>
Authored: Thu Aug 15 15:23:41 2013 -0400
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Mon Aug 19 19:40:13 2013 +0000

----------------------------------------------------------------------
 .../templates/admin_widgets/metadata_admin.html  |  2 ++
 Allura/allura/lib/macro.py                       |  7 +++++++
 Allura/allura/lib/utils.py                       |  3 ++-
 Allura/allura/lib/widgets/macros.py              |  6 ++++++
 Allura/allura/templates/jinja_master/lib.html    |  6 ++++++
 .../allura/templates/widgets/gittip_button.html  | 19 +++++++++++++++++++
 Allura/allura/tests/test_globals.py              |  6 ++++++
 7 files changed, 48 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e81ffc0d/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html b/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html
index 454f33a..bfce390 100644
--- a/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html
+++ b/Allura/allura/ext/admin/templates/admin_widgets/metadata_admin.html
@@ -67,9 +67,11 @@
     {{ widget.display_label(widget.fields.twitter_handle) }}
     <br>
     {{widget.display_field(widget.fields.twitter_handle) }}
+
     {{ widget.display_label(widget.fields.facebook_page) }}
     <br>
     {{widget.display_field(widget.fields.facebook_page) }}
+
     {% if c.project.neighborhood.features['google_analytics'] %}
     {{ widget.display_label(widget.fields.tracking_id) }}
     <br>

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e81ffc0d/Allura/allura/lib/macro.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/macro.py b/Allura/allura/lib/macro.py
index aac20ff..97d5206 100644
--- a/Allura/allura/lib/macro.py
+++ b/Allura/allura/lib/macro.py
@@ -294,6 +294,13 @@ def project_screenshots():
     response = ps.display(project=c.project)
     return response
 
+@macro()
+def gittip_button(username):
+    from allura.lib.widgets.macros import GittipButton
+    button = GittipButton(username=username)
+    g.resource_manager.register(button)
+    response = button.display(username=username)
+    return response
 
 # FIXME: this is SourceForge specific - need to provide a way for macros to come from other packages
 @macro()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e81ffc0d/Allura/allura/lib/utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index d6796e5..4bd1e3c 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -502,6 +502,7 @@ class ForgeHTMLSanitizer(_HTMLSanitizer):
     def unknown_starttag(self, tag, attrs):
         if 'iframe' in self.acceptable_elements:
             self.acceptable_elements.remove('iframe')
-        if (tag == 'iframe') and (dict(attrs).get('src', '').startswith('http://www.youtube.com/embed/')):
+        if (tag == 'iframe') and (dict(attrs).get('src', '').startswith('http://www.youtube.com/embed/') or
+				  dict(attrs).get('src', '').startswith('https://www.gittip.com/')):
             self.acceptable_elements.append('iframe')
         _HTMLSanitizer.unknown_starttag(self, tag, attrs)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e81ffc0d/Allura/allura/lib/widgets/macros.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/widgets/macros.py b/Allura/allura/lib/widgets/macros.py
index 0921ab9..5f93d2a 100644
--- a/Allura/allura/lib/widgets/macros.py
+++ b/Allura/allura/lib/widgets/macros.py
@@ -25,6 +25,12 @@ class Include(ew.Widget):
         'style':'width:270px;float:right;background-color:#ccc'
         }
 
+class GittipButton(ew.Widget):
+    template='jinja:allura:templates/widgets/gittip_button.html'
+    params=['username']
+    project=None
+
+
 class DownloadButton(ew.Widget):
     template='jinja:allura:templates/widgets/download_button.html'
     params=['project']

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e81ffc0d/Allura/allura/templates/jinja_master/lib.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/jinja_master/lib.html b/Allura/allura/templates/jinja_master/lib.html
index a3e8d6b..9d8b3c1 100644
--- a/Allura/allura/templates/jinja_master/lib.html
+++ b/Allura/allura/templates/jinja_master/lib.html
@@ -706,6 +706,12 @@ allowed, permitting basic styling and layout: &lt;div markdown style="float:left
 [[download_button]]
 </pre></div>
 
+<h2 id="md_ex_gittip_button{{id}}">Gittip Button</h2>
+<p>You can display a Gittip button in your wiki. You will need to set your gittip handle in the Metadata section of the Admin tool for your project.</p>
+<div class="codehilite"><pre>
+[[gittip_button username=foo]]
+</pre></div>
+
 <h2 id="md_ex_member{{id}}">Project Member List</h2>
 <p>You can display a list of project members. By default the list is limited to 20 members, and a link is provided to a page with the full list.</p>
 <div class="codehilite"><pre>

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e81ffc0d/Allura/allura/templates/widgets/gittip_button.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/widgets/gittip_button.html b/Allura/allura/templates/widgets/gittip_button.html
new file mode 100644
index 0000000..708793a
--- /dev/null
+++ b/Allura/allura/templates/widgets/gittip_button.html
@@ -0,0 +1,19 @@
+{#-
+       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.
+-#}
+<p><iframe style="border: 0; margin: 0; padding: 0;" src="https://www.gittip.com/{{username}}/widget.html" width="48pt" height="22pt"></iframe></p>

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/e81ffc0d/Allura/allura/tests/test_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_globals.py b/Allura/allura/tests/test_globals.py
index 60860a5..526af04 100644
--- a/Allura/allura/tests/test_globals.py
+++ b/Allura/allura/tests/test_globals.py
@@ -148,6 +148,12 @@ def test_macro_download_button():
         r = g.markdown_wiki.convert('[[download_button]]')
     assert_equal(r, '<div class="markdown_content"><p><span class="download-button-%s" style="margin-bottom: 1em; display: block;"></span></p>\n</div>' % p_test._id)
 
+def test_macro_gittip_button():
+    p_nbhd = M.Neighborhood.query.get(name='Projects')
+    p_test = M.Project.query.get(shortname='test', neighborhood_id=p_nbhd._id)
+    with h.push_config(c, project=p_test):
+        r = g.markdown_wiki.convert('[[gittip_button username=test]]')
+    assert_equal(r, u'<div class="markdown_content"><p><iframe height="22pt" src="https://www.gittip.com/test/widget.html" style="border: 0; margin: 0; padding: 0;" width="48pt"></iframe></p>\n</div>')
 
 def test_macro_neighborhood_feeds():
     p_nbhd = M.Neighborhood.query.get(name='Projects')


[22/25] git commit: [#6482] import page for existing projects

Posted by tv...@apache.org.
[#6482] import page for existing projects


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/7b5504f6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/7b5504f6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/7b5504f6

Branch: refs/heads/db/6482
Commit: 7b5504f68674906386d6a2d37647c8f5e6e64020
Parents: d924835
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Aug 9 22:14:07 2013 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Tue Aug 20 13:26:59 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/base.py           | 45 ++++++++++++--
 .../forgeimporters/templates/list_all.html      | 62 ++++++++++++++++++++
 ForgeImporters/setup.py                         |  3 +
 3 files changed, 104 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/7b5504f6/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index ee34ab3..eb5748e 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -22,15 +22,17 @@ from pkg_resources import iter_entry_points
 
 from tg import expose, validate, flash, redirect, config
 from tg.decorators import with_trailing_slash
-from pylons import tmpl_context as c
+from pylons import tmpl_context as c, app_globals as g
 from formencode import validators as fev, schema
+from webob import exc
 
 from allura.lib.decorators import require_post
 from allura.lib.decorators import task
 from allura.lib.security import require_access
-from allura.lib.plugin import ProjectRegistrationProvider
+from allura.lib.plugin import ProjectRegistrationProvider, AdminExtension
 from allura.lib import helpers as h
 from allura.lib import exceptions
+from allura.app import SitemapEntry
 
 from paste.deploy.converters import aslist
 
@@ -206,16 +208,16 @@ class ToolImporter(object):
     source = None  # string description of source, must match project importer
     controller = None
 
-    @classmethod
-    def by_name(self, name):
+    @staticmethod
+    def by_name(name):
         """
         Return a ToolImporter subclass instance given its entry-point name.
         """
         for ep in iter_entry_points('allura.importers', name):
             return ep.load()()
 
-    @classmethod
-    def by_app(self, app):
+    @staticmethod
+    def by_app(app):
         """
         Return a ToolImporter subclass instance given its target_app class.
         """
@@ -283,3 +285,34 @@ class ToolsValidator(fev.Set):
             pl = 's' if len(invalid) > 1 else ''
             raise fev.Invalid('Invalid tool%s selected: %s' % (pl, ', '.join(invalid)), value, state)
         return valid
+
+class ProjectToolsImportController(object):
+    '''List all importers available'''
+
+    @with_trailing_slash
+    @expose('jinja:forgeimporters:templates/list_all.html')
+    def index(self, *a, **kw):
+        importers = {}
+        for app_name, app in g.entry_points['tool'].iteritems():
+            importers[app] = ToolImporter.by_app(app)
+        return {
+            'importers': importers
+        }
+
+    @expose()
+    def _lookup(self, name, *remainder):
+        import_tool = ToolImporter.by_name(name)
+        if import_tool:
+            return import_tool.controller(), remainder
+        else:
+            raise exc.HTTPNotFound
+
+class ImportAdminExtension(AdminExtension):
+    '''Add import link to project admin sidebar'''
+
+    project_admin_controllers = {'import': ProjectToolsImportController}
+
+    def update_project_sidebar_menu(self, sidebar_links):
+        base_url = c.project.url() + 'admin/ext/'
+        link = SitemapEntry('Import', base_url+'import')
+        sidebar_links.append(link)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/7b5504f6/ForgeImporters/forgeimporters/templates/list_all.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/templates/list_all.html b/ForgeImporters/forgeimporters/templates/list_all.html
new file mode 100644
index 0000000..58c9f34
--- /dev/null
+++ b/ForgeImporters/forgeimporters/templates/list_all.html
@@ -0,0 +1,62 @@
+{#-
+       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.
+-#}
+{% extends g.theme.master %}
+{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
+
+{% block title %}{{c.project.name}} / Import{% endblock %}
+
+{% block header %}Import from external sources{% endblock %}
+
+{% block extra_css %}
+<style type="text/css">
+    .tool {
+        margin-bottom: 3em;
+    }
+    .tool img {
+        float: left;
+        margin: 0 1em;
+    }
+    .importer .descr {
+        margin: 0 2em 1em 2em;
+    }
+</style>
+{% endblock %}
+
+{% block content %}
+    {% for tool, tool_importers in importers.iteritems() %}
+        <div class="tool">
+        {% for importer_name, importer in tool_importers.iteritems() %}
+            {% if loop.first %}
+                <img src="{{ g.theme.app_icon_url(tool, 48) }}" alt="{{ tool.tool_label }} icon">
+                <h2>{{ tool.tool_label }}</h2>
+            {% endif %}
+            <div class="importer">
+                <a href="{{importer_name}}"><h3>
+                {{importer.source}}:
+                {{importer.tool_label}}
+                </h3>
+                </a>
+                <div class="descr">
+                {{importer.tool_description}}
+                </div>
+            </div>
+        {% endfor %}
+        </div>
+    {% endfor %}
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/7b5504f6/ForgeImporters/setup.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/setup.py b/ForgeImporters/setup.py
index b19fd17..d9af953 100644
--- a/ForgeImporters/setup.py
+++ b/ForgeImporters/setup.py
@@ -41,4 +41,7 @@ setup(name='ForgeImporters',
       google-code-tracker = forgeimporters.google.tracker:GoogleCodeTrackerImporter
       google-code-repo = forgeimporters.google.code:GoogleRepoImporter
       trac-tickets = forgeimporters.trac.tickets:TracTicketImporter
+
+      [allura.admin]
+      importers = forgeimporters.base:ImportAdminExtension
       """,)


[25/25] git commit: [#6482] simpler logic to build matrix of importers (thanks Cory)

Posted by tv...@apache.org.
[#6482] simpler logic to build matrix of importers (thanks Cory)


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/908217cf
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/908217cf
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/908217cf

Branch: refs/heads/db/6482
Commit: 908217cf3ed3eb01ae3e95bd5c4ec9e340d543e0
Parents: 417ed4d
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Tue Aug 13 21:12:37 2013 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Tue Aug 20 13:27:53 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/base.py           | 27 ++++----------------
 .../forgeimporters/templates/list_all.html      | 12 ++++-----
 2 files changed, 11 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/908217cf/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index 964d594..596ef4c 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -293,33 +293,16 @@ class ProjectToolsImportController(object):
     @with_trailing_slash
     @expose('jinja:forgeimporters:templates/list_all.html')
     def index(self, *a, **kw):
-        # make dictionaries of both axis
-        importers_by_source = defaultdict(dict)
-        importers_by_tool = defaultdict(dict)
+        importer_matrix = defaultdict(dict)
+        tools_with_importers = set()
         for ep in iter_entry_points('allura.importers'):
             importer = ep.load()
-            importers_by_source[importer.source][ep.name] = importer
             for tool in aslist(importer.target_app):
-                importers_by_tool[tool][ep.name] = importer
-
-        relevant_tools = sorted(importers_by_tool.keys(), key=lambda t: t.tool_label)
-
-        # build a full matrix including empty spots
-        importer_matrix = dict() # source -> [importer names]
-        for source, src_importers in importers_by_source.iteritems():
-            row = list()
-            for tool in relevant_tools:
-                for ep_name, importer in src_importers.iteritems():
-                    if tool in aslist(importer.target_app):
-                        row.append(ep_name)
-                        break
-                else:
-                    row.append(None)
-            importer_matrix[source] = row
-
+                tools_with_importers.add(tool.tool_label)
+                importer_matrix[importer.source][tool.tool_label] = ep.name
         return {
-            'tools': relevant_tools,
             'importer_matrix': importer_matrix,
+            'tools': tools_with_importers,
         }
 
     @expose()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/908217cf/ForgeImporters/forgeimporters/templates/list_all.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/templates/list_all.html b/ForgeImporters/forgeimporters/templates/list_all.html
index b91e0df..b4c0a06 100644
--- a/ForgeImporters/forgeimporters/templates/list_all.html
+++ b/ForgeImporters/forgeimporters/templates/list_all.html
@@ -31,18 +31,18 @@
     <thead>
     <tr>
         <th></th>
-        {% for tool in tools %}
-            <th>{{tool.tool_label}}</th>
+        {% for tool in tools|sort %}
+            <th>{{tool}}</th>
         {% endfor %}
     </tr>
     </thead>
-    {% for source, importers in importer_matrix|dictsort %}
+    {% for source in importer_matrix.keys()|sort  %}
         <tr>
         <td><strong>{{ source }}</strong></td>
-        {% for importer_name in importers %}
+        {% for tool in tools|sort %}
             <td>
-                {% if importer_name %}
-                <a href="{{importer_name}}">Import</a>
+                {% if importer_matrix[source].get(tool) %}
+                <a href="{{ importer_matrix[source][tool] }}">Import</a>
                 {% endif %}
             </td>
         {% endfor %}


[24/25] git commit: [#6482] add a test for the importer admin page

Posted by tv...@apache.org.
[#6482] add a test for the importer admin page


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/417ed4df
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/417ed4df
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/417ed4df

Branch: refs/heads/db/6482
Commit: 417ed4dfdc42b2496eb67f500176d5cec415139d
Parents: 451a691
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Tue Aug 13 20:38:30 2013 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Tue Aug 20 13:27:53 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/base.py           |  4 +-
 .../forgeimporters/tests/test_base.py           | 74 ++++++++++++++------
 2 files changed, 56 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/417ed4df/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index a5023da..964d594 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -337,5 +337,5 @@ class ImportAdminExtension(AdminExtension):
 
     def update_project_sidebar_menu(self, sidebar_links):
         base_url = c.project.url() + 'admin/ext/'
-        link = SitemapEntry('Import', base_url+'import')
-        sidebar_links.append(link)
+        link = SitemapEntry('Import', base_url+'import/')
+        sidebar_links.append(link)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/417ed4df/ForgeImporters/forgeimporters/tests/test_base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/test_base.py b/ForgeImporters/forgeimporters/tests/test_base.py
index 57ed227..f1c93ad 100644
--- a/ForgeImporters/forgeimporters/tests/test_base.py
+++ b/ForgeImporters/forgeimporters/tests/test_base.py
@@ -19,6 +19,10 @@ from unittest import TestCase
 
 from formencode import Invalid
 import mock
+from tg import expose
+from nose.tools import assert_equal
+
+from alluratest.controller import TestController
 
 from .. import base
 
@@ -76,17 +80,25 @@ TA1 = mock.Mock(tool_label='foo', tool_description='foo_desc')
 TA2 = mock.Mock(tool_label='qux', tool_description='qux_desc')
 TA3 = mock.Mock(tool_label='baz', tool_description='baz_desc')
 
-class TestToolImporter(TestCase):
-    class TI1(base.ToolImporter):
-        target_app = TA1
+class TI1Controller(object):
+    @expose()
+    def index(self, *a, **kw):
+        return 'test importer 1 controller webpage'
+
+class TI1(base.ToolImporter):
+    target_app = TA1
+    controller = TI1Controller
+
+class TI2(base.ToolImporter):
+    target_app = TA2
+    tool_label = 'bar'
+    tool_description = 'bar_desc'
+
+class TI3(base.ToolImporter):
+    target_app = [TA2, TA2]
 
-    class TI2(base.ToolImporter):
-        target_app = TA2
-        tool_label = 'bar'
-        tool_description = 'bar_desc'
+class TestToolImporter(TestCase):
 
-    class TI3(base.ToolImporter):
-        target_app = [TA2, TA2]
 
     @mock.patch.object(base, 'iter_entry_points')
     def test_by_name(self, iep):
@@ -104,27 +116,27 @@ class TestToolImporter(TestCase):
     @mock.patch.object(base, 'iter_entry_points')
     def test_by_app(self, iep):
         eps = iep.return_value = [
-                ep('importer1', importer=self.TI1),
-                ep('importer2', importer=self.TI2),
-                ep('importer3', importer=self.TI3),
+                ep('importer1', importer=TI1),
+                ep('importer2', importer=TI2),
+                ep('importer3', importer=TI3),
             ]
         importers = base.ToolImporter.by_app(TA2)
         self.assertEqual(set(importers.keys()), set([
                 'importer2',
                 'importer3',
             ]))
-        self.assertIsInstance(importers['importer2'], self.TI2)
-        self.assertIsInstance(importers['importer3'], self.TI3)
+        self.assertIsInstance(importers['importer2'], TI2)
+        self.assertIsInstance(importers['importer3'], TI3)
 
     def test_tool_label(self):
-        self.assertEqual(self.TI1().tool_label, 'foo')
-        self.assertEqual(self.TI2().tool_label, 'bar')
-        self.assertEqual(self.TI3().tool_label, 'qux')
+        self.assertEqual(TI1().tool_label, 'foo')
+        self.assertEqual(TI2().tool_label, 'bar')
+        self.assertEqual(TI3().tool_label, 'qux')
 
     def test_tool_description(self):
-        self.assertEqual(self.TI1().tool_description, 'foo_desc')
-        self.assertEqual(self.TI2().tool_description, 'bar_desc')
-        self.assertEqual(self.TI3().tool_description, 'qux_desc')
+        self.assertEqual(TI1().tool_description, 'foo_desc')
+        self.assertEqual(TI2().tool_description, 'bar_desc')
+        self.assertEqual(TI3().tool_description, 'qux_desc')
 
 
 class TestToolsValidator(TestCase):
@@ -172,3 +184,25 @@ class TestToolsValidator(TestCase):
                 mock.call('value1'),
                 mock.call('value2'),
             ])
+
+
+class TestProjectToolsImportController(TestController):
+
+    def test_pages(self):
+        admin_page = self.app.get('/admin/')
+        with mock.patch.object(base, 'iter_entry_points') as iep:
+            iep.return_value = [
+                ep('importer1', importer=TI1),
+                ep('importer2', importer=TI2),
+                ep('importer3', importer=TI3),
+            ]
+            import_main_page = admin_page.click('Import')
+        url = import_main_page.environ['PATH_INFO']
+        assert url.endswith('/admin/ext/import/'), url
+
+        with mock.patch.object(base.ToolImporter, 'by_name') as by_name:
+            by_name.return_value = TI1
+            import1_page = import_main_page.click('Import', href=r'importer1$')
+        url = import1_page.environ['PATH_INFO']
+        assert url.endswith('/admin/ext/import/importer1'), url
+        assert_equal(import1_page.body, 'test importer 1 controller webpage')


[05/25] git commit: [#4818] don't error if tool doesn't exist any more

Posted by tv...@apache.org.
[#4818] don't error if tool doesn't exist any more


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/c0dbb7fe
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/c0dbb7fe
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/c0dbb7fe

Branch: refs/heads/db/6482
Commit: c0dbb7fe53a0fdc244cb6c9904533564dd7e6559
Parents: df942cb
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Aug 9 20:01:21 2013 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Wed Aug 14 13:12:13 2013 +0000

----------------------------------------------------------------------
 Allura/allura/ext/admin/admin_main.py | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c0dbb7fe/Allura/allura/ext/admin/admin_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index 92efacb..98a34c8 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -153,7 +153,7 @@ class AdminApp(Application):
 
 
 class AdminExtensionLookup(object):
-    
+
     @expose()
     def _lookup(self, name, *remainder):
         for ext_name, admin_extension in g.entry_points['admin'].iteritems():
@@ -177,8 +177,11 @@ class ProjectAdminController(BaseController):
     @with_trailing_slash
     @expose('jinja:allura.ext.admin:templates/project_admin.html')
     def index(self, **kw):
-        scm_tools = [tool for tool in c.project.app_configs if issubclass(
-                g.entry_points["tool"][tool.tool_name], RepositoryApp)]
+        scm_tools = []
+        for tool in c.project.app_configs:
+            app = g.entry_points["tool"].get(tool.tool_name)
+            if app and issubclass(app, RepositoryApp):
+                scm_tools.append(tool)
         return dict(scm_tools=scm_tools)
 
     @without_trailing_slash


[08/25] git commit: [#6553] fix tests corresponding to taskd error handling

Posted by tv...@apache.org.
[#6553] fix tests corresponding to taskd error handling


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/6657808d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/6657808d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/6657808d

Branch: refs/heads/db/6482
Commit: 6657808db549aa90dfff9e0ca6c45f8b2bfaddad
Parents: c700603
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Wed Aug 14 13:32:53 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Wed Aug 14 13:32:53 2013 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_commands.py |  3 +--
 Allura/allura/tests/test_tasks.py    | 12 +++++++++++-
 2 files changed, 12 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/6657808d/Allura/allura/tests/test_commands.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_commands.py b/Allura/allura/tests/test_commands.py
index 0cecfbf..09dc3b6 100644
--- a/Allura/allura/tests/test_commands.py
+++ b/Allura/allura/tests/test_commands.py
@@ -344,8 +344,7 @@ class TestBackgroundCommand(object):
     def test_invalid_args(self):
         M.MonQTask.query.remove()
         show_models.ReindexCommand.post('--invalid-option')
-        with td.raises(Exception):
-            M.MonQTask.run_ready()
+        M.MonQTask.run_ready()
         task = M.MonQTask.query.get(task_name=self.task_name)
         assert_equal(task.state, 'error')
         assert_in('Error parsing args', task.result)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/6657808d/Allura/allura/tests/test_tasks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_tasks.py b/Allura/allura/tests/test_tasks.py
index c85ce50..c72d253 100644
--- a/Allura/allura/tests/test_tasks.py
+++ b/Allura/allura/tests/test_tasks.py
@@ -21,6 +21,7 @@ import operator
 import sys
 import unittest
 from base64 import b64encode
+import logging
 
 import mock
 from pylons import tmpl_context as c, app_globals as g
@@ -28,6 +29,7 @@ from datadiff.tools import assert_equal
 from nose.tools import assert_in
 from ming.orm import FieldProperty, Mapper
 from ming.orm import ThreadLocalORMSession
+from testfixtures import LogCapture
 
 from alluratest.controller import setup_basic_test, setup_global_objects
 
@@ -74,7 +76,15 @@ class TestEventTasks(unittest.TestCase):
         setup_basic_test()
         setup_global_objects()
         t = raise_exc.post()
-        self.assertRaises(CompoundError, t)
+        with LogCapture(level=logging.ERROR) as l:
+            t()
+        # l.check() would be nice, but string is too detailed to check
+        assert_equal(l.records[0].name, 'allura.model.monq_model')
+        msg = l.records[0].getMessage()
+        assert_in("AssertionError('assert 0',)", msg)
+        assert_in("AssertionError('assert 5',)", msg)
+        assert_in(' on job <MonQTask ', msg)
+        assert_in(' (error) P:10 allura.tests.test_tasks.raise_exc ', msg)
         for x in range(10):
             assert ('assert %d' % x) in t.result
 


[16/25] git commit: [#6480] add Apache License header to Allura/allura/tests/test_decorators.py

Posted by tv...@apache.org.
[#6480] add Apache License header to Allura/allura/tests/test_decorators.py


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/1c62317d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/1c62317d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/1c62317d

Branch: refs/heads/db/6482
Commit: 1c62317dbbb01bfd413b83aa7e6906f9a1505249
Parents: 2b0a101
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Aug 16 15:17:14 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Aug 16 15:17:14 2013 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_decorators.py | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/1c62317d/Allura/allura/tests/test_decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_decorators.py b/Allura/allura/tests/test_decorators.py
index 41ba631..d4b70a5 100644
--- a/Allura/allura/tests/test_decorators.py
+++ b/Allura/allura/tests/test_decorators.py
@@ -1,3 +1,20 @@
+#       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.
+
 from unittest import TestCase
 
 from mock import patch


[10/25] git commit: [#6506] Add custom User-Agent and auto-retries for project export requests

Posted by tv...@apache.org.
[#6506] Add custom User-Agent and auto-retries for project export requests

Signed-off-by: Tim Van Steenburgh <tv...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/490c83e3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/490c83e3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/490c83e3

Branch: refs/heads/db/6482
Commit: 490c83e38af601ddeed4b4f7ca3ed1a7e5721ed0
Parents: 675495e
Author: Tim Van Steenburgh <tv...@gmail.com>
Authored: Tue Aug 13 21:37:31 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Thu Aug 15 16:09:31 2013 +0000

----------------------------------------------------------------------
 Allura/allura/scripts/trac_export.py               | 17 +++++++++++------
 ForgeImporters/forgeimporters/base.py              | 16 ++++++++++++++++
 ForgeImporters/forgeimporters/google/__init__.py   |  8 ++++----
 .../forgeimporters/tests/google/test_extractor.py  |  7 ++++---
 ForgeImporters/forgeimporters/tests/test_base.py   | 13 +++++++++++++
 ForgeTracker/forgetracker/import_support.py        | 10 +++++++---
 .../forgewiki/scripts/wiki_from_trac/extractors.py | 17 ++++++++++-------
 7 files changed, 65 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/490c83e3/Allura/allura/scripts/trac_export.py
----------------------------------------------------------------------
diff --git a/Allura/allura/scripts/trac_export.py b/Allura/allura/scripts/trac_export.py
index d53afbc..4d908bc 100644
--- a/Allura/allura/scripts/trac_export.py
+++ b/Allura/allura/scripts/trac_export.py
@@ -18,7 +18,6 @@
 #       under the License.
 
 import logging
-import socket
 import sys
 import csv
 import urlparse
@@ -28,14 +27,20 @@ import time
 import re
 from optparse import OptionParser
 from itertools import islice
-from datetime import datetime
 
 import feedparser
 from BeautifulSoup import BeautifulSoup, NavigableString
 import dateutil.parser
 import pytz
 
-from allura.lib import helpers as h
+try:
+    from forgeimporters.base import ProjectExtractor
+    urlopen = ProjectExtractor.urlopen
+except ImportError:
+    try:
+        from allura.lib.helpers import urlopen
+    except ImportError:
+        from urllib2 import urlopen
 
 log = logging.getLogger(__name__)
 
@@ -124,7 +129,7 @@ class TracExport(object):
 
     def csvopen(self, url):
         self.log_url(url)
-        f = h.urlopen(url)
+        f = urlopen(url)
         # Trac doesn't throw 403 error, just shows normal 200 HTML page
         # telling that access denied. So, we'll emulate 403 ourselves.
         # TODO: currently, any non-csv result treated as 403.
@@ -146,7 +151,7 @@ class TracExport(object):
         from html2text import html2text
         url = self.full_url(self.TICKET_URL % id, 'rss')
         self.log_url(url)
-        d = feedparser.parse(h.urlopen(url))
+        d = feedparser.parse(urlopen(url))
         res = []
         for comment in d['entries']:
             c = {}
@@ -163,7 +168,7 @@ class TracExport(object):
         # Scrape HTML to get ticket attachments
         url = self.full_url(self.ATTACHMENT_LIST_URL % id)
         self.log_url(url)
-        f = h.urlopen(url)
+        f = urlopen(url)
         soup = BeautifulSoup(f)
         attach = soup.find('div', id='attachments')
         list = []

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/490c83e3/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index e1af4f7..ee34ab3 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -16,6 +16,7 @@
 #       under the License.
 
 import logging
+import urllib2
 
 from pkg_resources import iter_entry_points
 
@@ -28,6 +29,7 @@ from allura.lib.decorators import require_post
 from allura.lib.decorators import task
 from allura.lib.security import require_access
 from allura.lib.plugin import ProjectRegistrationProvider
+from allura.lib import helpers as h
 from allura.lib import exceptions
 
 from paste.deploy.converters import aslist
@@ -57,6 +59,20 @@ def import_tool(importer_name, project_name=None, mount_point=None, mount_label=
             mount_point=mount_point, mount_label=mount_label, **kw)
 
 
+class ProjectExtractor(object):
+    """Base class for project extractors.
+
+    Subclasses should use :meth:`urlopen` to make HTTP requests, as it provides
+    a custom User-Agent and automatically retries timed-out requests.
+
+    """
+    @staticmethod
+    def urlopen(url, retries=3, codes=(408,), **kw):
+        req = urllib2.Request(url, **kw)
+        req.add_header('User-Agent', 'Allura Data Importer (http://sf.net/p/allura)')
+        return h.urlopen(req, retries=retries, codes=codes)
+
+
 class ProjectImporter(BaseController):
     """
     Base class for project importers.

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/490c83e3/ForgeImporters/forgeimporters/google/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/__init__.py b/ForgeImporters/forgeimporters/google/__init__.py
index a307bcd..a12389b 100644
--- a/ForgeImporters/forgeimporters/google/__init__.py
+++ b/ForgeImporters/forgeimporters/google/__init__.py
@@ -17,7 +17,6 @@
 
 import re
 import urllib
-import urllib2
 from urlparse import urlparse, urljoin
 from collections import defaultdict
 try:
@@ -29,11 +28,12 @@ import logging
 from BeautifulSoup import BeautifulSoup
 
 from allura import model as M
+from forgeimporters.base import ProjectExtractor
 
 
 log = logging.getLogger(__name__)
 
-class GoogleCodeProjectExtractor(object):
+class GoogleCodeProjectExtractor(ProjectExtractor):
     BASE_URL = 'http://code.google.com'
     RE_REPO_TYPE = re.compile(r'(svn|hg|git)')
 
@@ -82,7 +82,7 @@ class GoogleCodeProjectExtractor(object):
         self.url = (self.get_page_url(page_name_or_url) if page_name_or_url in
                 self.PAGE_MAP else page_name_or_url)
         self.page = self._page_cache[page_name_or_url] = \
-                BeautifulSoup(urllib2.urlopen(self.url))
+                BeautifulSoup(self.urlopen(self.url))
         return self.page
 
     def get_page_url(self, page_name):
@@ -103,7 +103,7 @@ class GoogleCodeProjectExtractor(object):
         if icon_url == self.DEFAULT_ICON:
             return
         icon_name = urllib.unquote(urlparse(icon_url).path).split('/')[-1]
-        fp_ish = urllib2.urlopen(icon_url)
+        fp_ish = self.urlopen(icon_url)
         fp = StringIO(fp_ish.read())
         M.ProjectFile.save_image(
             icon_name, fp,

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/490c83e3/ForgeImporters/forgeimporters/tests/google/test_extractor.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/google/test_extractor.py b/ForgeImporters/forgeimporters/tests/google/test_extractor.py
index b4e64c0..9b6db45 100644
--- a/ForgeImporters/forgeimporters/tests/google/test_extractor.py
+++ b/ForgeImporters/forgeimporters/tests/google/test_extractor.py
@@ -19,12 +19,13 @@ from unittest import TestCase
 
 import mock
 
-from ... import google
+from forgeimporters import google
+from forgeimporters import base
 
 
 class TestGoogleCodeProjectExtractor(TestCase):
     def setUp(self):
-        self._p_urlopen = mock.patch.object(google.urllib2, 'urlopen')
+        self._p_urlopen = mock.patch.object(base.ProjectExtractor, 'urlopen')
         self._p_soup = mock.patch.object(google, 'BeautifulSoup')
         self.urlopen = self._p_urlopen.start()
         self.soup = self._p_soup.start()
@@ -105,7 +106,7 @@ class TestGoogleCodeProjectExtractor(TestCase):
 
     def _make_extractor(self, html):
         from BeautifulSoup import BeautifulSoup
-        with mock.patch.object(google, 'urllib2'):
+        with mock.patch.object(base.ProjectExtractor, 'urlopen'):
             extractor = google.GoogleCodeProjectExtractor(self.project, 'my-project')
         extractor.page = BeautifulSoup(html)
         extractor.get_page = lambda pagename: extractor.page

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/490c83e3/ForgeImporters/forgeimporters/tests/test_base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/test_base.py b/ForgeImporters/forgeimporters/tests/test_base.py
index 68be24d..57ed227 100644
--- a/ForgeImporters/forgeimporters/tests/test_base.py
+++ b/ForgeImporters/forgeimporters/tests/test_base.py
@@ -23,6 +23,19 @@ import mock
 from .. import base
 
 
+class TestProjectExtractor(TestCase):
+    @mock.patch('forgeimporters.base.h.urlopen')
+    @mock.patch('forgeimporters.base.urllib2.Request')
+    def test_urlopen(self, Request, urlopen):
+        r = base.ProjectExtractor.urlopen('myurl', data='foo')
+        Request.assert_called_once_with('myurl', data='foo')
+        req = Request.return_value
+        req.add_header.assert_called_once_with(
+                'User-Agent', 'Allura Data Importer (http://sf.net/p/allura)')
+        urlopen.assert_called_once_with(req, retries=3, codes=(408,))
+        self.assertEqual(r, urlopen.return_value)
+
+
 @mock.patch.object(base.ToolImporter, 'by_name')
 @mock.patch.object(base, 'c')
 def test_import_tool(c, by_name):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/490c83e3/ForgeTracker/forgetracker/import_support.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/import_support.py b/ForgeTracker/forgetracker/import_support.py
index 04d453a..e22c50e 100644
--- a/ForgeTracker/forgetracker/import_support.py
+++ b/ForgeTracker/forgetracker/import_support.py
@@ -34,6 +34,12 @@ from allura import model as M
 # Local imports
 from forgetracker import model as TM
 
+try:
+    from forgeimporters.base import ProjectExtractor
+    urlopen = ProjectExtractor.urlopen
+except ImportError:
+    urlopen = h.urlopen
+
 log = logging.getLogger(__name__)
 
 class ImportException(Exception):
@@ -276,15 +282,13 @@ class ImportSupport(object):
         comment.import_id = c.api_token.api_key
 
     def make_attachment(self, org_ticket_id, ticket_id, att_dict):
-        import urllib2
         if att_dict['size'] > self.ATTACHMENT_SIZE_LIMIT:
             self.errors.append('Ticket #%s: Attachment %s (@ %s) is too large, skipping' %
                                (org_ticket_id, att_dict['filename'], att_dict['url']))
             return
-        f = urllib2.urlopen(att_dict['url'])
+        f = urlopen(att_dict['url'])
         TM.TicketAttachment.save_attachment(att_dict['filename'], ResettableStream(f),
                                             artifact_id=ticket_id)
-        f.close()
 
 
     #

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/490c83e3/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py b/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py
index 7f111d7..af07f50 100644
--- a/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py
+++ b/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py
@@ -24,15 +24,18 @@ from urllib import quote, unquote
 from urlparse import urljoin, urlsplit
 
 try:
-    import requests
-except:
-    # Ignore this import if the requests package is not installed
-    pass
+    from forgeimporters.base import ProjectExtractor
+    urlopen = ProjectExtractor.urlopen
+except ImportError:
+    try:
+        from allura.lib.helpers import urlopen
+    except ImportError:
+        from urllib2 import urlopen
 
 try:
     # Ignore this import if the html2text package is not installed
     import html2text
-except:
+except ImportError:
     pass
 
 from BeautifulSoup import BeautifulSoup
@@ -128,8 +131,8 @@ class WikiExporter(object):
         glue = '&' if '?' in suburl else '?'
         return  url + glue + 'format=' + type
 
-    def fetch(self, url, **kwargs):
-        return requests.get(url, **kwargs)
+    def fetch(self, url):
+        return urlopen(url)
 
     def page_list(self):
         url = urljoin(self.base_url, self.PAGE_LIST_URL)


[15/25] git commit: [#6506] Added TracWikiImporter to requirements-sf.txt

Posted by tv...@apache.org.
[#6506] Added TracWikiImporter to requirements-sf.txt


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/2b0a101f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/2b0a101f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/2b0a101f

Branch: refs/heads/db/6482
Commit: 2b0a101f0a7a581fbde2fbc03441bcbcb3e616b1
Parents: 3e67aa6
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Aug 16 14:46:34 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Aug 16 14:46:34 2013 +0000

----------------------------------------------------------------------
 requirements-sf.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/2b0a101f/requirements-sf.txt
----------------------------------------------------------------------
diff --git a/requirements-sf.txt b/requirements-sf.txt
index 6d006ec..cc66c69 100644
--- a/requirements-sf.txt
+++ b/requirements-sf.txt
@@ -20,7 +20,7 @@ wsgipreload==1.2
 pyzmq==2.1.7
 html2text==3.200.3dev-20121112
 PyMollom==0.1
-TracWikiImporter=0.1.0
+TracWikiImporter==0.1.0
 
 # use version built from https://github.com/johnsca/GitPython/commits/tv/6000
 # for unmerged fixes for [#5411], [#6000], and [#6078]


[13/25] git commit: [#6506] Fixed unicode and attribute errors

Posted by tv...@apache.org.
[#6506] Fixed unicode and attribute errors

Signed-off-by: Cory Johns <cj...@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/08758635
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/08758635
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/08758635

Branch: refs/heads/db/6482
Commit: 0875863598e47a5d1990f3ae6b0d8edd04ddaa6c
Parents: 37aacca
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Thu Aug 15 20:55:35 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Thu Aug 15 20:55:35 2013 +0000

----------------------------------------------------------------------
 ForgeTracker/forgetracker/import_support.py             | 12 ++++++------
 ForgeTracker/forgetracker/scripts/import_tracker.py     |  2 +-
 .../forgewiki/scripts/wiki_from_trac/extractors.py      |  6 +++---
 3 files changed, 10 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/08758635/ForgeTracker/forgetracker/import_support.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/import_support.py b/ForgeTracker/forgetracker/import_support.py
index e22c50e..9637ebc 100644
--- a/ForgeTracker/forgetracker/import_support.py
+++ b/ForgeTracker/forgetracker/import_support.py
@@ -199,10 +199,10 @@ class ImportSupport(object):
                 new_f, conv = transform
                 remapped[new_f] = conv(v)
 
-        description = self.link_processing(remapped['description'])
+        description = h.really_unicode(self.link_processing(remapped['description']))
         if ticket_dict['submitter'] and not remapped['reported_by_id']:
-            description = 'Originally created by: {0}\n\n{1}'.format(
-                    ticket_dict['submitter'], description)
+            description = u'Originally created by: {0}\n\n{1}'.format(
+                    h.really_unicode(ticket_dict['submitter']), description)
         remapped['description'] = description
 
         ticket_num = ticket_dict['id']
@@ -273,10 +273,10 @@ class ImportSupport(object):
     def make_comment(self, thread, comment_dict):
         ts = self.parse_date(comment_dict['date'])
         author_id = self.get_user_id(comment_dict['submitter'])
-        text = self.link_processing(comment_dict['comment'])
+        text = h.really_unicode(self.link_processing(comment_dict['comment']))
         if not author_id and comment_dict['submitter']:
-            text = 'Originally posted by: {0}\n\n{1}'.format(
-                    comment_dict['submitter'], text)
+            text = u'Originally posted by: {0}\n\n{1}'.format(
+                    h.really_unicode(comment_dict['submitter']), text)
         comment = thread.post(text=text, timestamp=ts)
         comment.author_id = author_id
         comment.import_id = c.api_token.api_key

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/08758635/ForgeTracker/forgetracker/scripts/import_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/scripts/import_tracker.py b/ForgeTracker/forgetracker/scripts/import_tracker.py
index 32b4d1c..c60a775 100644
--- a/ForgeTracker/forgetracker/scripts/import_tracker.py
+++ b/ForgeTracker/forgetracker/scripts/import_tracker.py
@@ -60,7 +60,7 @@ def import_tracker(cli, project, tool, import_options, doc_txt,
         doc_import['trackers']['default'] = {}
         doc_import['trackers']['default']['artifacts'] = [ticket_in]
         res = cli.call(url, doc=json.dumps(doc_import), options=json.dumps(import_options))
-        assert res['status'] and not res['errors']
+        assert res['status'] and not res['errors'], res['errors']
         if validate:
             if res['warnings']:
                 print "Ticket id %s warnings: %s" % (ticket_in['id'], res['warnings'])

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/08758635/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py b/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py
index af07f50..6415bbf 100644
--- a/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py
+++ b/ForgeWiki/forgewiki/scripts/wiki_from_trac/extractors.py
@@ -138,7 +138,7 @@ class WikiExporter(object):
         url = urljoin(self.base_url, self.PAGE_LIST_URL)
         self.log('Fetching list of pages from %s' % url)
         r = self.fetch(url)
-        html = BeautifulSoup(r.content)
+        html = BeautifulSoup(r)
         pages = html.find('div', attrs=self.CONTENT_DIV_ATTRS) \
                     .find('ul').findAll('li')
         pages = [page.find('a').text
@@ -165,14 +165,14 @@ class WikiExporter(object):
         url = self.url(self.PAGE_URL % title)
         self.log('Fetching page %s' % url)
         r = self.fetch(url)
-        html = BeautifulSoup(r.content)
+        html = BeautifulSoup(r)
         return html.find('div', attrs=self.CONTENT_DIV_ATTRS)
 
     def _get_page_regex(self, title):
         url = self.url(self.PAGE_URL % title, 'txt')
         self.log('Fetching page %s' % url)
         r = self.fetch(url)
-        return r.content
+        return r
 
     def convert_title(self, title):
         title = self.RENAME_PAGES.get(title, title)


[11/25] git commit: [#6506] Minor mocking cleanup

Posted by tv...@apache.org.
[#6506] Minor mocking cleanup

Signed-off-by: Cory Johns <cj...@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/3f6bf33c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/3f6bf33c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/3f6bf33c

Branch: refs/heads/db/6482
Commit: 3f6bf33c4177e1e9889950383830f9374214c3eb
Parents: 490c83e
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Thu Aug 15 16:31:10 2013 +0000
Committer: Cory Johns <cj...@slashdotmedia.com>
Committed: Thu Aug 15 16:31:10 2013 +0000

----------------------------------------------------------------------
 Allura/allura/lib/decorators.py        | 6 +-----
 Allura/allura/tests/test_decorators.py | 7 +++----
 2 files changed, 4 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3f6bf33c/Allura/allura/lib/decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/decorators.py b/Allura/allura/lib/decorators.py
index 137152e..8604a92 100644
--- a/Allura/allura/lib/decorators.py
+++ b/Allura/allura/lib/decorators.py
@@ -32,10 +32,6 @@ from pylons import tmpl_context as c
 from allura.lib import helpers as h
 
 
-def _get_model():
-    from allura import model as M
-    return M
-
 def task(*args, **kw):
     """Decorator that adds a ``.post()`` function to the decorated callable.
 
@@ -62,7 +58,7 @@ def task(*args, **kw):
             cm = (h.notifications_disabled if project and
                     kw.get('notifications_disabled') else h.null_contextmanager)
             with cm(project):
-                M = _get_model()
+                from allura import model as M
                 return M.MonQTask.post(func, args, kwargs, delay=delay)
         # if decorating a class, have to make it a staticmethod
         # or it gets a spurious cls argument

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3f6bf33c/Allura/allura/tests/test_decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_decorators.py b/Allura/allura/tests/test_decorators.py
index 893f327..41ba631 100644
--- a/Allura/allura/tests/test_decorators.py
+++ b/Allura/allura/tests/test_decorators.py
@@ -20,8 +20,8 @@ class TestTask(TestCase):
         self.assertTrue(hasattr(func, 'post'))
 
     @patch('allura.lib.decorators.c')
-    @patch('allura.lib.decorators._get_model')
-    def test_post(self, c, _get_model):
+    @patch('allura.model.MonQTask')
+    def test_post(self, c, MonQTask):
         @task(disable_notifications=True)
         def func(s, foo=None, **kw):
             pass
@@ -34,6 +34,5 @@ class TestTask(TestCase):
             self.assertEqual(f, func)
 
         c.project.notifications_disabled = False
-        M = _get_model.return_value
-        M.MonQTask.post.side_effect = mock_post
+        MonQTask.post.side_effect = mock_post
         func.post('test', foo=2, delay=1)


[02/25] git commit: [#5177] Added test for default source and target_branch values for merge requests

Posted by tv...@apache.org.
[#5177] Added test for default source and target_branch values for merge requests

Signed-off-by: Cory Johns <cj...@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/dfafc625
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/dfafc625
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/dfafc625

Branch: refs/heads/db/6482
Commit: dfafc62517a4ed101fa38f1e93c2b11e8d2146ef
Parents: 45732c0
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Tue Aug 13 19:18:32 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Tue Aug 13 21:28:33 2013 +0000

----------------------------------------------------------------------
 .../forgegit/tests/functional/test_controllers.py | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/dfafc625/ForgeGit/forgegit/tests/functional/test_controllers.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index d8c3f2c..597c5bb 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -33,6 +33,7 @@ from allura.lib import helpers as h
 from alluratest.controller import TestController
 from allura.tests.decorators import with_tool
 from forgegit.tests import with_git
+from forgegit import model as GM
 
 class _TestCase(TestController):
 
@@ -517,6 +518,23 @@ class TestFork(_TestCase):
                           params=dict(status='rejected')).follow()
         assert 'Merge Request #%s:  (rejected)' % mr_num in r, r
 
+    def test_merge_request_default_branches(self):
+        _select_val = lambda r, n: r.html.find('select', {'name': n}).find(selected=True).string
+        r = self.app.get('/p/test2/code/request_merge')
+        assert_equal(_select_val(r, 'source_branch'), 'master')
+        assert_equal(_select_val(r, 'target_branch'), 'master')
+        r = self.app.get('/p/test2/code/ci/zz/tree/').click('Request Merge')
+        assert_equal(_select_val(r, 'source_branch'), 'zz')
+        assert_equal(_select_val(r, 'target_branch'), 'master')
+        GM.Repository.query.get(_id=c.app.repo._id).default_branch_name = 'zz'
+        ThreadLocalORMSession.flush_all()
+        r = self.app.get('/p/test2/code/request_merge')
+        assert_equal(_select_val(r, 'source_branch'), 'master')
+        assert_equal(_select_val(r, 'target_branch'), 'zz')
+        r = self.app.get('/p/test2/code/ci/zz/tree/').click('Request Merge')
+        assert_equal(_select_val(r, 'source_branch'), 'zz')
+        assert_equal(_select_val(r, 'target_branch'), 'zz')
+
 class TestDiff(TestController):
 
     def setUp(self):


[23/25] git commit: [#6482] change page to show importers in a grid

Posted by tv...@apache.org.
[#6482] change page to show importers in a grid


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/451a6915
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/451a6915
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/451a6915

Branch: refs/heads/db/6482
Commit: 451a6915eb37292a00331d9d1030d90646696dc9
Parents: 7b5504f
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Tue Aug 13 19:20:11 2013 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Tue Aug 20 13:27:53 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/base.py           | 31 +++++++++--
 .../forgeimporters/templates/list_all.html      | 58 ++++++++------------
 2 files changed, 51 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/451a6915/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index eb5748e..a5023da 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -17,6 +17,7 @@
 
 import logging
 import urllib2
+from collections import defaultdict
 
 from pkg_resources import iter_entry_points
 
@@ -292,11 +293,33 @@ class ProjectToolsImportController(object):
     @with_trailing_slash
     @expose('jinja:forgeimporters:templates/list_all.html')
     def index(self, *a, **kw):
-        importers = {}
-        for app_name, app in g.entry_points['tool'].iteritems():
-            importers[app] = ToolImporter.by_app(app)
+        # make dictionaries of both axis
+        importers_by_source = defaultdict(dict)
+        importers_by_tool = defaultdict(dict)
+        for ep in iter_entry_points('allura.importers'):
+            importer = ep.load()
+            importers_by_source[importer.source][ep.name] = importer
+            for tool in aslist(importer.target_app):
+                importers_by_tool[tool][ep.name] = importer
+
+        relevant_tools = sorted(importers_by_tool.keys(), key=lambda t: t.tool_label)
+
+        # build a full matrix including empty spots
+        importer_matrix = dict() # source -> [importer names]
+        for source, src_importers in importers_by_source.iteritems():
+            row = list()
+            for tool in relevant_tools:
+                for ep_name, importer in src_importers.iteritems():
+                    if tool in aslist(importer.target_app):
+                        row.append(ep_name)
+                        break
+                else:
+                    row.append(None)
+            importer_matrix[source] = row
+
         return {
-            'importers': importers
+            'tools': relevant_tools,
+            'importer_matrix': importer_matrix,
         }
 
     @expose()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/451a6915/ForgeImporters/forgeimporters/templates/list_all.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/templates/list_all.html b/ForgeImporters/forgeimporters/templates/list_all.html
index 58c9f34..b91e0df 100644
--- a/ForgeImporters/forgeimporters/templates/list_all.html
+++ b/ForgeImporters/forgeimporters/templates/list_all.html
@@ -21,42 +21,32 @@
 
 {% block title %}{{c.project.name}} / Import{% endblock %}
 
-{% block header %}Import from external sources{% endblock %}
-
-{% block extra_css %}
-<style type="text/css">
-    .tool {
-        margin-bottom: 3em;
-    }
-    .tool img {
-        float: left;
-        margin: 0 1em;
-    }
-    .importer .descr {
-        margin: 0 2em 1em 2em;
-    }
-</style>
-{% endblock %}
+{% block header %}Import{% endblock %}
 
 {% block content %}
-    {% for tool, tool_importers in importers.iteritems() %}
-        <div class="tool">
-        {% for importer_name, importer in tool_importers.iteritems() %}
-            {% if loop.first %}
-                <img src="{{ g.theme.app_icon_url(tool, 48) }}" alt="{{ tool.tool_label }} icon">
-                <h2>{{ tool.tool_label }}</h2>
-            {% endif %}
-            <div class="importer">
-                <a href="{{importer_name}}"><h3>
-                {{importer.source}}:
-                {{importer.tool_label}}
-                </h3>
-                </a>
-                <div class="descr">
-                {{importer.tool_description}}
-                </div>
-            </div>
+<p>
+    To import from one of these external sources into your project, just click on "Import" and fill out the form.
+</p>
+<table>
+    <thead>
+    <tr>
+        <th></th>
+        {% for tool in tools %}
+            <th>{{tool.tool_label}}</th>
+        {% endfor %}
+    </tr>
+    </thead>
+    {% for source, importers in importer_matrix|dictsort %}
+        <tr>
+        <td><strong>{{ source }}</strong></td>
+        {% for importer_name in importers %}
+            <td>
+                {% if importer_name %}
+                <a href="{{importer_name}}">Import</a>
+                {% endif %}
+            </td>
         {% endfor %}
-        </div>
+        </tr>
     {% endfor %}
+</table>
 {% endblock %}


[06/25] git commit: [#4818] add documentation for extensions

Posted by tv...@apache.org.
[#4818] add documentation for extensions


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/df942cb2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/df942cb2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/df942cb2

Branch: refs/heads/db/6482
Commit: df942cb262f9b541dd6235420e3a39c1398e6693
Parents: 5f836ba
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Aug 9 19:41:28 2013 +0000
Committer: Tim Van Steenburgh <tv...@gmail.com>
Committed: Wed Aug 14 13:12:13 2013 +0000

----------------------------------------------------------------------
 Allura/allura/app.py               |  2 +-
 Allura/allura/lib/plugin.py        | 10 +++---
 Allura/docs/api/app.rst            | 13 +-------
 Allura/docs/api/lib/plugin.rst     | 24 ++++++++++++++
 Allura/docs/api/lib/spam.rst       | 24 ++++++++++++++
 Allura/docs/extending.rst          | 55 +++++++++++++++++++++++++++++++++
 Allura/docs/guides/message_bus.rst |  4 ++-
 Allura/docs/index.rst              |  3 +-
 8 files changed, 115 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/df942cb2/Allura/allura/app.py
----------------------------------------------------------------------
diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index c1d2517..5ac11f2 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -164,7 +164,7 @@ class Application(object):
     The base Allura pluggable application
 
     After extending this, expose the app by adding an entry point in your
-    setup.py:
+    setup.py::
 
         [allura]
         myapp = foo.bar.baz:MyAppClass

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/df942cb2/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index f972c26..f600363 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -56,12 +56,12 @@ class AuthenticationProvider(object):
     '''
     An interface to provide authentication services for Allura.
 
-    To use a new provider, expose an entry point in setup.py:
+    To use a new provider, expose an entry point in setup.py::
 
         [allura.auth]
         myprovider = foo.bar:MyAuthProvider
 
-    Then in your .ini file, set auth.method=myprovider
+    Then in your .ini file, set ``auth.method=myprovider``
     '''
 
     def __init__(self, request):
@@ -346,7 +346,7 @@ class ProjectRegistrationProvider(object):
     and the default.  Extend this class with your own if you need to add more
     functionality.
 
-    To use a new provider, expose an entry point in setup.py:
+    To use a new provider, expose an entry point in setup.py::
 
         [allura.project_registration]
         myprovider = foo.bar:MyAuthProvider
@@ -625,7 +625,7 @@ class ThemeProvider(object):
     and the default.  Extend this class with your own if you need to add more
     functionality.
 
-    To use a new provider, expose an entry point in setup.py:
+    To use a new provider, expose an entry point in setup.py::
 
         [allura.theme]
         myprovider = foo.bar:MyThemeProvider
@@ -855,7 +855,7 @@ class UserPreferencesProvider(object):
     '''
     An interface for user preferences, like display_name and email_address
 
-    To use a new provider, expose an entry point in setup.py:
+    To use a new provider, expose an entry point in setup.py::
 
         [allura.user_prefs]
         myprefs = foo.bar:MyUserPrefProvider

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/df942cb2/Allura/docs/api/app.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/api/app.rst b/Allura/docs/api/app.rst
index 491d0b4..f99a0c2 100644
--- a/Allura/docs/api/app.rst
+++ b/Allura/docs/api/app.rst
@@ -22,15 +22,4 @@
 
 .. automodule:: allura.app
 
-  .. autoclass:: Application
-      :members:
-
-  .. autoclass:: ConfigOption
-      :members:
-
-  .. autoclass:: DefaultAdminController
-      :members:
-
-  .. autoclass:: SitemapEntry
-      :members:
-
+    :members:

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/df942cb2/Allura/docs/api/lib/plugin.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/api/lib/plugin.rst b/Allura/docs/api/lib/plugin.rst
new file mode 100644
index 0000000..77e619b
--- /dev/null
+++ b/Allura/docs/api/lib/plugin.rst
@@ -0,0 +1,24 @@
+..     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.
+
+.. _plugin_module:
+
+:mod:`allura.lib.plugin`
+-------------------------------------
+
+.. automodule:: allura.lib.plugin
+    :members:

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/df942cb2/Allura/docs/api/lib/spam.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/api/lib/spam.rst b/Allura/docs/api/lib/spam.rst
new file mode 100644
index 0000000..b9558a3
--- /dev/null
+++ b/Allura/docs/api/lib/spam.rst
@@ -0,0 +1,24 @@
+..     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.
+
+.. _spam_module:
+
+:mod:`allura.lib.spam`
+-------------------------------------
+
+.. automodule:: allura.lib.spam
+    :members:

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/df942cb2/Allura/docs/extending.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/extending.rst b/Allura/docs/extending.rst
new file mode 100644
index 0000000..a430202
--- /dev/null
+++ b/Allura/docs/extending.rst
@@ -0,0 +1,55 @@
+..     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.
+
+Extending Allura with Entry Points
+===================================
+
+There are many extension points to extending Allura.  They all make themselves
+known to Allura via python entry points defined in ``setup.py``.  Many are then
+available immediately.  Others, such as authentication providers or themes, need
+to be specified in your ``.ini`` file, since you may only have one enabled at a time.
+
+The available extension points for Allura are:
+
+* :class:`allura.app.Application` (aka tool) and :class:`allura.app.Artifact`
+* :class:`allura.lib.plugin.ThemeProvider`
+* :class:`allura.lib.plugin.ProjectRegistrationProvider`
+* :class:`allura.lib.plugin.AuthenticationProvider`
+* :class:`allura.lib.plugin.UserPreferencesProvider`
+* :class:`allura.lib.plugin.AdminExtension`
+* :class:`allura.lib.spam.SpamFilter`
+* ``site_stats`` in the root API data.  Docs in :class:`allura.controllers.rest.RestController`
+* :mod:`allura.lib.package_path_loader` (for overriding templates)
+
+A listing of available 3rd-party extensions is at https://sourceforge.net/p/allura/wiki/Extensions/
+
+Other entry points are used to provider ``paster`` commands and ``easy_widget`` configuration.
+
+
+Event Handlers
+==============
+
+Another way to extend Allura is set up event handlers to respond to Allura events.
+There is documentation and examples at :ref:`events`.
+
+The events that allura publishes are:
+
+* project_created
+* project_updated
+* repo_cloned
+* repo_refreshed
+* repo_clone_task_failed
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/df942cb2/Allura/docs/guides/message_bus.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/guides/message_bus.rst b/Allura/docs/guides/message_bus.rst
index f8285e1..11067c9 100644
--- a/Allura/docs/guides/message_bus.rst
+++ b/Allura/docs/guides/message_bus.rst
@@ -61,6 +61,8 @@ well::
 
     commit()
 
+.. _events:
+
 Events
 -------------------
 
@@ -82,7 +84,7 @@ Under the covers, this is scheduling an `event` task that calls all the handlers
 for a particular named event.  Note that you can pass arguments (\*args, and
 \*\*kwargs) to event handlers just like you do to tasks, with the exception that
 the topic name (above, this would be 'project_updated') is always the first
-parameter passed to the event handler.  
+parameter passed to the event handler.
 
 Running the Task Daemon
 ----------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/df942cb2/Allura/docs/index.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/index.rst b/Allura/docs/index.rst
index 56b1b52..877b98d 100644
--- a/Allura/docs/index.rst
+++ b/Allura/docs/index.rst
@@ -57,12 +57,13 @@ Inside the Platform Components
    guides/message_bus
    guides/email
    guides/permissions
+   extending
 
 API Documentation
 ==================
 
 .. toctree::
-   :maxdepth: 1
+   :maxdepth: 2
    :glob:
 
    api/*