add Tasks API App Engine sample
diff --git a/samples/tasks_appengine/README b/samples/tasks_appengine/README
new file mode 100644
index 0000000..56da2e8
--- /dev/null
+++ b/samples/tasks_appengine/README
@@ -0,0 +1,2 @@
+Sample code for Getting Started with Tasks API on App Engine article.
+http://code.google.com/appengine/articles/python/getting_started_with_tasks_api.html
diff --git a/samples/tasks_appengine/app.yaml b/samples/tasks_appengine/app.yaml
new file mode 100644
index 0000000..35a7dc6
--- /dev/null
+++ b/samples/tasks_appengine/app.yaml
@@ -0,0 +1,12 @@
+application: mytasks
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /oauth2callback
+ script: oauth2client/appengine.py
+- url: /css
+ static_dir: css
+- url: .*
+ script: main.py
diff --git a/samples/tasks_appengine/css/style.css b/samples/tasks_appengine/css/style.css
new file mode 100644
index 0000000..cef1a0b
--- /dev/null
+++ b/samples/tasks_appengine/css/style.css
@@ -0,0 +1,48 @@
+body {
+ width: 600px;
+ margin: auto;
+ margin-top: 48px;
+ margin-bottom: 48px;
+ font-family: 'Coming Soon', serif;
+ font-size: 35px;
+}
+
+ul {
+ -moz-border-radius: 20px;
+ -webkit-border-radius: 20px;
+ border-radius: 20px;
+ background-color: #f8f6c6;
+ background-image: -webkit-linear-gradient(#fdfce8, #f8f6c6);
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ border: 5px solid #e3e2be;
+ -moz-box-shadow: 8px 8px 8px #ccc;
+ -webkit-box-shadow: 8px 8px 8px #ccc;
+ box-shadow: 8px 8px 8px #ccc;
+}
+
+li {
+ padding-left: 20px;
+ border-bottom: 4px solid #e3e2be;
+}
+
+li .check {
+ display: block;
+ float: left;
+ border-right: 8px solid #e3e2be;
+ padding-right: 20px;
+ width: 20px;
+}
+
+li .title {
+ margin-left: 4px;
+ padding-left: 20px;
+ border-left: 4px solid #e3e2be;
+ color: black;
+ text-decoration: none;
+}
+
+li:last-child {
+ border-bottom: none;
+}
diff --git a/samples/tasks_appengine/main.py b/samples/tasks_appengine/main.py
new file mode 100644
index 0000000..54260bd
--- /dev/null
+++ b/samples/tasks_appengine/main.py
@@ -0,0 +1,57 @@
+# Copyright (C) 2011 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from google.appengine.dist import use_library
+use_library('django', '1.2')
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp import template
+from google.appengine.ext.webapp.util import run_wsgi_app
+from apiclient.discovery import build
+import httplib2
+from oauth2client.appengine import OAuth2Decorator
+import settings
+
+decorator = OAuth2Decorator(client_id=settings.CLIENT_ID,
+ client_secret=settings.CLIENT_SECRET,
+ scope=settings.SCOPE,
+ user_agent='mytasks')
+
+
+class MainHandler(webapp.RequestHandler):
+
+ @decorator.oauth_aware
+ def get(self):
+ if decorator.has_credentials():
+ service = build('tasks', 'v1', http=decorator.http())
+ result = service.tasks().list(tasklist='@default').execute()
+ tasks = result.get('items', [])
+ for task in tasks:
+ task['title_short'] = truncate(task['title'], 26)
+ self.response.out.write(template.render('templates/index.html',
+ {'tasks': tasks}))
+ else:
+ url = decorator.authorize_url()
+ self.response.out.write(template.render('templates/index.html',
+ {'tasks': [],
+ 'authorize_url': url}))
+
+
+def truncate(s, l):
+ return s[:l] + '...' if len(s) > l else s
+
+application = webapp.WSGIApplication([('/', MainHandler)], debug=True)
+
+
+def main():
+ run_wsgi_app(application)
diff --git a/samples/tasks_appengine/settings.py.sample b/samples/tasks_appengine/settings.py.sample
new file mode 100644
index 0000000..ee76355
--- /dev/null
+++ b/samples/tasks_appengine/settings.py.sample
@@ -0,0 +1,3 @@
+CLIENT_ID='PASTE_YOUR_GENERATED_CLIENT_ID_HERE'
+CLIENT_SECRET='PASTE_YOUR GENERATED_CLIENT_SECRET_HERE'
+SCOPE='https://www.googleapis.com/auth/tasks.readonly'
diff --git a/samples/tasks_appengine/templates/index.html b/samples/tasks_appengine/templates/index.html
new file mode 100644
index 0000000..7160f0c
--- /dev/null
+++ b/samples/tasks_appengine/templates/index.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<html>
+ <head>
+ <link href="/css/style.css" rel="stylesheet" type="text/cs">
+ <link href="http://fonts.googleapis.com/css?family=Coming+Soon:regular" rel="stylesheet" type="text/css">
+ </head>
+ <body>
+ <ul id="tasklist">
+ {% if tasks|length == 0 %}
+ <li>
+ <span class="check">
+ <span>
+
+ </span>
+ </span>
+ {% if authorize_url %}
+ <a class="title" title="Grant read access to your tasks" href="{{ authorize_url }}">Grant Access</a>
+ {% else %}
+ <a class="title" title="Add more tasks in Gmail" href="http://mail.google.com/">Add More Tasks</a>
+ {% endif %}
+ </li>
+ {% endif %}
+ {% for task in tasks %}
+ <li class="{{ task.status }}">
+ <span class="check">
+ <span>
+ {% if task.status == "completed" %}
+ ✓
+ {% else %}
+
+ {% endif %}
+ </span>
+ </span>
+ <a class="title" title="{{ task.title }}">{{ task.title_short }}</a>
+ </li>
+ {% endfor %}
+ </ul>
+ </body>
+</html>