Make use of PolyModel for Works, Documents, etc. Add some (but not all) of
authorTodd Larsen <tlarsen@google.com>
Mon, 29 Sep 2008 15:31:50 +0000
changeset 206 832335761384
parent 205 4a86df751222
child 207 8ecc2e4198cd
Make use of PolyModel for Works, Documents, etc. Add some (but not all) of the missing Models related to Documents. Refactor site settings to be useful for any "/home" page view. Make the resulting home settings store a reference to the Document, rather than looking up the Document by a hard-code key name. This is to set the stage for Document editing being generic, and then being able to select from some existing documents which one to use as the "/home" static content. This makes it possible to pre-edit several Documents, have them Reviewed, and then quickly change the "/home" page content as a setting, rather than editing the Document in the settings form (though settings forms might still embed the current document into their form for convenience). Patch by: Todd Larsen Review by: Pawel Solyga Review URL: http://codereviews.googleopensourceprograms.com/1001
app/soc/logic/document.py
app/soc/logic/key_name.py
app/soc/logic/site/settings.py
app/soc/models/document.py
app/soc/models/documentation.py
app/soc/models/home_settings.py
app/soc/models/proposal.py
app/soc/models/site_settings.py
app/soc/models/work.py
app/soc/models/works_authors.py
app/soc/views/site/home.py
--- a/app/soc/logic/document.py	Mon Sep 29 14:03:45 2008 +0000
+++ b/app/soc/logic/document.py	Mon Sep 29 15:31:50 2008 +0000
@@ -21,40 +21,33 @@
   '"Pawel Solyga" <pawel.solyga@gmail.com>',
   ]
 
+
 from google.appengine.ext import db
 
+from soc.logic import key_name
+
 import soc.models.document
 import soc.logic.model
 
-def getDocumentFromPath(path):
+
+def getDocument(path, link_name):
   """Returns Document entity for a given path, or None if not found.  
     
   Args:
     path: a request path of the Document that uniquely identifies it
   """
   # lookup by Doc:path key name
-  key_name = getDocumentKeyNameForPath(path)
+  name = key_name.nameDocument(path, link_name)
   
-  if key_name:
-    document = soc.models.document.Document.get_by_key_name(key_name)
+  if name:
+    document = soc.models.document.Document.get_by_key_name(name)
   else:
     document = None
   
   return document
 
-def getDocumentKeyNameForPath(path):
-  """Return a Datastore key_name for a Document from the path.
-  
-  Args:
-    path: a request path of the Document that uniquely identifies it
-  """
-  if not path:
-    return None
 
-  return 'Doc:%s' % path
-
-
-def updateOrCreateDocumentFromPath(path, **document_properties):
+def updateOrCreateDocument(path, **document_properties):
   """Update existing Document entity, or create new one with supplied properties.
 
   Args:
@@ -68,13 +61,13 @@
     supplied path and properties.
   """
   # attempt to retrieve the existing Document
-  document = getDocumentFromPath(path)
+  document = getDocument(path, document_properties['link_name'])
 
   if not document:
     # document did not exist, so create one in a transaction
-    key_name = getDocumentKeyNameForPath(path)
+    name = key_name.nameDocument(path, document_properties['link_name'])
     document = soc.models.document.Document.get_or_insert(
-      key_name, **document_properties)
+        name, **document_properties)
 
   # there is no way to be sure if get_or_insert() returned a new Document or
   # got an existing one due to a race, so update with document_properties anyway,
--- a/app/soc/logic/key_name.py	Mon Sep 29 14:03:45 2008 +0000
+++ b/app/soc/logic/key_name.py	Mon Sep 29 15:31:50 2008 +0000
@@ -22,15 +22,32 @@
   ]
 
 
+import soc.models.document
 import soc.models.site_settings
 import soc.models.user
 
 
 def nameSiteSettings(path):
   """Returns a SiteSettings key name constructed from a supplied path."""
