فهرست منبع

HUE-724 [core] Integrate Django messages for notifications

Adding a middleware for easing the sending of notifications
Testing it in useradmin when editing a user
Romain Rigaux 13 سال پیش
والد
کامیت
fb9491b8a0

+ 6 - 5
apps/beeswax/src/beeswax/views.py

@@ -22,6 +22,7 @@ import logging
 import re
 import re
 
 
 from django import forms
 from django import forms
+from django.contrib import messages
 from django.core import urlresolvers
 from django.core import urlresolvers
 from django.db.models import Q
 from django.db.models import Q
 from django.http import HttpResponse, QueryDict
 from django.http import HttpResponse, QueryDict
@@ -221,7 +222,7 @@ def safe_get_design(request, design_type, design_id=None):
   """
   """
   Return a new design, if design_id is None,
   Return a new design, if design_id is None,
   Return the design with the given id and type. If the design is not found,
   Return the design with the given id and type. If the design is not found,
-  flash a message and return a new design.
+  display a notification and return a new design.
   """
   """
   design = None
   design = None
 
 
@@ -229,7 +230,7 @@ def safe_get_design(request, design_type, design_id=None):
     try:
     try:
       design = models.SavedQuery.get(design_id, request.user, design_type)
       design = models.SavedQuery.get(design_id, request.user, design_type)
     except models.SavedQuery.DoesNotExist:
     except models.SavedQuery.DoesNotExist:
-      request.flash.put(_t('Design does not exist'))
+      messages.error(request, _t('Design does not exist'))
   if design is None:
   if design is None:
     design = models.SavedQuery(owner=request.user, type=design_type)
     design = models.SavedQuery(owner=request.user, type=design_type)
   return design
   return design
@@ -542,7 +543,7 @@ def save_design(request, form, type, design, explicit_save):
   LOG.info('Saved %sdesign "%s" (id %s) for %s' %
   LOG.info('Saved %sdesign "%s" (id %s) for %s' %
            (explicit_save and '' or 'auto ', design.name, design.id, design.owner))
            (explicit_save and '' or 'auto ', design.name, design.id, design.owner))
   if explicit_save:
   if explicit_save:
-    request.flash.put(_t('Saved design "%(name)s"') % {'name': design.name})
+    messages.error(request, _t('Saved design "%(name)s"') % {'name': design.name})
   # Design may now have a new/different id
   # Design may now have a new/different id
   return design
   return design
 
 
@@ -676,7 +677,7 @@ def clone_design(request, design_id):
   copy.name = design.name + ' (copy)'
   copy.name = design.name + ' (copy)'
   copy.owner = request.user
   copy.owner = request.user
   copy.save()
   copy.save()
-  request.flash.put(_t('Copied design: %(name)s') % {'name': design.name})
+  messages.error(request, _t('Copied design: %(name)s') % {'name': design.name})
   return format_preserving_redirect(
   return format_preserving_redirect(
       request, urlresolvers.reverse(execute_query, kwargs={'design_id': copy.id}))
       request, urlresolvers.reverse(execute_query, kwargs={'design_id': copy.id}))
 
 
@@ -1079,7 +1080,7 @@ def _save_results_ctas(request, query_history, target_table, result_meta):
     table_loc = request.fs.urlsplit(table_obj.sd.location)[2]
     table_loc = request.fs.urlsplit(table_obj.sd.location)[2]
     request.fs.rename_star(result_meta.table_dir, table_loc)
     request.fs.rename_star(result_meta.table_dir, table_loc)
     LOG.debug("Moved results from %s to %s" % (result_meta.table_dir, table_loc))
     LOG.debug("Moved results from %s to %s" % (result_meta.table_dir, table_loc))
-    request.flash.put(_t('Saved query results as new table %(table)s') % {'table': target_table})
+    messages.error(request, _t('Saved query results as new table %(table)s') % {'table': target_table})
     query_history.save_state(models.QueryHistory.STATE.expired)
     query_history.save_state(models.QueryHistory.STATE.expired)
   except Exception, ex:
   except Exception, ex:
     LOG.error('Error moving data into storage of table %s. Will drop table.' % (target_table,))
     LOG.error('Error moving data into storage of table %s. Will drop table.' % (target_table,))

