Added the new way to process invites for club_admin only.
authorLennard de Rijk <ljvderijk@gmail.com>
Fri, 23 Jan 2009 12:00:48 +0000
changeset 920 39badbfb80be
parent 919 3f9072bab1d4
child 921 e499cc2641f6
Added the new way to process invites for club_admin only. This patch contains the separability needed to ensure that we can successfully do the access checks we need. Host invites can for the time being only be completed by developers due to the reorganization. Patch by: Lennard de Rijk Reviewed by: to-be-reviewed
app/soc/templates/soc/request/process_invite.html
app/soc/views/helper/redirects.py
app/soc/views/models/club_admin.py
app/soc/views/models/host.py
app/soc/views/models/request.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/templates/soc/request/process_invite.html	Fri Jan 23 12:00:48 2009 +0000
@@ -0,0 +1,37 @@
+{% 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_id }}
+{% endblock %}
+
+{% block body %}
+<p>
+<div="text"> 
+You have been invited to become a {{ entity.role_verbose }} for {{entity.scope.name }}. 
+Please select your response.
+</div>
+</p>
+
+<tr>
+  <td>
+    <input type="button" onclick="location.href='/{{ entity.role }}/accept_invite/{{ entity.scope_path }}/{{ entity.link_id }}'" value="Accept"/>
+    <input type="button" onclick="location.href='/{{ module_name }}/process_invite/{{ entity.scope_path }}/{{ entity.role }}/{{ entity.link_id }}?status=rejected'" value="Reject"/>
+  </td>
+</tr>
+</table>
+
+{% endblock %}
--- a/app/soc/views/helper/redirects.py	Fri Jan 23 11:40:35 2009 +0000
+++ b/app/soc/views/helper/redirects.py	Fri Jan 23 12:00:48 2009 +0000
@@ -91,9 +91,15 @@
   """Returns the redirect for accepting an invite.
   """
 
