app/soc/views/models/base.py
changeset 363 d35ffa6ca643
child 373 dcd7013ae0d5
equal deleted inserted replaced
362:d904f4a76f6f 363:d35ffa6ca643
       
     1 #!/usr/bin/python2.5
       
     2 #
       
     3 # Copyright 2008 the Melange authors.
       
     4 #
       
     5 # Licensed under the Apache License, Version 2.0 (the "License");
       
     6 # you may not use this file except in compliance with the License.
       
     7 # You may obtain a copy of the License at
       
     8 # 
       
     9 #   http://www.apache.org/licenses/LICENSE-2.0
       
    10 # 
       
    11 # Unless required by applicable law or agreed to in writing, software
       
    12 # distributed under the License is distributed on an "AS IS" BASIS,
       
    13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    14 # See the License for the specific language governing permissions and
       
    15 # limitations under the License.
       
    16 
       
    17 """Helpers functions for displaying views.
       
    18 """
       
    19 
       
    20 __authors__ = [
       
    21   '"Todd Larsen" <tlarsen@google.com>',
       
    22   '"Sverre Rabbelier" <sverer@rabbelier.nl>',
       
    23   '"Pawel Solyga" <pawel.solyga@gmail.com>',
       
    24   ]
       
    25 
       
    26 
       
    27 from django import http
       
    28 from django.utils.translation import ugettext_lazy
       
    29 
       
    30 import soc.logic
       
    31 import soc.logic.out_of_band
       
    32 import soc.views.helper.lists
       
    33 import soc.views.helper.responses
       
    34 import soc.views.out_of_band
       
    35 
       
    36 from soc.logic import models
       
    37 from soc.logic import validate
       
    38 from soc.views import simple
       
    39 from soc.views import helper
       
    40 from soc.views.helper import access
       
    41 
       
    42 
       
    43 class View:
       
    44   """Views for entity classes.
       
    45 
       
    46   The View class functions specific to Entity classes by relying
       
    47   on the the child-classes to define the following fields:
       
    48 
       
    49   self._logic: the logic singleton for this entity
       
    50 
       
    51   Args:
       
    52     rights: This dictionary should be filled with the access check
       
    53             functions that should be called
       
    54 
       
    55     params: This dictionary should be filled with the parameters
       
    56             specific to this entity.
       
    57   """
       
    58 
       
    59   def __init__(self, params=None, rights=None):
       
    60     """
       
    61     """
       
    62 
       
    63     new_rights = {}
       
    64     new_rights['base'] = [access.checkIsLoggedIn]
       
    65 
       
    66     self._rights = soc.logic.dicts.mergeDicts(rights, new_rights)
       
    67     self._params = params
       
    68 
       
    69     self.DEF_SUBMIT_MSG_PARAM_NAME = 's'
       
    70 
       
    71     self.DEF_CREATE_NEW_ENTITY_MSG = ugettext_lazy(
       
    72         ' You can create a new %(model_type)s by visiting'
       
    73         ' <a href="%(create)s">Create '
       
    74         'a New %(Type)s</a> page.')
       
    75 
       
    76   def public(self, request, **kwargs):
       
    77     """Displays the public page for the entity specified by **kwargs
       
    78 
       
    79     Args:
       
    80       request: the Django request object
       
    81       kwargs: the Key Fields for the specified entity
       
    82     """
       
    83 
       
    84     try:
       
    85       self.checkAccess('edit', request)
       
    86     except soc.views.out_of_band.AccessViolationResponse, alt_response:
       
    87       return alt_response.response()
       
    88 
       
    89     # create default template context for use with any templates
       
    90     context = helper.responses.getUniversalContext(request)
       
    91     entity = None
       
    92 
       
    93     try:
       
    94       entity = self._logic.getIfFields(**kwargs)
       
    95     except soc.logic.out_of_band.ErrorResponse, error:
       
    96       template = soc._params['public_template']
       
    97       return simple.errorResponse(request, error, template, context)
       
    98 
       
    99     if not entity:
       
   100       #TODO: Change this into a proper redirect
       
   101       return http.HttpResponseRedirect('/')
       
   102 
       
   103     self._makePublic(entity)
       
   104 
       
   105     context['entity'] = entity
       
   106     context['entity_type'] = self._params['name']
       
   107 
       
   108     template = self._params['public_template']
       
   109 
       
   110     return helper.responses.respond(request, template, context)
       
   111 
       
   112   def create(self, request, **kwargs):
       
   113     """Displays the create page for this entity type
       
   114 
       
   115     request: the django request object
       
   116     kwargs: not used
       
   117     """
       
   118 
       
   119     # Create page is an edit page with no key fields
       
   120     kwargs = {}
       
   121     return self.edit(request, **kwargs)
       
   122 
       
   123   def edit(self, request, **kwargs):
       
   124     """Displays the public page for the entity specified by **kwargs
       
   125 
       
   126     Args:
       
   127       request: The Django request object
       
   128       kwargs: The Key Fields for the specified entity
       
   129     """
       
   130 
       
   131     try:
       
   132       self.checkAccess('edit', request)
       
   133     except soc.views.out_of_band.AccessViolationResponse, alt_response:
       
   134       return alt_response.response()
       
   135 
       
   136     entity = None
       
   137 
       
   138     try:
       
   139       entity = self._logic.getIfFields(**kwargs)
       
   140     except soc.logic.out_of_band.ErrorResponse, error:
       
   141       template = soc._params['public_template']
       
   142       error.message = error.message + self.DEF_CREATE_NEW_ENTITY_MSG % {
       
   143           'entity_type_lower' : self._params['name_lower'],
       
   144           'entity_type_upper' : self._parmas['name_upper'],
       
   145           'create' : self._redirects['create']
       
   146           }
       
   147       return simple.errorResponse(request, error, template, context)
       
   148 
       
   149     if request.method == 'POST':
       
   150       return self.editPost(request, entity)
       
   151     else:
       
   152       return self.editGet(request, entity)
       
   153 
       
   154   def editPost(self, request, entity):
       
   155     """Same as edit, but on POST
       
   156     """
       
   157 
       
   158     context = helper.responses.getUniversalContext(request)
       
   159 
       
   160     if entity:
       
   161       form = self._params['edit_form'](request.POST)
       
   162     else:
       
   163       form = self._params['create_form'](request.POST)
       
   164 
       
   165     if not form.is_valid():
       
   166       return
       
   167 
       
   168     fields = self.collectCleanedFields(form)
       
   169 
       
   170     self._editPost(request, entity, fields)
       
   171 
       
   172     keys = self._logic.extractKeyFields(fields)
       
   173     entity = self._logic.updateOrCreateFromFields(fields, **keys)
       
   174 
       
   175     if not entity:
       
   176       return http.HttpResponseRedirect('/')
       
   177 
       
   178     params=self._params['edit_params']
       
   179     #TODO(SRabbelier) Construct a suffix
       
   180     suffix = None
       
   181 
       
   182     # redirect to (possibly new) location of the entity
       
   183     # (causes 'Profile saved' message to be displayed)
       
   184     return helper.responses.redirectToChangedSuffix(
       
   185         request, None, suffix,
       
   186         params=params)
       
   187 
       
   188   def editGet(self, request, entity):
       
   189     """Same as edit, but on GET
       
   190     """
       
   191 
       
   192     context = helper.responses.getUniversalContext(request)
       
   193     #TODO(SRabbelier) Construct a suffix
       
   194     suffix = None
       
   195     is_self_referrer = helper.requests.isReferrerSelf(request, suffix=suffix)
       
   196 
       
   197     # Remove the params from the request, this is relevant only if
       
   198     # someone bookmarked a POST page.
       
   199     if request.GET.get(self.DEF_SUBMIT_MSG_PARAM_NAME):
       
   200       if (not entity) or (not is_self_referrer):
       
   201         return http.HttpResponseRedirect(request.path)
       
   202 
       
   203     if entity:
       
   204       # Note: no message will be displayed if parameter is not present
       
   205       context['notice'] = helper.requests.getSingleIndexedParamValue(
       
   206               request,
       
   207               self.DEF_SUBMIT_MSG_PARAM_NAME,
       
   208               values=self._params['save_message'])
       
   209 
       
   210       # populate form with the existing entity
       
   211       form = self._params['edit_form'](instance=entity)
       
   212     else:
       
   213       form = self._params['create_form']()
       
   214 
       
   215     context['form'] = form
       
   216     context['entity'] = entity
       
   217     context['entity_type'] = self._params['name']
       
   218     context['entity_type_plural'] = self._params['name_plural']
       
   219 
       
   220     template = self._params['create_template']
       
   221 
       
   222     return helper.responses.respond(request, template, context)
       
   223 
       
   224   def list(self, request):
       
   225     """Displays the list page for the entity type
       
   226     """
       
   227 
       
   228     try:
       
   229       self.checkAccess('list', request)
       
   230     except soc.views.out_of_band.AccessViolationResponse, alt_response:
       
   231       return alt_response.response()
       
   232 
       
   233     context = helper.responses.getUniversalContext(request)
       
   234 
       
   235     offset, limit = helper.lists.cleanListParameters(
       
   236       offset=request.GET.get('offset'), limit=request.GET.get('limit'))
       
   237 
       
   238     # Fetch one more to see if there should be a 'next' link
       
   239     entities = self._logic.getForLimitAndOffset(limit + 1, offset=offset)
       
   240 
       
   241     context['pagination_form'] = helper.lists.makePaginationForm(request, limit)
       
   242 
       
   243     templates = self._params['lists_template']
       
   244 
       
   245     context = helper.lists.setList(request, context, entities, 
       
   246                                  offset, limit, templates)
       
   247 
       
   248     context['entity_type'] = self._params['name']
       
   249     context['entity_type_plural'] = self._params['name_plural']
       
   250 
       
   251     template = self._params['list_template']
       
   252 
       
   253     return helper.responses.respond(request, template, context)
       
   254 
       
   255   def delete(self, request, **kwargs):
       
   256     """Shows the delete page for the entity specified by kwargs
       
   257 
       
   258     Args:
       
   259       request: The Django request object
       
   260       kwargs: The Key Fields for the specified entity
       
   261     """
       
   262 
       
   263     try:
       
   264       self.checkAccess('delete', request)
       
   265     except soc.views.out_of_band.AccessViolationResponse, alt_response:
       
   266       return alt_response.response()
       
   267 
       
   268     # create default template context for use with any templates
       
   269     context = helper.responses.getUniversalContext(request)
       
   270     entity = None
       
   271 
       
   272     try:
       
   273       entity = models.sponsor.logic.getIfFields(**kwargs)
       
   274     except soc.logic.out_of_band.ErrorResponse, error:
       
   275       template = self._templates['create']
       
   276       error.message = error.message + DEF_CREATE_NEW_ENTITY_MSG % {
       
   277           'entity_type_lower' : self._name,
       
   278           'entity_type_upper' : self._Name,
       
   279            'create' : self._redirects['create']
       
   280            }
       
   281       return simple.errorResponse(request, error, template, context)
       
   282 
       
   283     if not entity:
       
   284       #TODO: Create a proper error page for this
       
   285       return http.HttpResponseRedirect('/')
       
   286 
       
   287     if not self._logic.isDeletable(entity):
       
   288       # TODO: Direct user to page telling them they can't delete that entity, and why
       
   289       pass
       
   290 
       
   291     self._logic.delete(entity)
       
   292     redirect = self._params['delete_redirect']
       
   293 
       
   294     return http.HttpResponseRedirect(redirect)
       
   295 
       
   296   def _editPost(self, request, entity, fields):
       
   297     """Performs any required processing on the entity to post its edit page
       
   298 
       
   299     Args:
       
   300       request: The django request object
       
   301       entity: the entity to make public
       
   302       fields: The new field values
       
   303     """
       
   304 
       
   305     pass
       
   306 
       
   307   def checkUnspecified(self, access_type, request):
       
   308     """Checks whether an unspecified access_type should be allowed
       
   309 
       
   310     Args:
       
   311       access_type: the access type (such as 'list' or 'edit') that was
       
   312                    not present in the _rights dictionary when checking.
       
   313     """
       
   314 
       
   315     pass
       
   316 
       
   317   def checkAccess(self, access_type, request):
       
   318     """Runs all the defined checks for the specified type
       
   319 
       
   320     Args:
       
   321       access_type: the type of request (such as 'list' or 'edit')
       
   322       request: the Django request object
       
   323 
       
   324     Returns:
       
   325       True: If all the required access checks have been made successfully
       
   326       False: If a check failed, in this case self._response will contain
       
   327              the response provided by the failed access check.
       
   328     """
       
   329 
       
   330     if access_type not in self._rights:
       
   331       self.checkUnspecified(access_type, request)
       
   332       return
       
   333 
       
   334     # Call each access checker
       
   335     for check in self._rights['base']:
       
   336       check(request)
       
   337 
       
   338     for check in self._rights[access_type]:
       
   339       check(request)
       
   340 
       
   341     # All checks were successfull
       
   342     return
       
   343 
       
   344   def collectCleanedFields(self, form):
       
   345     """Collects all cleaned fields from form and returns them 
       
   346 
       
   347     Args:
       
   348       form: The form from which the cleaned fields should be collected
       
   349     """
       
   350 
       
   351     fields = {}
       
   352 
       
   353     for field, value in form.cleaned_data.iteritems():
       
   354       fields[field] = value
       
   355 
       
   356     return fields