-  return '%s:%s' % (soc.models.site_settings.SiteSettings.__name__, path)
+  return '%(type)s:%(path)s' % {
+      'type': soc.models.site_settings.SiteSettings.__name__,
+      'path': path,
+      }
+
+
+def nameDocument(path, link_name):
+  """Returns a Document key name constructed from a path and link name."""
+  return '%(type)s:%(path)s/%(link_name)s' % {
+      'type': soc.models.document.Document.__name__,
+      'path': path,
+      'link_name': link_name,
+      }
 
 
 def nameUser(email):
   """Returns a User key name constructed from a supplied email address."""
-  return '%s:%s' % (soc.models.user.User.__name__, email)
+  return '%(type)s:%(email)s' % {
+      'type': soc.models.user.User.__name__,
+      'email': email,
+      }
+
--- a/app/soc/logic/site/settings.py	Mon Sep 29 14:03:45 2008 +0000
+++ b/app/soc/logic/site/settings.py	Mon Sep 29 15:31:50 2008 +0000
@@ -29,36 +29,24 @@
 import soc.logic.model
 
 
-def getSiteSettingsFromPath(path):
+def getSiteSettings(path):
   """Returns SiteSettings entity for a given path, or None if not found.  
     
   Args:
     path: a request path of the SiteSettings that uniquely identifies it
   """
   # lookup by Settings:path key name
-  key_name = getSiteSettingsKeyNameForPath(path)
+  name = key_name.nameSiteSettings(path)
   
-  if key_name:
-    site_settings = soc.models.site_settings.SiteSettings.get_by_key_name(key_name)
+  if name:
+    site_settings = soc.models.site_settings.SiteSettings.get_by_key_name(name)
   else:
     site_settings = None
   
   return site_settings
 
 
-def getSiteSettingsKeyNameForPath(path):
-  """Return a Datastore key_name for a SiteSettings from the path.
-  
-  Args:
-    path: a request path of the SiteSettings that uniquely identifies it
-  """
-  if not path:
-    return None
-
-  return key_name.nameSiteSettings(path)
-
-
-def updateOrCreateSiteSettingsFromPath(path, **site_settings_properties):
+def updateOrCreateSiteSettings(path, **site_settings_properties):
   """Update existing SiteSettings entity, or create new one with supplied properties.
 
   Args:
@@ -72,13 +60,13 @@
     supplied path and properties.
   """
   # attempt to retrieve the existing Site Settings
-  site_settings = getSiteSettingsFromPath(path)
+  site_settings = getSiteSettings(path)
 
   if not site_settings:
     # site settings did not exist, so create one in a transaction
-    key_name = getSiteSettingsKeyNameForPath(path)
+    name = key_name.nameSiteSettings(path)
     site_settings = soc.models.site_settings.SiteSettings.get_or_insert(
-      key_name, **site_settings_properties)
+      name, **site_settings_properties)
 
   # there is no way to be sure if get_or_insert() returned a new SiteSettings or
   # got an existing one due to a race, so update with site_settings_properties anyway,
--- a/app/soc/models/document.py	Mon Sep 29 14:03:45 2008 +0000
+++ b/app/soc/models/document.py	Mon Sep 29 15:31:50 2008 +0000
@@ -20,50 +20,44 @@
   '"Pawel Solyga" <pawel.solyga@gmail.com>',
 ]
 
+
 from google.appengine.ext import db
 
+import polymodel
+
 from django.utils.translation import ugettext_lazy
 
-from soc.models import base
 import soc.models.user
+import soc.models.work
 
 
-class Document(base.ModelWithFieldAttributes):
+class Document(soc.models.work.Work):
   """Model of a Document.
   
-  Document is used for things like FAQs, front page text etc.
+  Document is used for things like FAQs, front page text, etc.
+
+  The specific way that the properties and relations inherited from Work
+  are used with a Document are described below.
+
+    work.title:  the title of the Document
+
+    work.abstract:  document summary displayed as a snippet in Document
+      list views
+
+    work.authors:  the Authors of the Work referred to by this relation
+      are the authors of the Document
+
+    work.reviews:  reviews of the Document by Reviewers
   """
 
