Attempt to rename User.id to User.account, in preparation for making User be
authorTodd Larsen <tlarsen@google.com>
Sat, 15 Nov 2008 03:12:33 +0000
changeset 481 94834a1e6c01
parent 480 9b07ddeb1412
child 482 839740b061ad
Attempt to rename User.id to User.account, in preparation for making User be derived from Linkable, which will have a property named 'id'. Patch by: Todd Larsen
app/soc/logic/accounts.py
app/soc/logic/model.py
app/soc/logic/models/base.py
app/soc/logic/models/document.py
app/soc/logic/models/home_settings.py
app/soc/logic/models/host.py
app/soc/logic/models/site_settings.py
app/soc/logic/models/sponsor.py
app/soc/logic/models/user.py
app/soc/logic/models/work.py
app/soc/logic/site/id_user.py
app/soc/logic/site/sidebar.py
app/soc/models/group.py
app/soc/models/role.py
app/soc/models/user.py
app/soc/templates/soc/user/edit.html
app/soc/templates/soc/user/edit_self.html
app/soc/templates/soc/user/list/user_heading.html
app/soc/templates/soc/user/list/user_row.html
app/soc/templates/soc/user/lookup.html
app/soc/views/helper/access.py
app/soc/views/helper/responses.py
app/soc/views/models/document.py
app/soc/views/models/sponsor.py
app/soc/views/models/user.py
app/soc/views/simple.py
app/soc/views/site/sponsor/profile.py
app/soc/views/site/user/profile.py
app/soc/views/user/profile.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/logic/accounts.py	Sat Nov 15 03:12:33 2008 +0000
@@ -0,0 +1,129 @@
+#!/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.
+
+"""Basic Google Account and User (Model) query functions.
+"""
+
+__authors__ = [
+  '"Chen Lunpeng" <forever.clp@gmail.com>',
+  '"Todd Larsen" <tlarsen@google.com>',
+  ]
+
+
+from google.appengine.api import users
+
+from soc.logic import models
+from soc.logic import out_of_band
+
+import soc.models.user
+import soc.logic.models.user
+
+
+def isDeveloper(account=None):
+  """Returns True if a Google Account is a Developer with special privileges.
+  
+  Since it only works on the current logged-in user, if account matches the
+  current logged-in Google Account, the App Engine Users API function
+  user.is_current_user_admin() is checked.  If that returns False, or
+  account is not the currently logged-in user, the is_developer property of
+  the User entity corresponding to the Google Account is checked next.
+  
+  This solves the "chicken-and-egg" problem of no User entity having its
+  is_developer property set, but no one being able to set it.
+  
+  Args:
+    account: a Google Account (users.User) object; if not supplied,
+      the current logged-in user is checked
+  """
+
+  # Get the currently logged in user
+  current = users.get_current_user()
+
+  if not (account or current):
+    # no Google Account was supplied or is logged in, so an unspecified
+    # User is definitely *not* a Developer
+    return False
+
+  if (((not account) or (account == current))
+      and users.is_current_user_admin()):
+    # no account supplied, or current logged-in user, and that user is in the
+    # Administration->Developers list in the App Engine console
+    return True
+
+  if not account:
+    account = current
+
+  user = models.user.logic.getForFields({'account': account}, unique=True)
+
+  if not user:
+    # no User entity for this Google Account, and account is not the
+    # currently logged-in user, so there is no conclusive way to check the
+    # Administration->Developers list in the App Engine console
+    return False
+  
+  return user.is_developer
+
+
+def isAccountAvailable(new_account,
+                       existing_user=None, existing_key_name=None):
+  """Returns True if Google Account is available for use by existing User.
+  
+  Args:
+    new_account: a Google Account (users.User) object with a (possibly)
+      new email
+    existing_user: an existing User entity; default is None, in which case
+      existing_key_name is used to look up the User entity
+    existing_key_name: the key_name of an existing User entity, used
+      when existing_user is not supplied; default is None
+  """
+  if not existing_user:
+    existing_user = models.user.logic.getFromKeyName(existing_key_name)
+
+  if existing_user:
+    old_email = existing_user.account.email()
+  else:
+    old_email = None
+
+  if new_account.email().lower() == old_email.lower():
+    # "new" email is same as existing User wanting it, so it is "available"
+    return True
+  # else: "new" email truly is new to the existing User, so keep checking
+
+  if not models.user.logic.getForFields({'account': new_account},
+                                        unique=True):
+    # new email address also does not belong to any other User,
+    # so it is available
+    return True
+
+  # email does not already belong to this User, but to some other User
+  return False
+
+
+# TODO(tlarsen): make this generic for any Linkable and move elsewhere
+def getUserFromLinkNameOr404(link_name):
+  """Like getUserFromLinkName but expects to find a user
+
+  Raises:
+    out_of_band.ErrorResponse if no User entity is found
+  """
+  user = models.user.logic.getForFields({'link_name': link_name},
+                                        unique=True)
+
+  if user:
+    return user
+
+  raise out_of_band.ErrorResponse(
+      'There is no user with a "link name" of "%s".' % link_name, status=404)
--- a/app/soc/logic/model.py	Fri Nov 14 06:36:42 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,183 +0,0 @@
-#!/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.
-
-"""Helpers functions for updating different kinds of models in datastore.
-"""
-
-__authors__ = [
-  '"Todd Larsen" <tlarsen@google.com>',
-  '"Sverre Rabbelier" <sverre@rabbelier.nl>',
-  '"Pawel Solyga" <pawel.solyga@gmail.com>',
-  ]
-
-
-from google.appengine.ext import db
-
-
-def getFullClassName(cls):
-  """Returns fully-qualified module.class name string.""" 
-
-  return '%s.%s' % (cls.__module__, cls.__name__) 
-
-
-def buildTypedQueryString(base_class, derived_class=None):
-  """Returns a GQL query string compatible with PolyModel.
-  
-  Args:
-    base_class: Model class that inherits directly from
-      polymodel.PolyModel, such as soc.models.work.Work
-    derived_class: optional more-specific Model class that
-      derives from base_class, such as soc.models.document.Document;
-      default is None, in which case the inheritance_line
-      property is *not* tested by the returned query string
-  """
-
-  query_str_parts = ['SELECT * FROM ', base_class.__name__]
-
-  if derived_class:
-    query_str_parts.extend(
-      [" WHERE inheritance_line = '", getFullClassName(derived_class), "'"])
-
-  return ''.join(query_str_parts)
-
-
-def buildOrderedQueryString(base_class, derived_class=None, order_by=None):
-  """Returns a an ordered GQL query string compatible with PolyModel.
-  
-  Args:
-    base_class, derived_class: see buildTypedQueryString()
-    order_by: optional field name by which to order the query results;
-      default is None, in which case no ORDER BY clause is placed in
-      the query string
-  """
-
-  query_str_parts = [
-    buildTypedQueryString(base_class, derived_class=derived_class)]
-
-  if order_by:
-    query_str_parts.extend([' ORDER BY ', order_by])
-
-  return ''.join(query_str_parts)
-
-
-def getEntitiesForLimitAndOffset(base_class, limit, offset=0,
-                                 derived_class=None, order_by=None):
-  """Returns entities for given offset and limit or None if not found.
-    
-  Args:
-    limit: max amount of entities to return
-    offset: optional offset in entities list which defines first entity to
-      return; default is zero (first entity)
-  """
-
-  query_string = buildOrderedQueryString(
-      base_class, derived_class=derived_class, order_by=order_by)
-
-  query = db.GqlQuery(query_string)
-
-  return query.fetch(limit, offset)
-
-
-def getNearestEntities(base_class, fields_to_try, derived_class=None):
-  """Get entities just before and just after the described entity.
-    
-  Args:
-    fields_to_try: ordered list of key/value pairs that "describe"
-      the desired entity (which may not necessarily exist), where key is
-      the name of the field, and value is an instance of that field
-      used in the comparison; if value is None, that field is skipped
-
-  Returns:
-    a two-tuple: ([nearest_entities], 'field_name')
-    
-    nearest_entities: list of entities being those just before and just
-      after the (possibly non-existent) entity identified by the first
-      of the supplied (non-None) fields
-        OR
-      possibly None if query had no results for the supplied field
-      that was used.
-  """
-
-  # SELECT * FROM base_class WHERE inheritance_line = 'derived_class'
-  typed_query_str = buildTypedQueryString(
-    base_class, derived_class=derived_class)
-  
-  if derived_class:
-    typed_query_str = typed_query_str + ' AND '
-  else:
-    typed_query_str = typed_query_str + ' WHERE '
-    
-  for field, value in fields_to_try:
-    if value is None:
-      # skip this not-supplied field
-      continue
-
-    query = db.GqlQuery('%s%s > :1' % (typed_query_str, field), value)
-    return query.fetch(1), field
-
-  # all fields exhausted, and all had None values
-  return (None, None)
-
-
-def findNearestEntitiesOffset(width, base_class, fields_to_try,
-                              derived_class=None):
-  """Finds offset of beginning of a range of entities around the nearest.
-  
-  Args:
-    width: the width of the "found" window around the nearest User found
-    base_class, fields_to_try, derived_class:  see getNearestEntities()
-    
-  Returns:
-    an offset into the list of entities that is width/2 less than the
-    offset of the first entity returned by getNearestEntities(), or zero
-    if that offset would be less than zero
-      OR
-    None if there are no nearest entities or the offset of the beginning of
-    the range cannot be found for some reason 
-  """
-
-  # find entity "nearest" to supplied fields
-  nearest_entities, field = getNearestEntities(
-      base_class, fields_to_try, derived_class=derived_class)
-  
-  if not nearest_entities:
-    # no "nearest" entity, so indicate that with None
-    return None
-
-  nearest_entity = nearest_entities[0]
-
-  # start search for beginning of nearest Users range at offset zero
-  offset = 0
-  entities = getEntitiesForLimitAndOffset(
-      base_class, width, offset=offset, derived_class=derived_class)
-  
-  while True:
-    for entity in entities:
-      if getattr(nearest_entity, field) == getattr(entity, field):
-        # nearest User found in current search range, so return a range start
-        return max(0, (offset - (width/2)))
-
-      offset = offset + 1
-
-    # nearest User was not in the current search range, so fetch the next set
-    entities = getEntitiesForLimitAndOffset(
-        base_class, width, offset=offset, derived_class=derived_class)
-
-    if not entities:
-      # nearest User never found, so indicate that with None
-      break
-
-  return None
--- a/app/soc/logic/models/base.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/models/base.py	Sat Nov 15 03:12:33 2008 +0000
@@ -45,6 +45,23 @@
   on the the child-classes to implement _model, _name and _key_name
   """
 
+  def __init__(self, model, base_model=None,
+               name=None, skip_properties=None):
+    """Defines the name, key_name and model for this entity.
+    """
+    self._model = model
+    self._base_model = base_model
+    
+    if name:
+      self._name = name
+    else:
+      self._name = self.getModelClassName()
+    
+    if skip_properties:
+      self._skip_properties = skip_properties
+    else:
+      self._skip_properties = []
+
   def _updateField(self, model, name, value):
     """Hook called when a field is updated.
 
