diff --git a/ckan/controllers/user.py b/ckan/controllers/user.py index 7166bfec33b..3976088886a 100644 --- a/ckan/controllers/user.py +++ b/ckan/controllers/user.py @@ -501,6 +501,8 @@ def dashboard(self, id=None): data_dict = {'id': id, 'user_obj': c.userobj} self._setup_template_variables(context, data_dict) + c.dashboard_activity_stream = h.dashboard_activity_stream(id) + # Mark the user's new activities as read whenever they view their # dashboard page. get_action('dashboard_mark_activities_as_read')(context, {}) diff --git a/ckan/lib/activity_streams.py b/ckan/lib/activity_streams.py index e047c9c92f5..54fe6a7b619 100644 --- a/ckan/lib/activity_streams.py +++ b/ckan/lib/activity_streams.py @@ -1,4 +1,5 @@ import re +import datetime from pylons.i18n import _ from webhelpers.html import literal @@ -178,9 +179,13 @@ def activity_stream_string_new_related_item(): # A list of activity types that may have details activity_stream_actions_with_detail = ['changed package'] -def activity_list_to_html(context, activity_stream): +def activity_list_to_html(context, activity_stream, is_dashboard=False): '''Return the given activity stream as a snippet of HTML.''' + # get the last time they read the dashboard + if (is_dashboard): + last_viewed = logic.get_action('dashboard_get_last_viewed')(context, {}) + activity_list = [] # These are the activity stream messages. for activity in activity_stream: detail = None @@ -220,10 +225,17 @@ def activity_list_to_html(context, activity_stream): for match in matches: snippet = activity_snippet_functions[match](activity, detail) data[str(match)] = snippet + timestamp = datetime.datetime.strptime(activity['timestamp'], '%Y-%m-%dT%H:%M:%S.%f').timetuple() + if (is_dashboard): + is_new = ( timestamp > last_viewed ) + else: + is_new = False; + activity_list.append({'msg': activity_msg, 'type': activity_type.replace(' ', '-').lower(), 'icon': activity_icon, 'data': data, - 'timestamp': activity['timestamp']}) + 'timestamp': activity['timestamp'], + 'is_new': is_new}) return literal(base.render('activity_streams/activity_stream_items.html', extra_vars={'activities': activity_list})) diff --git a/ckan/logic/action/get.py b/ckan/logic/action/get.py index 0adc1bb4623..c9b3455c9d8 100644 --- a/ckan/logic/action/get.py +++ b/ckan/logic/action/get.py @@ -2153,7 +2153,7 @@ def dashboard_activity_list_html(context, data_dict): ''' activity_stream = dashboard_activity_list(context, data_dict) - return activity_streams.activity_list_to_html(context, activity_stream) + return activity_streams.activity_list_to_html(context, activity_stream, is_dashboard=True) def dashboard_new_activities_count(context, data_dict): @@ -2179,6 +2179,11 @@ def dashboard_new_activities_count(context, data_dict): strptime(activity['timestamp'], fmt) > last_viewed] return len(new_activities) +def dashboard_get_last_viewed(context, data_dict): + model = context['model'] + user = model.User.get(context['user']) # The authorized user. + last_viewed = model.Dashboard.get_activity_stream_last_viewed(user.id) + return last_viewed.timetuple() def dashboard_mark_activities_as_read(context, data_dict): '''Mark all the authorized user's new dashboard activities as old. diff --git a/ckan/public/base/javascript/modules/dashboard.js b/ckan/public/base/javascript/modules/dashboard.js new file mode 100644 index 00000000000..7c30c655c6c --- /dev/null +++ b/ckan/public/base/javascript/modules/dashboard.js @@ -0,0 +1,11 @@ +this.ckan.module('dashboard', function ($, _) { + return { + initialize: function () { + if ($('.new', this.el)) { + setTimeout(function() { + $('.masthead .notifications').removeClass('notifications-important').html('0'); + }, 1000); + } + } + }; +}); diff --git a/ckan/public/base/javascript/resource.config b/ckan/public/base/javascript/resource.config index 8db348394b7..93ab18052e0 100644 --- a/ckan/public/base/javascript/resource.config +++ b/ckan/public/base/javascript/resource.config @@ -33,6 +33,7 @@ ckan = modules/resource-upload-field.js modules/follow.js modules/popover-context.js + modules/dashboard.js main = apply_html_class diff --git a/ckan/public/base/less/activity.less b/ckan/public/base/less/activity.less index 98a45a49eb7..1787c395d56 100644 --- a/ckan/public/base/less/activity.less +++ b/ckan/public/base/less/activity.less @@ -4,6 +4,7 @@ list-style-type: none; background: transparent url('@{imagePath}/dotted.png') 14px 0 repeat-y; .item { + position: relative; margin: 0 0 15px 0; padding: 0; .clearfix; @@ -41,6 +42,20 @@ white-space: nowrap; margin-left: 5px; } + .new { + display: block; + position: absolute; + overflow: hidden; + top: -3px; + left: -3px; + width: 10px; + height: 10px; + background-color: #A35647; + border: 1px solid #FFF; + text-indent: -1000px; + .border-radius(100px); + .box-shadow(0 1px 2px rgba(0, 0, 0, 0.2)); + } } } diff --git a/ckan/public/base/less/ckan.less b/ckan/public/base/less/ckan.less index 21a8d6ab07e..028c7de93a5 100644 --- a/ckan/public/base/less/ckan.less +++ b/ckan/public/base/less/ckan.less @@ -17,6 +17,7 @@ @import "activity.less"; @import "popover-context.less"; @import "follower-list.less"; +@import "dashboard.less"; body { // Using the masthead/footer gradient prevents the color from changing diff --git a/ckan/public/base/less/dashboard.less b/ckan/public/base/less/dashboard.less new file mode 100644 index 00000000000..63223bbcd7b --- /dev/null +++ b/ckan/public/base/less/dashboard.less @@ -0,0 +1,5 @@ +.module-my-datasets { + .empty { + padding: 10px; + } +} diff --git a/ckan/public/base/less/iehacks.less b/ckan/public/base/less/iehacks.less index 1035ad803d6..0e4b90a21c8 100644 --- a/ckan/public/base/less/iehacks.less +++ b/ckan/public/base/less/iehacks.less @@ -137,37 +137,9 @@ nav { .ie7-inline-block; } - nav ul { - margin-top: 5px; - li { - float: left; - a.active { - position: relative; - top: -1px; - background-color: lighten(@mastheadBackgroundColorStart, 2); - border-top: 1px solid darken(@mastheadBackgroundColorStart, 3); - border-bottom: 1px solid lighten(@mastheadBackgroundColorStart, 5); - } - } - } - .header-image { - display: block; - } - } - - // Footer - .footer-links { - .clearfix; - li { - float: left; - } } // Navs - .nav-item.active > a { - background-image: url(../images/background-tag-ie7.png); - background-position: 0 0; - } .module-narrow .nav-item.image { .clearfix; } diff --git a/ckan/templates/snippets/activity_item.html b/ckan/templates/snippets/activity_item.html index b7c3640da45..1cf57772027 100644 --- a/ckan/templates/snippets/activity_item.html +++ b/ckan/templates/snippets/activity_item.html @@ -1,4 +1,7 @@
  • + {% if activity.is_new %} + {{ _('New activity item') }} + {% endif %}

    {{ h.literal(activity.msg.format(**activity.data)) }} diff --git a/ckan/templates/user/dashboard.html b/ckan/templates/user/dashboard.html index 779f55580e5..1d43e9576b1 100644 --- a/ckan/templates/user/dashboard.html +++ b/ckan/templates/user/dashboard.html @@ -15,9 +15,9 @@ {% block primary_content %}

    -
    +

    {{ _('News feed') }} {{ _('Activity from users and datasets that you follow') }}

    - {{ h.dashboard_activity_stream(c.user_dict['id']) }} + {{ c.dashboard_activity_stream }}
    {% endblock %}