-  #: Document title displayed on the top of the page
-  title = db.StringProperty(required=True,
-      verbose_name=ugettext_lazy('Title'))
-  title.help_text = ugettext_lazy(
-      'Document title displayed on the top of the page')
-
-  #: Document link name used in URLs
-  link_name = db.StringProperty(required=True,
-      verbose_name=ugettext_lazy('Link name'))
-  link_name.help_text = ugettext_lazy('Document link name used in URLs')
-
-  #: Document short name used for sidebar menu
-  short_name = db.StringProperty(required=True,
-      verbose_name=ugettext_lazy('Short name'))
-  short_name.help_text = ugettext_lazy(
-      'Document short name used for sidebar menu')
-  
-  #: Content of the document
-  content = db.TextProperty(
-      verbose_name=ugettext_lazy('Content'))
-  
-  #: Date when the document was created.
-  created = db.DateTimeProperty(auto_now_add=True)
-  
-  #: Date when the document was modified.
-  modified = db.DateTimeProperty(auto_now=True)
+  #: Required db.TextProperty containing the Document contents.
+  #: Unlike the work.abstract, which is considered "public" information,
+  #: the content is only to be displayed to Persons in Roles eligible to
+  #: view them (which may be anyone, for example, with the site front page).
+  content = db.TextProperty(verbose_name=ugettext_lazy('Content'))
   
   #: User who created this document.
+  #: TODO(pawel.solyga): replace this with WorkAuthors relation
   user = db.ReferenceProperty(reference_class=soc.models.user.User,
                               required=True, collection_name='documents')
 
-
-
--- a/app/soc/models/documentation.py	Mon Sep 29 14:03:45 2008 +0000
+++ b/app/soc/models/documentation.py	Mon Sep 29 15:31:50 2008 +0000
@@ -21,41 +21,43 @@
   '"Sverre Rabbelier" <sverre@rabbelier.nl>',
 ]
 
+
 from google.appengine.ext import db
 
-from soc import models
-import soc.models.person
+import soc.models.role
 import soc.models.work
 
-class Documentation(db.Model):
-  """Model of Documentation, which is a Work authored by Administrators."""
-    
-  #: A required 1:1 relationship with a Work entity that contains the
-  #: general "work" properties of the Documentation. The 
-  #: back-reference in the Work model is a Query named 
-  #: 'documentation'.
-  #:
-  #:   work.authors: The Authors of the Work referred to by this 
-  #:     relation are the Administrators (or Hosts) creating the
-  #:     Documentation.
-  #:
-  #:   work.title: The title of the Documentation (e.g. "Verification
-  #:     of Eligibility").
-  #:
-  #:   work.abstract: Summary of the contents of the 'attachment', or
-  #:     just an indication that the required documentation was 
-  #:     supplied but is not actually attached.
-  #:
-  #:   work.reviews: Annotations to the Documentation made by other
-  #:     Administrators.
-  work = db.ReferenceProperty(reference_class=models.work.Work, required=True,
-                              collection_name="proposal")
+
+class Documentation(soc.models.work.Work):
+  """Model of Documentation, which is a Work authored by Administrators.
+
+  Documentation is items like tax forms, letters from schools confirming
+  enrollment, etc., (often scanned in) that are attached to a Role as
+  documentation related to that Role.
+
+  The specific way that some properties and relations inherited from Work
+  are used with a piece of Documentation are described below.
+
+  work.title: The title of the Documentation (e.g. "Verification
+    of Eligibility").
+
+  work.abstract: Summary of the contents of the 'attachment', or
+    just an indication that the required documentation was 
+    supplied but is not actually attached.
+
+  work.authors: The Authors of the Work referred to by this 
+    relation are the Administrators (or Hosts) creating the
+    Documentation.
+
+  work.reviews: Annotations to the Documentation made by other
+    Administrators.
+  """
 
   #: a many:1 relationship of Documentation entities that pertain
