| /* | |
| 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); |
| <?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> | |
| |
| // 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); | |
| } |
libraronin
commented
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I would like to use your badging gadget, but does this script still work with the new way forms and responses are handled in Google Drive now?