Add and use a DeveloperAccessChecker.
diff --git a/app/melange/request/access.py b/app/melange/request/access.py
index a44f92c..8c3f191 100644
--- a/app/melange/request/access.py
+++ b/app/melange/request/access.py
@@ -19,9 +19,12 @@
 from melange.request import exception
 from soc.logic import links
 
-_DEF_NOT_HOST = translation.ugettext(
+_MESSAGE_NOT_PROGRAM_ADMINISTRATOR = translation.ugettext(
     'You need to be a program administrator to access this page.')
 
+_MESSAGE_NOT_DEVELOPER = translation.ugettext(
+    'This page is only accessible to developers.')
+
 
 def ensureLoggedIn(self):
   """Ensures that the user is logged in.
@@ -97,6 +100,20 @@
     elif not data.gae_user:
       raise exception.LoginRequired()
     elif not data.is_host:
-      raise exception.Forbidden(message=_DEF_NOT_HOST)
+      raise exception.Forbidden(message=_MESSAGE_NOT_PROGRAM_ADMINISTRATOR)
 
 PROGRAM_ADMINISTRATOR_ACCESS_CHECKER = ProgramAdministratorAccessChecker()
+
+
+# TODO(nathaniel): Eliminate this or make it a
+# "SiteAdministratorAccessChecker" - there should be no aspects of Melange
+# that require developer action or are limited only to developers.
+class DeveloperAccessChecker(AccessChecker):
+  """AccessChecker that ensures that the user is a developer."""
+
+  def checkAccess(self, data, check, mutator):
+    """See AccessChecker.checkAccess for specification."""
+    if not data.is_developer:
+      raise exception.Forbidden(message=_MESSAGE_NOT_DEVELOPER)
+
+DEVELOPER_ACCESS_CHECKER = DeveloperAccessChecker()
diff --git a/app/soc/views/site.py b/app/soc/views/site.py
index 7cb0c30..b232e5d 100644
--- a/app/soc/views/site.py
+++ b/app/soc/views/site.py
@@ -24,6 +24,7 @@
 from django.forms import widgets as django_widgets
 from django.utils.translation import ugettext
 
+from melange.request import access
 from melange.request import exception
 from soc.logic import cleaning
 from soc.logic import site as site_logic
@@ -70,6 +71,11 @@
 class EditSitePage(base.RequestHandler):
   """View for the participant profile."""
 
+  # TODO(nathaniel): This page should use something like a "site admin
+  # access checker" - there should be no pages accessible only to
+  # developers.
+  access_checker = access.DEVELOPER_ACCESS_CHECKER
+
   def djangoURLPatterns(self):
     return [
         django_url(r'^site/edit$', self, name='edit_site_settings'),
@@ -85,10 +91,6 @@
 
     return {'data': json_data}
 
-  def checkAccess(self, data, check, mutator):
-    if not data.is_developer:
-      raise exception.Forbidden(message=DEF_NO_DEVELOPER)
-
   def templatePath(self):
     # TODO: make this specific to the current active program
     return 'soc/site/base.html'
diff --git a/tests/app/melange/request/test_access.py b/tests/app/melange/request/test_access.py
index 54c492b..6b2984e 100644
--- a/tests/app/melange/request/test_access.py
+++ b/tests/app/melange/request/test_access.py
@@ -19,6 +19,8 @@
 from nose.plugins import skip
 
 from melange.request import access
+from melange.request import exception
+from soc.views.helper import request_data
 
 
 class Explosive(object):
@@ -65,3 +67,24 @@
   def testAnonymousDeniedAccess(self):
     """Tests that logged-out users are denied access."""
     raise skip.SkipTest()
+
+
+class DeveloperAccessCheckerTest(unittest.TestCase):
+  """Tests the DeveloperAccessChecker class."""
+
+  def testDeveloperAccessAllowed(self):
+    data = request_data.RequestData(None, None, None)
+    # TODO(nathaniel): Reaching around RequestHandler public API.
+    data._is_developer = True
+
+    access_checker = access.DeveloperAccessChecker()
+    access_checker.checkAccess(data, None, None)
+
+  def testNonDeveloperAccessDenied(self):
+    data = request_data.RequestData(None, None, None)
+    # TODO(nathaniel): Reaching around RequestHandler public API.
+    data._is_developer = False
+
+    access_checker = access.DeveloperAccessChecker()
+    with self.assertRaises(exception.UserError):
+      access_checker.checkAccess(data, None, None)