You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/03/06 13:35:08 UTC

[01/26] allura git commit: [#7832] ticket:731 Webhook GET endpoint

Repository: allura
Updated Branches:
  refs/heads/ib/7830 7c2bef4e5 -> ad139b7b7 (forced update)


[#7832] ticket:731 Webhook GET endpoint


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

Branch: refs/heads/ib/7830
Commit: 41c054c4ed6e17b945f75c0f5d0928315c431491
Parents: 37384e9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 12:14:28 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:51 2015 +0000

----------------------------------------------------------------------
 Allura/allura/lib/repository.py      |  7 +++++++
 Allura/allura/tests/test_webhooks.py | 17 +++++++++++++++++
 Allura/allura/webhooks.py            | 19 +++++++++++++++++++
 3 files changed, 43 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/41c054c4/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 07b1b06..8fd414f 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -309,3 +309,10 @@ class RestWebhooksLookup(BaseController):
         }
         return {'webhooks': [hook.__json__() for hook in configured_hooks],
                 'limits': limits}
+
+    @expose()
+    def _lookup(self, name, *remainder):
+        for hook in self.app._webhooks:
+            if hook.type == name and hook.api_controller:
+                return hook.api_controller(hook, self.app), remainder
+        raise exc.HTTPNotFound, name

http://git-wip-us.apache.org/repos/asf/allura/blob/41c054c4/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index c758df1..5bbee21 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -713,3 +713,20 @@ class TestWebhookRestController(TestRestApiBase):
             'limits': {'repo-push': {'max': 3, 'used': 3}},
         }
         dd.assert_equal(r.json, expected)
+
+    def test_webhook_GET_404(self):
+        r = self.api_get(self.url + '/repo-push/invalid')
+        assert_equal(r.status_int, 404)
+
+    def test_webhook_GET(self):
+        webhook = self.webhooks[0]
+        r = self.api_get('{}/repo-push/{}'.format(self.url, webhook._id))
+        expected = {
+            '_id': unicode(webhook._id),
+            'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                   '/src/webhooks/repo-push/{}'.format(webhook._id),
+            'type': 'repo-push',
+            'hook_url': 'http://httpbin.org/post/0',
+            'mod_date': unicode(webhook.mod_date),
+        }
+        dd.assert_equal(r.json, expected)

http://git-wip-us.apache.org/repos/asf/allura/blob/41c054c4/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 2fa411f..dffaa21 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -191,6 +191,24 @@ class WebhookController(BaseController):
                 'form': form}
 
 
+class WebhookRestController(BaseController):
+    def __init__(self, sender, app):
+        super(WebhookRestController, self).__init__()
+        self.sender = sender()
+        self.app = app
+        self.create_form = WebhookController.create_form
+        self.edit_form = WebhookController.edit_form
+
+    @expose('json:')
+    def _default(self, webhook, **kw):
+        form = self.edit_form(self.sender, self.app)
+        try:
+            wh = form.fields['webhook'].to_python(webhook)
+        except Invalid:
+            raise exc.HTTPNotFound()
+        return wh.__json__()
+
+
 class SendWebhookHelper(object):
     def __init__(self, webhook, payload):
         self.webhook = webhook
@@ -278,6 +296,7 @@ class WebhookSender(object):
     type = None
     triggered_by = []
     controller = WebhookController
+    api_controller = WebhookRestController
 
     def get_payload(self, **kw):
         """Return a dict with webhook payload"""


