Mercurial > p > roundup > code
view roundup/cgi/engine_zopetal.py @ 5548:fea11d05110e
Avoid errors from selecting "no selection" on multilink (issue2550722).
As discussed in issue 2550722 there are various cases where selecting
"no selection" on a multilink can result in inappropriate errors from
Roundup:
* If selecting "no selection" produces a null edit (a value was set in
the multilink in an edit with an error, then removed again, along
with all other changes, in the next form submission), so the page is
rendered from the form contents including the "-<id>" value for "no
selection" for the multilink.
* If creating an item with a nonempty value for a multilink has an
error, and the resubmission changes that multilink to "no selection"
(and this in turn has subcases, according to whether the creation
then succeeds or fails on the resubmission, which need fixes in
different places in the Roundup code).
All of these cases have in common that it is expected and OK to have a
"-<id>" value for a submission for a multilink when <id> is not set in
that multilink in the database (because the original attempt to set
<id> in that multilink had an error), so the hyperdb.py logic to give
an error in that case is thus removed. In the subcase of the second
case where the resubmission with "no selection" has an error, the
templating code tries to produce a menu entry for the "-<id>"
multilink value, which also results in an error, hence the
templating.py change to ignore such values in the list for a
multilink.
| author | Joseph Myers <jsm@polyomino.org.uk> |
|---|---|
| date | Thu, 27 Sep 2018 11:33:01 +0000 |
| parents | 55f09ca366c4 |
| children | 38d04127d9bb |
line wrap: on
line source
"""Templating engine adapter for the legacy TAL implementation ported from Zope. """ __docformat__ = 'restructuredtext' import errno import mimetypes import os import os.path from roundup.cgi.templating import StringIO, context, translationService, TALLoaderBase from roundup.cgi.PageTemplates import PageTemplate, GlobalTranslationService from roundup.cgi.PageTemplates.Expressions import getEngine from roundup.cgi.TAL import TALInterpreter GlobalTranslationService.setGlobalTranslationService(translationService) class Loader(TALLoaderBase): templates = {} def __init__(self, dir): self.dir = dir def load(self, tplname): # find the source src, filename = self._find(tplname) # has it changed? try: stime = os.stat(src)[os.path.stat.ST_MTIME] except os.error as error: if error.errno != errno.ENOENT: raise if src in self.templates and \ stime <= self.templates[src].mtime: # compiled template is up to date return self.templates[src] # compile the template pt = RoundupPageTemplate() # use pt_edit so we can pass the content_type guess too content_type = mimetypes.guess_type(filename)[0] or 'text/html' pt.pt_edit(open(src).read(), content_type) pt.id = filename pt.mtime = stime # Add it to the cache. We cannot do this until the template # is fully initialized, as we could otherwise have a race # condition when running with multiple threads: # # 1. Thread A notices the template is not in the cache, # adds it, but has not yet set "mtime". # # 2. Thread B notices the template is in the cache, checks # "mtime" (above) and crashes. # # Since Python dictionary access is atomic, as long as we # insert "pt" only after it is fully initialized, we avoid # this race condition. It's possible that two separate # threads will both do the work of initializing the template, # but the risk of wasted work is offset by avoiding a lock. self.templates[src] = pt return pt class RoundupPageTemplate(PageTemplate.PageTemplate): """A Roundup-specific PageTemplate. Interrogate the client to set up Roundup-specific template variables to be available. See 'context' function for the list of variables. """ def render(self, client, classname, request, **options): """Render this Page Template""" if not self._v_cooked: self._cook() __traceback_supplement__ = (PageTemplate.PageTemplateTracebackSupplement, self) if self._v_errors: raise PageTemplate.PTRuntimeError('Page Template %s has errors.'%self.id) # figure the context c = context(client, self, classname, request) c.update({'options': options}) # and go output = StringIO() TALInterpreter.TALInterpreter(self._v_program, self.macros, getEngine().getContext(c), output, tal=1, strictinsert=0)() return output.getvalue()
