diff --git a/aestimia.js b/aestimia.js index 4f7dd8d..4d8dc1d 100644 --- a/aestimia.js +++ b/aestimia.js @@ -141,9 +141,24 @@ var aestimia = new Api(ENDPOINT, { var state = satisfied ? 'accepted' : 'rejected'; - if (state !== application.state) { - // TO DO - email applicant about change of application state - } + if (state !== application.state) + if (state === 'accepted') + application.getLearner() + .complete(function (err, learner) { + if (err || !learner) return; + + openbadger.awardBadge({ + email: learner.email, + badge: application.badgeId + }, function (err, assertionUrl) { + if (err) + return console.log(err); // Should probably log this + + // TO DO - email applicant about change of application state + + console.log('Badge awarded:', assetionUrl); + }); + }); application.updateAttributes({ state: state, diff --git a/controllers/backpack.js b/controllers/backpack.js index 8557a59..9792226 100644 --- a/controllers/backpack.js +++ b/controllers/backpack.js @@ -1,11 +1,16 @@ +const _ = require('underscore'); const openbadger = require('../openbadger'); const db = require('../db'); -const claim = db.model('Claim'); const isLearner = require('../middleware').isLearner; +const claim = db.model('Claim'); +const applications = db.model('Application'); + module.exports = function (app) { - app.get('/claim', [isLearner], function (req, res, next) { + app.get('/claim', [ + isLearner + ], function (req, res, next) { var claimCode = req.query.code; var user = res.locals.user; @@ -38,7 +43,9 @@ module.exports = function (app) { }); - app.post('/claim', [isLearner], function (req, res, next) { + app.post('/claim', [ + isLearner + ], function (req, res, next) { var claimCode = req.query.code; var user = res.locals.user; @@ -70,7 +77,7 @@ module.exports = function (app) { }); app.get('/mybadges', [ - isLearner, + isLearner, openbadger.middleware('getUserBadges') ], function (req, res, next) { var data = req.remote; @@ -125,6 +132,39 @@ module.exports = function (app) { }); }); + app.get('/myapplications', [ + isLearner + ], function (req, res, next) { + var user = req.session.user; + applications.findAll({where: ['LearnerId = ? AND State != ?', user.id, 'accepted']}).success(function (applications) { + openbadger.getBadges(function (err, data) { + _.each(applications, function(app) { + _.extend(app, _.findWhere(data.badges, {id: app.badgeId})); + }); + res.render('user/applications.html', { + items: _.map(applications, function(badge) { + badge.url = '/myapplications/' + badge.id; + return badge; + }) + }); + }); + }); + }); + + app.get('/myapplications/:id', [ + isLearner + ], function (req, res, next) { + var user = req.session.user; + openbadger.getBadge({id: req.params.id}, function(err, data) { + var badge = data.badge; + applications.find({where: {LearnerId: user.id, BadgeId: req.params.id}}).success(function (application) { + res.render('user/application.html', { + badge: _.extend(badge, application) + }); + }); + }); + }); + app.get('/favorites/:view?', function (req, res, next) { var badge = { thumbnail: '/media/images/badge.png', diff --git a/controllers/program.js b/controllers/program.js index 5d14e7c..af1ed47 100644 --- a/controllers/program.js +++ b/controllers/program.js @@ -218,7 +218,7 @@ module.exports = function (app) { }); }); - app.get('/earn/:badgeName', badger.middleware('getBadgeRecommendations'), function (req, res, next) { + app.get('/earn/:badgeName', badger.middleware('getBadgeRecommendations', {limit:4}), function (req, res, next) { var data = req.remote; res.render('badges/single.html', { badge: req.params.badge, diff --git a/openbadger.js b/openbadger.js index 3ccd675..346b311 100644 --- a/openbadger.js +++ b/openbadger.js @@ -33,6 +33,8 @@ function normalizeBadgeInstance (badge, id) { if (!badge.url) badge.url = '/mybadges/' + id; + badge.id = id; + return badge; } @@ -310,6 +312,25 @@ var openbadger = new Api(ENDPOINT, { }); }, + awardBadge: function awardBadge (query, callback) { + var email = query.email || query.session.user.email; + var shortname = query.badge; + + var params = { + auth: getJWTToken(email), + email: email + } + + this.post('/user/badge/' + shortname, { form: params }, function(err, data) { + if (err) + return callback(err, data); + + return callback(null, { + assetionUrl: data.url + }); + }); + }, + getBadgeFromCode: function getBadgeFromCode (query, callback) { var email = query.email; var code = query.code; @@ -339,11 +360,15 @@ var openbadger = new Api(ENDPOINT, { getBadgeRecommendations: function getBadgeRecommendations (query, callback) { var id = query.badgeName; + var limit = query.limit; + var params = { + limit: limit + }; if (!id) return callback(new errors.BadRequest('Invalid badge key')); - this.get('/badge/' + id + '/recommendations', function(err, data) { + this.get('/badge/' + id + '/recommendations', { qs: params }, function(err, data) { if (err) return callback(err, data); diff --git a/package.json b/package.json index b4e0ace..61737ec 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "knox": "~0.8.2", "mime": "~1.2.9", "mysql": "~2.0.0-alpha7", - "nunjucks": "~0.1.8a", + "nunjucks": "~0.1.9", "request": "~2.21.0", "sequelize": "~1.6.0", "tap": "~0.4.1", diff --git a/static/media/css/core.css b/static/media/css/core.css index ce95b6e..8dd46c7 100644 --- a/static/media/css/core.css +++ b/static/media/css/core.css @@ -3,10 +3,18 @@ html, body { height: 100%; - font-family: 'Open Sans', sans-serif; font-weight: 300; line-height: 18px; } +html, +body, +h1, +h2, +h3, +h4, +p { + font-family: 'Open Sans', sans-serif; +} body { margin: 0px; padding: 0px; @@ -125,6 +133,11 @@ ol { .navbar .nav-wrap ul.nav > li > a:hover { color: #c0c0c0; } +.navbar .nav-wrap ul.nav > li.active a, +.navbar .nav-wrap ul.nav > li.active a:hover { + border-left: none; + border-right: none; +} .navbar .nav-wrap ul.nav > li:last-child { border-right: none; } @@ -241,7 +254,7 @@ nav.pagination ul li a { overflow: hidden; } /*mobile*/ -@media only screen and (min-width: 320px) and (max-width: 599px) { +@media only screen and (max-width: 599px) { .container p, .container h1, .container h2, @@ -251,15 +264,16 @@ nav.pagination ul li a { #main { padding-top: 15px; } + #main .navbar-form select { + margin: 0 auto; + display: block; + } .form-search label, .form-inline label, .form-search .btn-group, .form-inline .btn-group { display: block; } - .filter label { - display: none; - } .navbar .nav-wrap, .container, .navbar-static-top .container, @@ -272,6 +286,14 @@ nav.pagination ul li a { margin-left: auto; margin-right: auto; } + .navbar .brand { + background-image: url('../img/csol_logo_sm.png'); + height: 150px; + background-position: top center; + } + .navbar-inner > .container { + width: 100%; + } .form-horizontal .control-label { text-align: left; float: none; @@ -293,16 +315,41 @@ nav.pagination ul li a { .navbar .nav-wrap { width: 90%; margin: 0 auto; + border-radius: 5px; } - .navbar .nav-wrap ul.nav li { + .navbar .nav-wrap ul.nav li, + .navbar .nav-wrap #dynWrap li, + .navbar .nav-wrap ul.nav li.active, + .navbar .nav-wrap #dynWrap li.active { display: block; } - .navbar .nav-wrap ul.nav li a { - padding: 5px 0; + .navbar .nav-wrap ul.nav li a, + .navbar .nav-wrap #dynWrap li a, + .navbar .nav-wrap ul.nav li.active a, + .navbar .nav-wrap #dynWrap li.active a, + .navbar .nav-wrap ul.nav li a:visited, + .navbar .nav-wrap #dynWrap li a:visited, + .navbar .nav-wrap ul.nav li.active a:visited, + .navbar .nav-wrap #dynWrap li.active a:visited, + .navbar .nav-wrap ul.nav li a:hover, + .navbar .nav-wrap #dynWrap li a:hover, + .navbar .nav-wrap ul.nav li.active a:hover, + .navbar .nav-wrap #dynWrap li.active a:hover { + font-size: 14px; + padding: 7px 0; + text-transform: capitalize; + line-height: 16px; + color: #000; + text-decoration: none; + display: block; + font-weight: 400; } .navbar .nav-wrap ul.nav li:nth-child(even), + .navbar .nav-wrap #dynWrap li:nth-child(even), .navbar .nav-wrap ul.nav > .active > a, - .navbar .nav-wrap ul.nav > .active > a:hover { + .navbar .nav-wrap #dynWrap > .active > a, + .navbar .nav-wrap ul.nav > .active > a:hover, + .navbar .nav-wrap #dynWrap > .active > a:hover { border-left: none; border-right: none; } @@ -600,26 +647,51 @@ input[type="password"].metered:focus:invalid:focus + .password-meter { border-color: #e9322d; } /* ------------------------------ */ -/* Mobile : width > iphone ------ */ -/* ------------------------------ */ -@media only screen and (min-width: 321px) and (max-width: 940px) { - .navbar .brand { - background-image: url('../img/csol_logo_480.png'); - height: 166px; - } -} -/* ------------------------------ */ -/* Mobile : width <= iphone ----- */ -/* ------------------------------ */ -@media only screen and (max-width: 320px) { - .navbar .brand { - background-image: url('../img/csol_logo_320.png'); - height: 116px; - } -} -/* ------------------------------ */ /* CSOL-site SPECIFIC ----------- */ /* ------------------------------ */ +body .navbar.filter label, +body .navbar.filter select, +body .navbar.filter input { + display: block; + float: left; +} +body .navbar.filter select { + margin-right: 25px; +} +body .navbar.filter label { + background: url('../img/icon-sprite.png') no-repeat top left; + width: 30px; + height: 30px; + text-indent: -9000px; + margin-bottom: 0px; + margin-top: 5px; +} +body .navbar.filter label.filter-age { + background-position: 0 -160px; +} +body .navbar.filter label.filter-category { + background-position: 0 -640px; +} +body .navbar.filter label.filter-org { + background-position: 0 -480px; +} +body .navbar.filter label.filter-activity { + background-position: 0 -720px; +} +/* ------------------------------ */ +/* Mobile : general ------------ */ +/* ------------------------------ */ +@media only screen and (max-width: 599px) { + body .navbar.filter .selectWrapper, + body .navbar.filter input { + clear: left; + margin: auto; + text-align: center; + width: 180px; + display: block; + float: none; + } +} /* ------------------------------ */ /* Mobile : width <= iphone ---- */ /* ------------------------------ */ @@ -665,9 +737,15 @@ body.home .navbar .navbar-inner { overflow: visible; max-height: 515px; } +body.home .navbar .nav > li { + font-weight: 400; +} body.home .navbar .nav > li.dropdown.open > .dropdown-toggle { color: #fff; } +body.home .navbar .nav > li span { + font-weight: 300; +} body.home .navbar .brand { background-image: url('../img/csol_logo_sm.png'); position: absolute; @@ -905,7 +983,7 @@ body.home .navbar .nav-wrap ul.nav > li:nth-child(even) { /* ------------------------------ */ /* Landing page : mobile -------- */ /* ------------------------------ */ -@media only screen and (min-width: 320px) and (max-width: 599px) { +@media only screen and (max-width: 599px) { body.home { background-image: url('../img/chalkboard_bg.jpg'); } @@ -952,3 +1030,98 @@ body.home .navbar .nav-wrap ul.nav > li:nth-child(even) { margin: 0 auto; } } +/* ------------------------------ */ +/* Filter sprite positions ------ */ +/* ------------------------------ */ +.sprite-activitylines { + background-position: 0 0; + width: 30px; + height: 30px; +} +.sprite-activitytype { + background-position: 0 -80px; + width: 30px; + height: 30px; +} +.sprite-age { + background-position: 0 -160px; + width: 30px; + height: 30px; +} +.sprite-badgetype { + background-position: 0 -240px; + width: 30px; + height: 30px; +} +.sprite-date { + background-position: 0 -320px; + width: 30px; + height: 30px; +} +.sprite-location { + background-position: 0 -400px; + width: 30px; + height: 30px; +} +.sprite-organization { + background-position: 0 -480px; + width: 30px; + height: 30px; +} +.sprite-search { + background-position: 0 -560px; + width: 30px; + height: 30px; +} +.sprite-topic { + background-position: 0 -640px; + width: 30px; + height: 30px; +} +.sprite-type { + background-position: 0 -720px; + width: 30px; + height: 30px; +} +/* FAQ MAKE LOOK GOOD STYLES HERE */ +.faq { + /* "Split numbering" is what I'm calling the following + ordered list numbering: + 1. ... + 2a. ... + 2b. ... + where 2 is the "split item" + */ + +} +.faq ol, +.faq ul { + margin-bottom: 10px; +} +.faq ol li, +.faq ul li { + list-style-position: inside; +} +.faq .split-numbering, +.faq .split-numbering ol { + counter-reset: section; + list-style-type: none; +} +.faq .split-numbering li ol { + counter-reset: subsection; +} +.faq .split-numbering li:before { + counter-increment: section; + content: counter(section) ". "; + width: 2em; + display: inline-block; +} +.faq li.split-item:before { + visibility: hidden; + position: absolute; + left: -999em; +} +.faq li.split-item li:before { + counter-increment: subsection; + content: counter(section) counter(subsection, lower-alpha) ". "; +} diff --git a/static/media/img/chi.png b/static/media/img/chi.png index cf61745..529b6f0 100644 Binary files a/static/media/img/chi.png and b/static/media/img/chi.png differ diff --git a/static/media/img/icon-sprite.png b/static/media/img/icon-sprite.png new file mode 100644 index 0000000..197b91e Binary files /dev/null and b/static/media/img/icon-sprite.png differ diff --git a/static/media/img/mac.png b/static/media/img/mac.png index 00769a9..1d39780 100644 Binary files a/static/media/img/mac.png and b/static/media/img/mac.png differ diff --git a/static/media/img/moz.png b/static/media/img/moz.png index e0b2cd2..ad101e8 100644 Binary files a/static/media/img/moz.png and b/static/media/img/moz.png differ diff --git a/static/media/js/custom_logic.js b/static/media/js/custom_logic.js index 9e75c08..fc245cd 100644 --- a/static/media/js/custom_logic.js +++ b/static/media/js/custom_logic.js @@ -5,8 +5,21 @@ var mob = 0; if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) mob = 1 $('.show-tooltip').tooltip(); + + /*move filter labels into the selectors*/ + if($('.navbar.filter').length != 0) { + $('.navbar.filter form label').each(function(){ + var selectID = ($(this).attr('for')); + var selectEle = $('#' + selectID); + $('#' + selectID + ' option:first').text($(this).text()); + var selectWrap = $('
').append(this,selectEle); + $('.navbar.filter form').prepend(selectWrap); + + }); + } + + /*landing page overrides*/ if($('body.home').length != 0) { - var vidLink = $('watch video').click(function(){ if($('#i_vid').length == 0) { var bkgFade = $(''); @@ -28,8 +41,9 @@ if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) mob /*landing page menu rearrange*/ $('This summer Mayor Rahm Emanuel is challenging all Chicago youth to participate in the Summer of Learning. School stops for the summer, but learning never should.
').prependTo('.footer .upper'); - $('{{ badge.description }}
-- {% if badge.activityType == 'offline' %} - Claim this badge - {% else %} - Apply - {% endif %} -
++ {% if badge.activityType == 'offline' %} + Claim this badge + {% else %} + Apply + {% endif %} +
+ {% endif %}After you sign up, you can: +
+Setting up an account is easy: +
+If you are younger than 13 years old, you will need to enter your parent’s or guardian’s email address as well to confirm that it’s okay for you to be visiting this website. +
+Use the filter on the Learn page to narrow down the list of learning opportunities. +
+You can filter the badges available based on the topic, the organization offering the badge, the age group best suited for the badge, the badge type (Participation, Skill, or Achievement), and activity type (online or offline). +
+The badges fall into at least one topic in the following STEAM categories: Science, Technology, Engineering, the Arts, or Math. Some badges fit in more than one category – for example, a badge may apply to both science and math. Filter by topic to see the badges by these different areas of interest. +
+Filter by organization to see the group offering the badge. The Chicago Summer of Learning has more than 100 organizations offering opportunities to earn badges, with more than 1,000 badges available. +
+The age filter helps you find badges that are appropriate for your age group. +
+The badge type filter helps you find badges that are based on skills, achievements, or participation. +
+Finally, the activity type notes whether the activity takes place online or offline. +
+After you’ve applied for and earned a badge, we’ll recommend other badges to you, too. You can click on those suggestions to get more information and see if any of them are interesting to you. Apply for as many as you’d like! +
+Click any badge to read details about the badge, including information on how to earn it. Some badges are awarded by local organizations based on work you do in their programs, others are available after completing activities online. The badge helps recognize what you learned and the work you did. +
+You can show off all the badges you earned to your teachers, parents, and friends. Some badges are great to show when you’re applying for jobs! +
+Each badge has different requirements and criteria, so read carefully and find out what you need to do to earn the badge. You might have to enter a code or create a piece of work. Follow the steps and let us know if you have any trouble with them – we’re here to help. You can email us at support@chicagosummeroflearning.org with any questions. +
+A mentor will review your work and approve your application if you meet the criteria to earn the badge. If your work is approved, you’ll earn the badge and see it on your dashboard page. +
+Yes, a mentor can decide not to award you the badge if you don’t meet all the requirements to earn it. This is why it’s important to make sure you follow all the steps necessary to earn each badge. +
+Yes, you can reapply as many times as you like. +
+Go to your dashboard page to see all your badges. This includes badges you have applied for, those waiting for review, those already reviewed, and those favorited. +
+If you’re younger than 13 years old, you can see all that you’ve done during the summer and be proud! +
+If you’re 13 years old or more, you can share all the badges you earned on Facebook or Twitter or send them to your Mozilla Backpack. There’s no need to humblebrag – show the world what you know! +
+Some of these badges show that you’ve learned skills that are valuable for internships and jobs. Others show skills that are specific to industries like music, the arts, technology, science, theater, and more. You can demonstrate all the things you know by making sure others can see your badges. You might also find others who share your interests! +
+Badges show the learning you’ve accomplished over the summer, not school credit. However, you can show the badges you earned throughout the summer to your teachers – some may count for credit, while others may unlock additional opportunities elsewhere. +
+No, you do not. Chicago Summer of Learning offers a variety of online activities that you can do on your own and at your own pace. This way, the program celebrates learning in all forms and offers multiple ways for you to explore the city and learn and earn badges. +
+Signing up your child(ren) is easy: +
+ +The site offers a wide variety of learning opportunities, including trips to museums, summer camps, and concert halls; activities involving civics, music, technology, and theater; and challenges to help develop your child’s self-confidence, leadership skills, and more. +
+Nearly 100 Chicago-area organizations are participating by offering unique badges that fall into at least one of five categories: Science, Technology, Engineering, the Arts, and Mathematics. Some badges even span more than one category! In addition, a variety of online learning opportunities provide Chicago youth the chance to engage in learning at their own pace, whenever and wherever they can. +
+Each badge includes specific information on the work necessary for badge achievement and the criteria assigned to it. This means your child will have very specific guidelines on what to do to earn the badge – and what it means once s/he has earned it. +
+Go to the Learn page and use the filters in the dropdown menus to narrow down the list of learning opportunities. You and your child can filter the badges available based on the topic, the organization offering the badge, the age group best suited for the badge, the badge type (Participation, Skill, or Achievement), and activity type (online or offline). +
+The badges fall into at least one topic in the following STEAM categories: Science, Technology, Engineering, the Arts, or Math. Some badges fit in more than one category – for example, a badge may apply to both science and math. Filter by topic to see the badges by these different areas of interest. +
+Filter by organization to see the group offering the badge. The Chicago Summer of Learning has more than 100 organizations offering opportunities to earn badges, with more than 1,000 badges available. +
+The age filter helps you find badges that are appropriate for your child’s age group. +
+The badge type filter helps you find badges that are based on skills, achievements, or participation. +
+Finally, the activity type notes whether the activity takes place online or offline. +
+Badges are a fun way to show off detailed learning, skills, and accomplishments in a very specific, unique way. Each badge has particular criteria that show what was learned, as well as what action was taken or project created as part of that learning. +
+Badges also can be displayed on Facebook, Twitter, and other social profiles to encourage sharing (and a little bit of bragging). +
+Badges show the learning your child has accomplished over the summer, not school credit. However, some badges may count for credit, while others may unlock additional opportunities elsewhere. To find out if any opportunities are available at school, your child should show all his/her summer badges to his/her teachers and ask if either of these is an option. +
+If your child is younger than 13 years old, you may delete content from your child’s account at any time. +
+If your child is younger than 13 years old, you may cancel your child’s account at any time. +
+Do you have a question that wasn’t answered here? Email us at support@chicagosummeroflearning.org and we’ll help you as soon as we can. +
+{% endblock %} diff --git a/views/programs/single.html b/views/programs/single.html index e6b2df6..6145cd6 100644 --- a/views/programs/single.html +++ b/views/programs/single.html @@ -17,7 +17,7 @@ -+ Issued by: + {% if badge.program.issuer.url %} + + {% endif %} + {{ badge.program.issuer.name }} + {% if badge.program.issuer.url %} + + {% endif %} +
+ ++ Issued to: {{ user.email }} +
+ +{{ badge.description }}
+Cupcake link to Badges page something about badges + should go here.
+ + {% for item in similar %} +