Added amp version of the article page
authorMarcel Werk <burntime@woltlab.com>
Sun, 19 Jun 2016 22:02:02 +0000 (00:02 +0200)
committerMarcel Werk <burntime@woltlab.com>
Sun, 19 Jun 2016 22:02:02 +0000 (00:02 +0200)
com.woltlab.wcf/templates/ampArticle.tpl [new file with mode: 0644]
com.woltlab.wcf/templates/ampFooter.tpl [new file with mode: 0644]
com.woltlab.wcf/templates/ampHeader.tpl [new file with mode: 0644]
com.woltlab.wcf/templates/article.tpl
wcfsetup/install/files/lib/page/ArticleAmpPage.class.php [new file with mode: 0644]
wcfsetup/install/files/style/ui/article.scss

diff --git a/com.woltlab.wcf/templates/ampArticle.tpl b/com.woltlab.wcf/templates/ampArticle.tpl
new file mode 100644 (file)
index 0000000..87f49b0
--- /dev/null
@@ -0,0 +1,68 @@
+{capture assign='pageTitle'}{$articleContent->title}{/capture}
+
+{capture assign='headContent'}
+       <script type="application/ld+json">
+               {
+                       "@context": "http://schema.org",
+                       "@type": "NewsArticle",
+                       "mainEntityOfPage": "{$regularCanonicalURL}",
+                       "headline": "{$articleContent->title}",
+                       "datePublished": "{@$article->time|plainTime}",
+                       "dateModified": "{@$article->time|plainTime}",
+                       "description": "{@$articleContent->getFormattedTeaser()}",
+                       "author": {
+                               "@type": "Person",
+                               "name": "{$article->username}"
+                       },
+                       "publisher": {
+                               "@type": "Organization",
+                               "name": "{PAGE_TITLE|language}",
+                               "logo": {
+                                       "@type": "ImageObject",
+                                       "url": "{@$__wcf->getPath()}images/default-logo.png",{* @TODO *}
+                                       "width": 288,
+                                       "height": 40
+                               }
+                       }
+                       {if $articleContent->getImage()}
+                       ,"image": {
+                               "@type": "ImageObject",
+                               "url": "{$articleContent->getImage()->getThumbnailLink('large')}",
+                               "width": {@$articleContent->getImage()->getThumbnailWidth('large')},
+                               "height": {@$articleContent->getImage()->getThumbnailHeight('large')}
+                       }
+                       {/if}
+               }
+       </script>
+{/capture}
+
+{include file='ampHeader'}
+
+<article class="article">
+       <header class="articleHeader">
+               <h1 class="articleTitle">{$articleContent->title}</h1>
+               <h2 class="articleAuthor">{$article->username}</h2>
+               <time class="articleDate" datetime="{@$article->time|date:'c'}">{@$article->time|plainTime}</time>
+       </header>
+       
+       {if $articleContent->getImage()}
+               <figure class="articleImage">
+                       <amp-img src="{$articleContent->getImage()->getThumbnailLink('large')}" alt="{$articleContent->getImage()->altText}" height="{@$articleContent->getImage()->getThumbnailHeight('large')}" width="{@$articleContent->getImage()->getThumbnailWidth('large')}" layout="responsive"></amp-img>
+                       {if $articleContent->getImage()->caption}
+                               <figcaption>{$articleContent->getImage()->caption}</figcaption>
+                       {/if}
+               </figure>
+       {/if}
+       
+       {if $articleContent->teaser}
+               <div class="articleTeaser">
+                       <p>{@$articleContent->getFormattedTeaser()}</p>
+               </div>
+       {/if}
+       
+       <div class="articleContent">
+               {@$articleContent->getFormattedContent()}
+       </div>
+</article>
+       
+{include file='ampFooter'}
diff --git a/com.woltlab.wcf/templates/ampFooter.tpl b/com.woltlab.wcf/templates/ampFooter.tpl
new file mode 100644 (file)
index 0000000..3b64c54
--- /dev/null
@@ -0,0 +1,48 @@
+               </main>
+               
+               <amp-sidebar id="sidebar" layout="nodisplay">
+                       <button on="tap:sidebar.close">{lang}wcf.global.button.close{/lang}</button>
+                       
+                       <h3>{lang}wcf.menu.page.navigation{/lang}</h3>
+                       <ol>
+                               {foreach from=$__wcf->getBoxHandler()->getBoxByIdentifier('com.woltlab.wcf.MainMenu')->getMenu()->getMenuItemNodeList() item=menuItemNode}
+                                       {if $menuItemNode->getDepth() == 1 || $menuItemNode->getParentNode()->isActiveNode()}   
+                                       <li>
+                                               <a href="{$menuItemNode->getURL()}">{lang}{$menuItemNode->title}{/lang}</a>
+                                       
+                                               {if $menuItemNode->hasChildren() && $menuItemNode->isActiveNode()}<ol>{else}</li>{/if}
+                                               
+                                               {if !$menuItemNode->hasChildren() && $menuItemNode->isLastSibling()}
+                                                       {@"</ol></li>"|str_repeat:$menuItemNode->getOpenParentNodes()}
+                                               {/if}
+                                       {/if}
+                               {/foreach}
+                       </ol>
+                       <ol>
+                               {foreach from=$__wcf->getBoxHandler()->getBoxByIdentifier('com.woltlab.wcf.FooterMenu')->getMenu()->getMenuItemNodeList() item=menuItemNode}
+                                       <li><a href="{$menuItemNode->getURL()}">{lang}{$menuItemNode->title}{/lang}</a></li>
+                               {/foreach}
+                       </ol>
+                                               
+                       <h3>{lang}wcf.menu.page.location{/lang}</h3>
+                       <ol class="breadcrumbs">
+                               {assign var=__breadcrumbsDepth value=0}
+                               {foreach from=$__wcf->getBreadcrumbs() item=$breadcrumb}
+                                       <li><a href="{$breadcrumb->getURL()}">{$breadcrumb->getLabel()}</a></li>
+                                       {assign var=__breadcrumbsDepth value=$__breadcrumbsDepth + 1}
+                               {/foreach}
+                       </ol>
+               </amp-sidebar>
+               
+               {if MODULE_COOKIE_POLICY_PAGE && !$__wcf->user->userID}
+                       <amp-user-notification layout="nodisplay" id="cookie-policy-notice">
+                               {lang}wcf.page.cookiePolicy.info{/lang}
+                               <button on="tap:cookie-policy-notice.dismiss">{lang}wcf.global.button.close{/lang}</button>
+                       </amp-user-notification>
+               {/if}
+               
+               <footer class="footer">
+                       <div class="copyright">{lang}wcf.page.copyright{/lang}</div>
+               </footer>
+       </body>
+</html>
diff --git a/com.woltlab.wcf/templates/ampHeader.tpl b/com.woltlab.wcf/templates/ampHeader.tpl
new file mode 100644 (file)
index 0000000..9aa9931
--- /dev/null
@@ -0,0 +1,193 @@
+<!doctype html>
+<html amp lang="{@$__wcf->language->getFixedLanguageCode()}">
+       <head>
+               <meta charset="utf-8">
+               <title>{@$pageTitle} - {PAGE_TITLE|language}</title>
+               <link rel="canonical" href="{$regularCanonicalURL}">
+               <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
+               {if !$headContent|empty}
+                       {@$headContent}
+               {/if}
+               <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Open+Sans:400,300,600">
+               <style amp-custom>
+                       html {
+                               box-sizing: border-box;
+                       }
+                       
+                       *,
+                       *::before,
+                       *::after {
+                               box-sizing: inherit;
+                       }
+                       
+                       body {
+                               background-color: #fff;
+                               font-family: "Open Sans", "Segoe UI", "Lucida Grande", "Helvetica", sans-serif;
+                               font-size: 14px;
+                       }
+                       
+                       a {
+                               color: rgb(231, 76, 60);
+                               text-decoration: none;
+                       }
+                       
+                       a:hover {
+                               color: rgb(192, 57, 43);
+                               text-decoration: none;
+                       }
+                       
+                       button {
+                               background: none;
+                               border: none;
+                               color: inherit;
+                               display: block;
+                               font-family: "Open Sans", "Segoe UI", "Lucida Grande", "Helvetica", sans-serif;
+                               font-size: 14px;
+                               margin-top: 5px;
+                               outline: 0;
+                               overflow: hidden;
+                               padding: 0;
+                               text-transform: uppercase;
+                       }
+                       
+                       .header {
+                               background-color: rgb(44, 62, 80);
+                               color: #fff;
+                               padding: 20px;
+                       }
+                       
+                       .header button {
+                               margin-top: 10px;
+                       }
+                       
+                       .footer {
+                               background-color: rgb(52, 73, 94);
+                               color: rgb(189, 195, 199);
+                               padding: 20px 10px;
+                       }
+                       
+                       .footer a {
+                               color: rgb(189, 195, 199);
+                       }
+                       
+                       .footer a:hover {
+                               color: rgb(255, 255, 255);
+                               text-decoration: none;
+                       }
+                       
+                       .footer .copyright {
+                               text-align: center;
+                       }
+                       
+                       .main {
+                               color: rgb(44, 62, 80);
+                               padding: 30px 10px;
+                       }
+                       
+                       .article .articleTitle {
+                               font-weight: 300;
+                               font-size: 23px;
+                               line-height: 1.05;
+                               margin: 0;
+                       }
+                       
+                       .article .articleAuthor {
+                               color: rgb(125, 130, 135);
+                               display: inline-block;
+                               font-size: 14px;
+                               font-weight: 400;
+                               margin: 5px 0 0 0;
+                       }
+                       
+                       .article .articleAuthor::after {
+                               color: rgb(125, 130, 135);
+                               content: "\00b7";
+                               margin-left: 6px;
+                       }
+                       
+                       .article .articleDate {
+                               color: rgb(125, 130, 135);
+                       }
+                       
+                       .article .articleImage,
+                       .article .articleContent,
+                       .article .articleTeaser {
+                               margin-top: 30px;
+                       }
+                       
+                       .article .articleTeaser {
+                               font-weight: 600;
+                       }
+                       
+                       amp-user-notification {
+                               background-color: rgb(217, 237, 247);
+                               color: rgb(49, 112, 143);
+                               padding: 10px;
+                       }
+                       
+                       amp-sidebar {
+                               padding: 20px 10px 10px;
+                               width: 250px;
+                       }
+                       
+                       amp-sidebar button {
+                               margin-top: 0;
+                               position: absolute;
+                               right: 10px;
+                               top: 10px;
+                       }
+                       
+                       amp-sidebar h3 {
+                               font-size: 18px;
+                               font-weight: 400;
+                               margin: 20px 0 0;
+                       }
+                       
+                       amp-sidebar ol {
+                               margin: 10px 0 0;
+                               padding: 0;
+                       }
+                       
+                       amp-sidebar ol ol {
+                               margin-left: 20px;
+                               margin-top: 0;
+                       }
+                       
+                       amp-sidebar ol + ol {
+                               margin-top: 0;
+                       }
+                       
+                       amp-sidebar li {
+                               list-style: none;
+                       }
+                       
+                       amp-sidebar li a {
+                               display: block;
+                               padding: 7px 7px 7px 0;
+                       }
+                       
+                       .breadcrumbs li:nth-child(2) {
+                               padding-left: 20px;
+                       }
+                       .breadcrumbs li:nth-child(3) {
+                               padding-left: 30px;
+                       }
+                       .breadcrumbs li:nth-child(4) {
+                               padding-left: 40px;
+                       }
+               </style>
+               {literal}<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>{/literal}
+               <script async custom-element="amp-sidebar" src="https://cdn.ampproject.org/v0/amp-sidebar-0.1.js"></script>
+               <script async custom-element="amp-user-notification" src="https://cdn.ampproject.org/v0/amp-user-notification-0.1.js"></script>
+               <script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
+               <script async src="https://cdn.ampproject.org/v0.js"></script>
+       </head>
+<body>
+       <header class="header">
+               <div class="logo">
+                       <a href="{link}{/link}"><amp-img width="288" height="40" src="{@$__wcf->getPath()}images/default-logo.png"></amp-img></a>{* @TODO *}
+               </div>
+               
+               <button on='tap:sidebar.toggle'>{lang}wcf.global.page.pagination{/lang}</button>
+       </header>
+       <main class="main">
index 1b3b1cf899ab18be4693b03557cad24ff34ac77d..ba470e47798022ed7c20f356afa26d50235bc9ff 100644 (file)
@@ -75,6 +75,7 @@
                        {/if}
                {/foreach}
        {/if}