@@ -85,6 +102,17 @@
     # construct the KeyName in the appropriate format
     return ":".join([self._name] + keyvalues)
 
+  def getModelClassName(self):
+    """Returns model class name string.
+    """ 
+    return self._model.__name__ 
+
+  def getFullModelClassName(self):
+    """Returns fully-qualified model module.class name string.
+    """ 
+    return '%s.%s' % (self._model.__module__,
+                      self.getModelClassName()) 
+
   def getKeyValues(self, entity):
     """Exctracts the key values from entity and returns them
 
@@ -266,6 +294,135 @@
 
     return query.fetch(limit, offset)
 
+  def buildTypedQueryString(self):
+    """Returns a GQL query string compatible with PolyModel.
+    """
+    return ''.join(self._buildTypedQueryParts())
+
+  def _buildTypedQueryParts(self):
+    if self._base_model:
+      return [
+          'SELECT * FROM ', self._base_model.__name__,
+          " WHERE inheritance_line = '", self.getFullModelClassName(), "'"]
+    
+    return ['SELECT * FROM ', self._model.__name__]
+
+  def buildOrderedQueryString(self, order_by=None):
+    """Returns a an ordered GQL query string compatible with PolyModel.
+  
+    Args:
+      order_by: optional field name by which to order the query results;
+        default is None, in which case no ORDER BY clause is placed in
+        the query string
+    """
+    return ''.join(self._buildOrderedQueryParts(order_by=order_by))
+
+  def _buildOrderedQueryParts(self, order_by=None):
+    query_str_parts = self._buildTypedQueryParts()
+
+    if order_by:
+      query_str_parts.extend([' ORDER BY ', order_by])
+
+    return query_str_parts
+
+  def getEntitiesForLimitAndOffset(self, limit, offset=0, order_by=None):
+    """Returns entities for given offset and limit or None if not found.
+    
+    Args:
+      limit: max amount of entities to return
+      offset: optional offset in entities list which defines first entity to
+        return; default is zero (first entity)
+    """
+    query_string = self.buildOrderedQueryString(order_by=order_by)
+    query = db.GqlQuery(query_string)
+
+    return query.fetch(limit, offset)
+
+  def getNearestEntities(self, fields_to_try):
+    """Get entities just before and just after the described entity.
+    
+    Args:
+      fields_to_try: ordered list of key/value pairs that "describe"
+        the desired entity (which may not necessarily exist), where key is
+        the name of the field, and value is an instance of that field
+        used in the comparison; if value is None, that field is skipped
+
+    Returns:
+      a two-tuple: ([nearest_entities], 'field_name')
+    
+      nearest_entities: list of entities being those just before and just
+        after the (possibly non-existent) entity identified by the first
+        of the supplied (non-None) fields
+          OR
+        possibly None if query had no results for the supplied field
+        that was used.
+    """
+    # SELECT * FROM base_class WHERE inheritance_line = 'derived_class'
+    typed_query_parts = self._buildTypedQueryParts()
+  
+    if self._base_model:
+      typed_query_parts.append(' AND %s > :1')
+    else:
+      typed_query_parts.append(' WHERE %s > :1')
+
+    typed_query_fmt = ''.join(typed_query_parts)
+
+    for field, value in fields_to_try:
+      if value is None:
+        # skip this not-supplied field
+        continue
+
+      query = db.GqlQuery(typed_query_fmt % field, value)
+      return query.fetch(1), field
+
+    # all fields exhausted, and all had None values
+    return (None, None)
+
+  def findNearestEntitiesOffset(width, fields_to_try):
+    """Finds offset of beginning of a range of entities around the nearest.
+  
+    Args:
+      width: the width of the "found" window around the nearest User found
+      fields_to_try:  see getNearestEntities()
+    
+    Returns:
+      an offset into the list of entities that is width/2 less than the
+      offset of the first entity returned by getNearestEntities(), or zero
+      if that offset would be less than zero
+        OR
+      None if there are no nearest entities or the offset of the beginning of
+      the range cannot be found for some reason 
+    """
+    # find entity "nearest" to supplied fields
+    nearest_entities, field = self.getNearestEntities(fields_to_try)
+  
+    if not nearest_entities:
+      # no "nearest" entity, so indicate that with None
+      return None
+
+    nearest_entity = nearest_entities[0]
+
+    # start search for beginning of nearest Users range at offset zero
+    offset = 0
+    entities = self.getEntitiesForLimitAndOffset(width, offset=offset)
+  
+    while True:
+      for entity in entities:
+        if getattr(nearest_entity, field) == getattr(entity, field):
+          # nearest User found in current search range, so return a range start
+          return max(0, (offset - (width/2)))
+
+        offset = offset + 1
+
+      # nearest User was not in the current search range, so fetch the next set
+      entities = self.getEntitiesForLimitAndOffset(width, offset=offset)
+
+      if not entities:
+        # nearest User never found, so indicate that with None
+        break
+
+    return None
+
   def updateModelProperties(self, model, model_properties):
     """Update existing model entity using supplied model properties.
 
