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