Simple Machines Community Forum

SMF Community Helpers => Customization Development => Topic started by: nend on October 29, 2016, 12:09:10 AM

Title: [MOD][WIP]Accelerated Mobile Pages
Post by: nend on October 29, 2016, 12:09:10 AM
Giving this away to anyone that wants it.

Sources/Amp.php
Code: [Select]
<?php

if (!defined('SMF'))
die('Hacking attempt...');

function 
AmpMain() {
if (isset($_REQUEST['t']))
AmpTopic($_REQUEST['t']);
elseif (isset($_REQUEST['m']))
AmpMedia($_REQUEST['m']);
else
si404();
}

function 
AmpTopic($id) {

global $context$smcFunc$scripturl$boardurl$modSettings;

loadTemplate('Amp');
$context['template_layers'] = array('html');
$context['sub_template'] = 'AmpTopic';
$modSettings['disableQueryCheck'] = 1;
$request $smcFunc['db_query']('''
SELECT
m.poster_time, m.subject, m.id_topic, m.id_member, m.id_msg, b.id_board, b.name AS board_name,
IFNULL(mem.real_name, m.poster_name) AS poster_name, m.body, b.thumb_board as thumb, m.modified_time,
(SELECT id_attach FROM {db_prefix}attachments WHERE id_thumb != 0 AND {db_prefix}attachments.id_msg = m.id_msg ORDER BY id_attach LIMIT 1) AS id_attach
FROM {db_prefix}topics AS t
INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg)
INNER JOIN {db_prefix}boards AS b ON (b.id_board = t.id_board)
LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
WHERE {query_wanna_see_board} AND t.id_topic = {int:id}
LIMIT 1' 
,
array(
'id' => (int) $id
)
);
$context['amp'] = $smcFunc['db_fetch_assoc']($request);

if ($context['amp']) {
$context['amp']['subject'] = censorText($context['amp']['subject']);
$context['page_title'] = $context['amp']['subject'];
$context['canonical'] = $scripturl '?topic=' $context['amp']['id_topic'];
$context['posted'] = date('Y-m-d\TH:i:s'$context['amp']['poster_time']);
$context['modified'] = date('Y-m-d\TH:i:s', empty($context['amp']['modified_time']) ? $context['amp']['poster_time'] : $context['amp']['modified_time']);
$context['head_img'] = false;
if (preg_match('[smg id=([0-9]+)(.*)]'$context['amp']['body'], $match))
$context['head_img'] = $context['rel_img'] = $scripturl.'?action=media;sa=media;in='.$match[1].';preview';
elseif (preg_match('#\[img(.*)\](.*)\[\/img\]#Ui'$context['amp']['body'], $match))
$context['head_img'] = $context['rel_img'] = $match[2];
elseif (isset($context['amp']['id_attach']) && $context['amp']['id_attach'] != 0)
$context['head_img'] = $context['rel_img'] = $scripturl '?action=dlattach;topic='.$context['amp']['id_topic'].';attach='.$context['amp']['id_attach'].';image';
elseif (isset($context['amp']['thumb']) && $context['amp']['thumb'] != '')
$context['rel_img'] = $boardurl.'/img/b/sm/'.$context['amp']['thumb'];
else
$context['rel_img'] = $boardurl.'/img/viral/site_thumb.png';

$context['amp']['body'] = amp_rewrite(parse_bbc($context['amp']['body']));
} else
si404();
}

function 
AmpMedia($id) {

global $context$smcFunc$scripturl$boardurl;

loadTemplate('Amp');
$context['template_layers'] = array('html');
$context['sub_template'] = 'AmpMedia';

$request $smcFunc['db_query']('','
SELECT m.id_media, m.id_member, m.title, m.description, m.time_added, m.time_added AS posted_on, m.id_preview, m.type,
IFNULL(mem.real_name, m.member_name) AS poster_name, m.id_thumb, a.name AS album_name, m.album_id, m.last_edited
FROM {db_prefix}aeva_media as m
INNER JOIN {db_prefix}aeva_albums as a ON (a.id_album = m.album_id)
LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = m.id_member)
WHERE FIND_IN_SET(-1, a.access) AND m.id_media = {int:id}
ORDER BY m.time_added DESC
LIMIT 1'
,
array(
'id' => (int) $id
)
);
$context['amp'] = $smcFunc['db_fetch_assoc']($request);

if ($context['amp']) {
$context['amp']['subject'] = censorText($context['amp']['title']);
$context['page_title'] = $context['amp']['title'];
$context['canonical'] = $scripturl '?action=media;sa=item;in=' $context['amp']['id_media'];
$context['posted'] = date('Y-m-d\TH:i:s'$context['amp']['posted_on']);
$context['modified'] = date('Y-m-d\TH:i:s', empty($context['amp']['last_edited']) ? $context['amp']['posted_on'] : $context['amp']['last_edited']);
$context['amp']['body'] = amp_rewrite(parse_bbc($context['amp']['description']));
$context['rel_img'] = empty($context['amp']['id_preview']) ? $boardurl.'/img/viral/site_thumb.png' $scripturl.'?action=media;sa=media;in='.$context['amp']['id_media'].';preview';
$context['head_img'] = empty($context['amp']['id_preview']) ? false $scripturl.'?action=media;sa=media;in='.$context['amp']['id_media'].';preview';
} else
si404();

}

function 
amp_rewrite($data) {

/*
 * This is resource intensive, especially since we parse the BCC and
 * now we are doing more just to make sure the markup is up to AMP standard.
 * Good thing it is just one post.
 */

// This does happen, empty media descriptions, so we return a empty string.
if (empty($data))
return('');

// Lets use the DOM first, the server will thank us for it.
libxml_use_internal_errors(true);
$DOM = new DOMDocument();
$DOM->loadHTML($data);
$XPath = new DOMXPath($DOM);

// Got class but no style, no borders no limits :P
foreach(array('style','border') as $attribute)
foreach($XPath->query('//*[@'.$attribute.']') as $item)
$item->removeAttribute($attribute);

// Unallowed stuff, button is allowed but figured it looked ugly with it and without the other stuff
foreach(array('script','frame','frameset','object','param','applet','embed','form','input','textarea','select','option','button','iframe','video','audio') as $item)
while($node $XPath->query('//'.$item)->item(0))
$node->parentNode->removeChild($node);

// Put the stuff back.
$data $DOM->saveHTML();

// Other stuff below, <amp- is not valid HTML, that is why we can't do it in the DOM. :(

//Some stuff needs to be done one at a time, borrowed from SMF.
$pos = -1;
while ($pos !== false){
$last_pos = isset($last_pos) ? max($pos$last_pos) : $pos;
$pos strpos($data'<'$pos 1);
if ($pos === false || $last_pos $pos)
$pos strlen($data) + 1;
if ($last_pos $pos 1){
$last_pos max($last_pos0);
$str substr($data$last_pos$pos $last_pos);

// Image is *
$str preg_replace('~<img (.*?)class="(emoji|smiley)\b.*?>
~si', '<amp-img width="30" height="30" $1></amp-img>', $str);
$str = preg_replace('~<img (.*?)>~si', '<center><amp-img width="300" height="200" $1></amp-img></center>', $str);

if ($str != substr($data, $last_pos, $pos - $last_pos)){
$data = substr($data, 0, $last_pos) . $str . substr($data, $pos);
$old_pos = strlen($data) + $last_pos;
$pos = strpos($data, '<', $last_pos);
$pos = $pos === false ? $old_pos : min($pos, $old_pos);
}
}
if ($pos >= strlen($data) - 1)
break;
}

// This could be better, when I get the chance to write the rest.
// This requires https. :(
/* $ctamp = array('video','audio','iframe');
foreach ($ctamp as $item)
$data = str_replace('<'.$item, '<amp-'.$item, $data);
*/
return $data;
}
?>

/Themes/???/Amp.template.php
Code: [Select]
<?php

function template_html_above(){

global $scripturl$context$boardurl;
$context['ob_rewrite_compat'] = true;

echo '
<!doctype html>
<html amp lang="en">
<head>
<meta charset="'
strtolower($context['character_set']), '">
<script async src="https://cdn.ampproject.org/v0.js"></script>
<title>'
$context['page_title'], '</title>
<link rel="canonical" href="'
xpath_urls_filter($context['canonical']), '" />
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<script type="application/ld+json">{
"@context": "http://schema.org",
"@type": "Article",
"mainEntityOfPage":{"@type":"WebPage","@id":"'
xpath_urls_filter($context['canonical']), '"},
"headline": "'
$context['page_title'], '",
"datePublished": "'
$context['posted'], '",
"dateModified":"'
$context['modified'], '",
"image":{"@type": "ImageObject","url": "'
.xpath_urls_filter($context['rel_img']).'","width": "auto","height": "auto"},
"author":{"@type":"Person","name":"'
.$context['amp']['poster_name'].'"},
"publisher":{"@type": "Organization","name":"SI Community","logo":{"@type":"ImageObject","url":"'
.$boardurl.'/img/viral/site_thumb.png","width":"auto","height":"auto"}}
}</script>
<script async custom-element="amp-ad" src="https://cdn.ampproject.org/v0/amp-ad-0.1.js"></script>
<script async custom-element="amp-social-share" src="https://cdn.ampproject.org/v0/amp-social-share-0.1.js"></script>
<script async custom-template="amp-mustache" src="https://cdn.ampproject.org/v0/amp-mustache-0.1.js"></script>
<script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
<style amp-custom>.logo {background-color:#28D;font-variant:small-caps;color:#ffc;text-align:left;padding:6px;box-shadow:0 1px 2px rgba(10,10,10,0.4)}.logo > a {color:#DEF;font:1.1em trebuchet,Helvetica,sans-serif;font-weight: bold;text-decoration:none}.logo amp-img{float:left}.heading {padding:5px;margin-top:0.83em}.who, .who a{color:#999;text-decoration:none}.sep{border:none;height:1px;background-color:#aaa;width:100px;}h3 a{text-decoration:none;color:#000;}</style>
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
</head>
<body>'
;
}

function 
template_html_below() {
echo '
<p class="heading">
<amp-social-share type="twitter" width="45" height="33"></amp-social-share><amp-social-share type="facebook" width="45" height="33"></amp-social-share><amp-social-share type="gplus" width="45" height="33"></amp-social-share><amp-social-share type="email" width="45" height="33"></amp-social-share><amp-social-share type="pinterest" width="45" height="33"></amp-social-share>
</p>
<amp-analytics type="googleanalytics">
<script type="application/json">{"vars": {"account": "UA-10276350-3"}}</script>
</amp-analytics>
</body>
</html>'
;
}

function 
template_AmpTopic(){

global $context$scripturl$boardurl;

echo '
<div class="logo">
<a href="'
.xpath_urls_filter($scripturl).'"><amp-img src="'.$boardurl.'/img/viral/site_thumb.svg" alt="SI Community" title="SI Community" height="20px" width="20px"></amp-img></a>
&nbsp;&nbsp;<a href="'
.xpath_urls_filter($scripturl.'?board=' $context['amp']['id_board']).'">' $context['amp']['board_name'] . '</a>
</div>
'
.($context['head_img'] ? '<a href="'.xpath_urls_filter($scripturl.'?topic='.$context['amp']['id_topic'].';topicseen#new').'"><amp-img width="600" height="400" layout="responsive" src="'.xpath_urls_filter($context['rel_img']).'"></amp-img></a>' '').'
<div class="heading">
<center>
<a href="'
.xpath_urls_filter($scripturl).'"><amp-img src="'.$boardurl.'/img/viral/site_thumb50.png" alt="SI Community" title="SI Community" height="50px" width="50px"></amp-img></a>
<h3><a href="'
.xpath_urls_filter($scripturl.'?topic='.$context['amp']['id_topic'].';topicseen#new').'">'.$context['page_title'].'</a></h3>
<hr class="sep">
<span>
<b>SI Community</b><br>
<small class="who">
<a href="'
.xpath_urls_filter($scripturl.'?action=profile;u=' $context['amp']['id_member']).'">' $context['amp']['poster_name'] . '</a> • 
'
.date('M j, Y'$context['amp']['poster_time']).'
</small>
</span>
</center>
</div>
<div class="heading">
'
.xpath_rewrite_buffer($context['amp']['body'], truetrue).'
<center>
<div class="amp-ad-container">
<amp-ad width=300 height=250 type="adsense" data-ad-client="ca-pub-3272970468196826" data-ad-slot="4059282756"></amp-ad>
</div>
</center>
<p><a href="'
.xpath_urls_filter($scripturl.'?topic='.$context['amp']['id_topic'].';topicseen#new').'"><button>Read More</button></a></p>
</div>'
;
}

function 
template_AmpMedia(){

global $context$scripturl$boardurl;

echo '
<div class="logo">
<a href="'
.xpath_urls_filter($scripturl).'"><amp-img src="'.$boardurl.'/img/viral/site_thumb.svg" alt="SI Community" title="SI Community" height="20px" width="20px"></amp-img></a>
&nbsp;&nbsp;<a href="'
.xpath_urls_filter($scripturl.'?action=media;sa=album;in='.$context['amp']['album_id']).'">' $context['amp']['album_name'] . '</a>
</div>'
;

if ($context['head_img'])
echo '
<a href="'
.xpath_urls_filter($scripturl.'?action=media;sa=item;in='.$context['amp']['id_media']).'"><amp-img width="600" height="400" layout="responsive" src="'.xpath_urls_filter($context['rel_img']).'"></amp-img></a>';
/* else
if ($context['amp']['type'] == 'audio' || $context['amp']['type'] == 'video')
echo '
<center><amp-video width="400" height="300" src="'.$scripturl.'?action=media;sa=media;in='.$context['amp']['id_media'].';v">
<div fallback>
<p>Your browser doesn’t support HTML5 audio/video</p>
</div>
</amp-video></center>';*/ //Need HTTPS :(

echo '
<div class="heading">
<center>
<a href="'
.xpath_urls_filter($scripturl).'"><amp-img src="'.$boardurl.'/img/viral/site_thumb50.png" alt="SI Community" title="SI Community" height="50px" width="50px"></amp-img></a>
<h3><a href="'
.xpath_urls_filter($scripturl.'?action=media;sa=item;in='.$context['amp']['id_media']).'">'.$context['page_title'].'</a></h3>
<hr class="sep">
<span>
<b>SI Community</b><br>
<small class="who">
<a href="'
.xpath_urls_filter($scripturl .'?action=profile;u='.$context['amp']['id_member']).'">' $context['amp']['poster_name'] . '</a> • 
'
.date('M j, Y'$context['amp']['posted_on']).'
</small>
</span>
</center>
</div>
<div class="heading">
'
.xpath_rewrite_buffer($context['amp']['body'], truetrue).'
<center>
<div class="amp-ad-container">
<amp-ad width=300 height=250 type="adsense" data-ad-client="ca-pub-3272970468196826" data-ad-slot="4059282756"></amp-ad>
</div>
</center>
<p><a href="'
.xpath_urls_filter($scripturl.'?action=media;sa=item;in='.$context['amp']['id_media']).'"><button>View Item</button></a></p>
</div>'
;
}
?>


Add to Who.english.php
Code: [Select]
$txt['whoamp_topic'] = 'Viewing AMP topic <a href="' . $scripturl . '?action=amp;t=%1$d">%2$s</a>.';
$txt['whoamp_media'] = 'Viewing AMP media <a href="' . $scripturl . '?action=amp;m=%1$d">%2$s</a>.';
$txt['whoamp_unknown'] = 'Viewing unknown AMP document.';

Find in /Sources/Who.php
Code: [Select]
// Unlisted or unknown action.
else
$data[$k] = $txt['who_unknown'];
Add Before
Code: [Select]
elseif ($actions['action'] == 'amp')
{
if (isset($actions['t'])) {
$msgid = (int) (isset($actions['t']) ? $actions['t'] : 0);
$result = $smcFunc['db_query']('', '
SELECT m.id_topic, m.subject
FROM {db_prefix}messages AS m
INNER JOIN {db_prefix}boards AS b ON (b.id_board = m.id_board)
INNER JOIN {db_prefix}topics AS t ON (t.id_topic = m.id_topic' . ($modSettings['postmod_active'] ? ' AND t.approved = {int:is_approved}' : '') . ')
WHERE m.id_msg = {int:id_msg}
AND {query_see_board}' . ($modSettings['postmod_active'] ? '
AND m.approved = {int:is_approved}' : '') . '
LIMIT 1',
array(
'is_approved' => 1,
'id_msg' => $msgid,
)
);
list ($id_topic, $subject) = $smcFunc['db_fetch_row']($result);
$data[$k] = sprintf($txt['whoamp_topic'], $id_topic, $subject);
$smcFunc['db_free_result']($result);
if (empty($id_topic))
$data[$k] = $txt['whoamp_unknown'];
} elseif (isset($actions['m'])) {
$mid = (int) (isset($actions['m']) ? $actions['m'] : 0);
$result = $smcFunc['db_query']('', '
SELECT m.id_media, m.title
FROM {db_prefix}aeva_media AS m
INNER JOIN {db_prefix}aeva_albums as a ON (a.id_album = m.album_id)
WHERE FIND_IN_SET(-1, a.access) AND m.id_media = {int:id_media}
LIMIT 1',
array(
'id_media' => $mid,
)
);
list ($id_media, $subject) = $smcFunc['db_fetch_row']($result);
$data[$k] = sprintf($txt['whoamp_media'], $id_media, $subject);
$smcFunc['db_free_result']($result);
if (empty($id_media))
$data[$k] = $txt['whoamp_unknown'];
} else
$data[$k] = $txt['whoamp_unknown'];
}

Find in /Sources/Load.php
Code: [Select]
$simpleActions = array(
'findmember',
'helpadmin',
'printpage',
'quotefast',
'spellcheck',
Add After
Code: [Select]
'amp',
Add to action array in index.php or use a hook
Code: [Select]
'amp' => array('Amp.php', 'AmpMain'),
You'll have to add the canonical to the topic page.
The media part is for Aeva Media, it can be removed.
You'll have to remove all the xpath stuff as that is a custom function for my site only.
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: Rock Lee on November 02, 2016, 11:13:47 AM
To Accelerate in load times? I fail to understand it all, excuse my English is not all good!


Regards!
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: rickey29 on December 19, 2016, 09:32:07 PM
Hi nend,

Thanks for your sharing!

Is it a good if we combine your code with some mobile-only theme, instead of default Simple Machines theme?

Thanks,

Rickey
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: nend on December 20, 2016, 11:43:36 AM
The code isn't really theme dependent. You can change aspects of the CSS in the code to make it more appealing or to match the theme of your website, but it is more like another markup.

Think of it like your RSS feed. You can change how data is displayed, but in the end it's mainly up to the reader, in AMP's case it's script interpreter to determine how the page is displayed.

Also keep in mind the canonical is required, the AMP document must link to the full document and visa versa. AMP validation is a real pain, but if you strip everything down to almost bare bones and use their special markup you shouldn't have too much trouble incorporating it.
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: gorbi on May 30, 2017, 01:07:19 AM
mod is ready?
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: vbgamer45 on May 30, 2017, 02:23:49 AM
More details at https://support.google.com/webmasters/answer/6340290?hl=en
Check the discovery part for the the topic page for this part of the mod "You'll have to add the canonical to the topic page."
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: luxint on June 25, 2017, 08:46:27 PM
I was just looking around for an amp solution to use on my new SMF board and came across this thread. Thanks for your generosity in sharing the code... it'll save me a LOT of work :)

I followed the steps precisely and once I'd removed the 404 function as it wasn't found, the page loaded OK, but it was just a blank page with 'Failed to load main template'. Any ideas how I can proceed?

Thanks again!

Lux
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: nend on July 01, 2017, 06:58:58 PM
Haven't had a chance to get back to work on it.

Here is what I have so far.

https://github.com/sicommnend/SMF-AMP
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: luxint on July 01, 2017, 07:10:10 PM
Thanks for getting back to me. I spent a bit of time with it and was able to cobble together a pretty good solution for my needs.

Thanks again for the great contribution. Saved me a TON of time :)

Lux
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: gorbi on July 04, 2017, 01:44:22 AM
Haven't had a chance to get back to work on it.

Here is what I have so far.

https://github.com/sicommnend/SMF-AMP
How to install it on the forum?
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: gorbi on July 12, 2017, 09:51:22 AM
Who can share the Google AMP mod?
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: vbgamer45 on July 12, 2017, 10:59:12 AM
That link should help just package it up as a zip then upload to SMF.
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: gorbi on July 12, 2017, 11:28:13 AM
That link should help just package it up as a zip then upload to SMF.
I did, but there were errors during the installation
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: Macabreo on July 12, 2017, 12:30:47 PM
Yeah really, who can share the Google AMP mod?
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: Apostaganha on July 19, 2017, 02:29:15 AM
ty!
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: neoweb on November 24, 2017, 02:52:23 AM
Is this 'project' still active? I want to use AMP on my website.
I have no idea what the code in the first post is doing.

And what should I do with:
Quote
You'll have to add the canonical to the topic page.
The media part is for Aeva Media, it can be removed.
You'll have to remove all the xpath stuff as that is a custom function for my site only.
I don't understand canonical, media part, Aeva Media? , xpath? hook?

I have the "Reseller" theme installed.
Title: Re: [MOD][WIP]Accelerated Mobile Pages
Post by: Rock Lee on November 25, 2017, 07:41:52 PM
Is this 'project' still active? I want to use AMP on my website.
I have no idea what the code in the first post is doing.

And what should I do with:
Quote
You'll have to add the canonical to the topic page.
The media part is for Aeva Media, it can be removed.
You'll have to remove all the xpath stuff as that is a custom function for my site only.
I don't understand canonical, media part, Aeva Media? , xpath? hook?

I have the "Reseller" theme installed.

It is a complex thing to apply always be in the most current due to many factors, I suggest read in the SMF wiki the terms do not know about it. Now in personal opinion is complex to apply to the forums SMF (Not impossible clear) only requires your time and knowledge half / advanced as it is not the same as a static page that does not undergo major change, a forum is characterized by that same volatility.

Regards!