app/soc/views/document/edit.py
changeset 477 8a8b1bd035c4
parent 448 075360be6743
child 482 839740b061ad
equal deleted inserted replaced
476:3b0662786f95 477:8a8b1bd035c4
       
     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 """Views for editing and examining Documents.
       
    18 """
       
    19 
       
    20 __authors__ = [
       
    21   '"Todd Larsen" <tlarsen@google.com>',
       
    22   ]
       
    23 
       
    24 
       
    25 from google.appengine.api import users
       
    26 
       
    27 from django import forms
       
    28 from django import http
       
    29 from django.utils.translation import ugettext_lazy
       
    30 
       
    31 from soc.logic import models
       
    32 from soc.logic import out_of_band
       
    33 from soc.logic import path_link_name
       
    34 from soc.logic.models import document
       
    35 
       
    36 from soc.views import helper
       
    37 from soc.views import simple
       
    38 from soc.views.helper import access
       
    39 from soc.views.helper import decorators
       
    40 from soc.views.user import profile
       
    41 
       
    42 import soc.models.document
       
    43 import soc.views.helper.forms
       
    44 import soc.views.helper.requests
       
    45 import soc.views.helper.responses
       
    46 import soc.views.helper.widgets
       
    47 import soc.views.out_of_band
       
    48 
       
    49 
       
    50 DEF_CREATE_NEW_DOC_MSG = ' You can create a new document by visiting the' \
       
    51                          ' <a href="/document/edit">Create ' \
       
    52                          'a New Document</a> page.'
       
    53 
       
    54 SUBMIT_MESSAGES = (
       
    55  ugettext_lazy('Document saved.'),
       
    56 )
       
    57 
       
    58 
       
    59 def getDocForForm(form):
       
    60   """Extracts doc fields from a form and creates a new doc from it
       
    61   """
       
    62 
       
    63   user = users.get_current_user()
       
    64 
       
    65   partial_path = form.cleaned_data.get('partial_path')
       
    66   link_name = form.cleaned_data.get('link_name')
       
    67 
       
    68   properties = {}
       
    69   properties['partial_path'] = partial_path
       
    70   properties['link_name'] = link_name
       
    71   properties['title'] = form.cleaned_data.get('title')
       
    72   properties['short_name'] = form.cleaned_data.get('short_name')
       
    73   properties['content'] = form.cleaned_data.get('content')
       
    74   properties['author'] = models.user.logic.getForFields({'id': user}, unique=True)
       
    75   properties['is_featured'] = form.cleaned_data.get('is_featured')
       
    76 
       
    77   key_fields = document.logic.getKeyFieldsFromDict(properties)
       
    78 
       
    79   doc = document.logic.updateOrCreateFromFields(properties, key_fields)
       
    80   return doc
       
    81 
       
    82 
       
    83 class CreateForm(helper.forms.BaseForm):
       
    84   """Django form displayed when Developer creates a Document.
       
    85   """
       
    86   content = forms.fields.CharField(widget=helper.widgets.TinyMCE(
       
    87       attrs={'rows':10, 'cols':40}))
       
    88 
       
    89   class Meta:
       
    90     model = soc.models.document.Document
       
    91 
       
    92     #: list of model fields which will *not* be gathered by the form
       
    93     exclude = ['inheritance_line', 'author', 'created', 'modified']
       
    94 
       
    95   def clean_partial_path(self):
       
    96     partial_path = self.cleaned_data.get('partial_path')
       
    97     # TODO(tlarsen): combine path and link_name and check for uniqueness
       
    98     return partial_path
       
    99 
       
   100   def clean_link_name(self):
       
   101     link_name = self.cleaned_data.get('link_name')
       
   102     # TODO(tlarsen): combine path and link_name and check for uniqueness
       
   103     return link_name
       
   104 
       
   105 
       
   106 DEF_DOCS_CREATE_TMPL = 'soc/models/edit.html'
       
   107 
       
   108 @decorators.view
       
   109 def create(request, page=None, template=DEF_DOCS_CREATE_TMPL):
       
   110   """View to create a new Document entity.
       
   111 
       
   112   Args:
       
   113     request: the standard django request object
       
   114     page: a soc.logic.site.page.Page object which is abstraction that combines 
       
   115       a Django view with sidebar menu info
       
   116     template: the "sibling" template (or a search list of such templates)
       
   117       from which to construct the public.html template name (or names)
       
   118 
       
   119   Returns:
       
   120     A subclass of django.http.HttpResponse which either contains the form to
       
   121     be filled out, or a redirect to the correct view in the interface.
       
   122   """
       
   123 
       
   124   try:
       
   125     access.checkIsDeveloper(request)
       
   126   except  soc.views.out_of_band.AccessViolationResponse, alt_response:
       
   127     # TODO(tlarsen): change this to just limit the Documents that can be
       
   128     #   created by the User in their current Role
       
   129     return alt_response.response()
       
   130 
       
   131   # create default template context for use with any templates
       
   132   context = helper.responses.getUniversalContext(request)
       
   133   context['page'] = page
       
   134 
       
   135   if request.method == 'POST':
       
   136     form = CreateForm(request.POST)
       
   137 
       
   138     if form.is_valid():
       
   139       doc = getDocForForm(form)
       
   140 
       
   141       if not doc:
       
   142         return http.HttpResponseRedirect('/')
       
   143 
       
   144       new_path = path_link_name.combinePath([doc.partial_path, doc.link_name])
       
   145 
       
   146       # redirect to new /document/edit/new_path?s=0
       
   147       # (causes 'Profile saved' message to be displayed)
       
   148       return helper.responses.redirectToChangedSuffix(
       
   149           request, None, new_path,
       
   150           params=profile.SUBMIT_PROFILE_SAVED_PARAMS)
       
   151   else: # method == 'GET':
       
   152     # no link name specified, so start with an empty form
       
   153     form = CreateForm()
       
   154 
       
   155   context['form'] = form
       
   156 
       
   157   return helper.responses.respond(request, template, context)
       
   158 
       
   159 
       
   160 DEF_DOCS_EDIT_TMPL = 'soc/models/edit.html'
       
   161 
       
   162 class EditForm(CreateForm):
       
   163   """Django form displayed a Document is edited.
       
   164   """
       
   165   doc_key_name = forms.fields.CharField(widget=forms.HiddenInput)
       
   166   created_by = forms.fields.CharField(widget=helper.widgets.ReadOnlyInput(),
       
   167                                       required=False)
       
   168 
       
   169 
       
   170 @decorators.view
       
   171 def edit(request, page=None, partial_path=None, link_name=None,
       
   172          template=DEF_DOCS_EDIT_TMPL):
       
   173   """View to modify the properties of a Document Model entity.
       
   174 
       
   175   Args:
       
   176     request: the standard django request object
       
   177     page: a soc.logic.site.page.Page object which is abstraction that combines 
       
   178       a Django view with sidebar menu info
       
   179     partial_path: the Document's site-unique "path" extracted from the URL,
       
   180       minus the trailing link_name
       
   181     link_name: the last portion of the Document's site-unique "path"
       
   182       extracted from the URL
       
   183     template: the "sibling" template (or a search list of such templates)
       
   184       from which to construct the public.html template name (or names)
       
   185 
       
   186   Returns:
       
   187     A subclass of django.http.HttpResponse which either contains the form to
       
   188     be filled out, or a redirect to the correct view in the interface.
       
   189   """
       
   190 
       
   191   try:
       
   192     access.checkIsDeveloper(request)
       
   193   except  soc.views.out_of_band.AccessViolationResponse, alt_response:
       
   194     # TODO(tlarsen): change this to just limit the Documents that can be
       
   195     #   edited by the User in their current Role
       
   196     return alt_response.response()
       
   197 
       
   198   # create default template context for use with any templates
       
   199   context = helper.responses.getUniversalContext(request)
       
   200   context['page'] = page
       
   201 
       
   202   doc = None  # assume that no Document entity will be found
       
   203 
       
   204   path = path_link_name.combinePath([partial_path, link_name])
       
   205 
       
   206   # try to fetch Document entity corresponding to path if one exists    
       
   207   try:
       
   208     if path:
       
   209       doc = document.logic.getFromFields(partial_path=partial_path,
       
   210                                          link_name=link_name)
       
   211   except out_of_band.ErrorResponse, error:
       
   212     # show custom 404 page when path doesn't exist in Datastore
       
   213     error.message = error.message + DEF_CREATE_NEW_DOC_MSG
       
   214     return simple.errorResponse(request, page, error, template, context)
       
   215 
       
   216   if request.method == 'POST':
       
   217     form = EditForm(request.POST)
       
   218 
       
   219     if form.is_valid():
       
   220       doc = getDocForForm(form)
       
   221       
       
   222       if not doc:
       
   223         return http.HttpResponseRedirect('/')
       
   224 
       
   225       new_path = path_link_name.combinePath([doc.partial_path, doc.link_name])
       
   226         
       
   227       # redirect to new /document/edit/new_path?s=0
       
   228       # (causes 'Profile saved' message to be displayed)
       
   229       return helper.responses.redirectToChangedSuffix(
       
   230           request, path, new_path,
       
   231           params=profile.SUBMIT_PROFILE_SAVED_PARAMS)
       
   232   else: # method == 'GET':
       
   233     # try to fetch Document entity corresponding to path if one exists
       
   234     if path:
       
   235       if doc:
       
   236         # is 'Profile saved' parameter present, but referrer was not ourself?
       
   237         # (e.g. someone bookmarked the GET that followed the POST submit) 
       
   238         if (request.GET.get(profile.SUBMIT_MSG_PARAM_NAME)
       
   239             and (not helper.requests.isReferrerSelf(request, suffix=path))):
       
   240           # redirect to aggressively remove 'Profile saved' query parameter
       
   241           return http.HttpResponseRedirect(request.path)
       
   242     
       
   243         # referrer was us, so select which submit message to display
       
   244         # (may display no message if ?s=0 parameter is not present)
       
   245         context['notice'] = (
       
   246             helper.requests.getSingleIndexedParamValue(
       
   247                 request, profile.SUBMIT_MSG_PARAM_NAME,
       
   248                 values=SUBMIT_MESSAGES))
       
   249 
       
   250         # populate form with the existing Document entity
       
   251         author_link_name = doc.author.link_name
       
   252         form = EditForm(initial={'doc_key_name': doc.key().name(),
       
   253             'title': doc.title, 'partial_path': doc.partial_path,
       
   254             'link_name': doc.link_name, 'short_name': doc.short_name,
       
   255             'content': doc.content, 'author': doc.author,
       
   256             'is_featured': doc.is_featured, 'created_by': author_link_name})
       
   257       else:
       
   258         if request.GET.get(profile.SUBMIT_MSG_PARAM_NAME):
       
   259           # redirect to aggressively remove 'Profile saved' query parameter
       
   260           return http.HttpResponseRedirect(request.path)
       
   261           
       
   262         context['lookup_error'] = ugettext_lazy(
       
   263             'Document with that path not found.')
       
   264         form = EditForm(initial={'link_name': link_name})
       
   265     else:  # no link name specified in the URL
       
   266       if request.GET.get(profile.SUBMIT_MSG_PARAM_NAME):
       
   267         # redirect to aggressively remove 'Profile saved' query parameter
       
   268         return http.HttpResponseRedirect(request.path)
       
   269 
       
   270       # no link name specified, so start with an empty form
       
   271       form = EditForm()
       
   272 
       
   273   context.update({'form': form,
       
   274                   'entity': doc})
       
   275 
       
   276   return helper.responses.respond(request, template, context)
       
   277 
       
   278 
       
   279 @decorators.view
       
   280 def delete(request, page=None, partial_path=None, link_name=None,
       
   281            template=DEF_DOCS_EDIT_TMPL):
       
   282   """Request handler to delete Document Model entity.
       
   283 
       
   284   Args:
       
   285     request: the standard django request object
       
   286     page: a soc.logic.site.page.Page object which is abstraction that combines 
       
   287       a Django view with sidebar menu info
       
   288     partial_path: the Document's site-unique "path" extracted from the URL,
       
   289       minus the trailing link_name
       
   290     link_name: the last portion of the Document's site-unique "path"
       
   291       extracted from the URL
       
   292     template: the "sibling" template (or a search list of such templates)
       
   293       from which to construct the public.html template name (or names)
       
   294 
       
   295   Returns:
       
   296     A subclass of django.http.HttpResponse which redirects 
       
   297     to /site/document/list.
       
   298   """
       
   299 
       
   300   try:
       
   301     access.checkIsDeveloper(request)
       
   302   except  soc.views.out_of_band.AccessViolationResponse, alt_response:
       
   303     # TODO(tlarsen): change this to just limit the Documents that can be
       
   304     #   deleted by the User in their current Role
       
   305     return alt_response.response()
       
   306 
       
   307   # create default template context for use with any templates
       
   308   context = helper.responses.getUniversalContext(request)
       
   309   context['page'] = page
       
   310 
       
   311   existing_doc = None
       
   312   path = path_link_name.combinePath([partial_path, link_name])
       
   313 
       
   314   # try to fetch Document entity corresponding to path if one exists    
       
   315   try:
       
   316     if path:
       
   317       existing_doc = document.logic.getFromFields(partial_path=partial_path,
       
   318                                                   link_name=link_name)
       
   319   except out_of_band.ErrorResponse, error:
       
   320     # show custom 404 page when path doesn't exist in Datastore
       
   321     error.message = error.message + DEF_CREATE_NEW_DOC_MSG
       
   322     return simple.errorResponse(request, page, error, template, context)
       
   323 
       
   324   if existing_doc:
       
   325     document.logic.delete(existing_doc)
       
   326 
       
   327   return http.HttpResponseRedirect('/document/list')