-  #: to a single Person.  The back-reference in the Person model is a
-  #: Query named 'docs'.
-  person = db.ReferenceProperty(reference_class=models.person.Person,
-                                collection_name="docs")
+  #: to a single Role.  The back-reference in the Role model is a
+  #: Query named 'documentation'.
+  role = db.ReferenceProperty(reference_class=soc.models.role.Role,
+                              collection_name='documentation')
 
   #: An optional db.BlobProperty containing the documentation
   #: (usually a scanned image of a paper document or a PDF file).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/models/home_settings.py	Mon Sep 29 15:31:50 2008 +0000
@@ -0,0 +1,55 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed 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.
+
+"""This module contains the HomeSettings Model."""
+
+__authors__ = [
+  '"Pawel Solyga" <pawel.solyga@gmail.com>',
+  '"Todd Larsen" <tlarsen@google.com>',
+]
+
+
+from google.appengine.ext import db
+
+import polymodel
+
+from django.utils.translation import ugettext_lazy
+
+import soc.models.document
+
+
+class HomeSettings(polymodel.PolyModel):
+  """Model that stores settings for various Home pages.
+
+  This Model is the basis for more specific "/home" view settings, such as
+  SiteSettings, ProgramSettings, etc.
+  """
+  
+  #: Reference to Document containing the contents of the "/home" page
+  home = db.ReferenceProperty(
+    reference_class=soc.models.document.Document,
+    collection_name='home')
+  home.help_text = ugettext_lazy(
+      'Document to be used as the "/home" page static contents.')
+  
+  #: Valid ATOM or RSS feed url or None if unused. Feed entries are shown 
+  #: on the site page using Google's JavaScript blog widget  
+  feed_url = db.LinkProperty(verbose_name=ugettext_lazy('Feed URL'))
+  feed_url.help_text = ugettext_lazy(
+      'The URL should be a valid ATOM or RSS feed. '
+      'Feed entries are shown on the home page.')
+  
+
--- a/app/soc/models/proposal.py	Mon Sep 29 14:03:45 2008 +0000
+++ b/app/soc/models/proposal.py	Mon Sep 29 15:31:50 2008 +0000
@@ -21,38 +21,38 @@
   '"Pawel Solyga" <pawel.solyga@gmail.com>',
 ]
 
+
 from google.appengine.ext import db
 
-from soc import models
-from soc.models import base
 import soc.models.work
 
-class Proposal(base.ModelWithFieldAttributes):
+
+class Proposal(soc.models.work.Work):
   """Model of a Proposal, which is a specific form of a Work.
 
+  The specific way that the properties and relations inherited from Work
+  are used with a Proposal are described below.
+
+  work.title:  the title of the Proposal
+
+  work.abstract:  publicly displayed as a proposal abstract or summary
+
+  work.authors:  the Authors of the Work referred to by this relation
+    are the authors of the Proposal
+
+  work.reviews:  reviews of the Proposal by Reviewers
+
   A Proposal entity participates in the following relationships implemented 
   as a db.ReferenceProperty elsewhere in another db.Model:
 
   tasks)  an optional 1:many relationship of Task entities using the
     Proposal as their foundation.  This relation is implemented as the
     'tasks' back-reference Query of the Task model 'proposal' reference.
-
   """
 
-	#: Required 1:1 relationship with a Work entity that contains the
-	#: general "work" properties of the Proposal.  The back-reference in the Work
-	#: model is a Query named 'proposal'.
-	#: 
-	#: work.authors:  the Authors of the Work referred to by this relation
-	#: are the authors of the Proposal.
-	#: work.title:  the title of the Proposal.
-	#: work.abstract:  publicly displayed as a proposal abstract or summary.
-	#: work.reviews:  reviews of the Proposal by Reviewers.
-  work = db.ReferenceProperty(reference_class=models.work.Work, required=True,
-                              collection_name="proposal")
-
   #: Required db.TextProperty describing the proposal in detail.
   #: Unlike the work.abstract, which is considered "public" information,
