Friday, March 13, 2015
Simplifying Migration from VBA to Google Apps Script
Editor’s Note: Guest author Bruce McPherson is a contributor to the Excel Liberation website and blog. -- Eric Koleda
If you are new to Google Apps Script and the JavaScript programming language, migrating legacy automation code written in Microsofts Visual Basic for Applications (VBA) can be a daunting task. This blog post describes a Google Apps Script library which mimics the behavior and calling structure of common VBA functions, allowing you to more easily convert your existing applications.
Retaining compatibility
If you are planning to continue to use VBA, you can minimize both the work involved in maintaining the same capability in both platforms, and in porting from one to the other, by preserving backwards compatibility with VBA. This means breaking a few JavaScript conventions, but the result is worth it.
For example, JavaScript variables are normally written in lowerCamelCase, with classes being in UpperCamelCase. VBA is not case sensitive, and uses hungarian notation by convention, except for the built-in functions, which have a capitalized first letter. Since the objective here is to minimize change, I have decided to retain this capitalization for VBA functions replacements (for example CStr()
, Mid()
etc. ).
In VBA, indices (normally) start at 1, while in JavaScript they start at 0. Since these functions are to minimize change in application written in VBA, they also start at 1. For example Mid(x, 1, 2)
means the first 2 characters of
string x in both VBA and JavaScript versions.
Enumeration of collections
JavaScript does not have a collection object. The vEquivalents library provides an implementation of a collection class so that continuity for migrated code that relies on the collection can be maintained. But how to enumerate through that collection? There are a number of ways, but the forEach()
method of the collection most closely resembles the For Each member in collection approach VBA developers are familiar with. The syntax may seem a little fiddly at first, since it passes the code you want executed against each member of the collection as an anonymous function.
var coll = new collection();
// by index
for (var i=1; i <= coll.count() ;i++) {
DebugPrint (coll.item(i));
}
// by key
for (k in coll.keys()) {
DebugPrint (coll.item(k));
}
// forEach
coll.forEach(
function (item, index) {
DebugPrint (item, index);
}
);
Including vEquivalents in your Google Apps Script Project
With the great new libraries functionality, you can now include these scripts in your project by using the project key "MEQ3tE5y5_cTOAgUbUKSIAiz3TLx7pV4j", or you can make a copy of the scripts directly to include in your own project. You will find a selection of other scripts in the library, but the VBA equivalents are all in the module vEquivalents. Note that as you include external libraries in your project (see here for how), you need to prefix the functions with the library identifier (for example mcpher.CStr()
)
Examples
You can access the documentation here, and you will see that most of the common VBA functions are included. Some examples are
var x = Trim(s);
var x = Len(s);
var a = Split(s);
var x = Instr(1, s1, s2);
var d = DateSerial(y, m, d);
MsgBox(s);
var x = InputBox(s);
DebugAssert (b, s);
var w = ActiveSheet();
Going beyond the built-in VBA functions
Using the same approach, I have converted many other VBA utility classes and procedures built over time and added them to this library. This means that the implementation of something on either platform not only looks the same, but can be accomplished in hours or even minutes. For example, on my blog I publish a daily API, implemented in both VBA and Apps Script (both versions even use ScriptDB for the same parameter data). Heres a recent one.
function testUkPostcodes() {
mcpher.generalDataSetQuery ("uk postcodes", "uk postcodes", "postcode");
}
Public Sub testUkPostcodes()
generalDataSetQuery "uk postcodes", "uk postcodes", "postcode"
End Sub
You can find more projects that have been migrated this way here.
Bruce McPherson profile | twitter Bruce McPherson is a contributor to Excel Liberation website and blog, and advocate for open data. |
Introducing AdSense Management Services in Apps Script
Editors note: this announcement is cross-posted from the Google Ads Developer Blog, which caters to AdWords, AdSense, DoubleClick and AdMob developers. We hope you enjoy this latest addition to Google Apps Script — Ryan Boyd
Starting today, the AdSense Management API is available as part of AdSense Services in Google Apps Script. This means that you’ll be able to do things like:
- Create AdSense performance reports for your AdSense accounts in a Google spreadsheet
- Create a chart based on your AdSense reporting data and display it in a Google Spreadsheet
- Embed your scripts in a Google Sites page, for instance to import a chart
- Use triggers to schedule the execution of your scripts, for instance to periodically update the chart imported in the Google Sites page
Accessing the API from Google Apps Scripts is very easy. The following snippet of code shows how to generate a report and populate columns of a spreadsheet with the data retrieved:
function generateReport() {If you want to generate a chart from your data instead of populating the spreadsheet, that’s very easy as well:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(Reports);
var startDate = Browser.inputBox(
"Enter a start date (format: yyyy-mm-dd)");
var endDate = Browser.inputBox(
"Enter an end date (format: yyyy-mm-dd)");
var args = {
metric: [PAGE_VIEWS, AD_REQUESTS, MATCHED_AD_REQUESTS,
INDIVIDUAL_AD_IMPRESSIONS],
dimension: [MONTH]};
var report = AdSense.Reports.generate(startDate, endDate, args).getRows();
for (var i=0; i<report.length; i++) {
var row = report[i];
sheet.getRange(A + String(i+2)).setValue(row[0]);
sheet.getRange(B + String(i+2)).setValue(row[1]);
sheet.getRange(C + String(i+2)).setValue(row[2]);
sheet.getRange(D + String(i+2)).setValue(row[3]);
sheet.getRange(E + String(i+2)).setValue(row[4]);
}
}
function generateLineChart() {A shiny line chart will be displayed in your spreadsheet, as shown in the following picture:
var doc = SpreadsheetApp.getActiveSpreadsheet();
var startDate = Browser.inputBox(
"Enter a start date (format: yyyy-mm-dd)");
var endDate = Browser.inputBox(
"Enter an end date (format: yyyy-mm-dd)");
var adClientId = Browser.inputBox("Enter an ad client id");
var args = {
filter: [AD_CLIENT_ID== + adClientId],
metric: [PAGE_VIEWS, AD_REQUESTS, MATCHED_AD_REQUESTS,
INDIVIDUAL_AD_IMPRESSIONS],
dimension: [MONTH]};
var report = AdSense.Reports.generate(startDate, endDate, args).getRows();
var data = Charts.newDataTable()
.addColumn(Charts.ColumnType.STRING, "Month")
.addColumn(Charts.ColumnType.NUMBER, "Page views")
.addColumn(Charts.ColumnType.NUMBER, "Ad requests")
.addColumn(Charts.ColumnType.NUMBER, "Matched ad requests")
.addColumn(Charts.ColumnType.NUMBER, "Individual ad impressions");
// Convert the metrics to numeric values.
for (var i=0; i<report.length; i++) {
var row = report[i];
data.addRow([row[0],parseInt(row[1]),parseInt(row[2]),
parseInt(row[3]),parseInt(row[4])]);
}
data.build();
var chart = Charts.newLineChart()
.setDataTable(data)
.setTitle("Performances per Month")
.build();
var app = UiApp.createApplication().setTitle("Performances");
var panel = app.createVerticalPanel()
.setHeight(350)
.setWidth(700);
panel.add(chart);
app.add(panel);
doc.show(app);
}
You can start using the service by checking out the reference documentation, that contains also some sample scripts, and this tutorial that implements the use cases mentioned above.
Happy Google Apps Scripting with the AdSense Management API!
Silvano Luciani profile Silvano Luciani joined Googles London office in 2011 to make the AdSense API developers happier people. Before that, he has worked in Finland, Italy, Spain and the UK, writing web based configuration management tools for ISPs, social networks, web based training materials, e-commerce apps and more. He has recently discovered that he loves charts, and has finally started to play the drums in the London’s office music room. If you can call what he does "playing the drums". |
Story Maps A Testing Tool After All
So you’re an agile tester and wonder why you should care about story maps. You may already be convinced that modelling your backlog in two dimensions is useful for helping the whole team visualize the big picture. However, story maps are also a valuable testing tool, providing two additional testing avenues. In the first case, the map itself offers the ability to test the validity of a solution. In the second, a story map improves a team’s ability to identify story slices and then test them.
* How to Create a User Story Map
* How to Prioritize a User Story Map
* Tips for Facilitating a User Story Mapping Session
* Dont Etch your User Story Map in Stone
* User Story Mapping Tool Review – SmartViewApp
* Story Maps - A Testing Tool After All
User story maps are a representation: they provide a means to visualize a system that might be built and are useful for testing the validity of that system before investing significant time and money. A story shared at a recent Agile Winnipeg event demonstrated this principle well. The company involved used story mapping to test an idea before building any software. The team had a project idea that they thought would serve their client well. After quickly building a story map around that idea, they presented the map to their client at the next customer conference. Although it soon became clear that the idea missed the mark, the customer was able to collaborate with the team on the spot, to adjust the map until it represented what they actually wanted built. The map itself was the tool that allowed for the idea to be tested (and then adjusted) and moved the project forward.
Testing Application Slices
As Crispin and Gregory demonstrated in their first book Agile Testing, identifying thin slices and small chunks is important for testing agile projects. Story maps help identify those slices but, perhaps more importantly, they help us understand how those thinly sliced stories might fit together to form a thin slice of the whole application. When undertaking an agile project, testers are required to make a vital shift in thinking; only test small pieces at a time. Despite this fundamental change, it is also important to ensure that the first few pieces fit together, enabling end to end testing as early as possible. The story map helps to identify and prioritize that first application slice. It may be based on a user scenario or just a string of stories that represent the smallest stories that allow left to right movement on the map.
Visualizing testing slices in your map |
As that the team identifies that first slice, utilizing excellent testing skills is crucial. By looking at the map, you can identify areas that will be difficult to test, areas where the test variations are still relatively unknown, or areas that represent higher risk. This activity can help identify stories that should be included in the first application slice.
When coding and testing begins, personas and user scenarios that were created can be revisited, helping to flesh out the map and application slices. Testing with a persona in mind helps ensure that the targeted customer will be satisfied with the solution. It may not be possible or wise to test if the application works well for everyone but testing should evaluate whether the targeted personas can use the application easily, and that the new functionality fits into, or adds to their current processes without getting in the way.
Story Maps—A Testing Tool After All
At first glance, the story map doesn’t appear to be an obvious asset for testing, but upon closer inspection, it proves its value in any testing toolbox. The map itself is a reliable way to test that the right system is being built before any code is written. The map also provides a visual aid for testing in horizontal application slices, allowing for early confirmation that a project is on the right track.
Subscribe to Winnipeg Agilist by Email
Thursday, March 12, 2015
Automating business processes with Google Apps Scripts
For example, your company can create a site for employees to browse and register for training sessions and career development programs. On the page describing each training session or class, you could add a “Register Now” button, which would automatically add registrants to the class roster, add the details of the session to each participants Google Calendar, and email users to confirm enrollment. All of these automated actions can be driven by a script embedded in the site.
Starting today, you can create, edit, and launch Google Apps Scripts from any Google Site, which allows you to automate business processes that involve multiple applications. As in the example above, an Apps Script function can automate tasks such as sending emails, scheduling calendar events, creating and updating site pages using data from other systems, and more.
You can build a script by clicking “More actions” > “Manage site” > “Apps Scripts.” Once you’ve added a script to your site, you can add links or buttons to trigger the script from any page. For tips to get started with scripts, visit the Google Apps Script site.
Posted by Laurent Tu, Google Apps Team
Want to weigh in on this topic? Discuss on Buzz
Announcing Version 1 9 of the NET Library for Google Data APIs
We have released version 1.9 of the .NET Library for Google Data APIs and it is available for download.
This version adds the following new features:
- support for 3-legged OAuth
- two new sample applications: BookThemAll (mashup of Calendar and Provisioning APIs) and UnshareProfiles (showcasing a new feature of the Google Apps Profiles API)
- updates to the Content for Shopping API to implement 20+ new item attributes
- support for new yt:rating system and Access Control settings to the YouTube API
This new version also removes the client library for the deprecated Google Base API and fixes 20 bugs.
For more details, please check the Release Notes and remember to file feature requests or bugs in the project issue tracker.
Claudio Cherubino profile | twitter | blog Claudio is a Developer Programs Engineer working on Google Apps APIs and the Google Apps Marketplace. Prior to Google, he worked as software developer, technology evangelist, community manager, consultant, technical translator and has contributed to many open-source projects, including MySQL, PHP, Wordpress, Songbird and Project Voldemort. |
User stories and sandbag dikes
1. They are both boundary objects. Well constructed dikes keep water out and contain a dry area. Well crafted user stories contain scope and keep additional scope out.
2. There is no value to the user unless all layers are complete. To build a dike, you need to pile the sandbags on top of each other in layers and also weave in the plastic. Leave a layer out or forget the plastic and the dike is useless to the homeowner. To complete a user story, you need to build all layers or tiers of the system. A new database table doesnt provide any value to the user.
3. Size is important. If you want to protect a home from flooding, the dike only needs to surround the home, not the whole property. In fact, if you build it too big (for example, to protect the house, the shed, and the swing set), there are more potential points of failure, it takes longer to build which increases the risk of the dike not being completed on time, and you could have spent more time building dikes to protect other homes. If your stories are too big (for example, to implement logging in for a site, you build the login w/ password, forgot password, password reset, max login attempts, etc before building other stories), there are more potential points of failure, it takes longer to build which increases the risk of the story not being completed on time, and you could have spent more time completing other stories to increase the value of your project.
4. Priority is important. If you build dikes around the homes that are less likely to be affected by the flood, then when you run out of time it is likely that more homes will be flooded. If you complete lower priority user stories first, then when you run out of money you may not have accomplished the goals of your project.
Wednesday, March 11, 2015
How to get from weekend idea to funded startup
Almost every developer has an idea and might want to start a company. Where do you start? Entrepreneurs Paul Buchheit, Joe Kraus, and Seth Priebatsch explained how to go from hacking on the nights and weekends to building an investor funded startup. We also discussed how to find co-founders, attract investors, and focus on the key decisions. You can watch the complete Google I/O session on YouTube. Here are some highlights.
Should I have a co-founder? Having strong co-founders join you in transforming your idea into a real company is critical to success. There is a positive correlation between the number of co-founders and successful outcomes up to about four co-founders. Beyond four co-founders there isn’t much data. But having more co-founders on your team definitely improves your chances of success.
What are important characteristics of a co-founder? It helps if you have worked together before, know each other well, have complimentary expertise, and can communicate openly and honestly. Joe Kraus said you should be able to settle arguments with great answers, not the compromise middle position. What else should you look for in a co-founder?
- Experience starting a company
- Domain experience and an understanding of the market
- Balance and different experience than your own
- Passion about the company vision
What matters most; team, traction, idea, or market segment? They all matter, but the people on the team are the number one consideration. The founding team shapes the product vision and sets the direction for the company. Potential employees and investors are attracted...or not, by the members of the founding team. The idea matters, but will probably change significantly over time, so most investors don’t fixate on the idea. The market segment is important, but only as a gauge of the range of successful outcomes. Traction from early users or customers makes it much easier to raise money.
How do you find investors? People invest in businesses they understand, or people they know. Look for investors that have started companies in your area, or have invested in similar companies in the past. Talk to everyone you know about your idea. Joe Kraus, co-founder of Excite, tells the story of how he read a book about starting companies, called the author, got introduced to other people, who introduced him to other people, and finally ended up with a $3M investment from Kleiner Perkins, one of the top VCs in the world.
Should you raise money from VCs or Angels? The first consideration is who can help you most. You want more than just money from investors. You want help, advice, introductions to other people who can help, and maybe access to press. Aside from help, it depends on how much money you need to raise. Friends and Family is the best place to start to raise small amounts of money. Angel investors can fund anywhere from $100K to $1M or more. Venture Capitalists (VCs) usually invest $1M to $3M in a first round Series A investment.
Incubators, Angels, and VCs - Seth Priebatsch, founder of SCVNGR.com did all three in starting his company. Seth entered a business plan competition at Princeton...and won. He used that to get the initial product built, and then applied to DreamIT, a startup incubator. That experience at the incubator allowed him to build and refine the product. Next he raised a small amount of money from Angels and brought on advisers to help him grow the company. That led to a small round from VCs. Seth believes the more investors you have, the more help, advice, and experience you get.
How do you arrive at a valuation for the company? Joe Kraus says it is an art, not a science. It depends on the stage of the company, the competition, and how fast the market segment is growing. Most early stage startups don’t have revenue and don’t have many users so the valuation is typically between $1M and $3M, and depends on the experience of the founding team, how much progress you have made on the product, and the relative success of competitors. The best way to determine a fair valuation is by having several competing investors give you proposals.
Do I need a business plan? No, but you do need a good slide deck that explains what you want to do, what problem it solves, why it will be successful, and how your team can execute on the vision. Here is a link to a post that explains how to pitch your company to investors. A good pitch deck and a product demo are what most investors are looking for. Business plans might be useful for helping you refine your ideas and vision, but most investors will never read it.
Are patents, IP, and trademarks important? Paul Buchheit says in most cases they don’t matter for early stage startups. Joe Kraus added, patents might be of some value to a potential acquirer, but probably just as a defense against patent infringement cases. Patents are very expensive to obtain (legal bills) and they take two to four years, sometimes longer, to actually get issued. By that time most startups are out of business, acquired, or moving on to something else. Even if you have a patent, most startups can’t afford to defend them in court against potential infringers. The legal expense of defending a patent, and time lost away from your business, make it nearly impossible for a small startup.
Posted by Don Dodge
Want to weigh in on this topic? Discuss on Buzz
Don Dodge profile | twitter | blog | events Don Dodge is a Developer Advocate at Google helping developers build new applications on Google platforms and technologies. Prior to joining Google Don was a startup evangelist at Microsoft. He is also a veteran of five start-ups including Forte Software, AltaVista, Napster, Bowstreet, and Groove Networks. |
Are we agile yet Applying fuzzy logic to the manifesto
In Greek logic (the logic of Plato and Aristotle) there are only two truth values. Any statement is either true, or false, but not both. Truth is absolute. This works nicely except for when we say things like:
If this statement is true, then it is also false, and if it is false, then it is also true, and if it is true, then it is also false - a fun logical circle which causes trouble for Greek logic. This liars paradox was ignored for about 2500 years. Since sentences are either true or false, then the sentence above obviously isnt a sentence. Case closed."This sentence is false"
In the last century, mathematicians started to realize that things were not so simple. A quote from my brother:
"There are contradictions in the laws of physics – We have Einstein’s relativity that explains planetary motion, gravity, all these big things. And we have quantum mechanics that explains subatomic energy and matter, all those tiny things. Unfortunately, they contradict each other in the middle. Because the one requires physical reality to be absolutely smooth and continuous, while the other requires quantum bumps, which are profoundly non-smooth and discontinuous. Right now the laws of physics are both true and false (and by false, I mean true.)"More common examples of statements that can be both true and false include: "I am young", "I am tall", and of course, "We are agile". Truth becomes fuzzy. The truth of those statements can lie somewhere in between absolutely false and absolutely true depending on who is saying them.
"A newborn is young" = truth value of 100%.Thus, fuzzy logic was born. Statements can have a truth value in between true and false.
"A 20 year old is young" = truth value of 80%
Now lets apply this to the question "Are we agile yet?" by looking at the four tenants of the agile manifesto:
Individuals and interactions over processes and toolsMany of us have read articles or have been in discussion groups where these statements are discussed at length and usually someone tries to apply Greek logic by making statements such as: "Agile has no documentation" and "Agile means no planning". However, the word "over" in the middle implies that fuzzy logic is at work. The manifesto authors are saying that for agile teams truth should be closer to the left than the right. We should focus more of our time on working software and less of our time creating documentation. Planning is still important but responding to change is more important so our plans need to be more flexible. For each statement, we want the left side to be more true than the right - the truth value should be above 50%. Additionally, having a truth value of 100% for any of the statements above can be a dangerous thing. We should stay fuzzy.
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan
So when answering the original question for your team or organization - "Are we agile yet?" - dont expect a checklist of practices to give you the answer. Instead, ask your team this question: "Given the statements above, which direction are we moving?" If you are moving to the left and have committed to staying on the left side of each equation, then youve joined the club - you are agile. Agile is a direction, and that is absolutely true.
Storing Items in Drive BLOBs vs Shortcuts
The Drive SDK allows apps to store all kinds of files and file-like items in user-managed cloud storage. Files can be standard document formats like PDF, images, video & audio clips, or even your proprietary application data files. Storing files in Drive makes it easy for users to organize, search, and securely share them with their coworkers, friends, or family.
However, some applications work better with document or application data stored in a database. For example, let’s imagine a modern, web-based project management tool that provides lots of awesome features via data objects that are assembled dynamically at runtime for presentation to the user. In such cases, there is no single file to store all the data that comprises the project -- though there is of course a named “file” item that users will want to save and list in their Drive. Drive applications like this can create file-like entries called shortcuts that allow users to organize, access, and share items as if they were files stored in Drive.
Creating Shortcuts
Creating a shortcut is not much different than creating a regular file. Just set the MIME type to application/vnd.google-apps.drive-sdk
, and make sure you don’t upload any actual content in the call to files.insert
. Here’s an example of creating a shortcut using Python:
shortcut = {
title: My project plan,
mimetype: application/vnd.google-apps.drive-sdk,
description: Project plan for the launch of our new product!
}
file = service.files().insert(body=shortcut).execute()
key = file[id] # Key to use when re-opening shortcuts
For examples in other supported languages, see the Drive SDK documentation.
Opening shortcuts in Drive always launches the application that created them. Shortcuts can even be synchronized to the desktop. Opening a shortcut from the desktop will launch the application that created it in a new browser tab.
Sharing and Security
Shortcuts require special consideration when it comes to sharing and security. Since the actual content is not stored in Drive, applications are responsible for enforcing permissions and ensuring that only authorized users are allowed to read or update content. Follow these best practices when working with shortcuts:
- Always call
files.get
with the current user’s access token to verify the user has access to the content. - Restrict user actions based on the
userPermission
property of the file and disable saves if the user only hasreader
orcommenter
roles.
Honoring permissions not only ensures the protection of user data, but also provides a consistent user experience and added value to Drive applications. Users should be able to safely share an item in Drive without worrying about the particular implementation details of the application that created it.
If you have any questions about shortcuts, don’t hesitate to ask us on our Stack Overflow tag, google-drive-sdk
Steven Bazyl profile | twitter | events Steve is a Developer Advocate for Google Drive, Google Apps, and the Google Apps Marketplace. He enjoys helping developers find ways to integrate their apps and bring added value to users. |
Google Apps Marketplace Developer Survey
Please fill out the survey below if you have an installable app on the Marketplace (using the Add it Now blue button), are actively building an installable app or previously had an installable app on the Marketplace.
Most questions are optional, so please provide the feedback most important to you even if you dont have time to complete the whole survey.
Wed also like to offer the opportunity to speak 1:1 with the Google team about your Marketplace app. If youre interested, please provide a proposed agenda at the end of the survey.
-Ryan, Scott, Steve and the entire Apps Marketplace team
If you do not see the survey embedded above, please visit the form directly.
Ryan Boyd profile | twitter | events Ryan is a Developer Advocate on the Google Apps Marketplace team, helping businesses build applications integrated into Google Apps. Wearing both engineering and business development hats, youll find Ryan writing code and helping businesses get to market with integrated features. |
DrEdit for Google Drive and Learning AngularJS
Since we released version 2 of the Google Drive SDK at Google I/O, we’ve been quietly updating the DrEdit sample application to use the new API. As part of the update, the UI for DrEdit has been rewritten to use AngularJS, a modern web application toolset developed by Google and used in apps at DoubleClick. You might be wondering -- why go through the trouble of rewriting the UI for a basic sample app just to show off some new API features? Turns out it was more of a happy coincidence, but a valuable one and great learning experience!
Practice what you preach
I had the pleasure of co-presenting a session on building great apps for Google Drive, and a big focus of the talk was on all the little things that go into making an app intuitive and user-friendly. This is particularly important for Google Drive, where many users are already familiar with the built-in apps like Docs, Presentations, and Spreadsheets.
The first version of DrEdit was a good demo app, but didn’t follow all of our recommendations. I didn’t want to tell developers all the things they should be doing without having tried them myself. I decided to write a separate sample for the talk and needed a solid base to build on. It was the perfect opportunity to learn a new tool!
HTML & Javascript, only smarter
Angular doesn’t aim to abstract away HTML, Javascript & CSS. Rather, it enhances HTML to make building dynamic apps easier. One benefit, besides a nice short learning curve, is the positive interaction with other tools. To give the app some structure, I used Bootstrap. For example, the HTML for displaying the authenticated user’s info and a small dropdown to link to their profile in the navigation bar only required a few minor changes from typical Bootstrap usage (shown in bold) to wire up to a controller.
<ul class="nav pull-right" ng-controller="UserCtrl">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
{{user.email}}
</a>
<ul class="dropdown-menu">
<li><a href="{{user.link}}" target="_blank">Profile</a></li>
</ul>
</li>
</ul>
Even models are plain javascript objects. Anything reachable through a scope (the binding between a view and controller) is considered part of the model. These can be primitives, hashes, or objects. No need to extend a base class or access properties through special properties. Rather than use change listeners that require special instrumentation, Angular uses dirty checking to detect model changes and update views.
The one catch with this approach is it requires any changes to the model to be made inside the scope of a scope.$apply(fn)
call. In most cases, this is done automatically. When working with external libraries or raw XMLHttpRequests that can fire asynchronous callbacks, calling $apply
yourself is necessary to make sure mutations are tracked correctly.
Speaking of asynchronous tasks…
Promises, Promises
No, I’m not talking about the hit song by 80’s band Naked Eyes, rather Angular’s $q service based on one of the proposed CommonJS Promises APIs. If you’re already familiar with JQuery’s deferred object or any of the other related implementations, this is familiar territory. If not, time to learn. Working with deferred objects can be a lot easier than the traditional callback approach. You can compose async tasks either serially or in parallel, chain callbacks, and return deferred objects from functions like normal results.
Where this mostly comes into play is Angular’s $http
service. If you’ve used jQuery, you’ll find it similar to jQuery.ajax() & the jqXHR result. It is based on the deferred/promises API and also ensures callbacks are executed correctly inside $apply
for safe & efficient model mutations. This combination makes it easy to work with remote services in Angular.
Room for improvement
Trying to learn some new frameworks while preparing for Google I/O and helping developers to launch apps on our updated API all within a few weeks was a lot to take on. A few corners were cut and there are a few things I’d like to revisit when time permits:
- Tests! AngularJS boasts testability as one of its key features and leverages dependency injection throughout to help keep things simple and testable. Since this was originally intended as a live demo instead of a reference app, I cut a few corners here. Yeah, I know better than that...
- Rethink how ACE is used. In most cases it’s easy to figure out if something should be a controller, directive, filter, or service. But trying to pigeonhole libraries like ACE into one of those is daunting. Out of expediency, I chose to hide ACE behind a service, but it feels like it belongs in a directive. It would be nice to be able to declare in HTML:
<editor content=”myModel.text”/>
and have that sync with the model just like any other input in Angular. I started down that route, but correctly wiring up ACE to do that was more effort than it seemed worth at the time. This resulted in some other warts with how the app’s routes & views are structured. - Talking to the backend servers. Not so much an issue with Angular, but rather with a late decision to replace the DrEdit UI. The original goal was a separate app. Once we decided to build on the previous sample, I didn’t want to make unnecessary changes to the server side code that was already written. This led to implementing some of the new features in less than ideal ways. For example, the editor can not independently save metadata from the document content when the file is renamed or starred but the content left untouched. A minor inefficiency, but something that could have been done better.
I know I’ve only scratched the surface and have a lot more to learn. Even so, it was incredibly fun diving head first into AngularJS, and I highly recommend considering it if you’re dissatisfied with your current framework or just want to learn something new!
Steven Bazyl profile | twitter | events Steve is a Developer Advocate for Google Drive, Google Apps, and the Google Apps Marketplace. He enjoys helping developers find ways to integrate their apps and bring added value to users. |
Documents List API Best Practices Batching ACL entries
ACL (Access Control List) entries control who can access Google Docs resources. This allows more specific control over resource privacy or permissions.
Many types of applications need to grant document access for several users at once. As an example: when a new user is added to a project in the Manymoon project management application, every user on the project needs to be granted access to all attached Google docs. If there are 10 users on the project and 10 shared documents, this means the app would typically need to perform 100 HTTP requests -- a lot of overhead. With batching of ACL requests, the application can reduce the number of requests to one per document, resulting in a 10x savings.
Before Batching
A typical ACL entry for a single user is created by making an HTTP POST to the ACL link provided with each resource entry. The POST body looks something like this:
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:gAcl=http://schemas.google.com/acl/2007>
<category scheme=http://schemas.google.com/g/2005#kind
term=http://schemas.google.com/acl/2007#accessRule/>
<gAcl:role value=writer/>
<gAcl:scope type=user value=new_writer@example.com/>
</entry>
To achieve the same thing using the Python client library, use the following code:
from gdata.acl.data import AclScope, AclRole
from gdata.docs.data import AclEntry
acl = AclEntry(
scope = AclScope(value=user@example.com, type=user),
role = AclRole(value=writer)
)
With Batching
Instead of submitting the requests separately, multiple ACL operations for a resource can be combined into a single batch
request. This is done by POSTing a feed of ACL entries. Each ACL entry in the feed must have a special batch:operation
element, describing the type of operation to perform on the ACL entry. Valid operations are query
, insert
, update
, and delete
.
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:gAcl=http://schemas.google.com/acl/2007
xmlns:batch=http://schemas.google.com/gdata/batch>
<category scheme=http://schemas.google.com/g/2005#kind
term=http://schemas.google.com/acl/2007#accessRule/>
<entry>
<category scheme=http://schemas.google.com/g/2005#kind
term=http://schemas.google.com/acl/2007#accessRule/>
<gAcl:role value=reader/>
<gAcl:scope type=domain value=example.com/>
<batch:operation type=insert/>
</entry>
<entry>
<category scheme=http://schemas.google.com/g/2005#kind
term=http://schemas.google.com/acl/2007#accessRule/>
<id>https://docs.google.com/feeds/default/private/full/document%3Adocument_id/acl/user%3Aold_writer%40example.com</id>
<gAcl:role value=writer/>
<gAcl:scope type=user value=new_writer@example.com/>
<batch:operation type=update/>
</entry>
</feed>
The following code represents the same operation in the Python client library:
from gdata.data import BatchOperation
from gdata.acl.data import AclScope, AclRole
from gdata.docs.data import AclEntry
acl1 = AclEntry(
scope=AclScope(value=example.com, type=domain),
role=AclRole(value=reader),
batch_operation=BatchOperation(type=insert)
)
acl2 = client.get_acl_entry_by_self_link(
(https://docs.google.com/feeds/default/private/full/
document%3Adocument_id/acl/user%3Aold_writer%40example.com))
acl2.scope = AclScope(value=new_writer@example.com, type=user)
acl2.role = AclRole(value=writer)
acl2.batch_operation = BatchOperation(type=update)
entries = [acl1, acl2]
The feed of these entries can now be submitted together to apply to a resource:
results = client.batch_process_acl_entries(resource, entries)
The return value is an AclFeed, with a list of AclEntry elements for each operation, the status of which can be checked individually:
for result in results.entry:
print entry.title.text, entry.batch_status.code
The examples shown here are using the raw protocol or the Python client library. The Java client library also supports batch operations on ACL entries.
For more information on how to use batch operations when managing ACLs, see the Google Documents List API documentation, and the Google Data APIs batch protocol reference guide. You can also find assistance in the Google Documents List API forum.
Ali Afshar profile | twitter Ali is a Developer Programs engineer at Google, working on Google Docs and the Shopping APIs which help shopping-based applications upload and search shopping content. As an eternal open source advocate, he contributes to a number of open source applications, and is the author of the PIDA Python IDE. Once an intensive care physician, he has a special interest in all aspects of technology for healthcare. |
Tuesday, March 10, 2015
Instant voting with Apps Script
From nations choosing presidents to offices selecting which coffee to brew, we often find ourselves involved in election systems designed to choose the best option. This spring my alma maters solar vehicle team, CalSol, needed to elect new leaders. Our previous system was painfully slow, involved "raising hands" in a room, and excluded any team members who could not attend a specific meeting. I set out to solve these problems and the result was an easy method for running fair elections in a matter of minutes.
I was able to build the system completely on Google products and technologies:
- Google Forms: Allows members to submit their votes from anywhere.
- Google Spreadsheets: Makes it easy to audit the votes and configure the system.
- Google Apps Script: Simple way to access the results and determine the winner.
I used a lesser known voting system called instant-runoff voting (IRV), or the alternative vote, which asks voters to rank candidates rather than cast a single vote. These votes, along with a secret voting key which I provided to each member, are recorded with a Google Form that automatically populates a spreadsheet. The code in Apps Script looks through the spreadsheet to count the votes while ensuring that each voting key is only used once. The secret keys not only prevent voters from casting multiple votes, but they also allow voters to change their vote by submitting the form again.
Below is a simplified snippet of code that shows the general process used to calculate the winner.
/* Some code omitted for clarity */
/* candidates is a list of names (strings) */
var candidates = get_all_candidates(results_range);
/* votes is an object mapping candidate names -> number of votes */
var votes = get_votes(results_range, candidates, keys_range, valid_keys);
/* winner is candidate name (string) or null */
var winner = get_winner(votes, candidates);
while (winner == null) {
/* Modify candidates to only include remaining candidates */
get_remaining_candidates(votes, candidates);
if (candidates.length == 0) {
Browser.msgBox("Tie");
return;
}
votes = get_votes(results_range, candidates, keys_range, valid_keys);
winner = get_winner(votes, candidates);
}
Browser.msgBox("Winner: " + winner);
I learned that putting a little effort into Apps Script can make people happy and save a lot of time. The team feedback was outstanding. One CalSol member said the process was an "Excellent, clean, and professional voting process. This should become a standard [for the team]." I was elated when I was able to close the polls during a meeting and announce the winners of twelve independent elections in just a matter of minutes.
If you like, you can watch a video demonstrating how to create and run your own election using this script:
Try the script yourself to make sure your coffee preferences are heard!
Chris Cartland profile | GitHub Chris is a Developer Programs Engineer based in Mountain View on the Google+ team. He previously worked on solar vehicles at UC Berkeley and wants developers to write software that makes our lives better. In his spare time he likes to play soccer and throw the ball in after doing a front handspring. |
Google Apps Marketplace We want your suggestions
The Google Apps Marketplace is growing, and we want your suggestions on how to make it bigger and better. Here is your chance to submit your big ideas and small suggestions on Google Apps Marketplace and the APIs used to create installable apps. You can also see existing ideas and what others have to say. Vote up the ideas you like the most. You tell us whats important.
We have set up two Product Ideas sites; one for improvements to the Marketplace itself, and another for applications you would like to see available for sale in the Marketplace. There are already lots of ideas and over 300 votes. Take a look at the ideas and add yours to the list.
Thank you from the Google Apps Marketplace team.
Retiring the Google Documents List API v3
With the arrival of the new Google Drive API v2, we are deprecating the Google Documents List API v3. We are confident that the Google Drive API covers all the functionality of the Documents List API, in addition to adding many improvements, including Drive UI Integration, a finer grained security model, and a better client library experience.
What does this mean for your app?
The Documents List API v3 will remain in action for more than a year, as per our deprecation policy, so there’s no rush, but we encourage you to migrate your code to the new platform. Documentation is available with samples in multiple languages and a migration guide outlining some of the major transition points.
If you have any questions or issues, please ask them on StackOverflow.com, where our team is waiting to hear from you.
Ali Afshar profile | twitter Tech Lead, Google Drive Developer Relations. As an eternal open source advocate, he contributes to a number of open source applications, and is the author of the PIDA Python IDE. Once an intensive care physician, he has a special interest in all aspects of technology for healthcare |
Add missing keys to resource files resx language
Why? While you code your application, youre not in the mood of entering a new key to each and every language file. So you just put them in the default language file (language.resx). And as you code and code, there are several keys you put in.
And then deployment. But you need to enter those keys into other languages. Send them to translators, copy the translations back?
OK, enough said. Borring, not worth a developer. :-) So, the solution...
1. The translation tool in the application (website): ResXManager
2. The "administrational" tool that adds all the missing keys from one file (languageNew.resx) to all the other files (language.de.resx, language.whatever.resx)
3. Small function to translate via google (but consider this to be quite unusable currently since if you send some heavy html, it will give you back all sorts of funny things), thxs to Piyush:
public string TranslateText(string input, string languagePair, Encoding encoding)
{
string url = String.Format("http://www.google.com/translate_t?hl=en&ie=UTF8&text={0}&langpair={1}", input, languagePair);
string result = String.Empty;
try
{
using (WebClient webClient = new WebClient())
{
webClient.Encoding = encoding;
result = webClient.DownloadString(url);
}
Match m = Regex.Match(result, "(?<=overflow:auto">)(.*?)(?=)");
if (m.Success)
result = m.Value;
}
catch { }
return result;
}
The ResxManager and administration is avaliable here. Cant really explain more currently. Check the files and if you dont get it, comment here. Will be glad to explain.
Lots of Excitement about Google Apps at I O 2011
Here are the highlights from the Google Apps track:
Sessions
In the Google Apps track, we had 8 sessions on a variety of topics of interest to Google Apps Marketplace developers, systems integrators and customers alike. All of the sessions are available in HD on YouTube and we’ve also posted many of the session slides and notes.Google Apps Marketplace:
- Launch and Grow your Business App on the Google Apps Marketplace provided an intro to the Apps Marketplace, but most of the session was third-party developers telling the story of their businesses, demoing their integrations and providing guidance for other developers looking for success on the Marketplace. Teaser: 30% free->paid conversion rates from GQueues on the Google Apps Marketplace.
- Apps Marketplace: Best Practices and Integrations covered a wealth of best practices for business app development and Google Apps integrations based on experience working with hundreds of developers building applications for the Google Apps Marketplace.
Google Apps Script:
- Enterprise Workflow with Apps Script showed how Google Apps Script can be used to build complex workflows using simple server-side JavaScript code. The speakers built on several examples for document publishing approval, showing lots of code for how it’s done.
- Developing Apps, Add-Ins and More stepped through building Add-Ins with deep integration into the Google Apps UI and full applications. The team announced the Apps Script GUI Builder to drag and drop UI components and full Apps Script APIs for Gmail and Google Docs.
Application APIs:
- Google Tasks API announced the brand-new API to interact with a user’s Google Tasks. Several third-party developers demonstrated how they integrated tasks with their project management and CRM apps.
- Using the Google Docs APIs to Store All your Information in the Cloud gave a very brief overview of the Documents List API followed by a deep-dive into gcategorically, an App Engine + Python sample app for uploading documents to Google Docs. Best practices for developing integrations with Google Doc were also covered. This session is very useful for Apps Marketplace developers, especially because of the new ability to upload all file types to all types of Google accounts.
Solutions and Administration:
- Developing Innovative Custom Business Solutions with Google Apps covered how web solution providers are driving us towards the goal of 100% web. Included many real-world examples from a variety of companies who are extending Google Apps using Apps Script, Google Sites, gadgets, Data APIs, App Engine, GWT and more.
- Compliance and Security in the Cloud talked about the suite of APIs and tools available for Google Apps customers to handle policy compliance, audit, incident response and more. Very helpful session for IT administrators, CTOs and CIOs using Google Apps, with much of the session diving into several examples using real-world use cases.
Developer Sandbox
We had 24 fantastic companies in our Developer Sandbox this year, showcasing the applications they built for the Google Apps Marketplace and the services they provide Google Apps customers as system integrators or VARs. We were excited to see many of the companies talking about new integrations they have recently built with Google Apps.Parties and Fun
The official After Hours event celebrated technical and artistic innovation and included robots, games and transforming vehicles in addition to a live performance from Jane’s Addiction. Many Google teams and companies attending I/O also threw plenty of great parties at nearby bars and restaurants.Thanks to all the developers who attended Google I/O and made it such an enormous success. Our team loved the chance to chat with many of you and learn more about your businesses and technical challenges.
Hope to see you all at Google I/O 2012!
Want to weigh in on this topic? Discuss on Buzz
Ryan Boyd profile | twitter | events Ryan is a Developer Advocate on the Google Apps Marketplace team, helping businesses build applications integrated into Google Apps. Wearing both engineering and business development hats, youll find Ryan writing code and helping businesses get to market with integrated features. |
Monday, March 9, 2015
Learn about Google Apps Script in NYC
- Gmail Snooze with Apps Script
- Content Approval Workflow
- Vacation calendar management
- Helpdesk Workflow
- Time Booking
What: Apps Script Hackathon
Date: Thursday, August 18th, 2011
Time: 2pm to 7pm EDT
Where: 76 9th Avenue, New York, NY
Register: Space is limited, register here.
For those who cannot attend in person, we invite you to try out a number of self-paced tutorials on the Apps Script documentation site.
Saurabh Gupta profile | twitter | blog Saurabh is a Developer Programs Engineer at Google. He works closely with Google Apps Script developers to help them extend Google Apps. Over the last 10 years, he has worked in the financial services industry in different roles. His current mission is to bring automation and collaboration to Google Apps users. |
Deprecating Tables and Records feeds of the Spreadsheets API
After more than two years in service, we have made the decision to deprecate the Table and Record feeds of the Google Spreadsheets API. Having thoroughly tested these feeds and received lots of your feedback, we feel that the functionality provided by these feeds is something much better satisfied by the List and Cell feeds in the API.
Our deprecation plan for these APIs will keep these feeds in service for an additional year from today’s date.
If you are a current user of the Table and Record feeds, we highly recommend that you take the time to migrate over to the List and Cell feeds. Since the List feed works very similarly to the Records feed, this should be a smooth process.
As always, if you have any questions, feel free to use the Spreadsheets API forum.
Posted by Vic Fryzel, Google Spreadsheets API Team
Want to weigh in on this topic? Discuss on Buzz
Agile Adoption 3 Vital Behaviours
Kicking Apps and Making Names
Way back in the dawn of time, before I joined Google — OK, fine, two months ago — I was a video-game designer. Occasionally, I had to come up with names for people or places. And Im no good at naming things.
So once, instead of manually naming hundreds of towns in a (fictitious) foreign country, I weaseled my way out of creativity by writing a ridiculous set of custom spreadsheet functions. My spreadsheet analyzed a list of real placenames from a similar country, then spit out plausible fake names with the same lexical structure.
It worked — but I was pushing spreadsheet functions so far that the “code” (if you can call it that) became difficult to maintain. At the same time, the reason I used a spreadsheet in the first place was so I could lean on the analytical power of pivot tables.
That’s what made Google Apps Script perfect for revamping the project. With Apps Script, I can still use pivot tables, then do the heavy lifting in JavaScript and package everything up as a tidy web app. I call it Name Generator … because I’m terrible at naming software, too.
Now, before you say, “There’s no way I’d call my daughter Harliance,” remember that the goal wasn’t to produce real names. The goal was to produce names that were good enough for a video game. Perhaps Harliance is a cyborg woman of negotiable virtue in dystopian future-America? You should probably pick your daughter’s name the old-fashioned way.
So let’s look at a few of the techniques that NameGen uses.
1. We start out in Google Sheets, first dropping a list of real names into column A, then slicing it into overlapping three-letter segments using the formula =MID($A2,COLUMN(B2)-1,3)
(that’s the version of the formula you’d use in cell B2; from there, just copy and paste the formula across the rest of the sheet and the cell references will update accordingly). Here’s a sample of one of the spreadsheets so you can see how the data is set up.
2. We then create a pivot table for each column in that first sheet, just summarizing the column by COUNTA
(the number of times each segment occurs). For example, since Lakisha, Nakia, and Nakisha (from our list of real names) share “aki” as letters 2 through 4, the pivot table for Segment 2 shows “aki: 3.”
The plan is that NameGen will randomly pick one of the starting three-letter segments, then look at the last two letters of its selection so that it can find an overlapping segment from the next column. The script then uses the pivot-table statistics to weight its selections toward more common segments. It continues until a segment ends in a blank. This diagram shows how it might build the name Calina:
3. This is where Apps Script takes over. Just once per source list of names, we run the utility function below (shown slightly simplified) to convert the spreadsheet data to a more useful format and store it in ScriptDb. The script can then pull the data from ScriptDb in about 0.5s, versus about 5s to read directly from the spreadsheet. Note that we’re using Script Properties to store the spreadsheet ID rather than cluttering up the code with extra variables.
function updateDb() {
var language = americanFemale;
// Look up the spreadsheet ID in Script Properties, then grab its sheets.
var ssId = ScriptProperties.getProperty(language);
var sheets = SpreadsheetApp.openById(ssId).getSheets();
var dictSize = sheets[0].getLastRow() - 1;
var segment = {};
// Transform each sheet into the segment object we want later.
for (var i = 0; i < sheets.length; i++) {
// Retrieve the list of real names (first loop) or a pivot table.
if (i === 0) {
segment.data = sheets[0].getRange(A:A).getValues();
} else {
segment.data = sheets[i].getDataRange().getValues();
}
// Store other properties so we can retrieve the right record later.
segment.index = i;
segment.language = language;
segment.size = dictSize;
// Save the object as a ScriptDb record, then start the loop again.
ScriptDb.getMyDb().save(segment);
}
}
4. Now, every time the app runs, it jumps straight to the generateNames()
function shown below (again slightly simplified). After it queries the database, it’s straight JavaScript — but one Apps Script–specific trick you’ll notice is that we assemble the data into an array using segments[current.index] = current.data
rather than segments.push(current.data)
. Because ScriptDb returns the records in an unpredictable order, we gave our objects an index
property to store the correct order.
function generateNames(language, numNames) {
// Query the database to find all results for this language.
var results = ScriptDb.getMyDb().query({language: language});
var current = {};
var segments = [];
// Assemble the DB records into an array so we can pass it around.
while (results.hasNext()) {
current = results.next();
segments[current.index] = current.data;
}
var names = [];
var segment = ;
for (var i = 0; i < numNames; i++) {
var name = ;
// For each requested name, pick one segment, making
// sure it overlaps with the previous two letters.
for (var j = 1; j < segments.length; j++) {
segment = randomSegment(segments[j], name);
name = name.slice(0, name.length - 2);
name += segment;
// If the segment wasnt full length (end of a name), done!
if (segment.length < 3) {
break;
}
}
names.push(name);
}
return names;
}
I haven’t explained the randomSegment()
function, but you can probably guess at how it works based on the description above. Still, if you want to dig in further, you can view the full source code here.
5. The only remaining step is to expose the results to the world through a web app. Apps Script provides several ways of doing this; I used Html Service, but didn’t require any of the advanced features. Here’s how little HTML we need to turn NameGen into a functional app:
<html>
<body>
<script>
function sendRequest() {
var language = document.getElementById(language).value;
var numNames = document.getElementById(numNames).value;
google.script.run.withSuccessHandler(updateField).
generateNames(language, numNames);
}
function updateField(names) {
var output = "";
for (var i = 0; i < names.length; i++) {
output += (names[i] + <br/>);
}
document.getElementById(resultsBox).innerHTML = output;
}
</script>
<select id="language">
<option value="americanFemale">American Females</option>
<option value="americanMale">American Males</option>
<option value="american">American Towns</option>
<option value="british">British Towns</option>
<option value="french">French Towns</option>
<option value="irish">Irish Towns</option>
<option value="italian">Italian Towns</option>
<option value="spanish">Spanish Towns</option>
</select>
<select id="numNames">
<option value=1>1</option>
<option value=10 selected>10</option>
<option value=100>100</option>
<option value=1000>1000</option>
</select>
<input id="generateButton" type="button"
value="Generate" onclick="sendRequest()">
<div id="resultsBox">
</div>
</body>
</html>
And presto chango, you have a web app that harnesses the tremendous power of Google’s infrastructure … to produce names that could only ever exist in a parallel universe. It’s like Adriano Celentano’s "Prisencolinensinainciusol" — a convincing rendition of an American pop song, unless you actually speak English, in which case it’s total gibberish.
Dan Lazin profile | twitter Dan is a technical writer on the Developer Relations team for Google Apps Script. Before joining Google, he worked as video-game designer and newspaper reporter. He has bicycled through 17 countries. |
Expensify Accelerates Sign ups with the Provisioning API
Expensify does expense reports that dont suck by importing expenses and receipts from your credit cards and mobile phones, submitting expense reports through email, and reimbursing online with QuickBooks and direct deposit.
The Foundation: Single Sign-On
We were really excited when Google approached us to be a launch partner for their new Google Apps Marketplace because tons of our customers use Google Apps -- including us! We have long wanted to put an Expenses link up there next to Mail and Documents, and now we had our chance.To do that, we installed the JanRain PHP OpenID library with the Google Apps Discovery extension. We’d already implemented Gmail OpenID and ironed out the kinks related to having multiple logins to a single account, so implementing this was a very straightforward process.
Lessons Learned
We quickly learned that while single sign-on is awesome, there was a high drop-off rate among admins installing Expensify into their domain. Digging deeper we determined it was due to the setup process being split: part of the setup was done from within Google Apps, but the final part had to be completed by signing in to our site. Not only that, a major part of the setup process was laboriously entering their employee’s emails. We decided to address each in turn by creating a setup wizard and importing the domain’s email list. We approached this change in two major ways:First, when the Google Apps admin installs the Expensify application, we created a custom configuration step that creates what we call an expense policy. This governs everything from who is part of the company, who can approve expenses, and ultimately who exports to QuickBooks to keep the general ledger in check. Previously this step had to be done after the application was already installed, usually when the admin first signed in. By making this step part of the install process, the entire setup felt much more intuitive and resulted in a higher completion rate.
Second, we used the Zend framework to connect to the Google Apps Provisioning API to fill the new expense policy with a list of all existing employees. This saved a ton of typing and resulted in a vast reduction in the time it took to deploy Expensify to the full company. With everything else in place, the code we used to do this looked something like this:
$oauthOptions = array(All of the users’ names and emails are presented in a table of employees that the person installing the app can use to set roles and quickly build an approval tree for all expenses.
requestScheme => Zend_Oauth::REQUEST_SCHEME_HEADER,
version => 1.0,
signatureMethod => HMAC-SHA1,
consumerKey => $CONSUMER_KEY,
consumerSecret => $CONSUMER_SECRET
);
$consumer = new Zend_Oauth_Consumer($oauthOptions);
$token = new Zend_Oauth_Token_Access();
$httpClient = $token->getHttpClient($oauthOptions);
$service = new Zend_Gdata_Gapps($httpClient, $domain );
$users = $service->retrieveAllUsers();
Once the setup is completed, we automatically create accounts for all of the selected employees and send out a brief email with tips to get started.
Results: 3.5x more users sign up
Overall it was a fast and painless process, has increased the flow of high-quality leads, and has accelerated their rate of converting into real users. Domain admins now sign up 3.5x more users right away than they have been using the previous two-part setup! Feel free to install our app to see how the setup process works, and respond to this post with questions about your implementation -- well try to help out as best we can. And of course if youre still doing your expense reports the old sucky way, please come visit our website (Expensify.com) and wed be happy to help ease your pain. Thanks for reading, let me know if theres anything I can help clarify!Posted by Zhenya Grinshteyn, Expensify
Want to weigh in on this topic? Discuss on Buzz
Google Data Authentication Choices
As developers using the Google Data APIs, one of the first challenges we tackle is learning and sorting through the Google Data authentication schemes. AuthSub, OAuth, ClientLogin, OpenID+OAuth, and so on. Perhaps you may be developing a Google App Engine web application for the first time and want access to a users Google Calendar. Maybe you are building a data processing application that requires access to a Google users data. If you are familiar with the Google Data APIs you likely know that there are many authentication options available depending on the API. So how do you decide? The best place to start is the Getting Started with Account Authorization guide. Also, the Authentication in Google Data Protocol page provides detail on the various authentication methods. This post provides references to many existing resources and some additional things to consider to help you decide which authentication method to use for your application.
Does your application need end user permission to access their Google data?
When developing web applications, developers are sometimes faced with deciding between AuthSub or OAuth. However, the first question should be, "Who will be granting permission to the users Google data?" If the end users are Google Apps users then most likely it will be an administrator granting access. Frequently developers ask the same question in another way, "Should I use 3-legged or 2-legged authentication?" Ideally, from a Google Apps user experience perspective, it is better to use 2-legged OAuth. This way the application is granted access to a users Google Apps data at the administrator level. The end user can start to use the application and the trust has already been established by an administrator. If you are developing a Marketplace application then it is very likely you will not need to engage in the 3-legged authentication dance.However, if you are writing an application that is wide open to anyone with a Google account, let the 3-legged dance begin. Both AuthSub and OAuth for Web Applications are designed for this purpose. However, the behavior and look and feel for each is slightly different. Both ultimately allow the user to grant your application access to their data, but knowing the differences helps you make the best choice. Here is an article that covers the topic and should help you choose. It has color coded highlights that compare the two.
Some general rules follow when choosing an authentication method:
End User | Authentication |
Google User (regular) | AuthSub or 3-Legged OAuth (3LO) |
Googe Apps User (hosted) | 2-Legged OAuth (2LO) |
No end user | 2LO or ClientLogin |
Gadgets
Again there are different options for choosing Google Data authentication from a Gadget container. The first option to consider is OAuth Proxy for Gadgets. OAuth proxy is a 3-legged dance asking the end user for permission to access their data. Once access has been granted then the gadget developer can use the gadgets.io.makeRequest()method to make the Google Data API calls. Most likely you will want the JSON response from the feed so remember to add the ?alt=json URL parameter. For more information and code examples on OAuth Proxy see Writing OAuth Gadgets.For Marketplace Gadgets another authentication option is available and uses OpenID. This option is used to authenticate the user and determine identity only and it does not provide authorization for API access. After the user is authenticated, server-to-server calls are used to make the requests, authorized via 2-legged OAuth and specifying the xoauth_requestor_id based on the authenticated user from OpenID. For more information on this approach see the Marketplace Best Practices page.
Secure Storage of Credentials
Adding layers of security is a common approach to to making data more secure. Google does provide various layers by providing different authentication and authorization methods. Registering web applications, supporting digital certificates, support for industry standards, (SAML, OAuth, OpenID) all help in providing security. However, one of the most common mistakes we can make is not taking care to protect important credentials. When working with Google Data ClientLogin and 2-legged OAuth, these credentials can be keys to the kingdom (e.g. administrator credentials or domain OAuth consumer secret) and therefore should be protected at all costs. Access to these credentials can potentially open the entire Google Apps domain data for the authorized services. This could have tremendous impact especially if you are maintaining 2-legged OAuth credentials for domains that have granted your application access, e.g. a Marketplace application. Therefore it is risky practice to embed them in source code or even a configuration file. Consider using an approach that allows you to enter credentials at runtime and store in memory or use your favorite method for secure storage of these important credentials.Google Apps Marketplace
With the March announcement of the Google Apps Marketplace, the decision making process may have become a little easier. OpenID+OAuth and 2-Legged OAuth are the schemes that are supported and likely will be used if your Marketplace application needs access to Google Apps data; which is very likely. You’ll notice that the Marketplace has embraced the open standards of OpenID and OAuth. While AuthSub and ClientLogin are proprietary to Google, they will likely not be useful in your Marketplace application.ClientLogin
If your application needs to create or make modifications to user accounts (using the Provisioning API) then your only current option is ClientLogin. But a common oversight is to not reuse the access token. Without proper reuse of this token, eventually your application will force the account into a CAPTCHA state which can easily be avoided if the access token is used for subsequent ClientLogin authentication requests. It is best to keep this token cached in memory and renew the token in some configured frequency/timeout.Summary
This post covered some important considerations for selecting a Google Data authentication method for your application. If you are new to Google Data authentications and want to have a better overall understanding then start with the Getting Started with Account Authorization guide. No matter which approach you choose make sure that accessing users data is done is a secure user friendly way.References
Google Data authentication is a vast topic. For your convenience here are a list of resources.Google Data Authentication - Start Here
Getting Started with Account Authorization
Authentication in the Google Data Protocol
OAuth
OAuth in the Google Data Protocol Client Libraries
OAuth for Web Applications
OAuth for Installed Applications
OAuth API Reference
Using OAuth with the Google Data APIs
Google Data API Tips - OAuth
OAuth Playground
OAuth-enabled http test client: oacurl
Gadgets
Authentication for Gadgets
Writing OAuth Gadgets
Fetching Remote Content
Creating a Google Data Gadget
JavaScript client library API reference
Using JSON with Google Data APIs
OAuth Enabled Gadgets on OpenSocial enabled Social Networks
Registration
Registration for Web-Based Applications
OpenID
Federated Login Service for Google Apps
OpenID+OAuth
Sharing and previewing Google Docs in Socialwok: Google Data APIs
Step2 Code Project
OpenID OAuth Extension
ClientLogin
ClientLogin in the Google Data Protocol Client Libraries
ClientLogin for Installed Applications
Google Data API Blog - ClientLogin
AuthSub
AuthSub Authentication for Web Applications
Using AuthSub in the Google Data Protocol JavaScript Client Library
Google Data API Tips - AuthSub
OpenID-based single sign on and OAuth data access for Google Apps
By Jeff Morgan, Senior Technical Consultant at Appirio
Want to weigh in on this topic? Discuss on Buzz