The last requirement for a channel is a user interface that lets admins add and configure your channel in Support. The admin interface consists of an HTML page with a form to specify the settings of the channel.

The Channel framework lets you iframe your HTML interface into Support. Zendesk needs to know the URL of the HTML file to pull into the iframe. You specify the URL in a manifest file that Zendesk can access on the internet. (The URL of the manifest file itself is specified in the channel installer you create in the next article.)

In this article, you build the admin interface for your community channel. To keep it simple, admins will only have one setting to configure: the id of the community topic where they want new posts to become tickets.

You also create the channel manifest file.

Topics covered:

This is the fourth part of a project to build a channel from scratch:

Disclaimer: Zendesk provides this article for demonstration and instructional purposes only. The channel developed in the article should not be used in a production environment. Zendesk does not provide support for developing a channel.

Creating the route to the admin interface

In this section, you create a route Zendesk can use to request the HTML of your admin interface when an user in Zendesk wants to change a setting in your channel.

You'll create the route first because it receives data from Zendesk that needs to be processed before sending the admin interface to Zendesk.

To create the route to your admin interface

Paste the following route into theservice.pyfile in your project folder and save.

             
...@route('/channels/community/admin_ui',method='POST')defshow_admin_ui():return_url=request.forms['return_url']name=request.forms['name']ifnotname:name='Help Center community channel'topic_id=''if'metadata'inrequest.formsandrequest.forms['metadata']:元数据=request.forms['metadata']元数据=urllib.parse.unquote(元数据)# URL-decode the JSON value元数据=json.loads(元数据)# convert decoded JSON to dicttopic_id=元数据['topic_id']data={'name':name,'topic_id':topic_id,'return_url':return_url}returntemplate('admin',data=data)...

How it works

The route defines a/channels/community/admin_uiURL for the admin interface to be iframed into Zendesk Support. Zendesk makes a POST request for the page so the route only accepts POST requests.

Zendesk sends areturn_urlparameter in the post request. You'll need the URL in a callback to Zendesk to finalize the settings update. Accordingly, theshow_admin_ui()function starts by getting the parameter:

             
return_url=request.forms['return_url']

Likereturn_url, you must includenamein the callback to Zendesk to finalize a settings update or the initial account setup. The initial name can be anything. It will appear in Support as an account name in the Channel Integrations page.

In the initial account setup, Zendesk will send an emptynameparameter in the post request. Accordingly, theshow_admin_ui()function gets thenameparameter and sets it if it's an empty string.

             
name=request.forms['name']ifnotname:name='Help Center community channel'

Next, the function decides what to display in the topic id field in the admin interface. The function begins by setting an empty string for the topic id in case the value hasn't been set yet:

             
topic_id=''

Later, you'll save the topic id entered by a user in the admin UI in the元数据parameter in Zendesk. For now, you check to see if the post request from Zendesk contains a元数据field and, if so, whether it's defined. The元数据field is not included in the initial post request so you have to check for it.

If the元数据field is present and defined, the function decodes its value and gets the existingtopic_idsetting from it:

             
if'metadata'inrequest.formsandrequest.forms['metadata']:元数据=request.forms['metadata']元数据=urllib.parse.unquote(元数据)# URL-decode the JSON value元数据=json.loads(元数据)# convert decoded JSON to dicttopic_id=元数据['topic_id']

Finally, the function passes thename,return_url, andtopic_idvalues to the admin template to create the admin UI:

             
data={'name':name,'topic_id':topic_id,'return_url':return_url}returntemplate('admin',data=data)

You build the admin template next.

Creating the admin interface

In this section, you create the HTML admin interface for your channel. An admin interface consists of an HTML page with a form to specify the settings of the channel.

在前一节中经过的路线name,return_url, andtopic_idparameters to a template named 'admin'. Accordingly, you create a template namedadminto accept the parameters and create the HTML page at runtime.

