This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
CryptoJS v3.0.2 | |
code.google.com/p/crypto-js | |
(c) 2009-2012 by Jeff Mott. All rights reserved. | |
code.google.com/p/crypto-js/wiki/License | |
http://crypto-js.googlecode.com/svn/tags/3.0.2/build/rollups/sha256.js | |
*/ | |
var CryptoJS=CryptoJS||function(i,p){var f={},q=f.lib={},j=q.Base=function(){function a(){}return{extend:function(h){a.prototype=this;var d=new a;h&&d.mixIn(h);d.$super=this;return d},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var d in a)a.hasOwnProperty(d)&&(this[d]=a[d]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.$super.extend(this)}}}(),k=q.WordArray=j.extend({init:function(a,h){a= | |
this.words=a||[];this.sigBytes=h!=p?h:4*a.length},toString:function(a){return(a||m).stringify(this)},concat:function(a){var h=this.words,d=a.words,c=this.sigBytes,a=a.sigBytes;this.clamp();if(c%4)for(var b=0;b<a;b++)h[c+b>>>2]|=(d[b>>>2]>>>24-8*(b%4)&255)<<24-8*((c+b)%4);else if(65535<d.length)for(b=0;b<a;b+=4)h[c+b>>>2]=d[b>>>2];else h.push.apply(h,d);this.sigBytes+=a;return this},clamp:function(){var a=this.words,b=this.sigBytes;a[b>>>2]&=4294967295<<32-8*(b%4);a.length=i.ceil(b/4)},clone:function(){var a= | |
j.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var b=[],d=0;d<a;d+=4)b.push(4294967296*i.random()|0);return k.create(b,a)}}),r=f.enc={},m=r.Hex={stringify:function(a){for(var b=a.words,a=a.sigBytes,d=[],c=0;c<a;c++){var e=b[c>>>2]>>>24-8*(c%4)&255;d.push((e>>>4).toString(16));d.push((e&15).toString(16))}return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c+=2)d[c>>>3]|=parseInt(a.substr(c,2),16)<<24-4*(c%8);return k.create(d,b/2)}},s=r.Latin1={stringify:function(a){for(var b= | |
a.words,a=a.sigBytes,d=[],c=0;c<a;c++)d.push(String.fromCharCode(b[c>>>2]>>>24-8*(c%4)&255));return d.join("")},parse:function(a){for(var b=a.length,d=[],c=0;c<b;c++)d[c>>>2]|=(a.charCodeAt(c)&255)<<24-8*(c%4);return k.create(d,b)}},g=r.Utf8={stringify:function(a){try{return decodeURIComponent(escape(s.stringify(a)))}catch(b){throw Error("Malformed UTF-8 data");}},parse:function(a){return s.parse(unescape(encodeURIComponent(a)))}},b=q.BufferedBlockAlgorithm=j.extend({reset:function(){this._data=k.create(); | |
this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=g.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var b=this._data,d=b.words,c=b.sigBytes,e=this.blockSize,f=c/(4*e),f=a?i.ceil(f):i.max((f|0)-this._minBufferSize,0),a=f*e,c=i.min(4*a,c);if(a){for(var g=0;g<a;g+=e)this._doProcessBlock(d,g);g=d.splice(0,a);b.sigBytes-=c}return k.create(g,c)},clone:function(){var a=j.clone.call(this);a._data=this._data.clone();return a},_minBufferSize:0});q.Hasher=b.extend({init:function(){this.reset()}, | |
reset:function(){b.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);this._doFinalize();return this._hash},clone:function(){var a=b.clone.call(this);a._hash=this._hash.clone();return a},blockSize:16,_createHelper:function(a){return function(b,d){return a.create(d).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return e.HMAC.create(a,d).finalize(b)}}});var e=f.algo={};return f}(Math); | |
(function(i){var p=CryptoJS,f=p.lib,q=f.WordArray,f=f.Hasher,j=p.algo,k=[],r=[];(function(){function f(a){for(var b=i.sqrt(a),d=2;d<=b;d++)if(!(a%d))return!1;return!0}function g(a){return 4294967296*(a-(a|0))|0}for(var b=2,e=0;64>e;)f(b)&&(8>e&&(k[e]=g(i.pow(b,0.5))),r[e]=g(i.pow(b,1/3)),e++),b++})();var m=[],j=j.SHA256=f.extend({_doReset:function(){this._hash=q.create(k.slice(0))},_doProcessBlock:function(f,g){for(var b=this._hash.words,e=b[0],a=b[1],h=b[2],d=b[3],c=b[4],i=b[5],j=b[6],k=b[7],l=0;64> | |
l;l++){if(16>l)m[l]=f[g+l]|0;else{var n=m[l-15],o=m[l-2];m[l]=((n<<25|n>>>7)^(n<<14|n>>>18)^n>>>3)+m[l-7]+((o<<15|o>>>17)^(o<<13|o>>>19)^o>>>10)+m[l-16]}n=k+((c<<26|c>>>6)^(c<<21|c>>>11)^(c<<7|c>>>25))+(c&i^~c&j)+r[l]+m[l];o=((e<<30|e>>>2)^(e<<19|e>>>13)^(e<<10|e>>>22))+(e&a^e&h^a&h);k=j;j=i;i=c;c=d+n|0;d=h;h=a;a=e;e=n+o|0}b[0]=b[0]+e|0;b[1]=b[1]+a|0;b[2]=b[2]+h|0;b[3]=b[3]+d|0;b[4]=b[4]+c|0;b[5]=b[5]+i|0;b[6]=b[6]+j|0;b[7]=b[7]+k|0},_doFinalize:function(){var f=this._data,g=f.words,b=8*this._nDataBytes, | |
e=8*f.sigBytes;g[e>>>5]|=128<<24-e%32;g[(e+64>>>9<<4)+15]=b;f.sigBytes=4*g.length;this._process()}});p.SHA256=f._createHelper(j);p.HmacSHA256=f._createHmacHelper(j)})(Math); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8" ?> | |
<Module> | |
<ModulePrefs title="Open Badges Issuer Gadget" width="800" height="700" author="Martin Hawksey" | |
description="This gadget allows you to integrate the the Mozilla Open Badges Issuer API into Google Sites. To use insert into a Google site and set a base url for your hosted assertions. To allow users to collect their badges direct them to the web address of the Site page containing the gadget adding ?claim_code={insert the rest of their assertion url} to the url" | |
author_email="apps@hawksey.info" scrolling="true" | |
title_url="http://mashe.hawksey.info" author_affiliation="http://mashe.hawksey.info" | |
screenshot="https://lh3.googleusercontent.com/-TqTcrKbb1dg/ULy8Iyb318I/AAAAAAAABo8/uZId4bI5G5s/s800/gadget-screenshot-280.png" | |
thumbnail="https://lh5.googleusercontent.com/-cBAUZxwrM4A/ULy8IzXNlyI/AAAAAAAABpA/-dB90Stogww/s800/gadget-screenshot-120.png"/> | |
<UserPref name="script_url" display_name="Base Url" datatype="string" default_value="" required="true"/> | |
<Content type="html"> | |
<![CDATA[ <script src="https://beta.openbadges.org/issuer.js"></script> | |
<script type="text/javascript"> | |
<!-- | |
var code = []; // initialize assesor array | |
var prefs = new gadgets.Prefs(); | |
// Modification of http://stackoverflow.com/a/647272/1027723/ | |
function getQueryString() { | |
var ref = document.referrer; | |
var qs= ref.split('?'); | |
var result = {}, queryString = qs[1], | |
re = /([^&=]+)=([^&]*)/g, m; | |
while (m = re.exec(queryString)) { | |
result[decodeURIComponent(m[1])] = decodeURIComponent(m[2]); | |
} | |
return result; | |
} | |
function init() { | |
var base_url = prefs.getString("script_url"); | |
var codes = getQueryString().claim_code; | |
code = codes.split(","); | |
if (codes != "") { | |
for ( var i = 0; i < code .length; i++ ) { | |
code[i] = base_url + code[i]; | |
} | |
document.getElementById("collectBadge").style.display='block'; | |
document.getElementById("msg").style.display='none'; | |
} | |
} | |
gadgets.util.registerOnLoadHandler(init); | |
// --> | |
</script> | |
<p id="msg">No claim code</p> | |
<p id="collectBadgeWrapper"><a href='javascript:void(0);' onclick='OpenBadges.issue(code, function(errors, successes) | |
{ });' id="collectBadge" style="display:none">Claim your badge</a> | |
</p> ]]> | |
</Content> | |
</Module> | |
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// initialising some of these as blank to to keep human readable structure | |
var badgeTemplate = { | |
"recipient": "", | |
"salt": "", | |
"evidence": "", | |
"issued_on": "", | |
"badge": { | |
"version": "0.1.0", | |
"name": "", | |
"image": "", | |
"description": "", | |
"criteria": "", | |
"issuer": { | |
"origin": "http://mashe.hawksey.info", | |
"name": "Martin Hawksey", | |
"org": "CETIS", | |
"contact": "app@hawksey.info" | |
} | |
} | |
}; | |
function onFormSumbit(e) { | |
// next three lines used to get last row number (might fail on simulatious form submits but will do for thsi project) | |
var doc = SpreadsheetApp.getActiveSpreadsheet(); | |
var sheet = doc.getSheetByName("DATA"); | |
var lastrow = sheet.getLastRow(); | |
var correct = false; // initialise the response to the question as incorrect | |
// next 4 lines read the form values submitted | |
var timestamp = e.values[0]; | |
var email = e.values[1]; | |
var name = e.values[2]; | |
var resp = e.values[3]; | |
var claim_code = []; // initialise an array of claim_codes | |
var baseUrl = "https://sites.google.com/a/hawksey.info/sandbox/openbadges"; //this is where my site is hosted | |
var claim_code_base = "row=" + lastrow; // our claim code is going to carry two pieces of info row number and type of badge | |
claim_code.push(Utilities.base64Encode(claim_code_base + "&type=user")); //everyone gets a user badge so we push this to our claim_code array encoding the value to disguise | |
// next 4 lines tests if user got the answer right and if so adds a claim_code | |
if (resp == ScriptProperties.getProperty("answer")){ // answer hidden as script property | |
claim_code.push(Utilities.base64Encode(claim_code_base + "&type=gold")); | |
correct = true; | |
} | |
var url = baseUrl+"?claim_code=" + claim_code.join(","); // build the url to be emailed to person. If multiple badges the claim_code is comma seperated | |
// compose text for the email | |
var emailText = "Hi "+name+",\nThanks for trying the Open Badges Issuer Gadget. To claim you badges visit \n\n" + url; | |
if (correct) { | |
emailText += "\n\n [As you got the question right you can claim the Issuer Gadget User and Gold badges]"; | |
} else { | |
emailText += "\n\n [Unfortunately you didn't attempt the optional question or got it wrong but you can still claim you Issuer Gadget User badge]"; | |
} | |
// using the MailApp function of Apps Script to send the email to the person | |
MailApp.sendEmail(email, "Claim your Open Badges Issuer Gadget Badges", emailText); | |
} | |
// this function builds the Assertion JSON blob for each badge | |
// the code is Published > Deployed as web app. To see an example render visit: | |
// https://script.google.com/a/macros/hawksey.info/s/AKfycbxnZUexQevCm_drrhKsYe5RdwCqE8bRkVDsmfT9b48/exec?claim_code=cm93PTImdHlwZT11c2Vy | |
// An issue with this is currently Google prevent cross domain access to the json file via ajax (which the issuer api uses) so the response is currently proxied using php | |
// <?php | |
// $binfo = json_decode(file_get_contents('https://script.google.com/a/macros/hawksey.info/s/AKfycbyO221yQaOb0kvZJQndKu48FFfBS3bspP6s0w7rPSvW_swo37c/exec?claim_code='.$_GET['claim_code'])); | |
// header('Content-Type: application/json'); | |
// echo json_encode($binfo); | |
// ?> | |
// try at http://hawksey.info/labs/gadget_proxy.php?claim_code=cm93PTImdHlwZT11c2Vy | |
function doGet(e){ | |
// The flow here is get the data from the spreadsheet and create the assertion blob | |
// Other ways of doing this use native NoSQL style ScriptDb (part of Apps Script), pre baking assertion | |
// then serving up directly in doGet | |
// to access spreadsheet data we need to get by id (stored by runing setup) | |
var doc = SpreadsheetApp.openById(ScriptProperties.getProperty('active')); | |
var sheet = doc.getSheetByName("DATA"); | |
var claim_encode = Utilities.base64Decode(e.parameter.claim_code); // decode claim_code | |
var claim_code = bin2String(claim_encode); // need to convert byte array to string | |
var data = getQueryString("?"+claim_code); // extract row and type passed in claim_code | |
// if question was answered correctly prepare gold badge | |
if (data.type == "gold"){ | |
var select ="*"; // used as part of evidence | |
var name = "Open Badges Issuer Gadget Gold"; | |
var image = "https://lh3.googleusercontent.com/-59t5UJTspPw/ULjMmXmB12I/AAAAAAAABoY/5l6Ee6uVVvo/s800/openbadgesgadgetgold.png"; | |
var description = "Awared for understanding urls required to use use the Open Badges Issuer Gadget emebed in a Google Site."; | |
} else { | |
var select ="A,B"; // used as part of evidence | |
var name = "Open Badges Issuer Gadget User"; | |
var image = "https://lh5.googleusercontent.com/-B8zpcTV0OTg/ULjMmPyIZVI/AAAAAAAABoU/8i87MPmpN2M/s800/openbadgesgadgetpart.png"; | |
var description = "Awarded for trying the Open Badges Issuer Gadget."; | |
} | |
// get some data recorded in the spreadsheet | |
var timestamp = sheet.getRange(data.row, 1).getValue(); | |
var email = sheet.getRange(data.row, 2).getValue(); | |
// publishing form response in another spreadsheet (using IMPORTRANGE) which lets us query | |
var evidence_base = "https://spreadsheets.google.com/tq?tqx=out:html&key=0ApRGZfrjxtuedFlmVHJ5RjBPVWxpSUg3djc5VFR1WVE"; | |
// fill in the badge assertion details | |
badgeTemplate.salt = "G00g1e"; | |
badgeTemplate.recipient = hashEmailAddress(email, badgeTemplate.salt); | |
badgeTemplate.evidence = evidence_base + "&tq="+encodeURIComponent("select "+select+" where A = datetime '"+Utilities.formatDate(timestamp, "GMT", "yyyy-MM-dd HH:mm:ss")+"'"); | |
badgeTemplate.issued_on = Utilities.formatDate(timestamp, "GMT", "yyyy-MM-dd"); | |
badgeTemplate.badge.name = name; | |
badgeTemplate.badge.image = image; | |
badgeTemplate.badge.description = description; | |
badgeTemplate.badge.criteria = "http://mashe.hawksey.info/2012/12/open-badges-issuer-gadget-google-sites"; | |
// now that Assertion is complete next lines publish it to the web | |
var output = ContentService.createTextOutput(); | |
output.setMimeType(ContentService.MimeType.JSON); | |
output.setContent(JSON.stringify(badgeTemplate)); | |
return output; | |
} | |
function setup(){ | |
ScriptProperties.setProperty('active', SpreadsheetApp.getActiveSpreadsheet().getId()); | |
} | |
// Based on https://github.com/mozilla/openbadges/wiki/How-to-hash-&-salt-in-various-languages. | |
function hashEmailAddress(email, salt) { | |
var hash = CryptoJS.SHA256(email+salt); | |
return 'sha256$'+ hash; | |
} | |
// Modification of http://stackoverflow.com/a/647272/1027723 | |
function getQueryString(ref) { | |
var qs= ref.split('?'); | |
var result = {}, queryString = qs[1], | |
re = /([^&=]+)=([^&]*)/g, m; | |
while (m = re.exec(queryString)) { | |
result[decodeURIComponent(m[1])] = decodeURIComponent(m[2]); | |
} | |
return result; | |
} | |
// From http://stackoverflow.com/a/3195961/1027723 | |
function bin2String(array) { | |
return String.fromCharCode.apply(String, array); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment