مثال على الخدمة الشاملة

توضّح هذه المقالة كيفية إنشاء تطبيق على App Engine بلغة Python يُرسِل رسائل إلكترونية مُشارَك فيها تعليقات توضيحية إلى المستخدمين يطلب منهم تأكيد اشتراك في قائمة بريدية مباشرةً من بريدهم الوارد ويجمع الاشتراكات في Datastore.

المتطلّبات الأساسية وإعداد المشروع

يفترض هذا الدليل أنّك سبق لك تثبيت حزمة تطوير برامج App Engine وأنّك تعرف كيفية إنشاء مشاريع App Engine وتشغيلها ونشرها.

أولاً، أنشئ دليلاً لمشروعك. ضَع جميع ملفات تطبيقك في هذا الدليل.

انسخ الرمز التالي إلى ملف باسم app.yaml واستبدِل العنصر النائب {{ APPID }} بمعرّف تطبيقك الفريد على App Engine:

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

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

libraries:
- name: jinja2
  version: latest

أنشئ ملفًا باسم main.py في مجلد مشروعك على App Engine وانسخ الرمز التالي لإعداد معالجات جمع الاشتراكات وعرضها وإرسال رسائل إلكترونية مُشارَك فيها تعليقات توضيحية:

import webapp2

from emailsender import EmailSender
from subscribe import SubscribeHandler

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

إضافة بيانات منظَّمة إلى الرسالة الإلكترونية

لنبدأ برسالة إلكترونية بسيطة جدًا تطلب من المستخدم تأكيد اشتراك في قائمة بريدية:

<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>

يمكنك إضافة بيانات منظَّمة بأحد التنسيقات المتوافقة (JSON-LD أو البيانات الجزئية) إلى head من الرسالة الإلكترونية لتحديد المطعم وإضافة OneClickAction. يتيح Gmail استخدام OneClickAction ويعرض واجهة مستخدم محدّدة للمستخدمين للسماح لهم بتأكيد اشتراكهم من بريدهم الوارد.

انسخ العلامة التالية إلى ملف باسم 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>

البيانات الجزئية

<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="Approve Expense"/>
        <div itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler">
          <link itemprop="url" href="https://myexpenses.com/approve?expenseId=abc123"/>
          <meta itemprop="url" content="{{ confirm_url }}"/>
          <link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"/>
        </div>
      </div>
      <meta itemprop="description" content="Approval request for John's $10.13 expense for office supplies"/>
    </div>
    <p>
      Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
    </p>
  </body>
</html>

تصف البيانات المنظَّمة أعلاه قائمة بريدية باسم "XYZ" وConfirmAction. معالِج الإجراء هو HttpActionHandler يُرسِل طلبات POST إلى عنوان URL المحدّد في سمة url.

إرسال طلبات الاشتراك إلى المستخدمين

انسخ الرمز التالي إلى ملف باسم emailsender.py في مجلد مشروع App Engine:

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)

تتطلّب فئة EmailSender أن يكون المستخدم مسجّلاً الدخول حتى يمكن استرداد عنوان بريده الإلكتروني. بعد ذلك، يتم تحميل نص الرسالة الإلكترونية من mail_template.html، واستبدال العنصر النائب confirm_url فيه بعنوان URL الجذر لتطبيق App Engine (https://APP-ID.appspot.com)، وإرسال الرسالة الإلكترونية إلى المستخدم الذي سجّل الدخول حاليًا باسمه.

جمع الاشتراكات وإدراجها

انسخ الرمز التالي إلى ملف باسم subscribe.py في مجلد مشروع App Engine:

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)

مَعلمة SubscribeHandlerclass listens to bothPOSTandGETrequests sent to the app root url (https://APP-ID.appspot.com).POSTrequests are used by Gmail to insert new subscriptions including theuser_id` التي تتوافق مع المستخدم، كما هو موضّح في المثال التالي:

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

يتحقّق معالِج الطلبات ببساطة من أنّه تمّ تحديد معرّف المستخدم المطلوب، ثمّ يخزّن الاشتراك في Datastore. يؤدي ذلك إلى إرسال رمز الاستجابة HTTP 200 مرة أخرى إلى Gmail للإشارة إلى نجاح الطلب. في حال عدم تضمين الطلب الحقل المطلوب، سيعرض معالِج الطلب رمز استجابة HTTP 400، ما يشير إلى أنّ الطلب غير صالح.

GET يتم استخدام طلبات عنوان URL الجذر للتطبيق لعرض الاشتراكات التي تم جمعها. يُجلب معالِج الطلبات أولاً جميع الاشتراكات من قاعدة البيانات ثم يطبعها في الصفحة مع عدّاد بسيط.

اختبار التطبيق

نشر تطبيقك على App Engine وزيارة https://APP-ID.appspot.com/email (استبدِل APP-ID بمعرّف تطبيقك على App Engine) لإرسال الرسالة الإلكترونية المُشارَك فيها تعليقات توضيحية إلى نفسك

الإجراءات في Gmail

بعد نشر تطبيقك وإدراج بعض الاشتراكات، انتقِل إلى تطبيقك على الرابط https://APP-ID.appspot.com للاطّلاع على صفحة تلخّص الاشتراكات.