Integrating Facebook Instant Articles into eZ Publish / eZ Platform
By: Ernesto Buenrostro | January 8, 2018 | Business solutions, eZ Publish development tips, and Web solutions
Facebook's solution to speed up mobile web page loading is called Facebook Instant Articles (FBIA), and it's only available to Facebook app users. Here's a look at how to integrate FBIA into eZ Publish / eZ Platform.
What is Facebook Instant Articles
Facebook Instant Articles is a new way to deliver fast and interactive articles on Facebook. When a user shares an article on Facebook and someone clicks the shared link from within the Facebook mobile app, the FBIA version of the article will load.
Facebook Instant Articles require a custom layout because Facebook uses an HTML5 subset to render the article content, and Facebook requires that publishers send the HTML source of the article to its servers before it can be published to its platform. Having the HTML stored in its servers allows Facebook to deliver the content faster.
Page elements
Some of the HTML tags need special formatting; for example, the <img> and <video> tags need to be wrapped in <figure> tags.
<!-- An image within your article --> <figure> <img src="http://example.com/images/img.jpg" /> <figcaption>The image caption</figcaption> </figure> <!-- A video within your article --> <figure> <video> <source src="http://example.com/videos/video.mp4" type="video/mp4" /> </video> </figure>
In FBIA, there are no stylesheets or JavaScript, but it provides some rich-media elements, such as:
- Slideshows
- Interactive maps
- Geotagged media
- Likes and comments on media
There are guidelines and code samples to help put Facebook Instant Articles HTML in place.
You style the articles using the Facebook interface, which offers a limited set of style options.
Integrating your articles with Facebook
Facebook offers plenty of options to publish articles to its platform:
- Crawler ingestion: Facebook's crawler visits your article when someone shares its URL for the first time on Facebook. If it finds the ia:markup_url meta tag, it will automatically ingest the Instant Article from the provided URL.
- API: Instant Articles API allows you to create, publish, update, and delete Instant Articles immediately, whenever there is a change to an article on your site.
- SDK: The Facebook Instant Articles SDK for PHP provides a native interface for creating and publishing Instant Articles. This ready-to-go set of tools provides the same functionality as the API.
- RSS: New stories will be automatically syndicated as Instant Articles; this will need to be an Instant Articles-specific RSS feed. Facebook recommends that your RSS feed include only articles that have been created or updated in the last hour and should contain a maximum of 100 articles.
We use the Facebook API to give us more control over creating, publishing, updating, and deleting Instant Articles from the Facebook servers.
The only content we send to Facebook's servers is the HTML source code; the assets remain on our servers.
Before we can start the FBIA integration, we must sign up, get a Facebook page ID, get an app ID, and generate an access token (Long-Lived Token).
After we have our Page ID and the access token, we're ready to start publishing articles to the FBIA development area.
For Facebook Instant Articles, there are separate development and production areas. Once you have signed up and generated your access token, page ID, and app ID, you can start to publish your articles to the development area.
In order to access the production area and publish your FBIA articles, you need to have your articles reviewed and approved by Facebook.
Formatting Facebook Instant Article pages in eZ Publish / eZ Platform
We are going to create a new page layout, which will be used to render only the article content. Menus, sidebars, and similar elements are not supported.
Edit the file "settings/override/layout.ini.append.php" and add the following configuration to define our new pagelayout and also set UseAccessPass=false to exclude the layout path in links.
<?php /* #?ini charset="utf-8"? ... [fia] PageLayout=pagelayout_fia.tpl UseAccessPass=false ... */ ?>
On our page layout template we will remove the page layout prefix layout/set/fia/ from the article URL. Facebook will use this URL to direct users who aren't using the Facebook app to your public article.
Our page layout content will look like:
{def $canonical_url = '' } {if $module_result.uri|begins_with( '/layout/set/fia/' )} {set $canonical_url = concat( 'http://www.example.com/', $module_result.uri|extract(16) )} {else} {set $canonical_url = concat( 'http://www.example.com/', $module_result.uri )} {/if} <!doctype html> <html lang="en" prefix="op: http://media.facebook.com/op#"> <head> <meta charset="utf-8"> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <meta property="op:markup_version" content="v1.0"> <link rel="canonical" href="{$canonical_url|wash()}"> </head> <body> <article> {$module_result.content} </article> </body> </html>
As you can see, it contains nothing but a few tags wrapping the article content. Also, note that we are including the canonical URL; this is very important as it's the ID to publish, edit, or delete an article from Facebook.
Then, we need to add the necessary overrides for our articles.
This will print the article title, publish date, article content, and a footer containing a list of related articles.
<header> {* The title shown in FB Instant Article *} <h1>{$node.data_map.title.content|wash()}</h1> {def $publishTime = $node.data_map.publish_date.content.timestamp } <time class="op-published" datetime="{$publishTime|datetime('custom','%c')}">{$publishTime|datetime('custom','%F %j%S, %Y')}</time> </header> {* article body *} {$node.data_map.body.content.output.output_text} <footer> <ul class="op-related-articles"> ... </ul> </footer>
We also need to make sure our image template renders the <img> tag in the required format.
We will override the image.tpl with the following code:
<figure> <img src="{$image.data_map.image.content.large.full_path|ezroot('no')}" /> <figcaption>{$image.name|wash()}</figcaption> </figure>
After we have the templates ready, we need to integrate the FBIA API into the eZ Publish / eZ Platform CMS. For this we will use a workflow triggered by the content publish event.
First, we need to create an event that will fetch the rendered content and push it to the Facebook servers. We create a new file "eventtypes/event/publishfia/publishfiatype.php" inside our extension folder.
When our event runs, we fetch the article's FBIA HTML source with an HTTP call. Then, we will push the source to Facebook using cURL, with the page ID and the Facebook access token.
<?php class PublishFIAType extends eZWorkflowEventType { const WORKFLOW_TYPE_STRING = "publishfia"; private $apiBaseUrl; private $accessToken; public function __construct() { parent::__construct( self::WORKFLOW_TYPE_STRING, 'Publish FB Instant Article' ); $this->apiBaseUrl = 'https://graph.facebook.com/v2.7/'; $this->accessToken = '<Your access token>'; $this->pageId = '<your page id>'; $this->siteURL = 'http://www.example.com'; } /** * Publish the Article to FBIA server * * @param eZWorkflowProcess $process * @param eZWorkflowEvent $event * @return int */ public function execute( $process, $event ) { $parameters = $process->attribute( 'parameter_list' ); // Get the published object $content = eZFunctionHandler::execute( 'content', 'object', [ 'object_id' => $parameters['object_id'] ] ); /* @var $content eZContentObject */ if ( $content->attribute( 'class_identifier' ) === 'article' ) { // Fetch the article content $httpTool = eZHTTPTool::instance(); $urlAlias = $content->attribute( 'main_node' )->attribute( 'url_alias' ); $fiaLayoutURL = $this->siteURL . '/layout/set/fia/' . $urlAlias; $pageContent = $this->httpTool->getDataByURL( $fiaLayoutURL ); // Push the content to FBIA $this->createOrUpdateArticle( $htmlSource ); } return eZWorkflowType::STATUS_ACCEPTED; } /** * Create or update a new FBIA * * @param string $htmlSource HTML source to push to Facebook * * @return array */ public function createOrUpdateArticle( $htmlSource ) { $data = [ 'access_token' => $this->accessToken, 'html_source' => $htmlSource, ]; $method = $this->pageId . '/instant_articles'; $curlHandle = curl_init(); curl_setopt( $curlHandle, 'CURLOPT_RETURNTRANSFER', true ); curl_setopt( $curlHandle, 'CURLOPT_SSL_VERIFYPEER', false ); curl_setopt( $curlHandle, 'CURLOPT_SSL_VERIFYHOST', false ); curl_setopt( $curlHandle, 'CURLOPT_POST', true ); curl_setopt( $curlHandle, 'CURLOPT_POSTFIELDS', $data ); curl_setopt( $curlHandle, 'CURLOPT_URL', "{$this->apiBaseUrl}{$method}" ); $curlResponse = curl_exec( $curlHandle ); $curlErrorNo = curl_errno( $curlHandle ); if ( $curlErrorNo ) { // log the error } curl_close( $curlHandle ); $response = false; if ( $curlResponse ) { $arrayResponse = json_decode( $curlResponse, true ); $response = isset( $arrayResponse ) ? $arrayResponse : $curlResponse; } return $response; } } PublishFIAType::registerEventType( PublishFIAType::WORKFLOW_TYPE_STRING, 'PublishFIAType' );
Register the event in an override of workflow.ini.
[EventSettings] ExtensionDirectories[]=my_extension AvailableEventTypes[]=event_publishfia
After this, we need to regenerate the autoloads and clear the cache.
Configuring the eZ workflow
Now, we need to attach the event to the publishing workflow. To do this, go to the "Setup" section and choose "Workflows" from the left sidebar.
- Click on "Standard", and then on "New workflow".
- Name it "After publish".
- In the select box, pick the event that was just created.
- Click "Add event", then OK.
- Go to "Triggers" section on the left navbar.
- In the displayed list, select the "After publish" workflow in the "content / publish / after" trigger.
- Apply the changes.
The workflow is now ready. Every time an editor publishes a new article in the CMS, it will be published to the Facebook servers.
This is just a basic integration of Facebook Instant Articles. It needs to be extended to handle additional edit actions, such as article renaming, articles moved to a different location, etc., gracefully.
On very active sites you should consider publishing articles in batches, so you don't slow down the server. This can be achieved using a queue such as the Mugo Queue, and using a cron job running every 5 minutes.