Browse Source

[core] Error views to handle non-ascii error messages

* Mako rendering should return a unicode string, so that its output
  can be safely included by another Mako template.
bc Wong 13 năm trước cách đây
mục cha
commit
a7345191ce

+ 5 - 5
desktop/core/src/desktop/lib/django_mako.py

@@ -18,14 +18,13 @@
 # Adapted from http://code.google.com/p/django-mako/source/browse/trunk/djangomako/shortcuts.py
 
 from django.http import HttpResponse
-from desktop.lib import apputil
+from desktop.lib import apputil, i18n
 import os
 import tempfile
 from mako.lookup import TemplateLookup, TemplateCollection
 import django.template
 
-ENCODING_ERRORS  = 'replace'
-OUTPUT_ENCODING = 'utf-8'
+ENCODING_ERRORS = 'replace'
 
 # Things to automatically import into all template namespaces
 IMPORTS=[
@@ -61,7 +60,8 @@ class DesktopLookup(TemplateCollection):
 
     loader = TemplateLookup(directories=[app_template_dir, self.desktop_template_dir],
                             module_directory=os.path.join(self.module_dir, app),
-                            output_encoding=OUTPUT_ENCODING,
+                            output_encoding=i18n.get_site_encoding(),
+                            input_encoding=i18n.get_site_encoding(),
                             encoding_errors=ENCODING_ERRORS,
                             default_filters=['unicode'], 
                             imports=IMPORTS)
@@ -102,7 +102,7 @@ def render_to_string_normal(template_name, django_context):
 
   template = lookup.get_template(template_name)
   result = template.render(**data_dict)
-  return result
+  return i18n.smart_unicode(result)
 
 # This variable is overridden in test code.
 render_to_string = render_to_string_normal

+ 4 - 1
desktop/core/src/desktop/lib/django_util.py

@@ -200,7 +200,10 @@ def render(template, request, data, json=None, template_lib=None, force_template
   if force-template is True, will render the non-AJAX template response even if the
   request is via AJAX. This is to facilitate fetching HTML fragments.
   """
-  if not force_template and not is_jframe_request(request) and (request.ajax or template is None):
+  # request.ajax is defined in the AjaxMiddleware. But we might hit
+  # errors before getting to that point.
+  is_ajax = getattr(request, "ajax", False)
+  if not force_template and not is_jframe_request(request) and (is_ajax or template is None):
     if json is not None:
       return render_json(json, request.GET.get("callback"))
     else:

+ 3 - 1
desktop/core/src/desktop/middleware.py

@@ -64,7 +64,9 @@ class ExceptionMiddleware(object):
   """
   def process_exception(self, request, exception):
     import traceback
-    logging.info("Processing exception: %s: %s" % (exception, traceback.format_exc()))
+    tb = traceback.format_exc()
+    logging.info("Processing exception: %s: %s" % (i18n.smart_unicode(exception),
+                                                   i18n.smart_unicode(tb)))
 
     if hasattr(exception, "response"):
       return exception.response(request)

+ 5 - 1
desktop/core/src/desktop/templates/common_header.mako

@@ -13,11 +13,15 @@
 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ## See the License for the specific language governing permissions and
 ## limitations under the License.
+<%!
+from desktop.lib.i18n import smart_unicode
+%>
+
 <!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="utf-8">
-  <title>${title}</title>
+  <title>${smart_unicode(title) | h}</title>
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta name="description" content="">
   <meta name="author" content="">

+ 3 - 2
desktop/core/src/desktop/templates/error.mako

@@ -15,16 +15,17 @@
 ## limitations under the License.
 <%!
 from desktop.views import commonheader, commonfooter
+from desktop.lib.i18n import smart_unicode
 %>
 ${commonheader("Error", "")}
 
   <div class="container-fluid">
     <h1>Error!</h1>
-    <pre>${error | h }</pre>
+    <pre>${smart_unicode(error) | h }</pre>
 
     %if traceback:
       <textarea style="width: 100%;" rows=80 readonly="readonly">
-      ${traceback | h}
+      ${smart_unicode(traceback) | h}
       </textarea>
     %endif
   </div>

+ 4 - 3
desktop/core/src/desktop/templates/popup_error.mako

@@ -15,6 +15,7 @@
 ## limitations under the License.
 <%!
 from desktop.views import commonheader, commonfooter
+from desktop.lib.i18n import smart_unicode
 %>
 
 ${commonheader(title, "", "60px")}
@@ -22,10 +23,10 @@ ${commonheader(title, "", "60px")}
 
 	<div class="container-fluid">
 		<div class="alert">
-			<p><strong>${message}</strong></p>
+			<p><strong>${smart_unicode(message) | h}</strong></p>
 
 			% if detail:
-			<p>${detail or ""}</p>
+			<p>${smart_unicode(detail) or ""}</p>
 			% endif
 
 			<div class="alert-actions">
@@ -37,4 +38,4 @@ ${commonheader(title, "", "60px")}
 
 	</div>
 
-${commonfooter()}
+${commonfooter()}

+ 17 - 6
desktop/core/src/desktop/tests.py

@@ -29,7 +29,7 @@ import desktop.urls
 import desktop.conf
 import logging
 import time
-from desktop.lib.django_util import TruncatingModel
+from desktop.lib.django_util import TruncatingModel, PopupException
 import desktop.views as views
 
 def setup_test_environment():
@@ -212,17 +212,22 @@ def test_truncating_model():
   assert_true(a.non_string_field == 10**10, 'non-string fields are not truncated')
 
 
-def test_500_handling():
+def test_error_handling():
   restore_django_debug = desktop.conf.DJANGO_DEBUG_MODE.set_for_testing(False)
   restore_500_debug = desktop.conf.HTTP_500_DEBUG_MODE.set_for_testing(False)
 
-  exc_msg = "error_raising_view: Test 500 handling"
+  exc_msg = "error_raising_view: Test earráid handling"
   def error_raising_view(request, *args, **kwargs):
     raise Exception(exc_msg)
 
+  def popup_exception_view(request, *args, **kwargs):
+    raise PopupException(exc_msg, title="earráid", detail=exc_msg)
+
   # Add an error view
-  error_url_pat = patterns('', url('^500_internal_error$', error_raising_view))[0]
-  desktop.urls.urlpatterns.append(error_url_pat)
+  error_url_pat = patterns('',
+                           url('^500_internal_error$', error_raising_view),
+                           url('^popup_exception$', popup_exception_view))
+  desktop.urls.urlpatterns.extend(error_url_pat)
   try:
     def store_exc_info(*args, **kwargs):
       pass
@@ -240,9 +245,15 @@ def test_500_handling():
     response = c.get('/500_internal_error')
     assert_equal(response.template.name, 'Technical 500 template')
     assert_true(exc_msg in response.content)
+
+    # PopupException
+    response = c.get('/popup_exception')
+    assert_true('popup_error.mako' in response.template)
+    assert_true(exc_msg in response.content)
   finally:
     # Restore the world
-    desktop.urls.urlpatterns.remove(error_url_pat)
+    for i in error_url_pat:
+      desktop.urls.urlpatterns.remove(i)
     restore_django_debug()
     restore_500_debug()