+ 2 - 1
apps/filebrowser/src/filebrowser/views.py

@@ -29,6 +29,7 @@ import stat as stat_module
 import os
 import os
 import simplejson
 import simplejson
 
 
+from django.contrib import messages
 from django.core import urlresolvers
 from django.core import urlresolvers
 from django.http import Http404, HttpResponse, HttpResponseNotModified
 from django.http import Http404, HttpResponse, HttpResponseNotModified
 from django.views.static import was_modified_since
 from django.views.static import was_modified_since
@@ -212,7 +213,7 @@ def save_file(request):
                          form.cleaned_data['contents'],
                          form.cleaned_data['contents'],
                          form.cleaned_data['encoding'])
                          form.cleaned_data['encoding'])
 
 
-    request.flash.put(_('Saved %(path)s.') % {'path': os.path.basename(path)})
+    messages.info(request, _('Saved %(path)s.') % {'path': os.path.basename(path)})
     """ Changing path to reflect the request path of the JFrame that will actually be returned."""
     """ Changing path to reflect the request path of the JFrame that will actually be returned."""
     request.path = urlresolvers.reverse("filebrowser.views.edit", kwargs=dict(path=path))
     request.path = urlresolvers.reverse("filebrowser.views.edit", kwargs=dict(path=path))
     return edit(request, path, form)
     return edit(request, path, form)

+ 1 - 1
apps/useradmin/src/useradmin/templates/list_users.mako

@@ -153,4 +153,4 @@ ${layout.menubar(section='users', _=_)}
         });
         });
     </script>
     </script>
 
 
-${commonfooter()}
+${commonfooter(messages)}

+ 5 - 2
apps/useradmin/src/useradmin/tests.py

@@ -227,7 +227,7 @@ def test_group_admin():
                     members=[User.objects.get(username="test").pk],
                     members=[User.objects.get(username="test").pk],
                     save="Save"), follow=True)
                     save="Save"), follow=True)
   assert_true("You must be a superuser" in response.content)
   assert_true("You must be a superuser" in response.content)
- 
+
   # Should be one group left, because we created the other group
   # Should be one group left, because we created the other group
   response = c.post('/useradmin/groups/delete/testgroup')
   response = c.post('/useradmin/groups/delete/testgroup')
   assert_true(len(Group.objects.all()) == 1)
   assert_true(len(Group.objects.all()) == 1)
@@ -259,7 +259,10 @@ def test_user_admin():
                          first_name=u"Inglés",
                          first_name=u"Inglés",
                          last_name=u"Español",
                          last_name=u"Español",
                          is_superuser="True",
                          is_superuser="True",
-                         is_active="True"))
+                         is_active="True"),
+                    follow=True)
+  assert_true("User information updated" in response.content,
+              "Notification should be displayed in: %s" % response.content)
   # Now make sure that those were materialized
   # Now make sure that those were materialized
   response = c.get('/useradmin/users/edit/test')
   response = c.get('/useradmin/users/edit/test')
   assert_equal(smart_unicode("Inglés"), response.context["form"].instance.first_name)
   assert_equal(smart_unicode("Inglés"), response.context["form"].instance.first_name)

+ 6 - 5
apps/useradmin/src/useradmin/views.py

@@ -36,7 +36,7 @@ from useradmin.models import GroupPermission, HuePermission, UserProfile, LdapGr
 from useradmin.models import get_profile, get_default_user_group
 from useradmin.models import get_profile, get_default_user_group
 import ldap_access
 import ldap_access
 
 
-from django.utils.translation import ugettext_lazy as _
+from django.utils.translation import ugettext as _
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
@@ -69,7 +69,7 @@ def delete_user(request, username):
       finally:
       finally:
         __users_lock.release()
         __users_lock.release()
 
 
