diff --git a/opentech/apply/funds/models/applications.py b/opentech/apply/funds/models/applications.py
index e9aa7fe05c85db10f40eb0255d6b946cf4c690ff..bb7af8ebaed272ac944f9fdb821d00de2dd79b86 100644
--- a/opentech/apply/funds/models/applications.py
+++ b/opentech/apply/funds/models/applications.py
@@ -115,8 +115,8 @@ class RoundBase(WorkflowStreamForm, SubmittableStreamForm):  # type: ignore
         FieldPanel('reviewers'),
         ReadOnlyPanel('get_workflow_name_display', heading="Workflow"),
         # Forms comes from parental key in models/forms.py
-        ReadOnlyInlinePanel('forms', help_text="Are copied from the parent fund."),
-        ReadOnlyInlinePanel('review_forms', help_text="Are copied from the parent fund."),
+        ReadOnlyInlinePanel('forms', help_text="Copied from the fund."),
+        ReadOnlyInlinePanel('review_forms', help_text="Copied from the fund."),
     ]
 
     edit_handler = TabbedInterface([
diff --git a/opentech/apply/funds/models/forms.py b/opentech/apply/funds/models/forms.py
index 24650f12215d49ded3c8b044f2079786075d877f..40bf70d7308353816a2ba3b97bde955f0a31ba99 100644
--- a/opentech/apply/funds/models/forms.py
+++ b/opentech/apply/funds/models/forms.py
@@ -42,7 +42,7 @@ class AbstractRelatedForm(Orderable):
 
     def __eq__(self, other):
         try:
-            return self.fields == other.fields
+            return self.fields == other.fields and self.sort_order == other.sort_order
         except AttributeError:
             return False
 
@@ -80,7 +80,7 @@ class AbstractRelatedReviewForm(Orderable):
 
     def __eq__(self, other):
         try:
-            return self.fields == other.fields
+            return self.fields == other.fields and self.sort_order == other.sort_order
         except AttributeError:
             return False
 
diff --git a/opentech/apply/funds/tests/factories/models.py b/opentech/apply/funds/tests/factories/models.py
index 12f7d836a15b9a4000c1e9c5bc30592d6cdaeeab..144d4ef4eac47e6e961a2017f4070f7e94fb2fb5 100644
--- a/opentech/apply/funds/tests/factories/models.py
+++ b/opentech/apply/funds/tests/factories/models.py
@@ -42,18 +42,25 @@ __all__ = [
     'LabSubmissionFactory',
     'SealedRoundFactory',
     'SealedSubmissionFactory',
+    'workflow_for_stages',
 ]
 
 
-class FundTypeFactory(wagtail_factories.PageFactory):
+def workflow_for_stages(stages):
+    return list(FundType.WORKFLOW_CHOICES.keys())[stages - 1]
+
+
+class AbstractApplicationFactory(wagtail_factories.PageFactory):
     class Meta:
-        model = FundType
+        abstract = True
 
     class Params:
         workflow_stages = 1
 
+    title = factory.Faker('sentence')
+
     # Will need to update how the stages are identified as Fund Page changes
-    workflow_name = factory.LazyAttribute(lambda o: list(FundType.WORKFLOW_CHOICES.keys())[o.workflow_stages - 1])
+    workflow_name = factory.LazyAttribute(lambda o: workflow_for_stages(o.workflow_stages))
 
     @factory.post_generation
     def parent(self, create, extracted_parent, **parent_kwargs):
@@ -85,7 +92,12 @@ class FundTypeFactory(wagtail_factories.PageFactory):
                 )
 
 
-class RequestForPartnersFactory(FundTypeFactory):
+class FundTypeFactory(AbstractApplicationFactory):
+    class Meta:
+        model = FundType
+
+
+class RequestForPartnersFactory(AbstractApplicationFactory):
     class Meta:
         model = RequestForPartners
 
@@ -156,16 +168,10 @@ class RoundBaseFormFactory(AbstractRelatedFormFactory):
     round = factory.SubFactory(RoundFactory)
 
 
-class LabFactory(wagtail_factories.PageFactory):
+class LabFactory(AbstractApplicationFactory):
     class Meta:
         model = LabType
 
-    class Params:
-        workflow_stages = 1
-        number_forms = 1
-
-    # Will need to update how the stages are identified as Fund Page changes
-    workflow_name = factory.LazyAttribute(lambda o: list(FundType.WORKFLOW_CHOICES.keys())[o.workflow_stages - 1])
     lead = factory.SubFactory(StaffFactory)
 
     @factory.post_generation
@@ -209,7 +215,7 @@ class ApplicationSubmissionFactory(factory.DjangoModelFactory):
         form_fields=factory.SelfAttribute('..form_fields'),
     )
     page = factory.SubFactory(FundTypeFactory)
