diff --git a/ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/project.xcworkspace/xcuserdata/lassebunk.xcuserdatad/UserInterfaceState.xcuserstate b/ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/project.xcworkspace/xcuserdata/lassebunk.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index cd62585..0000000 Binary files a/ContentService/Inventory-JSON-API-iOS/Inventory-iOS/Inventory.xcodeproj/project.xcworkspace/xcuserdata/lassebunk.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/IO2013/Drive/Code.gs b/IO2013/Drive/Code.gs new file mode 100644 index 0000000..1e473d9 --- /dev/null +++ b/IO2013/Drive/Code.gs @@ -0,0 +1,108 @@ +function doGet(e) { + var HTMLToOutput; + if(e.parameter.state){ + var state = JSON.parse(e.parameter.state); + if(state.action === 'create'){ + var newFolder = DriveApp.createFolder('New Assignment'); + + var templateQuiz = DriveApp.getFileById(QUIZ_TEMPLATE); + var templateRoster = DriveApp.getFileById(ROSTER_TEMPLATE); + + var newQuiz = templateQuiz.makeCopy('Quiz Questions'); + newFolder.addFile(newQuiz); + DriveApp.getRootFolder().removeFile(newQuiz); + + var newRoster = templateRoster.makeCopy('Roster'); + newFolder.addFile(newRoster); + DriveApp.getRootFolder().removeFile(newRoster); + + var objectToSaveForClose = { type: 'close', + rosterID : newRoster.getId()} + + var objectToSaveForPublish = { type: 'publish', + assignmentFolderID : newFolder.getId(), + rosterID : newRoster.getId(), + quizID : newQuiz.getId()} + + var closeBlob = Utilities.newBlob(JSON.stringify(objectToSaveForClose), 'application/drive-assignment-creator', 'Close Quiz'); + var publishBlob = Utilities.newBlob(JSON.stringify(objectToSaveForPublish), 'application/drive-assignment-creator', 'Publish Quiz'); + newFolder.createFile(closeBlob); + newFolder.createFile(publishBlob); + + HTMLToOutput = 'Assignment folder created with necessary template. Please update the Roster, fill in the quiz questions and click on the Publish icon in the folder. '; + } else { + var fileID = state.ids[0]; + var content = DriveApp.getFileById(fileID).getBlob().getDataAsString(); + var contentObject = JSON.parse(content); + var ssID = contentObject.rosterID; + var ss = SpreadsheetApp.openById(ssID); + var sheet = ss.getSheets()[0]; + var rosterRange = ss.getRangeByName('RosterRange'); + var rosterObjects = getRowsData(sheet, rosterRange); + + + if(contentObject.type === 'close'){ + for(var i in rosterObjects){ + var rosterItem = rosterObjects[i]; + var quickCopy = DriveApp.getFileById(rosterItem.fileid); + quickCopy.removeEditor(rosterItem.studentEmailAddress); + quickCopy.addViewer(rosterItem.studentEmailAddress); + } + HTMLToOutput = 'Quiz is now closed. Your students will no longer be able to save any changes. They wil be able to view your notes on their quizes. '; + + }else if(contentObject.type === 'publish'){ + var quizFileID = contentObject.quizID; + var quiz = DriveApp.getFileById(quizFileID); + var folderID = contentObject.assignmentFolderID; + var folder = DriveApp.getFolderById(folderID); + + for(var i in rosterObjects){ + var rosterItem = rosterObjects[i]; + var quizCopy = quiz.makeCopy('Quiz - ' + rosterItem.studentName); + folder.addFile(quizCopy); + DriveApp.getRootFolder().removeFile(quizCopy); + quizCopy.addEditor(rosterItem.studentEmailAddress); + rosterItem.fileid = quizCopy.getId(); + } + setRowsData(sheet,rosterObjects); + HTMLToOutput = 'Completed publishing your quizes. Your students should be able to see the files in their "Shared with me" view.'; + } + + } + + } + else if(e.parameter.code){//if we get "code" as a parameter in, then this is a callback. we can make this more explicit + getAndStoreAccessToken(e.parameter.code); + HTMLToOutput = 'App is installed, you can close this window now or open up Google Drive.'; + } + else {//we are starting from scratch or resetting + } + var t = HtmlService.createTemplateFromFile('ui') + t.message = HTMLToOutput; + return t.evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE) +} + +function getURLForAuthorization(){ + return AUTHORIZE_URL + '?response_type=code&client_id='+CLIENT_ID+'&redirect_uri='+REDIRECT_URL +'&scope=https://www.googleapis.com/auth/drive.install'; +} + +function getAndStoreAccessToken(code){ + var parameters = { method : 'post', + payload : 'client_id='+CLIENT_ID+'&client_secret='+CLIENT_SECRET+'&grant_type=authorization_code&redirect_uri='+REDIRECT_URL+'&code=' + code}; + //no need to do anything with the token going forward. + var response = UrlFetchApp.fetch(TOKEN_URL,parameters).getContentText(); +} + + +//replace with any template files you want - +var ROSTER_TEMPLATE = '0AkJNj_IM2wiPdEZzbFUxMjQteGtQS1JadHg5VmFMdGc'; +var QUIZ_TEMPLATE = '1_LIZp1lahFhNouXsZNI1s6UluBskt_LOwQGlJIj2ftY'; + +var AUTHORIZE_URL = 'https://accounts.google.com/o/oauth2/auth'; +var TOKEN_URL = 'https://accounts.google.com/o/oauth2/token'; + +//put your URL below or use ScriptApp.getService().getUrl(); +var REDIRECT_URL= 'https://script.google.com/macros/s/AKfycbxK6aULLp8CL47aiUM_tHZCX9-2YNOK0yrp-ujnUQV8CrUQkUGk/exec'; +var CLIENT_ID = ScriptProperties.getProperty('CLIENT_ID') +var CLIENT_SECRET = ScriptProperties.getProperty('CLIENT_SECRET'); + diff --git a/IO2013/Drive/README.md b/IO2013/Drive/README.md new file mode 100644 index 0000000..9748d95 --- /dev/null +++ b/IO2013/Drive/README.md @@ -0,0 +1,7 @@ +Companion code for Google IO 2013 session - Integrate Google Drive with Google Apps Script + +https://developers.google.com/events/io/sessions/325412094 + +For setup - + +You'll need to create OAuth 2 client ID/secret at http://developers.google.com/console and store it in the right ScriptProperties. diff --git a/IO2013/Drive/SpreadsheetUtils.gs b/IO2013/Drive/SpreadsheetUtils.gs new file mode 100644 index 0000000..aca6a3f --- /dev/null +++ b/IO2013/Drive/SpreadsheetUtils.gs @@ -0,0 +1,189 @@ +//https://developers.google.com/apps-script/storing_data_spreadsheets#reading + + +// getRowsData iterates row by row in the input range and returns an array of objects. +// Each object contains all the data for a given row, indexed by its normalized column name. +// Arguments: +// - sheet: the sheet object that contains the data to be processed +// - range: the exact range of cells where the data is stored +// - columnHeadersRowIndex: specifies the row number where the column names are stored. +// This argument is optional and it defaults to the row immediately above range; +// Returns an Array of objects. +function getRowsData(sheet, range, columnHeadersRowIndex) { + columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1; + var numColumns = range.getLastColumn() - range.getColumn() + 1; + var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns); + var headers = headersRange.getValues()[0]; + return getObjects(range.getValues(), normalizeHeaders(headers)); +} + +// setRowsData fills in one row of data per object defined in the objects Array. +// For every Column, it checks if data objects define a value for it. +// Arguments: +// - sheet: the Sheet Object where the data will be written +// - objects: an Array of Objects, each of which contains data for a row +// - optHeadersRange: a Range of cells where the column headers are defined. This +// defaults to the entire first row in sheet. +// - optFirstDataRowIndex: index of the first row where data should be written. This +// defaults to the row immediately below the headers. +function setRowsData(sheet, objects, optHeadersRange, optFirstDataRowIndex) { + var headersRange = optHeadersRange || sheet.getRange(1, 1, 1, sheet.getMaxColumns()); + var firstDataRowIndex = optFirstDataRowIndex || headersRange.getRowIndex() + 1; + var headers = normalizeHeaders(headersRange.getValues()[0]); + + var data = []; + for (var i = 0; i < objects.length; ++i) { + var values = [] + for (j = 0; j < headers.length; ++j) { + var header = headers[j]; + // If the header is non-empty and the object value is 0... + if ((header.length > 0) && (objects[i][header] == 0)) { + values.push(0); + } + // If the header is empty or the object value is empty... + else if ((!(header.length > 0)) || (objects[i][header]=='')) { + values.push(''); + } + else { + values.push(objects[i][header]); + } + } + data.push(values); + } + + var destinationRange = sheet.getRange(firstDataRowIndex, headersRange.getColumnIndex(), + objects.length, headers.length); + destinationRange.setValues(data); +} + +// getColumnsData iterates column by column in the input range and returns an array of objects. +// Each object contains all the data for a given column, indexed by its normalized row name. +// Arguments: +// - sheet: the sheet object that contains the data to be processed +// - range: the exact range of cells where the data is stored +// - rowHeadersColumnIndex: specifies the column number where the row names are stored. +// This argument is optional and it defaults to the column immediately left of the range; +// Returns an Array of objects. +function getColumnsData(sheet, range, rowHeadersColumnIndex) { + rowHeadersColumnIndex = rowHeadersColumnIndex || range.getColumnIndex() - 1; + var headersTmp = sheet.getRange(range.getRow(), rowHeadersColumnIndex, range.getNumRows(), 1).getValues(); + var headers = normalizeHeaders(arrayTranspose(headersTmp)[0]); + return getObjects(arrayTranspose(range.getValues()), headers); +} + + +// For every row of data in data, generates an object that contains the data. Names of +// object fields are defined in keys. +// Arguments: +// - data: JavaScript 2d array +// - keys: Array of Strings that define the property names for the objects to create +function getObjects(data, keys) { + var objects = []; + for (var i = 0; i < data.length; ++i) { + var object = {}; + var hasData = false; + for (var j = 0; j < data[i].length; ++j) { + var cellData = data[i][j]; + if (isCellEmpty(cellData)) { + continue; + } + object[keys[j]] = cellData; + hasData = true; + } + if (hasData) { + objects.push(object); + } + } + return objects; +} + +// Returns an Array of normalized Strings. +// Arguments: +// - headers: Array of Strings to normalize +function normalizeHeaders(headers) { + var keys = []; + for (var i = 0; i < headers.length; ++i) { + var key = normalizeHeader(headers[i]); + if (key.length > 0) { + keys.push(key); + } + } + return keys; +} + +// Normalizes a string, by removing all alphanumeric characters and using mixed case +// to separate words. The output will always start with a lower case letter. +// This function is designed to produce JavaScript object property names. +// Arguments: +// - header: string to normalize +// Examples: +// "First Name" -> "firstName" +// "Market Cap (millions) -> "marketCapMillions +// "1 number at the beginning is ignored" -> "numberAtTheBeginningIsIgnored" +function normalizeHeader(header) { + var key = ""; + var upperCase = false; + for (var i = 0; i < header.length; ++i) { + var letter = header[i]; + if (letter == " " && key.length > 0) { + upperCase = true; + continue; + } + if (!isAlnum(letter)) { + continue; + } + if (key.length == 0 && isDigit(letter)) { + continue; // first character must be a letter + } + if (upperCase) { + upperCase = false; + key += letter.toUpperCase(); + } else { + key += letter.toLowerCase(); + } + } + return key; +} + +// Returns true if the cell where cellData was read from is empty. +// Arguments: +// - cellData: string +function isCellEmpty(cellData) { + return typeof(cellData) == "string" && cellData == ""; +} + +// Returns true if the character char is alphabetical, false otherwise. +function isAlnum(char) { + return char >= 'A' && char <= 'Z' || + char >= 'a' && char <= 'z' || + isDigit(char); +} + +// Returns true if the character char is a digit, false otherwise. +function isDigit(char) { + return char >= '0' && char <= '9'; +} + +// Given a JavaScript 2d Array, this function returns the transposed table. +// Arguments: +// - data: JavaScript 2d Array +// Returns a JavaScript 2d Array +// Example: arrayTranspose([[1,2,3],[4,5,6]]) returns [[1,4],[2,5],[3,6]]. +function arrayTranspose(data) { + if (data.length == 0 || data[0].length == 0) { + return null; + } + + var ret = []; + for (var i = 0; i < data[0].length; ++i) { + ret.push([]); + } + + for (var i = 0; i < data.length; ++i) { + for (var j = 0; j < data[i].length; ++j) { + ret[j][i] = data[i][j]; + } + } + + return ret; +} \ No newline at end of file diff --git a/IO2013/Drive/ui.html b/IO2013/Drive/ui.html new file mode 100644 index 0000000..c009cf0 --- /dev/null +++ b/IO2013/Drive/ui.html @@ -0,0 +1,53 @@ + +
+
+

+Thank you for authorization the application. Please close this window and return to the spreadsheet.
+
+Close window
+
+
+
+
+
++Please click on the link below to authorize application to the YouTube Analytics API. This page will automatically refresh when you return. +
+ This a test for a Go-To action in Gmail. +
+ + \ No newline at end of file diff --git a/SAP/README.md b/SAP/README.md new file mode 100644 index 0000000..0b89520 --- /dev/null +++ b/SAP/README.md @@ -0,0 +1,16 @@ +SAP Demos +================ + +Sample SAP integrations. You'll need to create a [sample SAP account](http://scn.sap.com/docs/DOC-40986) at the SCN. + +You can watch the video of the [sample demos here](http://www.slideshare.net/SAPAppsPartner/tehcnical-webinar-technical-webinar-building-mashups-with-google-apps-and-sap-using-sap-netweaver-gateway) (start at about 1 hr in for Google Apps demos) + +These are not official Google samples. These are more of proof of concepts to support demos and other presentations. + +Please use this code as a guidline and not as production ready code. + +http://developers.google.com/apps-script + +http://script.google.com + +All code in this folder released under [Apache 2.0 license](http://www.apache.org/licenses/LICENSE-2.0). diff --git a/SAP/Sheets/Code.gs b/SAP/Sheets/Code.gs new file mode 100644 index 0000000..6e6055f --- /dev/null +++ b/SAP/Sheets/Code.gs @@ -0,0 +1,174 @@ +//sample instance +//http://scn.sap.com/docs/DOC-40986 + + +function onOpen(){ + SpreadsheetApp.getActiveSpreadsheet() + .addMenu('SAP NetWeaver',[{name:'SAP Data Wizard', functionName:'showUI'}, + null, + {name:'Load Business Partners', functionName:'getBusinessPartnerData'}, + {name:'Load Sales Orders', functionName:'getSalesOrderData'}, + null, + {name:'Load Items for Sales Order', functionName:'loadSalesItems'}, + null, + {name: 'Post to Google+', functionName:'googleplus'}]);//future ideas +} + +function showUI(){ + var ui = HtmlService.createTemplateFromFile('ui').evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE).setHeight(550); + + SpreadsheetApp.getActiveSpreadsheet().show(ui); +} + +function returnDate(unix_timestamp){ + //var unix_timestamp = '1370025951'; + var date = new Date(unix_timestamp*1000); + //Logger.log(date); + return date; +} + +function returnUnixtimestamp(dateString){ + //var dateString = '2013-04-01'; + var parts = dateString.match(/(\d+)/g); + var date = new Date(parts[0], parts[1]-1, parts[2]); // months are 0-based + var timestamp = date.getTime()/1000 + ''; + return timestamp; +} + + +function createOrSetActiveSheet(sheetName){ + var ss = SpreadsheetApp.getActiveSpreadsheet(); + var sheets = ss.getSheets(); + var sheet; + for(var i = 0;i
+
+
+
+
+
+
+
diff --git a/Twilio/MakePhoneCall/MakePhoneCall.gs b/Twilio/MakePhoneCall/MakePhoneCall.gs
index b4f866e..66cc6bd 100644
--- a/Twilio/MakePhoneCall/MakePhoneCall.gs
+++ b/Twilio/MakePhoneCall/MakePhoneCall.gs
@@ -19,7 +19,7 @@ function readRows() {
function makePhoneCall(name,number, message){
//URL is the callback to the current service with the message
- var url = ScriptApp.getService() + '?MSG'+message.replace(/ /g,'+'); //can't seem to send = in here
+ var url = ScriptApp.getService().getUrl() + '?MSG'+message.replace(/ /g,'+'); //can't seem to send = in here
Logger.log(url);
var payload = {
"From" : "2246773902"
diff --git a/WhiteHouseHackday/Code.gs b/WhiteHouseHackday/Code.gs
new file mode 100644
index 0000000..698b601
--- /dev/null
+++ b/WhiteHouseHackday/Code.gs
@@ -0,0 +1,155 @@
+function onOpen(){
+ SpreadsheetApp.getActiveSpreadsheet()
+ .addMenu('We the people',[{name:'Search Petitions', functionName:'showUI'},
+ {name:'Load Signatures', functionName:'loadSignatures'},
+ null,
+ {name: 'Twitter trending', functionName:'twitter'}]);//future ideas
+}
+
+function showUI(){
+ var ui = HtmlService.createTemplateFromFile('ui').evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE).setHeight(550);
+
+ SpreadsheetApp.getActiveSpreadsheet().show(ui);
+}
+
+function returnDate(unix_timestamp){
+ //var unix_timestamp = '1370025951';
+ var date = new Date(unix_timestamp*1000);
+ //Logger.log(date);
+ return date;
+}
+
+function returnUnixtimestamp(dateString){
+ //var dateString = '2013-04-01';
+ var parts = dateString.match(/(\d+)/g);
+ var date = new Date(parts[0], parts[1]-1, parts[2]); // months are 0-based
+ var timestamp = date.getTime()/1000 + '';
+ return timestamp;
+}
+
+
+function createOrSetActiveSheet(sheetName){
+ var ss = SpreadsheetApp.getActiveSpreadsheet();
+ var sheets = ss.getSheets();
+ var sheet;
+ for(var i = 0;i
+
+
+
+