-      # Send a flash message saying "deleted"?
+      request.info(_('The user was deleted !'))
       return redirect(reverse(list_users))
       return redirect(reverse(list_users))
     except User.DoesNotExist:
     except User.DoesNotExist:
       raise PopupException(_("User not found."))
       raise PopupException(_("User not found."))
@@ -95,7 +95,7 @@ def delete_group(request, name):
       finally:
       finally:
         __groups_lock.release()
         __groups_lock.release()
 
 
-      # Send a flash message saying "deleted"?
+      request.info(_('The group was deleted !'))
       return redirect(reverse(list_groups))
       return redirect(reverse(list_groups))
     except Group.DoesNotExist:
     except Group.DoesNotExist:
       raise PopupException(_("Group not found."))
       raise PopupException(_("Group not found."))
@@ -211,6 +211,7 @@ def edit_user(request, username=None):
 
 
           # All ok
           # All ok
           form.save()
           form.save()
+          request.info(_('User information updated'))
         finally:
         finally:
           __users_lock.release()
           __users_lock.release()
 
 
@@ -242,7 +243,7 @@ def edit_group(request, name=None):
     form = GroupEditForm(request.POST, instance=instance)
     form = GroupEditForm(request.POST, instance=instance)
     if form.is_valid():
     if form.is_valid():
       form.save()
       form.save()
-      request.flash.put('Group information updated')
+      request.info(_('Group information updated'))
       return list_groups(request)
       return list_groups(request)
 
 
   else:
   else:
@@ -271,7 +272,7 @@ def edit_permission(request, app=None, priv=None):
     form = PermissionsEditForm(request.POST, instance=instance)
     form = PermissionsEditForm(request.POST, instance=instance)
     if form.is_valid():
     if form.is_valid():
       form.save()
       form.save()
-      request.flash.put('Permission information updated')
+      request.info(_('Permission information updated'))
       return render("list_permissions.mako", request, dict(permissions=HuePermission.objects.all()))
       return render("list_permissions.mako", request, dict(permissions=HuePermission.objects.all()))
 
 
   else:
   else:

+ 24 - 54
desktop/core/src/desktop/middleware.py

@@ -21,6 +21,7 @@ import re
 import tempfile
 import tempfile
 
 
 from django.conf import settings
 from django.conf import settings
+from django.contrib import messages
 from django.contrib.auth import REDIRECT_FIELD_NAME
 from django.contrib.auth import REDIRECT_FIELD_NAME
 from django.core import exceptions, urlresolvers
 from django.core import exceptions, urlresolvers
 import django.db
 import django.db
@@ -29,7 +30,6 @@ from django.utils.http import urlquote
 from django.utils.encoding import iri_to_uri
 from django.utils.encoding import iri_to_uri
 import django.views.static
 import django.views.static
 import django.views.generic.simple
 import django.views.generic.simple
-import django.contrib.auth.views
 
 
 import desktop.conf
 import desktop.conf
 from desktop.lib import apputil, i18n
 from desktop.lib import apputil, i18n
@@ -144,6 +144,29 @@ class ClusterMiddleware(object):
       request.jt = None
       request.jt = None
 
 
 
 
+class NotificationMiddleware(object):
+  """
+  Manages setting request.info and request.error
+  """
+  def process_view(self, request, view_func, view_args, view_kwargs):
+
+    def message(title, detail=None):
+      if detail is None:
+        detail = ''
+      else:
+        detail = '<br/>%s' % detail
+      return '%s %s' % (title, detail)
+
+    def info(title, detail=None):
+      messages.info(request, message(title, detail))
+
+    def error(title, detail=None):
+      messages.error(request, message(title, detail))
+
+    request.info = info
+    request.error = error
+
+
 class AppSpecificMiddleware(object):
 class AppSpecificMiddleware(object):
   @classmethod
   @classmethod
   def augment_request_with_app(cls, request, view_func):
   def augment_request_with_app(cls, request, view_func):
@@ -330,59 +353,6 @@ class SessionOverPostMiddleware(object):
       request.COOKIES[cookie_key] = request.POST[cookie_key]
       request.COOKIES[cookie_key] = request.POST[cookie_key]
       del request.POST[cookie_key]
       del request.POST[cookie_key]
 
 
