incubator-allura-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From brond...@apache.org
Subject [2/3] git commit: [#6777] Added site-wide notification banner
Date Fri, 25 Oct 2013 18:32:35 GMT
[#6777] Added site-wide notification banner

Signed-off-by: Cory Johns <cjohns@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/b8aa69b2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/b8aa69b2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/b8aa69b2

Branch: refs/heads/master
Commit: b8aa69b252215483377fa9f1b1e2bc919a5f5b18
Parents: 3a67b54
Author: Cory Johns <cjohns@slashdotmedia.com>
Authored: Fri Oct 18 22:17:18 2013 +0000
Committer: Dave Brondsema <dbrondsema@slashdotmedia.com>
Committed: Fri Oct 25 18:29:21 2013 +0000

----------------------------------------------------------------------
 Allura/allura/lib/plugin.py                     | 18 ++++++-
 Allura/allura/model/notification.py             | 20 +++++++
 Allura/allura/nf/allura/css/site_style.css      | 37 +++++++++++++
 Allura/allura/public/nf/js/allura-base.js       |  9 ++++
 .../allura/templates/jinja_master/master.html   |  1 +
 .../templates/jinja_master/theme_macros.html    |  9 ++++
 Allura/allura/tests/test_plugin.py              | 55 +++++++++++++++++++-
 7 files changed, 147 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b8aa69b2/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 3d8610d..a2825f2 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -28,7 +28,7 @@ from cStringIO import StringIO
 from random import randint
 from hashlib import sha256
 from base64 import b64encode
-from datetime import datetime
+from datetime import datetime, timedelta
 import json
 
 try:
@@ -41,6 +41,7 @@ from tg import config
 from pylons import tmpl_context as c, app_globals as g
 from webob import exc
 from bson.tz_util import FixedOffset
+from paste.deploy.converters import asbool, asint
 
 from ming.utils import LazyProperty
 from ming.orm import state
@@ -866,6 +867,21 @@ class ThemeProvider(object):
         else:
             return app.icon_url(size)
 
+    def get_site_notification(self):
+        from pylons import request, response
+        from allura.model.notification import SiteNotification
+        note = SiteNotification.current()
+        if note is None:
+            return None
+        closed_cookie = ('notification-closed-%s' % note._id).encode('utf8')
+        seen_cookie = ('notification-seen-%s' % note._id).encode('utf8')
+        closed = asbool(request.cookies.get(closed_cookie))
+        seen = asint(request.cookies.get(seen_cookie, '0'))+1
+        if closed or note.impressions > 0 and seen > note.impressions:
+            return None
+        response.set_cookie(seen_cookie, str(seen+1), max_age=timedelta(days=365))
+        return note
+
 class LocalProjectRegistrationProvider(ProjectRegistrationProvider):
     pass
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b8aa69b2/Allura/allura/model/notification.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/notification.py b/Allura/allura/model/notification.py
index 718a60f..260e07d 100644
--- a/Allura/allura/model/notification.py
+++ b/Allura/allura/model/notification.py
@@ -620,3 +620,23 @@ class MailFooter(object):
             email=toaddr,
             app_url=app_url,
             setting_url=setting_url)
+
+
+class SiteNotification(MappedClass):
+    """
+    Storage for site-wide notification.
+    """
+
+    class __mongometa__:
+        session = main_orm_session
+        name = 'site_notification'
+        indexes = ['deleted']
+
+    _id = FieldProperty(S.ObjectId)
+    content = FieldProperty(str, if_missing='')
+    deleted = FieldProperty(bool, if_missing=False)
+    impressions = FieldProperty(int, if_missing=lambda:config.get('site_notification.impressions',
0))
+
+    @classmethod
+    def current(cls):
+        return cls.query.find({'deleted': False}).sort('_id', -1).limit(1).first()

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b8aa69b2/Allura/allura/nf/allura/css/site_style.css
----------------------------------------------------------------------
diff --git a/Allura/allura/nf/allura/css/site_style.css b/Allura/allura/nf/allura/css/site_style.css
index b23b7b3..f91003e 100644
--- a/Allura/allura/nf/allura/css/site_style.css
+++ b/Allura/allura/nf/allura/css/site_style.css
@@ -563,6 +563,43 @@ blockquote {
   margin: 0 auto;
 }
 
+#site-notification {
+  margin: 0 auto;
+  margin-bottom: 20px;
+  *zoom: 1;
+  width: 940px;
+  border-size: 2px;
+  border-style: solid;
+  -moz-box-shadow: rgba(0, 0, 0, 0.5) 0 4px 10px 0;
+  -webkit-box-shadow: rgba(0, 0, 0, 0.5) 0 4px 10px 0;
+  -o-box-shadow: rgba(0, 0, 0, 0.5) 0 4px 10px 0;
+  box-shadow: rgba(0, 0, 0, 0.5) 0 4px 10px 0;
+  opacity: 0.9;
+  -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=90);
+  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=90);
+  -moz-border-radius: 4px;
+  -webkit-border-radius: 4px;
+  -o-border-radius: 4px;
+  -ms-border-radius: 4px;
+  -khtml-border-radius: 4px;
+  border-radius: 4px;
+  padding: 10px;
+}
+#site-notification .ico-close {
+  float: right;
+  margin-top: -10px;
+  margin-right: -10px;
+  display: none;
+  font-size: 18px;
+  width: 18px;
+  height: 18px;
+  color: #1a1a1a;
+  cursor: pointer;
+}
+#site-notification:hover .ico-close {
+  display: block;
+}
+
 .hub {
   width: 200px;
   overflow: hidden;

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b8aa69b2/Allura/allura/public/nf/js/allura-base.js
----------------------------------------------------------------------
diff --git a/Allura/allura/public/nf/js/allura-base.js b/Allura/allura/public/nf/js/allura-base.js
index 7830c0b..9c47a48 100644
--- a/Allura/allura/public/nf/js/allura-base.js
+++ b/Allura/allura/public/nf/js/allura-base.js
@@ -216,4 +216,13 @@ $(function(){
     // Provide CSRF protection
     var cval = $.cookie('_session_id');
     $('form[method=post]').append('<input name="_session_id" type="hidden" value="'+cval+'">');
+
+    $('#site-notification .ico-close').click(function() {
+        var $note = $(this).parent();
+        $note.hide();
+        $.cookie('notification-closed-'+$note.data('notification-id'), 'true', {
+            expires: 365,
+            path: '/'
+        });
+    });
 });

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b8aa69b2/Allura/allura/templates/jinja_master/master.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/jinja_master/master.html b/Allura/allura/templates/jinja_master/master.html
index e297b03..25e9e45 100644
--- a/Allura/allura/templates/jinja_master/master.html
+++ b/Allura/allura/templates/jinja_master/master.html
@@ -77,6 +77,7 @@
     {% endfor %}
     {% endblock %}
     {{theme_macros.header(g.login_url, '/auth/logout')}}
+    {{theme_macros.site_notification()}}
     {% set flash = tg.flash_obj.render('flash', use_js=False) %}
     <section id="page-body" class="{{g.document_class(neighborhood)}}">
 	  <div id="nav_menu_holder">

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b8aa69b2/Allura/allura/templates/jinja_master/theme_macros.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/jinja_master/theme_macros.html b/Allura/allura/templates/jinja_master/theme_macros.html
index 9f7a4f2..577a474 100644
--- a/Allura/allura/templates/jinja_master/theme_macros.html
+++ b/Allura/allura/templates/jinja_master/theme_macros.html
@@ -123,3 +123,12 @@
     </div>
 {%- endmacro %}
 
+{%- macro site_notification() %}
+    {% set note = g.theme.get_site_notification() %}
+    {% if note %}
+        <div id="site-notification" class="info" data-notification-id="{{note._id}}">
+            <b title="Close" class="ico ico-close" data-icon="D"></b>
+            {{note.content|safe}}
+        </div>
+    {% endif %}
+{%- endmacro %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b8aa69b2/Allura/allura/tests/test_plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_plugin.py b/Allura/allura/tests/test_plugin.py
index 57f8dbf..b1b3f3f 100644
--- a/Allura/allura/tests/test_plugin.py
+++ b/Allura/allura/tests/test_plugin.py
@@ -16,13 +16,15 @@
 #       under the License.
 
 from functools import partial
-from nose.tools import assert_equals, assert_raises
+from nose.tools import assert_equals, assert_raises, assert_is_none, assert_is
 from mock import Mock, MagicMock, patch
 from formencode import Invalid
+from datetime import timedelta
 
 from allura import model as M
 from allura.lib.utils import TruthyCallable
 from allura.lib.plugin import ProjectRegistrationProvider
+from allura.lib.plugin import ThemeProvider
 from allura.lib.exceptions import ProjectConflict, ProjectShortnameInvalid
 
 
@@ -61,3 +63,54 @@ class TestProjectRegistrationProvider(object):
         assert_raises(ProjectShortnameInvalid, v, 'this is invalid and too long', neighborhood=nbhd)
         Project.query.get.return_value = Mock()
         assert_raises(ProjectConflict, v, 'thisislegit', neighborhood=nbhd)
+
+
+class TestThemeProvider(object):
+    @patch('allura.model.notification.SiteNotification')
+    @patch('pylons.response')
+    @patch('pylons.request')
+    def test_get_site_notification_no_note(self, request, response, SiteNotification):
+        SiteNotification.current.return_value = None
+        assert_is_none(ThemeProvider().get_site_notification())
+        assert not response.set_cookie.called
+
+    @patch('allura.model.notification.SiteNotification')
+    @patch('pylons.response')
+    @patch('pylons.request')
+    def test_get_site_notification_closed(self, request, response, SiteNotification):
+        SiteNotification.current.return_value._id = 'deadbeef'
+        request.cookies = {'notification-closed-deadbeef': 'true'}
+        assert_is_none(ThemeProvider().get_site_notification())
+        assert not response.set_cookie.called
+
+    @patch('allura.model.notification.SiteNotification')
+    @patch('pylons.response')
+    @patch('pylons.request')
+    def test_get_site_notification_impressions_over(self, request, response, SiteNotification):
+        note = SiteNotification.current.return_value
+        note._id = 'deadbeef'
+        note.impressions = 2
+        request.cookies = {'notification-seen-deadbeef': '3'}
+        assert_is_none(ThemeProvider().get_site_notification())
+        assert not response.set_cookie.called
+
+    @patch('allura.model.notification.SiteNotification')
+    @patch('pylons.response')
+    @patch('pylons.request')
+    def test_get_site_notification_impressions_under(self, request, response, SiteNotification):
+        note = SiteNotification.current.return_value
+        note._id = 'deadbeef'
+        note.impressions = 2
+        request.cookies = {'notification-seen-deadbeef': '1'}
+        assert_is(ThemeProvider().get_site_notification(), note)
+        response.set_cookie.assert_called_once_with('notification-seen-deadbeef', '3', max_age=timedelta(days=365))
+
+    @patch('allura.model.notification.SiteNotification')
+    @patch('pylons.response')
+    @patch('pylons.request')
+    def test_get_site_notification_impressions_persistent(self, request, response, SiteNotification):
+        note = SiteNotification.current.return_value
+        note._id = 'deadbeef'
+        note.impressions = 0
+        request.cookies = {'notification-seen-deadbeef': '1000'}
+        assert_is(ThemeProvider().get_site_notification(), note)


Mime
View raw message