app/soc/views/site/docs/edit.py
changeset 377 d94ec6f104cc
parent 376 ce8b3a9fa0de
child 378 51c41e8bd45f
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 """Developer 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="/site/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_SITE_DOCS_CREATE_TMPL = 'soc/site/docs/edit.html'
       
   111 
       
   112 @decorators.view
       
   113 def create(request, page=None, template=DEF_SITE_DOCS_CREATE_TMPL):
       
   114   """View for a Developer 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     return alt_response.response()
       
   132 
       
   133   # create default template context for use with any templates
       
   134   context = helper.responses.getUniversalContext(request)
       
   135   context['page'] = page
       
   136 
       
   137   if request.method == 'POST':
       
   138     form = CreateForm(request.POST)
       
   139 
       
   140     if form.is_valid():
       
   141       doc = getDocForForm(form)
       
   142 
       
   143       if not doc:
       
   144         return http.HttpResponseRedirect('/')
       
   145 
       
   146       new_path = path_link_name.combinePath([doc.partial_path, doc.link_name])
       
   147 
       
   148       # redirect to new /site/docs/edit/new_path?s=0
       
   149       # (causes 'Profile saved' message to be displayed)
       
   150       return helper.responses.redirectToChangedSuffix(
       
   151           request, None, new_path,
       
   152           params=profile.SUBMIT_PROFILE_SAVED_PARAMS)
       
   153   else: # method == 'GET':
       
   154     # no link name specified, so start with an empty form
       
   155     form = CreateForm()
       
   156 
       
   157   context['form'] = form
       
   158 
       
   159   return helper.responses.respond(request, template, context)
       
   160 
       
   161 
       
   162 DEF_SITE_DOCS_EDIT_TMPL = 'soc/site/docs/edit.html'
       
   163 
       
   164 class EditForm(CreateForm):
       
   165   """Django form displayed when Developer edits a Document.
       
   166   """
       
   167   doc_key_name = forms.fields.CharField(widget=forms.HiddenInput)
       
   168   created_by = forms.fields.CharField(widget=helper.widgets.ReadOnlyInput(),
       
   169                                       required=False)
       
   170 
       
   171 
       
   172 @decorators.view
       
   173 def edit(request, page=None, partial_path=None, link_name=None,
       
   174          template=DEF_SITE_DOCS_EDIT_TMPL):
       
   175   """View for a Developer to modify the properties of a Document Model entity.
       
   176 
       
   177   Args:
       
   178     request: the standard django request object
       
   179     page: a soc.logic.site.page.Page object which is abstraction that combines 
       
   180       a Django view with sidebar menu info
       
   181     partial_path: the Document's site-unique "path" extracted from the URL,
       
   182       minus the trailing link_name
       
   183     link_name: the last portion of the Document's site-unique "path"
       
   184       extracted from the URL
       
   185     template: the "sibling" template (or a search list of such templates)
       
   186       from which to construct the public.html template name (or names)
       
   187 
       
   188   Returns:
       
   189     A subclass of django.http.HttpResponse which either contains the form to
       
   190     be filled out, or a redirect to the correct view in the interface.
       
   191   """
       
   192 
       
   193   try:
       
   194     access.checkIsDeveloper(request)
       
   195   except  soc.views.out_of_band.AccessViolationResponse, alt_response:
       
   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 /site/docs/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                   'existing_doc': 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_SITE_DOCS_EDIT_TMPL):
       
   282   """Request handler for a Developer 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/docs/list.
       
   298   """
       
   299 
       
   300   try:
       
   301     access.checkIsDeveloper(request)
       
   302   except  soc.views.out_of_band.AccessViolationResponse, alt_response:
       
   303     return alt_response.response()
       
   304 
       
   305   # create default template context for use with any templates
       
   306   context = helper.responses.getUniversalContext(request)
       
   307 
       
   308   existing_doc = None
       
   309   path = path_link_name.combinePath([partial_path, link_name])
       
   310 
       
   311   # try to fetch Document entity corresponding to path if one exists    
       
   312   try:
       
   313     if path:
       
   314       existing_doc = document.logic.getFromFields(partial_path=partial_path,
       
   315                                                   link_name=link_name)
       
   316   except out_of_band.ErrorResponse, error:
       
   317     # show custom 404 page when path doesn't exist in Datastore
       
   318     error.message = error.message + DEF_CREATE_NEW_DOC_MSG
       
   319     return simple.errorResponse(request, page, error, template, context)
       
   320 
       
   321   if existing_doc:
       
   322     document.logic.delete(existing_doc)
       
   323 
       
   324   return http.HttpResponseRedirect('/site/docs/list')