+       <link rel="amphtml" href="{link controller='ArticleAmp' object=$articleContent}{/link}">
 {/capture}
 
 {include file='header'}
diff --git a/wcfsetup/install/files/lib/page/ArticleAmpPage.class.php b/wcfsetup/install/files/lib/page/ArticleAmpPage.class.php
new file mode 100644 (file)
index 0000000..b318184
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+namespace wcf\page;
+use wcf\data\article\content\ViewableArticleContent;
+use wcf\data\article\ArticleEditor;
+use wcf\data\article\ViewableArticle;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\page\PageLocationManager;
+use wcf\system\WCF;
+
+/**
+ * Shows the amp version of an article.
+ *
+ * @author     Marcel Werk
+ * @copyright  2001-2016 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    WoltLabSuite\Core\Page
+ * @since      3.0
+ */
+class ArticleAmpPage extends AbstractPage {
+       /**
+        * @inheritDoc
+        */
+       public $templateName = 'ampArticle';
+       
+       /**
+        * @inheritDoc
+        */
+       public $neededModules = ['MODULE_ARTICLE'];
+       
+       /**
+        * article content id
+        * @var integer
+        */
+       public $articleContentID = 0;
+       
+       /**
+        * article content object
+        * @var ViewableArticleContent
+        */
+       public $articleContent;
+       
+       /**
+        * article object
+        * @var ViewableArticle
+        */
+       public $article;
+       
+       /**
+        * @inheritDoc
+        */
+       public function readParameters() {
+               parent::readParameters();
+               
+               if (isset($_REQUEST['id'])) $this->articleContentID = intval($_REQUEST['id']);
+               $this->articleContent = ViewableArticleContent::getArticleContent($this->articleContentID);
+               if ($this->articleContent === null) {
+                       throw new IllegalLinkException();
+               }
+               $this->article = ViewableArticle::getArticle($this->articleContent->articleID);
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function checkPermissions() {
+               parent::checkPermissions();
+               
+               if (!$this->article->canRead()) {
+                       throw new PermissionDeniedException();
+               }
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function readData() {
+               parent::readData();
+               
+               // update view count
+               $articleEditor = new ArticleEditor($this->article->getDecoratedObject());
+               $articleEditor->updateCounters([
+                       'views' => 1
+               ]);
+               
+               // set location
+               PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.CategoryArticleList', $this->article->categoryID, $this->article->getCategory());
+               foreach ($this->article->getCategory()->getParentCategories() as $parentCategory) {
+                       PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.CategoryArticleList', $parentCategory->categoryID, $parentCategory);
+               }
+               PageLocationManager::getInstance()->addParentLocation('com.woltlab.wcf.ArticleList');
+       }
+       
+       /**
+        * @inheritDoc
+        */
+       public function assignVariables() {
+               parent::assignVariables();
+               
+               WCF::getTPL()->assign([
+                       'articleContentID' => $this->articleContentID,
+                       'articleContent' => $this->articleContent,
+                       'article' => $this->article,
+                       'category' => $this->article->getCategory(),
+                       'regularCanonicalURL' => $this->articleContent->getLink()
+               ]);
+       }
+}
index 597e510ca7b6aec0fa64307ab5429e976079f9f8..2651f9ff275733d11a2b294072280d5b99a14d6f 100644 (file)
@@ -6,7 +6,8 @@
                overflow: hidden;
                
                img {
-                       width: 100%;
+                       height: auto !important;
+                       width: 100% !important;
                }
        }