Permalink
Browse files

Revamp notification system

* Don't use `jquery.gritter.js`

  + Notifications now work better without javascript

  + User needs to dismiss the notifications explicitly

* Factor `notification.m.jnj`

* Factor `GTW.RST.TOP.Response.indicate_notifications`
  • Loading branch information...
tanzer committed Jun 10, 2015
1 parent b1b742f commit 44813caf41e030f020520d49a7cf5665e4c02ee5
View
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (C) 2010-2014 Martin Glueck All rights reserved
+# Copyright (C) 2010-2015 Martin Glueck All rights reserved
# Langstrasse 4, A--2244 Spannberg, Austria. martin@mangari.org
# ****************************************************************************
# This module is part of the package GTW.
@@ -19,10 +19,14 @@
# 20-Feb-2010 (MG) Creation
# 17-Aug-2012 (MG) Add new `Cached` property and adapt pickle behavior
# 18-Aug-2012 (MG) Fix `discarge` to avoid `empty` head/tail result
-# 24-Oct-2014 (CT) Add `Notification_Collection.__repr__`, use `portable_repr`
+# 24-Oct-2014 (CT) Add `Notification_Collection.__repr__`,
+# use `portable_repr`
# 24-Oct-2014 (CT) Fix spelling: s/discarge/disgorge/g
# 11-Dec-2014 (CT) Add `Notification_Collection.__bool__`
# 16-Dec-2014 (CT) Add missing import for `TFL.Meta.Once_Property`
+# 10-Jun-2015 (CT) Add `Notification_Collection.__len__`, `.disgorged`;
+# remove `Notification_Collection.Cached`
+# 10-Jun-2015 (CT) Add `Notification` arg `css_class`, property `datetime`
# ««revision-date»»···
#--
"""
@@ -63,6 +67,7 @@
import _TFL._Meta.Object
import _TFL._Meta.Once_Property
+import _TFL.Accessor
import datetime
@@ -95,40 +100,43 @@ def append (self, arg) :
self._notifications.append (arg)
# end def append
- @TFL.Meta.Once_Property
- def Cached (self) :
- return tuple (self)
- # end def Cached
-
def disgorge (self, head = "", joiner = "\n", tail = "") :
- self.Cached = items = tuple (self._notifications)
- result = []
+ items = tuple (self._notifications)
+ result = []
if items :
result.append (head)
result.append \
( joiner.join
( pyk.text_type (s)
- for s in sorted (items, key = lambda n : n.time)
+ for s in sorted (items, key = TFL.Getter.time)
)
)
result.append (tail)
self._notifications = []
return "".join (result)
# end def disgorge
+ def disgorged (self) :
+ result, self._notifications = self._notifications, []
+ return sorted (result, key = TFL.Getter.time)
+ # end def disgorged
+
def __bool__ (self) :
return bool (self._notifications)
# end def __bool__
def __getstate__ (self) :
- self.__dict__.pop ("Cached", ())
return self.__dict__
# end def __getstate__
def __iter__ (self) :
return iter (self._notifications)
# end def __iter__
+ def __len__ (self) :
+ return len (self._notifications)
+ # end def __len__
+
def __repr__ (self) :
return portable_repr (self._notifications)
# end def __repr__
@@ -139,11 +147,19 @@ def __repr__ (self) :
class Notification (TFL.Meta.Object) :
"""A notification based on plain text."""
- def __init__ (self, message, time = None) :
- self.message = message
- self.time = time or datetime.datetime.now ()
+ def __init__ (self, message, time = None, css_class = None) :
+ self.message = message
+ self.time = time or datetime.datetime.now ()
+ self.css_class = css_class
# end def __init__
+ @TFL.Meta.Once_Property
+ def datetime (self) :
+ dt = self.time
+ if isinstance (dt, datetime.datetime) :
+ return dt.strftime ("%Y-%m-%d %H:%M:%S")
+ # end def datetime
+
def __repr__ (self) :
return pyk.reprify \
( "%s (%s, %s)"
View
@@ -28,6 +28,9 @@
# 13-Mar-2015 (CT) Change `anti_csrf_token` to method with arg `form_action`
# 17-Mar-2015 (CT) Signify `Anti_CSRF` in `session`
# 9-Jun-2015 (CT) Add guard `self._request.user` to `username`
+# 10-Jun-2015 (CT) Add `indicate_notifications`, `notifications_added`
+# 10-Jun-2015 (CT) Use `GTW.Notification_Collection` in `add_notification`,
+# not `_set_session_cookie`
# ««revision-date»»···
#--
@@ -43,12 +46,15 @@
import _GTW._RST.Signed_Token
import base64
+import datetime
import time
class _RST_TOP_Response_ (GTW.RST.Response) :
"""Extend GTW.RST.Response with session handling."""
- _own_vars = ("username", )
+ _own_vars = ("notifications_added", "username")
+
+ notifications_added = 0
@Once_Property
def session (self) :
@@ -72,11 +78,12 @@ def username (self, value) :
# end def username
def add_notification (self, noti) :
- notifications = self.session.notifications
+ notifications = GTW.Notification_Collection (self.session)
if notifications is not None :
if not isinstance (noti, GTW.Notification) :
noti = GTW.Notification (noti)
notifications.append (noti)
+ self.notifications_added += 1
# end def add_notification
def anti_csrf_token (self, form_action = None) :
@@ -87,13 +94,24 @@ def anti_csrf_token (self, form_action = None) :
return result
# end def anti_csrf_token
+ def indicate_notifications (self) :
+ added = self.notifications_added
+ if added :
+ notifications = GTW.Notification_Collection (self.session)
+ if len (notifications) :
+ ### `notifications` got disgorged
+ ### -> this response contains embedded notifications
+ ### -> clear the Etag and the last_modified to prevent caching
+ self.set_etag ("")
+ self.last_modified = datetime.datetime.utcfromtimestamp (0)
+ # end def indicate_notifications
+
def _set_session_cookie (self) :
request = self._request
session = self.session
name = request.session_cookie_name
value = request.new_secure_cookie (session.sid)
cookie = self.set_secure_cookie (name, value, max_age = 1<<31)
- GTW.Notification_Collection (session)
return cookie
# end def _set_session_cookie
View
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
-# Copyright (C) 2012-2014 Mag. Christian Tanzer All rights reserved
+# Copyright (C) 2012-2015 Mag. Christian Tanzer All rights reserved
# Glasauergasse 32, A--1130 Wien, Austria. tanzer@swing.co.at
# #*** <License> ************************************************************#
# This module is part of the package GTW.RST.TOP.
-#
+#
# This module is licensed under the terms of the BSD 3-Clause License
# <http://www.c-tanzer.at/license/bsd_3c.html>.
# #*** </License> ***********************************************************#
@@ -29,6 +29,7 @@
# 2-May-2013 (CT) Add argument `resource` to `_http_response_finish`...
# 10-Dec-2013 (CT) Add `href_login`; add `s_domain` to `login_url`
# 11-Dec-2013 (CT) Add default for `csrf_check_p`
+# 10-Jun-2015 (CT) Use `response.indicate_notifications`, not home-grown code
# ««revision-date»»···
#--
@@ -153,15 +154,9 @@ def _http_response (self, resource, request, response) :
# end def _http_response
def _http_response_finish (self, resource, request, response) :
- notifications = response.session.notifications
- if notifications and notifications.Cached :
- ### this response contains notifications ->
- ### clear the Etag and the last_modified to prevent caching of
- ### responses with notifications
- response.set_etag ("")
- response.last_modified = datetime.datetime.utcfromtimestamp (0)
- response._set_session_cookie ()
- response.session.save ()
+ response.indicate_notifications ()
+ response._set_session_cookie ()
+ response.session.save ()
scope = self.scope
if scope :
scope.commit ()
View
@@ -58,6 +58,7 @@
# 2-Dec-2014 (CT) Add `setattr`
# 21-Jan-2015 (CT) Add `filtered_dict`
# 23-Jan-2015 (CT) Add `html_char_ref`
+# 10-Jun-2015 (CT) Add `uuid`
# ««revision-date»»···
#--
@@ -80,6 +81,7 @@
import _TFL.Sorted_By
import itertools
+import uuid
class GTW (TFL.Meta.Object) :
"""Provide additional global functions for Jinja templates."""
@@ -298,6 +300,10 @@ def uri (self, scheme, uri, text = None, ** kw) :
return result
# end def uri
+ def uuid (self) :
+ return uuid.uuid1 ().hex
+ # end def uuid
+
vimeo_video = staticmethod (HTML.vimeo_video)
youtube_video = staticmethod (HTML.youtube_video)
View
@@ -1,6 +1,6 @@
{#- jinja template: html/base.jnj -#}
{#
-## Copyright (C) 2009-2014 Mag. Christian Tanzer All rights reserved
+## Copyright (C) 2009-2015 Mag. Christian Tanzer All rights reserved
## Glasauergasse 32, A--1130 Wien, Austria. tanzer@swing.co.at
## ****************************************************************************
## This template is part of the package JNJ.
@@ -41,13 +41,15 @@
## 20-Feb-2014 (CT) Add `nav_off_canvas`
## 9-Apr-2014 (CT) Add `width=device-width` to `viewport`
## 20-Jun-2014 (CT) Add blocks `body_main` and `body_document`
+## 10-Jun-2015 (CT) Factor `page_notifications.render`
## ««revision-date»»···
##--
#}
-{%- import (html_version or "html/5.jnj") as X -%}
-{%- import "html/media_fragments.jnj" as MF with context %}
-{%- import "html/navigation.jnj" as NF with context %}
+{%- import (html_version or "html/5.jnj") as X -%}
+{%- import "html/media_fragments.jnj" as MF with context %}
+{%- import "html/navigation.jnj" as NF with context %}
+{%- import "html/page_notifications.m.jnj" as PN with context %}
{%- call X.html (lang = page.language|default ("en")) -%}
{%- if not page -%}{%- set page = NAV -%}{%- endif -%}
@@ -102,13 +104,9 @@
</p>
{%- block body_document -%}
<div id="document" class="pg_body">
- {%- block notifications -%}
- {%- if notifications %}
- <div class="notifications">
- {{- notifications.disgorge ("<p>", "</p><p>", "</p>") -}}
- </div>
- {% endif -%}
- {%- endblock notifications -%}
+ {%- block page_notifications -%}
+ {{ PN.render (notifications) }}
+ {%- endblock page_notifications -%}
{%- block body_headline %}
{%- if page.head_line %}
<h1 class="headline{% if page.headline_class %} {{ page.headline_class }}{% endif %}">{{ page.head_line|safe }}</h1>
View
@@ -588,23 +588,14 @@ style_sheet = Style_Sheet \
Include ("html/print.media")
Script (GTW.Script._.Modernizr)
-
-### the base file requires at least the jquery.gritter stuff for the
-### notification
-Script (GTW.Script._.jQuery_UI)
-Script (GTW.Script._.jQuery_Gritter)
-
Script (GTW.Script._.GTW_nav_off_canvas)
-CSS_Link (GTW.CSS_Link._.jQuery_UI)
-CSS_Link (GTW.CSS_Link._.jQuery_Gritter)
CSS_Link \
( "/media/GTW/css/base_ie_lt7_hacks.css"
, media_type = "screen"
, condition = "lt IE 8"
)
-JS_On_Ready ('$.gritter.Convert_Patagraphs_to_Gitter ("notifications");')
JS_On_Ready ("""$(".pg_nav_show").gtw_nav_off_canvas ();""");
### __END__ html/base.jnj.media
@@ -0,0 +1,55 @@
+{#- jinja template: html/page_notifications.m.jnj -#}
+{#
+## Copyright (C) 2015 Mag. Christian Tanzer All rights reserved
+## Glasauergasse 32, A--1130 Wien, Austria. tanzer@swing.co.at
+## #*** <License> ************************************************************#
+## This template is part of the package JNJ.
+##
+## This template is licensed under the terms of the BSD 3-Clause License
+## <http://www.c-tanzer.at/license/bsd_3c.html>.
+## #*** </License> ***********************************************************#
+##
+##++
+## Name
+## html/page_notifications.m.jnj
+##
+## Purpose
+## Template macros rendering page notifications
+##
+## Revision Dates
+## 10-Jun-2015 (CT) Creation
+## ««revision-date»»···
+##--
+#}
+
+{%- macro render (notifications) -%}
+ {%- if notifications %}
+ {%- set close_id = GTW.uuid () %}
+ <form class="page-notifications">
+ <label class="close" for="{{ close_id }}" {# -#}
+ title="{{ GTW._T ("Click to remove notifications")}}"{#- -#}
+ >
+ <i class="fa fa-close fa-fw"></i>{#- -#}
+ </label>
+ {{- X.input.checkbox
+ ( id = close_id
+ , title = GTW._T ("Click to remove notifications")
+ )
+ -}}
+ <ul>
+ {%- for notification in notifications.disgorged () %}
+ {%- set kw = GTW.filtered_dict
+ ( class = notification.css_class
+ , title = notification.datetime
+ )
+ -%}
+ <li{{ kw|xmlattr }}>
+ {{ notification.message }}
+ </li>
+ {% endfor -%}
+ </ul>
+ </form>
+ {% endif -%}
+{%- endmacro -%} {#- render -#}
+
+{#- __END__ jinja template: html/page_notifications.m.jnj -#}
Oops, something went wrong.

0 comments on commit 44813ca

Please sign in to comment.