Added basic 'invite' functionality
authorSverre Rabbelier <srabbelier@gmail.com>
Sun, 16 Nov 2008 23:04:48 +0000
changeset 495 87afae6e4c51
parent 494 5e9c656a1b68
child 496 875bdc5741eb
Added basic 'invite' functionality Currently the invite can only be created, existing ones can be listed and edited, or deleted if desired.
app/soc/logic/models/request.py
app/soc/models/request.py
app/soc/templates/soc/models/create_invite.html
app/soc/templates/soc/models/invite.html
app/soc/templates/soc/request/list/request_heading.html
app/soc/templates/soc/request/list/request_row.html
app/soc/templates/soc/request/public.html
app/soc/templates/soc/sponsor/edit.html
app/soc/templates/soc/user/list/user_row.html
app/soc/views/models/base.py
app/soc/views/models/host.py
app/soc/views/models/request.py
app/soc/views/models/role.py
app/soc/views/models/user.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/logic/models/request.py	Sun Nov 16 23:04:48 2008 +0000
@@ -0,0 +1,60 @@
+#!/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.
+
+"""Host (Model) query functions.
+"""
+
+__authors__ = [
+  '"Sverre Rabbelier" <sverer@rabbelier.nl>',
+  ]
+
+
+from soc.logic import key_name
+from soc.logic.models import base
+
+import soc.models.request
+
+
+class Logic(base.Logic):
+  """Logic methods for the Request model
+  """
+
+  def __init__(self):
+    """Defines the name, key_name and model for this entity.
+    """
+
+    base.Logic.__init__(self, soc.models.request.Request)
+
+  def getKeyValues(self, entity):
+    """See base.Logic.getKeyNameValues.
+    """
+
+    return [entity.role, entity.to.link_name, entity.requester.link_name]
+
+  def getKeyValuesFromFields(self, fields):
+    """See base.Logic.getKeyValuesFromFields.
+    """
+
+    return [fields['role'], fields['group_ln'], fields['user_ln']]
+
+  def getKeyFieldNames(self):
+    """See base.Logic.getKeyFieldNames
+    """
+
+    return ['role', 'group_ln', 'user_ln']
+
+
+logic = Logic()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/models/request.py	Sun Nov 16 23:04:48 2008 +0000
@@ -0,0 +1,59 @@
+#!/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 Request Model."""
+
+__authors__ = [
+  '"Sverre Rabbelier" <sverre@rabbelier.nl>',
+]
+
+
+import polymodel
+
+from google.appengine.ext import db
+
+from django.utils.translation import ugettext_lazy
+
+import soc.models.user
+import soc.models.group
+
+
+class Request(polymodel.PolyModel):
+  """A request is made to allow a person to create a new Role entity.
+  """
+
+  requester = db.ReferenceProperty(reference_class=soc.models.user.User,
+                              required=True, collection_name="requests")
+  requester.help_text = ugettext_lazy(
+      'This is the user who the request is made for')
+
+  role = db.StringProperty()
+  role.help_text = ugettext_lazy(
+      'This should be the type of the role that is requested')
+
+  to = db.ReferenceProperty(reference_class=soc.models.group.Group,
+                            required=True, collection_name="requests")
+  to.help_text = ugettext_lazy(
+      'The group that the request should be made to '
+      '(this group should have the authority to grant the request)')
+
+  accepted = db.BooleanProperty()
+  accepted.help_text = ugettext_lazy(
+      'Field used to indicate whether a request has been accepted')
+
+  declined = db.BooleanProperty()
+  declined.help_text = ugettext_lazy(
+      'Field used to indicate that a request has been rejected by the user')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/models/create_invite.html	Sun Nov 16 23:04:48 2008 +0000
@@ -0,0 +1,18 @@
+{% extends "soc/models/list.html" %}
+{% comment %}
+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.
+{% endcomment %}
+
+{% block instructions %}
+Please use this form to invite someone to become a {{ entity_type }}.
+{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/models/invite.html	Sun Nov 16 23:04:48 2008 +0000
@@ -0,0 +1,27 @@
+{% extends "soc/models/edit.html" %}
+{% comment %}
+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.
+{% endcomment %}
+
+{% block instructions %}
+Hit 'Send Invitation' to send the invite. 
+{% endblock %}
+
+{% block submit_buttons %}
+ <td> 
+  <input style="font-weight: bold" type="submit" value="Send Invitation"/></span>
+ </td>
+ <td>
+  <input type="button" onclick="location.href='/'" value="Cancel"/>
+ </td>
+{% endblock %}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/request/list/request_heading.html	Sun Nov 16 23:04:48 2008 +0000
@@ -0,0 +1,7 @@
+<tr align="left">
+  <th class="first" align="right">Requester</th>
+  <th>Role</th>
+  <th>To</th>
+  <th>Accepted</th>
+  <th>Declined</th>
+</tr>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/request/list/request_row.html	Sun Nov 16 23:04:48 2008 +0000
@@ -0,0 +1,11 @@
+<tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
+onclick="document.location.href='/{{ entity_type|lower }}/edit/{{ data_element.role|lower }}/{{ data_element.to.link_name }}/{{ data_element.requester.link_name }}'" name="name">
+  <td align="right"><div class="user_link_name"><a class="noul"
+         href="/{{ entity_type|lower }}/edit/{{ data_element.role|lower }}/{{ data_element.to.link_name }}/{{ data_element.requester.link_name }}">{{ data_element.requester.link_name }}</a>
+     </div>
+  </td>
+  <td><div class="role">{{ data_element.role }}</div></td>
+  <td><div class="to">{{ data_element.to.link_name }}</div></td>
+  <td><div class="accepted">{{ data_element.accepted }}</div></td>
+  <td><div class="declined">{{ data_element.declined }}</div></td>
+</tr>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/request/public.html	Sun Nov 16 23:04:48 2008 +0000
@@ -0,0 +1,32 @@
+{% extends "soc/base.html" %}
+{% comment %}
+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.
+{% endcomment %}
+{% load forms_helpers %}
+
+{% block header_title %}
+{{ page_name }} for {{ entity.requester.link_name }}
+{% endblock %}
+
+{% block body %}
+<p>
+ <table>
+  {% readonly_field_as_table_row entity.fields.role.label entity.role %}
+  {% readonly_field_as_table_row entity.fields.requester.label entity.requester.link_name %}
+  {% readonly_field_as_table_row entity.fields.to.label entity.to.link_name %} 
+  {% readonly_field_as_table_row entity.fields.accepted.label entity.accepted %}
+  {% readonly_field_as_table_row entity.fields.declined.label entity.declined %}
+  <!-- TODO(pawel.solyga) make this generic -->
+ </table>
+</p>
+{% endblock %}
--- a/app/soc/templates/soc/sponsor/edit.html	Sun Nov 16 23:03:37 2008 +0000
+++ b/app/soc/templates/soc/sponsor/edit.html	Sun Nov 16 23:04:48 2008 +0000
@@ -15,4 +15,7 @@
 
 {% block submit_buttons %}
 {{ block.super }}
+<td>
+ <input type="button" onclick="location.href='/host/invite/{{ entity.link_name }}'" value="Invite Host"/>
+</td>
 {% endblock %}
--- a/app/soc/templates/soc/user/list/user_row.html	Sun Nov 16 23:03:37 2008 +0000
+++ b/app/soc/templates/soc/user/list/user_row.html	Sun Nov 16 23:04:48 2008 +0000
@@ -1,7 +1,7 @@
 <tr class="off" onmouseover="this.className='on'" onmouseout="this.className='off'" 
-onclick="document.location.href='/user/edit/{{ data_element.link_name }}'" name="name">
+onclick="document.location.href='/{{redirect_action|lower}}/{{ data_element.link_name }}'" name="name">
   <td align="right"><div class="account"><a class="noul"
-         href="/user/edit/{{ data_element.link_name }}">{{ data_element.account }}</a>
+         href="/{{redirect_action|lower}}/{{ data_element.link_name }}">{{ data_element.account }}</a>
      </div>
   </td>
   <td><div class="email">{{ data_element.account.email }}</a></div></td>
--- a/app/soc/views/models/base.py	Sun Nov 16 23:03:37 2008 +0000
+++ b/app/soc/views/models/base.py	Sun Nov 16 23:04:48 2008 +0000
@@ -105,6 +105,8 @@
             'soc.views.models.%s.public', 'Show %(name)s'),
         (r'^%(name_lower)s/create$',
             'soc.views.models.%s.create', 'Create %(name)s'),