--- a/app/soc/logic/models/document.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/models/document.py	Sat Nov 15 03:12:33 2008 +0000
@@ -26,6 +26,7 @@
 from soc.logic.models import base
 
 import soc.models.document
+import soc.models.work
 
 
 class Logic(base.Logic):
@@ -35,10 +36,8 @@
   def __init__(self):
     """Defines the name, key_name and model for this entity.
     """
-
-    self._name = "Document"
-    self._model = soc.models.document.Document
-    self._skip_properties = []
+    base.Logic.__init__(self, soc.models.document.Document,
+                        base_model=soc.models.work.Work)
 
   def getKeyValues(self, entity):
     """See base.Logic.getKeyNameValues.
--- a/app/soc/logic/models/home_settings.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/models/home_settings.py	Sat Nov 15 03:12:33 2008 +0000
@@ -36,10 +36,7 @@
   def __init__(self):
     """Defines the name, key_name and model for this entity.
     """
-
-    self._name = "HomeSettings"
-    self._model = soc.models.home_settings.HomeSettings
-    self._skip_properties = []
+    base.Logic.__init__(self, soc.models.home_settings.HomeSettings)
   
   def getKeyValues(self, entity):
     """See base.Logic.getKeyNameValues.
--- a/app/soc/logic/models/host.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/models/host.py	Sat Nov 15 03:12:33 2008 +0000
@@ -26,6 +26,7 @@
 from soc.logic.models import base
 
 import soc.models.host
+import soc.models.role
 
 
 class Logic(base.Logic):
@@ -35,10 +36,8 @@
   def __init__(self):
     """Defines the name, key_name and model for this entity.
     """
-
-    self._name = "Host"
-    self._model = soc.models.host.Host
-    self._skip_properties = []
+    base.Logic.__init__(self, soc.models.host.Host,
+                        base_model=soc.models.role.Role)
 
   def getKeyValues(self, entity):
     """See base.Logic.getKeyNameValues.
--- a/app/soc/logic/models/site_settings.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/models/site_settings.py	Sat Nov 15 03:12:33 2008 +0000
@@ -26,6 +26,7 @@
 from soc.logic.models import base
 from soc.logic.models import home_settings
 
+import soc.models.home_settings
 import soc.models.site_settings
 
 
@@ -40,10 +41,8 @@
   def __init__(self):
     """Defines the name, key_name and model for this entity.
     """
-
-    self._name = "SiteSettings"
-    self._model = soc.models.site_settings.SiteSettings
-    self._skip_properties = []
+    base.Logic.__init__(self, soc.models.site_settings.SiteSettings,
+                        base_model=soc.models.home_settings.HomeSettings)
 
   def getMainKeyValues(self):
     """Returns the default key values for the site settings"""
--- a/app/soc/logic/models/sponsor.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/models/sponsor.py	Sat Nov 15 03:12:33 2008 +0000
@@ -25,6 +25,7 @@
 from soc.logic import key_name
 from soc.logic.models import base
 
+import soc.models.group
 import soc.models.sponsor
 
 
@@ -35,10 +36,8 @@
   def __init__(self):
     """Defines the name, key_name and model for this entity.
     """
-
-    self._name = "Sponsor"
-    self._model = soc.models.sponsor.Sponsor
-    self._skip_properties = []
+    base.Logic.__init__(self, soc.models.sponsor.Sponsor,
+                        base_model=soc.models.group.Group)
 
   def getKeyValues(self, entity):
     """See base.Logic.getKeyNameValues.
--- a/app/soc/logic/models/user.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/models/user.py	Sat Nov 15 03:12:33 2008 +0000
@@ -34,18 +34,18 @@
   def __init__(self):
     """Defines the name, key_name and model for this entity.
     """
-
-    self._name = "User"
-    self._model = soc.models.user.User
-    self._skip_properties = ['former_ids']
+    base.Logic.__init__(self, soc.models.user.User,
+                        skip_properties=['former_accounts'])
 
-  def isFormerId(self, id):
-    """Returns true if id is in former ids"""
+  def isFormerAccount(self, account):
+    """Returns true if account is a former account of some User.
+    """
     # TODO(pawel.solyga): replace 1000 with solution that works for any number of queries
-    users_with_former_ids = soc.models.user.User.gql('WHERE former_ids != :1', None).fetch(1000)
+    users_with_former_accounts = soc.models.user.User.gql(
+        'WHERE former_accounts != :1', None).fetch(1000)
     
-    for former_id_user in users_with_former_ids: 
-      if id in former_id_user.former_ids:
+    for former_account_user in users_with_former_accounts: 
+      if account in former_account_user.former_accounts:
         return True
     
     return False
@@ -54,7 +54,7 @@
     """See base.Logic.getKeyValues.
     """
 
-    return [entity.id.email()]
+    return [entity.account.email()]
 
   def getSuffixValues(self, entity):
     """See base.Logic.getSuffixValues
@@ -74,7 +74,7 @@
         }
 
     entity = self.getForFields(properties, unique=True)
-    return [entity.id.email()]
+    return [entity.account.email()]
 
   def getKeyFieldNames(self):
     """See base.Logic.getKeyFieldNames
@@ -82,28 +82,28 @@
 
     return ['email']
 