-	#: the contents of 'details' is only to be displayed to Persons in roles
+  #: the contents of 'details' is only to be displayed to Persons in Roles
   #: that have a "need to know" the details.
   details = db.TextProperty(required=True)
+
--- a/app/soc/models/site_settings.py	Mon Sep 29 14:03:45 2008 +0000
+++ b/app/soc/models/site_settings.py	Mon Sep 29 15:31:50 2008 +0000
@@ -20,21 +20,17 @@
   '"Pawel Solyga" <pawel.solyga@gmail.com>',
 ]
 
+
 from google.appengine.ext import db
 
 from django.utils.translation import ugettext_lazy
 
-from soc.models import base
+import soc.models.home_settings
 
-class SiteSettings(base.ModelWithFieldAttributes):
+
+class SiteSettings(soc.models.home_settings.HomeSettings):
   """Model of a SiteSettings, which stores per site configuration."""
-  
-  #: Valid ATOM or RSS feed url or None if unused. Feed entries are shown 
-  #: on the site page using Google's JavaScript blog widget  
-  feed_url = db.LinkProperty(
-      verbose_name=ugettext_lazy('Feed URL'))
-  feed_url.help_text = ugettext_lazy(
-      'The URL should be a valid ATOM or RSS feed. '
-      'Feed entries are shown on the site page.')
-  
+  # there is currently no site-specific configuration that is different from
+  # other /home page configuration (but this will change...)
+  pass 
 
--- a/app/soc/models/work.py	Mon Sep 29 14:03:45 2008 +0000
+++ b/app/soc/models/work.py	Mon Sep 29 15:31:50 2008 +0000
@@ -22,29 +22,21 @@
 ]
 
 from google.appengine.ext import db
-from soc.models import base
 
-class Work(base.ModelWithFieldAttributes):
-  """Model of a Work created by one or more Authors.
+from django.utils.translation import ugettext_lazy
 
-  Work is a "base entity" of other more specific "works" created by "persons".
+import polymodel
 
-  A Work entity participates in the following relationships implemented
-  as a db.ReferenceProperty elsewhere in another db.Model:
 
-   proposal), survey), documentation)
-     a 1:1 relationship with each entity containing a more specific type of
-     "work".  These relationships are represented explicitly in the other
-     "work" models by a db.ReferenceProperty named 'work'.  The
-     collection_name argument to db.ReferenceProperty should be set to the
-     singular of the entity model name of the other "work" class.  The above
-     relationship names correspond, respectively to these Models:
-       Proposal, Survey, Documentation
-     The relationships listed here are mutually exclusive.  For example,
-     a Work cannot be both a Proposal and a Survey at the same time.
+class Work(polymodel.PolyModel):
+  """Model of a Work created by one or more Persons in Roles.
 
-   persons)  a many:many relationship with Persons, stored in a separate
-     WorksPersons model.  See the WorksPersons model class for details.
+  Work is a "base entity" of other more specific "works" created by Persons
+  serving in "roles".
+
+   authors)  a many:many relationship with Roles, stored in a separate
+     WorksAuthors model, used to represent authorship of the Work.  See
+     the WorksAuthors model class for details.
 
    reviews)  a 1:many relationship between a Work and the zero or more
      Reviews of that Work.  This relation is implemented as the 'reviews'
@@ -54,8 +46,34 @@
   #: Required field indicating the "title" of the work, which may have
   #: different uses depending on the specific type of the work. Works
   #: can be indexed, filtered, and sorted by 'title'.
-  title = db.StringProperty(required=True)
+  title = db.StringProperty(required=True,
+      verbose_name=ugettext_lazy('Title'))
+  title.help_text = ugettext_lazy(
+      'title of the document; often used in the window title')
+
+  #: optional, indexed plain text field used for different purposes,
+  #: depending on the specific type of the work
+  abstract = db.StringProperty(multiline=True)
+  abstract.help_text = ugettext_lazy(
+      'short abstract, summary, or snippet;'
+      ' 500 characters or less, plain text displayed publicly')
 
