Switch to side-by-side view

--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -18,6 +18,7 @@
 from allura.lib import plugin
 from allura.lib import exceptions
 from allura.lib import security
+from allura.lib.security import has_access
 
 from .session import main_orm_session
 from .session import project_doc_session, project_orm_session
@@ -437,6 +438,35 @@
             raise exceptions.ToolError, 'Mount point "%s" is invalid' % name
         provider = plugin.ProjectRegistrationProvider.get()
         return provider.register_subproject(self, name, user or c.user, install_apps)
+
+    def ordered_mounts(self):
+        '''Returns an array of a projects mounts (tools and sub-projects) in
+        toolbar order.'''
+        result = []
+        for sub in self.direct_subprojects:
+            result.append({'ordinal':sub.ordinal, 'sub':sub})
+        for ac in self.app_configs:
+            if ac.tool_name != 'search':
+                ordinal = ac.options['ordinal'] if 'ordinal' in ac.options else 0
+                result.append({'ordinal':ordinal, 'ac':ac})
+        return sorted(result, key=lambda e: e['ordinal'])
+
+    def first_mount(self, required_access=None):
+        '''Returns the first (toolbar order) mount, or the first mount to
+        which the user has the required access.'''
+        mounts = self.ordered_mounts()
+        if mounts and required_access is None:
+            return mounts[0]
+        for mount in mounts:
+            if 'sub' in mount:
+                obj = mount['sub']
+            elif 'ac' in mount:
+                obj = self.app_instance(mount['ac'])
+            else:
+                continue
+            if has_access(obj, required_access):
+                return mount
+        return None
 
     def delete(self):
         # Cascade to subprojects