-  def updateOrCreateFromId(self, properties, id):
-    """Like updateOrCreateFromKeyName, but resolves id to a key_name first.
+  def updateOrCreateFromAccount(self, properties, account):
+    """Like updateOrCreateFromKeyName, but resolves account to key_name first.
     """
 
     # attempt to retrieve the existing entity
-    user = soc.models.user.User.gql('WHERE id = :1', id).get()
+    user = soc.models.user.User.gql('WHERE account = :1', account).get()
     
     if user:
       key_name = user.key().name()
     else:
-      key_name  = self.getKeyNameForFields({'email': id.email()})
+      key_name  = self.getKeyNameForFields({'email': account.email()})
 
     return self.updateOrCreateFromKeyName(properties, key_name)
 
   def _updateField(self, model, name, value):
-    """Special case logic for id.
+    """Special case logic for account.
 
-    When the id is changed, the former_ids field should be appended
-    with the old id.
+    When the account is changed, the former_accounts field should be appended
+    with the old account.
     """
-    if name == 'id' and model.id != value:
-      model.former_ids.append(model.id)
+    if name == 'account' and model.account != value:
+      model.former_accounts.append(model.account)
 
     return True
 
--- a/app/soc/logic/models/work.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/models/work.py	Sat Nov 15 03:12:33 2008 +0000
@@ -36,10 +36,7 @@
   def __init__(self):
     """Defines the name, key_name and model for this entity.
     """
-
-    self._name = "Work"
-    self._model = soc.models.work.Work
-    self._skip_properties = []
+    base.Logic.__init__(self, soc.models.work.Work)
 
   def getKeyValues(self, entity):
     """See base.Logic.getKeyNameValues.
--- a/app/soc/logic/site/id_user.py	Fri Nov 14 06:36:42 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-#!/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.
-
-"""Basic ID (Google Account) and User (Model) query functions.
-"""
-
-__authors__ = [
-  '"Chen Lunpeng" <forever.clp@gmail.com>',
-  '"Todd Larsen" <tlarsen@google.com>',
-  ]
-
-
-from google.appengine.api import users
-
-from soc.logic import model
-from soc.logic import models
-from soc.logic import out_of_band
-
-import soc.models.user
-
-
-def findNearestUsersOffset(width, id=None, link_name=None):
-  """Finds offset of beginning of a range of Users around the nearest User.
-  
-  Args:
-    width: the width of the "found" window around the nearest User found 
-    id: a Google Account (users.User) object, or None
-    link_name: link name input in the Lookup form or None if not supplied.
-    
-  Returns:
-    an offset into the list of Users that is width/2 less than the
-    offset of the first User returned by getNearestUsers(), or zero if
-    that offset would be less than zero
-      OR
-    None if there are no nearest Users or the offset of the beginning of
-    the range cannot be found for some reason 
-  """
-  return model.findNearestEntitiesOffset(
-    width, soc.models.user.User, [('id', id), ('link_name', link_name)])
-
-
-def isIdDeveloper(id=None):
-  """Returns True if a Google Account is a Developer with special privileges.
-  
-  Since it only works on the current logged-in user, if id matches the
-  current logged-in Google Account, the App Engine Users API function
-  user.is_current_user_admin() is checked.  If that returns False, or
-  id is not the currently logged-in user, the is_developer property of
-  the User entity corresponding to the id Google Account is checked next.
-  
-  This solves the "chicken-and-egg" problem of no User entity having its
-  is_developer property set, but no one being able to set it.
-  
-  Args:
-    id: a Google Account (users.User) object; if id is not supplied,
-      the current logged-in user is checked
-  """
-
-  # Get the currently logged in user
-  current_id = users.get_current_user()
-
-  if not (id or current_id):
-    # no Google Account was supplied or is logged in, so an unspecified
-    # User is definitely *not* a Developer
-    return False
-
-  if ((not id) or (id == current_id)) and users.is_current_user_admin():
-    # no id supplied, or current logged-in user, and that user is in the
-    # Administration->Developers list in the App Engine console
-    return True
-
-  if not id:
-    id = current_id
-
-  user = models.user.logic.getForFields({'id': id}, unique=True)
-
-  if not user:
-    # no User entity for this Google Account, and id is not the currently
-    # logged-in user, so there is no conclusive way to check the
-    # Administration->Developers list in the App Engine console
-    return False
-  
-  return user.is_developer
-
-
-def isIdAvailable(new_id, existing_user=None, existing_key_name=None):
-  """Returns True if Google Account is available for use by existing User.
-  
-  Args:
-    new_id: a Google Account (users.User) object with a (possibly) new email
-    existing_user: an existing User entity; default is None, in which case
-      existing_key_name is used to look up the User entity
-    existing_key_name: the key_name of an existing User entity, used
-      when existing_user is not supplied; default is None
-  """
-  if not existing_user:
-    existing_user = models.user.logic.getFromKeyName(existing_key_name)
-
-  if existing_user:
-    old_email = existing_user.id.email()
-  else:
-    old_email = None
-
-  if new_id.email().lower() == old_email.lower():
-    # "new" email is same as existing User wanting it, so it is "available"
-    return True
-  # else: "new" email truly is new to the existing User, so keep checking
-
-  if not models.user.logic.getForFields({'id': new_id}, unique=True):
-    # new email address also does not belong to any other User,
-    # so it is available
-    return True
-
-  # email does not already belong to this User, but to some other User
-  return False
-
-
-def getUserFromLinkName(link_name):
-  """Returns User entity for link_name or None if not found.
-    
-  Args:
-    link_name: link name used in URLs to identify user
-  """
-  return soc.models.user.User.gql('WHERE link_name = :1', link_name).get()
-
-
-def getUserFromLinkNameOr404(link_name):
-  """Like getUserFromLinkName but expects to find a user
-
-  Raises:
-    out_of_band.ErrorResponse if no User entity is found
-  """
-
-  user = getUserFromLinkName(link_name)
-
-  if user:
-    return user
-
-  raise out_of_band.ErrorResponse(
-      'There is no user with a "link name" of "%s".' % link_name, status=404)
-
-
-def doesLinkNameBelongToId(link_name, id):
-  """Returns True if supplied link name belongs to supplied Google Account.
-  
-  Args:
-    link_name: link name used in URLs to identify user
-    id: a Google Account object
-  """
-
-  if not id:
-    # link name cannot belong to an unspecified User
-    return False
-
-  user = models.user.logic.getForFields({'id': id}, unique=True)
-
-  if not user:
-    # no User corresponding to id Google Account, so no link name at all 
-    return False
-
-  return user.link_name == link_name
--- a/app/soc/logic/site/sidebar.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/logic/site/sidebar.py	Sat Nov 15 03:12:33 2008 +0000
@@ -25,24 +25,24 @@
 
 from google.appengine.api import users
 
+from soc.logic import accounts
 from soc.logic import menu
-from soc.logic.site import id_user
 from soc.logic.site import map
 
 
-def buildUserSidebar(id=None, **ignored):
+def buildUserSidebar(account=None, **ignored):
   """Returns a list of menu items for the User portion of the sidebar.
   
   Args:
-    id: a Google Account (users.User) object; default is None, in which
+    account: a Google Account (users.User) object; default is None, in which
       case users.get_current_user() is called 
     **ignored: other keyword arguments supplied to other sidebar builder
       functions, but ignored by this one
   """
-  if id is None:
-    id = users.get_current_user()
+  if account is None:
+    account = users.get_current_user()
 
-  if not id:
+  if not account:
     return [map.user_signin_sub_menu.makeMenuItem()]
 
   return [map.user_signout_sub_menu.makeMenuItem()]
@@ -54,12 +54,12 @@
   Args:
     is_admin: Boolean indicating that current user is a "Developer"
       (site super-user); default is None, in which case
-      id_user.isIdDeveloper() is called 
+      accounts.isDeveloper() is called 
     **ignored: other keyword arguments supplied to other sidebar builder
       functions, but ignored by this one
   """
   if is_admin is None:
-    is_admin = id_user.isIdDeveloper()
+    is_admin = accounts.isDeveloper()
 
   if not is_admin:
     # user is either not logged in or not a "Developer", so return no menu
--- a/app/soc/models/group.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/models/group.py	Sat Nov 15 03:12:33 2008 +0000
@@ -68,8 +68,8 @@
       verbose_name=ugettext_lazy('Home Page URL'))
   
   #: Required email address used as the "public" contact mechanism for
-  #: the Group (as opposed to the founder.id email address which is kept
-  #: secret, revealed only to Developers).
+  #: the Group (as opposed to the founder.account email address which is
+  #: kept secret, revealed only to Developers).
   email = db.EmailProperty(required=True,
       verbose_name=ugettext_lazy('Email'))  
   
--- a/app/soc/models/role.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/models/role.py	Sat Nov 15 03:12:33 2008 +0000
@@ -116,7 +116,7 @@
   #====================================================================
 
   #: Required field used as the 'public' contact mechanism for the