-  #: large, non-indexed text field used for different purposes, depending
-  #: on the specific type of the work.
-  abstract = db.TextProperty()
+  #: Required link name, appended to a "path" to form the document URL.
+  #: The combined "path" and link name must be globally unique on the
+  #: site (but, unlike some link names, a Work link name can be reused,
+  #: as long as the combination with the preceding path is unique).
+  link_name = db.StringProperty(required=True,
+      verbose_name=ugettext_lazy('Link name'))
+  link_name.help_text = ugettext_lazy('link name used in URLs')
+
+  #: short name used in places such as the sidebar menu and breadcrumb trail
+  #: (optional: title will be used if short_name is not present)
+  short_name = db.StringProperty(verbose_name=ugettext_lazy('Short name'))
+  short_name.help_text = ugettext_lazy(
+      'short name used, for example, in the sidebar menu')
+  
+  #: date when the work was created
+  created = db.DateTimeProperty(auto_now_add=True)
+  
+  #: date when the work was last modified
+  modified = db.DateTimeProperty(auto_now=True)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/models/works_authors.py	Mon Sep 29 15:31:50 2008 +0000
@@ -0,0 +1,43 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed 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.
+
+"""The WorksAuthors Model links one author (Role) to one Work."""
+
+__authors__ = [
+  '"Todd Larsen" <tlarsen@google.com>',
+]
+
+
+from google.appengine.ext import db
+
+import soc.models.role.Role
+import soc.models.work.Work
+
+
+class WorksAuthors(db.Model):
+  """Model linking one Work to its author Role.
+  """
+
+  #: the Role end of a single 1:1 link in the many:many relationship
+  #: between Works and Roles
+  author = db.ReferenceProperty(reference_class=soc.models.role.Role,
+                                required=True, collection_name='authors')
+
+  #: the Work end of a single 1:1 link in the many:many relationship
+  #: between Works and Roles
+  work = db.ReferenceProperty(reference_class=soc.models.work.Work,
+                              required=True, collection_name='works')
+
--- a/app/soc/views/site/home.py	Mon Sep 29 14:03:45 2008 +0000
+++ b/app/soc/views/site/home.py	Mon Sep 29 15:31:50 2008 +0000
@@ -59,7 +59,8 @@
     model = soc.models.document.Document
     
     #: list of model fields which will *not* be gathered by the form
