This document shows how to build an App Engine app in Python that sends
annotated emails to users asking to confirm a mailing list subscription directly
from their inbox and collects the subscriptions in Datastore.

## Prerequisites and project setup

This guide assumes you have already installed the [App Engine SDK](https://developers.google.com/appengine)
and know how to create, run, and publish App Engine projects.

First, create a directory for your project. Put all of the files for your
application in this directory.

Copy the following code to a file named `app.yaml` and replace the
`{{ APPID }}` placeholder
with your unique App Engine app ID:

    application: {{ APPID }}
    version: 1
    runtime: python27
    api_version: 1
    threadsafe: true

    handlers:
    - url: /.*
      script: main.app

    libraries:
    - name: jinja2
      version: latest

Create a file named `main.py` in your App Engine project folder and copy
the following code to set up the handlers for collecting and listing
subscriptions, and for sending annotated emails:

    import webapp2

    from emailsender import EmailSender
    from subscribe import SubscribeHandler

    app = webapp2.WSGIApplication([('/', SubscribeHandler), ('/email', EmailSender)], debug=True)

## Add structured data to the email

Start with an email asking the user to confirm a mailing list subscription:

    <html>
      <head>
        <title>Please confirm your subscription to Mailing-List XYZ?</title>
      </head>
      <body>
        <p>
          Dear John, please confirm that you wish to be subscribed to the
          mailing list XYZ
        </p>
      </body>
    </html>

You can add structured data in one of the supported formats
([JSON-LD](https://developers.google.com/workspace/gmail/markup/reference/formats/json-ld) or
[Microdata](https://developers.google.com/workspace/gmail/markup/reference/formats/microdata)) to the `head`
of the email to define the email message and add a
[OneClickAction](https://developers.google.com/workspace/gmail/markup/reference/one-click-action).
Gmail supports `OneClickAction` and displays UI elements that allow
users to confirm their subscription from their inbox.

Copy the following markup into a file named `mail_template.html`:

### JSON-LD

    <html>
      <head>
      <title>Please confirm your subscription to Mailing-List XYZ?</title>
      </head>
      <body>
        <script type="application/ld+json">
        {
          "@context": "http://schema.org",
          "@type": "EmailMessage",
          "potentialAction": {
            "@type": "ConfirmAction",
            "name": "Confirm Subscription",
            "handler": {
              "@type": "HttpActionHandler",
              "url": "{{ confirm_url }}",
              "method": "http://schema.org/HttpRequestMethod/POST"
            }
          },
          "description": "Confirm subscription to mailing list XYZ"
        }
        </script>
        <p>
          Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
        </p>
      </body>
    </html>

### Microdata

    <html>
      <head>
        <title>Please confirm your subscription to Mailing-List XYZ?</title>
      </head>
      <body>
        <div itemscope itemtype="http://schema.org/EmailMessage">
          <div itemprop="potentialAction" itemscope itemtype="http://schema.org/ConfirmAction">
            <meta itemprop="name" content="Confirm Subscription"/>
            <div itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler">
              <meta itemprop="url" content="{{ confirm_url }}"/>
              <link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"/>
            </div>
          </div>
          <meta itemprop="description" content="Confirm subscription to mailing list XYZ"/>
        </div>
        <p>
          Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
        </p>
      </body>
    </html>

The preceding structured data describes a mailing list called "XYZ" and a
`ConfirmAction`. The handler for the action is an `HttpActionHandler` that sends
POST requests to the URL specified in the `url` property.

**Important** : In the preceding code, replace the
`{{ confirm_url }}`
placeholder with the URL of your App Engine app (such as
`https://mailing-list.appspot.com`) before sending any emails. The code
described in the following steps handles this replacement.

## Send subscription requests to users

Copy the following code into a file named `emailsender.py` in the
App Engine project folder:

    import jinja2
    import os
    import webapp2

    from google.appengine.api import mail
    from google.appengine.api import users

    from urlparse import urlparse

    class EmailSender(webapp2.RequestHandler):

      def get(self):
        # require users to be logged in to send emails
        user = users.get_current_user()
        if not user:
          self.redirect(users.create_login_url(self.request.uri))
          return

        email = user.email()

        # The confirm url corresponds to the App Engine app url
        pr = urlparse(self.request.url)
        confirm_url = '%s://%s?user=%s' % (pr.scheme, pr.netloc, user.user_id())

        # load the email template and replace the placeholder with the confirm url
        jinja_environment = jinja2.Environment(
            loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
        template = jinja_environment.get_template('mail_template.html')
        email_body = template.render({'confirm_url': confirm_url})

        message = mail.EmailMessage(
            sender = email,
            to = email,
            subject = 'Please confirm your subscription to Mailing-List XYZ',
            html = email_body)

        try:
          message.send()
          self.response.write('OK')
        except:
          self.error(500)

The `EmailSender` class requires the user to be logged in so that their email
address can be retrieved. Then, it loads the email body from
`mail_template.html`, replaces the `confirm_url` placeholder in it with the root
URL of the App Engine app (`https://APP-ID.appspot.com`), and sends the
email to the logged-in user.

## Collect and list subscriptions

Copy the following code into a file named `subscribe.py` in the
App Engine project folder:

    import webapp2

    from emailsender import EmailSender
    from google.appengine.ext import db


    class SubscribeHandler(webapp2.RequestHandler):

      def post(self):
        user_id = self.request.get('user')

        # insert the subscription into the Datastore
        subscription = Subscription(user_id=user_id)
        subscription.put()

      def get(self):
        # retrieve up to 1000 subscriptions from the Datastore
        subscriptions = Subscription.all().fetch(1000)

        if not subscriptions:
          self.response.write('No subscriptions')
          return

        count = len(subscriptions)

        for s in subscriptions:
          self.response.write('%s subscribed<br/>' % (s.user_id))

        self.response.write('<br/>')
        self.response.write('%d subscriptions.' % (count))


    class Subscription(db.Model):
        user_id = db.TextProperty(required=True)

The `SubscribeHandler` class handles both `POST` and `GET` requests sent to the
app root URL (`https://APP-ID.appspot.com`). `POST` requests are used by
Gmail to insert new subscriptions including the `user_id`
parameter that corresponds to the user, as in the following example:

    https://subscribe.appspot.com/?user_id=123abcd

The request handler checks that the required `user_id` is defined and stores
the subscription in
[Datastore](https://developers.google.com/appengine/docs/python/gettingstartedpython27/usingdatastore).
This sends an `HTTP 200` response code back to Gmail to signal
the successful request. If the request doesn't include the required field, the
request handler returns an `HTTP 400` response code, signaling the invalid
request.

`GET` requests to the app root URL list the subscriptions that have been
collected. The request handler fetches all subscriptions from Datastore
and displays them on the page along with a counter.

## Test the app

[Deploy your app to App Engine](https://developers.google.com/appengine/docs/python/gettingstartedpython27/uploading)
and visit `https://APP-ID.appspot.com/email` (replace `APP-ID` with your
App Engine app ID) to send the annotated email to yourself.

![Actions in Gmail](https://developers.google.com/static/workspace/gmail/markup/images/actions-one-click-action.png)

After deploying your app and inserting subscriptions, visit your app at
`https://APP-ID.appspot.com` to view a page summarizing the subscriptions.