You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2015/11/03 20:30:53 UTC

[1/4] allura git commit: [#7919] fix some tests

Repository: allura
Updated Branches:
  refs/heads/db/7919 e8a55ebeb -> e1ca8deb8


[#7919] fix some tests


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

Branch: refs/heads/db/7919
Commit: 9637c4e6441e1934b26ea351e6a604d7550a2708
Parents: e8a55eb
Author: Dave Brondsema <da...@brondsema.net>
Authored: Mon Nov 2 20:14:32 2015 -0500
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Mon Nov 2 20:14:50 2015 -0500

----------------------------------------------------------------------
 .../allura/templates/jinja_master/top_nav.html  |  2 +-
 Allura/allura/tests/functional/test_home.py     | 20 +++++++++++---------
 .../tests/functional/test_neighborhood.py       |  2 +-
 3 files changed, 13 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/9637c4e6/Allura/allura/templates/jinja_master/top_nav.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/jinja_master/top_nav.html b/Allura/allura/templates/jinja_master/top_nav.html
index 91b7194..85e3056 100644
--- a/Allura/allura/templates/jinja_master/top_nav.html
+++ b/Allura/allura/templates/jinja_master/top_nav.html
@@ -35,7 +35,7 @@
     </li>
   {% endfor %}
   {% if h.has_access(c.project, 'admin')() %}
-    <li id="add-tool-container"><a class="add-tool-toggle">Add New...</a></li>
+    <li id="add-tool-container"><a href='#' class="add-tool-toggle">Add New...</a></li>
     <button id="toggle-admin-btn"><i class="fa fa-lock"></i></button>
   {% endif %}
 </ul>

http://git-wip-us.apache.org/repos/asf/allura/blob/9637c4e6/Allura/allura/tests/functional/test_home.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_home.py b/Allura/allura/tests/functional/test_home.py
index 58f1d61..4c25e05 100644
--- a/Allura/allura/tests/functional/test_home.py
+++ b/Allura/allura/tests/functional/test_home.py
@@ -19,13 +19,12 @@ import json
 import re
 
 from pylons import tmpl_context as c
+from nose.tools import assert_equal, assert_not_in, assert_in
+from ming.orm import ThreadLocalORMSession
 
 from allura.tests import TestController
 from allura.tests import decorators as td
 from allura import model as M
-from ming.orm import ThreadLocalORMSession
-
-from nose.tools import assert_equal, assert_not_in
 
 
 class TestProjectHome(TestController):
@@ -37,6 +36,7 @@ class TestProjectHome(TestController):
         assert re.search(r'<!-- Server: \S+ -->',
                          str(root.html)), 'Missing Server comment'
         nav_links = root.html.find('div', dict(id='top_nav')).findAll('a')
+        nav_links = [nl for nl in nav_links if nl['class'] != 'add-tool-toggle']
         assert len(nav_links) == len(response.json['menu'])
         for nl, entry in zip(nav_links, response.json['menu']):
             assert nl['href'] == entry['url']
@@ -53,10 +53,10 @@ class TestProjectHome(TestController):
         menu = response.json['menu']
         wikis = menu[-2]['children']
         assert_equal(len(wikis), 2)
-        assert {u'url': u'/p/test/wiki/', u'name': u'Wiki', u'icon':
-                u'tool-wiki', 'tool_name': 'wiki'} in wikis, wikis
-        assert {u'url': u'/p/test/wiki2/', u'name': u'wiki2', u'icon':
-                u'tool-wiki', 'tool_name': 'wiki'} in wikis, wikis
+        assert_in({'url': '/p/test/wiki/', 'name': 'Wiki', 'mount_point': 'wiki',
+                   'icon': 'tool-wiki', 'tool_name': 'wiki', 'is_anchored': False, 'ordinal': 3}, wikis)
+        assert_in({'url': '/p/test/wiki2/', 'name': 'wiki2', 'mount_point': 'wiki2',
+                   'icon': 'tool-wiki', 'tool_name': 'wiki', 'is_anchored': False, 'ordinal': 4}, wikis)
 
     def test_sitemap_limit_per_tool(self):
         """Test that sitemap is limited to max of 10 items per tool type."""
@@ -85,8 +85,10 @@ class TestProjectHome(TestController):
         menu = response.json['menu']
         wiki_menu = [m for m in menu if m['tool_name'] == 'wiki'][0]
         assert_equal(len(wiki_menu['children']), 10)
-        assert {u'url': u'/p/test/_list/wiki', u'name': u'More...',
-                u'icon': u'tool-wiki', 'tool_name': 'wiki'} in wiki_menu['children']
+        for child in wiki_menu['children']:
+            child.pop('ordinal')
+        assert_in({'url': '/p/test/_list/wiki', 'name': 'More...', 'mount_point': '_list',
+                   'icon': 'tool-wiki', 'tool_name': 'wiki', 'is_anchored': False}, wiki_menu['children'])
 
     @td.with_wiki
     def test_neighborhood_home(self):

http://git-wip-us.apache.org/repos/asf/allura/blob/9637c4e6/Allura/allura/tests/functional/test_neighborhood.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_neighborhood.py b/Allura/allura/tests/functional/test_neighborhood.py
index 7281554..d946c7b 100644
--- a/Allura/allura/tests/functional/test_neighborhood.py
+++ b/Allura/allura/tests/functional/test_neighborhood.py
@@ -714,7 +714,7 @@ class TestNeighborhood(TestController):
             status=302).follow()
         p = M.Project.query.get(shortname='testtemp')
         # make sure the correct tools got installed in the right order
-        top_nav = r.html.find('div', {'id': 'top_nav'})
+        top_nav = r.html.find('div', {'id': 'top_nav'}).contents[1]
         assert top_nav.contents[1].contents[1].contents[1]['href'] == '/adobe/testtemp/wiki/'
         assert 'Wiki' in top_nav.contents[1].contents[1].contents[1].contents[0]
         assert top_nav.contents[1].contents[3].contents[1]['href'] == '/adobe/testtemp/discussion/'


[2/4] allura git commit: [#7919] make tests more robust (particularly, running various test_neighborhood.py tests on their own)

Posted by br...@apache.org.
[#7919] make tests more robust (particularly, running various test_neighborhood.py tests on their own)


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

Branch: refs/heads/db/7919
Commit: 245413a9d9fd7962f7cf540ec3e83b71eccc8fc7
Parents: 9637c4e
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Nov 3 11:44:10 2015 -0500
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Tue Nov 3 11:44:33 2015 -0500

----------------------------------------------------------------------
 Allura/allura/lib/utils.py                        | 8 +++++++-
 Allura/allura/tests/functional/test_admin.py      | 5 ++++-
 AlluraTest/alluratest/controller.py               | 2 +-
 ForgeBlog/forgeblog/tests/functional/test_rest.py | 4 ++--
 4 files changed, 14 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/245413a9/Allura/allura/lib/utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index eb273d2..b2dd0c2 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -383,7 +383,13 @@ class AntiSpam(object):
                 new_params = cls.validate_request(params=params)
                 params.update(new_params)
             except (ValueError, TypeError, binascii.Error):
-                raise Invalid(error_msg, params, None)
+                testing = pylons.request.environ.get('paste.testing', False)
+                if testing:
+                    # re-raise so we can see problems more easily
+                    raise
+                else:
+                    # regular antispam failure handling
+                    raise Invalid(error_msg, params, None)
         return before_validate(antispam_hook)
 
 

http://git-wip-us.apache.org/repos/asf/allura/blob/245413a9/Allura/allura/tests/functional/test_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_admin.py b/Allura/allura/tests/functional/test_admin.py
index 16590d7..0b9a857 100644
--- a/Allura/allura/tests/functional/test_admin.py
+++ b/Allura/allura/tests/functional/test_admin.py
@@ -39,6 +39,7 @@ from alluratest.controller import TestRestApiBase, setup_trove_categories
 from allura import model as M
 from allura.app import SitemapEntry
 from allura.lib.plugin import AdminExtension
+from allura.lib import helpers as h
 from allura.ext.admin.admin_main import AdminApp
 
 from forgetracker.tracker_main import ForgeTrackerApp
@@ -1284,7 +1285,9 @@ class TestRestInstallTool(TestRestApiBase):
                 'mount_point': 'ticketsmount1',
                 'mount_label': 'tickets_label1'
             }
-            c.project.install_app('tickets', mount_point=data['mount_point'])
+            project = M.Project.query.get(shortname='test')
+            with h.push_config(c, user=M.User.query.get()):
+                project.install_app('tickets', mount_point=data['mount_point'])
             r = self.api_post('/rest/p/test/admin/install_tool/', **data)
             assert_equals(r.json['success'], False)
             assert_equals(r.json['info'], 'Mount point already exists.')

http://git-wip-us.apache.org/repos/asf/allura/blob/245413a9/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index babf262..6ce1b71 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -155,6 +155,7 @@ class TestController(object):
         """Method called by nose before running each test"""
         self.app = ValidatingTestApp(
             setup_functional_test(app_name=self.application_under_test))
+        self.app.extra_environ = {'REMOTE_ADDR': '127.0.0.1'}  # remote_addr needed by AntiSpam
         if self.validate_skip:
             self.app.validate_skip = self.validate_skip
         if asbool(tg.config.get('smtp.mock')):
@@ -196,7 +197,6 @@ class TestRestApiBase(TestController):
 
     def setUp(self):
         super(TestRestApiBase, self).setUp()
-        setup_global_objects()
         self._use_token = None
         self._token_cache = {}
 

http://git-wip-us.apache.org/repos/asf/allura/blob/245413a9/ForgeBlog/forgeblog/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/ForgeBlog/forgeblog/tests/functional/test_rest.py b/ForgeBlog/forgeblog/tests/functional/test_rest.py
index e7a9d7b..ed3e9fc 100644
--- a/ForgeBlog/forgeblog/tests/functional/test_rest.py
+++ b/ForgeBlog/forgeblog/tests/functional/test_rest.py
@@ -18,7 +18,7 @@
 #       under the License.
 from datetime import date
 
-from nose.tools import assert_equal
+from nose.tools import assert_equal, assert_in
 from allura.lib import helpers as h
 from allura.tests import decorators as td
 from allura import model as M
@@ -51,7 +51,7 @@ class TestBlogApi(TestRestApiBase):
         url = '/rest' + BM.BlogPost.query.find().first().url()
         r = self.api_get('/rest/p/test/blog/')
         assert_equal(r.json['posts'][0]['title'], 'test')
-        assert_equal(r.json['posts'][0]['url'], h.absurl(url))
+        assert_in(url, r.json['posts'][0]['url'])
 
         r = self.api_get(url)
         assert_equal(r.json['title'], data['title'])


[3/4] allura git commit: [#7919] make _getProjectUrl and needed AJAX urls work for nbhd-projects and subprojects

Posted by br...@apache.org.
[#7919] make _getProjectUrl and needed AJAX urls work for nbhd-projects and subprojects


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

Branch: refs/heads/db/7919
Commit: 8324dd65cfedb61166b0384528fc5d88523d80fb
Parents: 245413a
Author: Dave Brondsema <da...@brondsema.net>
Authored: Mon Nov 2 18:41:03 2015 -0500
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Tue Nov 3 11:44:34 2015 -0500

----------------------------------------------------------------------
 Allura/allura/controllers/project.py             | 10 ++++++----
 Allura/allura/controllers/rest.py                |  6 +++++-
 Allura/allura/public/nf/js/navbar.es6.js         | 19 +++++++++++++++----
 .../allura/tests/functional/test_neighborhood.py |  3 +++
 4 files changed, 29 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/8324dd65/Allura/allura/controllers/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index 75b1aaf..0d689a8 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -76,8 +76,9 @@ class NeighborhoodController(object):
         self.neighborhood = neighborhood
         self.neighborhood_name = self.neighborhood.name
         self.prefix = self.neighborhood.shortname_prefix
-        self.browse = NeighborhoodProjectBrowseController(
-            neighborhood=self.neighborhood)
+        self.browse = NeighborhoodProjectBrowseController(neighborhood=self.neighborhood)
+        # 'admin' without underscore will pass through to _lookup which will find the regular "admin" tool mounted
+        # on the --init-- Project record for this neighborhood.
         self._admin = NeighborhoodAdminController(self.neighborhood)
         self._moderate = NeighborhoodModerateController(self.neighborhood)
         self.import_project = ProjectImporterController(self.neighborhood)
@@ -375,17 +376,18 @@ class ToolListController(object):
 class ProjectController(FeedController):
 
     def __init__(self):
-        setattr(self, '_nav.json', self._nav)
         self.screenshot = ScreenshotsController()
         self._list = ToolListController()
 
     @expose('json:')
-    def _nav(self):
+    def _nav(self, **kw):
         return c.project.nav_data()
 
     @expose()
     def _lookup(self, name, *remainder):
         name = unquote(name)
+        if name == '_nav.json':
+            return self, ['_nav']
         subproject = M.Project.query.get(
             shortname=c.project.shortname + '/' + name,
             neighborhood_id=c.project.neighborhood_id)

http://git-wip-us.apache.org/repos/asf/allura/blob/8324dd65/Allura/allura/controllers/rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index 30d28d9..7723c0d 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -281,7 +281,10 @@ class NeighborhoodRestController(object):
         return rest_has_access(self._neighborhood, user, perm)
 
     @expose()
-    def _lookup(self, name, *remainder):
+    def _lookup(self, name=None, *remainder):
+
+        # TODO: make this match NeighborhoodController._lookup so that /rest/p/admin/configure_tool_grouping works
+
         provider = plugin.ProjectRegistrationProvider.get()
         try:
             provider.shortname_validator.to_python(
@@ -291,6 +294,7 @@ class NeighborhoodRestController(object):
         name = self._neighborhood.shortname_prefix + name
         project = M.Project.query.get(
             shortname=name, neighborhood_id=self._neighborhood._id, deleted=False)
+        log.info('nbhd rest proj=%s ... %s', project, name)
         if not project:
             raise exc.HTTPNotFound, name
 

http://git-wip-us.apache.org/repos/asf/allura/blob/8324dd65/Allura/allura/public/nf/js/navbar.es6.js
----------------------------------------------------------------------
diff --git a/Allura/allura/public/nf/js/navbar.es6.js b/Allura/allura/public/nf/js/navbar.es6.js
index f76abb7..b1d21c1 100644
--- a/Allura/allura/public/nf/js/navbar.es6.js
+++ b/Allura/allura/public/nf/js/navbar.es6.js
@@ -1,16 +1,27 @@
 'use strict';
 
 /**
- * Gets the current url.
+ * Gets the current project url.
 
  * @constructor
  * @param {bool} rest - Return a "rest" version of the url.
  * @returns {string}
  */
 function _getProjectUrl(rest = true) {
-    var [nbhd, proj] = window.location.pathname.split('/').slice(1, 3);
-    var base = `${window.location.protocol}//${window.location.host}`;
-    return rest ? `${base}/rest/${nbhd}/${proj}` : `${base}/${nbhd}/${proj}`;
+    var nbhd, proj, nbhd_proj;
+    var ident_classes = document.getElementById('page-body').className.split(' ');
+    for (var cls of ident_classes) {
+        if (cls.indexOf('project-') === 0) {
+            proj = cls.slice('project-'.length);
+        }
+    }
+    nbhd = window.location.pathname.split('/')[1];
+    if (proj === '--init--') {
+        nbhd_proj = nbhd;
+    } else {
+        nbhd_proj = `${nbhd}/${proj}`;
+    }
+    return (rest ? '/rest/' : '/') + nbhd_proj;
 }
 
 function slugify(text) {

http://git-wip-us.apache.org/repos/asf/allura/blob/8324dd65/Allura/allura/tests/functional/test_neighborhood.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_neighborhood.py b/Allura/allura/tests/functional/test_neighborhood.py
index d946c7b..593a34c 100644
--- a/Allura/allura/tests/functional/test_neighborhood.py
+++ b/Allura/allura/tests/functional/test_neighborhood.py
@@ -949,6 +949,9 @@ class TestNeighborhood(TestController):
         assert 'View More Projects' in str(link)
         assert link['href'] == '/adobe/'
 
+    def test_nav_json(self):
+        self.app.get('/p/_nav.json')
+
 
 class TestPhoneVerificationOnProjectRegistration(TestController):
     def test_phone_verification_fragment_renders(self):


[4/4] allura git commit: [#7919] unify _lookup for regular nbhd controller and rest controller

Posted by br...@apache.org.
[#7919] unify _lookup for regular nbhd controller and rest controller

Enables /rest/p/admin/configure_tool_grouping to work for nbhd-level
project nav settings.


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

Branch: refs/heads/db/7919
Commit: e1ca8deb8423f54ffbeee4f8163a0f652ec6fc2d
Parents: 8324dd6
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Nov 3 14:24:05 2015 -0500
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Tue Nov 3 14:30:35 2015 -0500

----------------------------------------------------------------------
 Allura/allura/controllers/project.py        | 51 ++-------------
 Allura/allura/controllers/rest.py           | 83 +++++++++++++++++-------
 Allura/allura/ext/admin/admin_main.py       |  3 +-
 Allura/allura/tests/functional/test_rest.py | 15 +++++
 4 files changed, 81 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/e1ca8deb/Allura/allura/controllers/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index 0d689a8..c05a165 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -19,7 +19,6 @@ import re
 import logging
 from datetime import datetime, timedelta
 from urllib import unquote
-from hashlib import sha1
 
 from bson import ObjectId
 from tg import expose, flash, redirect, validate, request, config, session
@@ -29,7 +28,6 @@ from paste.deploy.converters import asbool
 from webob import exc
 import jinja2
 import pymongo
-from formencode.api import Invalid
 
 from ming.utils import LazyProperty
 
@@ -42,7 +40,8 @@ from allura.lib import utils
 from allura.lib.decorators import require_post
 from allura.controllers.error import ErrorController
 from allura.controllers.feed import FeedArgs, FeedController
-from allura.lib.security import require_access, has_access
+from allura.controllers.rest import nbhd_lookup_first_path
+from allura.lib.security import require_access
 from allura.lib.security import RoleCache
 from allura.lib.widgets import forms as ff
 from allura.lib.widgets import form_fields as ffw
@@ -58,8 +57,7 @@ log = logging.getLogger(__name__)
 class W:
     resize_editor = ffw.AutoResizeTextarea()
     project_summary = plw.ProjectSummary()
-    add_project = plugin.ProjectRegistrationProvider.get().add_project_widget(
-        antispam=True)
+    add_project = plugin.ProjectRegistrationProvider.get().add_project_widget(antispam=True)
     page_list = ffw.PageList()
     page_size = ffw.PageSize()
     project_select = ffw.NeighborhoodProjectSelect
@@ -75,7 +73,6 @@ class NeighborhoodController(object):
     def __init__(self, neighborhood):
         self.neighborhood = neighborhood
         self.neighborhood_name = self.neighborhood.name
-        self.prefix = self.neighborhood.shortname_prefix
         self.browse = NeighborhoodProjectBrowseController(neighborhood=self.neighborhood)
         # 'admin' without underscore will pass through to _lookup which will find the regular "admin" tool mounted
         # on the --init-- Project record for this neighborhood.
@@ -92,47 +89,7 @@ class NeighborhoodController(object):
 
     @expose()
     def _lookup(self, pname, *remainder):
-        pname = unquote(pname)
-        provider = plugin.ProjectRegistrationProvider.get()
-        try:
-            provider.shortname_validator.to_python(
-                pname, check_allowed=False, neighborhood=self.neighborhood)
-        except Invalid:
-            project = None
-        else:
-            project = M.Project.query.get(
-                shortname=self.prefix + pname, neighborhood_id=self.neighborhood._id)
-        if project is None and self.prefix == 'u/':
-            # create user-project if it is missing
-            user = M.User.query.get(username=pname, disabled=False, pending=False)
-            if user:
-                project = user.private_project()
-                if project.shortname != self.prefix + pname:
-                    # might be different URL than the URL requested
-                    # e.g. if username isn't valid project name and user_project_shortname() converts the name
-                    redirect(project.url())
-        if project is None:
-            # look for neighborhood tools matching the URL
-            project = self.neighborhood.neighborhood_project
-            c.project = project
-            return ProjectController()._lookup(pname, *remainder)
-        if project and self.prefix == 'u/':
-            # make sure user-projects are associated with an enabled user
-            user = project.user_project_of
-            if not user or user.disabled or user.pending:
-                raise exc.HTTPNotFound
-        if project.database_configured == False:
-            if remainder == ('user_icon',):
-                redirect(g.forge_static('images/user.png'))
-            elif c.user.username == pname:
-                log.info('Configuring %s database for access to %r',
-                         pname, remainder)
-                project.configure_project(is_user_project=True)
-            else:
-                raise exc.HTTPNotFound, pname
-        c.project = project
-        if project is None or (project.deleted and not has_access(c.project, 'update')()):
-            raise exc.HTTPNotFound, pname
+        c.project, remainder = nbhd_lookup_first_path(self.neighborhood, pname, c.user, *remainder)
         return ProjectController(), remainder
 
     @expose('jinja:allura:templates/neighborhood_project_list.html')

http://git-wip-us.apache.org/repos/asf/allura/blob/e1ca8deb/Allura/allura/controllers/rest.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index 7723c0d..e59087c 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -19,6 +19,7 @@
 
 """REST Controller"""
 import logging
+from urllib import unquote
 
 import oauth2 as oauth
 from paste.util.converters import asbool
@@ -36,6 +37,7 @@ from allura.lib import security
 from allura.lib import plugin
 from allura.lib.exceptions import Invalid
 from allura.lib.decorators import require_post
+from allura.lib.security import has_access
 
 log = logging.getLogger(__name__)
 action_logger = h.log_action(log, 'API:')
@@ -271,6 +273,61 @@ class AppRestControllerMixin(object):
         return rest_has_access(c.app, user, perm)
 
 
+def nbhd_lookup_first_path(nbhd, name, current_user, *remainder):
+    """
+    Resolve first part of a neighborhood url.  May raise 404, redirect, or do other side effects.
+
+    Shared between NeighborhoodController and NeighborhoodRestController
+
+    :param nbhd: neighborhood
+    :param name: project or tool name (next part of url)
+    :param current_user: a User
+    :param remainder: remainder of url
+
+    :return: project (to be set as c.project)
+    :return: remainder (possibly modified)
+    """
+
+    prefix = nbhd.shortname_prefix
+    pname = unquote(name)
+    provider = plugin.ProjectRegistrationProvider.get()
+    try:
+        provider.shortname_validator.to_python(pname, check_allowed=False, neighborhood=nbhd)
+    except Invalid:
+        project = None
+    else:
+        project = M.Project.query.get(shortname=prefix + pname, neighborhood_id=nbhd._id)
+    if project is None and prefix == 'u/':
+        # create user-project if it is missing
+        user = M.User.query.get(username=pname, disabled=False, pending=False)
+        if user:
+            project = user.private_project()
+            if project.shortname != prefix + pname:
+                # might be different URL than the URL requested
+                # e.g. if username isn't valid project name and user_project_shortname() converts the name
+                redirect(project.url())
+    if project is None:
+        # look for neighborhood tools matching the URL
+        project = nbhd.neighborhood_project
+        return project, (pname,) + remainder  # include pname in new remainder, it is actually the nbhd tool path
+    if project and prefix == 'u/':
+        # make sure user-projects are associated with an enabled user
+        user = project.user_project_of
+        if not user or user.disabled or user.pending:
+            raise exc.HTTPNotFound
+    if project.database_configured is False:
+        if remainder == ('user_icon',):
+            redirect(g.forge_static('images/user.png'))
+        elif current_user.username == pname:
+            log.info('Configuring %s database for access to %r', pname, remainder)
+            project.configure_project(is_user_project=True)
+        else:
+            raise exc.HTTPNotFound, pname
+    if project is None or (project.deleted and not has_access(project, 'update')()):
+        raise exc.HTTPNotFound, pname
+    return project, remainder
+
+
 class NeighborhoodRestController(object):
 
     def __init__(self, neighborhood):
@@ -282,29 +339,9 @@ class NeighborhoodRestController(object):
 
     @expose()
     def _lookup(self, name=None, *remainder):
-
-        # TODO: make this match NeighborhoodController._lookup so that /rest/p/admin/configure_tool_grouping works
-
-        provider = plugin.ProjectRegistrationProvider.get()
-        try:
-            provider.shortname_validator.to_python(
-                name, check_allowed=False, neighborhood=self._neighborhood)
-        except Invalid:
-            raise exc.HTTPNotFound, name
-        name = self._neighborhood.shortname_prefix + name
-        project = M.Project.query.get(
-            shortname=name, neighborhood_id=self._neighborhood._id, deleted=False)
-        log.info('nbhd rest proj=%s ... %s', project, name)
-        if not project:
-            raise exc.HTTPNotFound, name
-
-        if project and name and name.startswith('u/'):
-            # make sure user-projects are associated with an enabled user
-            user = project.user_project_of
-            if not user or user.disabled:
-                raise exc.HTTPNotFound
-
-        c.project = project
+        if not name:
+            raise exc.HTTPNotFound
+        c.project, remainder = nbhd_lookup_first_path(self._neighborhood, name, c.user, *remainder)
         return ProjectRestController(), remainder
 
 

http://git-wip-us.apache.org/repos/asf/allura/blob/e1ca8deb/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 99cf879..4d361bb 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -862,7 +862,7 @@ class ProjectAdminRestController(BaseController):
         }
 
     @expose('json:')
-    @require_post()
+    @require_post() # FIXME
     def mount_point(self, mount_point=None, **kw):
         """
         Returns a tool from a given mount point
@@ -879,6 +879,7 @@ class ProjectAdminRestController(BaseController):
 
         try:
             info = json.dumps(tool)
+            # FIXME
         except TypeError:
             info = "Could not serialize tool."
 

http://git-wip-us.apache.org/repos/asf/allura/blob/e1ca8deb/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 1a2006d..90b71ed 100644
--- a/Allura/allura/tests/functional/test_rest.py
+++ b/Allura/allura/tests/functional/test_rest.py
@@ -248,6 +248,21 @@ class TestRestHome(TestRestApiBase):
         assert_equal(r.status_int, 200)
         assert_equal(r.json['result'], False)
 
+    def test_neighborhood(self):
+        self.api_get('/rest/p/', status=404)
+
+    def test_neighborhood_tools(self):
+        r = self.api_get('/rest/p/wiki/Home/')
+        assert_equal(r.status_int, 200)
+        assert_equal(r.json['title'], 'Home')
+
+        r = self.api_get('/rest/p/admin/installable_tools')
+        assert_equal(r.status_int, 403)
+
+        r = self.api_get('/rest/p/admin/installable_tools', user='root')
+        assert_equal(r.status_int, 200)
+        assert [t for t in r.json['tools'] if t['tool_label'] == 'Wiki'], r.json
+
     def test_project_has_access_no_params(self):
         self.api_get('/rest/p/test/has_access', status=404)
         self.api_get('/rest/p/test/has_access?user=root', status=404)