+        (r'^%(name_lower)s/create/%(key_fields)s$',
+            'soc.views.models.%s.create', 'Create %(name)s'),
         (r'^%(name_lower)s/delete/%(key_fields)s$',
             'soc.views.models.%s.delete', 'Delete %(name)s'),
         (r'^%(name_lower)s/edit/%(key_fields)s$',
@@ -113,6 +115,8 @@
             'soc.views.models.%s.list', 'List %(name_plural)s'),
         ]
 
+    new_params['list_redirect_action'] = params['name_short'] + '/edit'
+
     self._rights = dicts.merge(rights, new_rights)
     self._params = dicts.merge(params, new_params)
 
@@ -186,7 +190,7 @@
     if not kwargs:
       return self.edit(request, page_name=page_name, params=params, **empty_kwargs)
     else:
-      return self.edit(request, page_name=page_name, params=params, seed=kwargs)
+      return self.edit(request, page_name=page_name, params=params, seed=kwargs, **empty_kwargs)
 
   def edit(self, request, page_name=None, params=None, seed=None, **kwargs):
     """Displays the edit page for the entity specified by **kwargs
@@ -295,6 +299,7 @@
       self._editGet(request, entity, form)
     else:
       if seed:
+        self._editSeed(request, seed)
         form = params['create_form'](initial=seed)
       else:
         form = params['create_form']()
@@ -336,6 +341,7 @@
 
     context['entity_type'] = params['name']
     context['entity_type_plural'] = params['name_plural']
+    context['redirect_action'] = params['list_redirect_action']
 
     template = params['list_template']
 
--- a/app/soc/views/models/host.py	Sun Nov 16 23:03:37 2008 +0000
+++ b/app/soc/views/models/host.py	Sun Nov 16 23:04:48 2008 +0000
@@ -31,6 +31,7 @@
 
 import soc.models.host
 import soc.logic.models.host
+import soc.logic.models.sponsor
 import soc.views.helper
 
 
@@ -71,7 +72,7 @@
 
   pass
 
-class View(base.View):
+class View(role.RoleView):
   """View methods for the Host model
   """
 
@@ -89,6 +90,10 @@
     params = {}
     rights = {}
 
+    params['logic'] = soc.logic.models.host.logic
+    params['group_logic'] = soc.logic.models.sponsor.logic
+    params['invite_filter'] = {'group_ln': 'link_name'}
+
     params['name'] = "Host"
     params['name_short'] = "Host"
     params['name_plural'] = "Hosts"
@@ -100,6 +105,7 @@
     params['edit_template'] = 'soc/models/edit.html'
     params['public_template'] = 'soc/host/public.html'
     params['list_template'] = 'soc/models/list.html'
+    params['invite_template'] = 'soc/models/invite.html'
 
     params['lists_template'] = {
       'list_main': 'soc/list/list_main.html',
@@ -109,7 +115,7 @@
     }
 
     params['delete_redirect'] = '/host/list'
-    params['invite_redirect'] = '/host/list'
+    params['invite_redirect'] = '/request/list'
 
     params['save_message'] = [ugettext_lazy('Profile saved.')]
 
@@ -123,14 +129,7 @@
     params = dicts.merge(original_params, params)
     rights = dicts.merge(original_rights, rights)
 
-    base.View.__init__(self, rights=rights, params=params)
-
-  def _editPost(self, request, entity, fields):
-    """See base.View._editPost().
-    """
-
-    fields['sponsor_ln'] = fields['sponsor'].link_name
-    fields['user_ln'] = fields['user'].link_name
+    role.RoleView.__init__(self, original_rights=rights, original_params=params)
 
 
 view = View()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/views/models/request.py	Sun Nov 16 23:04:48 2008 +0000
@@ -0,0 +1,172 @@
+#!/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.
+
+"""Views for Requests.
+"""
+
+__authors__ = [
+    '"Sverre Rabbelier" <sverre@rabbelier.nl>',
+    '"Lennard de Rijk" <ljvderijk@gmail.com>',
+    '"Pawel Solyga" <pawel.solyga@gmail.com>',
+  ]
+
+
+from google.appengine.api import users
+
+from django import forms
+from django.utils.translation import ugettext_lazy
+
+from soc.logic import dicts
+from soc.logic import validate
+from soc.logic.models import sponsor as sponsor_logic
+from soc.logic.models import user as user_logic
+from soc.views import helper
+from soc.views.helper import widgets
+from soc.views.models import base
+
+import soc.models.request
+import soc.logic.models.request
+import soc.logic.dicts
+import soc.views.helper
+import soc.views.helper.widgets
+
+class CreateForm(helper.forms.BaseForm):
+  """Django form displayed when Developer creates a Request.
+  """
+
+  class Meta:
+    model = soc.models.request.Request
+
+    #: list of model fields which will *not* be gathered by the form 
+    exclude = ['inheritance_line', 'requester', 'to', 'role', 'declined']
+
+  role = forms.CharField(widget=helper.widgets.ReadOnlyInput())
+
+  user = forms.CharField(
+      label=soc.models.request.Request.requester.verbose_name,
+      help_text=soc.models.request.Request.requester.help_text,
+      widget=helper.widgets.ReadOnlyInput())  
+
+  group = forms.CharField(
+      label=soc.models.request.Request.to.verbose_name,
+      help_text=soc.models.request.Request.to.help_text,
+      widget=helper.widgets.ReadOnlyInput())
+
+  def clean_user(self):
+    self.cleaned_data['requester'] =  user_logic.logic.getForFields(
+        {'link_name': self.cleaned_data['user']}, unique=True)
+    return self.cleaned_data['user']
+
+  def clean_group(self):
+    self.cleaned_data['to'] = sponsor_logic.logic.getFromFields(
+        link_name=self.cleaned_data['group'])
+    return self.cleaned_data['group']
+
+class EditForm(CreateForm):
+  """Django form displayed when Developer edits a Request.
+  """
+
+  pass
+
+
+class View(base.View):
+  """View methods for the Docs model
+  """
+
+  def __init__(self, original_params=None, original_rights=None):
+    """Defines the fields and methods required for the base View class
+    to provide the user with list, public, create, edit and delete views.
+
+    Params:
+      original_params: a dict with params for this View
+      original_rights: a dict with right definitions for this View
+    """
+
+    self._logic = soc.logic.models.request.logic
+
+    params = {}
+    rights = {}
+
+    params['name'] = "Request"
+    params['name_short'] = "Request"
+    params['name_plural'] = "Requests"
+
+    params['edit_form'] = EditForm
+    params['create_form'] = CreateForm
+
+    # TODO(tlarsen) Add support for Django style template lookup
+    params['edit_template'] = 'soc/models/edit.html'
+    params['public_template'] = 'soc/request/public.html'
+    params['list_template'] = 'soc/models/list.html'
+
+    params['lists_template'] = {
+      'list_main': 'soc/list/list_main.html',
+      'list_pagination': 'soc/list/list_pagination.html',
+      'list_row': 'soc/request/list/request_row.html',
+      'list_heading': 'soc/request/list/request_heading.html',
+    }
+
+    params['sidebar_defaults'] = [('/%s/list', 'List %(plural)s')]
+
+    params['delete_redirect'] = '/request/list'
+    params['create_redirect'] = '/request'
+
+    params['save_message'] = [ugettext_lazy('Request saved.')]
+
+    params['edit_params'] = {
+        self.DEF_SUBMIT_MSG_PARAM_NAME: self.DEF_SUBMIT_MSG_PROFILE_SAVED,
+        }
+
+    rights['list'] = [helper.access.checkIsDeveloper]
+    rights['delete'] = [helper.access.checkIsDeveloper]
+
+    params = dicts.merge(original_params, params)
+    rights = dicts.merge(original_rights, rights)
+
+    base.View.__init__(self, rights=rights, params=params)
+
+  def _editSeed(self, request, seed):
+    """See base.View._editGet().
+    """
+
+    # fill in the email field with the data from the entity
+    seed['user'] = seed['user_ln']
+    seed['group'] = seed['group_ln']
+
+  def _editGet(self, request, entity, form):
+    """See base.View._editGet().
+    """
+
+    # fill in the email field with the data from the entity
+    form.fields['user'].initial = entity.requester.link_name
+    form.fields['group'].initial = entity.to.link_name 
+
+  def _editPost(self, request, entity, fields):
+    """See base.View._editPost().
+    """
+
+    # fill in the account field with the user created from email
+    fields['user_ln'] = fields['requester'].link_name
+    fields['group_ln'] = fields['to'].link_name
+
+
+view = View()
+
+create = view.create
+edit = view.edit
+delete = view.delete
+list = view.list
+public = view.public
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/views/models/role.py	Sun Nov 16 23:04:48 2008 +0000
@@ -0,0 +1,202 @@
+#!/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.
+
+"""Views for Sponsor profiles.
+"""
+
+__authors__ = [
+    '"Sverre Rabbelier" <sverre@rabbelier.nl>',
+  ]
+
+
+from google.appengine.api import users
+
+from django import forms
+from django import http
+from django.utils.translation import ugettext_lazy
+
+from soc.models import request as request_model
+from soc.logic import dicts
+from soc.logic.models import request as request_logic
+from soc.logic.models import user as user_logic
+from soc.views import helper
+from soc.views.models import base
+from soc.views.models import user as user_view
+
+import soc.models.request
+import soc.views.helper.widgets
+
+
+class RequestForm(helper.forms.BaseForm):
+  """Django form displayed when creating a new Invite
+  """
+
+  class Meta:
+    """Inner Meta class that defines some behavior for the form.
+    """
+
+    #: db.Model subclass for which the form will gather information
+    model = soc.models.request.Request
+
+    #: exclude pretty much everything, model=None would also remove the help text etc. 
+    exclude = ['inheritance_line', 'requester', 'to', 'role', 'accepted', 'declined']
+
+  requester = forms.CharField(widget=helper.widgets.ReadOnlyInput())
+
+  role = forms.CharField(widget=helper.widgets.ReadOnlyInput())
+
+  to = forms.CharField(widget=helper.widgets.ReadOnlyInput())
+
+
+class RoleView(base.View):
+  """Views for all entities that inherit from Role
+
+  All views that only Role entities have are defined in this subclass.
+  """
+
+  def __init__(self, original_params=None, original_rights=None):
+    """
+
+    Args:
+      rights: This dictionary should be filled with the access check
+        functions that should be called, it will be modified in-place.
+      params: This dictionary should be filled with the parameters
+    """
+
+    params = {}
+    rights = {}
+
+    params = dicts.merge(original_params, params)
+    rights = dicts.merge(original_rights, rights)
+
+    base.View.__init__(self, rights=rights, params=params)
+
+  def invite(self, request, page_name=None, params=None, **kwargs):
+    """Displays the request promotion to Role page.
+    """
+
+    new_params = {}
+
+    new_params['list_template'] = 'soc/models/create_invite.html'
+    new_params['list_redirect_action'] = 'request/create/%s/%s' % (
+        self._params['name_short'].lower(),
+        kwargs['link_name'])
+    new_params['list_redirect_entity'] = self._params['name']
+    new_params['name'] = self._params['name']
+    new_params['name_short'] = self._params['name_short']
+    new_params['name_plural'] = self._params['name_plural']
+
+    params = dicts.merge(params, new_params)
+
+    try:
+      self.checkAccess('invite', request)
+    except soc.views.out_of_band.AccessViolationResponse, alt_response:
+      return alt_response.response()
+
+    return user_view.list(request, page_name=page_name, params=params)
+
+  def promote(self, request, page_name=None, **kwargs):
+    """Displays the promote to Role page.
+
+    Args:
+      request: the standard Django HTTP request object
+      page: a soc.logic.site.page.Page object which is abstraction
+        that combines a Django view with sidebar menu info
+      kwargs: the Key Fields for the specified entity
+    """
+
+    properties = {
+        'accepted': True,
+        }
+
+    entity = request_logic.logic.updateOrCreateFromFields(properties, **kwargs)
+
+    # TODO(SRabbelier) finish this
+
+  def accept(self, request, page_name=None, params=None, **kwargs):
+    """Displays the accept a Role request page.
+
+    Args:
+      request: the standard Django HTTP request object
+      page: a soc.logic.site.page.Page object which is abstraction
+        that combines a Django view with sidebar menu info
+      kwargs: the Key Fields for the specified entity
+    """
+
+    entity = request_logic.logic.getFromFields(**kwargs)
+
+    if entity.declined:
+      properties = {
+          'declined': False,
+          }
+
+      request_logic.logic.updateModelProperties(entity, **properties)
+
+    if not entity.accepted:
+      raise Error("The request has not yet been accepted")
+
+    id = users.get_current_user()
+    user = models.user.logic.getFromFields(email=id.email())
+
+    if entity.user != user:
+      raise Error("The request is being accepted by the wrong person")
+
+    if entity.role != params['name'].lower():
+      raise Error("The wrong module is handling the request")
+
+    redirect = params['accept_redirect']
+    suffix = self._logic.getKeySuffix(entity)
+
+    return helper.responses.redirectToChangedSuffix(
+        request, suffix, suffix)
+
+  def decline(self, request, page_name=None, **kwargs):
+    """Displays the decline a Role request page.
+
+    Args:
+      request: the standard Django HTTP request object
+      page: a soc.logic.site.page.Page object which is abstraction
+        that combines a Django view with sidebar menu info
+      kwargs: the Key Fields for the specified entity
+    """
+
+    properties = {
+        'declined': True,
+        }
+
+    request_logic.logic.updateOrCreateFromFields(properties, **kwargs)
+
+    redirect = self._params['decline_redirect']
+    suffix = self._logic.getKeySuffix(entity)
+
+    return helper.responses.redirectToChangedSuffix(
+        request, suffix, suffix)
+
+  def getDjangoURLPatterns(self):
+    """see base.View.getDjangoURLPatterns()
+    """
+
+    params = {}
+    default_patterns = self._params['django_patterns_defaults']
+    default_patterns += [
+        (r'^%(name_lower)s/invite/%(lnp)s$',
+            'soc.views.models.%s.invite', 'Invite %(name)s')]
+
+    params['django_patterns_defaults'] = default_patterns
+    patterns = super(RoleView, self).getDjangoURLPatterns(params)
+
+    return patterns
+
--- a/app/soc/views/models/user.py	Sun Nov 16 23:03:37 2008 +0000
+++ b/app/soc/views/models/user.py	Sun Nov 16 23:04:48 2008 +0000
@@ -184,7 +184,6 @@
     """
     # fill in the email field with the data from the entity
     form.fields['email'].initial = entity.account.email()
-    
 
   def _editPost(self, request, entity, fields):
     """See base.View._editPost().