-  #: Role (as opposed to the user.id email address which is
+  #: Role (as opposed to the user.account email address which is
   #: kept secret).
   email = db.EmailProperty(
       required=True,
--- a/app/soc/models/user.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/models/user.py	Sat Nov 15 03:12:33 2008 +0000
@@ -65,14 +65,14 @@
   #: This email address is only used in an automated fashion by 
   #: Melange web applications and is not made visible to other users 
   #: of any Melange application.
-  id = db.UserProperty(required=True,
+  account = db.UserProperty(required=True,
       verbose_name=ugettext_lazy('User account'))
-  id.help_text = ugettext_lazy(
-      'Email address of a valid user (Google Account).')
+  account.help_text = ugettext_lazy(
+      'A valid Google Account.')
 
   #: A list (possibly empty) of former Google Accounts associated with
   #: this User.
-  former_ids = db.ListProperty(users.User)
+  former_accounts = db.ListProperty(users.User)
 
   #: Required field storing a nickname; displayed publicly.
   #: Nicknames can be any valid UTF-8 text.
--- a/app/soc/templates/soc/user/edit.html	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/templates/soc/user/edit.html	Sat Nov 15 03:12:33 2008 +0000
@@ -17,7 +17,7 @@
  {{ page.short_name }}
  {% if existing_user %}
 for {{ existing_user.nick_name }}
-<a href="mailto:{{ existing_user.id.email }} ">&lt;{{ existing_user.id.email }}&gt;</a>
+<a href="mailto:{{ existing_user.account.email }} ">&lt;{{ existing_user.account.email }}&gt;</a>
  {% endif %}
 {% endblock %}
 {% block body %}
@@ -31,9 +31,9 @@
   {{ form.key_name }}
  <table>
   {% if existing_user %}
-  {% readonly_field_as_table_row "Id" existing_user.id %}
+  {% readonly_field_as_table_row "Account" existing_user.account %}
   {% endif %}
-  {% field_as_table_row form.id %}
+  {% field_as_table_row form.account %}
 {% if lookup_error %}
 <tr>
  <td>&nbsp;</td>
@@ -45,12 +45,12 @@
   {% field_as_table_row form.link_name %}
   {% field_as_table_row form.nick_name %}
   {% field_as_table_row form.is_developer %}
-  {% if existing_user.former_ids %}
+  {% if existing_user.former_accounts %}
   <tr>
-   <td class="formfieldlabel">Former ids</td>
+   <td class="formfieldlabel">Former Accounts</td>
    <td>
-       {% for former_id in existing_user.former_ids %}
-       {{ former_id }}<br />
+       {% for former_account in existing_user.former_accounts %}
+       {{ former_account }}<br />
        {% endfor %}
    </td>
    <td class="formfieldrequired">&nbsp;</td>
--- a/app/soc/templates/soc/user/edit_self.html	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/templates/soc/user/edit_self.html	Sat Nov 15 03:12:33 2008 +0000
@@ -16,9 +16,9 @@
 {% block page_title %}User Profile{% endblock %}
 {% block header_title %}
 {% if user %}
-Modify Existing User Profile for {{ user.nick_name }} <a href="mailto:{{ id.email }} ">&lt;{{ id.email }}&gt;</a>
+Modify Existing User Profile for {{ user.nick_name }} <a href="mailto:{{ account.email }} ">&lt;{{ account.email }}&gt;</a>
 {% else %}
-Create a New User Profile for <a href="mailto:{{ id.email }} ">&lt;{{ id.email }}&gt;</a>
+Create a New User Profile for <a href="mailto:{{ account.email }} ">&lt;{{ account.email }}&gt;</a>
 {% endif %}
 {% endblock %}
 {% block body %}
--- a/app/soc/templates/soc/user/list/user_heading.html	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/templates/soc/user/list/user_heading.html	Sat Nov 15 03:12:33 2008 +0000
@@ -1,5 +1,5 @@
 <tr align="left">
-  <th class="first" align="right">Id</th>
+  <th class="first" align="right">Account</th>
   <th>Email</th>
   <th>Nickname</th>
   <th>Linkname</th>
--- a/app/soc/templates/soc/user/list/user_row.html	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/templates/soc/user/list/user_row.html	Sat Nov 15 03:12:33 2008 +0000
@@ -1,10 +1,10 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
 onclick="document.location.href='/user/edit/{{ data_element.link_name }}'" name="name">
-  <td align="right"><div class="id"><a class="noul"
-         href="/user/edit/{{ data_element.link_name }}">{{ data_element.id }}</a>
+  <td align="right"><div class="account"><a class="noul"
+         href="/user/edit/{{ data_element.link_name }}">{{ data_element.account }}</a>
      </div>
   </td>
-  <td><div class="email">{{ data_element.id.email }}</a></div></td>
+  <td><div class="email">{{ data_element.account.email }}</a></div></td>
   <td><div class="nick_name">{{ data_element.nick_name }}</div></td>
   <td><div class="link_name">{{ data_element.link_name }}</div></td>
 </tr>
--- a/app/soc/templates/soc/user/lookup.html	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/templates/soc/user/lookup.html	Sat Nov 15 03:12:33 2008 +0000
@@ -23,7 +23,7 @@
 <form method="POST">
  <table>
 {% if found_user %}
-{% readonly_field_as_table_row "Id" found_user.id %}
+{% readonly_field_as_table_row "Account" found_user.account %}
 {% endif %}
 {% if email_error %}
 <tr>
@@ -33,7 +33,7 @@
  </td>
 </tr>
 {% endif %}
-  {% field_as_table_row form.id %}
+  {% field_as_table_row form.account %}
 {% if link_name_error %}
 <tr>
  <td>&nbsp;</td>
@@ -57,12 +57,12 @@
   <td class="formfieldhelptext">&nbsp;</td>
  </tr>
  
- {% if found_user.former_ids %}
+ {% if found_user.former_accounts %}
  <tr>
-  <td class="formfieldlabel">Former ids</td>
+  <td class="formfieldlabel">Former Accounts</td>
   <td>
-      {% for former_id in found_user.former_ids %}
-      {{ former_id }}<br />
+      {% for former_account in found_user.former_accounts %}
+      {{ former_account }}<br />
       {% endfor %}
   </td>
   <td class="formfieldrequired">&nbsp;</td>
--- a/app/soc/views/helper/access.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/views/helper/access.py	Sat Nov 15 03:12:33 2008 +0000
@@ -34,8 +34,8 @@
 
 from django.utils.translation import ugettext_lazy
 
+from soc.logic import accounts
 from soc.logic import models
-from soc.logic.site import id_user
 from soc.views.simple import requestLogin
 
 import soc.views.out_of_band
@@ -90,15 +90,15 @@
      AccessViolationResponse: If the required authorization is not met.
 
   Returns:
-    None if User exists for id, or a subclass of
+    None if User exists for a Google Account, or a subclass of
     django.http.HttpResponse which contains the alternate response
     should be returned by the calling view.
   """
 
   checkIsLoggedIn(request)
 
-  id = users.get_current_user()
-  user = models.user.logic.getForFields({'id': id}, unique=True)
+  user = models.user.logic.getForFields(
+      {'account': users.get_current_user()}, unique=True)
 
   if user:
     return
@@ -119,16 +119,14 @@
      AccessViolationResponse: If the required authorization is not met.
 
   Returns:
-    None if id is logged in and logged-in user is a Developer, or a
-    subclass of django.http.HttpResponse which contains the alternate
+    None if Google Account is logged in and logged-in user is a Developer,
+    or a subclass of django.http.HttpResponse which contains the alternate
     response should be returned by the calling view.
   """
 
   checkIsUser(request)
 
-  id = users.get_current_user()
-
-  if id_user.isIdDeveloper(id=id):
+  if accounts.isDeveloper(account=users.get_current_user()):
     return None
 
   login_message_fmt = DEF_DEV_LOGOUT_LOGIN_MSG_FMT % {
@@ -159,8 +157,9 @@
 
   checkIsUser(request)
 
-  id = users.get_current_user()
-  host = soc.logic.host.getHostFromProgram(id, program)
+  # TODO(alturin): the soc.logic.host module does not seem to exist...
+  host = soc.logic.host.getHostFromProgram(
+      users.get_current_user(), program)
 
   if host:
     return
--- a/app/soc/views/helper/responses.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/views/helper/responses.py	Sat Nov 15 03:12:33 2008 +0000
@@ -28,9 +28,9 @@
 from django import http
 from django.template import loader
 
+from soc.logic import accounts
 from soc.logic import system
 from soc.logic.models import site_settings
-from soc.logic.site import id_user
 from soc.logic.site import sidebar
 from soc.views import helper
 from soc.views.helper import html_menu
@@ -81,9 +81,9 @@
     
     {
       'request': the Django HTTP request object passed in by the caller
-      'id': the logged-in Google Account if there is one
+      'account': the logged-in Google Account if there is one
       'user': the User entity corresponding to the Google Account in
-        context['id']
+        context['account']
       'is_admin': True if users.is_current_user_admin() is True
       'is_debug': True if system.isDebug() is True
       'sign_in': a Google Account login URL
@@ -92,16 +92,16 @@
     }
   """
 
-  id = users.get_current_user()
+  account = users.get_current_user()
 
   context = {}
   context['request'] = request
 
-  if id:
-    context['id'] = id
+  if account:
+    context['account'] = account
     context['user'] = soc.logic.models.user.logic.getForFields(
-        {'id': id}, unique=True)
-    context['is_admin'] = id_user.isIdDeveloper(id=id)
+        {'account': account}, unique=True)
+    context['is_admin'] = accounts.isDeveloper(account=account)
 
   context['is_debug'] = system.isDebug()
   context['sign_in'] = users.create_login_url(request.path)
--- a/app/soc/views/models/document.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/views/models/document.py	Sat Nov 15 03:12:33 2008 +0000
@@ -136,8 +136,9 @@
     """See base.View._editPost().
     """
 
-    id = users.get_current_user()
-    user = soc.logic.models.user.logic.getForFields({'id': id}, unique=True)
+    account = users.get_current_user()
+    user = soc.logic.models.user.logic.getForFields({'account': account},
+                                                    unique=True)
     fields['author'] = user
 
   def _editGet(self, request, entity, form):
--- a/app/soc/views/models/sponsor.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/views/models/sponsor.py	Sat Nov 15 03:12:33 2008 +0000
@@ -136,8 +136,9 @@
     """See base.View._editPost().
     """
 
-    id = users.get_current_user()
-    user = soc.logic.models.user.logic.getForFields({'id': id}, unique=True)
+    account = users.get_current_user()
+    user = soc.logic.models.user.logic.getForFields({'account': account},
+                                                    unique=True)
     fields['founder'] = user
 
 
--- a/app/soc/views/models/user.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/views/models/user.py	Sat Nov 15 03:12:33 2008 +0000
@@ -43,8 +43,8 @@
   """
 
   email = forms.EmailField(
-      label=soc.models.user.User.id.verbose_name,
-      help_text=soc.models.user.User.id.help_text)
+      label=soc.models.user.User.account.verbose_name,
+      help_text=soc.models.user.User.account.help_text)
 
   link_name = forms.CharField(
       label=soc.models.user.User.link_name.verbose_name,
@@ -74,15 +74,15 @@
     return link_name
 
   def clean_email(self):
-    form_id = users.User(email=self.cleaned_data.get('email'))
+    form_account = users.User(email=self.cleaned_data.get('email'))
     key_name = self.data.get('key_name')
     if key_name:
       user = user_logic.logic.getFromKeyName(key_name)
-      old_email = user.id.email()
+      old_email = user.account.email()
     else:
       old_email = None
 
-    new_email = form_id.email()
+    new_email = form_account.email()
 
     if new_email != old_email \
         and user_logic.logic.getFromFields(email=new_email):
@@ -164,8 +164,7 @@
 
     params = dicts.merge(params, {'edit_template': 'soc/user/edit_self.html'})
 
-    id = users.get_current_user()
-    properties = {'id': id}
+    properties = {'account': users.get_current_user()}
 
     entity = self._logic.getForFields(properties, unique=True)
     keys = self._logic.getKeyFieldNames()
@@ -178,14 +177,14 @@
     """See base.View._editGet().
     """
     # fill in the email field with the data from the entity
-    form.fields['email'].initial = entity.id.email()
+    form.fields['email'].initial = entity.account.email()
     
 
   def _editPost(self, request, entity, fields):
     """See base.View._editPost().
     """
-    # fill in the id field with the user created from email
-    fields['id'] = users.User(fields['email'])
+    # fill in the account field with the user created from email
+    fields['account'] = users.User(fields['email'])
 
 
 view = View()
--- a/app/soc/views/simple.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/views/simple.py	Sat Nov 15 03:12:33 2008 +0000
@@ -25,8 +25,8 @@
 
 from django.utils.translation import ugettext_lazy
 
+from soc.logic import accounts
 from soc.logic import out_of_band
-from soc.logic.site import id_user
 from soc.views import helper
 from soc.views.helper import decorators
 
@@ -71,7 +71,7 @@
 
   try:
     if link_name:
-      user = id_user.getUserFromLinkNameOr404(link_name)
+      user = accounts.getUserFromLinkNameOr404(link_name)
   except out_of_band.ErrorResponse, error:
     return errorResponse(request, page, error, template, context)
 
--- a/app/soc/views/site/sponsor/profile.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/views/site/sponsor/profile.py	Sat Nov 15 03:12:33 2008 +0000
@@ -115,8 +115,8 @@
   context = helper.responses.getUniversalContext(request)
   context['page'] = page
 
-  logged_in_id = users.get_current_user()
-  user = models.user.logic.getForFields({'id': logged_in_id}, unique=True)
+  user = models.user.logic.getForFields(
+      {'account': users.get_current_user()}, unique=True)
   sponsor_form = None
   existing_sponsor = None
 
--- a/app/soc/views/site/user/profile.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/views/site/user/profile.py	Sat Nov 15 03:12:33 2008 +0000
@@ -28,10 +28,10 @@
 from django import http
 from django.utils.translation import ugettext_lazy
 
+from soc.logic import accounts
 from soc.logic import models
 from soc.logic import out_of_band
 from soc.logic import validate
-from soc.logic.site import id_user
 from soc.views import simple
 from soc.views import helper
 from soc.views.helper import access
@@ -57,9 +57,9 @@
   Also, this form only permits entry and editing  of some of the User entity
   Properties, not all of them.
   """
-  id = forms.EmailField(required=False,
-      label=soc.models.user.User.id.verbose_name,
-      help_text=soc.models.user.User.id.help_text)
+  account = forms.EmailField(required=False,
+      label=soc.models.user.User.account.verbose_name,
+      help_text=soc.models.user.User.account.help_text)
 
   link_name = forms.CharField(required=False,
       label=soc.models.user.User.link_name.verbose_name,
@@ -80,8 +80,8 @@
     
     return link_name
 
-  def clean_id(self):
-    email = self.cleaned_data.get('id')
+  def clean_account(self):
+    email = self.cleaned_data.get('account')
     
     if not email:
       # email not supplied (which is OK), so do not try to convert it
@@ -130,19 +130,21 @@
     form = LookupForm(request.POST)
 
     if form.is_valid():
-      form_id = form.cleaned_data.get('id')
+      form_account = form.cleaned_data.get('account')
       
-      if form_id:
+      if form_account:
         # email provided, so attempt to look up user by email
-        user = models.user.logic.getForFields({'id': form_id}, unique=True)
+        user = models.user.logic.getForFields(
+            {'account': form_account}, unique=True)
 
         if user:
           lookup_message = ugettext_lazy('User found by email.')
         else:
           email_error = ugettext_lazy('User with that email not found.')
           range_width = helper.lists.getPreferredListPagination()
-          nearest_user_range_start = id_user.findNearestUsersOffset(
-              range_width, id=form_id)
+          nearest_user_range_start = (
+              models.user.logic.findNearestEntitiesOffset(
+                  width, [('account', form_account)]))
             
           if nearest_user_range_start is not None:            
             context['lookup_link'] = './list?offset=%s&limit=%s' % (
@@ -153,8 +155,8 @@
         
         if link_name:
           # link name provided, so try to look up by link name 
-          user = id_user.getUserFromLinkName(link_name)
-        
+          user = models.user.logic.getForFields({'link_name': link_name},
+                                                unique=True)        
           if user:
             lookup_message = ugettext_lazy('User found by link name.')
             # clear previous error, since User was found
@@ -167,8 +169,9 @@
                 'User with that link name not found.')
             if context['lookup_link'] is None:
               range_width = helper.lists.getPreferredListPagination()
-              nearest_user_range_start = id_user.findNearestUsersOffset(
-                  range_width, link_name=link_name)
+              nearest_user_range_start = (
+                models.user.logic.findNearestEntitiesOffset(
+                    width, [('link_name', link_name)]))
             
               if nearest_user_range_start is not None:
                 context['lookup_link'] = './list?offset=%s&limit=%s' % (
@@ -179,7 +182,7 @@
   if user:
     # User entity found, so populate form with existing User information
     # context['found_user'] = user
-    form = LookupForm(initial={'id': user.id.email(),
+    form = LookupForm(initial={'account': user.account.email(),
                                'link_name': user.link_name})
 
     if request.path.endswith('lookup'):
@@ -207,9 +210,9 @@
   in the Meta class, because the form behavior is unusual and normally
   required Properties of the User model need to sometimes be omitted.
   """
-  id = forms.EmailField(
-      label=soc.models.user.User.id.verbose_name,
-      help_text=soc.models.user.User.id.help_text)
+  account = forms.EmailField(
+      label=soc.models.user.User.account.verbose_name,
+      help_text=soc.models.user.User.account.help_text)
 
   link_name = forms.CharField(
       label=soc.models.user.User.link_name.verbose_name,
@@ -235,21 +238,23 @@
     key_name = self.data.get('key_name')
     user = models.user.logic.getFromKeyName(key_name)
     
-    linkname_user_exist = id_user.getUserFromLinkName(link_name)
+    linkname_user_exist = models.user.logic.getForFields(
+        {'link_name': link_name}, unique=True)
+        
     if (user and user.link_name != link_name) and linkname_user_exist:
       raise forms.ValidationError("This link name is already in use.")
 
     return link_name
 
-  def clean_id(self):
-    form_id = users.User(email=self.cleaned_data.get('id'))
-    if not id_user.isIdAvailable(
-        form_id, existing_key_name=self.data.get('key_name')):
+  def clean_account(self):
+    form_account = users.User(email=self.cleaned_data.get('account'))
+    if not accounts.isAccountAvailable(
+        form_account, existing_key_name=self.data.get('key_name')):
       raise forms.ValidationError("This account is already in use.")
-    if models.user.logic.isFormerId(form_id):
+    if models.user.logic.isFormerAccount(form_account):
       raise forms.ValidationError("This account is invalid. "
-          "It exists as former id.")
-    return form_id
+          "It exists as a former account.")
+    return form_account
 
 
 DEF_SITE_USER_PROFILE_EDIT_TMPL = 'soc/user/edit.html'
@@ -289,7 +294,7 @@
   # try to fetch User entity corresponding to link_name if one exists
   try:
     if link_name:
-      user = id_user.getUserFromLinkNameOr404(link_name)
+      user = accounts.getUserFromLinkNameOr404(link_name)
   except out_of_band.ErrorResponse, error:
     # show custom 404 page when link name doesn't exist in Datastore
     error.message = error.message + DEF_CREATE_NEW_USER_MSG
@@ -304,7 +309,7 @@
       new_link_name = form.cleaned_data.get('link_name')
 
       properties = {}
-      properties['id'] = form.cleaned_data.get('id')
+      properties['account'] = form.cleaned_data.get('account')
       properties['link_name']  = new_link_name
       properties['nick_name']  = form.cleaned_data.get('nick_name')
       properties['is_developer'] = form.cleaned_data.get('is_developer')
@@ -340,7 +345,7 @@
 
         # populate form with the existing User entity
         form = EditForm(initial={'key_name': user.key().name(),
-            'id': user.id.email(), 'link_name': user.link_name,
+            'account': user.account.email(), 'link_name': user.link_name,
             'nick_name': user.nick_name, 'is_developer': user.is_developer})
       else:
         if request.GET.get(profile.SUBMIT_MSG_PARAM_NAME):
@@ -372,9 +377,9 @@
   in the Meta class, because the form behavior is unusual and normally
   required Properties of the User model need to sometimes be omitted.
   """
-  id = forms.EmailField(
-      label=soc.models.user.User.id.verbose_name,
-      help_text=soc.models.user.User.id.help_text)
+  account = forms.EmailField(
+      label=soc.models.user.User.account.verbose_name,
+      help_text=soc.models.user.User.account.help_text)
 
   link_name = forms.CharField(
       label=soc.models.user.User.link_name.verbose_name,
@@ -395,19 +400,20 @@
     if not validate.isLinkNameFormatValid(link_name):
       raise forms.ValidationError("This link name is in wrong format.")
     else:
-      if id_user.getUserFromLinkName(link_name):
+      if models.user.logic.getForFields({'link_name': link_name},
+                                        unique=True):
         raise forms.ValidationError("This link name is already in use.")
     return link_name
 
-  def clean_id(self):
-    new_email = self.cleaned_data.get('id')
-    form_id = users.User(email=new_email)
-    if models.user.logic.getForFields({'id': form_id}, unique=True):
+  def clean_account(self):
+    new_email = self.cleaned_data.get('account')
+    form_account = users.User(email=new_email)
+    if models.user.logic.getForFields({'account': form_account}, unique=True):
       raise forms.ValidationError("This account is already in use.")
-    if models.user.logic.isFormerId(form_id):
+    if models.user.logic.isFormerAccount(form_account):
       raise forms.ValidationError("This account is invalid. "
-          "It exists as former id.")
-    return form_id
+          "It exists as a former account.")
+    return form_account
 
 
 DEF_SITE_CREATE_USER_PROFILE_TMPL = 'soc/user/edit.html'
@@ -441,17 +447,17 @@
     form = CreateForm(request.POST)
 
     if form.is_valid():
-      form_id = form.cleaned_data.get('id')
+      form_account = form.cleaned_data.get('account')
       link_name = form.cleaned_data.get('link_name')
 
       properties = {
-        'id': form_id,
+        'account': form_account,
         'link_name': link_name,
         'nick_name': form.cleaned_data.get('nick_name'),
         'is_developer': form.cleaned_data.get('is_developer'),
       }
 
-      key_fields = {'email': form_id.email()}
+      key_fields = {'email': form_account.email()}
       user = models.user.logic.updateOrCreateFromFields(properties,
                                                         key_fields)
 
--- a/app/soc/views/user/profile.py	Fri Nov 14 06:36:42 2008 +0000
+++ b/app/soc/views/user/profile.py	Sat Nov 15 03:12:33 2008 +0000
@@ -28,10 +28,10 @@
 from django import http
 from django.utils.translation import ugettext_lazy
 
+from soc.logic import accounts
 from soc.logic import models
 from soc.logic import out_of_band
 from soc.logic import validate
-from soc.logic.site import id_user
 from soc.views import helper
 from soc.views import simple
 from soc.views.helper import decorators
@@ -53,15 +53,15 @@
     model = soc.models.user.User
     
     #: list of model fields which will *not* be gathered by the form
-    exclude = ['id', 'former_ids', 'is_developer']
+    exclude = ['account', 'former_accounts', 'is_developer']
   
   def clean_link_name(self):
     link_name = self.cleaned_data.get('link_name')
     if not validate.isLinkNameFormatValid(link_name):
       raise forms.ValidationError("This link name is in wrong format.")
 
-    user = id_user.getUserFromLinkName(link_name)
-
+    user = models.user.logic.getForFields({'link_name': link_name},
+                                          unique=True)
     if user:
       raise forms.ValidationError("This link name is already in use.")
 
@@ -99,12 +99,12 @@
     A subclass of django.http.HttpResponse which either contains the form to
     be filled out, or a redirect to the correct view in the interface.
   """
-  id = users.get_current_user()
+  account = users.get_current_user()
   
   # create default template context for use with any templates
   context = helper.responses.getUniversalContext(request)
 
-  if (not id) and (not link_name):
+  if (not account) and (not link_name):
     # not logged in, and no link name, so request that the user sign in 
     return simple.requestLogin(request, page, template, context,
         # TODO(tlarsen): /user/profile could be a link to a help page instead
@@ -113,7 +113,7 @@
             ' or modify an existing one, you must first'
             ' <a href="%(sign_in)s">sign in</a>.'))
 
-  if (not id) and link_name:
+  if (not account) and link_name:
     # not logged in, so show read-only public profile for link_name user
     return simple.public(request, page=page, template=template, 
                          link_name=link_name, context=context)
@@ -123,13 +123,13 @@
   # try to fetch User entity corresponding to link_name if one exists
   try:
     if link_name:
-      link_name_user = id_user.getUserFromLinkNameOr404(link_name)
+      link_name_user = accounts.getUserFromLinkNameOr404(link_name)
   except out_of_band.ErrorResponse, error:
     # show custom 404 page when link name doesn't exist in Datastore
     return simple.errorResponse(request, page, error, template, context)
   
   # link_name_user will be None here if link name was already None...
-  if link_name_user and (link_name_user.id != id):
+  if link_name_user and (link_name_user.account != account):
     # link_name_user exists but is not the currently logged in Google Account,
     # so show public view for that (other) User entity
     return simple.public(request, page=page, template=template, 
@@ -143,17 +143,17 @@
       properties = {
         'link_name': new_link_name,
         'nick_name': form.cleaned_data.get("nick_name"),
-        'id': id,
+        'account': account,
       }
 
-      # check if user account is not in former_ids
+      # check if user account is not in former_accounts
       # if it is show error message that account is invalid
-      if models.user.logic.isFormerId(id):
+      if models.user.logic.isFormerAccount(account):
         msg = DEF_USER_ACCOUNT_INVALID_MSG
         error = out_of_band.ErrorResponse(msg)
         return simple.errorResponse(request, page, error, template, context)
       
-      user = models.user.logic.updateOrCreateFromId(properties, id)
+      user = models.user.logic.updateOrCreateFromAccount(properties, account)
       
       # redirect to /user/profile?s=0
       # (causes 'Profile saved' message to be displayed)
@@ -161,7 +161,7 @@
           request, None, params=SUBMIT_PROFILE_SAVED_PARAMS)
   else: # request.method == 'GET'
     # try to fetch User entity corresponding to Google Account if one exists
-    user = models.user.logic.getForFields({'id': id}, unique=True)
+    user = models.user.logic.getForFields({'account': account}, unique=True)
 
     if user:
       # is 'Profile saved' parameter present, but referrer was not ourself?