[22/26] allura git commit: [#7830] ticket:729 Add tests

Posted by je...@apache.org.
[#7830] ticket:729 Add tests


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

Branch: refs/heads/ib/7830
Commit: 0135de28fd3d3e9384994070890e10d93f6ca259
Parents: 46767d9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 19 15:53:48 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Mar 6 09:43:08 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/model/test_repo.py          | 38 ++++++++++++++++
 Allura/allura/tests/test_tasks.py               | 11 +++++
 .../forgegit/tests/model/test_repository.py     | 46 ++++++++++++++++++++
 3 files changed, 95 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/0135de28/Allura/allura/tests/model/test_repo.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/model/test_repo.py b/Allura/allura/tests/model/test_repo.py
index 9e2af07..669f940 100644
--- a/Allura/allura/tests/model/test_repo.py
+++ b/Allura/allura/tests/model/test_repo.py
@@ -713,3 +713,41 @@ class TestModelCache(unittest.TestCase):
         session.assert_called_once_with(tree1)
         session.return_value.flush.assert_called_once_with(tree1)
         session.return_value.expunge.assert_called_once_with(tree1)
+
+
+class TestMergeRequest(object):
+
+    def setUp(self):
+        setup_basic_test()
+        setup_global_objects()
+        self.mr = M.MergeRequest(app_config=mock.Mock(_id=ObjectId()))
+        self.mr.app = mock.Mock(forkable=True)
+
+    def test_can_merge(self):
+        assert_equal(self.mr.can_merge(),
+                     self.mr.app.repo.can_merge.return_value)
+        self.mr.app.repo.can_merge.assert_called_once_with(self.mr)
+
+        self.mr.app.reset_mock()
+        self.mr.app.forkable = False
+        assert_equal(self.mr.can_merge(), False)
+        assert_equal(self.mr.app.repo.can_merge.called, False)
+
+    @mock.patch('allura.tasks.repo_tasks.merge', autospec=True)
+    def test_merge(self, merge_task):
+        self.mr.merge_task_status = lambda: None
+        self.mr.merge()
+        merge_task.post.assert_called_once_with(self.mr._id)
+
+        merge_task.reset_mock()
+        self.mr.merge_task_status = lambda: 'ready'
+        self.mr.merge()
+        assert_equal(merge_task.post.called, False)
+
+    def test_merge_task_status(self):
+        from allura.tasks import repo_tasks
+        assert_equal(self.mr.merge_task_status(), None)
+        repo_tasks.merge.post(self.mr._id)
+        assert_equal(self.mr.merge_task_status(), 'ready')
+        M.MonQTask.run_ready()
+        assert_equal(self.mr.merge_task_status(), 'complete')

http://git-wip-us.apache.org/repos/asf/allura/blob/0135de28/Allura/allura/tests/test_tasks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_tasks.py b/Allura/allura/tests/test_tasks.py
index 13d5c17..d822229 100644
--- a/Allura/allura/tests/test_tasks.py
+++ b/Allura/allura/tests/test_tasks.py
@@ -64,6 +64,17 @@ class TestRepoTasks(unittest.TestCase):
         assert_equal(post_event.call_args[0][2], None)
         # ignore args[3] which is a traceback string
 
+    @mock.patch('allura.tasks.repo_tasks.session', autospec=True)
+    @mock.patch.object(M, 'MergeRequest', autospec=True)
+    def test_merge(self, MR, session):
+        mr = mock.Mock(_id='_id')
+        MR.query.get.return_value = mr
+        repo_tasks.merge(mr._id)
+        mr.app.repo.merge.assert_called_once_with(mr)
+        assert_equal(mr.status, 'merged')
+        session.assert_called_once_with(mr)
+        session.return_value.flush.assert_called_once_with(mr)
+
 
 class TestEventTasks(unittest.TestCase):
 

http://git-wip-us.apache.org/repos/asf/allura/blob/0135de28/ForgeGit/forgegit/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index 231e479..d79fc7b 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -592,6 +592,52 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         }
         assert_equals(payload, expected_payload)
 
+    def test_can_merge(self):
+        mr = mock.Mock(downstream_repo_url='downstream-url',
+                       source_branch='source-branch',
+                       target_branch='target-branch',
+                       downstream=mock.Mock(commit_id='cid'))
+        git = mock.Mock()
+        git.merge_tree.return_value = 'clean merge'
+        self.repo._impl._git.git = git
+        assert_equal(self.repo.can_merge(mr), True)
+        git.fetch.assert_called_once_with('downstream-url', 'source-branch')
+        git.merge_base.assert_called_once_with('cid', 'target-branch')
+        git.merge_tree.assert_called_once_with(
+            git.merge_base.return_value,
+            'target-branch',
+            'cid')
+        git.merge_tree.return_value = '+<<<<<<<'
+        assert_equal(self.repo.can_merge(mr), False)
+
+    @mock.patch('forgegit.model.git_repo.tempfile', autospec=True)
+    @mock.patch('forgegit.model.git_repo.git', autospec=True)
+    @mock.patch('forgegit.model.git_repo.GitImplementation', autospec=True)
+    @mock.patch('forgegit.model.git_repo.shutil', autospec=True)
+    def test_merge(self, shutil, GitImplementation, git, tempfile):
+        mr = mock.Mock(downstream_repo_url='downstream-url',
+                       source_branch='source-branch',
+                       target_branch='target-branch',
+                       downstream=mock.Mock(commit_id='cid'))
+        _git = mock.Mock()
+        self.repo._impl._git.git = _git
+        self.repo.merge(mr)
+        git.Repo.clone_from.assert_called_once_with(
+            self.repo.clone_url('rw'),
+            to_path=tempfile.mkdtemp.return_value,
+            bare=False)
+        tmp_repo = GitImplementation.return_value._git
+        assert_equal(
+            tmp_repo.git.fetch.call_args_list,
+            [mock.call('origin', 'target-branch'),
+             mock.call('downstream-url', 'source-branch')])
+        tmp_repo.git.checkout.assert_called_once_with('target-branch')
+        tmp_repo.git.merge.assert_called_once_with('cid')
+        tmp_repo.git.push.assert_called_once_with('origin', 'target-branch')
+        shutil.rmtree.assert_called_once_with(
+            tempfile.mkdtemp.return_value,
+            ignore_errors=True)
+
 
 class TestGitImplementation(unittest.TestCase):
 


[12/26] allura git commit: [#7832] ticket:740 Remove result:ok from responses

Posted by je...@apache.org.
[#7832] ticket:740 Remove result:ok from responses


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

Branch: refs/heads/ib/7830
Commit: 7a934faebf5d0cacf61626217fcbea0c910558f3
Parents: aa6daf7
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Feb 25 14:07:51 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:53 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_webhooks.py | 61 +++++++++++++------------------
 Allura/allura/webhooks.py            |  7 ++--
 2 files changed, 28 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/7a934fae/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index e86dbd8..d7fb75c 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -721,16 +721,14 @@ class TestWebhookRestController(TestRestApiBase):
         webhook = self.webhooks[0]
         r = self.api_get('{}/repo-push/{}'.format(self.url, webhook._id))
         expected = {
-            u'result': u'ok',
-            u'webhook': {
-                '_id': unicode(webhook._id),
-                'url': 'http://localhost/rest/adobe/adobe-1/admin'
-                       '/src/webhooks/repo-push/{}'.format(webhook._id),
-                'type': 'repo-push',
-                'hook_url': 'http://httpbin.org/post/0',
-                'mod_date': unicode(webhook.mod_date),
-            },
+            '_id': unicode(webhook._id),
+            'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                   '/src/webhooks/repo-push/{}'.format(webhook._id),
+            'type': 'repo-push',
+            'hook_url': 'http://httpbin.org/post/0',
+            'mod_date': unicode(webhook.mod_date),
         }
+        dd.assert_equal(r.status_int, 200)
         dd.assert_equal(r.json, expected)
 
     def test_create_validation(self):
@@ -767,15 +765,12 @@ class TestWebhookRestController(TestRestApiBase):
         webhook = M.Webhook.query.get(hook_url=data['url'])
         assert_equal(webhook.secret, 'super-secret')  # secret generated
         expected = {
-            u'result': u'ok',
-            u'webhook': {
-                '_id': unicode(webhook._id),
-                'url': 'http://localhost/rest/adobe/adobe-1/admin'
-                       '/src/webhooks/repo-push/{}'.format(webhook._id),
-                'type': 'repo-push',
-                'hook_url': data['url'],
-                'mod_date': unicode(webhook.mod_date),
-            },
+            '_id': unicode(webhook._id),
+            'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                   '/src/webhooks/repo-push/{}'.format(webhook._id),
+            'type': 'repo-push',
+            'hook_url': data['url'],
+            'mod_date': unicode(webhook.mod_date),
         }
         dd.assert_equal(r.json, expected)
         assert_equal(M.Webhook.query.find().count(), len(self.webhooks) + 1)
@@ -830,15 +825,12 @@ class TestWebhookRestController(TestRestApiBase):
         assert_equal(webhook.hook_url, data['url'])
         assert_equal(webhook.secret, 'secret-0')
         expected = {
-            u'result': u'ok',
-            u'webhook': {
-                '_id': unicode(webhook._id),
-                'url': 'http://localhost/rest/adobe/adobe-1/admin'
-                       '/src/webhooks/repo-push/{}'.format(webhook._id),
-                'type': 'repo-push',
-                'hook_url': data['url'],
-                'mod_date': unicode(webhook.mod_date),
-            },
+            '_id': unicode(webhook._id),
+            'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                   '/src/webhooks/repo-push/{}'.format(webhook._id),
+            'type': 'repo-push',
+            'hook_url': data['url'],
+            'mod_date': unicode(webhook.mod_date),
         }
         dd.assert_equal(r.json, expected)
 
@@ -853,15 +845,12 @@ class TestWebhookRestController(TestRestApiBase):
         assert_equal(webhook.hook_url, 'http://hook.slack.com/abcd')
         assert_equal(webhook.secret, 'new-secret')
         expected = {
-            u'result': u'ok',
-            u'webhook': {
-                '_id': unicode(webhook._id),
-                'url': 'http://localhost/rest/adobe/adobe-1/admin'
-                       '/src/webhooks/repo-push/{}'.format(webhook._id),
-                'type': 'repo-push',
-                'hook_url': 'http://hook.slack.com/abcd',
-                'mod_date': unicode(webhook.mod_date),
-            },
+            '_id': unicode(webhook._id),
+            'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                   '/src/webhooks/repo-push/{}'.format(webhook._id),
+            'type': 'repo-push',
+            'hook_url': 'http://hook.slack.com/abcd',
+            'mod_date': unicode(webhook.mod_date),
         }
         dd.assert_equal(r.json, expected)
 

http://git-wip-us.apache.org/repos/asf/allura/blob/7a934fae/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 19903d5..d41f477 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -243,7 +243,7 @@ class WebhookRestController(BaseController):
             # refetch updated values (e.g. mod_date)
             session(webhook).expunge(webhook)
             webhook = M.Webhook.query.get(_id=webhook._id)
-            return {'result': 'ok', 'webhook': webhook}
+            return webhook.__json__()
         else:
             limits = {
                 'max': M.Webhook.max_hooks(
@@ -275,7 +275,7 @@ class WebhookRestController(BaseController):
         elif request.method == 'DELETE':
             return self._delete(wh)
         else:
-            return {'result': 'ok', 'webhook': wh}
+            return wh.__json__()
 
     def _edit(self, webhook, form, **kw):
         old_secret = webhook.secret
@@ -300,8 +300,7 @@ class WebhookRestController(BaseController):
         # refetch updated values (e.g. mod_date)
         session(webhook).expunge(webhook)
         webhook = M.Webhook.query.get(_id=webhook._id)
-        return {'result': 'ok',
-                'webhook': webhook}
+        return webhook.__json__()
 
     def _delete(self, webhook):
         webhook.delete()


[06/26] allura git commit: [#7832] ticket:731 Delete webhook endpoint

Posted by je...@apache.org.
[#7832] ticket:731 Delete webhook endpoint


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

Branch: refs/heads/ib/7830
Commit: f392d5b2880d1ff777cd402f8db24f62e0eaa962
Parents: 2fac9ec
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 23 09:58:09 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:52 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_webhooks.py | 16 ++++++++++++++++
 Allura/allura/webhooks.py            | 11 +++++++++++
 2 files changed, 27 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/f392d5b2/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index ee9cd0b..50a9803 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -878,3 +878,19 @@ class TestWebhookRestController(TestRestApiBase):
         webhook = M.Webhook.query.get(_id=webhook._id)
         assert_equal(webhook.hook_url, 'http://httpbin.org/post/0')
         assert_equal(webhook.secret, 'secret-0')
+
+    def test_delete_validation(self):
+        url = '{}/repo-push/invalid'.format(self.url)
+        self.api_delete(url, status=404)
+
+    def test_delete(self):
+        assert_equal(M.Webhook.query.find().count(), 3)
+        webhook = self.webhooks[0]
+        url = '{}/repo-push/{}'.format(self.url, webhook._id)
+        msg = 'delete webhook repo-push {} {}'.format(
+            webhook.hook_url, self.git.config.url())
+        with td.audits(msg):
+            r = self.api_delete(url, status=200)
+        dd.assert_equal(r.json, {u'result': u'ok'})
+        assert_equal(M.Webhook.query.find().count(), 2)
+        assert_equal(M.Webhook.query.get(_id=webhook._id), None)

http://git-wip-us.apache.org/repos/asf/allura/blob/f392d5b2/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index f30994f..19903d5 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -272,6 +272,8 @@ class WebhookRestController(BaseController):
             raise exc.HTTPNotFound()
         if request.method == 'POST':
             return self._edit(wh, form, **kw)
+        elif request.method == 'DELETE':
+            return self._delete(wh)
         else:
             return {'result': 'ok', 'webhook': wh}
 
@@ -301,6 +303,15 @@ class WebhookRestController(BaseController):
         return {'result': 'ok',
                 'webhook': webhook}
 
+    def _delete(self, webhook):
+        webhook.delete()
+        M.AuditLog.log(
+            'delete webhook %s %s %s',
+            webhook.type,
+            webhook.hook_url,
+            webhook.app_config.url())
+        return {'result': 'ok'}
+
 
 class SendWebhookHelper(object):
     def __init__(self, webhook, payload):


[04/26] allura git commit: [#7832] ticket:731 List webhooks API

Posted by je...@apache.org.
[#7832] ticket:731 List webhooks API


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

Branch: refs/heads/ib/7830
Commit: 804d86d6472d16591faf9fb6e6898dce1d6a636f
Parents: 8c604ce
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 11:07:16 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:51 2015 +0000

----------------------------------------------------------------------
 Allura/allura/lib/repository.py      |  6 ++-
 Allura/allura/model/webhook.py       | 10 +++++
 Allura/allura/tests/test_webhooks.py | 72 ++++++++++++++++++++++++++++++-
 3 files changed, 86 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/804d86d6/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index d5d467b..41e671d 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -293,4 +293,8 @@ class RestWebhooksLookup(BaseController):
         webhooks = self.app._webhooks
         if len(webhooks) == 0:
             raise exc.HTTPNotFound()
-        return {'test': 'works'}
+        configured_hooks = M.Webhook.query.find({
+            'type': {'$in': [wh.type for wh in webhooks]},
+            'app_config_id': self.app.config._id}
+        ).sort('_id', 1).all()
+        return {'webhooks': [hook.__json__() for hook in configured_hooks]}

http://git-wip-us.apache.org/repos/asf/allura/blob/804d86d6/Allura/allura/model/webhook.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
index d1165ff..0d5140e 100644
--- a/Allura/allura/model/webhook.py
+++ b/Allura/allura/model/webhook.py
@@ -22,6 +22,7 @@ from paste.deploy.converters import asint
 from tg import config
 
 from allura.model import Artifact
+from allura.lib import helpers as h
 
 
 class Webhook(Artifact):
@@ -53,3 +54,12 @@ class Webhook(Artifact):
     def update_limit(self):
         self.last_sent = dt.datetime.utcnow()
         session(self).flush(self)
+
+    def __json__(self):
+        return {
+            '_id': unicode(self._id),
+            'url': h.absurl(u'/rest' + self.url()),
+            'type': unicode(self.type),
+            'hook_url': unicode(self.hook_url),
+            'mod_date': self.mod_date,
+        }

http://git-wip-us.apache.org/repos/asf/allura/blob/804d86d6/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 330c4bd..d750d9d 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -27,6 +27,7 @@ from nose.tools import (
     assert_not_in,
     assert_in,
 )
+from datadiff import tools as dd
 from formencode import Invalid
 from ming.odm import session
 from pylons import tmpl_context as c
@@ -42,7 +43,11 @@ from allura.webhooks import (
     SendWebhookHelper,
 )
 from allura.tests import decorators as td
-from alluratest.controller import setup_basic_test, TestController
+from alluratest.controller import (
+    setup_basic_test,
+    TestController,
+    TestRestApiBase,
+)
 
 
 # important to be distinct from 'test' and 'test2' which ForgeGit and
@@ -639,3 +644,68 @@ class TestModels(TestWebhookBase):
         self.wh.update_limit()
         session(self.wh).expunge(self.wh)
         assert_equal(M.Webhook.query.get(_id=self.wh._id).last_sent, _now)
+
+    def test_json(self):
+        expected = {
+            '_id': unicode(self.wh._id),
+            'url': u'http://localhost/rest/adobe/adobe-1/admin'
+                   u'/src/webhooks/repo-push/{}'.format(self.wh._id),
+            'type': u'repo-push',
+            'hook_url': u'http://httpbin.org/post',
+            'mod_date': self.wh.mod_date,
+        }
+        dd.assert_equal(self.wh.__json__(), expected)
+
+
+
+class TestWebhookRestController(TestRestApiBase):
+    def setUp(self):
+        super(TestWebhookRestController, self).setUp()
+        self.patches = self.monkey_patch()
+        for p in self.patches:
+            p.start()
+        self.setup_with_tools()
+        self.project = M.Project.query.get(shortname=test_project_with_repo)
+        self.git = self.project.app_instance('src')
+        self.url = str('/rest' + self.git.admin_url + 'webhooks')
+        self.webhooks = []
+        for i in range(3):
+            webhook = M.Webhook(
+                type='repo-push',
+                app_config_id=self.git.config._id,
+                hook_url='http://httpbin.org/post/{}'.format(i),
+                secret='secret-{}'.format(i))
+            session(webhook).flush(webhook)
+            self.webhooks.append(webhook)
+
+    def tearDown(self):
+        super(TestWebhookRestController, self).tearDown()
+        for p in self.patches:
+            p.stop()
+
+    @with_git
+    def setup_with_tools(self):
+        pass
+
+    def monkey_patch(self):
+        gen_secret = patch.object(
+            WebhookController,
+            'gen_secret',
+            return_value='super-secret',
+            autospec=True)
+        # we don't need actual repo here, and this avoids test conflicts when
+        # running in parallel
+        repo_init = patch.object(M.Repository, 'init', autospec=True)
+        return [gen_secret, repo_init]
+
+    def test_webhooks_list(self):
+        r = self.api_get(self.url)
+        webhooks = [{
+            '_id': unicode(wh._id),
+            'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                   '/src/webhooks/repo-push/{}'.format(wh._id),
+            'type': 'repo-push',
+            'hook_url': 'http://httpbin.org/post/{}'.format(n),
+            'mod_date': unicode(wh.mod_date),
+        } for n, wh in enumerate(self.webhooks)]
+        dd.assert_equal(r.json, {'webhooks': webhooks})


[21/26] allura git commit: [#7830] ticket:729 Move actual merge to background task

Posted by je...@apache.org.
[#7830] ticket:729 Move actual merge to background task


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

Branch: refs/heads/ib/7830
Commit: 46767d9ff92ca6c205dcf0d7fadffaca2094cf9e
Parents: 0a9980e
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 19 12:14:52 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Mar 6 09:43:07 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/repository.py         | 11 +--
 Allura/allura/model/repository.py               | 27 ++++---
 Allura/allura/tasks/repo_tasks.py               | 15 ++++
 Allura/allura/templates/repo/merge_request.html | 82 +++++++++++++++++---
 4 files changed, 107 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/46767d9f/Allura/allura/controllers/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index 0869982..2d1295c 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -365,6 +365,7 @@ class MergeRequestController(object):
         return dict(
             downstream_app=downstream_app,
             req=self.req,
+            status=self.req.merge_task_status(),
             page=page,
             limit=limit,
             count=self.req.discussion_thread.post_count)
@@ -446,13 +447,13 @@ class MergeRequestController(object):
         require_access(c.app, 'write')
         if self.req.status != 'open' or not self.req.can_merge():
             raise exc.HTTPNotFound
-        ok = self.req.merge()
-        if ok:
-            flash('Merged successfully', 'ok')
-        else:
-            flash('Merge failed. Please, merge manually', 'error')
+        self.req.merge()
         redirect(self.req.url())
 
+    @expose('json:')
+    def merge_task_status(self):
+        return {'status': self.req.merge_task_status()}
+
 
 class RefsController(object):
 

http://git-wip-us.apache.org/repos/asf/allura/blob/46767d9f/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 5e4a171..d866598 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -23,7 +23,7 @@ import string
 import re
 from subprocess import Popen, PIPE
 from hashlib import sha1
-from datetime import datetime
+from datetime import datetime, timedelta
 from time import time
 from collections import defaultdict, OrderedDict
 from urlparse import urljoin
@@ -818,16 +818,21 @@ class MergeRequest(VersionedArtifact, ActivityObject):
         return result
 
     def merge(self):
-        if not self.app.forkable:
-            return False
-        try:
-            self.app.repo.merge(self)
-        except:
-            log.exception("Can't merge merge request %s", self.url())
-            return False
-        self.status = 'merged'
-        session(self).flush(self)
-        return True
+        in_progress = self.merge_task_status() in ['ready', 'busy']
+        if self.app.forkable and not in_progress:
+            from allura.tasks import repo_tasks
+            repo_tasks.merge.post(self._id)
+
+    def merge_task_status(self):
+        task = MonQTask.query.find({
+            'state': {'$in': ['busy', 'complete', 'error', 'ready']},  # needed to use index
+            'task_name': 'allura.tasks.repo_tasks.merge',
+            'args': [self._id],
+            'time_queue': {'$gt': datetime.utcnow() - timedelta(days=1)}, # constrain on index further
+        }).sort('_id', -1).limit(1).first()
+        if task:
+            return task.state
+        return None
 
 
 # Basic commit information

http://git-wip-us.apache.org/repos/asf/allura/blob/46767d9f/Allura/allura/tasks/repo_tasks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tasks/repo_tasks.py b/Allura/allura/tasks/repo_tasks.py
index 61cac83..e50f9d2 100644
--- a/Allura/allura/tasks/repo_tasks.py
+++ b/Allura/allura/tasks/repo_tasks.py
@@ -20,6 +20,7 @@ import logging
 import traceback
 
 from pylons import tmpl_context as c, app_globals as g
+from ming.odm import session
 
 from allura.lib.decorators import task
 from allura.lib.repository import RepositoryApp
@@ -152,3 +153,17 @@ def tarball(revision, path):
         log.warn(
             'Skipped creation of snapshot: %s:%s because revision is not specified' %
             (c.project.shortname, c.app.config.options.mount_point))
+
+
+@task
+def merge(merge_request_id):
+    from allura import model as M
+    log = logging.getLogger(__name__)
+    mr = M.MergeRequest.query.get(_id=merge_request_id)
+    try:
+        mr.app.repo.merge(mr)
+    except:
+        log.exception("Can't merge merge request %s", mr.url())
+        return
+    mr.status = 'merged'
+    session(mr).flush(mr)

http://git-wip-us.apache.org/repos/asf/allura/blob/46767d9f/Allura/allura/templates/repo/merge_request.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/repo/merge_request.html b/Allura/allura/templates/repo/merge_request.html
index 060b5b9..1520270 100644
--- a/Allura/allura/templates/repo/merge_request.html
+++ b/Allura/allura/templates/repo/merge_request.html
@@ -33,6 +33,19 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
 {% endblock %}
 
 {% block content %}
+  <div class="grid-19">
+    <div id="task_status">
+      {% if status == 'complete' %}
+        <h2 class="complete">Merged</h2>
+      {% else %}
+        <img src="{{g.forge_static('images/spinner.gif')}}" class="spinner" style="display:none"/>
+        <h2 class="busy ready">Merging...</h2>
+        <h2 class="complete">Merged</h2>
+        <h2 class="fail">Something went wrong. Please, merge manually</h2>
+      {% endif %}
+    </div>
+  </div>
+
   {% if req.downstream_repo %}
     <p>
       <a href="{{req.creator_url}}">{{req.creator_name}}</a>
@@ -49,7 +62,7 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
       <div class="grid-19">
         <form action="merge" method="POST">
           {{ lib.csrf_token() }}
-          <input type="submit" value="Merge"{% if not can_merge %}disabled="disabled"{% endif %}>
+          <input type="submit" value="Merge"{% if not can_merge or status in ('ready', 'busy') %}disabled="disabled"{% endif %}>
           {% if can_merge %}
             <div class="merge-ok">
               Merge request has no conflicts. You can merge automatically.
@@ -68,14 +81,16 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
     <div class="grid-19"><a href="#discussion_holder">Discuss</a></div>
 
     {% if h.has_access(c.app, 'write')() %}
-       <div class="grid-19">To merge the commits, please execute the following commands in your working
-         copy: </div>
-       <div class="grid-19"><textarea
-          style="width:80%; height:60px;"
-          readonly
-          >{{ c.app.repo.merge_command(req) | safe }}</textarea></div>
-      {{ c.mr_dispose_form.display(action="save", value=dict(status=req.status)) }}
-       <br style="clear:both">
+      <div class="grid-19">
+        To merge the commits, please execute the following commands in your working copy:
+      </div>
+      <div class="grid-19">
+        <textarea style="width:80%; height:60px;" readonly>{{ c.app.repo.merge_command(req) | safe }}</textarea>
+      </div>
+      {% if status not in ('ready', 'busy') %}
+        {{ c.mr_dispose_form.display(action="save", value=dict(status=req.status)) }}
+        <br style="clear:both">
+      {% endif %}
     {% endif %}
   {% else %}
     <p>
@@ -83,12 +98,10 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
       <a href="{{req.creator_url}}">{{req.creator_name}}</a>
       is deleted
     </p>
-
     <div>{{g.markdown.convert(req.description)}}</div>
-
     {% if h.has_access(c.app, 'write')() %}
       {{ c.mr_dispose_form.display(action="save", value=dict(status=req.status)) }}
-       <br style="clear:both">
+      <br style="clear:both">
     {% endif %}
   {% endif %}
 
@@ -111,5 +124,50 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
 <style type="text/css">
   .merge-ok { color: green; }
   .merge-conflicts { color: red; }
+
+  #task_status { margin: 0 10px; }
+  #task_status h2 { display: none; }
+  #task_status .{{ status }} { display: inline-block; }
+  #task_status h2.complete { color: #C6D880; }
+  #task_status h2.busy, #task_status h2.busy { color: #003565; }
+  #task_status h2.fail { color: #f33; }
 </style>
 {% endblock %}
+
+{% block extra_js %}
+{{ super() }}
+<script type="text/javascript">
+$(function() {
+    {% if status in ('ready', 'busy') %}
+        $('.spinner').show();
+        var delay = 500;
+        function check_status() {
+          $.get("{{request.path.rstrip('/') + '/merge_task_status'}}", function(data) {
+                if (data.status === 'complete') {
+                    $('.spinner').hide();
+                    $('#task_status h2').hide();
+                    $('#task_status h2.complete').show();
+                    location.reload();
+                } else {
+                    if (data.status === 'ready' || data.status === 'busy') {
+                        // keep waiting
+                        $('#task_status h2').hide();
+                        $('#task_status h2.busy').show();
+                    } else {
+                        // something went wrong
+                        $('.spinner').hide();
+                        $('#task_status h2').hide();
+                        $('#task_status h2.fail').show();
+                    }
+                    if (delay < 60000){
+                        delay = delay * 2;
+                    }
+                    window.setTimeout(check_status, delay);
+                }
+            });
+        }
+        window.setTimeout(check_status, delay);
+    {% endif %}
+});
+</script>
+{% endblock %}


[05/26] allura git commit: [#7832] ticket:731 Test for permissions

Posted by je...@apache.org.
[#7832] ticket:731 Test for permissions


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

Branch: refs/heads/ib/7830
Commit: a747991c01a63399d28b10dd579559147022cfb5
Parents: f392d5b
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 23 10:34:35 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:52 2015 +0000

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


http://git-wip-us.apache.org/repos/asf/allura/blob/a747991c/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 50a9803..a336f32 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -894,3 +894,17 @@ class TestWebhookRestController(TestRestApiBase):
         dd.assert_equal(r.json, {u'result': u'ok'})
         assert_equal(M.Webhook.query.find().count(), 2)
         assert_equal(M.Webhook.query.get(_id=webhook._id), None)
+
+    def test_permissions(self):
+        self.api_get(self.url, user='test-user', status=403)
+        self.api_get(self.url, user='*anonymous', status=401)
+        url = self.url + '/repo-push/'
+        self.api_post(url, user='test-user', status=403)
+        self.api_post(url, user='*anonymous', status=401)
+        url = self.url + '/repo-push/' + str(self.webhooks[0]._id)
+        self.api_get(url, user='test-user', status=403)
+        self.api_get(url, user='*anonymous', status=401)
+        self.api_post(url, user='test-user', status=403)
+        self.api_post(url, user='*anonymous', status=401)
+        self.api_delete(url, user='test-user', status=403)
+        self.api_delete(url, user='*anonymous', status=401)


[20/26] allura git commit: [#7830] ticket:729 One-click merge for git

Posted by je...@apache.org.
[#7830] ticket:729 One-click merge for git


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

Branch: refs/heads/ib/7830
Commit: 0a9980eb872466cfe40b65b514ccf858cc11ca96
Parents: 0126faa
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Feb 18 17:00:27 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Mar 6 09:43:06 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/repository.py         | 13 ++++++++
 Allura/allura/model/repository.py               | 24 +++++++++++++++
 Allura/allura/templates/repo/merge_request.html | 26 ++++++++++++++++
 ForgeGit/forgegit/model/git_repo.py             | 32 ++++++++++++++++++++
 4 files changed, 95 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/0a9980eb/Allura/allura/controllers/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index 1e1e840..0869982 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -440,6 +440,19 @@ class MergeRequestController(object):
             self.req.status = status
         redirect('.')
 
+    @expose()
+    @require_post()
+    def merge(self):
+        require_access(c.app, 'write')
+        if self.req.status != 'open' or not self.req.can_merge():
+            raise exc.HTTPNotFound
+        ok = self.req.merge()
+        if ok:
+            flash('Merged successfully', 'ok')
+        else:
+            flash('Merge failed. Please, merge manually', 'error')
+        redirect(self.req.url())
+
 
 class RefsController(object):
 

http://git-wip-us.apache.org/repos/asf/allura/blob/0a9980eb/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 0030aa5..5e4a171 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -805,6 +805,30 @@ class MergeRequest(VersionedArtifact, ActivityObject):
                 self.request_number, self.project.name, self.app.repo.name))
         return result
 
+    def can_merge(self):
+        if not self.app.forkable:
+            return False
+        try:
+            result = self.app.repo.can_merge(self)
+        except:
+            log.exception(
+                "Can't determine if merge request %s can be merged",
+                self.url())
+            return False
+        return result
+
+    def merge(self):
+        if not self.app.forkable:
+            return False
+        try:
+            self.app.repo.merge(self)
+        except:
+            log.exception("Can't merge merge request %s", self.url())
+            return False
+        self.status = 'merged'
+        session(self).flush(self)
+        return True
+
 
 # Basic commit information
 # One of these for each commit in the physical repo on disk. The _id is the

http://git-wip-us.apache.org/repos/asf/allura/blob/0a9980eb/Allura/allura/templates/repo/merge_request.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/repo/merge_request.html b/Allura/allura/templates/repo/merge_request.html
index 3c79654..060b5b9 100644
--- a/Allura/allura/templates/repo/merge_request.html
+++ b/Allura/allura/templates/repo/merge_request.html
@@ -44,6 +44,25 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
 
     <div>{{g.markdown.convert(req.description)}}</div>
 
+    {% if req.status == 'open' and c.app.forkable and h.has_access(c.app, 'write')() %}
+      {% set can_merge = req.can_merge() %}
+      <div class="grid-19">
+        <form action="merge" method="POST">
+          {{ lib.csrf_token() }}
+          <input type="submit" value="Merge"{% if not can_merge %}disabled="disabled"{% endif %}>
+          {% if can_merge %}
+            <div class="merge-ok">
+              Merge request has no conflicts. You can merge automatically.
+            </div>
+          {% else %}
+            <div class="merge-conflicts">
+              Merge request has conflicts. Follow manual instructions below to merge.
+            </div>
+          {% endif %}
+        </form>
+      </div>
+    {% endif %}
+
     {{ c.log_widget.display(value=req.commits, app=downstream_app) }}
 
     <div class="grid-19"><a href="#discussion_holder">Discuss</a></div>
@@ -87,3 +106,10 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
         count=count)}}
   </div>
 {% endblock %}
+
+{% block extra_css %}
+<style type="text/css">
+  .merge-ok { color: green; }
+  .merge-conflicts { color: red; }
+</style>
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/allura/blob/0a9980eb/ForgeGit/forgegit/model/git_repo.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/model/git_repo.py b/ForgeGit/forgegit/model/git_repo.py
index 7fda67a..831da5b 100644
--- a/ForgeGit/forgegit/model/git_repo.py
+++ b/ForgeGit/forgegit/model/git_repo.py
@@ -19,6 +19,7 @@ import os
 import shutil
 import string
 import logging
+import tempfile
 from datetime import datetime
 
 import tg
@@ -95,6 +96,37 @@ class Repository(M.Repository):
             merge_request.downstream.commit_id,
         )
 
+    def can_merge(self, mr):
+        """
+        Given merge request `mr` determine if it can be merged w/o conflicts.
+        """
+        g = self._impl._git.git
+        # http://stackoverflow.com/a/6283843
+        # fetch source branch
+        g.fetch(mr.downstream_repo_url, mr.source_branch)
+        # find merge base
+        merge_base = g.merge_base(mr.downstream.commit_id, mr.target_branch)
+        # print out merge result, but don't actually touch anything
+        merge_tree = g.merge_tree(
+            merge_base, mr.target_branch, mr.downstream.commit_id)
+        return '+<<<<<<<' not in merge_tree
+
+    def merge(self, mr):
+        g = self._impl._git.git
+        # can't merge in bare repo, so need to clone
+        tmp_path = tempfile.mkdtemp()
+        tmp_repo = git.Repo.clone_from(
+            self.clone_url('rw'),
+            to_path=tmp_path,
+            bare=False)
+        tmp_repo = GitImplementation(Object(full_fs_path=tmp_path))._git
+        tmp_repo.git.fetch('origin', mr.target_branch)
+        tmp_repo.git.checkout(mr.target_branch)
+        tmp_repo.git.fetch(mr.downstream_repo_url, mr.source_branch)
+        tmp_repo.git.merge(mr.downstream.commit_id)
+        tmp_repo.git.push('origin', mr.target_branch)
+        shutil.rmtree(tmp_path, ignore_errors=True)
+
     def rev_to_commit_id(self, rev):
         return self._impl.rev_parse(rev).hexsha
 


[15/26] allura git commit: [#7827] remove jquery.tools.min.js references since it is deleted

Posted by je...@apache.org.
[#7827] remove jquery.tools.min.js references since it is deleted


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

Branch: refs/heads/ib/7830
Commit: 132cf7e66f7ad8b2a11498e53f45ec7e553c31fb
Parents: 862c185
Author: Dave Brondsema <da...@brondsema.net>
Authored: Mon Mar 2 12:39:05 2015 -0500
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Mon Mar 2 12:39:05 2015 -0500

----------------------------------------------------------------------
 Allura/LICENSE   | 1 -
 LICENSE          | 1 -
 rat-excludes.txt | 1 -
 3 files changed, 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/132cf7e6/Allura/LICENSE
----------------------------------------------------------------------
diff --git a/Allura/LICENSE b/Allura/LICENSE
index c260bee..d2b93e1 100644
--- a/Allura/LICENSE
+++ b/Allura/LICENSE
@@ -229,7 +229,6 @@ under the MIT license.  For details, see the individual files:
     allura/lib/widgets/resources/js/jquery.colorPicker.js
     allura/lib/widgets/resources/js/jquery.tagsinput.js
     allura/lib/widgets/resources/js/jquery.textarea.js
-    allura/lib/widgets/resources/js/jquery.tools.min.js
     allura/public/nf/js/jquery.flot.js
     allura/public/nf/js/jquery.maxlength.min.js
     allura/public/nf/js/jquery.viewport.js

http://git-wip-us.apache.org/repos/asf/allura/blob/132cf7e6/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index c40175b..8ec8955 100644
--- a/LICENSE
+++ b/LICENSE
@@ -230,7 +230,6 @@ under the MIT license.  For details, see the individual files:
     Allura/allura/lib/widgets/resources/js/jquery.colorPicker.js
     Allura/allura/lib/widgets/resources/js/jquery.tagsinput.js
     Allura/allura/lib/widgets/resources/js/jquery.textarea.js
-    Allura/allura/lib/widgets/resources/js/jquery.tools.min.js
     Allura/allura/public/nf/js/jquery.flot.js
     Allura/allura/public/nf/js/jquery.maxlength.min.js
     allura/public/nf/js/jquery.viewport.js

http://git-wip-us.apache.org/repos/asf/allura/blob/132cf7e6/rat-excludes.txt
----------------------------------------------------------------------
diff --git a/rat-excludes.txt b/rat-excludes.txt
index f69384f..2d62d50 100644
--- a/rat-excludes.txt
+++ b/rat-excludes.txt
@@ -23,7 +23,6 @@ Allura/allura/lib/widgets/resources/js/jquery.autosize-min.js
 Allura/allura/lib/widgets/resources/js/jquery.colorPicker.js
 Allura/allura/lib/widgets/resources/js/jquery.tagsinput.js
 Allura/allura/lib/widgets/resources/js/jquery.textarea.js
-Allura/allura/lib/widgets/resources/js/jquery.tools.min.js
 Allura/allura/public/nf/js/jquery.flot.js
 Allura/allura/public/nf/js/jquery.maxlength.min.js
 Allura/allura/public/nf/js/jquery.tablesorter.js


[13/26] allura git commit: [#7832] ticket:740 Remove debug logging

Posted by je...@apache.org.
[#7832] ticket:740 Remove debug logging


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

Branch: refs/heads/ib/7830
Commit: aa6daf7878c22e034a7ba188338eda08e3ed9ccc
Parents: f6074f2
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Feb 25 13:57:16 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:53 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/rest.py | 3 ---
 1 file changed, 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/aa6daf78/Allura/allura/controllers/rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index 2c1a042..ab21649 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -50,8 +50,6 @@ class RestController(object):
         headers_auth = 'Authorization' in request.headers
         params_auth = 'oauth_token' in request.params
         params_auth = params_auth or 'access_token' in request.params
-        log.error(headers_auth)
-        log.error(request.headers)
         if headers_auth or params_auth:
             return self.oauth._authenticate()
         else:
@@ -111,7 +109,6 @@ class OAuthNegotiator(object):
     def _authenticate(self):
         bearer_token_prefix = 'OAuth BearerToken access_token='
         auth = request.headers.get('Authorization')
-        log.error(auth)
         if auth and auth.startswith(bearer_token_prefix):
             access_token = auth[len(bearer_token_prefix):]
         else:


[03/26] allura git commit: [#7832] ticket:731 Add limits to list API

Posted by je...@apache.org.
[#7832] ticket:731 Add limits to list API


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

Branch: refs/heads/ib/7830
Commit: 37384e9966c8528249f12fa00a895c40cff28900
Parents: 804d86d
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 11:58:19 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:51 2015 +0000

----------------------------------------------------------------------
 Allura/allura/lib/repository.py      | 13 ++++++++++++-
 Allura/allura/model/webhook.py       |  7 +++++++
 Allura/allura/tests/test_webhooks.py |  6 +++++-
 Allura/allura/webhooks.py            |  5 ++---
 4 files changed, 26 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/37384e99/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 41e671d..07b1b06 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -22,6 +22,7 @@ from pylons import tmpl_context as c, app_globals as g
 from pylons import request
 from tg import expose, redirect, flash, validate
 from tg.decorators import with_trailing_slash, without_trailing_slash
+from webob import exc
 from bson import ObjectId
 
 from ming.utils import LazyProperty
@@ -297,4 +298,14 @@ class RestWebhooksLookup(BaseController):
             'type': {'$in': [wh.type for wh in webhooks]},
             'app_config_id': self.app.config._id}
         ).sort('_id', 1).all()
-        return {'webhooks': [hook.__json__() for hook in configured_hooks]}
+        limits = {
+            wh.type: {
+                'max': M.Webhook.max_hooks(wh.type, self.app.config.tool_name),
+                'used': M.Webhook.query.find({
+                    'type': wh.type,
+                    'app_config_id': self.app.config._id,
+                }).count(),
+            } for wh in webhooks
+        }
+        return {'webhooks': [hook.__json__() for hook in configured_hooks],
+                'limits': limits}

http://git-wip-us.apache.org/repos/asf/allura/blob/37384e99/Allura/allura/model/webhook.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
index 0d5140e..bb36e5f 100644
--- a/Allura/allura/model/webhook.py
+++ b/Allura/allura/model/webhook.py
@@ -16,6 +16,7 @@
 #       under the License.
 
 import datetime as dt
+import json
 
 from ming.odm import FieldProperty, session
 from paste.deploy.converters import asint
@@ -55,6 +56,12 @@ class Webhook(Artifact):
         self.last_sent = dt.datetime.utcnow()
         session(self).flush(self)
 
+    @classmethod
+    def max_hooks(self, type, tool_name):
+        type = type.replace('-', '_')
+        limits = json.loads(config.get('webhook.%s.max_hooks' % type, '{}'))
+        return limits.get(tool_name.lower(), 3)
+
     def __json__(self):
         return {
             '_id': unicode(self._id),

http://git-wip-us.apache.org/repos/asf/allura/blob/37384e99/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index d750d9d..c758df1 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -708,4 +708,8 @@ class TestWebhookRestController(TestRestApiBase):
             'hook_url': 'http://httpbin.org/post/{}'.format(n),
             'mod_date': unicode(wh.mod_date),
         } for n, wh in enumerate(self.webhooks)]
-        dd.assert_equal(r.json, {'webhooks': webhooks})
+        expected = {
+            'webhooks': webhooks,
+            'limits': {'repo-push': {'max': 3, 'used': 3}},
+        }
+        dd.assert_equal(r.json, expected)

http://git-wip-us.apache.org/repos/asf/allura/blob/37384e99/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index fb37820..2fa411f 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -313,13 +313,12 @@ class WebhookSender(object):
         Checks if limit of webhooks created for given project/app is reached.
         Returns False if limit is reached, True otherwise.
         '''
-        _type = self.type.replace('-', '_')
-        limits = json.loads(config.get('webhook.%s.max_hooks' % _type, '{}'))
         count = M.Webhook.query.find(dict(
             app_config_id=app.config._id,
             type=self.type,
         )).count()
-        return count < limits.get(app.config.tool_name.lower(), 3)
+        limit = M.Webhook.max_hooks(self.type, app.config.tool_name)
+        return count < limit
 
 
 class RepoPushWebhookSender(WebhookSender):


[19/26] allura git commit: Remove a few sourceforge.net references

Posted by je...@apache.org.
Remove a few sourceforge.net references


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

Branch: refs/heads/ib/7830
Commit: 0126faa1d11332b7ff0c510e9b3e42f88479cb7c
Parents: ae5d274
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Mar 3 16:19:54 2015 -0500
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Tue Mar 3 16:19:54 2015 -0500

----------------------------------------------------------------------
 Allura/docs/development/contributing.rst | 2 +-
 Allura/docs/platform/platform_tour.rst   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/0126faa1/Allura/docs/development/contributing.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/development/contributing.rst b/Allura/docs/development/contributing.rst
index e96e5be..e22ceb1 100644
--- a/Allura/docs/development/contributing.rst
+++ b/Allura/docs/development/contributing.rst
@@ -104,7 +104,7 @@ Finding Something to Work On
 ----------------------------
 Tickets that are relatively simple and good for new contributors have a
 "bitesize" label, and can be found here:
-https://sourceforge.net/p/allura/tickets/search/?q=labels%3Abitesize
+https://forge-allura.apache.org/p/allura/tickets/search/?q=labels%3Abitesize+AND+status%3Aopen
 
 Find one that looks good, and leave a comment on the ticket or mailing list
 to let us know you're working on it. If you get stuck, remember that we're

http://git-wip-us.apache.org/repos/asf/allura/blob/0126faa1/Allura/docs/platform/platform_tour.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform/platform_tour.rst b/Allura/docs/platform/platform_tour.rst
index c3ca064..6d43ccd 100644
--- a/Allura/docs/platform/platform_tour.rst
+++ b/Allura/docs/platform/platform_tour.rst
@@ -201,7 +201,7 @@ Email Integration
 
 The Allura platform provides easy-to-use email integration.  Forge email addresses
 are of the form
-<topic>@<mount_point>[.<subproject>]*.<subproject>.projects.sourceforge.net.
+:samp:`<topic>@<mount_point>[.<subproject>].<project>.mysite.com`.
 When a message is received on such an email address, the address is parsed and
 the sending user is identified (if possible).  Based on the parsed address, the
 pylons context attributes `c.project` and `c.app` are set, and the application is


[14/26] allura git commit: [#7840] ticket:740 Format auth header according to spec

Posted by je...@apache.org.
[#7840] ticket:740 Format auth header according to spec


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

Branch: refs/heads/ib/7830
Commit: 862c18502642b099a586d0add28e35ddfe9a2eb4
Parents: 7a934fa
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Feb 25 14:21:58 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:53 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/rest.py           | 2 +-
 Allura/allura/tests/functional/test_rest.py | 6 +++---
 AlluraTest/alluratest/controller.py         | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/862c1850/Allura/allura/controllers/rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index ab21649..8eafa30 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -107,7 +107,7 @@ class OAuthNegotiator(object):
         return result
 
     def _authenticate(self):
-        bearer_token_prefix = 'OAuth BearerToken access_token='
+        bearer_token_prefix = 'Bearer '
         auth = request.headers.get('Authorization')
         if auth and auth.startswith(bearer_token_prefix):
             access_token = auth[len(bearer_token_prefix):]

http://git-wip-us.apache.org/repos/asf/allura/blob/862c1850/Allura/allura/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_rest.py b/Allura/allura/tests/functional/test_rest.py
index fca1078..43a92d1 100644
--- a/Allura/allura/tests/functional/test_rest.py
+++ b/Allura/allura/tests/functional/test_rest.py
@@ -92,7 +92,7 @@ class TestRestHome(TestRestApiBase):
     @mock.patch('allura.controllers.rest.request')
     def test_bearer_token_non_bearer_via_headers(self, request, OAuthAccessToken):
         request.headers = {
-            'Authorization': 'OAuth BearerToken access_token=foo'
+            'Authorization': 'Bearer foo'
         }
         request.scheme = 'https'
         self._patch_token(OAuthAccessToken)
@@ -106,7 +106,7 @@ class TestRestHome(TestRestApiBase):
     @mock.patch('allura.controllers.rest.request')
     def test_bearer_token_invalid_via_headers(self, request, OAuthAccessToken):
         request.headers = {
-            'Authorization': 'OAuth BearerToken access_token=foo'
+            'Authorization': 'Bearer foo'
         }
         request.scheme = 'https'
         self._patch_token(OAuthAccessToken)
@@ -138,7 +138,7 @@ class TestRestHome(TestRestApiBase):
         ThreadLocalODMSession.flush_all()
         token = access_token.api_key
         request.headers = {
-            'Authorization': 'OAuth BearerToken access_token={}'.format(token)
+            'Authorization': 'Bearer {}'.format(token)
         }
         request.scheme = 'https'
         r = self.api_post('/rest/p/test/wiki', access_token='foo')

http://git-wip-us.apache.org/repos/asf/allura/blob/862c1850/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index c64ea5f..0f13c5d 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -224,7 +224,7 @@ class TestRestApiBase(TestController):
 
         token = self.token(user).api_key
         headers = {
-            'Authorization': 'OAuth BearerToken access_token={}'.format(token)
+            'Authorization': 'Bearer {}'.format(token)
         }
 
         fn = getattr(self.app, method.lower())


[07/26] allura git commit: [#7832] ticket:731 Endpoint for creating webhook

Posted by je...@apache.org.
[#7832] ticket:731 Endpoint for creating webhook


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

Branch: refs/heads/ib/7830
Commit: 9e16b0b2f7ff1c2ff112c264e4126b1696bf2b58
Parents: 41c054c
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 13:52:38 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:52 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_webhooks.py | 65 ++++++++++++++++++++++++++++++-
 Allura/allura/webhooks.py            | 61 +++++++++++++++++++++++++++++
 2 files changed, 125 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/9e16b0b2/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 5bbee21..237de87 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -657,7 +657,6 @@ class TestModels(TestWebhookBase):
         dd.assert_equal(self.wh.__json__(), expected)
 
 
-
 class TestWebhookRestController(TestRestApiBase):
     def setUp(self):
         super(TestWebhookRestController, self).setUp()
@@ -730,3 +729,67 @@ class TestWebhookRestController(TestRestApiBase):
             'mod_date': unicode(webhook.mod_date),
         }
         dd.assert_equal(r.json, expected)
+
+    def test_create_validation(self):
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+        r = self.api_get(self.url + '/repo-push', {}, status=405)
+
+        r = self.api_post(self.url + '/repo-push', {}, status=400)
+        expected = {
+            u'result': u'error',
+            u'error': {u'url': u'Please enter a value'},
+        }
+        assert_equal(r.json, expected)
+
+        data = {'url': 'qwer', 'secret': 'qwe'}
+        r = self.app.post(self.url + '/repo-push', data, status=400)
+        expected = {
+            u'result': u'error',
+            u'error': {
+                u'url': u'You must provide a full domain name (like qwer.com)'
+            },
+        }
+        assert_equal(r.json, expected)
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+
+    def test_create(self):
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+        data = {u'url': u'http://hook.slack.com/abcd'}
+        limit = json.dumps({'git': 10})
+        with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
+            r = self.app.post(self.url + '/repo-push', data, status=201)
+        webhook = M.Webhook.query.get(hook_url=data['url'])
+        assert_equal(webhook.secret, 'super-secret')  # secret generated
+        assert_equal(r.json['result'], 'ok')
+        assert_equal(r.json['webhook']['_id'], unicode(webhook._id))
+        assert_equal(r.json['webhook']['type'], 'repo-push')
+        assert_equal(r.json['webhook']['hook_url'], data['url'])
+        assert_equal(
+            r.json['webhook']['url'],
+            u'http://localhost/rest/adobe/adobe-1/admin'
+            u'/src/webhooks/repo-push/{}'.format(webhook._id))
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks) + 1)
+
+    def test_create_duplicates(self):
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+        data = {u'url': self.webhooks[0].hook_url}
+        limit = json.dumps({'git': 10})
+        with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
+            r = self.app.post(self.url + '/repo-push', data, status=400)
+        expected = {u'result': u'error',
+                    u'error': u'_the_form: "repo-push" webhook already '
+                              u'exists for Git http://httpbin.org/post/0'}
+        assert_equal(r.json, expected)
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+
+    def test_create_limit_reached(self):
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+        data = {u'url': u'http://hook.slack.com/abcd'}
+        r = self.app.post(self.url + '/repo-push', data, status=400)
+        expected = {
+            u'result': u'error',
+            u'limits': {u'max': 3, u'used': 3},
+            u'error': u'You have exceeded the maximum number of webhooks '
+                      u'you are allowed to create for this project/app'}
+        assert_equal(r.json, expected)
+        assert_equal(M.Webhook.query.find().count(), len(self.webhooks))

http://git-wip-us.apache.org/repos/asf/allura/blob/9e16b0b2/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index dffaa21..fe41342 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -28,6 +28,7 @@ from bson import ObjectId
 from tg import expose, validate, redirect, flash, config
 from tg.decorators import with_trailing_slash, without_trailing_slash
 from pylons import tmpl_context as c
+from pylons import response
 from formencode import validators as fev, schema, Invalid
 from ming.odm import session
 from webob import exc
@@ -199,6 +200,66 @@ class WebhookRestController(BaseController):
         self.create_form = WebhookController.create_form
         self.edit_form = WebhookController.edit_form
 
+    def _error(self, e):
+        error = getattr(e, 'error_dict', None)
+        if error:
+            _error = {}
+            for k, v in error.iteritems():
+                _error[k] = unicode(v)
+            return _error
+        error = getattr(e, 'msg', None)
+        if not error:
+            error = getattr(e, 'message', '')
+        return error
+
+    def update_webhook(self, wh, url, secret=None):
+        controller = WebhookController(self.sender.__class__, self.app)
+        controller.update_webhook(wh, url, secret)
+
+    @expose('json:')
+    @require_post()
+    def index(self, **kw):
+        response.content_type = 'application/json'
+        try:
+            params = {'secret': kw.pop('secret', ''),
+                      'url': kw.pop('url', None)}
+            valid = self.create_form().to_python(params)
+        except Exception as e:
+            response.status_int = 400
+            return {'result': 'error', 'error': self._error(e)}
+        if self.sender.enforce_limit(self.app):
+            webhook = M.Webhook(
+                type=self.sender.type,
+                app_config_id=self.app.config._id)
+            try:
+                self.update_webhook(webhook, valid['url'], valid['secret'])
+            except Invalid as e:
+                response.status_int = 400
+                return {'result': 'error', 'error': self._error(e)}
+            M.AuditLog.log('add webhook %s %s %s',
+                           webhook.type, webhook.hook_url,
+                           webhook.app_config.url())
+            response.status_int = 201
+            return {'result': 'ok', 'webhook': webhook.__json__()}
+        else:
+            limits = {
+                'max': M.Webhook.max_hooks(
+                    self.sender.type,
+                    self.app.config.tool_name),
+                'used': M.Webhook.query.find({
+                    'type': self.sender.type,
+                    'app_config_id': self.app.config._id,
+                }).count(),
+            }
+            resp = {
+                'result': 'error',
+                'error': 'You have exceeded the maximum number of webhooks '
+                         'you are allowed to create for this project/app',
+                'limits': limits,
+            }
+            response.status_int = 400
+            return resp
+
     @expose('json:')
     def _default(self, webhook, **kw):
         form = self.edit_form(self.sender, self.app)


[26/26] allura git commit: [#7830] ticket:744 Change author and message for merge commit

Posted by je...@apache.org.
[#7830] ticket:744 Change author and message for merge commit


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

Branch: refs/heads/ib/7830
Commit: ad139b7b75883cd29be61905f5f886707b9556b0
Parents: 3f2acda
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Mar 6 11:24:08 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Mar 6 11:24:08 2015 +0000

----------------------------------------------------------------------
 ForgeGit/forgegit/model/git_repo.py              | 10 +++++++++-
 ForgeGit/forgegit/tests/model/test_repository.py | 13 +++++++++++--
 2 files changed, 20 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/ad139b7b/ForgeGit/forgegit/model/git_repo.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/model/git_repo.py b/ForgeGit/forgegit/model/git_repo.py
index 62e0604..8841f52 100644
--- a/ForgeGit/forgegit/model/git_repo.py
+++ b/ForgeGit/forgegit/model/git_repo.py
@@ -123,7 +123,15 @@ class Repository(M.Repository):
         tmp_repo.git.fetch('origin', mr.target_branch)
         tmp_repo.git.checkout(mr.target_branch)
         tmp_repo.git.fetch(mr.downstream_repo.full_fs_path, mr.source_branch)
-        tmp_repo.git.merge(mr.downstream.commit_id)
+        author = h.really_unicode(c.user.display_name or c.user.username)
+        tmp_repo.git.config('user.name', author)
+        tmp_repo.git.config('user.email', '')
+        msg = u'Merge {} branch {} into {}\n\n{}'.format(
+            mr.downstream_repo.url(),
+            mr.source_branch,
+            mr.target_branch,
+            h.absurl(mr.url()))
+        tmp_repo.git.merge(mr.downstream.commit_id, '-m', msg)
         tmp_repo.git.push('origin', mr.target_branch)
         shutil.rmtree(tmp_path, ignore_errors=True)
 

http://git-wip-us.apache.org/repos/asf/allura/blob/ad139b7b/ForgeGit/forgegit/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index 325a6ea..a9de6ba 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -615,9 +615,12 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
     @mock.patch('forgegit.model.git_repo.GitImplementation', autospec=True)
     @mock.patch('forgegit.model.git_repo.shutil', autospec=True)
     def test_merge(self, shutil, GitImplementation, git, tempfile):
-        mr = mock.Mock(downstream_repo=Object(full_fs_path='downstream-url'),
+        mr = mock.Mock(downstream_repo=mock.Mock(
+                           full_fs_path='downstream-url',
+                           url=lambda: 'downstream-repo-url'),
                        source_branch='source-branch',
                        target_branch='target-branch',
+                       url=lambda: '/merge-request/1/',
                        downstream=mock.Mock(commit_id='cid'))
         _git = mock.Mock()
         self.repo._impl._git.git = _git
@@ -632,7 +635,13 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
             [mock.call('origin', 'target-branch'),
              mock.call('downstream-url', 'source-branch')])
         tmp_repo.git.checkout.assert_called_once_with('target-branch')
-        tmp_repo.git.merge.assert_called_once_with('cid')
+        assert_equal(
+            tmp_repo.git.config.call_args_list,
+            [mock.call('user.name', 'Test Admin'),
+             mock.call('user.email', '')])
+        msg = u'Merge downstream-repo-url branch source-branch into target-branch'
+        msg += u'\n\nhttp://localhost/merge-request/1/'
+        tmp_repo.git.merge.assert_called_once_with('cid', '-m', msg)
         tmp_repo.git.push.assert_called_once_with('origin', 'target-branch')
         shutil.rmtree.assert_called_once_with(
             tempfile.mkdtemp.return_value,


[09/26] allura git commit: [#7832] ticket:731 Endpoint for editing webhooks

Posted by je...@apache.org.
[#7832] ticket:731 Endpoint for editing webhooks


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

Branch: refs/heads/ib/7830
Commit: 0979146633b40f75329695b1e2b27e4721ea20cd
Parents: 9e16b0b
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 16:27:06 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:52 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_webhooks.py | 125 +++++++++++++++++++++++++-----
 Allura/allura/webhooks.py            |  38 ++++++++-
 2 files changed, 140 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/09791466/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 237de87..ee9cd0b 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -721,20 +721,23 @@ class TestWebhookRestController(TestRestApiBase):
         webhook = self.webhooks[0]
         r = self.api_get('{}/repo-push/{}'.format(self.url, webhook._id))
         expected = {
-            '_id': unicode(webhook._id),
-            'url': 'http://localhost/rest/adobe/adobe-1/admin'
-                   '/src/webhooks/repo-push/{}'.format(webhook._id),
-            'type': 'repo-push',
-            'hook_url': 'http://httpbin.org/post/0',
-            'mod_date': unicode(webhook.mod_date),
+            u'result': u'ok',
+            u'webhook': {
+                '_id': unicode(webhook._id),
+                'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                       '/src/webhooks/repo-push/{}'.format(webhook._id),
+                'type': 'repo-push',
+                'hook_url': 'http://httpbin.org/post/0',
+                'mod_date': unicode(webhook.mod_date),
+            },
         }
         dd.assert_equal(r.json, expected)
 
     def test_create_validation(self):
         assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
-        r = self.api_get(self.url + '/repo-push', {}, status=405)
+        r = self.api_get(self.url + '/repo-push', status=405)
 
-        r = self.api_post(self.url + '/repo-push', {}, status=400)
+        r = self.api_post(self.url + '/repo-push', status=400)
         expected = {
             u'result': u'error',
             u'error': {u'url': u'Please enter a value'},
@@ -742,7 +745,7 @@ class TestWebhookRestController(TestRestApiBase):
         assert_equal(r.json, expected)
 
         data = {'url': 'qwer', 'secret': 'qwe'}
-        r = self.app.post(self.url + '/repo-push', data, status=400)
+        r = self.api_post(self.url + '/repo-push', status=400, **data)
         expected = {
             u'result': u'error',
             u'error': {
@@ -757,17 +760,24 @@ class TestWebhookRestController(TestRestApiBase):
         data = {u'url': u'http://hook.slack.com/abcd'}
         limit = json.dumps({'git': 10})
         with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
-            r = self.app.post(self.url + '/repo-push', data, status=201)
+            msg = 'add webhook repo-push {} {}'.format(
+                data['url'], self.git.config.url())
+            with td.audits(msg):
+                r = self.api_post(self.url + '/repo-push', status=201, **data)
         webhook = M.Webhook.query.get(hook_url=data['url'])
         assert_equal(webhook.secret, 'super-secret')  # secret generated
-        assert_equal(r.json['result'], 'ok')
-        assert_equal(r.json['webhook']['_id'], unicode(webhook._id))
-        assert_equal(r.json['webhook']['type'], 'repo-push')
-        assert_equal(r.json['webhook']['hook_url'], data['url'])
-        assert_equal(
-            r.json['webhook']['url'],
-            u'http://localhost/rest/adobe/adobe-1/admin'
-            u'/src/webhooks/repo-push/{}'.format(webhook._id))
+        expected = {
+            u'result': u'ok',
+            u'webhook': {
+                '_id': unicode(webhook._id),
+                'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                       '/src/webhooks/repo-push/{}'.format(webhook._id),
+                'type': 'repo-push',
+                'hook_url': data['url'],
+                'mod_date': unicode(webhook.mod_date),
+            },
+        }
+        dd.assert_equal(r.json, expected)
         assert_equal(M.Webhook.query.find().count(), len(self.webhooks) + 1)
 
     def test_create_duplicates(self):
@@ -775,7 +785,7 @@ class TestWebhookRestController(TestRestApiBase):
         data = {u'url': self.webhooks[0].hook_url}
         limit = json.dumps({'git': 10})
         with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
-            r = self.app.post(self.url + '/repo-push', data, status=400)
+            r = self.api_post(self.url + '/repo-push', status=400, **data)
         expected = {u'result': u'error',
                     u'error': u'_the_form: "repo-push" webhook already '
                               u'exists for Git http://httpbin.org/post/0'}
@@ -785,7 +795,7 @@ class TestWebhookRestController(TestRestApiBase):
     def test_create_limit_reached(self):
         assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
         data = {u'url': u'http://hook.slack.com/abcd'}
-        r = self.app.post(self.url + '/repo-push', data, status=400)
+        r = self.api_post(self.url + '/repo-push', status=400, **data)
         expected = {
             u'result': u'error',
             u'limits': {u'max': 3, u'used': 3},
@@ -793,3 +803,78 @@ class TestWebhookRestController(TestRestApiBase):
                       u'you are allowed to create for this project/app'}
         assert_equal(r.json, expected)
         assert_equal(M.Webhook.query.find().count(), len(self.webhooks))
+
+    def test_edit_validation(self):
+        webhook = self.webhooks[0]
+        url = u'{}/repo-push/{}'.format(self.url, webhook._id)
+        data = {'url': 'qwe', 'secret': 'qwe'}
+        r = self.api_post(url, status=400, **data)
+        expected = {
+            u'result': u'error',
+            u'error': {
+                u'url': u'You must provide a full domain name (like qwe.com)'
+            },
+        }
+        assert_equal(r.json, expected)
+
+    def test_edit(self):
+        webhook = self.webhooks[0]
+        url = '{}/repo-push/{}'.format(self.url, webhook._id)
+        # change only url
+        data = {'url': 'http://hook.slack.com/abcd'}
+        msg = ('edit webhook repo-push\n'
+               'http://httpbin.org/post/0 => http://hook.slack.com/abcd\n')
+        with td.audits(msg):
+            r = self.api_post(url, status=200, **data)
+        webhook = M.Webhook.query.get(_id=webhook._id)
+        assert_equal(webhook.hook_url, data['url'])
+        assert_equal(webhook.secret, 'secret-0')
+        expected = {
+            u'result': u'ok',
+            u'webhook': {
+                '_id': unicode(webhook._id),
+                'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                       '/src/webhooks/repo-push/{}'.format(webhook._id),
+                'type': 'repo-push',
+                'hook_url': data['url'],
+                'mod_date': unicode(webhook.mod_date),
+            },
+        }
+        dd.assert_equal(r.json, expected)
+
+        # change only secret
+        data = {'secret': 'new-secret'}
+        msg = ('edit webhook repo-push\n'
+               'http://hook.slack.com/abcd => http://hook.slack.com/abcd\n'
+               'secret changed')
+        with td.audits(msg):
+            r = self.api_post(url, status=200, **data)
+        webhook = M.Webhook.query.get(_id=webhook._id)
+        assert_equal(webhook.hook_url, 'http://hook.slack.com/abcd')
+        assert_equal(webhook.secret, 'new-secret')
+        expected = {
+            u'result': u'ok',
+            u'webhook': {
+                '_id': unicode(webhook._id),
+                'url': 'http://localhost/rest/adobe/adobe-1/admin'
+                       '/src/webhooks/repo-push/{}'.format(webhook._id),
+                'type': 'repo-push',
+                'hook_url': 'http://hook.slack.com/abcd',
+                'mod_date': unicode(webhook.mod_date),
+            },
+        }
+        dd.assert_equal(r.json, expected)
+
+    def test_edit_duplicates(self):
+        webhook = self.webhooks[0]
+        url = '{}/repo-push/{}'.format(self.url, webhook._id)
+        # change only url
+        data = {'url': 'http://httpbin.org/post/1'}
+        r = self.api_post(url, status=400, **data)
+        expected = {u'result': u'error',
+                    u'error': u'_the_form: "repo-push" webhook already '
+                              u'exists for Git http://httpbin.org/post/1'}
+        assert_equal(r.json, expected)
+        webhook = M.Webhook.query.get(_id=webhook._id)
+        assert_equal(webhook.hook_url, 'http://httpbin.org/post/0')
+        assert_equal(webhook.secret, 'secret-0')

http://git-wip-us.apache.org/repos/asf/allura/blob/09791466/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index fe41342..f30994f 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -28,7 +28,7 @@ from bson import ObjectId
 from tg import expose, validate, redirect, flash, config
 from tg.decorators import with_trailing_slash, without_trailing_slash
 from pylons import tmpl_context as c
-from pylons import response
+from pylons import response, request
 from formencode import validators as fev, schema, Invalid
 from ming.odm import session
 from webob import exc
@@ -240,7 +240,10 @@ class WebhookRestController(BaseController):
                            webhook.type, webhook.hook_url,
                            webhook.app_config.url())
             response.status_int = 201
-            return {'result': 'ok', 'webhook': webhook.__json__()}
+            # refetch updated values (e.g. mod_date)
+            session(webhook).expunge(webhook)
+            webhook = M.Webhook.query.get(_id=webhook._id)
+            return {'result': 'ok', 'webhook': webhook}
         else:
             limits = {
                 'max': M.Webhook.max_hooks(
@@ -267,7 +270,36 @@ class WebhookRestController(BaseController):
             wh = form.fields['webhook'].to_python(webhook)
         except Invalid:
             raise exc.HTTPNotFound()
-        return wh.__json__()
+        if request.method == 'POST':
+            return self._edit(wh, form, **kw)
+        else:
+            return {'result': 'ok', 'webhook': wh}
+
+    def _edit(self, webhook, form, **kw):
+        old_secret = webhook.secret
+        old_url = webhook.hook_url
+        try:
+            params = {'secret': kw.pop('secret', old_secret),
+                      'url': kw.pop('url', old_url),
+                      'webhook': unicode(webhook._id)}
+            valid = form.to_python(params)
+        except Exception as e:
+            response.status_int = 400
+            return {'result': 'error', 'error': self._error(e)}
+        try:
+            self.update_webhook(webhook, valid['url'], valid['secret'])
+        except Invalid as e:
+            response.status_int = 400
+            return {'result': 'error', 'error': self._error(e)}
+        M.AuditLog.log(
+            'edit webhook %s\n%s => %s\n%s',
+            webhook.type, old_url, valid['url'],
+            'secret changed' if old_secret != valid['secret'] else '')
+        # refetch updated values (e.g. mod_date)
+        session(webhook).expunge(webhook)
+        webhook = M.Webhook.query.get(_id=webhook._id)
+        return {'result': 'ok',
+                'webhook': webhook}
 
 
 class SendWebhookHelper(object):


[18/26] allura git commit: [#7835] Restructured the docs and updated the theme.

Posted by je...@apache.org.
[#7835] Restructured the docs and updated the theme.


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

Branch: refs/heads/ib/7830
Commit: ae5d2746dc48e0173005225ae0f319e60d3d5190
Parents: 132cf7e
Author: Heith Seewald <hs...@slashdotmedia.com>
Authored: Mon Feb 23 04:05:38 2015 +0000
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Tue Mar 3 16:00:59 2015 -0500

----------------------------------------------------------------------
 Allura/allura/model/project.py                 |  11 +-
 Allura/allura/webhooks.py                      |   6 +-
 Allura/development.ini                         |   2 +-
 Allura/docs/_static/images/messages.png        | Bin 118641 -> 284938 bytes
 Allura/docs/administration.rst                 | 114 ---------
 Allura/docs/api/index.rst                      |  27 ++
 Allura/docs/conf.py                            |  23 +-
 Allura/docs/contributing.rst                   | 206 ----------------
 Allura/docs/development/contributing.rst       | 210 ++++++++++++++++
 Allura/docs/development/extending.rst          | 120 +++++++++
 Allura/docs/development/external.rst           |  36 +++
 Allura/docs/development/index.rst              |  30 +++
 Allura/docs/development/testing.rst            |  80 ++++++
 Allura/docs/extending.rst                      | 116 ---------
 Allura/docs/faq.rst                            |  68 ------
 Allura/docs/getting_started/about.rst          |  49 ++++
 Allura/docs/getting_started/administration.rst | 115 +++++++++
 Allura/docs/getting_started/index.rst          |  32 +++
 Allura/docs/getting_started/installation.rst   | 107 ++++++++
 Allura/docs/getting_started/scm_host.rst       | 241 ++++++++++++++++++
 Allura/docs/getting_started/scm_host_ssh.rst   | 203 +++++++++++++++
 Allura/docs/getting_started/using.rst          |  64 +++++
 Allura/docs/guides/email.rst                   |  69 ------
 Allura/docs/guides/message_bus.rst             |  97 --------
 Allura/docs/guides/permissions.rst             |  60 -----
 Allura/docs/guides/scm.rst                     | 151 ------------
 Allura/docs/index.rst                          |  77 +-----
 Allura/docs/installation.rst                   | 108 --------
 Allura/docs/intro.rst                          |  41 ----
 Allura/docs/online.rst                         |  54 ----
 Allura/docs/platform.rst                       | 118 ---------
 Allura/docs/platform/email.rst                 |  71 ++++++
 Allura/docs/platform/index.rst                 |  30 +++
 Allura/docs/platform/message_bus.rst           | 101 ++++++++
 Allura/docs/platform/permissions.rst           |  64 +++++
 Allura/docs/platform/platform.rst              | 137 +++++++++++
 Allura/docs/platform/platform_tour.rst         | 258 ++++++++++++++++++++
 Allura/docs/platform/scm.rst                   | 150 ++++++++++++
 Allura/docs/platform_tour.rst                  | 257 -------------------
 Allura/docs/scm_host.rst                       | 236 ------------------
 Allura/docs/scm_host_ssh.rst                   | 197 ---------------
 Allura/docs/tutorials/testing.rst              |  76 ------
 Allura/docs/using.rst                          |  58 -----
 INSTALL.markdown                               |   4 +-
 requirements.txt                               |   1 +
 45 files changed, 2163 insertions(+), 2112 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/allura/model/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index 42e8e8d..b66ecec 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -690,14 +690,15 @@ class Project(SearchIndexable, MappedClass, ActivityNode, ActivityObject):
         return roles
 
     def install_apps(self, apps_params):
-        '''
-        Install many apps at once. Better than doing individually if you expect
+        """ Install many apps at once.
+
+        Better than doing individually if you expect
         default name conflicts (e.g. "code" for both git & svn), by using the
         tool_label value.
 
-        :param list apps_params: list of dicts, where each dict is the args used
-        in install_app()
-        '''
+        :param list apps_params: list of dicts, where each dict is the args used in install_app()
+        """
+
         # determine all the mount points
         mount_points = dict()
         for app_params in apps_params:

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index d41f477..c42be86 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -409,9 +409,9 @@ class WebhookSender(object):
         """Post a task that will send webhook payload
 
         :param params_or_list: dict with keyword parameters to be passed to
-        :meth:`get_payload` or a list of such dicts. If it's a list for each
-        element appropriate payload will be submitted, but limit will be
-        enforced only once for each webhook.
+            :meth:`get_payload` or a list of such dicts. If it's a list for each
+            element appropriate payload will be submitted, but limit will be
+            enforced only once for each webhook.
         """
         if not isinstance(params_or_list, list):
             params_or_list = [params_or_list]

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index c581808..ba06cc5 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -209,7 +209,7 @@ scm.host.rw.hg = /srv/hg$path
 scm.host.ro.svn = file:///srv/svn$path/
 scm.host.rw.svn = file:///srv/svn$path/
 
-# SCM settings for chroot + ldap configuration.  See Allura/docs/scm_host.rst
+# SCM settings for chroot + ldap configuration.  See Allura/docs/getting_started/scm_host.rst
 # scm.host.ro.git = git://git.localhost$path
 # scm.host.rw.git = ssh://$username@localhost:8022/scm-repo$path
 # scm.host.ro.hg = http://hg.localhost$path

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/_static/images/messages.png
----------------------------------------------------------------------
diff --git a/Allura/docs/_static/images/messages.png b/Allura/docs/_static/images/messages.png
index 14c63ad..2f88b34 100644
Binary files a/Allura/docs/_static/images/messages.png and b/Allura/docs/_static/images/messages.png differ

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/administration.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/administration.rst b/Allura/docs/administration.rst
deleted file mode 100644
index b4f69f5..0000000
--- a/Allura/docs/administration.rst
+++ /dev/null
@@ -1,114 +0,0 @@
-..     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.
-
-Administration
-=================
-
-Commands, Scripts, and Tasks
-----------------------------
-
-Allura has many `paster` commands and `paster` scripts that can be run from the
-server commandline to administrate Allura.  There are also tasks that can be
-run through the `taskd` system.  New tasks can be submitted via the web at
-/nf/admin/task_manager  Some paster commands and scripts have been set up
-so that they are runnable as tasks too, giving you the convenience of starting
-them through the web and letting `taskd` execute them, rather than from a server
-shell.
-
-Commands can be discovered and run via the `paster` command when you are in the
-'Allura' directory that has your .ini file.  For example::
-
-    (env-allura) Allura$ paster help
-    ... all commands listed here ...
-
-    (env-allura) Allura$ paster create-neighborhood --help
-    ... specific command help ...
-
-    (env-allura) Allura$ paster create-neighborhood development.ini myneighborhood myuser ...
-
-
-Scripts are in the `scripts/` directory and run via `paster script`.  An extra
-`--` is required to separate script arguments from paster arguments.  Example::
-
-    (env-allura) Allura$ paster script development.ini ../scripts/create-allura-sitemap.py -- --help
-    ... help output ...
-
-    (env-allura) Allura$ paster script development.ini ../scripts/create-allura-sitemap.py -- -u 100
-
-TODO:   explain important scripts, commands
-
-Tasks can be run via the web interface at /nf/admin/task_manager  You must know
-the full task name, e.g. `allura.tasks.admin_tasks.install_app`  You can
-optionally provide a username and project and app which will get set on the
-current context (`c`).  You should specify what args and kwargs will be passed
-as parameters to the task.  They are specified in JSON format on the form.
-
-See the listing of :mod:`some available tasks <allura.tasks.admin_tasks>`.
-
-TODO: explain how to run scripttasks and commandtasks
-
-
-Client Scripts
---------------
-
-Allura includes some client scripts that use Allura APIs and do not have to be run
-from an Allura server.  They do require various python packages to be installed
-and possibly a local Allura codebase set up.
-
-One such script is `wiki-copy.py` which reads the wiki pages from one Allura wiki
-instance and uploads them to another Allura wiki instance.  It can be run as:
-
-.. code-block:: console
-
-    $ python scripts/wiki-copy.py --help
-
-
-Site Notifications
-------------------
-
-Allura has support for site-wide notifications that appear below the site header,
-but there is currently no UI for managing them.  They can easily be inserted via
-manual mongo queries, however:
-
-.. code-block:: console
-
-    > db.site_notification.insert({
-    ... active: true,
-    ... impressions: 10,
-    ... content: 'You can now reimport exported project data.'
-    ... })
-
-This will create a notification that will be shown for 10 page views or until the
-user closes it manually.  An `impressions` value of 0 will show the notification
-indefinitely (until closed).  The notification content can contain HTML.  Only the
-most recent notification will be shown, unless it has `active:false`, in which case
-no notification will be shown.
-
-
-Using Projects and Tools
-------------------------
-
-We currently don't have any further documentation for basic operations of managing
-users, projects, and tools on Allura.  However, SourceForge has help docs that cover
-these functions https://sourceforge.net/p/forge/documentation/Docs%20Home/  Note
-that this documentation also covers some SourceForge features that are not part of Allura.
-
-
-Public API Documentation
-------------------------
-
-Allura's web api is currently documented at https://sourceforge.net/p/forge/documentation/Allura%20API/

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/api/index.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/api/index.rst b/Allura/docs/api/index.rst
new file mode 100644
index 0000000..2103410
--- /dev/null
+++ b/Allura/docs/api/index.rst
@@ -0,0 +1,27 @@
+..     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.
+
+**********
+Allura API
+**********
+
+
+.. toctree::
+    :maxdepth: 3
+    :glob:
+
+    *

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/conf.py
----------------------------------------------------------------------
diff --git a/Allura/docs/conf.py b/Allura/docs/conf.py
index 03ad27f..c201b9c 100644
--- a/Allura/docs/conf.py
+++ b/Allura/docs/conf.py
@@ -37,6 +37,8 @@
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+import os
+
 extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx',
               'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig']
 
@@ -99,21 +101,30 @@ exclude_trees = ['_build']
 # The name of the Pygments (syntax highlighting) style to use.
 pygments_style = 'sphinx'
 
+# A list of document names that are present, but not currently included in the toctree.
+unused_docs = 'getting_started/scm_host_ssh.rst'
+
 # A list of ignored prefixes for module index sorting.
 #modindex_common_prefix = []
 
 # -- Options for HTML output ---------------------------------------------
 
-# The theme to use for HTML and HTML Help pages.  Major themes that come with
-# Sphinx are currently 'default' and 'sphinxdoc'.
-html_theme = 'nature'
+# on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org
+on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+
+if not on_rtd:  # only import and set the theme if we're building docs locally
+    import sphinx_rtd_theme
+    html_theme = 'sphinx_rtd_theme'
+    html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+
+# otherwise, readthedocs.org uses their theme by default, so no need to specify it
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-html_theme_options = {
-    'sidebarwidth': 280,  # default 230 (px)
-}
+# html_theme_options = {
+#     'sidebarwidth': 280,  # default 230 (px)
+# }
 
 # Add any paths that contain custom themes here, relative to this directory.
 #html_theme_path = []

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/contributing.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/contributing.rst b/Allura/docs/contributing.rst
deleted file mode 100644
index e899d05..0000000
--- a/Allura/docs/contributing.rst
+++ /dev/null
@@ -1,206 +0,0 @@
-..     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.
-
-Contributing to Allura
-======================
-For developers interested in hacking on Allura or its components, this guide
-will (hopefully) be a roadmap to help you get started and guide you on your
-way.
-
-Getting Help
-------------
-Along the way, you will no doubt have questions that aren't addressed here.
-For help, you can get in touch with other Allura developers on the developer
-mailing list (dev@allura.apache.org) or in the #allura channel on
-the Freenode IRC network.
-
-Installing Allura
------------------
-Before hacking on Allura, you'll need to get an Allura instance up and running
-so you can see and test the changes you make. You can install Allura from
-scratch, or by using our pre-built Vagrant image. Instructions for these
-approaches can be found here:
-
-* `Install from scratch <https://forge-allura.apache.org/p/allura/git/ci/master/tree/INSTALL.markdown>`_
-* `Install from Vagrant image <https://forge-allura.apache.org/p/allura/wiki/Install%20and%20Run%20Allura%20-%20Vagrant/>`_
-
-Managing Services
------------------
-Allura is comprised of a handful of separate services, all of which must be
-running in order for the system to be fully functional. These services (and
-how to start them) are covered in the install documentation, but are mentioned
-again here simply to reiterate the components of a complete Allura system.
-
-External services:
-
-* MongoDB - database
-* Solr - searching/indexing
-
-Allura services:
-
-* Web server - the Allura web application
-* :doc:`Taskd <guides/message_bus>` - background task daemon
-* Inbound email handler - processes email sent to the Allura instance (e.g.,
-  a reply to a ticket notification email)
-
-Logging
--------
-The logs for Allura services can be found in ``/var/log/allura/``.
-The most important of these is ``allura.log``, as it will contain log messages
-for all Allura application code.
-
-Technology Stack
-----------------
-`MongoDB <http://www.mongodb.org/>`_ - Allura stores all of its data in MongoDB.
-If you're new to MongoDB, you'll want to keep the `reference docs
-<http://docs.mongodb.org/manual/reference/>`_ handy until you're familiar with
-basic query operations and syntax.
-
-`Solr <http://lucene.apache.org/solr/>`_ - Allura artifact data is indexed into
-Solr so that it can be searched. In general, you won't need to know much about
-Solr in order to work on Allura.
-
-`Turbogears <http://turbogears.org/>`_ - Allura is built on the TurboGears web
-framework. Understanding `TG controller basics <http://turbogears.readthedocs.org/en/tg2.3.0b2/turbogears/controllers.html>`_
-and `object dispatch <http://turbogears.readthedocs.org/en/tg2.3.0b2/turbogears/objectdispatch.html>`_,
-TurboGears' mechanism for routing an HTTP request to the code that should handle
-it, are critical for understanding how a request is handled by Allura.
-
-`Ming <http://merciless.sourceforge.net/index.html>`_ - Allura interfaces with
-MongoDB through Ming, a library which provides an Object Document Mapper for
-MongoDB. Fortunately, the query syntax is mostly identical to that of
-native MongoDB, so the learning curve is pretty flat.
-
-`EasyWidgets <http://easywidgets.pythonisito.com/index.html>`_ - An HTML template
-and form validation library used by Allura. The learning curve on EasyWidgets
-is, ironically, not easy. Be prepared to dig into the source if you want to
-do something complicated with EW. Fortunately, there are lots of exmaples in
-the Allura source already.
-
-`Jinja <http://jinja.pocoo.org/>`_ - HTML template library used by Allura.
-
-If you want to work on the front end of Allura, you'll also need some CSS and
-Javascript skills, and basic knowledge of JQuery.
-
-Finding Something to Work On
-----------------------------
-Tickets that are relatively simple and good for new contributors have a
-"bitesize" label, and can be found here:
-https://sourceforge.net/p/allura/tickets/search/?q=labels%3Abitesize
-
-Find one that looks good, and leave a comment on the ticket or mailing list
-to let us know you're working on it. If you get stuck, remember that we're
-available to help on the mailing list or IRC.
-
-Code Organization
------------------
-The core Allura platform code is in the ``Allura/`` directory in the top-level of the
-repo. The ``Forge*/`` directories contain Allura "tools" - plugins that extend the
-core platform. For an overview of the platform and services it provides, read
-the :doc:`Platform Tour <platform_tour>` documentation. If you're interested in
-developing a new Allura plugin, you may find this `blog series <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/>`_
-helpful.
-
-Tracing a Request
------------------
-Whether you're fixing a bug or adding a new feature, one of your first
-questions will be, "Where is the code that is handling this request (or serving
-this page)?" For a new contributor, answering this question can be surprisingly
-challenging. Here are some tips to help you out:
-
-1. The root controller for the entire application is in
-``Allura/allura/controllers/root.py`` - dispatch for *every* request begins
-here. It is possible (albeit difficult) to trace the path your request
-will take through the code from this starting point if you have a
-thorough knowledge of Turbogears' request dispatch mechanics. But, nobody
-wants to do this if they can avoid it.
-
-2. Is the page being served part of a tool (e.g. Ticket Tracker, Wiki, etc)?
-Most of the time, the answer is yes. If you know which tool is handling the
-request, you can skip right to the root controller for that tool. To find the
-root controller, first find the main entry point for the tool, which is defined
-in the ``[allura]`` section of the tool's  ``setup.py`` file. So, for example,
-if you know the request is being handled by a Ticket Tracker, look in
-``ForgeTracker/setup.py`` and you'll see that that its entry point is
-``forgetracker.tracker_main:ForgeTrackerApp``. Each Allura tool instance
-defines a ``root`` attribute which is its root controller. So once you've found
-the main tool class, you can find its root controller and begin tracing your
-request from there.
-
-3. Search for things! ``grep`` and equivalents are your friends. If you're
-looking at an html page and want to find the controller code for it, try
-searching the code base for some (static) text on the page. If your search
-successfully turns up an html page, search again on the name of the html file.
-There's a good change you'll find the controller method that renders that page.
-
-Interactive Debugging
----------------------
-If you've never used ``ipdb`` before, you'll find it's a great tool for
-interactive debugging of Python code. In order to use ``ipdb`` to debug Allura,
-you'll first need to make sure that the process you're debugging is running in
-the foreground. In most cases you'll be debugging either the web app process
-or the taskd (background worker) process.
-
-First, make sure sure ipdb is installed in your virtual environment::
-
-    pip install ipdb
-
-Then, find the line of code where you want to start the interactive debugger,
-and insert this line above it::
-
-    import ipdb; ipdb.set_trace()
-
-Now, kill any running web or taskd procs and restart them in the
-foreground::
-
-    cd Allura
-    # web
-    pkill "paster serve" && paster serve --reload ../development.ini
-    # taskd
-    pkill "paster taskd" && paster taskd ../development.ini
-
-Make a request to the web app, and when your line of code is hit, a debug
-session will start on the console where the process is running.
-
-For more information about using ``pdb``, see the `official documentation
-<http://docs.python.org/2/library/pdb.html>`_.
-
-Testing
--------
-To run all the tests, execute ``./run_tests`` in the repo root. To run tests
-for a single package, for example ``forgetracker``::
-
-  cd ForgeTracker && nosetests
-
-To learn more about the ``nose`` test runner, consult the `documentation
-<http://nose.readthedocs.org/en/latest/>`_.
-
-When writing code for Allura, don't forget that you'll need to also create
-tests that cover behaviour that you've added or changed. You may find this
-:doc:`short guide <tutorials/testing>` helpful.
-
-
-Submitting a Merge Request
---------------------------
-Before submitting a merge request, make sure your changes conform to our
-`contribution guidelines <https://forge-allura.apache.org/p/allura/wiki/Contributing%20Code/>`_.
-Once your changes are finished and tested, submit them to be merged back into
-the main repo:
-
-* Fork the main Allura repo from here: https://forge-allura.apache.org/p/allura/git/
-* Commit and push your changes to your fork
-* Submit a Merge Request from your fork

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/development/contributing.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/development/contributing.rst b/Allura/docs/development/contributing.rst
new file mode 100644
index 0000000..e96e5be
--- /dev/null
+++ b/Allura/docs/development/contributing.rst
@@ -0,0 +1,210 @@
+..     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.
+
+************
+Contributing
+************
+
+Contributing to Allura
+======================
+For developers interested in hacking on Allura or its components, this guide
+will (hopefully) be a roadmap to help you get started and guide you on your
+way.
+
+Getting Help
+------------
+Along the way, you will no doubt have questions that aren't addressed here.
+For help, you can get in touch with other Allura developers on the developer
+mailing list (dev@allura.apache.org) or in the #allura channel on
+the Freenode IRC network.
+
+Installing Allura
+-----------------
+Before hacking on Allura, you'll need to get an Allura instance up and running
+so you can see and test the changes you make. You can install Allura from
+scratch, or by using our pre-built Vagrant image. Instructions for these
+approaches can be found here:
+
+* `Install from scratch <https://forge-allura.apache.org/p/allura/git/ci/master/tree/INSTALL.markdown>`_
+* `Install from Vagrant image <https://forge-allura.apache.org/p/allura/wiki/Install%20and%20Run%20Allura%20-%20Vagrant/>`_
+
+Managing Services
+-----------------
+Allura is comprised of a handful of separate services, all of which must be
+running in order for the system to be fully functional. These services (and
+how to start them) are covered in the install documentation, but are mentioned
+again here simply to reiterate the components of a complete Allura system.
+
+External services:
+
+* MongoDB - database
+* Solr - searching/indexing
+
+Allura services:
+
+* Web server - the Allura web application
+* :doc:`Taskd <../platform/message_bus>` - background task daemon
+* Inbound email handler - processes email sent to the Allura instance (e.g.,
+  a reply to a ticket notification email)
+
+Logging
+-------
+The logs for Allura services can be found in ``/var/log/allura/``.
+The most important of these is ``allura.log``, as it will contain log messages
+for all Allura application code.
+
+Technology Stack
+----------------
+`MongoDB <http://www.mongodb.org/>`_ - Allura stores all of its data in MongoDB.
+If you're new to MongoDB, you'll want to keep the `reference docs
+<http://docs.mongodb.org/manual/reference/>`_ handy until you're familiar with
+basic query operations and syntax.
+
+`Solr <http://lucene.apache.org/solr/>`_ - Allura artifact data is indexed into
+Solr so that it can be searched. In general, you won't need to know much about
+Solr in order to work on Allura.
+
+`Turbogears <http://turbogears.org/>`_ - Allura is built on the TurboGears web
+framework. Understanding `TG controller basics <http://turbogears.readthedocs.org/en/tg2.3.0b2/turbogears/controllers.html>`_
+and `object dispatch <http://turbogears.readthedocs.org/en/tg2.3.0b2/turbogears/objectdispatch.html>`_,
+TurboGears' mechanism for routing an HTTP request to the code that should handle
+it, are critical for understanding how a request is handled by Allura.
+
+`Ming <http://merciless.sourceforge.net/index.html>`_ - Allura interfaces with
+MongoDB through Ming, a library which provides an Object Document Mapper for
+MongoDB. Fortunately, the query syntax is mostly identical to that of
+native MongoDB, so the learning curve is pretty flat.
+
+`EasyWidgets <http://easywidgets.pythonisito.com/index.html>`_ - An HTML template
+and form validation library used by Allura. The learning curve on EasyWidgets
+is, ironically, not easy. Be prepared to dig into the source if you want to
+do something complicated with EW. Fortunately, there are lots of exmaples in
+the Allura source already.
+
+`Jinja <http://jinja.pocoo.org/>`_ - HTML template library used by Allura.
+
+If you want to work on the front end of Allura, you'll also need some CSS and
+Javascript skills, and basic knowledge of JQuery.
+
+Finding Something to Work On
+----------------------------
+Tickets that are relatively simple and good for new contributors have a
+"bitesize" label, and can be found here:
+https://sourceforge.net/p/allura/tickets/search/?q=labels%3Abitesize
+
+Find one that looks good, and leave a comment on the ticket or mailing list
+to let us know you're working on it. If you get stuck, remember that we're
+available to help on the mailing list or IRC.
+
+Code Organization
+-----------------
+The core Allura platform code is in the ``Allura/`` directory in the top-level of the
+repo. The ``Forge*/`` directories contain Allura "tools" - plugins that extend the
+core platform. For an overview of the platform and services it provides, read
+the :doc:`Platform Tour <../platform/platform_tour>` documentation. If you're interested in
+developing a new Allura plugin, you may find this `blog series <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/>`_
+helpful.
+
+Tracing a Request
+-----------------
+Whether you're fixing a bug or adding a new feature, one of your first
+questions will be, "Where is the code that is handling this request (or serving
+this page)?" For a new contributor, answering this question can be surprisingly
+challenging. Here are some tips to help you out:
+
+1. The root controller for the entire application is in
+``Allura/allura/controllers/root.py`` - dispatch for *every* request begins
+here. It is possible (albeit difficult) to trace the path your request
+will take through the code from this starting point if you have a
+thorough knowledge of Turbogears' request dispatch mechanics. But, nobody
+wants to do this if they can avoid it.
+
+2. Is the page being served part of a tool (e.g. Ticket Tracker, Wiki, etc)?
+Most of the time, the answer is yes. If you know which tool is handling the
+request, you can skip right to the root controller for that tool. To find the
+root controller, first find the main entry point for the tool, which is defined
+in the ``[allura]`` section of the tool's  ``setup.py`` file. So, for example,
+if you know the request is being handled by a Ticket Tracker, look in
+``ForgeTracker/setup.py`` and you'll see that that its entry point is
+``forgetracker.tracker_main:ForgeTrackerApp``. Each Allura tool instance
+defines a ``root`` attribute which is its root controller. So once you've found
+the main tool class, you can find its root controller and begin tracing your
+request from there.
+
+3. Search for things! ``grep`` and equivalents are your friends. If you're
+looking at an html page and want to find the controller code for it, try
+searching the code base for some (static) text on the page. If your search
+successfully turns up an html page, search again on the name of the html file.
+There's a good change you'll find the controller method that renders that page.
+
+Interactive Debugging
+---------------------
+If you've never used ``ipdb`` before, you'll find it's a great tool for
+interactive debugging of Python code. In order to use ``ipdb`` to debug Allura,
+you'll first need to make sure that the process you're debugging is running in
+the foreground. In most cases you'll be debugging either the web app process
+or the taskd (background worker) process.
+
+First, make sure sure ipdb is installed in your virtual environment::
+
+    pip install ipdb
+
+Then, find the line of code where you want to start the interactive debugger,
+and insert this line above it::
+
+    import ipdb; ipdb.set_trace()
+
+Now, kill any running web or taskd procs and restart them in the
+foreground::
+
+    cd Allura
+    # web
+    pkill "paster serve" && paster serve --reload ../development.ini
+    # taskd
+    pkill "paster taskd" && paster taskd ../development.ini
+
+Make a request to the web app, and when your line of code is hit, a debug
+session will start on the console where the process is running.
+
+For more information about using ``pdb``, see the `official documentation
+<http://docs.python.org/2/library/pdb.html>`_.
+
+Testing
+-------
+To run all the tests, execute ``./run_tests`` in the repo root. To run tests
+for a single package, for example ``forgetracker``::
+
+  cd ForgeTracker && nosetests
+
+To learn more about the ``nose`` test runner, consult the `documentation
+<http://nose.readthedocs.org/en/latest/>`_.
+
+When writing code for Allura, don't forget that you'll need to also create
+tests that cover behaviour that you've added or changed. You may find this
+:doc:`short guide <../development/testing>` helpful.
+
+
+Submitting a Merge Request
+--------------------------
+Before submitting a merge request, make sure your changes conform to our
+`contribution guidelines <https://forge-allura.apache.org/p/allura/wiki/Contributing%20Code/>`_.
+Once your changes are finished and tested, submit them to be merged back into
+the main repo:
+
+* Fork the main Allura repo from here: https://forge-allura.apache.org/p/allura/git/
+* Commit and push your changes to your fork
+* Submit a Merge Request from your fork

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/development/extending.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/development/extending.rst b/Allura/docs/development/extending.rst
new file mode 100644
index 0000000..e45b1c2
--- /dev/null
+++ b/Allura/docs/development/extending.rst
@@ -0,0 +1,120 @@
+..     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
+*********
+
+Extension APIs and 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.plugin.SiteAdminExtension`
+* :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)
+* ``[allura.timers]`` functions which return a list or single :class:`timermiddleware.Timer` which will be included in stats.log timings
+* :mod:`allura.ext.user_profile`
+* ``[allura.middleware]`` classes, which are standard WSGI middleware.  They will receive the ``app`` instance and a ``config`` dict as constructor parameters.
+  The middleware will be used for all requests.  By default the middleware wraps the base app directly and other middleware wrap around it.
+  If your middleware needs to wrap around the other Allura middleware (except error handling), set ``when = 'outer'`` on your middleware.
+* :class:`allura.webhooks.WebhookSender`
+
+A listing of available 3rd-party extensions is at https://forge-allura.apache.org/p/allura/wiki/Extensions/
+
+To disable any Allura entry point, simply add an entry in your ``.ini`` config file
+with names and values corresponding to entry points defined in any ``setup.py`` file.
+For example if you have ForgeImporter set up, but want to disable the google code importers:
+
+.. code-block:: ini
+
+    disable_entry_points.allura.project_importers = google-code
+    disable_entry_points.allura.importers = google-code-tracker, google-code-repo
+
+Other entry points are used to provide ``paster`` commands and ``easy_widget`` configuration,
+which are not part of Allura but are used by Allura.
+
+
+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
+* trove_category_created
+* trove_category_updated
+* trove_category_deleted
+
+
+Markdown Macros
+===============
+
+Most text inputs in Allura accept Markdown text which is parsed and turned into
+HTML before being rendered. The Markdown text may contain "macros" - custom
+commands which extend the Markdown language. Here's an example of a macro
+that comes with Allura::
+
+    [[project_admins]]
+
+Include this macro in a wiki page or other Markdown content, and when rendered
+it will be replaced by an actual list of the project's admin users.
+
+Extending Allura with your own macros is simple, requiring two basic steps:
+
+1. Decide on a name for your macro, then create a function with that name, and
+   decorate it with the `macro()` decorator from Allura. The function can
+   accept keyword arguments, and must return text or HTML. For example::
+
+    from allura.lib.macro import macro
+
+    @macro()
+    def hello(name='World'):
+        return "<p>Hello {}!</p>".format(name)
+
+2. Add an entry point for your macro to the `setup.py` for your package::
+
+    [allura.macros]
+    hello_macro = mypkg.mymodule:hello
+
+Note that the key name (`hello_macro` in this case) doesn't matter - the macro
+is named after the function name. Our example macro could be used in a couple
+ways::
+
+    [[hello]]
+    [[hello name=Universe]]
+
+For more help with macros, consult the source code for the macros that ship
+with Allura. You can find them in the `allura.lib.macro` package.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/development/external.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/development/external.rst b/Allura/docs/development/external.rst
new file mode 100644
index 0000000..6346bbf
--- /dev/null
+++ b/Allura/docs/development/external.rst
@@ -0,0 +1,36 @@
+..     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.
+
+***************************
+Writing an Allura-based app
+***************************
+
+External links
+    * `Getting Started <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/06/part-1-getting-started/>`_
+    * `Forms, Artifacts, and Testing <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/06/part-2-creating-our-first-paste/>`_
+    * `Adding a Custom Icon <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/12/part-3-adding-a-custom-icon/>`_
+    * `Adding a Sidebar Menu <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/12/adding-a-sidebar-menu/>`_
+    * `Adding Custom CSS <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/12/part-5-adding-custom-css/>`_
+
+
+Generated API docs, useful for browsing through the code, viewing inheritance, etc:
+
+* http://allura.sourceforge.net/epydoc/
+
+Our project page, including tickets, discussion forums, etc.:
+
+* https://allura.apache.org/

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/development/index.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/development/index.rst b/Allura/docs/development/index.rst
new file mode 100644
index 0000000..1e15906
--- /dev/null
+++ b/Allura/docs/development/index.rst
@@ -0,0 +1,30 @@
+..     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.
+
+***********************
+Development & community
+***********************
+
+
+.. toctree::
+    :maxdepth: 3
+
+    extending
+    contributing
+    external
+    testing
+

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/development/testing.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/development/testing.rst b/Allura/docs/development/testing.rst
new file mode 100644
index 0000000..bcff43f
--- /dev/null
+++ b/Allura/docs/development/testing.rst
@@ -0,0 +1,80 @@
+..     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.
+
+*****************
+Testing in Allura
+*****************
+
+Writing Tests for Allura Tools
+==============================
+
+Testing the controllers and models of an Allura tool is fairly
+straightforward.  Generally, you should follow the example of tests in the
+`allura/tests/functional` directory for controller tests and
+`allura.tests.model` for model tests.  For functional tests, the Allura platform
+provides a convenient "test harness" :class:`allura.controllers.test.TestController` controller
+class which is used as the application root for the
+:class:`allura.tests.TestController` class.
+
+In order to test your new tool controllers, you simply need to use the `self.app.get()`
+and `self.app.post()` methods of your test controller.  The test harness makes
+all the tools available in the system available under the URL /*entry point
+name*/.  So to test the :mod:`allura.ext.project_home` tool, for instance, we
+need only write the following::
+
+    from allura.tests import TestController
+
+    class TestProjectHome(TestController):
+
+        def test_home(self):
+            r = self.app.get('/home/')
+
+Whenever you use the :class:`allura.tests.TestController` app property, the
+test harness sets up the context so that `c.project` is always the
+`projects/test` project and whichever tool name you request is mounted at its
+entry point (so the Wiki tool will be mounted at /Wiki/).  `c.user` is always
+set to the `test-admin` user to avoid authentication issues.
+
+The framework used to generate the WSGI environment for testing your tools is
+provided by the `WebTest <http://pythonpaste.org/webtest/>`_ module, where you can
+find further documentation for the `.get()` and `.post()` methods.
+
+Testing Allura models is also straightforward, though you will often
+need to setup pylons global objects before your test. If the code under test
+uses pylons globals (like `g` and `c`), but your test doesn't require the
+fully-loaded wsgi app, you can do something like this:
+
+.. code-block:: python
+
+    from pylons import tmpl_context as c
+
+    from alluratest.controller import setup_unit_test
+    from allura.lib import helpers a h
+    from allura import model as M
+
+    def setUp():
+        # set up pylons globals
+        setup_unit_test()
+
+        # set c.project and c.app
+        h.set_context('test', 'wiki', neighborhood='Projects'):
+        c.user = M.User.query.get(username='test-admin')
+
+Testing the tasks and events is  similar to testing models.  Generally, you will
+simply want to call your `@task` and `@event_handler` methods directly rather
+than setting up a full mocking infrastructure, though it is possible to use the
+MonQTask model in the allura model if you wish to do more functional/integration testing.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/extending.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/extending.rst b/Allura/docs/extending.rst
deleted file mode 100644
index 825bcbd..0000000
--- a/Allura/docs/extending.rst
+++ /dev/null
@@ -1,116 +0,0 @@
-..     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.
-
-Extension APIs and 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.plugin.SiteAdminExtension`
-* :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)
-* ``[allura.timers]`` functions which return a list or single :class:`timermiddleware.Timer` which will be included in stats.log timings
-* :mod:`allura.ext.user_profile`
-* ``[allura.middleware]`` classes, which are standard WSGI middleware.  They will receive the ``app`` instance and a ``config`` dict as constructor parameters.
-  The middleware will be used for all requests.  By default the middleware wraps the base app directly and other middleware wrap around it.
-  If your middleware needs to wrap around the other Allura middleware (except error handling), set ``when = 'outer'`` on your middleware.
-* :class:`allura.webhooks.WebhookSender`
-
-A listing of available 3rd-party extensions is at https://forge-allura.apache.org/p/allura/wiki/Extensions/
-
-To disable any Allura entry point, simply add an entry in your ``.ini`` config file
-with names and values corresponding to entry points defined in any ``setup.py`` file.
-For example if you have ForgeImporter set up, but want to disable the google code importers:
-
-.. code-block:: ini
-
-    disable_entry_points.allura.project_importers = google-code
-    disable_entry_points.allura.importers = google-code-tracker, google-code-repo
-
-Other entry points are used to provide ``paster`` commands and ``easy_widget`` configuration,
-which are not part of Allura but are used by Allura.
-
-
-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
-* trove_category_created
-* trove_category_updated
-* trove_category_deleted
-
-
-Markdown Macros
-===============
-
-Most text inputs in Allura accept Markdown text which is parsed and turned into
-HTML before being rendered. The Markdown text may contain "macros" - custom
-commands which extend the Markdown language. Here's an example of a macro
-that comes with Allura::
-
-    [[project_admins]]
-
-Include this macro in a wiki page or other Markdown content, and when rendered
-it will be replaced by an actual list of the project's admin users.
-
-Extending Allura with your own macros is simple, requiring two basic steps:
-
-1. Decide on a name for your macro, then create a function with that name, and
-   decorate it with the `macro()` decorator from Allura. The function can
-   accept keyword arguments, and must return text or HTML. For example::
-
-    from allura.lib.macro import macro
-
-    @macro()
-    def hello(name='World'):
-        return "<p>Hello {}!</p>".format(name)
-
-2. Add an entry point for your macro to the `setup.py` for your package::
-
-    [allura.macros]
-    hello_macro = mypkg.mymodule:hello
-
-Note that the key name (`hello_macro` in this case) doesn't matter - the macro
-is named after the function name. Our example macro could be used in a couple
-ways::
-
-    [[hello]]
-    [[hello name=Universe]]
-
-For more help with macros, consult the source code for the macros that ship
-with Allura. You can find them in the `allura.lib.macro` package.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/faq.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/faq.rst b/Allura/docs/faq.rst
deleted file mode 100644
index 0c26b43..0000000
--- a/Allura/docs/faq.rst
+++ /dev/null
@@ -1,68 +0,0 @@
-..     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.
-
-Why not improve existing tools like Trac, Redmine or Bugzilla?
---------------------------------------------------------------
-
-One word.  Scalability.
-
-Ok, two words.  Scalability and Performance
-
-Ok, three words:  Scalability, Performance, and Flexibility
-
-Seriously though, we didn't think that any of the existing systems have
-actually hit the right usability, scalability, or flexibility targets that
-we needed to hit, and we knew that it would be a **lot** of work to get
-any of them to do what we needed.
-
-But we knew e-mail integration was going to be a big deal to our forge,
-so we did take a long look at Roundup, which is a very well designed
-system build from the ground up around the idea of e-mail integration.
-
-If you were so inspired by Roundup, why not just use it?
---------------------------------------------------------
-
-We liked the flexible schema system provided by Roundup's HyperTable layer,
-but thought that native MongoDB bindings were both cleaner, faster, and
-ultimately more powerful.
-
-Sure we sacrifice the flexibility of Roundup's
-backend, but our main goal is to make usable, high performance system,
-not to maximize the number of backend storages systems supported.
-
-Why create all the apps as plugins?
------------------------------------
-
-We know that some projects are going to want more locked down
-access controls in their bug trackers, or more workflow based
-processes.  These things are inevitable, and we really do want
-to support them, but at the same time they are going to conflict
-with the way many other projects want to work.
-
-Building a plugin (tool in Allura terms) system, and standard
-integration points makes it possible to serve everybody in one
-way or another.
-
-Why not just allow web-based extensions?
-----------------------------------------
-
-We talked about this quite a bit, and decided that we could write local
-native tools more quickly and easily, and that we could build a
-local app tool that proxied out to an external webapp, and that
-we could provide a lot more flexibility by allowing custom
-external webapp proxies -- which brought us right back to local
-tools.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/getting_started/about.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/about.rst b/Allura/docs/getting_started/about.rst
new file mode 100644
index 0000000..a96c0a9
--- /dev/null
+++ b/Allura/docs/getting_started/about.rst
@@ -0,0 +1,49 @@
+..     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.
+
+***********
+Why Allura?
+***********
+
+
+Rather than build yet another forge, we decided to do something new.   We wanted to build a new kind of extensible forge, and a new set of highly integrated forge tools.
+
+
+Allura is an **open** platform for **open** processes
+-----------------------------------------------------
+
+It's easy to get frustrated with existing development tools.   Too often they are overbearing, complex, and make assumptions that get in your way.  And even if they are open source, it's often difficult to get them to work the way you need them too.
+
+Which is why we created Allura.   It's designed to be truly **open**, in many different senses of the word.
+
+It's open in bunch of ways:
+
+* It's a combination of tools available under *Free* or *Open Source* licenses.
+* It's designed around a plugin architecture, and anybody willing to contribute a tool can play.
+* It's being hosted publicly, and is built through contributions of individuals and companies who want to promote Open Source development.
+* It's designed to provide a structure around which welcoming (open) communities can grow.
+* Its core tools are designed around inclusive development processes.
+
+We looked at existing forges, but to achieve all those goals, we decided we needed to build something new.
+
+Allura is designed to support an **ecosystem**
+----------------------------------------------
+
+Allura is at once a **set of tools** to help people collaboratively develop software, and an **open platform** on which innovative new tools be built.
+
+
+

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/getting_started/administration.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/administration.rst b/Allura/docs/getting_started/administration.rst
new file mode 100644
index 0000000..4410f55
--- /dev/null
+++ b/Allura/docs/getting_started/administration.rst
@@ -0,0 +1,115 @@
+..     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.
+
+**************
+Administration
+**************
+
+Commands, Scripts, and Tasks
+----------------------------
+
+Allura has many `paster` commands and `paster` scripts that can be run from the
+server commandline to administrate Allura.  There are also tasks that can be
+run through the `taskd` system.  New tasks can be submitted via the web at
+/nf/admin/task_manager  Some paster commands and scripts have been set up
+so that they are runnable as tasks too, giving you the convenience of starting
+them through the web and letting `taskd` execute them, rather than from a server
+shell.
+
+Commands can be discovered and run via the `paster` command when you are in the
+'Allura' directory that has your .ini file.  For example::
+
+     paster help
+    ... all commands listed here ...
+
+     paster create-neighborhood --help
+    ... specific command help ...
+
+     paster create-neighborhood development.ini myneighborhood myuser ...
+
+
+Scripts are in the `scripts/` directory and run via `paster script`.  An extra
+`--` is required to separate script arguments from paster arguments.  Example::
+
+     paster script development.ini ../scripts/create-allura-sitemap.py -- --help
+    ... help output ...
+
+     paster script development.ini ../scripts/create-allura-sitemap.py -- -u 100
+
+TODO:   explain important scripts, commands
+
+Tasks can be run via the web interface at /nf/admin/task_manager  You must know
+the full task name, e.g. `allura.tasks.admin_tasks.install_app`  You can
+optionally provide a username and project and app which will get set on the
+current context (`c`).  You should specify what args and kwargs will be passed
+as parameters to the task.  They are specified in JSON format on the form.
+
+See the listing of :mod:`some available tasks <allura.tasks.admin_tasks>`.
+
+TODO: explain how to run scripttasks and commandtasks
+
+
+Client Scripts
+--------------
+
+Allura includes some client scripts that use Allura APIs and do not have to be run
+from an Allura server.  They do require various python packages to be installed
+and possibly a local Allura codebase set up.
+
+One such script is `wiki-copy.py` which reads the wiki pages from one Allura wiki
+instance and uploads them to another Allura wiki instance.  It can be run as:
+
+.. code-block:: console
+
+    $ python scripts/wiki-copy.py --help
+
+
+Site Notifications
+------------------
+
+Allura has support for site-wide notifications that appear below the site header,
+but there is currently no UI for managing them.  They can easily be inserted via
+manual mongo queries, however:
+
+.. code-block:: console
+
+    > db.site_notification.insert({
+    ... active: true,
+    ... impressions: 10,
+    ... content: 'You can now reimport exported project data.'
+    ... })
+
+This will create a notification that will be shown for 10 page views or until the
+user closes it manually.  An `impressions` value of 0 will show the notification
+indefinitely (until closed).  The notification content can contain HTML.  Only the
+most recent notification will be shown, unless it has `active:false`, in which case
+no notification will be shown.
+
+
+Using Projects and Tools
+------------------------
+
+We currently don't have any further documentation for basic operations of managing
+users, projects, and tools on Allura.  However, SourceForge has help docs that cover
+these functions https://sourceforge.net/p/forge/documentation/Docs%20Home/  Note
+that this documentation also covers some SourceForge features that are not part of Allura.
+
+
+Public API Documentation
+------------------------
+
+Allura's web api is currently documented at https://sourceforge.net/p/forge/documentation/Allura%20API/

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/getting_started/index.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/index.rst b/Allura/docs/getting_started/index.rst
new file mode 100644
index 0000000..6f59b58
--- /dev/null
+++ b/Allura/docs/getting_started/index.rst
@@ -0,0 +1,32 @@
+..     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.
+
+
+***************
+Getting Started
+***************
+
+.. toctree::
+    :maxdepth: 2
+
+    about
+    installation
+    using
+    administration
+    scm_host
+
+

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/getting_started/installation.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/installation.rst b/Allura/docs/getting_started/installation.rst
new file mode 100644
index 0000000..3d3321a
--- /dev/null
+++ b/Allura/docs/getting_started/installation.rst
@@ -0,0 +1,107 @@
+..     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.
+
+************
+Installation
+************
+
+
+Our step-by-step setup instructions are in our INSTALL.markdown file.  You can read it online at https://forge-allura.apache.org/p/allura/git/ci/master/tree/INSTALL.markdown  You should be able to get Allura up and running in well under an hour by following those instructions.
+
+For a faster and easier setup, see our `Vagrant/VirtualBox installation guide <https://forge-allura.apache.org/p/allura/wiki/Install%20and%20Run%20Allura%20-%20Vagrant/>`_
+
+Configuring Optional Features
+-----------------------------
+
+The `development.ini` file has many options you can explore and configure.  It is geared towards development, so you will want to review
+carefully and make changes for production use.
+
+To run SVN and Git services, see the :doc:`scm_host` page.
+
+Some features may be added as separate `Allura extensions <https://forge-allura.apache.org/p/allura/wiki/Extensions/>`_
+
+Enabling inbound email
+^^^^^^^^^^^^^^^^^^^^^^
+
+Allura can listen for email messages and update tools and artifacts.  For example, every ticket has an email address, and
+emails sent to that address will be added as comments on the ticket.  To set up the SMTP listener, run:
+
+.. code-block:: bash
+
+    nohup paster smtp_server development.ini > /var/log/allura/smtp.log &
+
+By default this uses port 8825.  Depending on your mail routing, you may need to change that port number.
+And if the port is in use, this command will fail.  You can check the log file for any errors.
+To change the port number, edit `development.ini` and change `forgemail.port` to the appropriate port number for your environment.
+
+SMTP in development
+^^^^^^^^^^^^^^^^^^^
+
+The following command can be used for quick and easy monitoring of smtp during development.
+Just be sure the port matches the `smtp_port` from your `development.ini` (8826 by default).
+
+.. code-block:: bash
+
+    python -m smtpd -n -c DebuggingServer localhost:8826
+
+This will create a new debugging server that discards messages and prints them to stdout.
+
+
+Using LDAP
+^^^^^^^^^^
+
+Allura has a pluggable authentication system, and can use an existing LDAP system. In your config
+file (e.g. :file:`development.ini`), there are several "ldap" settings to set:
+
+* Change auth.method to: :samp:`auth.method = ldap`
+* Set all the :samp:`auth.ldap.{*}` settings to match your LDAP server configuration. (:samp:`auth.ldap.schroot_name` won't be
+  used, don't worry about it.)
+* Keep :samp:`auth.ldap.autoregister = true` This means Allura will use existing users from your LDAP
+  server.
+* Set :samp:`auth.allow_user_registration = false` since your users already are present in LDAP.
+* Change user_prefs_storage.method to :samp:`user_prefs_storage.method = ldap`
+* Change :samp:`user_prefs_storage.ldap.fields.display_name` if needed (e.g. if display names are stored
+  in a different LDAP attribute).
+
+Restart Allura and you should be all set.  Now users can log in with their LDAP credentials and their
+Allura records will be automatically created the first time they log in.
+
+Note: if you want users to register new accounts into your LDAP system via Allura, you should turn
+off :samp:`autoregister` and turn on :samp:`allow_user_registration`
+
+Enabling RabbitMQ
+^^^^^^^^^^^^^^^^^
+
+For faster notification of background jobs, you can use RabbitMQ.  Assuming a base setup from the INSTALL, run these commands
+to install rabbitmq and set it up:
+
+.. code-block:: bash
+
+    sudo aptitude install rabbitmq-server
+    sudo rabbitmqctl add_user testuser testpw
+    sudo rabbitmqctl add_vhost testvhost
+    sudo rabbitmqctl set_permissions -p testvhost testuser ""  ".*" ".*"
+    pip install amqplib==0.6.1 kombu==1.0.4
+
+Then edit Allura/development.ini and change `amqp.enabled = false` to `amqp.enabled = true` and uncomment the other `amqp` settings.
+
+If your `paster taskd` process is still running, restart it:
+
+.. code-block:: bash
+
+    pkill -f taskd
+    nohup paster taskd development.ini > /var/log/allura/taskd.log &
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/getting_started/scm_host.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/scm_host.rst b/Allura/docs/getting_started/scm_host.rst
new file mode 100644
index 0000000..0e91a15
--- /dev/null
+++ b/Allura/docs/getting_started/scm_host.rst
@@ -0,0 +1,241 @@
+..     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.
+
+.. _scm_hosting:
+
+**************
+SCM Host Setup
+**************
+
+
+Git and Subversion Hosting Installation
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Allura can manage and display Git and SVN repositories, but it doesn't
+automatically run the git and svn services for you.  Here we'll describe how
+to set up standard git and svn services to work with Allura, so that you can
+checkout and commit code with those repositories.  The instructions here assume
+an Ubuntu system, but should be similar on other systems.
+
+.. note::
+
+    For developing with Allura or simple testing of Allura, you do not need to run
+    these services.  You can use local filesystem access to git and svn, which
+    works with no additional configuration.
+
+Git
+---
+
+We'll cover the basics to get you going.  For additional options and details,
+see http://git-scm.com/docs/git-http-backend and http://git-scm.com/book/en/Git-on-the-Server
+and subsequent chapters.
+
+.. code-block:: bash
+
+    sudo chmod 775 /srv/*  # make sure apache can read the repo dirs
+    sudo apt-get install apache2
+    sudo a2enmod proxy rewrite
+    sudo vi /etc/apache2/sites-available/default
+
+And add the following text within the :code:`<VirtualHost>` block:
+
+.. code-block:: apache
+
+    SetEnv GIT_PROJECT_ROOT /srv/git
+    SetEnv GIT_HTTP_EXPORT_ALL
+    ProxyPass /git/ !
+    ScriptAlias /git/ /usr/lib/git-core/git-http-backend/
+
+    # no authentication required at all - for testing purposes
+    SetEnv REMOTE_USER=git-allura
+
+Then exit vim (:kbd:`<esc> :wq`) and run:
+
+.. code-block:: shell-session
+
+    sudo service apache2 reload
+
+To test that it's working, run: :command:`git ls-remote http://localhost/git/p/test/git/`
+(if using Vagrant, you may also use :code:`localhost:8088` from your host machine).
+If there is no output, that is fine (it's an empty repo).
+
+.. warning::
+
+    This configuration has no authentication and is suitable for development only.  See :ref:`below <auth_apache>` for auth config.
+
+Now you will want to change the :samp:`scm.host.{*}.git`
+settings in :file:`development.ini`, so that the proper commands are shown to your visitors
+when they browse the code repo web pages.  The exact values to use will depend on the
+hostnames and port numbers you are using.
+
+Read-only `git://`
+^^^^^^^^^^^^^^^^^^
+If you want to run a separate readonly git service, using the git protocol instead of http,
+run: :program:`git daemon --reuseaddr --export-all --base-path=/srv/git /srv/git`  It can
+be accessed at :code:`git://localhost/p/test/git`
+
+
+Subversion
+----------
+
+These instructions will cover the recommended easiest way to run Subversion with Allura.
+For an overview of other options, see http://svnbook.red-bean.com/en/1.8/svn.serverconfig.choosing.html
+and subsequent chapters.
+
+.. code-block:: bash
+
+    sudo chown allura:allura /srv/svn  # or other user, as needed (e.g. "vagrant")
+
+    cat > /srv/svn/svnserve.conf <<EOF
+    [general]
+    realm = My Site SVN
+    # no authentication required at all - for testing purposes
+    anon-access = write
+    EOF
+
+    svnserve -d -r /srv/svn --log-file /tmp/svnserve.log --config-file /srv/svn/svnserve.conf
+
+Test by running: :command:`svn info svn://localhost/p/test/code/`.  If you need to kill it,
+run :command:`killall svnserve`  More info at http://svnbook.red-bean.com/en/1.8/svn.serverconfig.svnserve.html
+
+.. warning::
+
+    This configuration has no authentication and is suitable for development only.
+    (Maybe Allura could gain SASL support someday and use `svnserve with SASL <http://svnbook.red-bean.com/en/1.7/svn.serverconfig.svnserve.html#svn.serverconfig.svnserve.sasl>`_)
+
+Now you will want to change the :samp:`scm.host.{*}.svn`
+settings in :file:`development.ini`, so that the proper commands are shown to your visitors
+when they browse the code repo web pages.
+
+Alternate Setup with HTTP
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To use SVN over HTTP, you will need to patch and compile an Apache module, so
+that all svn repos can be dynamically served.
+
+.. warning::
+
+    Not easy.
+
+.. code-block:: console
+
+    sudo aptitude install libapache2-svn
+
+Test accessing http://localhost/ (`localhost:8088` if using Vagrant).
+
+Now we'll configure Apache to serve a single project's repositories and make sure
+that works.
+
+.. code-block:: console
+
+    sudo vi /etc/apache2/mods-available/dav_svn.conf
+
+Uncomment and change to :code:`<Location /svn/p/test>`.  Set
+:code:`SVNParentPath /srv/svn/p/test`  Then run:
+
+.. code-block:: console
+
+    sudo service apache2 reload
+
+Test at http://localhost/svn/p/test/code/ (`localhost:8088` if using Vagrant)
+
+That configuration works only for the repositories in a single project.  You must either
+create a new configuration for each project within Allura, or compile a patch
+to make `SVNParentPath` be recursive.  The patch is at http://pastie.org/8550810
+and must be applied to the source of Subversion 1.7's mod_dav_svn and then
+recompiled and installed.  (See http://subversion.tigris.org/issues/show_bug.cgi?id=3588
+for the request to include this patch in Subversion itself).  Once that is working,
+you can modify :file:`dav_svn.conf` to look like:
+
+.. code-block:: apache
+
+    <Location /svn>
+      DAV svn
+      SVNParentPath /srv/svn
+      ...
+
+Then Apache SVN will serve repositories for all Allura projects and subprojects.
+
+.. warning::
+
+    This configuration has no authentication and is suitable for development only.  See :ref:`the next section <auth_apache>` for auth config.
+
+
+.. _auth_apache:
+
+Configuring Auth with Apache
+----------------------------
+
+This is the easiest way to integrate authentication and authorization for SCM access with Allura.  It uses
+mod_python and the handler in :file:`scripts/ApacheAccessHandler.py` to query Allura directly
+for auth and permissions before allowing access to the SCM.  Of course, this only works
+for SCM access over HTTP(S).
+
+First, you need to ensure that mod_python is installed:
+
+.. code-block:: console
+
+    sudo aptitude install libapache2-mod-python
+
+Then, in the VirtualHost section where you proxy SCM requests to git, SVN, or Hg, add the
+access handler, e.g.:
+
+.. code-block:: console
+
+    sudo vi /etc/apache2/sites-available/default
+
+.. code-block:: apache
+
+    <LocationMatch "^/(git|svn|hg)/">
+        AddHandler mod_python .py
+        # Change this path if needed:
+        PythonAccessHandler /home/vagrant/src/allura/scripts/ApacheAccessHandler.py
+        AuthType Basic
+        AuthName "SCM Access"
+        AuthBasicAuthoritative off
+        # Change this path if needed:
+        PythonOption ALLURA_VIRTUALENV /home/vagrant/env-allura
+        # This routes back to the allura webapp, port 8080 if running with paster server (~/start_allura)
+        # In a production environment, run allura with a real WSGI server, and
+        # change the IP address and port number as appropriate.
+        # And use https if possible, since the username and password are otherwise
+        # sent in the clear to Allura.
+        PythonOption ALLURA_AUTH_URL http://127.0.0.1:8080/auth/do_login
+        PythonOption ALLURA_PERM_URL http://127.0.0.1:8080/auth/repo_permissions
+    </LocationMatch>
+
+.. code-block:: console
+
+    sudo service apache2 reload
+
+To test that it's working, run: :command:`git ls-remote
+http://localhost/git/p/test/git/` (if using Vagrant, use :code:`localhost:8088`
+from your host machine). If there is no output, that is fine (it's an empty
+repo). If it errors, look in :file:`/var/log/apache2/error.log` for the error
+message.
+
+.. warning::
+
+    Currently, for Mercurial, the handler doesn't correctly distinguish read
+    and write requests and thus requires WRITE permission for every request.
+    See ticket #7288
+
+
+Advanced Alternative
+--------------------
+
+An advanced alternative for SCM hosting using :ref:`SSH, LDAP, and a FUSE driver <scm_hosting_ssh>` is available.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/getting_started/scm_host_ssh.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/scm_host_ssh.rst b/Allura/docs/getting_started/scm_host_ssh.rst
new file mode 100644
index 0000000..bba57cf
--- /dev/null
+++ b/Allura/docs/getting_started/scm_host_ssh.rst
@@ -0,0 +1,203 @@
+..     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.
+
+:orphan:
+
+.. _scm_hosting_ssh:
+
+***************
+SCM Hosting-SSH
+***************
+
+Configuring Git/SVN/Hg to use Allura auth via LDAP and ssh
+==========================================================
+
+The following instructions will use a chroot, a custom FUSE driver, and LDAP.
+Once completed, an ssh-based configuration of Git, SVN, or Hg that has repos in
+the chroot directory will authenticate the users against LDAP and authorize via an Allura API.
+Allura will be configured to authenticate against LDAP as well.
+
+.. note::
+
+    The previous git & svn configuration instructions are not ssh-based, so will not work with this configuration.
+    You'll have to reconfigure git & svn to use ssh:// instead of http or svn protocols.
+
+We assume you are using a version of Ubuntu with
+support for schroot and debootstrap.  We will use a chroot jail to allow users to
+access their repositories via ssh.
+
+Install a chroot environment
+----------------------------
+
+These instructions are based on the documentation in `Debootstrap Chroot`_.  and `OpenLDAPServer`_.
+
+Install debootstrap and schroot: :program:`aptitude install debootstrap schroot`
+
+Append the following text to the file :file:`/etc/schroot/schroot.conf`
+
+.. code-block:: ini
+
+    [scm]
+    description=Ubuntu Chroot for SCM Hosting
+    type=directory
+    directory=/var/chroots/scm
+    script-config=scm/config
+
+Create a directory :file:`/etc/schroot/scm` and populate it with some files:
+
+.. code-block:: console
+
+    # mkdir /etc/schroot/scm
+    # cat > /etc/schroot/scm/config <<EOF
+    FSTAB="/etc/schroot/scm/fstab"
+    COPYFILES="/etc/schroot/scm/copyfiles"
+    NSSDATABASES="/etc/schroot/scm/nssdatabases"
+    EOF
+    # cat > /etc/schroot/scm/fstab <<EOF
+    /proc		/proc		none    rw,rbind        0       0
+    /sys		/sys		none    rw,rbind        0       0
+    /dev            /dev            none    rw,rbind        0       0
+    /tmp		/tmp		none	rw,bind		0	0
+    EOF
+    # cat > /etc/schroot/scm/copyfiles <<EOF
+    /etc/resolv.conf
+    EOF
+    # cat > /etc/schroot/scm/nssdatabases <<EOF
+    services
+    protocols
+    networks
+    hosts
+    EOF
+
+Create a directory :file:`/var/chroots/scm` and create the bootstrap environment.  (You may substitute a mirror from the  `ubuntu mirror list`_ for archive.ubuntu.com)
+
+.. code-block:: console
+
+    $ sudo mkdir -p /var/chroots/scm
+    $ sudo debootstrap --variant=buildd --arch amd64 --components=main,universe --include=git,mercurial,subversion,openssh-server,slapd,ldap-utils,ldap-auth-client,curl maverick /var/chroots/scm http://archive.ubuntu.com/ubuntu/
+
+Test that the chroot is installed by entering it:
+
+.. code-block:: console
+
+    # schroot -c scm -u root
+    (scm) # logout
+
+Configure OpenLDAP in the Chroot
+--------------------------------
+
+Copy the ldap-setup script into the chroot environment:
+
+.. code-block:: console
+
+    $ sudo cp Allura/ldap-setup.py Allura/ldap-userconfig.py /var/chroots/scm
+    $ sudo chmod +x /var/chroots/scm/ldap-*.py
+
+Log in to the chroot environment:
+
+.. code-block:: console
+
+    # schroot -c scm -u root
+
+Run the setup script, following the prompts:
+
+.. code-block:: console
+
+    (scm) # python /ldap-setup.py
+
+In particular, you will need to answer the following questions (substitute your custom suffix if you are not using dc=localdomain):
+
+* Should debconf manage LDAP configuration? **yes**
+* LDAP server Uniform Resource Identifier: **ldapi:///**
+* Distinguished name of the search base: **dc=localdomain**
+* LDAP version to use: **1** (version 3)
+* Make local root Database admin: **yes**
+* Does the LDAP database require login? **no**
+* LDAP account for root: **cn=admin,dc=localdomain**
+* LDAP root account password: *empty*
+* Local crypt to use when changing passwords: **2** (crypt)
+* PAM profiles to enable: **2**
+
+Update the chroot ssh configuration
+-----------------------------------
+
+Update the file :file:`/var/chroot/scm/etc/ssh/sshd_config`, changing the port directive:
+
+.. code-block:: guess
+
+    # Port 22
+    Port 8022
+
+Setup the Custom FUSE Driver
+----------------------------
+
+Copy the accessfs script into the chroot environment:
+
+.. code-block:: console
+
+    $ sudo cp fuse/accessfs.py /var/chroots/scm
+
+Configure allura to point to the chrooted scm environment:
+
+.. code-block:: console
+
+    $ sudo ln -s /var/chroots/scm /srv/git
+    $ sudo ln -s /var/chroots/scm /srv/hg
+    $ sudo ln -s /var/chroots/scm /srv/svn
+
+Log in to the chroot environment & install packages:
+
+.. code-block:: console
+
+    # schroot -c scm -u root
+    (scm) # apt-get install python-fuse
+
+Create the SCM directories:
+
+.. code-block:: console
+
+    (scm) # mkdir /scm /scm-repo
+
+Mount the FUSE filesystem:
+
+.. code-block:: console
+
+    (scm) # python /accessfs.py /scm-repo -o allow_other -s -o root=/scm
+
+Start the SSH daemon:
+
+.. code-block:: console
+
+    (scm) # /etc/init.d/ssh start
+
+Configure Allura to Use the LDAP Server
+---------------------------------------
+
+Set the following values in your .ini file:
+
+.. code-block:: ini
+
+    auth.method = ldap
+
+    auth.ldap.server = ldap://localhost
+    auth.ldap.suffix = ou=people,dc=localdomain
+    auth.ldap.admin_dn = cn=admin,dc=localdomain
+    auth.ldap.admin_password = secret
+
+.. _Debootstrap Chroot: https://help.ubuntu.com/community/DebootstrapChroot
+.. _OpenLDAPServer: https://help.ubuntu.com/10.10/serverguide/C/openldap-server.html
+.. _ubuntu mirror list: https://launchpad.net/ubuntu/+archivemirrors


[10/26] allura git commit: [#7832] ticket:731 Test fixes and some amends

Posted by je...@apache.org.
[#7832] ticket:731 Test fixes and some amends

- Raise 404 instead of 500 when no app name in _lookup in admin REST controller
- Add tests for bearer token via headers and update existing tests
- Don't test that webhook values was not changed: mim does not play nicely with
  it, it works with actual requests, though


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

Branch: refs/heads/ib/7830
Commit: f6074f23efa4fc1c5754acb32dd71b8b5ff2f43b
Parents: 5994229
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 23 12:57:44 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:53 2015 +0000

----------------------------------------------------------------------
 Allura/allura/ext/admin/admin_main.py       |  5 +-
 Allura/allura/tests/functional/test_rest.py | 59 ++++++++++++++++++++++++
 Allura/allura/tests/test_webhooks.py        |  4 --
 3 files changed, 63 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/f6074f23/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 d5ae068..b51c816 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -885,7 +885,10 @@ class ProjectAdminRestController(BaseController):
         }
 
     @expose()
-    def _lookup(self, name, *remainder):
+    def _lookup(self, *args):
+        if len(args) == 0:
+            raise exc.HTTPNotFound, args
+        name, remainder = args[0], args[1:]
         app = c.project.app_instance(name)
         if app is None or app.admin_api_root is None:
             raise exc.HTTPNotFound, name

http://git-wip-us.apache.org/repos/asf/allura/blob/f6074f23/Allura/allura/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_rest.py b/Allura/allura/tests/functional/test_rest.py
index 51ae930..fca1078 100644
--- a/Allura/allura/tests/functional/test_rest.py
+++ b/Allura/allura/tests/functional/test_rest.py
@@ -39,6 +39,7 @@ class TestRestHome(TestRestApiBase):
     @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
     @mock.patch('allura.controllers.rest.request')
     def test_bearer_token_non_bearer(self, request, OAuthAccessToken):
+        request.headers = {}
         request.params = {'access_token': 'foo'}
         request.scheme = 'https'
         self._patch_token(OAuthAccessToken)
@@ -51,6 +52,7 @@ class TestRestHome(TestRestApiBase):
     @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
     @mock.patch('allura.controllers.rest.request')
     def test_bearer_token_invalid(self, request, OAuthAccessToken):
+        request.headers = {}
         request.params = {'access_token': 'foo'}
         request.scheme = 'https'
         self._patch_token(OAuthAccessToken)
@@ -80,11 +82,68 @@ class TestRestHome(TestRestApiBase):
             is_bearer=True,
         )
         ThreadLocalODMSession.flush_all()
+        request.headers = {}
         request.params = {'access_token': access_token.api_key}
         request.scheme = 'https'
         r = self.api_post('/rest/p/test/wiki', access_token='foo')
         assert_equal(r.status_int, 200)
 
+    @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
+    @mock.patch('allura.controllers.rest.request')
+    def test_bearer_token_non_bearer_via_headers(self, request, OAuthAccessToken):
+        request.headers = {
+            'Authorization': 'OAuth BearerToken access_token=foo'
+        }
+        request.scheme = 'https'
+        self._patch_token(OAuthAccessToken)
+        access_token = OAuthAccessToken.query.get.return_value
+        access_token.is_bearer = False
+        r = self.api_post('/rest/p/test/wiki', access_token='foo')
+        assert_equal(r.status_int, 403)
+        OAuthAccessToken.query.get.assert_called_once_with(api_key='foo')
+
+    @mock.patch('allura.controllers.rest.M.OAuthAccessToken')
+    @mock.patch('allura.controllers.rest.request')
+    def test_bearer_token_invalid_via_headers(self, request, OAuthAccessToken):
+        request.headers = {
+            'Authorization': 'OAuth BearerToken access_token=foo'
+        }
+        request.scheme = 'https'
+        self._patch_token(OAuthAccessToken)
+        OAuthAccessToken.query.get.return_value = None
+        r = self.api_post('/rest/p/test/wiki', access_token='foo')
+        assert_equal(r.status_int, 403)
+
+    @mock.patch('allura.controllers.rest.request')
+    @td.with_wiki
+    def test_bearer_token_valid_via_headers(self, request):
+        user = M.User.by_username('test-admin')
+        consumer_token = M.OAuthConsumerToken(
+            name='foo',
+            description='foo app',
+        )
+        request_token = M.OAuthRequestToken(
+            consumer_token_id=consumer_token._id,
+            user_id=user._id,
+            callback='manual',
+            validation_pin=h.nonce(20),
+            is_bearer=True,
+        )
+        access_token = M.OAuthAccessToken(
+            consumer_token_id=consumer_token._id,
+            request_token_id=request_token._id,
+            user_id=user._id,
+            is_bearer=True,
+        )
+        ThreadLocalODMSession.flush_all()
+        token = access_token.api_key
+        request.headers = {
+            'Authorization': 'OAuth BearerToken access_token={}'.format(token)
+        }
+        request.scheme = 'https'
+        r = self.api_post('/rest/p/test/wiki', access_token='foo')
+        assert_equal(r.status_int, 200)
+
     def test_bad_path(self):
         r = self.api_post('/rest/1/test/wiki/')
         assert r.status_int == 404

http://git-wip-us.apache.org/repos/asf/allura/blob/f6074f23/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index a336f32..e86dbd8 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -868,16 +868,12 @@ class TestWebhookRestController(TestRestApiBase):
     def test_edit_duplicates(self):
         webhook = self.webhooks[0]
         url = '{}/repo-push/{}'.format(self.url, webhook._id)
-        # change only url
         data = {'url': 'http://httpbin.org/post/1'}
         r = self.api_post(url, status=400, **data)
         expected = {u'result': u'error',
                     u'error': u'_the_form: "repo-push" webhook already '
                               u'exists for Git http://httpbin.org/post/1'}
         assert_equal(r.json, expected)
-        webhook = M.Webhook.query.get(_id=webhook._id)
-        assert_equal(webhook.hook_url, 'http://httpbin.org/post/0')
-        assert_equal(webhook.secret, 'secret-0')
 
     def test_delete_validation(self):
         url = '{}/repo-push/invalid'.format(self.url)


[16/26] allura git commit: [#7835] Restructured the docs and updated the theme.

Posted by je...@apache.org.
http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/scm_host.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/scm_host.rst b/Allura/docs/scm_host.rst
deleted file mode 100644
index d3fe205..0000000
--- a/Allura/docs/scm_host.rst
+++ /dev/null
@@ -1,236 +0,0 @@
-..     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.
-
-.. _scm_hosting:
-
-Git and Subversion Hosting Installation
-=======================================
-
-Allura can manage and display Git and SVN repositories, but it doesn't
-automatically run the git and svn services for you.  Here we'll describe how
-to set up standard git and svn services to work with Allura, so that you can
-checkout and commit code with those repositories.  The instructions here assume
-an Ubuntu system, but should be similar on other systems.
-
-.. note::
-
-    For developing with Allura or simple testing of Allura, you do not need to run
-    these services.  You can use local filesystem access to git and svn, which
-    works with no additional configuration.
-
-Git
----
-
-We'll cover the basics to get you going.  For additional options and details,
-see http://git-scm.com/docs/git-http-backend and http://git-scm.com/book/en/Git-on-the-Server
-and subsequent chapters.
-
-.. code-block:: bash
-
-    sudo chmod 775 /srv/*  # make sure apache can read the repo dirs
-    sudo apt-get install apache2
-    sudo a2enmod proxy rewrite
-    sudo vi /etc/apache2/sites-available/default
-
-And add the following text within the :code:`<VirtualHost>` block:
-
-.. code-block:: apache
-
-    SetEnv GIT_PROJECT_ROOT /srv/git
-    SetEnv GIT_HTTP_EXPORT_ALL
-    ProxyPass /git/ !
-    ScriptAlias /git/ /usr/lib/git-core/git-http-backend/
-
-    # no authentication required at all - for testing purposes
-    SetEnv REMOTE_USER=git-allura
-
-Then exit vim (:kbd:`<esc> :wq`) and run:
-
-.. code-block:: shell-session
-
-    sudo service apache2 reload
-
-To test that it's working, run: :command:`git ls-remote http://localhost/git/p/test/git/`
-(if using Vagrant, you may also use :code:`localhost:8088` from your host machine).
-If there is no output, that is fine (it's an empty repo).
-
-.. warning::
-
-    This configuration has no authentication and is suitable for development only.  See :ref:`below <auth_apache>` for auth config.
-
-Now you will want to change the :samp:`scm.host.{*}.git`
-settings in :file:`development.ini`, so that the proper commands are shown to your visitors
-when they browse the code repo web pages.  The exact values to use will depend on the
-hostnames and port numbers you are using.
-
-Read-only `git://`
-^^^^^^^^^^^^^^^^^^
-If you want to run a separate readonly git service, using the git protocol instead of http,
-run: :program:`git daemon --reuseaddr --export-all --base-path=/srv/git /srv/git`  It can
-be accessed at :code:`git://localhost/p/test/git`
-
-
-Subversion
-----------
-
-These instructions will cover the recommended easiest way to run Subversion with Allura.
-For an overview of other options, see http://svnbook.red-bean.com/en/1.8/svn.serverconfig.choosing.html
-and subsequent chapters.
-
-.. code-block:: bash
-
-    sudo chown allura:allura /srv/svn  # or other user, as needed (e.g. "vagrant")
-
-    cat > /srv/svn/svnserve.conf <<EOF
-    [general]
-    realm = My Site SVN
-    # no authentication required at all - for testing purposes
-    anon-access = write
-    EOF
-
-    svnserve -d -r /srv/svn --log-file /tmp/svnserve.log --config-file /srv/svn/svnserve.conf
-
-Test by running: :command:`svn info svn://localhost/p/test/code/`.  If you need to kill it,
-run :command:`killall svnserve`  More info at http://svnbook.red-bean.com/en/1.8/svn.serverconfig.svnserve.html
-
-.. warning::
-
-    This configuration has no authentication and is suitable for development only.
-    (Maybe Allura could gain SASL support someday and use `svnserve with SASL <http://svnbook.red-bean.com/en/1.7/svn.serverconfig.svnserve.html#svn.serverconfig.svnserve.sasl>`_)
-
-Now you will want to change the :samp:`scm.host.{*}.svn`
-settings in :file:`development.ini`, so that the proper commands are shown to your visitors
-when they browse the code repo web pages.
-
-Alternate Setup with HTTP
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-To use SVN over HTTP, you will need to patch and compile an Apache module, so
-that all svn repos can be dynamically served.
-
-.. warning::
-
-    Not easy.
-
-.. code-block:: console
-
-    sudo aptitude install libapache2-svn
-
-Test accessing http://localhost/ (`localhost:8088` if using Vagrant).
-
-Now we'll configure Apache to serve a single project's repositories and make sure
-that works.
-
-.. code-block:: console
-
-    sudo vi /etc/apache2/mods-available/dav_svn.conf
-
-Uncomment and change to :code:`<Location /svn/p/test>`.  Set
-:code:`SVNParentPath /srv/svn/p/test`  Then run:
-
-.. code-block:: console
-
-    sudo service apache2 reload
-
-Test at http://localhost/svn/p/test/code/ (`localhost:8088` if using Vagrant)
-
-That configuration works only for the repositories in a single project.  You must either
-create a new configuration for each project within Allura, or compile a patch
-to make `SVNParentPath` be recursive.  The patch is at http://pastie.org/8550810
-and must be applied to the source of Subversion 1.7's mod_dav_svn and then
-recompiled and installed.  (See http://subversion.tigris.org/issues/show_bug.cgi?id=3588
-for the request to include this patch in Subversion itself).  Once that is working,
-you can modify :file:`dav_svn.conf` to look like:
-
-.. code-block:: apache
-
-    <Location /svn>
-      DAV svn
-      SVNParentPath /srv/svn
-      ...
-
-Then Apache SVN will serve repositories for all Allura projects and subprojects.
-
-.. warning::
-
-    This configuration has no authentication and is suitable for development only.  See :ref:`the next section <auth_apache>` for auth config.
-
-
-.. _auth_apache:
-
-Configuring Auth with Apache
-----------------------------
-
-This is the easiest way to integrate authentication and authorization for SCM access with Allura.  It uses
-mod_python and the handler in :file:`scripts/ApacheAccessHandler.py` to query Allura directly
-for auth and permissions before allowing access to the SCM.  Of course, this only works
-for SCM access over HTTP(S).
-
-First, you need to ensure that mod_python is installed:
-
-.. code-block:: console
-
-    sudo aptitude install libapache2-mod-python
-
-Then, in the VirtualHost section where you proxy SCM requests to git, SVN, or Hg, add the
-access handler, e.g.:
-
-.. code-block:: console
-
-    sudo vi /etc/apache2/sites-available/default
-
-.. code-block:: apache
-
-    <LocationMatch "^/(git|svn|hg)/">
-        AddHandler mod_python .py
-        # Change this path if needed:
-        PythonAccessHandler /home/vagrant/src/allura/scripts/ApacheAccessHandler.py
-        AuthType Basic
-        AuthName "SCM Access"
-        AuthBasicAuthoritative off
-        # Change this path if needed:
-        PythonOption ALLURA_VIRTUALENV /home/vagrant/env-allura
-        # This routes back to the allura webapp, port 8080 if running with paster server (~/start_allura)
-        # In a production environment, run allura with a real WSGI server, and
-        # change the IP address and port number as appropriate.
-        # And use https if possible, since the username and password are otherwise
-        # sent in the clear to Allura.
-        PythonOption ALLURA_AUTH_URL http://127.0.0.1:8080/auth/do_login
-        PythonOption ALLURA_PERM_URL http://127.0.0.1:8080/auth/repo_permissions
-    </LocationMatch>
-
-.. code-block:: console
-
-    sudo service apache2 reload
-
-To test that it's working, run: :command:`git ls-remote
-http://localhost/git/p/test/git/` (if using Vagrant, use :code:`localhost:8088`
-from your host machine). If there is no output, that is fine (it's an empty
-repo). If it errors, look in :file:`/var/log/apache2/error.log` for the error
-message.
-
-.. warning::
-
-    Currently, for Mercurial, the handler doesn't correctly distinguish read
-    and write requests and thus requires WRITE permission for every request.
-    See ticket #7288
-
-
-Advanced Alternative
---------------------
-
-An advanced alternative for SCM hosting using :ref:`SSH, LDAP, and a FUSE driver <scm_hosting_ssh>` is available.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/scm_host_ssh.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/scm_host_ssh.rst b/Allura/docs/scm_host_ssh.rst
deleted file mode 100644
index 81b21d8..0000000
--- a/Allura/docs/scm_host_ssh.rst
+++ /dev/null
@@ -1,197 +0,0 @@
-..     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.
-
-.. _scm_hosting_ssh:
-
-Configuring Git/SVN/Hg to use Allura auth via LDAP and ssh
-==========================================================
-
-The following instructions will use a chroot, a custom FUSE driver, and LDAP.
-Once completed, an ssh-based configuration of Git, SVN, or Hg that has repos in
-the chroot directory will authenticate the users against LDAP and authorize via an Allura API.
-Allura will be configured to authenticate against LDAP as well.
-
-.. note::
-
-    The previous git & svn configuration instructions are not ssh-based, so will not work with this configuration.
-    You'll have to reconfigure git & svn to use ssh:// instead of http or svn protocols.
-
-We assume you are using a version of Ubuntu with
-support for schroot and debootstrap.  We will use a chroot jail to allow users to
-access their repositories via ssh.
-
-Install a chroot environment
-----------------------------
-
-These instructions are based on the documentation in `Debootstrap Chroot`_.  and `OpenLDAPServer`_.
-
-Install debootstrap and schroot: :program:`aptitude install debootstrap schroot`
-
-Append the following text to the file :file:`/etc/schroot/schroot.conf`
-
-.. code-block:: ini
-
-    [scm]
-    description=Ubuntu Chroot for SCM Hosting
-    type=directory
-    directory=/var/chroots/scm
-    script-config=scm/config
-
-Create a directory :file:`/etc/schroot/scm` and populate it with some files:
-
-.. code-block:: console
-
-    # mkdir /etc/schroot/scm
-    # cat > /etc/schroot/scm/config <<EOF
-    FSTAB="/etc/schroot/scm/fstab"
-    COPYFILES="/etc/schroot/scm/copyfiles"
-    NSSDATABASES="/etc/schroot/scm/nssdatabases"
-    EOF
-    # cat > /etc/schroot/scm/fstab <<EOF
-    /proc		/proc		none    rw,rbind        0       0
-    /sys		/sys		none    rw,rbind        0       0
-    /dev            /dev            none    rw,rbind        0       0
-    /tmp		/tmp		none	rw,bind		0	0
-    EOF
-    # cat > /etc/schroot/scm/copyfiles <<EOF
-    /etc/resolv.conf
-    EOF
-    # cat > /etc/schroot/scm/nssdatabases <<EOF
-    services
-    protocols
-    networks
-    hosts
-    EOF
-
-Create a directory :file:`/var/chroots/scm` and create the bootstrap environment.  (You may substitute a mirror from the  `ubuntu mirror list`_ for archive.ubuntu.com)
-
-.. code-block:: console
-
-    $ sudo mkdir -p /var/chroots/scm
-    $ sudo debootstrap --variant=buildd --arch amd64 --components=main,universe --include=git,mercurial,subversion,openssh-server,slapd,ldap-utils,ldap-auth-client,curl maverick /var/chroots/scm http://archive.ubuntu.com/ubuntu/
-
-Test that the chroot is installed by entering it:
-
-.. code-block:: console
-
-    # schroot -c scm -u root
-    (scm) # logout
-
-Configure OpenLDAP in the Chroot
---------------------------------
-
-Copy the ldap-setup script into the chroot environment:
-
-.. code-block:: console
-
-    $ sudo cp Allura/ldap-setup.py Allura/ldap-userconfig.py /var/chroots/scm
-    $ sudo chmod +x /var/chroots/scm/ldap-*.py
-
-Log in to the chroot environment:
-
-.. code-block:: console
-
-    # schroot -c scm -u root
-
-Run the setup script, following the prompts:
-
-.. code-block:: console
-
-    (scm) # python /ldap-setup.py
-
-In particular, you will need to answer the following questions (substitute your custom suffix if you are not using dc=localdomain):
-
-* Should debconf manage LDAP configuration? **yes**
-* LDAP server Uniform Resource Identifier: **ldapi:///**
-* Distinguished name of the search base: **dc=localdomain**
-* LDAP version to use: **1** (version 3)
-* Make local root Database admin: **yes**
-* Does the LDAP database require login? **no**
-* LDAP account for root: **cn=admin,dc=localdomain**
-* LDAP root account password: *empty*
-* Local crypt to use when changing passwords: **2** (crypt)
-* PAM profiles to enable: **2**
-
-Update the chroot ssh configuration
------------------------------------
-
-Update the file :file:`/var/chroot/scm/etc/ssh/sshd_config`, changing the port directive:
-
-.. code-block:: guess
-
-    # Port 22
-    Port 8022
-
-Setup the Custom FUSE Driver
-----------------------------
-
-Copy the accessfs script into the chroot environment:
-
-.. code-block:: console
-
-    $ sudo cp fuse/accessfs.py /var/chroots/scm
-
-Configure allura to point to the chrooted scm environment:
-
-.. code-block:: console
-
-    $ sudo ln -s /var/chroots/scm /srv/git
-    $ sudo ln -s /var/chroots/scm /srv/hg
-    $ sudo ln -s /var/chroots/scm /srv/svn
-
-Log in to the chroot environment & install packages:
-
-.. code-block:: console
-
-    # schroot -c scm -u root
-    (scm) # apt-get install python-fuse
-
-Create the SCM directories:
-
-.. code-block:: console
-
-    (scm) # mkdir /scm /scm-repo
-
-Mount the FUSE filesystem:
-
-.. code-block:: console
-
-    (scm) # python /accessfs.py /scm-repo -o allow_other -s -o root=/scm
-
-Start the SSH daemon:
-
-.. code-block:: console
-
-    (scm) # /etc/init.d/ssh start
-
-Configure Allura to Use the LDAP Server
----------------------------------------
-
-Set the following values in your .ini file:
-
-.. code-block:: ini
-
-    auth.method = ldap
-
-    auth.ldap.server = ldap://localhost
-    auth.ldap.suffix = ou=people,dc=localdomain
-    auth.ldap.admin_dn = cn=admin,dc=localdomain
-    auth.ldap.admin_password = secret
-
-.. _Debootstrap Chroot: https://help.ubuntu.com/community/DebootstrapChroot
-.. _OpenLDAPServer: https://help.ubuntu.com/10.10/serverguide/C/openldap-server.html
-.. _ubuntu mirror list: https://launchpad.net/ubuntu/+archivemirrors

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/tutorials/testing.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/tutorials/testing.rst b/Allura/docs/tutorials/testing.rst
deleted file mode 100644
index 646a24e..0000000
--- a/Allura/docs/tutorials/testing.rst
+++ /dev/null
@@ -1,76 +0,0 @@
-..     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.
-
-Writing Tests for Allura Tools
-==============================
-
-Testing the controllers and models of an Allura tool is fairly
-straightforward.  Generally, you should follow the example of tests in the
-`allura/tests/functional` directory for controller tests and
-`allura.tests.model` for model tests.  For functional tests, the Allura platform
-provides a convenient "test harness" :class:`allura.controllers.test.TestController` controller
-class which is used as the application root for the
-:class:`allura.tests.TestController` class.
-
-In order to test your new tool controllers, you simply need to use the `self.app.get()`
-and `self.app.post()` methods of your test controller.  The test harness makes
-all the tools available in the system available under the URL /*entry point
-name*/.  So to test the :mod:`allura.ext.project_home` tool, for instance, we
-need only write the following::
-
-    from allura.tests import TestController
-
-    class TestProjectHome(TestController):
-
-        def test_home(self):
-            r = self.app.get('/home/')
-
-Whenever you use the :class:`allura.tests.TestController` app property, the
-test harness sets up the context so that `c.project` is always the
-`projects/test` project and whichever tool name you request is mounted at its
-entry point (so the Wiki tool will be mounted at /Wiki/).  `c.user` is always
-set to the `test-admin` user to avoid authentication issues.
-
-The framework used to generate the WSGI environment for testing your tools is
-provided by the `WebTest <http://pythonpaste.org/webtest/>`_ module, where you can
-find further documentation for the `.get()` and `.post()` methods.
-
-Testing Allura models is also straightforward, though you will often
-need to setup pylons global objects before your test. If the code under test
-uses pylons globals (like `g` and `c`), but your test doesn't require the
-fully-loaded wsgi app, you can do something like this:
-
-.. code-block:: python
-
-    from pylons import tmpl_context as c
-
-    from alluratest.controller import setup_unit_test
-    from allura.lib import helpers a h
-    from allura import model as M
-
-    def setUp():
-        # set up pylons globals
-        setup_unit_test()
-
-        # set c.project and c.app
-        h.set_context('test', 'wiki', neighborhood='Projects'):
-        c.user = M.User.query.get(username='test-admin')
-
-Testing the tasks and events is  similar to testing models.  Generally, you will
-simply want to call your `@task` and `@event_handler` methods directly rather
-than setting up a full mocking infrastructure, though it is possible to use the
-MonQTask model in the allura model if you wish to do more functional/integration testing.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/using.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/using.rst b/Allura/docs/using.rst
deleted file mode 100644
index d9be6fe..0000000
--- a/Allura/docs/using.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-..     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.
-
-We don't have much end-user help for Allura yet.  SourceForge projects use Allura,
-though, so their support documentation may be useful to anyone using Allura:
-
-Configuring your project
-------------------------
-
-See SourceForge help page: https://sourceforge.net/p/forge/documentation/Create%20a%20New%20Project/
-
-Note there are some SourceForge-specific references that don't apply to other Allura instances.
-
-
-Using tickets
--------------
-
-See SourceForge help page: https://sourceforge.net/p/forge/documentation/Tickets/
-
-
-Using the wiki
---------------
-
-See SourceForge help page: https://sourceforge.net/p/forge/documentation/Wiki/
-
-
-Using a discussion forum
-------------------------
-
-See SourceForge help page: https://sourceforge.net/p/forge/documentation/Discussion/
-
-
-Adding an external link
------------------------
-
-See SourceForge help page: https://sourceforge.net/p/forge/documentation/External%20Link/
-
-
-Using markdown syntax
----------------------
-
-Everything in Allura uses Markdown formatting, with several customizations and macros
-specifically for Allura.  There are "Formatting Help" buttons throughout Allura for
-easy reference to the Markdown syntax.  One such page is https://forge-allura.apache.org/p/allura/wiki/markdown_syntax/
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/INSTALL.markdown
----------------------------------------------------------------------
diff --git a/INSTALL.markdown b/INSTALL.markdown
index f552a2a..220d11f 100644
--- a/INSTALL.markdown
+++ b/INSTALL.markdown
@@ -126,7 +126,7 @@ The default configuration stores repos in `/srv`, so we need to create those dir
 
 If you don't have `sudo` permission or just want to store them somewhere else, change the `/srv` paths in `development.ini`
 
-If you want to set up remote access to the repositories, see <http://forge-allura.apache.org/docs/scm_host.html>
+If you want to set up remote access to the repositories, see <http://forge-allura.apache.org/docs/getting_started/scm_host.html>
 
 ### Allura task processing
 
@@ -159,7 +159,7 @@ register a new project in your own forge, visit /p/add_project
 ## Extra
 
 * Read more documentation: <http://forge-allura.apache.org/docs/>
-    * Including how to enable extra features: <http://forge-allura.apache.org/docs/installation.html>
+    * Including how to enable extra features: <http://forge-allura.apache.org/docs/getting_started/installation.html>
 * Ask questions and discuss Allura on the <http://mail-archives.apache.org/mod_mbox/allura-dev/>
 * Run the test suite (slow): `$ ALLURA_VALIDATION=none ./run_tests`
 * File bug reports at <https://forge-allura.apache.org/p/allura/tickets/new/> (login required)

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/requirements.txt
----------------------------------------------------------------------
diff --git a/requirements.txt b/requirements.txt
index 5e67556..6de0f60 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -79,3 +79,4 @@ WebTest==1.4.0
 testfixtures==3.0.0
 q==2.3
 WebError==0.10.3
+sphinx-rtd-theme==0.1.6


[25/26] allura git commit: [#7830] ticket:744 Use full_fs_path instead of clone_url for merge

Posted by je...@apache.org.
[#7830] ticket:744 Use full_fs_path instead of clone_url for merge


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

Branch: refs/heads/ib/7830
Commit: 3f2acda45c85b4f296c1f3396c2c56e3f88a1987
Parents: 5f206d6
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Mar 6 10:08:34 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Mar 6 10:54:22 2015 +0000

----------------------------------------------------------------------
 ForgeGit/forgegit/model/git_repo.py              | 6 +++---
 ForgeGit/forgegit/tests/model/test_repository.py | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/3f2acda4/ForgeGit/forgegit/model/git_repo.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/model/git_repo.py b/ForgeGit/forgegit/model/git_repo.py
index 831da5b..62e0604 100644
--- a/ForgeGit/forgegit/model/git_repo.py
+++ b/ForgeGit/forgegit/model/git_repo.py
@@ -103,7 +103,7 @@ class Repository(M.Repository):
         g = self._impl._git.git
         # http://stackoverflow.com/a/6283843
         # fetch source branch
-        g.fetch(mr.downstream_repo_url, mr.source_branch)
+        g.fetch(mr.downstream_repo.full_fs_path, mr.source_branch)
         # find merge base
         merge_base = g.merge_base(mr.downstream.commit_id, mr.target_branch)
         # print out merge result, but don't actually touch anything
@@ -116,13 +116,13 @@ class Repository(M.Repository):
         # can't merge in bare repo, so need to clone
         tmp_path = tempfile.mkdtemp()
         tmp_repo = git.Repo.clone_from(
-            self.clone_url('rw'),
+            self.full_fs_path,
             to_path=tmp_path,
             bare=False)
         tmp_repo = GitImplementation(Object(full_fs_path=tmp_path))._git
         tmp_repo.git.fetch('origin', mr.target_branch)
         tmp_repo.git.checkout(mr.target_branch)
-        tmp_repo.git.fetch(mr.downstream_repo_url, mr.source_branch)
+        tmp_repo.git.fetch(mr.downstream_repo.full_fs_path, mr.source_branch)
         tmp_repo.git.merge(mr.downstream.commit_id)
         tmp_repo.git.push('origin', mr.target_branch)
         shutil.rmtree(tmp_path, ignore_errors=True)

http://git-wip-us.apache.org/repos/asf/allura/blob/3f2acda4/ForgeGit/forgegit/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index d79fc7b..325a6ea 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -593,7 +593,7 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         assert_equals(payload, expected_payload)
 
     def test_can_merge(self):
-        mr = mock.Mock(downstream_repo_url='downstream-url',
+        mr = mock.Mock(downstream_repo=Object(full_fs_path='downstream-url'),
                        source_branch='source-branch',
                        target_branch='target-branch',
                        downstream=mock.Mock(commit_id='cid'))
@@ -615,7 +615,7 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
     @mock.patch('forgegit.model.git_repo.GitImplementation', autospec=True)
     @mock.patch('forgegit.model.git_repo.shutil', autospec=True)
     def test_merge(self, shutil, GitImplementation, git, tempfile):
-        mr = mock.Mock(downstream_repo_url='downstream-url',
+        mr = mock.Mock(downstream_repo=Object(full_fs_path='downstream-url'),
                        source_branch='source-branch',
                        target_branch='target-branch',
                        downstream=mock.Mock(commit_id='cid'))
@@ -623,7 +623,7 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
         self.repo._impl._git.git = _git
         self.repo.merge(mr)
         git.Repo.clone_from.assert_called_once_with(
-            self.repo.clone_url('rw'),
+            self.repo.full_fs_path,
             to_path=tempfile.mkdtemp.return_value,
             bare=False)
         tmp_repo = GitImplementation.return_value._git


[24/26] allura git commit: [#7830] ticket:733 Temporary fix for the case when downstream repo has no new commits from upstream

Posted by je...@apache.org.
[#7830] ticket:733 Temporary fix for the case when downstream repo has no new commits from upstream

In the case of Mercurial, log raises Exception. It might happen after automerge
of merge request. Will be fixed more rigorously in [#7836].


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

Branch: refs/heads/ib/7830
Commit: 5f206d61f5d116ddd9ab0f9654318a7254df3936
Parents: facc4c0
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 26 14:20:16 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Mar 6 09:43:10 2015 +0000

----------------------------------------------------------------------
 Allura/allura/model/repository.py | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/5f206d61/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index d866598..867a6d2 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -775,10 +775,16 @@ class MergeRequest(VersionedArtifact, ActivityObject):
                 commit = rev._id
             else:
                 commit = self.app.repo.head
-            return list(c.app.repo.log(
-                self.downstream.commit_id,
-                exclude=commit,
-                id_only=False))
+            try:
+                return list(c.app.repo.log(
+                    self.downstream.commit_id,
+                    exclude=commit,
+                    id_only=False))
+            except Exception:
+                log.exception(
+                    "Can't get commits for merge request",
+                    self.url())
+                return []
 
     @classmethod
     def upsert(cls, **kw):


[17/26] allura git commit: [#7835] Restructured the docs and updated the theme.

Posted by je...@apache.org.
http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/getting_started/using.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/getting_started/using.rst b/Allura/docs/getting_started/using.rst
new file mode 100644
index 0000000..84d9f93
--- /dev/null
+++ b/Allura/docs/getting_started/using.rst
@@ -0,0 +1,64 @@
+..     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.
+
+************
+Using Allura
+************
+
+
+We don't have much end-user help for Allura yet.  SourceForge projects use Allura,
+though, so their support documentation may be useful to anyone using Allura:
+
+
+Configuring your project
+------------------------
+
+See SourceForge help page: https://sourceforge.net/p/forge/documentation/Create%20a%20New%20Project/
+
+Note there are some SourceForge-specific references that don't apply to other Allura instances.
+
+
+Using tickets
+-------------
+
+See SourceForge help page: https://sourceforge.net/p/forge/documentation/Tickets/
+
+
+Using the wiki
+--------------
+
+See SourceForge help page: https://sourceforge.net/p/forge/documentation/Wiki/
+
+
+Using a discussion forum
+------------------------
+
+See SourceForge help page: https://sourceforge.net/p/forge/documentation/Discussion/
+
+
+Adding an external link
+-----------------------
+
+See SourceForge help page: https://sourceforge.net/p/forge/documentation/External%20Link/
+
+
+Using markdown syntax
+---------------------
+
+Everything in Allura uses Markdown formatting, with several customizations and macros
+specifically for Allura.  There are "Formatting Help" buttons throughout Allura for
+easy reference to the Markdown syntax.  One such page is https://forge-allura.apache.org/p/allura/wiki/markdown_syntax/

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/guides/email.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/guides/email.rst b/Allura/docs/guides/email.rst
deleted file mode 100644
index d88105e..0000000
--- a/Allura/docs/guides/email.rst
+++ /dev/null
@@ -1,69 +0,0 @@
-..     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.
-
-Guide to email integration in the Allura
-========================================
-
-Email routing
--------------
-
-routing mechanism will be a dotted path from the project to
-the application/tool to the specific artifact within that app that is
-used like this::
-
-    subproject.app.artifact@project.example.com
-
-Which would translate into the devtools project which is a subproject of
-turbogears, and it's bug tracker and ticket 142 in that tracker::
-
-    devtools.bug.142@turbogears.sf.net
-
-
-And it in turn would be published to the message bus, which will assure
-that all tools that are registered to be notified for that e-mail
-addresses are called like that.
-
-If your app has more than one artifact type, you could nest them inside
-`project.app.something.id.*`
-
-If you were working with the bug tracker directly on the TurboGears project::
-
-    bug.142@turbogears.sf.net
-
-The Allura platform allows you to setup other message types, such as commit
-messages, to go into amqp with the same routing information, and turn into
-"messages" just like e-mail.
-
-Email Content Handling
-----------------------
-
-On Allura message bodies should be composed as markdown.
-Multi-part mime encoded messages should be sent include plain text
-(the markdown) and html (rendered from the markdown).
-
-Users are allowed to register that they want plain text only.
-
-We will also include some text in the footer of the e-mail message with a
-link to the message online.   We can use this link to guess where in the
-thread the message belongs in the case of a messed up e-mail client that
-does not set the headers for the reply properly.
-
-The nice thing about this is that it's pretty much already implemented
-for us via the meta tool.
-
-This metadata syntax will let you set fields on tickets and otherwise
-interact with the system via e-mail, assuming you have such permissions.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/guides/message_bus.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/guides/message_bus.rst b/Allura/docs/guides/message_bus.rst
deleted file mode 100644
index cef23a0..0000000
--- a/Allura/docs/guides/message_bus.rst
+++ /dev/null
@@ -1,97 +0,0 @@
-..     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.
-
-Guide to the Allura task and event system
-=========================================
-
-Our event system is driven by a MongoDB-based queuing system, most of which you
-can ignore, because we've simplified it down to two ideas: *tasks* and *event handlers*.
-
-Glossary
---------
-
-Before we get into the details perhaps a few definitions are in order:
-
-* **app** -- tool for allura such as the tracker, scm, or wiki apps
-* **task** -- callable defined in a app that gets invoked by the `taskd` daemon
-* **event handler** -- callable defined in an app that gets called on message
-  events.  Event handlers are identified by a string name, so a single event can
-  fan out to multiple callables.
-
-Tasks
------
-
-The `MonQTask` class is central to the Allura asynchronous processing system.
-Simply put, a `MonQTask` is a document in MongoDB that contains a context
-(project/app/user), a function pointer (specified as a dotted string), and
-arguments to that function.  Tasks are scheduled by creating `MonQTask`
-documents, and the `taskd` daemon executes them as though they were happening in
-the web context.
-
-To simplify the use of tasks, Allura provides a decorator `@task` that marks a
-function as 'taskable.'  This decorator adds a `.post` method to the function
-object that allows the function to be scheduled as a MonQTask.  For instance, the
-`commit` task (flushing Solr caches) is defined in as the following::
-
-    @task
-    def commit():
-        g.solr.commit()
-
-In order to schedule this task for execution by taskd, simply use the `.post`
-method::
-
-    commit.post()
-
-If we wanted to call `commit` directly (e.g. in a test), we can still do that as
-well::
-
-    commit()
-
-.. _events:
-
-Events
-------
-
-Events provide fanout capability for messages, letting several functions get
-called in response to the same 'event.'  To note a function as an event handler,
-you use the `@event_handler` decorator.  For instance, there is an event handler
-on all project updates to subscribe the project's admins to project changes::
-
-    @event_handler('project_updated')
-    def subscribe_admins(topic):
-        c.app.subscribe_admins()
-
-In order to invoke all the event handlers for a particular topic, we use the
-`g.post_event` helper::
-
-    g.post_event('project_updated')
-
-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.
-
-Running the Task Daemon
------------------------
-
-In order to actually run the asynchronous tasks, we have written a paster command
-`taskd`.  This creates a configurable number of worker processes that watch for
-changes to the `MonQTask` collection and execute requested tasks.  `taskd` can be
-run on any server, but should have similar access to the MongoDB databases and
-configuration files used to run the web app server, as it tries to replicate the
-request context as closely as possible when running tasks.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/guides/permissions.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/guides/permissions.rst b/Allura/docs/guides/permissions.rst
deleted file mode 100644
index 713b08c..0000000
--- a/Allura/docs/guides/permissions.rst
+++ /dev/null
@@ -1,60 +0,0 @@
-..     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.
-
-Guide to Users, Groups and Permissions in Allura
-================================================
-
-User/Group model
-----------------
-
-In the allura system `users` can be assigned to various `groups` or
-roles on a per-project basis.
-
-Users can be members of many groups, and `groups` can
-be assigned a list of `permissions` like `edit`,
-`mdoderate` or `read`.   Tools can define their own
-set of permissions, for their artifacts.
-
-Individual artifacts and ACL's
-------------------------------
-
-You may want to assign a permission
-to particular people or roles for a specific `Artifact` such as
-one bug in the ticket tracker.  The Allura platform supports this via
-an additive ACL field on every `Artifact` instance.  It is not exposed
-via the UI currently.
-
-Permission hierarchy
---------------------
-
-Projects and subprojects can define user groups, but for any particular
-subproject the set of groups the user belongs to is additive.  This follows
-the basic principle that sub-project permissions and artifact permissions
-can *allow* additional access, but can't *restrict* it beyond
-what permissions are allowed by a higher level project.
-
-Permission predicates
----------------------
-
-Predicates are simple functions, several of which are defined in Allura
-itself, and which can be added by any tool, which return true if
-permission is granted, and false if it is not.
-
-An example predicate function `has_project_access` takes two params, an object
-and a `permission` string.  It then checks to see if the current user
-(picked up from the environment) has permission to perform that action on
-that object, following the rules above.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/guides/scm.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/guides/scm.rst b/Allura/docs/guides/scm.rst
deleted file mode 100644
index 9d8e987..0000000
--- a/Allura/docs/guides/scm.rst
+++ /dev/null
@@ -1,151 +0,0 @@
-..     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.
-
-Guide to SCM tools in Allura
-============================
-
-Overview
---------
-
-The interface API and most of the controller and view structure of
-code repository type apps is defined in the base classes in the
-Allura package, which consists of the classes in the following
-packages:
-
-* `allura.lib.repository` for the base application and implementation classes
-* `allura.controllers.repository` for the base controllers
-* `allura.model.repository` for the repo metadata models
-* `allura.model.repo_refresh` for the repo metadata refresh logic
-
-
-Application and Implementation
-------------------------------
-
-The `Application` structure for SCM apps follows the normal pattern for
-Allura applications, though they should inherit from
-`allura.lib.repository.RepositoryApp` instead of `allura.app.Application`.
-The apps are then responsible for implementing subclasses of
-`allura.lib.repository.Repository` and `allura.lib.repository.RepositoryImplementation`.
-
-The `Repository` subclass is responsible for implementing tool-specific
-logic using the metadata models and proxying the rest of its logic to the
-`RepositoryImplementation` subclass, which talks directly to the underlying
-SCM tool, such as `GitPython`, `Mercurial`, or `pysvn`.
-
-Historically, more was done in the `Repository` subclass using the metadata
-models, but we are trying to move away from those toward making the SCM apps
-thin wrappers around the underlying SCM tool (see `Indexless`_, below).
-
-
-Controller / View Dispatch
---------------------------
-
-All of the SCM apps use the base controllers in `allura.controllers.repository`
-with only minimal customization through subclassing (primarily to
-override the template for displaying instructions for setting up a newly
-created repository), so the dispatch for all SCM apps follows the same
-pattern.
-
-The root controller for SCM apps is `allura.controllers.repository.RepoRootController`.
-This controller has views for repo-level actions, such as forking and merging,
-and the SCM app attaches a refs and a commits (ci) controller to dispatch symbolic
-references and explicit commit IDs, respectively.  (This should be refactored to be
-done in the `RepoRootController` so that the dispatch can be followed more easily.)
-(Also, `ForgeSVN` actually eschews this class and uses `BranchBrowser` directly as
-its root, in order to tweak the URL slightly, but it monkeypatches the relevant
-views over, so the dispatch ends up working more or less the same.)
-
-The refs controller, `allura.controllers.repository.RefsController`, handles
-symbolic references, and in particular handles custom escape handling to detect
-what is part of the ref name vs part of the remainder of the URL to dispatch.
-This is then handed off to the `BranchBrowserClass` which is a pointer to
-the implementation within the specific SCM app which handles the empty
-repo instructions or hands it back to the generic commits controller.
-
-The commits controller, `allura.controllers.repository.CommitsController`,
-originally only handled explicit commit IDs, but was modified to allow for
-persistent symbolic refs in the URL ("/p/allura/git/ci/master/", e.g.),
-it was changed to have the same escape parsing logic as the refs controller.
-Regardless, it just parses out the reference / ID from the URL and hands
-off to the commit browser.
-
-The commit browser, `allura.controllers.repository.CommitBrowser`, holds
-the views related to a specific commit, such as viewing the commit details
-(message and changes), a log of the commit history starting with the commit,
-or creating a snapshot of the code as of the commit.  It also has a "tree"
-endpoint for browsing the file system tree as of the commit.
-
-The tree browser, `allura.controllers.repository.TreeBrowser`, holds the
-view for viewing the file system contents at a specific path for a given
-commit.  The only complication here is that, instead of parsing out the
-entire tree path at once, it recursively dispatches to itself to build
-up the path a piece at a time.  Tree browsing also depends on the
-`Last Commit Logic`_ to create the data needed to display the last
-commit that touched each file or directory within the given directory.
-
-
-Last Commit Logic
------------------
-
-Determining which commit was the last to touch a given set of files or
-directories can be complicated, depending on the specific SCM tool.
-Git and Mercurial require manually walking up the commit history to
-discover this information, while SVN can return it all from a singlle
-`info2` command (though the SVN call will be signficantly slower than
-any individual call to Git or Mercurial).  Because this can sometimes
-be costly to generate, it is cached via the `allura.model.repository.LastCommit`
-model.  This will generate the data on demand by calling the underlying
-SCM tool, if necessary, but the data is currently pre-generated during
-the post-push refresh logic for Git and Mercurial.
-
-The overall logic for generating this data for Git and Mercurial is as follows:
-
-1. All items modified in the current commit get their info from the
-   current commit
-
-2. The info for the remaining items is found:
-
- * If there is a `LastCommit` record for the parent directory, all of
-   the remaining items get their info from the previous `LastCommit`
-
- * Otherwise, the list of remaining items is sent to the SCM implementation,
-   which repeatedly asks the SCM for the last commit to touch any of the
-   items for which we are missing information, removing items from the list
-   as commits are reported as having modified them
-
-3. Once all of the items have information, or if a processing timeout is reached,
-   the gathered information is saved in the `LastCommit` model and returned
-
-
-
-Indexless
----------
-
-Currently, there are model classes which encapsulate SCM metadata
-(such as commits, file system structure, etc) in a generic (agnostic to
-the underlying tool implementation) way.  However, this means that we're
-duplicating in mongo a lot of data that is already tracked by the
-underlying SCM tool, and this data must also be indexed for new repos
-and after every subsequent push before the commits or files are browsable
-via the web interface.
-
-To minimize this duplication of data and reduce or eliminate the delay
-between commits being pushed and them being visible, we are trying to
-move toward a lightweight API layer that requests the data from the
-underlying SCM tool directly, with intelligent caching at the points
-and in the format that makes the most sense to make rendering the SCM
-pages as fast as possible.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/index.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/index.rst b/Allura/docs/index.rst
index 7cb4686..fe3a65b 100644
--- a/Allura/docs/index.rst
+++ b/Allura/docs/index.rst
@@ -20,79 +20,18 @@
    You can adapt this file completely to your liking, but it should at least
    contain the root `toctree` directive.
 
-Introduction
-============
 
-.. toctree::
-   :maxdepth: 2
-
-   intro
-
-Running Allura
-==============
-
-.. toctree::
-   :maxdepth: 2
-
-   installation
-   administration
-   scm_host
-
-Using Allura
-============
-
-.. toctree::
-   :maxdepth: 2
-
-   using
-
-Extending Allura
-================
-
-.. toctree::
-   :maxdepth: 3
-
-   extending
-
-* Writing an Allura-based app
-    * `Getting Started <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/06/part-1-getting-started/>`_
-    * `Forms, Artifacts, and Testing <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/06/part-2-creating-our-first-paste/>`_
-    * `Adding a Custom Icon <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/12/part-3-adding-a-custom-icon/>`_
-    * `Adding a Sidebar Menu <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/12/adding-a-sidebar-menu/>`_
-    * `Adding Custom CSS <https://sourceforge.net/u/vansteenburgh/allura-plugin-development/2013/12/part-5-adding-custom-css/>`_
-
-Developing Allura
-=================
-
-.. toctree::
-   :maxdepth: 3
-
-   contributing
-   platform
-   platform_tour
-   guides/message_bus
-   guides/email
-   guides/permissions
-   guides/scm
-   tutorials/testing
-
-
-API Documentation
-=================
-
-.. toctree::
-   :maxdepth: 2
-   :glob:
-
-   api/*
+*****************
+Table of contents
+*****************
 
-Background Info
-===============
 .. toctree::
-   :maxdepth: 1
+    :maxdepth: 3
 
-   faq
-   online
+    getting_started/index
+    platform/index
+    development/index
+    api/index
 
 Indices and tables
 ==================

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/installation.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/installation.rst b/Allura/docs/installation.rst
deleted file mode 100644
index ce26d06..0000000
--- a/Allura/docs/installation.rst
+++ /dev/null
@@ -1,108 +0,0 @@
-..     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.
-
-Installation
-============
-
-Install
--------
-
-Our step-by-step setup instructions are in our INSTALL.markdown file.  You can read it online at https://forge-allura.apache.org/p/allura/git/ci/master/tree/INSTALL.markdown  You should be able to get Allura up and running in well under an hour by following those instructions.
-
-For a faster and easier setup, see our `Vagrant/VirtualBox installation guide <https://forge-allura.apache.org/p/allura/wiki/Install%20and%20Run%20Allura%20-%20Vagrant/>`_
-
-Configuring Optional Features
------------------------------
-
-The `development.ini` file has many options you can explore and configure.  It is geared towards development, so you will want to review
-carefully and make changes for production use.
-
-To run SVN and Git services, see the :doc:`scm_host` page.
-
-Some features may be added as separate `Allura extensions <https://forge-allura.apache.org/p/allura/wiki/Extensions/>`_
-
-Enabling inbound email
-^^^^^^^^^^^^^^^^^^^^^^
-
-Allura can listen for email messages and update tools and artifacts.  For example, every ticket has an email address, and
-emails sent to that address will be added as comments on the ticket.  To set up the SMTP listener, run:
-
-.. code-block:: shell-session
-
-    (env-allura)~/src/forge/Allura$ nohup paster smtp_server development.ini > /var/log/allura/smtp.log &
-
-By default this uses port 8825.  Depending on your mail routing, you may need to change that port number.
-And if the port is in use, this command will fail.  You can check the log file for any errors.
-To change the port number, edit `development.ini` and change `forgemail.port` to the appropriate port number for your environment.
-
-SMTP in development
-^^^^^^^^^^^^^^^^^^^
-
-The following command can be used for quick and easy monitoring of smtp during development.
-Just be sure the port matches the `smtp_port` from your `development.ini` (8826 by default).
-
-.. code-block:: shell-session
-
-    (env-allura)~/src/forge/Allura$ python -m smtpd -n -c DebuggingServer localhost:8826
-
-This will create a new debugging server that discards messages and prints them to stdout.
-
-
-Using LDAP
-^^^^^^^^^^
-
-Allura has a pluggable authentication system, and can use an existing LDAP system. In your config
-file (e.g. :file:`development.ini`), there are several "ldap" settings to set:
-
-* Change auth.method to: :samp:`auth.method = ldap`
-* Set all the :samp:`auth.ldap.{*}` settings to match your LDAP server configuration. (:samp:`auth.ldap.schroot_name` won't be
-  used, don't worry about it.)
-* Keep :samp:`auth.ldap.autoregister = true` This means Allura will use existing users from your LDAP
-  server.
-* Set :samp:`auth.allow_user_registration = false` since your users already are present in LDAP.
-* Change user_prefs_storage.method to :samp:`user_prefs_storage.method = ldap`
-* Change :samp:`user_prefs_storage.ldap.fields.display_name` if needed (e.g. if display names are stored
-  in a different LDAP attribute).
-
-Restart Allura and you should be all set.  Now users can log in with their LDAP credentials and their
-Allura records will be automatically created the first time they log in.
-
-Note: if you want users to register new accounts into your LDAP system via Allura, you should turn
-off :samp:`autoregister` and turn on :samp:`allow_user_registration`
-
-Enabling RabbitMQ
-^^^^^^^^^^^^^^^^^
-
-For faster notification of background jobs, you can use RabbitMQ.  Assuming a base setup from the INSTALL, run these commands
-to install rabbitmq and set it up:
-
-.. code-block:: shell-session
-
-    (env-allura)~$ sudo aptitude install rabbitmq-server
-    (env-allura)~$ sudo rabbitmqctl add_user testuser testpw
-    (env-allura)~$ sudo rabbitmqctl add_vhost testvhost
-    (env-allura)~$ sudo rabbitmqctl set_permissions -p testvhost testuser ""  ".*" ".*"
-    (env-allura)~$ pip install amqplib==0.6.1 kombu==1.0.4
-
-Then edit Allura/development.ini and change `amqp.enabled = false` to `amqp.enabled = true` and uncomment the other `amqp` settings.
-
-If your `paster taskd` process is still running, restart it:
-
-.. code-block:: shell-session
-
-    (env-allura)~/src/forge/Allura$ pkill -f taskd
-    (env-allura)~/src/forge/Allura$ nohup paster taskd development.ini > /var/log/allura/taskd.log &
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/intro.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/intro.rst b/Allura/docs/intro.rst
deleted file mode 100644
index 341ec25..0000000
--- a/Allura/docs/intro.rst
+++ /dev/null
@@ -1,41 +0,0 @@
-..     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.
-
-Rather than build yet another forge, we decided to do something new.   We wanted to build a new kind of extensible forge, and a new set of highly integrated forge tools. 
-
-Allura is an **open** platform for **open** processes
------------------------------------------------------
-
-It's easy to get frustrated with existing development tools.   Too often they are overbearing, complex, and make assumptions that get in your way.  And even if they are open source, it's often difficult to get them to work the way you need them too. 
-
-Which is why we created Allura.   It's designed to be truly **open**, in many different senses of the word. 
-
-It's open in bunch of ways:
- 
-* It's a combination of tools available under *Free* or *Open Source* licenses. 
-* It's designed around a plugin architecture, and anybody willing to contribute a tool can play.
-* It's being hosted publicly, and is built through contributions of individuals and companies who want to promote Open Source development.
-* It's designed to provide a structure around which welcoming (open) communities can grow. 
-* Its core tools are designed around inclusive development processes.
-
-We looked at existing forges, but to achieve all those goals, we decided we needed to build something new.
-
-Allura is designed to support an **ecosystem**
-----------------------------------------------
-
-Allura is at once a **set of tools** to help people collaboratively develop software, and an **open platform** on which innovative new tools be built. 
-

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/online.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/online.rst b/Allura/docs/online.rst
deleted file mode 100644
index e68dd44..0000000
--- a/Allura/docs/online.rst
+++ /dev/null
@@ -1,54 +0,0 @@
-..     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.
-
-Online References
-=================
-
-Generated API docs, useful for browsing through the code, viewing inheritance, etc:
-
-* http://allura.sourceforge.net/epydoc/
-
-Our project page, including tickets, discussion forums, etc.:
-
-* https://allura.apache.org/
-
-
-Much of the current forge was inspired by Roundup
--------------------------------------------------
-
-http://roundup.sourceforge.net/index.html
-
-After we told ESR about roundup, he posted some interesting ideas about how to "federate" roundup entities "namespaces" that are ultimately just url-prefixes per project:
-
-http://esr.ibiblio.org/?p=1359
-
-Message based system needed
----------------------------
-
-http://ejohn.org/blog/google-groups-is-dead/
-
-
-Potential pitfalls for groupware development
---------------------------------------------
-
-http://research.microsoft.com/en-us/um/people/jgrudin/past/papers/cacm94/cacm94.html
-
-AMQP Online References
-----------------------
-
-http://blogs.digitar.com/jjww/2009/01/rabbits-and-warrens/
-http://www.igvita.com/2009/10/08/advanced-messaging-routing-with-amqp/

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/platform.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform.rst b/Allura/docs/platform.rst
deleted file mode 100644
index acf0124..0000000
--- a/Allura/docs/platform.rst
+++ /dev/null
@@ -1,118 +0,0 @@
-..     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.
-
-Platform Architecture overview
-==============================
-
-I'm told that the reason you build a platform is to "reduce the marginal cost
-of developing applications."  Sounds good.   Well, actually it sounds a bit
-dry.  But it's about right, we want to make creating new online development
-tools faster, easier, and more fun, which I guess is the "reduce the marginal
-cost" thing.
-
-Platform building blocks
-------------------------
-
-Before we get into the details of how to extend the Allura platform, perhaps
-it would be smart to explain some of the big pieces and why they are there.
-
-We wanted Allura tools to be fast, we needed them to scale, and we had some
-complex requirements for data storage and extensibility.  So, we needed a
-**fast,** flexible, and easy to use data persistence system.
-
-We were very impressed by the general message architecture of Roundup, but we
-wanted to extend it from just email messages to include scm commits, and we
-added a message bus (RabbitMQ which we'll talk about in a second), to make
-it fast.
-
-.. image:: _static/images/messages.png
-   :alt: Message Architecture
-
-We were also impressed by the flexibility of Roundup's Hypertable system in
-allowing for ad-hock ticket schema additions.
-
-It definitely seemed like something we wanted in a next generation forge,
-because we wanted app tools to be able to:
-
-* create and version their own document types,
-* extend existing document structures,
-* and to mange document revisions, access control lists, and other
-  platform level data.
-
-In spite of the power and flexibility of the Roundup HyperTable
-implementation, we had some concerns about performance and scalability.
-
-Fortunately several of the Allura authors used MongoDB
-in rewriting the download flow of SourceForge.net, and knew that it could
-handle huge loads (we saturated a 2gb network connection on the server
-with 6% cpu utilization).
-
-We also knew that MongoDB's flexible replication system would allow us
-to build the forge in such a way that we could easily provide a
-package of all project data to developers concerned about lock-in.
-
-Not only that but Rick Copeland had built a couple of custom Object
-*Non*-Relational Mappers (ONRMs?) before, including one for MongoDB,
-and he whipped up Ming, which backed on MongoDB and gave us exactly
-what we needed.
-
-As I mentioned before we also needed a fast, flexible message bus and queuing
-system. RabbitMQ was (lightning) fast, (shockingly) flexible, but not super
-easy to use. Fortunately we didn't have to roll our own wrapper here, as
-the Python community already whipped up Carrot, and Celery, which made
-working with the RabbitMQ based AMQP bus a LOT easer.
-
-
-Application Tools
------------------
-
-Writing a tool for Allura is as simple as defining a few controllers
-to handle particular URL's, templates to render pages, and defining the schemas
-of any Allura document types that your tool requires.
-
-.. image:: _static/images/tools.png
-   :alt: App Tools
-   :align: right
-
-When you write Allura tools, you'll get lots of stuff for free:
-
-* Search-ability of your Artifacts
-* Artifact versioning for accountability and transparency
-* Ability to extend existing Artifacts
-* Reuse central User/group/permission management
-* A robust and flexible permissions system
-* Access to a real-time event publishing system
-
-What's in a tool?
-~~~~~~~~~~~~~~~~~
-
-The most basic app tool consists of a few things:
-
-* A controller object (instantiated per request)
-* Template files (optional)
-* UI Widgets (optional)
-* Extensions to existing Artifacts (optional)
-* New Artifact types (optional)
-* Event listener tools (optional)
-* Event publisher (optional)
-
-Users/groups and Permissions
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In order to facilitate more open processes, where more users can contribute
--- while still protecting data -- documents can easily be "versioned", and
-the platform provides tools to manage versioned documents for you.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/platform/email.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform/email.rst b/Allura/docs/platform/email.rst
new file mode 100644
index 0000000..754a278
--- /dev/null
+++ b/Allura/docs/platform/email.rst
@@ -0,0 +1,71 @@
+..     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.
+
+*****
+Email
+*****
+
+
+Email routing
+-------------
+
+routing mechanism will be a dotted path from the project to
+the application/tool to the specific artifact within that app that is
+used like this::
+
+    subproject.app.artifact@project.example.com
+
+Which would translate into the devtools project which is a subproject of
+turbogears, and it's bug tracker and ticket 142 in that tracker::
+
+    devtools.bug.142@turbogears.sf.net
+
+
+And it in turn would be published to the message bus, which will assure
+that all tools that are registered to be notified for that e-mail
+addresses are called like that.
+
+If your app has more than one artifact type, you could nest them inside
+`project.app.something.id.*`
+
+If you were working with the bug tracker directly on the TurboGears project::
+
+    bug.142@turbogears.sf.net
+
+The Allura platform allows you to setup other message types, such as commit
+messages, to go into amqp with the same routing information, and turn into
+"messages" just like e-mail.
+
+Email Content Handling
+----------------------
+
+On Allura message bodies should be composed as markdown.
+Multi-part mime encoded messages should be sent include plain text
+(the markdown) and html (rendered from the markdown).
+
+Users are allowed to register that they want plain text only.
+
+We will also include some text in the footer of the e-mail message with a
+link to the message online.   We can use this link to guess where in the
+thread the message belongs in the case of a messed up e-mail client that
+does not set the headers for the reply properly.
+
+The nice thing about this is that it's pretty much already implemented
+for us via the meta tool.
+
+This metadata syntax will let you set fields on tickets and otherwise
+interact with the system via e-mail, assuming you have such permissions.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/platform/index.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform/index.rst b/Allura/docs/platform/index.rst
new file mode 100644
index 0000000..36fd94e
--- /dev/null
+++ b/Allura/docs/platform/index.rst
@@ -0,0 +1,30 @@
+..     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.
+
+********
+Platform
+********
+
+.. toctree::
+    :maxdepth: 2
+
+    platform
+    platform_tour
+    email
+    message_bus
+    permissions
+    scm

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/platform/message_bus.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform/message_bus.rst b/Allura/docs/platform/message_bus.rst
new file mode 100644
index 0000000..8211936
--- /dev/null
+++ b/Allura/docs/platform/message_bus.rst
@@ -0,0 +1,101 @@
+..     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.
+
+***********
+Message Bus
+***********
+
+Guide to the Allura task and event system
+=========================================
+
+Our event system is driven by a MongoDB-based queuing system, most of which you
+can ignore, because we've simplified it down to two ideas: *tasks* and *event handlers*.
+
+Glossary
+--------
+
+Before we get into the details perhaps a few definitions are in order:
+
+* **app** -- tool for allura such as the tracker, scm, or wiki apps
+* **task** -- callable defined in a app that gets invoked by the `taskd` daemon
+* **event handler** -- callable defined in an app that gets called on message
+  events.  Event handlers are identified by a string name, so a single event can
+  fan out to multiple callables.
+
+Tasks
+-----
+
+The `MonQTask` class is central to the Allura asynchronous processing system.
+Simply put, a `MonQTask` is a document in MongoDB that contains a context
+(project/app/user), a function pointer (specified as a dotted string), and
+arguments to that function.  Tasks are scheduled by creating `MonQTask`
+documents, and the `taskd` daemon executes them as though they were happening in
+the web context.
+
+To simplify the use of tasks, Allura provides a decorator `@task` that marks a
+function as 'taskable.'  This decorator adds a `.post` method to the function
+object that allows the function to be scheduled as a MonQTask.  For instance, the
+`commit` task (flushing Solr caches) is defined in as the following::
+
+    @task
+    def commit():
+        g.solr.commit()
+
+In order to schedule this task for execution by taskd, simply use the `.post`
+method::
+
+    commit.post()
+
+If we wanted to call `commit` directly (e.g. in a test), we can still do that as
+well::
+
+    commit()
+
+.. _events:
+
+Events
+------
+
+Events provide fanout capability for messages, letting several functions get
+called in response to the same 'event.'  To note a function as an event handler,
+you use the `@event_handler` decorator.  For instance, there is an event handler
+on all project updates to subscribe the project's admins to project changes::
+
+    @event_handler('project_updated')
+    def subscribe_admins(topic):
+        c.app.subscribe_admins()
+
+In order to invoke all the event handlers for a particular topic, we use the
+`g.post_event` helper::
+
+    g.post_event('project_updated')
+
+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.
+
+Running the Task Daemon
+-----------------------
+
+In order to actually run the asynchronous tasks, we have written a paster command
+`taskd`.  This creates a configurable number of worker processes that watch for
+changes to the `MonQTask` collection and execute requested tasks.  `taskd` can be
+run on any server, but should have similar access to the MongoDB databases and
+configuration files used to run the web app server, as it tries to replicate the
+request context as closely as possible when running tasks.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/platform/permissions.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform/permissions.rst b/Allura/docs/platform/permissions.rst
new file mode 100644
index 0000000..77b4a0f
--- /dev/null
+++ b/Allura/docs/platform/permissions.rst
@@ -0,0 +1,64 @@
+..     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.
+
+***********
+Permissions
+***********
+
+Guide to Users, Groups and Permissions in Allura
+================================================
+
+User/Group model
+----------------
+
+In the allura system `users` can be assigned to various `groups` or
+roles on a per-project basis.
+
+Users can be members of many groups, and `groups` can
+be assigned a list of `permissions` like `edit`,
+`mdoderate` or `read`.   Tools can define their own
+set of permissions, for their artifacts.
+
+Individual artifacts and ACL's
+------------------------------
+
+You may want to assign a permission
+to particular people or roles for a specific `Artifact` such as
+one bug in the ticket tracker.  The Allura platform supports this via
+an additive ACL field on every `Artifact` instance.  It is not exposed
+via the UI currently.
+
+Permission hierarchy
+--------------------
+
+Projects and subprojects can define user groups, but for any particular
+subproject the set of groups the user belongs to is additive.  This follows
+the basic principle that sub-project permissions and artifact permissions
+can *allow* additional access, but can't *restrict* it beyond
+what permissions are allowed by a higher level project.
+
+Permission predicates
+---------------------
+
+Predicates are simple functions, several of which are defined in Allura
+itself, and which can be added by any tool, which return true if
+permission is granted, and false if it is not.
+
+An example predicate function `has_project_access` takes two params, an object
+and a `permission` string.  It then checks to see if the current user
+(picked up from the environment) has permission to perform that action on
+that object, following the rules above.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/platform/platform.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform/platform.rst b/Allura/docs/platform/platform.rst
new file mode 100644
index 0000000..7e64755
--- /dev/null
+++ b/Allura/docs/platform/platform.rst
@@ -0,0 +1,137 @@
+..     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.
+
+*****************
+Platform Overview
+*****************
+
+
+
+I'm told that the reason you build a platform is to "reduce the marginal cost
+of developing applications."  Sounds good.   Well, actually it sounds a bit
+dry.  But it's about right, we want to make creating new online development
+tools faster, easier, and more fun, which I guess is the "reduce the marginal
+cost" thing.
+
+
+Platform building blocks
+------------------------
+
+Before we get into the details of how to extend the Allura platform, perhaps
+it would be smart to explain some of the big pieces and why they are there.
+
+We wanted Allura tools to be fast, we needed them to scale, and we had some
+complex requirements for data storage and extensibility.  So, we needed a
+**fast,** flexible, and easy to use data persistence system.
+
+We were very impressed by the general message architecture of Roundup, but we
+wanted to extend it from just email messages to include scm commits, and we
+added a message bus (RabbitMQ which we'll talk about in a second), to make
+it fast.
+
+.. image:: ../_static/images/messages.png
+   :alt: Message Architecture
+
+We were also impressed by the flexibility of Roundup's Hypertable system in
+allowing for ad-hock ticket schema additions.
+
+It definitely seemed like something we wanted in a next generation forge,
+because we wanted app tools to be able to:
+
+* create and version their own document types,
+* extend existing document structures,
+* and to mange document revisions, access control lists, and other
+  platform level data.
+
+In spite of the power and flexibility of the Roundup HyperTable
+implementation, we had some concerns about performance and scalability.
+
+Fortunately several of the Allura authors used MongoDB
+in rewriting the download flow of SourceForge.net, and knew that it could
+handle huge loads (we saturated a 2gb network connection on the server
+with 6% cpu utilization).
+
+We also knew that MongoDB's flexible replication system would allow us
+to build the forge in such a way that we could easily provide a
+package of all project data to developers concerned about lock-in.
+
+Not only that but Rick Copeland had built a couple of custom Object
+*Non*-Relational Mappers (ONRMs?) before, including one for MongoDB,
+and he whipped up Ming, which backed on MongoDB and gave us exactly
+what we needed.
+
+As I mentioned before we also needed a fast, flexible message bus and queuing
+system. RabbitMQ was (lightning) fast, (shockingly) flexible, but not super
+easy to use. Fortunately we didn't have to roll our own wrapper here, as
+the Python community already whipped up Carrot, and Celery, which made
+working with the RabbitMQ based AMQP bus a LOT easer.
+
+
+Application Tools
+-----------------
+
+Writing a tool for Allura is as simple as defining a few controllers
+to handle particular URL's, templates to render pages, and defining the schemas
+of any Allura document types that your tool requires.
+
+.. image:: ../_static/images/tools.png
+   :alt: App Tools
+   :align: right
+
+When you write Allura tools, you'll get lots of stuff for free:
+
+* Search-ability of your Artifacts
+* Artifact versioning for accountability and transparency
+* Ability to extend existing Artifacts
+* Reuse central User/group/permission management
+* A robust and flexible permissions system
+* Access to a real-time event publishing system
+
+What's in a tool?
+~~~~~~~~~~~~~~~~~
+
+The most basic app tool consists of a few things:
+
+* A controller object (instantiated per request)
+* Template files (optional)
+* UI Widgets (optional)
+* Extensions to existing Artifacts (optional)
+* New Artifact types (optional)
+* Event listener tools (optional)
+* Event publisher (optional)
+
+Users/groups and Permissions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In order to facilitate more open processes, where more users can contribute
+-- while still protecting data -- documents can easily be "versioned", and
+the platform provides tools to manage versioned documents for you.
+
+
+
+Why create all the tools as plugins?
+------------------------------------
+
+We know that some projects are going to want more locked down
+access controls in their bug trackers, or more workflow based
+processes.  These things are inevitable, and we really do want
+to support them, but at the same time they are going to conflict
+with the way many other projects want to work.
+
+Building a plugin (tool in Allura terms) system, and standard
+integration points makes it possible to serve everybody in one
+way or another.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/platform/platform_tour.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform/platform_tour.rst b/Allura/docs/platform/platform_tour.rst
new file mode 100644
index 0000000..c3ca064
--- /dev/null
+++ b/Allura/docs/platform/platform_tour.rst
@@ -0,0 +1,258 @@
+..     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.
+
+*************
+Platform Tour
+*************
+
+Introduction
+^^^^^^^^^^^^
+
+Allura is implemented as a collection of tool applications on top of a
+robust and open platform.  Some of the services provided by the platform include:
+
+- Indexing and search
+- Authentication and Authorization
+- Email integration (every tool application gets its own email address)
+- Asynchronous processing with background tasks and events
+- `Markdown <http://daringfireball.net/projects/markdown/>`_ markup formatting
+- Simple autolinking between different artifacts in the forge
+- Attachment handling
+- Tool administration
+
+Tools, on the other hand, provide the actual user interface and logic to
+manipulate forge artifacts.  Some of the tools currently implemented include:
+
+admin
+  This tool is installed in all projects, and allows the administration of the
+  project's tools, authentication, and authorization
+Git, Hg, SVN
+  These tools allow you to host a version control system in Allura.
+  They also provides the ability to "fork" Git and Hg repos in order to
+  provide your own extensions.
+Wiki
+  This tool provides a basic wiki with support for comments, attachments, and
+  notifications.
+Tracker
+  This tool provides an extensible ticketing system for tracking feature
+  requests, defects, or support requests.
+Discussion
+  This tool provides a forum interface with full email integration as well.
+  The forum also handles attachments to posts either via the web interface or via email.
+
+The Context Object
+------------------
+
+The Pylons "context" object `c` has several properties which are automatically
+set for each request:
+
+project
+  The current project
+app
+  The current tool application object.
+user
+  The current user
+
+Allura platform provides the following functions to manage the context object,
+if you need to change the context for some situation:
+
+.. function:: allura.lib.helpers.push_config(obj, **kw)
+   :noindex:
+
+   Context manager (used with the `with` statement) used to temporarily set the
+   attributes of a particular object, resetting them to their previous values
+   at the end of the `with` block.  Used most frequently with the context object
+   `c`::
+
+       c.project = some_project
+       with push_config(c, project=other_project):
+           ...
+           # code in this block will have c.project == other_project
+       # code here will have c.project == some_project
+
+.. function:: allura.lib.helpers.set_context(project_id, mount_point=None, app_config_id=None)
+   :noindex:
+
+   Set the context object `c` according to the given `project_id` and optionally either a
+   `mount_point`, an `app_config_id`.  `c.project` is set to the corresponding
+   project object.  If the mount_point or app_config_id is
+   specified, then `c.app` will be set to the corresponding tool application
+   object.
+
+
+Artifacts
+---------
+
+We've mentioned artifacts a couple of times now without definition.  An artifact,
+as used in Allura, is some object that a tool needs to store in the
+forge.  The platform provides facilities for controlling access to individual
+artifacts if that's what the tool designer favors.  For instance, the Discussion
+tool allows a user to edit or delete their own posts, but not to edit or delete
+others (unless the user has the 'moderate' permission on the forum itself).
+Some examples of artifacts in the current tools:
+
+- Discussion: Forum, Thread, Post, Attachment
+- Wiki: Page, Comment, Attachment
+- Tracker: Ticket, Comment, Attachment
+- SCM: Repository, Commit, Patch
+
+In order to implement your own artifact, you should override at least a few of
+the methods of the `allura.model.artifact.Artifact` class::
+
+    from ming.orm.property import FieldProperty
+    from allura.model import Artifact
+
+    class NewArtifact(Artifact):
+        class __mongometa__:
+            name='my_new_artifact' # collection where this artifact is stored
+        type_s = 'My Artifact' # 'type' of the artifact used in search results
+
+        # Add your own properties here (beyond those provided by Artifact)
+        shortname = FieldProperty(str)
+
+        def url(self):
+            'Each artifact should have its own URL '
+            return self.app.url + self.shortname + '/'
+
+        def index(self):
+            'Return the fields you want indexed on this artifact'
+            result = Artifact.index(self)
+            result.update(type_s=self.type_s,
+                          name_s=self.shortname,
+                          text=self.shortname)
+            return result
+
+        def shorthand_id(self):
+            'Used in the generation of short links like [my_artifact]'
+            return self.shortname
+
+Platform services provided for artifacts
+----------------------------------------
+
+Whenever you create, modify, or delete an artifact, the platform does a couple of
+things for you:
+
+- The artifact is added to the index and will appear in searches
+- A shortlink is generated for the artifact (e.g. [MyWikiPage] or [#151]).  This allows you
+  to reference the artifact from other artifacts.  Whenever the commit message
+  is displayed in the SCM tool, any references to `[#151]` will be
+  automatically linked to that Ticket's page.
+
+Shortlinks work only within a project hierarchy (in order to link to some other project's
+page, you'll have to use the full URL).  Sometimes, a shortlink may need to be
+differentiated based on its location in a subproject or in one of many tools of
+the same type within a project.  In order to do this, shortlinks may be prefixed
+by either the tool mount point or a project ID and tool mount point.
+
+For instance, suppose we have an ticket tracker called 'features' and one called 'bugs'.
+They both have many tickets in them.  To distinguish, use the tracker mount point
+within the reference.  For example [features:#3] or [bugs:#3]
+
+Asynchronous Processing
+-----------------------
+
+Much of the actual functionality of Allura comes from code that runs
+*outside* the context of a web request, in the `taskd` server (invoked by
+running `paster taskd development.ini`.  Asynchronous processing is performed
+by two types of functions, *tasks* and *events*, differentiated as follows:
+
+Task
+    Tasks are module-level global functions.  They are annotated with the `@task`
+    decorator and are invoked with the `.post` method.  For instance, to schedule
+    a task  `foobar` to execute in the `taskd` context, you would write::
+
+       @task
+       def foobar(a,b,c=5): ...
+       
+       foobar.post(9,1,c=15)
+
+Event
+    Events are intended for "fan-out" types of events.  Events have a string
+    name, and are  "listened" for by using the `@event_handler` decorator.  The
+    `g.post_event()` helper is provided to run the event handlers for a
+    particular event in the `taskd` context.  Multiple event handlers can be
+    registered for each event::
+
+        @event_handler('event_name')
+        def handler1(topic, *args, **kwargs): ...
+
+        @event_handler('event_name')
+        def handler2(topic, *args, **kwargs): ...
+
+        g.post_event('event_name', 1,2,3, a=5)
+
+
+Email Integration
+-----------------
+
+The Allura platform provides easy-to-use email integration.  Forge email addresses
+are of the form
+<topic>@<mount_point>[.<subproject>]*.<subproject>.projects.sourceforge.net.
+When a message is received on such an email address, the address is parsed and
+the sending user is identified (if possible).  Based on the parsed address, the
+pylons context attributes `c.project` and `c.app` are set, and the application is
+queried to determine whether the identified user has authority to send an email
+to the given app/topic combination by calling `c.app.has_access(user, topic)`.
+If the user has access, the message is decomposed into its component parts (if a
+multipart MIME-encoded message) and `c.app.handle_message(topic, message)` is
+called for each part with the following components to the `msg` dict:
+
+headers
+  The actual headers parsed from the body of the message
+message_id
+  The `Message-ID` header (which should be universally
+  unique and is
+  generated by the email client), used for determining which messages are replies
+  to which other messages
+in_reply_to
+  The `In-Reply-To` header, used for determining which messages are replies to
+  which other messages
+references
+  The `References` header, used for determining which messages refer to
+  which other messages
+filename
+  Optional, if the part is an attachment with a filename, this will be populated
+content_type
+  The MIME content_type of the message part
+payload
+  The actual content of the message part
+
+The Allura platform also provides full support for *sending* email without
+worrying about the specifics of SMTP or sendmail handling.  In order to send an
+email, simply post a task for `allura.tasks.mail_tasks.sendmail` with the
+following arguments:
+
+fromaddr
+  Return address on the message (usually the topic@tool_name that generated
+  it)
+destinations
+  List of email addresses and/or :class:`bson.ObjectId` s for
+  :class:`allura.model.auth.User` objects
+text
+  Markdown-formatted body of the message (If the user has requested html or
+  combined text+html messages in their preferences, the Markdown will be so
+  rendered.  Otherwise a plain text message will be sent.)
+reply_to
+  Address to which replies should be sent
+subject
+  Subject of the message
+message_id
+  Value to put in the `Message-ID` header (the `_id` field of a
+  :class:`allura.model.artifact.Message` is suitable for this)
+in_reply_to (optional)
+  Value to put in the `In-Reply-To` header (the `parent_id` field of a
+  :class:`allura.model.artifact.Message` is suitable for this)

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/platform/scm.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform/scm.rst b/Allura/docs/platform/scm.rst
new file mode 100644
index 0000000..d8ab6d6
--- /dev/null
+++ b/Allura/docs/platform/scm.rst
@@ -0,0 +1,150 @@
+..     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.
+
+*******************
+SCM tools in Allura
+*******************
+
+
+The interface API and most of the controller and view structure of
+code repository type apps is defined in the base classes in the
+Allura package, which consists of the classes in the following
+packages:
+
+* `allura.lib.repository` for the base application and implementation classes
+* `allura.controllers.repository` for the base controllers
+* `allura.model.repository` for the repo metadata models
+* `allura.model.repo_refresh` for the repo metadata refresh logic
+
+
+Application and Implementation
+------------------------------
+
+The `Application` structure for SCM apps follows the normal pattern for
+Allura applications, though they should inherit from
+`allura.lib.repository.RepositoryApp` instead of `allura.app.Application`.
+The apps are then responsible for implementing subclasses of
+`allura.lib.repository.Repository` and `allura.lib.repository.RepositoryImplementation`.
+
+The `Repository` subclass is responsible for implementing tool-specific
+logic using the metadata models and proxying the rest of its logic to the
+`RepositoryImplementation` subclass, which talks directly to the underlying
+SCM tool, such as `GitPython`, `Mercurial`, or `pysvn`.
+
+Historically, more was done in the `Repository` subclass using the metadata
+models, but we are trying to move away from those toward making the SCM apps
+thin wrappers around the underlying SCM tool (see `Indexless`_, below).
+
+
+Controller / View Dispatch
+--------------------------
+
+All of the SCM apps use the base controllers in `allura.controllers.repository`
+with only minimal customization through subclassing (primarily to
+override the template for displaying instructions for setting up a newly
+created repository), so the dispatch for all SCM apps follows the same
+pattern.
+
+The root controller for SCM apps is `allura.controllers.repository.RepoRootController`.
+This controller has views for repo-level actions, such as forking and merging,
+and the SCM app attaches a refs and a commits (ci) controller to dispatch symbolic
+references and explicit commit IDs, respectively.  (This should be refactored to be
+done in the `RepoRootController` so that the dispatch can be followed more easily.)
+(Also, `ForgeSVN` actually eschews this class and uses `BranchBrowser` directly as
+its root, in order to tweak the URL slightly, but it monkeypatches the relevant
+views over, so the dispatch ends up working more or less the same.)
+
+The refs controller, `allura.controllers.repository.RefsController`, handles
+symbolic references, and in particular handles custom escape handling to detect
+what is part of the ref name vs part of the remainder of the URL to dispatch.
+This is then handed off to the `BranchBrowserClass` which is a pointer to
+the implementation within the specific SCM app which handles the empty
+repo instructions or hands it back to the generic commits controller.
+
+The commits controller, `allura.controllers.repository.CommitsController`,
+originally only handled explicit commit IDs, but was modified to allow for
+persistent symbolic refs in the URL ("/p/allura/git/ci/master/", e.g.),
+it was changed to have the same escape parsing logic as the refs controller.
+Regardless, it just parses out the reference / ID from the URL and hands
+off to the commit browser.
+
+The commit browser, `allura.controllers.repository.CommitBrowser`, holds
+the views related to a specific commit, such as viewing the commit details
+(message and changes), a log of the commit history starting with the commit,
+or creating a snapshot of the code as of the commit.  It also has a "tree"
+endpoint for browsing the file system tree as of the commit.
+
+The tree browser, `allura.controllers.repository.TreeBrowser`, holds the
+view for viewing the file system contents at a specific path for a given
+commit.  The only complication here is that, instead of parsing out the
+entire tree path at once, it recursively dispatches to itself to build
+up the path a piece at a time.  Tree browsing also depends on the
+`Last Commit Logic`_ to create the data needed to display the last
+commit that touched each file or directory within the given directory.
+
+
+Last Commit Logic
+-----------------
+
+Determining which commit was the last to touch a given set of files or
+directories can be complicated, depending on the specific SCM tool.
+Git and Mercurial require manually walking up the commit history to
+discover this information, while SVN can return it all from a singlle
+`info2` command (though the SVN call will be signficantly slower than
+any individual call to Git or Mercurial).  Because this can sometimes
+be costly to generate, it is cached via the `allura.model.repository.LastCommit`
+model.  This will generate the data on demand by calling the underlying
+SCM tool, if necessary, but the data is currently pre-generated during
+the post-push refresh logic for Git and Mercurial.
+
+The overall logic for generating this data for Git and Mercurial is as follows:
+
+1. All items modified in the current commit get their info from the
+   current commit
+
+2. The info for the remaining items is found:
+
+ * If there is a `LastCommit` record for the parent directory, all of
+   the remaining items get their info from the previous `LastCommit`
+
+ * Otherwise, the list of remaining items is sent to the SCM implementation,
+   which repeatedly asks the SCM for the last commit to touch any of the
+   items for which we are missing information, removing items from the list
+   as commits are reported as having modified them
+
+3. Once all of the items have information, or if a processing timeout is reached,
+   the gathered information is saved in the `LastCommit` model and returned
+
+
+
+Indexless
+---------
+
+Currently, there are model classes which encapsulate SCM metadata
+(such as commits, file system structure, etc) in a generic (agnostic to
+the underlying tool implementation) way.  However, this means that we're
+duplicating in mongo a lot of data that is already tracked by the
+underlying SCM tool, and this data must also be indexed for new repos
+and after every subsequent push before the commits or files are browsable
+via the web interface.
+
+To minimize this duplication of data and reduce or eliminate the delay
+between commits being pushed and them being visible, we are trying to
+move toward a lightweight API layer that requests the data from the
+underlying SCM tool directly, with intelligent caching at the points
+and in the format that makes the most sense to make rendering the SCM
+pages as fast as possible.

http://git-wip-us.apache.org/repos/asf/allura/blob/ae5d2746/Allura/docs/platform_tour.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/platform_tour.rst b/Allura/docs/platform_tour.rst
deleted file mode 100644
index 06d3140..0000000
--- a/Allura/docs/platform_tour.rst
+++ /dev/null
@@ -1,257 +0,0 @@
-..     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.
-
-Platform Tour
-=============
-
-Introduction
-------------
-
-Allura is implemented as a collection of tool applications on top of a
-robust and open platform.  Some of the services provided by the platform include:
-
-- Indexing and search
-- Authentication and Authorization
-- Email integration (every tool application gets its own email address)
-- Asynchronous processing with background tasks and events
-- `Markdown <http://daringfireball.net/projects/markdown/>`_ markup formatting
-- Simple autolinking between different artifacts in the forge
-- Attachment handling
-- Tool administration
-
-Tools, on the other hand, provide the actual user interface and logic to
-manipulate forge artifacts.  Some of the tools currently implemented include:
-
-admin
-  This tool is installed in all projects, and allows the administration of the
-  project's tools, authentication, and authorization
-Git, Hg, SVN
-  These tools allow you to host a version control system in Allura.
-  They also provides the ability to "fork" Git and Hg repos in order to
-  provide your own extensions.
-Wiki
-  This tool provides a basic wiki with support for comments, attachments, and
-  notifications.
-Tracker
-  This tool provides an extensible ticketing system for tracking feature
-  requests, defects, or support requests.
-Discussion
-  This tool provides a forum interface with full email integration as well.
-  The forum also handles attachments to posts either via the web interface or via email.
-
-The Context Object
-------------------
-
-The Pylons "context" object `c` has several properties which are automatically
-set for each request:
-
-project
-  The current project
-app
-  The current tool application object.
-user
-  The current user
-
-Allura platform provides the following functions to manage the context object,
-if you need to change the context for some situation:
-
-.. function:: allura.lib.helpers.push_config(obj, **kw)
-   :noindex:
-
-   Context manager (used with the `with` statement) used to temporarily set the
-   attributes of a particular object, resetting them to their previous values
-   at the end of the `with` block.  Used most frequently with the context object
-   `c`::
-
-       c.project = some_project
-       with push_config(c, project=other_project):
-           ...
-           # code in this block will have c.project == other_project
-       # code here will have c.project == some_project
-
-.. function:: allura.lib.helpers.set_context(project_id, mount_point=None, app_config_id=None)
-   :noindex:
-
-   Set the context object `c` according to the given `project_id` and optionally either a
-   `mount_point`, an `app_config_id`.  `c.project` is set to the corresponding
-   project object.  If the mount_point or app_config_id is
-   specified, then `c.app` will be set to the corresponding tool application
-   object.
-
-
-Artifacts
----------
-
-We've mentioned artifacts a couple of times now without definition.  An artifact,
-as used in Allura, is some object that a tool needs to store in the
-forge.  The platform provides facilities for controlling access to individual
-artifacts if that's what the tool designer favors.  For instance, the Discussion
-tool allows a user to edit or delete their own posts, but not to edit or delete
-others (unless the user has the 'moderate' permission on the forum itself).
-Some examples of artifacts in the current tools:
-
-- Discussion: Forum, Thread, Post, Attachment
-- Wiki: Page, Comment, Attachment
-- Tracker: Ticket, Comment, Attachment
-- SCM: Repository, Commit, Patch
-
-In order to implement your own artifact, you should override at least a few of
-the methods of the `allura.model.artifact.Artifact` class::
-
-    from ming.orm.property import FieldProperty
-    from allura.model import Artifact
-
-    class NewArtifact(Artifact):
-        class __mongometa__:
-            name='my_new_artifact' # collection where this artifact is stored
-        type_s = 'My Artifact' # 'type' of the artifact used in search results
-
-        # Add your own properties here (beyond those provided by Artifact)
-        shortname = FieldProperty(str)
-
-        def url(self):
-            'Each artifact should have its own URL '
-            return self.app.url + self.shortname + '/'
-
-        def index(self):
-            'Return the fields you want indexed on this artifact'
-            result = Artifact.index(self)
-            result.update(type_s=self.type_s,
-                          name_s=self.shortname,
-                          text=self.shortname)
-            return result
-
-        def shorthand_id(self):
-            'Used in the generation of short links like [my_artifact]'
-            return self.shortname
-
-Platform services provided for artifacts
-----------------------------------------
-
-Whenever you create, modify, or delete an artifact, the platform does a couple of
-things for you:
-
-- The artifact is added to the index and will appear in searches
-- A shortlink is generated for the artifact (e.g. [MyWikiPage] or [#151]).  This allows you
-  to reference the artifact from other artifacts.  Whenever the commit message
-  is displayed in the SCM tool, any references to `[#151]` will be
-  automatically linked to that Ticket's page.
-
-Shortlinks work only within a project hierarchy (in order to link to some other project's
-page, you'll have to use the full URL).  Sometimes, a shortlink may need to be
-differentiated based on its location in a subproject or in one of many tools of
-the same type within a project.  In order to do this, shortlinks may be prefixed
-by either the tool mount point or a project ID and tool mount point.
-
-For instance, suppose we have an ticket tracker called 'features' and one called 'bugs'.
-They both have many tickets in them.  To distinguish, use the tracker mount point
-within the reference.  For example [features:#3] or [bugs:#3]
-
-Asynchronous Processing
------------------------
-
-Much of the actual functionality of Allura comes from code that runs
-*outside* the context of a web request, in the `taskd` server (invoked by
-running `paster taskd development.ini`.  Asynchronous processing is performed
-by two types of functions, *tasks* and *events*, differentiated as follows:
-
-Task
-    Tasks are module-level global functions.  They are annotated with the `@task`
-    decorator and are invoked with the `.post` method.  For instance, to schedule
-    a task  `foobar` to execute in the `taskd` context, you would write::
-
-       @task
-       def foobar(a,b,c=5): ...
-       
-       foobar.post(9,1,c=15)
-
-Event
-    Events are intended for "fan-out" types of events.  Events have a string
-    name, and are  "listened" for by using the `@event_handler` decorator.  The
-    `g.post_event()` helper is provided to run the event handlers for a
-    particular event in the `taskd` context.  Multiple event handlers can be
-    registered for each event::
-
-        @event_handler('event_name')
-        def handler1(topic, *args, **kwargs): ...
-
-        @event_handler('event_name')
-        def handler2(topic, *args, **kwargs): ...
-
-        g.post_event('event_name', 1,2,3, a=5)
-
-
-Email Integration
------------------
-
-The Allura platform provides easy-to-use email integration.  Forge email addresses
-are of the form
-<topic>@<mount_point>[.<subproject>]*.<subproject>.projects.sourceforge.net.
-When a message is received on such an email address, the address is parsed and
-the sending user is identified (if possible).  Based on the parsed address, the
-pylons context attributes `c.project` and `c.app` are set, and the application is
-queried to determine whether the identified user has authority to send an email
-to the given app/topic combination by calling `c.app.has_access(user, topic)`.
-If the user has access, the message is decomposed into its component parts (if a
-multipart MIME-encoded message) and `c.app.handle_message(topic, message)` is
-called for each part with the following components to the `msg` dict:
-
-headers
-  The actual headers parsed from the body of the message
-message_id
-  The `Message-ID` header (which should be universally
-  unique and is
-  generated by the email client), used for determining which messages are replies
-  to which other messages
-in_reply_to
-  The `In-Reply-To` header, used for determining which messages are replies to
-  which other messages
-references
-  The `References` header, used for determining which messages refer to
-  which other messages
-filename
-  Optional, if the part is an attachment with a filename, this will be populated
-content_type
-  The MIME content_type of the message part
-payload
-  The actual content of the message part
-
-The Allura platform also provides full support for *sending* email without
-worrying about the specifics of SMTP or sendmail handling.  In order to send an
-email, simply post a task for `allura.tasks.mail_tasks.sendmail` with the
-following arguments:
-
-fromaddr
-  Return address on the message (usually the topic@tool_name that generated
-  it)
-destinations
-  List of email addresses and/or :class:`bson.ObjectId` s for
-  :class:`allura.model.auth.User` objects
-text
-  Markdown-formatted body of the message (If the user has requested html or
-  combined text+html messages in their preferences, the Markdown will be so
-  rendered.  Otherwise a plain text message will be sent.)
-reply_to
-  Address to which replies should be sent
-subject
-  Subject of the message
-message_id
-  Value to put in the `Message-ID` header (the `_id` field of a
-  :class:`allura.model.artifact.Message` is suitable for this)
-in_reply_to (optional)
-  Value to put in the `In-Reply-To` header (the `parent_id` field of a
-  :class:`allura.model.artifact.Message` is suitable for this)


[08/26] allura git commit: [#7832] ticket:731 Add support for DELETE to test app

Posted by je...@apache.org.
[#7832] ticket:731 Add support for DELETE to test app


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

Branch: refs/heads/ib/7830
Commit: 2fac9ece7929e1e88d08252097b1595a7fd4ce69
Parents: 0979146
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 16:28:30 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:52 2015 +0000

----------------------------------------------------------------------
 AlluraTest/alluratest/controller.py | 11 +++++++----
 AlluraTest/alluratest/validation.py |  7 +++++++
 2 files changed, 14 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/2fac9ece/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 4237624..4398a82 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -209,7 +209,7 @@ class TestRestApiBase(TestController):
 
         return self._token_cache[username]
 
-    def _api_getpost(self, method, path, wrap_args=None, user='test-admin', status=None, **params):
+    def _api_call(self, method, path, wrap_args=None, user='test-admin', status=None, **params):
         '''
         If you need to use one of the method kwargs as a URL parameter,
         pass params={...} as a dict instead of **kwargs
@@ -224,7 +224,7 @@ class TestRestApiBase(TestController):
 
         params['access_token'] = self.token(user).api_key
 
-        fn = self.app.post if method == 'POST' else self.app.get
+        fn = getattr(self.app, method.lower())
 
         response = fn(
             str(path),
@@ -236,7 +236,10 @@ class TestRestApiBase(TestController):
             return response
 
     def api_get(self, path, wrap_args=None, user='test-admin', status=None, **params):
-        return self._api_getpost('GET', path, wrap_args, user, status, **params)
+        return self._api_call('GET', path, wrap_args, user, status, **params)
 
     def api_post(self, path, wrap_args=None, user='test-admin', status=None, **params):
-        return self._api_getpost('POST', path, wrap_args, user, status, **params)
+        return self._api_call('POST', path, wrap_args, user, status, **params)
+
+    def api_delete(self, path, wrap_args=None, user='test-admin', status=None, **params):
+        return self._api_call('DELETE', path, wrap_args, user, status, **params)

http://git-wip-us.apache.org/repos/asf/allura/blob/2fac9ece/AlluraTest/alluratest/validation.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/validation.py b/AlluraTest/alluratest/validation.py
index 1bf6b22..97691df 100644
--- a/AlluraTest/alluratest/validation.py
+++ b/AlluraTest/alluratest/validation.py
@@ -315,3 +315,10 @@ class ValidatingTestApp(PostParamCheckingTestApp):
         if not self.validate_skip and not val_params['validate_skip']:
             self._validate(resp, 'post', val_params)
         return resp
+
+    def delete(self, *args, **kw):
+        val_params, kw = self._get_validation_params(kw)
+        resp = super(ValidatingTestApp, self).delete(*args, **kw)
+        if not self.validate_skip and not val_params['validate_skip']:
+            self._validate(resp, 'delete', val_params)
+        return resp


[02/26] allura git commit: [#7832] ticket:731 Add rest api controllers for app admin

Posted by je...@apache.org.
[#7832] ticket:731 Add rest api controllers for app admin


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

Branch: refs/heads/ib/7830
Commit: 8c604cef5fe8d6a4568343ac56474425332e45fc
Parents: e5e176d
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 20 09:48:05 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:51 2015 +0000

----------------------------------------------------------------------
 Allura/allura/app.py                  |  5 +++++
 Allura/allura/ext/admin/admin_main.py |  7 +++++++
 Allura/allura/lib/repository.py       | 20 ++++++++++++++++++++
 3 files changed, 32 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/8c604cef/Allura/allura/app.py
----------------------------------------------------------------------
diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index b9fd6b7..4e1ed5d 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -211,6 +211,10 @@ class Application(object):
     :cvar Controller api_root: Serves API access at
         /rest/<neighborhood>/<project>/<app>/. Default is None - subclasses
         should override to expose API access to the Application.
+    :cvar Controller admin_api_root: Serves Admin API access at
+        /rest/<neighborhood>/<project>/admin/<app>/. Default is None -
+        subclasses should override to expose Admin API access to the
+        Application.
     :ivar Controller admin: Serves admin functions at
         /<neighborhood>/<project>/<admin>/<app>/. Default is a
         :class:`DefaultAdminController` instance.
@@ -227,6 +231,7 @@ class Application(object):
     script_name = None
     root = None  # root controller
     api_root = None
+    admin_api_root = None
     permissions = []
     permissions_desc = {
         'unmoderated_post': 'Post comments without moderation.',

http://git-wip-us.apache.org/repos/asf/allura/blob/8c604cef/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 b812b0d..d5ae068 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -884,6 +884,13 @@ class ProjectAdminRestController(BaseController):
                     % (tool, mount_point, mount_label)
         }
 
+    @expose()
+    def _lookup(self, name, *remainder):
+        app = c.project.app_instance(name)
+        if app is None or app.admin_api_root is None:
+            raise exc.HTTPNotFound, name
+        return app.admin_api_root, remainder
+
 
 class PermissionsController(BaseController):
     def _check_security(self):

http://git-wip-us.apache.org/repos/asf/allura/blob/8c604cef/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 9a4966f..d5d467b 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -28,6 +28,7 @@ from ming.utils import LazyProperty
 
 import allura.tasks
 from allura import version
+from allura.controllers.base import BaseController
 from allura.lib import helpers as h
 from allura import model as M
 from allura.lib import security
@@ -75,6 +76,7 @@ class RepositoryApp(Application):
     def __init__(self, project, config):
         Application.__init__(self, project, config)
         self.admin = RepoAdminController(self)
+        self.admin_api_root = RepoAdminRestController(self)
 
     def main_menu(self):
         '''Apps should provide their entries to be added to the main nav
@@ -274,3 +276,21 @@ class RepoAdminController(DefaultAdminController):
         else:
             flash("Invalid external checkout URL: %s" % c.form_errors['external_checkout_url'], "error")
         redirect(c.project.url() + 'admin/tools')
+
+
+class RepoAdminRestController(BaseController):
+    def __init__(self, app):
+        self.app = app
+        self.webhooks = RestWebhooksLookup(app)
+
+
+class RestWebhooksLookup(BaseController):
+    def __init__(self, app):
+        self.app = app
+
+    @expose('json:')
+    def index(self, **kw):
+        webhooks = self.app._webhooks
+        if len(webhooks) == 0:
+            raise exc.HTTPNotFound()
+        return {'test': 'works'}


[23/26] allura git commit: [#7830] ticket:733 Don't catch exception in merge task

Posted by je...@apache.org.
[#7830] ticket:733 Don't catch exception in merge task

So that merge task will get 'error' status and proper message will be displayed
to the user.


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

Branch: refs/heads/ib/7830
Commit: facc4c067e0ed3481fd44017a7227df0b8b92549
Parents: 0135de2
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 26 13:16:23 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Mar 6 09:43:09 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tasks/repo_tasks.py | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/facc4c06/Allura/allura/tasks/repo_tasks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tasks/repo_tasks.py b/Allura/allura/tasks/repo_tasks.py
index e50f9d2..e873694 100644
--- a/Allura/allura/tasks/repo_tasks.py
+++ b/Allura/allura/tasks/repo_tasks.py
@@ -160,10 +160,6 @@ def merge(merge_request_id):
     from allura import model as M
     log = logging.getLogger(__name__)
     mr = M.MergeRequest.query.get(_id=merge_request_id)
-    try:
-        mr.app.repo.merge(mr)
-    except:
-        log.exception("Can't merge merge request %s", mr.url())
-        return
+    mr.app.repo.merge(mr)
     mr.status = 'merged'
     session(mr).flush(mr)


[11/26] allura git commit: [#7832] ticket:731 Support oauth via headers

Posted by je...@apache.org.
[#7832] ticket:731 Support oauth via headers

Currently, we check only body of the request to determine if oauth tokens are
provided. We need to be able to authenticate via headers also. This is useful
for some HTTP methods, which don't expect parameters in request body (e.g.
DELETE).


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

Branch: refs/heads/ib/7830
Commit: 59942295a2b51421121136e49943f435053bc319
Parents: a747991
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 23 12:00:09 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 27 22:40:53 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/rest.py   | 19 +++++++++++++++----
 AlluraTest/alluratest/controller.py |  6 +++++-
 2 files changed, 20 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/59942295/Allura/allura/controllers/rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index 1001343..2c1a042 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -47,7 +47,12 @@ class RestController(object):
 
     def _authenticate_request(self):
         'Based on request.params or oauth, authenticate the request'
-        if 'oauth_token' in request.params or 'access_token' in request.params:
+        headers_auth = 'Authorization' in request.headers
+        params_auth = 'oauth_token' in request.params
+        params_auth = params_auth or 'access_token' in request.params
+        log.error(headers_auth)
+        log.error(request.headers)
+        if headers_auth or params_auth:
             return self.oauth._authenticate()
         else:
             return None
@@ -104,15 +109,21 @@ class OAuthNegotiator(object):
         return result
 
     def _authenticate(self):
-        if 'access_token' in request.params:
+        bearer_token_prefix = 'OAuth BearerToken access_token='
+        auth = request.headers.get('Authorization')
+        log.error(auth)
+        if auth and auth.startswith(bearer_token_prefix):
+            access_token = auth[len(bearer_token_prefix):]
+        else:
+            access_token = request.params.get('access_token')
+        if access_token:
             # handle bearer tokens
             # skip https check if auth invoked from tests
             testing = request.environ.get('paste.testing', False)
             if not testing and request.scheme != 'https':
                 request.environ['pylons.status_code_redirect'] = True
                 raise exc.HTTPForbidden
-            access_token = M.OAuthAccessToken.query.get(
-                api_key=request.params['access_token'])
+            access_token = M.OAuthAccessToken.query.get(api_key=access_token)
             if not (access_token and access_token.is_bearer):
                 request.environ['pylons.status_code_redirect'] = True
                 raise exc.HTTPForbidden

http://git-wip-us.apache.org/repos/asf/allura/blob/59942295/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 4398a82..c64ea5f 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -222,13 +222,17 @@ class TestRestApiBase(TestController):
             status = [200, 201, 301, 302, 400, 403, 404]
         params = variabledecode.variable_encode(params, add_repetitions=False)
 
-        params['access_token'] = self.token(user).api_key
+        token = self.token(user).api_key
+        headers = {
+            'Authorization': 'OAuth BearerToken access_token={}'.format(token)
+        }
 
         fn = getattr(self.app, method.lower())
 
         response = fn(
             str(path),
             params=params,
+            headers=headers,
             status=status)
         if response.status_int in [301, 302]:
             return response.follow()