-  return '/%s/create/%s/%s' % (
+  return '/%s/accept_invite/%s/%s' % (
       entity.role, entity.scope_path, entity.link_id)
 
+def inviteProcessRedirect(entity, _):
+  """Returns the redirect for processing an invite
+  """
+
+  return '/request/process_invite/%s/%s/%s' % (
+      entity.scope_path, entity.role, entity.link_id)
 
 def getApplicantRedirect(entity, params):
   """Returns the redirect for processing accepted Applications.
--- a/app/soc/views/models/club_admin.py	Fri Jan 23 11:40:35 2009 +0000
+++ b/app/soc/views/models/club_admin.py	Fri Jan 23 12:00:48 2009 +0000
@@ -19,22 +19,33 @@
 
 __authors__ = [
     '"Sverre Rabbelier" <sverre@rabbelier.nl>',
+    '"Lennard de Rijk" <ljvderijk@gmail.com>'
   ]
 
 
+from django import http
 from django import forms
 
 from soc.logic import cleaning
 from soc.logic import dicts
+from soc.logic.models import club as club_logic
+from soc.logic.models import user as user_logic
+from soc.logic.models import request as request_logic
+from soc.views.helper import access
+from soc.views.helper import decorators
+from soc.views.helper import dynaform
 from soc.views.helper import redirects
+from soc.views.helper import responses
+from soc.views.helper import widgets
 from soc.views.models import base
 from soc.views.models import club as club_view
+from soc.views.models import request as request_view
 
 import soc.logic.models.club_admin
 
 
 class View(base.View):
-  """View methods for the Program model.
+  """View methods for the Club Admin model.
   """
 
   def __init__(self, params=None):
@@ -45,27 +56,258 @@
       params: a dict with params for this View
     """
 
+    rights = {}
+    rights['create'] = [access.checkIsHost]
+    rights['edit'] = [access.checkIsMyActiveRole(soc.logic.models.club_admin)]
+    rights['delete'] = [access.checkIsHost]
+    rights['invite'] = [access.checkIsClubAdminForClub]
+    rights['accept_invite'] = [access.checkCanCreateFromRequest('club_admin')]
+
     new_params = {}
     new_params['logic'] = soc.logic.models.club_admin.logic
+    new_params['rights'] = rights
 
     new_params['scope_view'] = club_view
     new_params['scope_redirect'] = redirects.getCreateRedirect
 
     new_params['name'] = "Club Admin"
 
-    new_params['extra_dynaexclude'] = ['user', 'org']
+    new_params['extra_dynaexclude'] = ['user', 'club', 'state']
+
+    new_params['create_extra_dynafields'] = {
+       'scope_path': forms.CharField(widget=forms.HiddenInput,
+                                  required=True),
+       'clean_link_id' : cleaning.clean_existing_user('link_id'),
+       'clean_home_page' : cleaning.clean_url('home_page'),
+       'clean_blog' : cleaning.clean_url('blog'),
+       'clean_photo_url' : cleaning.clean_url('photo_url')}
 
+    patterns = [(r'^%(url_name)s/(?P<access_type>invite)/%(lnp)s$',
+        'soc.views.models.%(module_name)s.invite',
+        'Create invite for %(name_plural)s'),
+        (r'^%(url_name)s/(?P<access_type>accept_invite)/%(scope)s/%(lnp)s$',
+        'soc.views.models.%(module_name)s.acceptInvite',
+        'Accept invite for %(name_plural)s')]
+
+    new_params['extra_django_patterns'] = patterns
+    
     params = dicts.merge(params, new_params)
 
     super(View, self).__init__(params=params)
 
+    # create and store the special form for invited users
+    updated_fields = {
+        'link_id': forms.CharField(widget=widgets.ReadOnlyInput(),
+            required=False)}
+
+    invited_create_form = dynaform.extendDynaForm(
+        dynaform = self._params['create_form'],
+        dynafields = updated_fields)
+
+    params['invited_create_form'] = invited_create_form
+
+
+  def _editPost(self, request, entity, fields):
+    """See base.View._editPost().
+    """
+
+    fields['user'] = fields['link_id']
+    fields['link_id'] = fields['user'].link_id
+
+    club = club_logic.logic.getFromKeyName(fields['scope_path'])
+    fields['club'] =  club
+
+    super(View, self)._editPost(request, entity, fields)
+
+
+  @decorators.merge_params
+  @decorators.check_access
+  def acceptInvite(self, request, access_type,
+                   page_name=None, params=None, **kwargs):
+    """Creates the page process an invite into a Club Admin.
+
+    Args:
+      request: the standard Django HTTP request object
+      access_type : the name of the access type which should be checked
+      context: dictionary containing the context for this view
+      params: a dict with params for this View
+      kwargs: the Key Fields for the specified entity
+    """
+  
+     # get the context for this webpage
+    context = responses.getUniversalContext(request)
+    context['page_name'] = page_name
+
+    if request.method == 'POST':
+      return self.acceptInvitePost(request, context, params, **kwargs)
+    else:
+      # request.method == 'GET'
+      return self.acceptInviteGet(request, context, params, **kwargs)
+
+  def acceptInviteGet(self, request, context, params, **kwargs):
+    """Handles the GET request concerning the creation of a Club Admin via an
+    invite.
+
+    Args:
+      request: the standard Django HTTP request object
+      context: dictionary containing the context for this view
+      params: a dict with params for this View
+      kwargs: the Key Fields for the specified entity
+    """
+
+    # create the form using the scope_path and link_id from kwargs as initial value
+    fields = {'link_id' : kwargs['link_id'],
+              'scope_path' : kwargs['scope_path']}
+    form = params['invited_create_form'](initial=fields)
+
+    # construct the appropriate response
+    return super(View, self)._constructResponse(request, entity=None,
+        context=context, form=form, params=params)
+
+  def acceptInvitePost(self, request, context, params, **kwargs):
+    """Handles the POST request concerning the creation of a Club Admin via an
+    invite.
+
+    Args:
+      request: the standard Django HTTP request object
+      context: dictionary containing the context for this view
+      params: a dict with params for this View
+      kwargs: the Key Fields for the specified entity
+    """
+
+    # populate the form using the POST data
+    form = params['invited_create_form'](request.POST)
+
+    if not form.is_valid():
+      # return the invalid form response
+      return self._constructResponse(request, entity=None, context=context,
+          form=form, params=params)
+
+    # collect the cleaned data from the valid form
+    key_name, fields = soc.views.helper.forms.collectCleanedFields(form)
+    
+    # fill in the appropriate fields that were missing in the form
+    fields['user'] = fields['link_id']
+    fields['link_id'] = fields['user'].link_id
+
+    club = club_logic.logic.getFromKeyName(fields['scope_path'])
+    fields['club'] =  club
+    fields['scope'] = club
+    
+    # make sure that this role becomes active once more in case this user
+    # has been reinvited
+    fields ['state'] = 'active'
+    
+    # get the key_name for the new entity
+    key_fields =  self._logic.getKeyFieldsFromDict(fields)
+    key_name = self._logic.getKeyNameForFields(key_fields)
+
+    # create new Club Admin entity
+    entity = self._logic.updateOrCreateFromKeyName(fields, key_name)
+
+    # redirect to the roles overview page
+    return http.HttpResponseRedirect('/user/roles')
+
+
+  @decorators.merge_params
+  @decorators.check_access
+  def invite(self, request, access_type,
+                   page_name=None, params=None, **kwargs):
+    """Creates the page upon which a Club Admin can invite another Club Admin.
+
+    Args:
+      request: the standard Django HTTP request object
+      access_type : the name of the access type which should be checked
+      context: dictionary containing the context for this view
+      params: a dict with params for this View
+      kwargs: the Key Fields for the specified entity
+    """
+
+    # get the context for this webpage
+    context = responses.getUniversalContext(request)
+    context['page_name'] = page_name
+
+    if request.method == 'POST':
+      return self.invitePost(request, context, params, **kwargs)
+    else:
+      # request.method == 'GET'
+      return self.inviteGet(request, context, params, **kwargs)
+
+  def inviteGet(self, request, context, params, **kwargs):
+    """Handles the GET request concerning the view that creates an invite
+    for becoming a Club Admin.
+
+    Args:
+      request: the standard Django HTTP request object
+      page_name: the page name displayed in templates as page and header title
+      params: a dict with params for this View
+      kwargs: the Key Fields for the specified entity
+    """
+
+    # set the role to the right name
+    fields = {'role' : '%(module_name)s' %(params)}
+
+    # get the request view parameters and initialize the create form
+    request_params = request_view.view.getParams()
+    form = request_params['create_form'](initial=fields)
+
+    # construct the appropriate response
+    return super(View, self)._constructResponse(request, entity=None,
+        context=context, form=form, params=params)
+
+  def invitePost(self, request, context, params, **kwargs):
+    """Handles the POST request concerning the view that creates an invite
+    for becoming a Club Admin.
+
+    Args:
+      request: the standard Django HTTP request object
+      page_name: the page name displayed in templates as page and header title
+      params: a dict with params for this View
+      kwargs: the Key Fields for the specified entity
+    """
+
+    # get the request view parameters and populate the form using POST data
+    request_params = request_view.view.getParams()
+    form = request_params['create_form'](request.POST)
+
+    if not form.is_valid():
+      # return the invalid form response
+      return self._constructResponse(request, entity=None, context=context,
+          form=form, params=params)
+
+    # collect the cleaned data from the valid form
+    key_name, form_fields = soc.views.helper.forms.collectCleanedFields(form)
+    
+    # get the club entity for which this request is for from link_id in kwargs
+    club = club_logic.logic.getForFields({'link_id' : kwargs['link_id']}, unique=True)
+    
+    # create the fields for the new request entity
+    request_fields = {'link_id' : form_fields['link_id'].link_id,
+        'scope' : club,
+        'scope_path' : club.link_id,
+        'role' : params['module_name'],
+        'role_verbose' : params['name'],
+        'group_accepted' : True,
+        'completed' : False}
+
+    # extract the key_name for the new request entity
+    key_fields = request_logic.logic.getKeyFieldsFromDict(request_fields)
+    key_name = request_logic.logic.getKeyNameForFields(key_fields)
+
+    # create the request entity
+    entity = request_logic.logic.updateOrCreateFromKeyName(request_fields, key_name)
+    
+    # TODO(ljvderijk) redirect to a more useful place like the club homepage
+    return http.HttpResponseRedirect('/')
 
 
 view = View()
 
+acceptInvite = view.acceptInvite
 create = view.create
 delete = view.delete
 edit = view.edit
+invite = view.invite
 list = view.list
 public = view.public
 export = view.export
--- a/app/soc/views/models/host.py	Fri Jan 23 11:40:35 2009 +0000
+++ b/app/soc/views/models/host.py	Fri Jan 23 12:00:48 2009 +0000
@@ -78,6 +78,9 @@
 
   pass
 
+# TODO(ljvderijk) rewrite the Host View module to comply with the new request system
+# so that non-developers can create Hosts
+
 class View(role.View):
   """View methods for the Host model.
   """
@@ -91,8 +94,9 @@
     """
 
     rights = {}
-    rights['create'] = [access.checkIsHost]
-    rights['edit'] = [access.checkIsHost]
+    rights['create'] = [access.checkIsDeveloper]
+    # TODO(ljvderijk) write the edit check
+    #rights['edit'] = [access.checkIsMyHost]
     rights['list'] = [access.checkIsHost]
 
     new_params = {}
--- a/app/soc/views/models/request.py	Fri Jan 23 11:40:35 2009 +0000
+++ b/app/soc/views/models/request.py	Fri Jan 23 12:00:48 2009 +0000
@@ -27,8 +27,11 @@
 from google.appengine.api import users
 
 from django import forms
+from django import http
+from django.core import urlresolvers
 from django.utils.translation import ugettext_lazy
 
+from soc.logic import cleaning
 from soc.logic import dicts
 from soc.logic.models import sponsor as sponsor_logic
 from soc.logic.models import user as user_logic
@@ -37,6 +40,8 @@
 from soc.views.helper import access
 from soc.views.helper import decorators
 from soc.views.helper import redirects
+from soc.views.helper import responses
+from soc.views.helper import widgets
 from soc.views.models import base
 
 import soc.models.request
@@ -45,53 +50,10 @@
 import soc.views.helper
 import soc.views.helper.lists
 import soc.views.helper.responses
-import soc.views.helper.widgets
-
-
-class CreateForm(helper.forms.BaseForm):
-  """Django form displayed when Developer creates a Request.
-  """
-
-  class Meta:
-    """Inner Meta class that defines some behavior for the form.
-    """
-    model = soc.models.request.Request
-
-    #: list of model fields which will *not* be gathered by the form
-    exclude = ['scope', 'scope_path', 'link_id', 'role', 'declined']
-
-  role = forms.CharField(widget=helper.widgets.ReadOnlyInput())
-
-  user = forms.CharField(
-      label=soc.models.request.Request.link_id.verbose_name,
-      help_text=soc.models.request.Request.link_id.help_text,
-      widget=helper.widgets.ReadOnlyInput())
-
-  group = forms.CharField(
-      label=soc.models.request.Request.scope.verbose_name,
-      help_text=soc.models.request.Request.scope.help_text,
-      widget=helper.widgets.ReadOnlyInput())
-
-  def clean_user(self):
-    self.cleaned_data['requester'] =  user_logic.logic.getForFields(
-        {'link_id': self.cleaned_data['user']}, unique=True)
-    return self.cleaned_data['user']
-
-  def clean_group(self):
-    self.cleaned_data['to'] = sponsor_logic.logic.getFromFields(
-        link_id=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.
+  """View methods for the Request model.
   """
 
   def __init__(self, params=None):
@@ -104,7 +66,11 @@
 
     rights = {}
     rights['listSelf'] = [access.checkAgreesToSiteToS]
-    rights['create'] = [access.checkCanInvite]
+    rights['create'] = [access.allow] # TODO(ljvderijk) Set to deny once host has been converted
+    rights['edit'] = [access.checkIsDeveloper]
+    rights['process_invite'] = [access.checkIsMyUncompletedRequest]
+    rights['list'] = [access.checkIsDeveloper]
+    rights['delete'] = [access.checkIsDeveloper]
 
     new_params = {}
     new_params['rights'] = rights
@@ -112,17 +78,87 @@
 
     new_params['name'] = "Request"
 
-    new_params['edit_form'] = EditForm
-    new_params['create_form'] = CreateForm
-
     new_params['sidebar_defaults'] = [('/%s/list', 'List %(name_plural)s', 'list')]
 
     new_params['save_message'] = [ugettext_lazy('Request saved.')]
+    
+    new_params['extra_dynaexclude'] = ['group_accepted', 'user_accepted', 
+        'role_verbose', 'completed']
+    
+    # TODO(ljvderijk) add clean field that checks to see if the user already has
+    # the role that's been entered in the create form fields
+    new_params['create_extra_dynafields'] = {
+        'role' : forms.CharField(widget=widgets.ReadOnlyInput(),
+                                   required=True),
+        'clean_link_id': cleaning.clean_existing_user('link_id')
+        }
+
+    patterns = [(r'^%(url_name)s/(?P<access_type>invite)/%(lnp)s$',
+        'soc.views.models.%(module_name)s.invite',
+        'Create invite for %(name_plural)s'),
+        (r'^%(url_name)s/(?P<access_type>process_invite)/%(key_fields)s$',
+          'soc.views.models.%(module_name)s.processInvite',
+          'Process Invite to for a Role')]
+
+    new_params['extra_django_patterns'] = patterns
+    
+    new_params['invite_processing_template'] = 'soc/request/process_invite.html'
 
     params = dicts.merge(params, new_params)
 
     super(View, self).__init__(params=params)
 
+
+  @decorators.merge_params
+  @decorators.check_access
+  def processInvite(self, request, access_type,
+                   page_name=None, params=None, **kwargs):
+    """Creates the page upon which an invite can be processed.
+
+    Args:
+      request: the standard Django HTTP request object
+      access_type : the name of the access type which should be checked
+      context: dictionary containing the context for this view
+      params: a dict with params for this View
+      kwargs: the Key Fields for the specified entity
+    """
+
+    # get the context for this webpage
+    context = responses.getUniversalContext(request)
+    context['page_name'] = page_name
+    
+    request_logic = params['logic']
+
+    # get the request entity using the information from kwargs
+    fields = {'link_id' : kwargs['link_id'],
+        'scope_path' : kwargs['scope_path'],
+        'role' : kwargs['role'],
+        'group_accepted' : True,
+        'user_accepted' : False,
+        'completed' : False}
+    request_entity = request_logic.getForFields(fields, unique=True)
+    
+    get_dict = request.GET
+    
+    if 'status' in get_dict.keys():
+      if get_dict['status'] == 'rejected':
+        # this invite has been rejected mark accepted as False and mark completed as True
+        request_logic.updateModelProperties(request_entity, {
+            'user_accepted' : False, 'completed' : True})
+        
+        # redirect to user role overview
+        return http.HttpResponseRedirect('/user/roles')
+
+    # put the entity in the context
+    context['entity'] = request_entity
+    context['module_name'] = params['module_name']
+
+    #display the invite processing page using the appropriate template
+    template = params['invite_processing_template']
+
+    return responses.respond(request, template, context=context)
+
+
   @decorators.merge_params
   @decorators.check_access
   def listSelf(self, request, access_type,
@@ -140,26 +176,29 @@
     properties = {'account': users.get_current_user()}
     user_entity = user_logic.logic.getForFields(properties, unique=True)
 
-    # construct the Unhandled Requests list
+    # construct the Unhandled Invites list
 
-    # only select the requests for this user that haven't been handled yet
+    # only select the Invites for this user that haven't been handled yet
     filter = {'link_id': user_entity.link_id,
-              'group_accepted' : True}
+              'group_accepted' : True,
+              'user_accepted' : False,
+              'completed' : False}
 
     uh_params = params.copy()
-    uh_params['list_action'] = (redirects.inviteAcceptedRedirect, None)
+    uh_params['list_action'] = (redirects.inviteProcessRedirect, None)
     uh_params['list_description'] = ugettext_lazy(
-        "An overview of your unhandled requests.")
+        "An overview of your unhandled invites.")
 
     uh_list = helper.lists.getListContent(
         request, uh_params, filter, 0)
 
     # construct the Open Requests list
 
-    # only select the requests for the user
+    # only select the requests from the user
     # that haven't been accepted by an admin yet
     filter = {'link_id' : user_entity.link_id,
-              'group_accepted' : False}
+              'group_accepted' : False,
+              'completed' : False}
 
     ar_params = params.copy()
     ar_params['list_description'] = ugettext_lazy(
@@ -174,32 +213,20 @@
     # call the _list method from base to display the list
     return self._list(request, params, contents, page_name)
 
-  def _editSeed(self, request, seed):
-    """See base.View._editGet().
-    """
-
-    # fill in the email field with the data from the entity
-    seed['user'] = seed['link_id']
-    seed['group'] = seed['scope_path']
-
-  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.link_id
-    form.fields['group'].initial = entity.scope_path
-
-    super(View, self)._editGet(request, entity, form)
 
   def _editPost(self, request, entity, fields):
     """See base.View._editPost().
     """
 
-    # fill in the account field with the user created from email
-    fields['link_id'] = fields['requester'].link_id
-    fields['scope_path'] = fields['to'].link_id
-    fields['scope'] = fields['to']
+    # TODO(ljvderijk) remove this once host has been rewritten
+    callback, args, kwargs = urlresolvers.resolve(request.path)
+
+    # fill in the fields via kwargs
+    fields['link_id'] = kwargs['link_id']
+    fields['scope_path'] = kwargs['scope_path']
+    fields['role'] = kwargs['role']
+    fields['role_verbose'] = 'Some Role'
+    fields['group_accepted'] = True
 
     super(View, self)._editPost(request, entity, fields)
 
@@ -211,6 +238,7 @@
 delete = view.delete
 list = view.list
 list_self = view.listSelf
+processInvite = view.processInvite
 public = view.public
 export = view.export