To create the admin interface

  1. Create a subfolder namedviewsin your project folder.

  2. In theviewsfolder, create a text file nameadmin.tpl.

    The .tpl extension denotes a Bottle template file. It's used to build an HTML page at runtime.

  3. Add the following markup to theadmin.tplfile:

                   
    <html><body><styletype="text/css">body{padding:20px;margin:10px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;}input{margin-bottom:10px;}style><h3>Community Channelh3><formmethod="post"action="/channels/community/settings"><divclass="form-group"><labelfor="topic_id_field">Topic IDlabel><br><inputtype="text"id="topic_id_field"name="topic_id"value="{{data['topic_id']}}"><br><inputtype="hidden"name="name"value="{{data['name']}}"><inputtype="hidden"name="return_url"value="{{data['return_url']}}"><inputtype="submit"value="Save"style="font-size:110%;">div>form>body>html>

    The markup creates a UI that looks as follows when run and previewed in Postman:

    The form lets an admin specify a topic id. The form uses a template expression to make thevalueattribute dynamic:

                   
    value="{{data['topic_id']}}"

    If the admin has already set a topic id, it's displayed in the form field. Otherwise, an empty string is displayed.

    Thenameandreturn_urlare embedded in the form as hidden fields:

                   
    <inputtype="hidden"name="name"value="{{data['name']}}"><inputtype="hidden"name="return_url"value="{{data['return_url']}}">

    In other words, these values will be sent back to your service when the admin submits the form. You have to preserve the values through the transaction because you have to use them in a callback to Zendesk to finalize the settings update.

    The form'sactionattribute indicates that the form submits the settings data to your integration service at/channels/community/settings. You build the route to process the form data in the next section.

Processing the admin settings

Your admin interface contains a form with anaction提交的属性设置ata with some hidden field values to your integration service at/channels/community/settings. The form data consists of following fields:topic_id,name, andreturn_url.

Your integration service must perform the following tasks when it receives the form data from your admin interface:

  1. Validate the topic id
  2. Save the topic id in the元数据parameter in Zendesk
  3. Include thenameparameter with the元数据parameter in the callback to the Zendeskreturn_urlto finalize the update process

In this section, you add a/channels/community/settings路线your integration service to process the form data received from your admin interface.

To process the admin data

  1. Paste the following route into theservice.pyfile in your project folder and save.

                   
    ...@route('/channels/community/settings',method='POST')defprocess_admin_settings():topic_id=request.forms['topic_id']ifnottopic_idornottopic_id.isdigit():returntemplate('admin',data={'topic_id':'invalid'})# set metadata元数据={'topic_id':topic_id}# convert metadata dict to JSON, then URL encode元数据=urllib.parse.quote(json.dumps(元数据))data={'return_url':request.forms['return_url'],'name':request.forms['name'],'metadata':元数据}returntemplate('admin_callback',data=data)...

    Theprocess_admin_settings()function receives thetopic_idvalue from your admin interface, performs some basic validation, and saves the value in the元数据parameter in Zendesk.

    The route then retrieves thenameandreturn_urlvalues you stored in hidden form fields in the admin interface and passes them along with元数据to a template namedadmin_callback, which will make a callback to the Zendeskreturn_urlto finalize the update process. You'll create the template in the next steps.

  2. In theviewsfolder, create a file nameadmin_callback.tpl.

  3. Add the following markup to theadmin_callback.tplfile:

                   
    <html><body><formid="finish"method="post"action="{{data['return_url']}}"><inputtype="hidden"name="name"value="{{data['name']}}"><inputtype="hidden"name="元数据"value="{{data['元数据']}}">form><scripttype="text/javascript">// post the formvarform=document.forms['finish'];form.submit();script>body>html>

    The template defines a self-submitting HTML form that posts to the Zendeskreturn_url. The form is never displayed in Zendesk. Instead, it submits thenameand元数据parameters on load in Zendesk. Sending an HTML form instead of the data directly might seem unusual but it allows the data to be submitted from the same origin.

    Thenameparameter is required to finalize the update process.

Creating the channel manifest

The manifest file describes the capabilities of your channel and provides information that Zendesk Support needs to interact with it. You must make the manifest available at a known public URL so that Zendesk can access it.

In this section, you create the manifest file and make it available at a public URL.

To create the manifest file

  1. Create a file calledintegration_manifest.jsonand save it in the same folder as yourservice.pyfile.

  2. Add the following JSON object to theintegration_manifest.jsonfile and save it:

                   
    {"name":"Community Channel","id":"my-community-channel-abc123","version":"1.0.0","urls":{"admin_ui":"{glitch_url}/channels/community/admin_ui","pull_url":"{glitch_url}/channels/community/pull","channelback_url":"{glitch_url}/channels/community/channelback"}}

    SeeManifest Formatin the dev docs for details about the property values.

    Theidmust be unique for each channel. For the tutorial, include some random numbers or letters.

    Later in the tutorial, you'll replace{glitch_url}with the URL for your channel in Glitch.

  3. In yourservice.pyfile, add the following route:

                   
    ...@route('/channels/community/manifest')defserve_manifest():file=Path('integration_manifest.json')withfile.open(mode='r')asf:manifest=json.load(f)response.headers['Content-Type']='application/json'returnmanifest...

    Theserve_manifest()function opens the JSON file, reads it, and returns the contents in the HTTP response.

You finished the admin interface and manifest for your channel. All that's left is to deploy the channel.

Next part:Deploying the channel