-    workflow_name = factory.LazyAttribute(lambda o: list(FundType.WORKFLOW_CHOICES.keys())[o.workflow_stages - 1])
+    workflow_name = factory.LazyAttribute(lambda o: workflow_for_stages(o.workflow_stages))
     round = factory.SubFactory(
         RoundFactory,
         workflow_name=factory.SelfAttribute('..workflow_name'),
diff --git a/opentech/apply/funds/tests/test_admin_form.py b/opentech/apply/funds/tests/test_admin_form.py
index 527cb2c63436d713fdfa95cb9071f6eb287406cd..8c4336f10dc74e8e2b6ba78b75f1bee723feb2bf 100644
--- a/opentech/apply/funds/tests/test_admin_form.py
+++ b/opentech/apply/funds/tests/test_admin_form.py
@@ -4,16 +4,22 @@ import factory
 
 from opentech.apply.funds.models import FundType
 
-from .factories import ApplicationFormFactory, FundTypeFactory
+from .factories import ApplicationFormFactory, FundTypeFactory, workflow_for_stages
 from opentech.apply.review.tests.factories import ReviewFormFactory
 
 
-def formset_base(field, total, delete, factory):
+def formset_base(field, total, delete, factory, same=False):
     base_data = {
         f'{field}-TOTAL_FORMS': total + delete,
         f'{field}-INITIAL_FORMS': 0,
     }
-    application_forms = factory.create_batch(total + delete)
+
+    required_forms = total + delete
+
+    if not same:
+        application_forms = factory.create_batch(required_forms)
+    else:
+        application_forms = [factory()] * required_forms
 
     deleted = 0
     for i, form in enumerate(application_forms):
@@ -28,11 +34,15 @@ def formset_base(field, total, delete, factory):
     return base_data
 
 
-def form_data(number_forms=0, delete=0):
-    form_data = formset_base('forms', number_forms, delete, factory=ApplicationFormFactory)
-    review_form_data = formset_base('review_forms', number_forms, False, factory=ReviewFormFactory)
+def form_data(number_forms=0, delete=0, stages=None, same_forms=False):
+    form_data = formset_base('forms', number_forms, delete, same=same_forms, factory=ApplicationFormFactory)
+    review_form_data = formset_base('review_forms', number_forms, False, same=same_forms, factory=ReviewFormFactory)
     form_data.update(review_form_data)
-    form_data.update(factory.build(dict, FACTORY_CLASS=FundTypeFactory))
+
+    fund_data = factory.build(dict, FACTORY_CLASS=FundTypeFactory)
+    fund_data['workflow_name'] = workflow_for_stages(stages or number_forms)
+
+    form_data.update(fund_data)
     return form_data
 
 
@@ -56,7 +66,7 @@ class TestWorkflowFormAdminForm(TestCase):
         self.assertTrue(form.is_valid(), form.errors.as_text())
 
     def test_doesnt_validates_with_two_forms_one_stage(self):
-        form = self.submit_data(form_data(2))
+        form = self.submit_data(form_data(2, stages=1))
         self.assertFalse(form.is_valid())
         self.assertTrue(form.errors['__all__'])
         formset_errors = form.formsets['forms'].errors
@@ -64,3 +74,7 @@ class TestWorkflowFormAdminForm(TestCase):
         self.assertFalse(formset_errors[0])
         # second form is too many
         self.assertTrue(formset_errors[1]['form'])
+
+    def test_can_save_two_forms(self):
+        form = self.submit_data(form_data(2))
+        self.assertTrue(form.is_valid())
diff --git a/opentech/apply/funds/tests/test_admin_views.py b/opentech/apply/funds/tests/test_admin_views.py
new file mode 100644
index 0000000000000000000000000000000000000000..b065ad10da7a3acf82b850bb41ce1d33478af9da
--- /dev/null
+++ b/opentech/apply/funds/tests/test_admin_views.py
@@ -0,0 +1,44 @@
+from django.test import TestCase
+from django.urls import reverse
+
+from opentech.apply.users.tests.factories import SuperUserFactory
+from opentech.apply.home.factories import ApplyHomePageFactory
+
+from .test_admin_form import form_data
+
+
+class TestFundCreationView(TestCase):
+    @classmethod
+    def setUpTestData(cls):
+        cls.user = SuperUserFactory()
+        cls.home = ApplyHomePageFactory()
+
+    def create_page(self, forms=1, same_forms=False):
+        self.client.force_login(self.user)
+        url = reverse('wagtailadmin_pages:add', args=('funds', 'fundtype', self.home.id))
+
+        data = form_data(forms, same_forms=same_forms)
+        data['action-publish'] = True
+
+        response = self.client.post(url, data=data, secure=True, follow=True)
+        self.assertContains(response, 'success')
+
+        self.home.refresh_from_db()
+        fund = self.home.get_children().first()
+
+        return fund.specific
+
+    def test_can_create_fund(self):
+        fund = self.create_page()
+        self.assertEqual(fund.forms.count(), 1)
+        self.assertEqual(fund.review_forms.count(), 1)
+
+    def test_can_create_multi_phase_fund(self):
+        fund = self.create_page(2)
+        self.assertEqual(fund.forms.count(), 2)
+        self.assertEqual(fund.review_forms.count(), 2)
+
+    def test_can_create_multi_phase_fund_reuse_forms(self):
+        fund = self.create_page(2, same_forms=True)
+        self.assertEqual(fund.forms.count(), 2)
+        self.assertEqual(fund.review_forms.count(), 2)
diff --git a/opentech/apply/funds/tests/test_models.py b/opentech/apply/funds/tests/test_models.py
index b4e043677ce42d10b2195fdd575ec1c0f59e84b6..a32420a93bf3aef0db8e267539c1ec49441d5adf 100644
--- a/opentech/apply/funds/tests/test_models.py
+++ b/opentech/apply/funds/tests/test_models.py
@@ -9,8 +9,6 @@ from django.core import mail
 from django.core.exceptions import ValidationError
 from django.test import TestCase, override_settings
 
-from wagtail.core.models import Site
-
 from opentech.apply.funds.models import ApplicationSubmission
 from opentech.apply.funds.blocks import EmailBlock, FullNameBlock
 from opentech.apply.funds.workflow import Request
@@ -183,7 +181,6 @@ class TestRoundModelWorkflowAndForms(TestCase):
 @override_settings(ROOT_URLCONF='opentech.apply.urls')
 class TestFormSubmission(TestCase):
     def setUp(self):
-        self.site = Site.objects.first()
         self.User = get_user_model()
 
         self.email = 'test@test.com'
@@ -191,8 +188,7 @@ class TestFormSubmission(TestCase):
 
         fund = FundTypeFactory()
 
-        self.site.root_page = fund
-        self.site.save()
+        self.site = fund.get_site()
 
         self.round_page = RoundFactory(parent=fund, now=True)
         self.lab_page = LabFactory(lead=self.round_page.lead)
@@ -212,9 +208,10 @@ class TestFormSubmission(TestCase):
 
         request = make_request(user, data, method='post', site=self.site)
 
-        try:
+        if page.get_parent().id != self.site.root_page.id:
+            # Its a fund
             response = page.get_parent().serve(request)
-        except AttributeError:
+        else:
             response = page.serve(request)
 
         if not ignore_errors:
diff --git a/opentech/apply/home/factories.py b/opentech/apply/home/factories.py
index aa53b814af17d25b41e4a9a896fa12f3892c0df5..89628412d6bbb099533b39452a8c0ce1a54a037b 100644
--- a/opentech/apply/home/factories.py
+++ b/opentech/apply/home/factories.py
@@ -8,6 +8,20 @@ class ApplyHomePageFactory(wagtail_factories.PageFactory):
     class Meta:
         model = ApplyHomePage
 
+    @classmethod
+    def _create(cls, model_class, *args, **kwargs):
+        try:
+            # We cant use "django_get_or_create" in meta as wagtail factories wont respect it
+            return model_class.objects.get(slug=kwargs['slug'])
+        except model_class.DoesNotExist:
+            return super()._create(model_class, *args, **kwargs)
+
+    @factory.post_generation
+    def parent(self, create, extracted_parent, **parent_kwargs):
+        if create and not self.get_parent():
+            root = ApplyHomePage.get_first_root_node()
+            root.add_child(instance=self)
+
     @factory.post_generation
     def site(self, create, extracted_site, **site_kwargs):
         if create:
diff --git a/opentech/apply/home/wagtail_hooks.py b/opentech/apply/home/wagtail_hooks.py
new file mode 100644
index 0000000000000000000000000000000000000000..07ad3f9b3001bd54bbdee1ada3dbfd23defad8fc
--- /dev/null
+++ b/opentech/apply/home/wagtail_hooks.py
@@ -0,0 +1,23 @@
+from wagtail.core import hooks
+
+from opentech.apply.users.groups import STAFF_GROUP_NAME
+
+from .models import ApplyHomePage
+
+
+@hooks.register('construct_explorer_page_queryset')
+def exclude_fund_pages(parent_page, pages, request):
+    # Don't allow editors to access the Apply pages in the explorer unless they know whats up
+    if not request.user.is_superuser:
+        pages = pages.not_descendant_of(ApplyHomePage.objects.first(), inclusive=True)
+
+    return pages
+
+
+@hooks.register('construct_main_menu')
+def hide_explorer_menu_item_from_frank(request, menu_items):
+    if not request.user.is_superuser:
+        groups = list(request.user.groups.all())
+        # If the user is only in the staff group they should never see the explorer menu item
+        if len(groups) == 1 and groups[0].name == STAFF_GROUP_NAME:
+            menu_items[:] = [item for item in menu_items if item.name != 'explorer']
diff --git a/opentech/public/utils/wagtail_hooks.py b/opentech/public/utils/wagtail_hooks.py
index ec99c6dc25710552d8f0cf59acc69216fcde660c..51d7eb042495a8c5a31816cc8cab74d6b8896ce7 100644
--- a/opentech/public/utils/wagtail_hooks.py
+++ b/opentech/public/utils/wagtail_hooks.py
@@ -1,6 +1,6 @@
+from wagtail.core import hooks
 from wagtail.contrib.modeladmin.options import ModelAdminGroup, ModelAdmin, modeladmin_register
 
-
 from opentech.public.news.models import NewsType
 from opentech.public.people.models import PersonType
 
@@ -22,3 +22,9 @@ class TaxonomiesModelAdminGroup(ModelAdminGroup):
 
 
 modeladmin_register(TaxonomiesModelAdminGroup)
+
+
+# Hide forms from the side menu, remove if adding public.forms back in
+@hooks.register('construct_main_menu')
+def hide_snippets_menu_item(request, menu_items):
+    menu_items[:] = [item for item in menu_items if item.name != 'forms']