From 8fb18148fd6a0587978c99d74faf716104f80168 Mon Sep 17 00:00:00 2001 From: Jonathan Piacenti Date: Tue, 8 Dec 2015 16:27:33 +0000 Subject: [PATCH] Add share modal to badge listing. --- lms/djangoapps/badges/api/serializers.py | 2 +- lms/djangoapps/student_profile/views.py | 4 + .../views/badge_list_container.js | 2 + .../student_profile/views/badge_list_view.js | 6 +- .../js/student_profile/views/badge_view.js | 33 +++-- .../views/learner_profile_factory.js | 8 +- .../student_profile/views/share_modal_view.js | 48 +++++++ lms/static/sass/views/_learner-profile.scss | 117 ++++++++++++++++++ .../student_profile/badge.underscore | 14 +++ .../student_profile/share_modal.underscore | 30 +++++ 10 files changed, 254 insertions(+), 10 deletions(-) create mode 100644 lms/static/js/student_profile/views/share_modal_view.js create mode 100644 lms/templates/student_profile/share_modal.underscore diff --git a/lms/djangoapps/badges/api/serializers.py b/lms/djangoapps/badges/api/serializers.py index bd3739be31a7..54e669e9ec27 100644 --- a/lms/djangoapps/badges/api/serializers.py +++ b/lms/djangoapps/badges/api/serializers.py @@ -1,5 +1,5 @@ """ -Serializers for badging +Serializers for Badges """ from rest_framework import serializers diff --git a/lms/djangoapps/student_profile/views.py b/lms/djangoapps/student_profile/views.py index 0ee58e68966e..751a683a4d95 100644 --- a/lms/djangoapps/student_profile/views.py +++ b/lms/djangoapps/student_profile/views.py @@ -7,6 +7,7 @@ from django.http import Http404 from django.views.decorators.http import require_http_methods from django_countries import countries +from django.contrib.staticfiles.storage import staticfiles_storage from edxmako.shortcuts import render_to_response, marketing_link from microsite_configuration import microsite @@ -87,6 +88,9 @@ def learner_profile_context(request, profile_username, user_is_staff): 'country_options': list(countries), 'find_courses_url': marketing_link('COURSES'), 'language_options': settings.ALL_LANGUAGES, + 'badges_logo': staticfiles_storage.url('certificates/images/backpack-logo.png'), + 'badges_icon': staticfiles_storage.url('certificates/images/ico-mozillaopenbadges.png'), + 'backpack_ui_img': staticfiles_storage.url('certificates/images/backpack-ui.png'), 'platform_name': microsite.get_value('platform_name', settings.PLATFORM_NAME), }, 'disable_courseware_js': True, diff --git a/lms/static/js/student_profile/views/badge_list_container.js b/lms/static/js/student_profile/views/badge_list_container.js index e53bce12c8c1..7426e2cea379 100644 --- a/lms/static/js/student_profile/views/badge_list_container.js +++ b/lms/static/js/student_profile/views/badge_list_container.js @@ -9,6 +9,8 @@ initialize: function (options) { BadgeListContainer.__super__.initialize.call(this, options); this.listView.find_courses_url = options.find_courses_url; + this.listView.badgeMeta = options.badgeMeta; + this.listView.ownProfile = options.ownProfile; }, type: 'badge', itemViewClass: BadgeView, diff --git a/lms/static/js/student_profile/views/badge_list_view.js b/lms/static/js/student_profile/views/badge_list_view.js index 182af7c1c2f6..a5c3ef5ec246 100644 --- a/lms/static/js/student_profile/views/badge_list_view.js +++ b/lms/static/js/student_profile/views/badge_list_view.js @@ -15,7 +15,11 @@ row = $('
'); this.$el.append(row); } - var item = new BadgeView({model: badge}).render().el; + var item = new BadgeView({ + model: badge, + badgeMeta: this.badgeMeta, + ownProfile: this.ownProfile + }).render().el; row.append(item); this.itemViews.push(item); }, this); diff --git a/lms/static/js/student_profile/views/badge_view.js b/lms/static/js/student_profile/views/badge_view.js index 559c560e5391..5eba42e5421f 100644 --- a/lms/static/js/student_profile/views/badge_view.js +++ b/lms/static/js/student_profile/views/badge_view.js @@ -1,18 +1,37 @@ ;(function (define, undefined) { 'use strict'; - define([ - 'gettext', 'jquery', 'underscore', 'backbone', 'moment', 'text!templates/student_profile/badge.underscore'], - function (gettext, $, _, Backbone, Moment, badgeTemplate) { + define(['gettext', 'jquery', 'underscore', 'backbone', 'moment', + 'text!templates/student_profile/badge.underscore', + 'js/student_profile/views/share_modal_view'], + function (gettext, $, _, Backbone, Moment, badgeTemplate, ShareModalView) { var BadgeView = Backbone.View.extend({ + initialize: function(options) { + this.context = _.extend(this.options.model.toJSON(), { + 'created': new Moment(this.options.model.toJSON().created), + 'ownProfile': options.ownProfile, + 'badgeMeta': options.badgeMeta + }); + }, attributes: { 'class': 'badge-display' }, - render: function () { - var context = _.extend(this.options.model.toJSON(), { - 'created': new Moment(this.options.model.toJSON().created) + events: { + 'click .share-button': 'createModal' + }, + createModal: function() { + var modal = new ShareModalView({ + model: new Backbone.Model(this.context), + shareButton: this.shareButton }); - this.$el.html(_.template(badgeTemplate, context)); + modal.$el.hide(); + modal.render(); + $('body').append(modal.$el); + modal.$el.fadeIn('short', 'swing', _.bind(modal.ready, modal)); + }, + render: function () { + this.$el.html(_.template(badgeTemplate, this.context)); + this.shareButton = this.$el.find('.share-button'); return this; } }); diff --git a/lms/static/js/student_profile/views/learner_profile_factory.js b/lms/static/js/student_profile/views/learner_profile_factory.js index 4ac76bc1e44c..720fbf4cd793 100644 --- a/lms/static/js/student_profile/views/learner_profile_factory.js +++ b/lms/static/js/student_profile/views/learner_profile_factory.js @@ -131,7 +131,13 @@ var badgeListContainer = new BadgeListContainer({ 'attributes': {'class': 'badge-set-display'}, 'collection': badgeCollection, - 'find_courses_url': options.find_courses_url + 'find_courses_url': options.find_courses_url, + 'ownProfile': options.own_profile, + 'badgeMeta': { + 'badges_logo': options.badges_logo, + 'backpack_ui_img': options.backpack_ui_img, + 'badges_icon': options.badges_icon + } }); var learnerProfileView = new LearnerProfileView({ diff --git a/lms/static/js/student_profile/views/share_modal_view.js b/lms/static/js/student_profile/views/share_modal_view.js new file mode 100644 index 000000000000..21744ed79314 --- /dev/null +++ b/lms/static/js/student_profile/views/share_modal_view.js @@ -0,0 +1,48 @@ +;(function (define, undefined) { + 'use strict'; + define(['gettext', 'jquery', 'underscore', 'backbone', 'moment', + 'text!templates/student_profile/share_modal.underscore'], + function (gettext, $, _, Backbone, Moment, badgeModalTemplate) { + + var ShareModalView = Backbone.View.extend({ + attributes: { + 'class': 'badges-overlay' + }, + events: { + 'click .badges-modal': function (event) {event.stopPropagation();}, + 'click .badges-modal .close': 'close', + 'click .badges-overlay': 'close', + 'keydown': 'keyAction', + 'focus .focusguard-start': 'focusGuardStart', + 'focus .focusguard-end': 'focusGuardEnd' + }, + focusGuardStart: function () { + // Should only be selected directly if shift-tabbing from the start, so grab last item. + this.$el.find("a").last().focus(); + }, + focusGuardEnd: function() { + this.$el.find(".badges-modal").focus(); + }, + close: function () { + this.$el.fadeOut('short', 'swing', _.bind(this.remove, this)); + this.options.shareButton.focus(); + }, + keyAction: function (event) { + if (event.keyCode === $.ui.keyCode.ESCAPE) { + this.close(); + } + }, + ready: function() { + // Focusing on the modal background directly doesn't work, probably due + // to its positioning. + this.$el.find('.badges-modal').focus(); + }, + render: function () { + this.$el.html(_.template(badgeModalTemplate, this.model.toJSON())); + return this; + } + }); + + return ShareModalView; + }); +}).call(this, define || RequireJS.define); diff --git a/lms/static/sass/views/_learner-profile.scss b/lms/static/sass/views/_learner-profile.scss index d1aacf96a722..d0ee5ce6d633 100644 --- a/lms/static/sass/views/_learner-profile.scss +++ b/lms/static/sass/views/_learner-profile.scss @@ -372,6 +372,37 @@ font-weight: bold; color: $blue-s3; } + .share-button { + @extend %t-action3; + @extend %button-reset; + background: $gray-l6; + color: $gray-d1; + padding: ($baseline / 4) ($baseline / 2); + margin-bottom: ($baseline / 2); + display: inline-block; + border-radius: 5px; + border: 2px solid $gray-d1; + cursor: pointer; + transition: background .5s; + .share-prefix { + display: inline-block; + vertical-align: middle; + } + .share-icon-container { + display: inline-block; + img.icon-mozillaopenbadges { + max-width: 1.5em; + margin-right: .25em; + } + } + &:hover { + background: $gray-l4; + } + &:active { + box-shadow: inset 0 4px 15px 0 $black-t2; + transition: none; + } + } } } .badge-placeholder { @@ -379,4 +410,90 @@ box-shadow: inset 0 0 4px 0 $gray-l4; } } + + // ------------------------------ + // #BADGES MODAL + // ------------------------------ + .badges-overlay { + @extend %ui-depth1; + position: fixed; + top: 0; + left: 0; + background-color: $dark-trans-bg; /* dim the background */ + width: 100%; + height: 100%; + vertical-align: middle; + + .badges-modal { + @extend %t-copy-lead1; + @extend %ui-depth2; + color: $lighter-base-font-color; + box-sizing: content-box; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80%; + max-width: 700px; + max-height: calc(100% - 100px); + margin-right: auto; + margin-left: auto; + border-top: rem(10) solid $blue-l2; + background: $light-gray3; + padding-right: ($baseline * 2); + padding-left: ($baseline * 2); + padding-bottom: ($baseline); + overflow-x: hidden; + + .modal-header { + margin-top: ($baseline / 2); + margin-bottom: ($baseline / 2); + } + + .close { + @extend %button-reset; + @extend %t-strong; + color: $lighter-base-font-color; + position: absolute; + right: ($baseline); + top: $baseline; + cursor: pointer; + padding: ($baseline / 4) ($baseline / 2); + @include transition(all $tmg-f2 ease-in-out 0s); + &:focus, &:hover { + background-color: $blue-d2; + border-radius: 3px; + color: $white; + } + } + + .badges-steps { + display: table; + } + + .image-container{ + // Lines the image up with the content of the above list. + @include ltr { + @include padding-left(2em); + } + @include rtl { + @include padding-right(1em); + float: right; + } + } + + .backpack-logo { + @include float(right); + @include margin-left($baseline); + } + } + } + + .modal-hr { + display: block; + border: none; + background-color: $light-gray; + height: rem(2); + width: 100%; + } } diff --git a/lms/templates/student_profile/badge.underscore b/lms/templates/student_profile/badge.underscore index 00b7f3313f7d..10901497270d 100644 --- a/lms/templates/student_profile/badge.underscore +++ b/lms/templates/student_profile/badge.underscore @@ -4,6 +4,20 @@
<%- badge_class.display_name %>

<%- badge_class.description %>

+ <% if (ownProfile) { %> +
+ <% } %>
<%- interpolate( diff --git a/lms/templates/student_profile/share_modal.underscore b/lms/templates/student_profile/share_modal.underscore new file mode 100644 index 000000000000..402756bb2659 --- /dev/null +++ b/lms/templates/student_profile/share_modal.underscore @@ -0,0 +1,30 @@ +
+
+ +

<%- gettext("Share on Mozilla Backpack") %>

+

<%- gettext("To share your certificate on Mozilla Backpack, you must first have a Backpack account. Complete the following steps to add your certificate to Backpack.") %> +

+ + +
    +
  1. <%= + interpolate( + gettext("Create a %(link_start)sMozilla Backpack%(link_end)s account, or log in to your existing account"), + {link_start: '', link_end: ''}, + true + )%> +
  2. +
  3. <%= + interpolate( + gettext("%(download_link_start)sDownload this image (right-click or option-click, save as)%(link_end)s and then %(upload_link_start)supload%(link_end)s it to your backpack.
  4. "), { + download_link_start: '', + link_end: '', upload_link_start: '' + }, + true + )%> +
+
+ +
+
+
\ No newline at end of file