-    exclude = ['user','modified','created','link_name']
+    exclude = ['user','modified','created','link_name', 'inheritance_line']
+
 
 class SiteSettingsForm(forms_helpers.DbModelForm):
   """Django form displayed when creating or editing Site Settings.
@@ -70,6 +71,9 @@
     #: db.Model subclass for which the form will gather information
     model = soc.models.site_settings.SiteSettings
 
+    #: list of model fields which will *not* be gathered by the form
+    exclude = ['inheritance_line', 'home']
+
   def clean_feed_url(self):
     feed_url = self.cleaned_data.get('feed_url')
 
@@ -82,7 +86,10 @@
 
     return feed_url
 
-DEF_SITE_HOME_PATH = 'site/home'
+
+DEF_SITE_SETTINGS_PATH = 'site'
+DEF_SITE_HOME_DOC_LINK_NAME = 'home'
+
 DEF_SITE_HOME_PUBLIC_TMPL = 'soc/site/home/public.html'
 
 def public(request, template=DEF_SITE_HOME_PUBLIC_TMPL):
@@ -98,16 +105,16 @@
   # create default template context for use with any templates
   context = response_helpers.getUniversalContext(request)
   
-  document = soc.logic.document.getDocumentFromPath(DEF_SITE_HOME_PATH)
-  site_settings = soc.logic.site.settings.getSiteSettingsFromPath(DEF_SITE_HOME_PATH)
-  
-  if document:
-    document.content = template_helpers.unescape(document.content)
-    context.update({'site_document': document})
-  
+  site_settings = soc.logic.site.settings.getSiteSettings(DEF_SITE_SETTINGS_PATH)
+
   if site_settings:
     context.update({'site_settings': site_settings})
-    
+    site_doc = site_settings.home
+  
+    if site_doc:
+      site_doc.content = template_helpers.unescape(site_doc.content)
+      context.update({'site_document': site_doc})
+
   return response_helpers.respond(request, template, context)
 
 
@@ -129,13 +136,13 @@
   logged_in_id = users.get_current_user()
   
   alt_response = simple.getAltResponseIfNotDeveloper(request, context, 
-                                                        id = logged_in_id)
+                                                     id=logged_in_id)
   if alt_response:
     # not a developer
     return alt_response
   
   alt_response = simple.getAltResponseIfNotLoggedIn(request, context, 
-                                                        id = logged_in_id)
+                                                    id=logged_in_id)
   if alt_response:
     # not logged in
     return alt_response
@@ -156,47 +163,43 @@
 
     if document_form.is_valid() and settings_form.is_valid():
       title = document_form.cleaned_data.get('title')
-      link_name = DEF_SITE_HOME_PATH
+      link_name = DEF_SITE_HOME_DOC_LINK_NAME
       short_name = document_form.cleaned_data.get('short_name')
+      abstract = document_form.cleaned_data.get('abstract')
       content = document_form.cleaned_data.get('content')
       
+      site_doc = soc.logic.document.updateOrCreateDocument(
+          DEF_SITE_SETTINGS_PATH, link_name=link_name, title=title,
+          short_name=short_name, abstract=abstract, content=content,
+          user=id_user.getUserFromId(logged_in_id))
+      
       feed_url = settings_form.cleaned_data.get('feed_url')
-      
-      document = soc.logic.document.updateOrCreateDocumentFromPath(
-                                  DEF_SITE_HOME_PATH,
-                                  link_name = link_name,
-                                  title = title,
-                                  short_name = short_name,
-                                  content = content,
-                                  user = id_user.getUserFromId(logged_in_id))
 
-      site_settings = soc.logic.site.settings.updateOrCreateSiteSettingsFromPath(
-                                  DEF_SITE_HOME_PATH,
-                                  feed_url = feed_url)
+      site_settings = soc.logic.site.settings.updateOrCreateSiteSettings(
+          DEF_SITE_SETTINGS_PATH, home=site_doc, feed_url=feed_url)
       
       context.update({'submit_message': 'Site Settings saved.'})
   else: # request.method == 'GET'
-    # try to fetch Document entity by unique key_name   
-    document = soc.logic.document.getDocumentFromPath(DEF_SITE_HOME_PATH)
+    # try to fetch SiteSettings entity by unique key_name
+    site_settings = soc.logic.site.settings.getSiteSettings(
+        DEF_SITE_SETTINGS_PATH)
 
-    if document:
+    if site_settings:
+      # populate form with the existing SiteSettings entity
+      settings_form = SiteSettingsForm(instance=site_settings)
+      site_doc = site_settings.home
+    else:
+      # no SiteSettings entity exists for this key_name, so show a blank form
+      settings_form = SiteSettingsForm()
+      site_doc = None
+
+    if site_doc:
       # populate form with the existing Document entity
-      document_form = DocumentForm(instance=document)
+      document_form = DocumentForm(instance=site_doc)
     else:
       # no Document entity exists for this key_name, so show a blank form
       document_form = DocumentForm()
       
-    # try to fetch SiteSettings entity by unique key_name   
-    site_settings = soc.logic.site.settings.getSiteSettingsFromPath(
-                                                        DEF_SITE_HOME_PATH)
-
-    if site_settings:
-      # populate form with the existing SiteSettings entity
-      settings_form = SiteSettingsForm(instance=site_settings)
-    else:
-      # no SiteSettings entity exists for this key_name, so show a blank form
-      settings_form = SiteSettingsForm()
-    
   context.update({'document_form': document_form,
                   'settings_form': settings_form })