-class FlashMessageMiddleware(object):
-  """
-  Builds request.flash for manipulating flash messages.
-  request.flash is an instance of the FlashMessenger object.
-  """
-  def process_request(self, request):
-    assert not hasattr(request, "flash")
-    request.flash = FlashMessenger(request)
-    return None
-
-  def process_exception(self, request, exception):
-    request.flash.warn_if_non_empty()
-    return None
-
-  def process_response(self, request, response):
-    # This seems to happen if common middleware's "trailing slash" support gets
-    # activated.
-    if not hasattr(request, "flash"):
-      return response
-
-    request.flash.warn_if_non_empty()
-    return response
-
-class FlashMessenger(object):
-  """
-  Manages storing and rendering flash messages.
-  """
-
-  def __init__(self,request):
-    self.request = request
-
-  def put(self, text):
-    """
-    Places flash messages to be rendered. Note that this will not be HTML-escaped.
-    """
-    self.request.session.setdefault("flashMessages", []).append(text)
-    self.request.session.modified = True
-
-  def get(self):
-    """
-    Gets messages to be displayed, only messages that have not been displayed prior.
-    Always returns a list, though that list may be empty.
-    """
-    messages = self.request.session.get('flashMessages')
-    if messages is not None:
-      del self.request.session['flashMessages']
-      return messages
-    else:
-      return []
-
-  def warn_if_non_empty(self):
-    if self.request.session.get('flashMessages') is not None and len(self.request.session['flashMessages']) > 0:
-      logging.warning("Returning request with unmaterialized flash messages: %s" % repr(self.request.session['flashMessages']))
 
 
 class DatabaseLoggingMiddleware(object):
 class DatabaseLoggingMiddleware(object):
   """
   """

+ 2 - 1
desktop/core/src/desktop/settings.py

@@ -111,7 +111,8 @@ MIDDLEWARE_CLASSES = [
     'desktop.middleware.AjaxMiddleware',
     'desktop.middleware.AjaxMiddleware',
     # Must be after Session, Auth, and Ajax.  Before everything else.
     # Must be after Session, Auth, and Ajax.  Before everything else.
     'desktop.middleware.LoginAndPermissionMiddleware',
     'desktop.middleware.LoginAndPermissionMiddleware',
-    'desktop.middleware.FlashMessageMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'desktop.middleware.NotificationMiddleware',
     'desktop.middleware.JFrameMiddleware',
     'desktop.middleware.JFrameMiddleware',
     'desktop.middleware.ExceptionMiddleware',
     'desktop.middleware.ExceptionMiddleware',
     'desktop.middleware.ClusterMiddleware',
     'desktop.middleware.ClusterMiddleware',

+ 13 - 0
desktop/core/src/desktop/templates/common_footer.html

@@ -16,5 +16,18 @@ See the License for the specific language governing permissions and
 limitations under the License.
 limitations under the License.
 {% endcomment %}
 {% endcomment %}
 
 
+
+{% if messages %}
+    <script>
+      {% for message in messages %}
+          {% if message.tags == 'error' %}
+              $.jHueNotify.error('{{ message|safe }}');
+          {% else %}
+              $.jHueNotify.info('{{ message|safe }}');
+          {% endif %}
+      {% endfor %}
+    </script>
+{% endif %}
+
 	</body>
 	</body>
 </html>
 </html>

+ 4 - 2
desktop/core/src/desktop/views.py

@@ -276,11 +276,13 @@ def commonheader(title, section, padding="60px"):
     padding=padding
     padding=padding
   ))
   ))
 
 
-def commonfooter():
+def commonfooter(messages=None):
   """
   """
   Returns the rendered common footer
   Returns the rendered common footer
   """
   """
-  return render_to_string("common_footer.html")
+  if messages is None:
+    messages = {}
+  return render_to_string("common_footer.html", {'messages': messages})
 
 
 def commonheader_iframe():
 def commonheader_iframe():
   """
   """