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