Added box layout / box handler
authorMarcel Werk <burntime@woltlab.com>
Fri, 4 Dec 2015 17:37:17 +0000 (18:37 +0100)
committerMarcel Werk <burntime@woltlab.com>
Fri, 4 Dec 2015 17:37:17 +0000 (18:37 +0100)
13 files changed:
com.woltlab.wcf/templates/__box.tpl [new file with mode: 0644]
com.woltlab.wcf/templates/footer.tpl
com.woltlab.wcf/templates/header.tpl
com.woltlab.wcf/templates/pageHeader.tpl
wcfsetup/install/files/acp/templates/styleAdd.tpl
wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php
wcfsetup/install/files/lib/data/box/Box.class.php
wcfsetup/install/files/lib/system/WCF.class.php
wcfsetup/install/files/lib/system/box/BoxHandler.class.php [new file with mode: 0644]
wcfsetup/install/files/style/layout/layout.scss
wcfsetup/install/files/style/layout/pageHeader.scss
wcfsetup/install/files/style/layout/pageHeaderSticky.scss
wcfsetup/setup/db/install.sql

diff --git a/com.woltlab.wcf/templates/__box.tpl b/com.woltlab.wcf/templates/__box.tpl
new file mode 100644 (file)
index 0000000..d30caf7
--- /dev/null
@@ -0,0 +1,7 @@
+<{if $box->showHeader}section{else}div{/if} class="box">
+       {if $box->showHeader}<h1 class="boxTitle">{$box->getTitle()}</h1>{/if}
+       
+       <div class="boxContent">
+               {@$box->getContent()}
+       </div>
+{if $box->showHeader}</section>{else}</div>{/if}
index 55d2a2b5ce12e5739b51b4f91b61f5696214571f..af446c05f5a789c8f8eee06918c2f903067354f7 100644 (file)
@@ -1,46 +1,87 @@
                                {event name='contents'}
                                
+                               {hascontent}
+                                       <div class="boxesContentBottom">
+                                               {content}
+                                                       {foreach from=$__wcf->getBoxHandler()->getBoxes('contentBottom') item=box}
+                                                               {@$box}
+                                                       {/foreach}
+                                               {/content}
+                                       </div>
+                               {/hascontent}
+                               
                                {if MODULE_WCF_AD && $__disableAds|empty}
                                        {@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.footer.content')}
                                {/if}
                        </div>
-                       
-                       {if $sidebarOrientation|isset && $sidebarOrientation == 'right'}
-                               {@$__sidebar}
-                       {/if}
+                               
+                       {hascontent}
+                               <aside class="sidebar boxesSidebarRight">
+                                       {content}
+                                               {event name='boxesSidebarRightTop'}
+                                                                                               
+                                               {* WCF2.1 Fallback *}
+                                               {if !$sidebar|empty}
+                                                       {if !$sidebarOrientation|isset || $sidebarOrientation == 'right'}
+                                                               {@$sidebar}
+                                                       {/if}
+                                               {/if}
+                                               
+                                               {if !$sidebarRight|empty}
+                                                       {@$sidebarLeft}
+                                               {/if}
+                                               
+                                               {foreach from=$__wcf->getBoxHandler()->getBoxes('sidebarRight') item=box}
+                                                       {@$box}
+                                               {/foreach}
+                                       
+                                               {event name='boxesSidebarRightBottom'}
+                                       {/content}
+                               </aside>
+                       {/hascontent}
                </div>
        </section>
-       
-       <div id="pageFooterBoxes">
+                               
+       {hascontent}
+               <div class="boxesBottom">
+                       <div class="layoutBoundary">
+                               {content}
+                                       {foreach from=$__wcf->getBoxHandler()->getBoxes('bottom') item=box}
+                                               {@$box}
+                                       {/foreach}
+                               {/content}
+                       </div>  
+               </div>
+       {/hascontent}
+                               
+       <div class="boxesFooterBoxes">                  
                <div class="layoutBoundary">
                        {hascontent}
                                <ul>
-                                       {content}{if !$footerBoxes|empty}{@$footerBoxes}{/if}{/content}
+                                       {content}
+                                               {if !$footerBoxes|empty}{@$footerBoxes}{/if}
+                                       
+                                               {foreach from=$__wcf->getBoxHandler()->getBoxes('footerBoxes') item=box}
+                                                       {@$box}
+                                               {/foreach}
+                                       {/content}
                                </ul>
                        {/hascontent}
                </div>
        </div>
        
-       {*include file='pageNavbarBottom'*}
-       
-       {*<nav id="footerNavigation" class="navigation navigationFooter">
-               {include file='footerMenu'}
-               
-               <ul class="navigationIcons">
-                       <li id="toTopLink" class="toTopLink"><a href="{$__wcf->getAnchor('top')}" title="{lang}wcf.global.scrollUp{/lang}" class="jsTooltip"><span class="icon icon16 icon-arrow-up"></span> <span class="invisible">{lang}wcf.global.scrollUp{/lang}</span></a></li>
-                       {event name='navigationIcons'}
-               </ul>
-               
-               <ul class="navigationItems">
-                       {if SHOW_CLOCK}
-                               <li title="{lang}wcf.date.timezone.{@'/'|str_replace:'.':$__wcf->getUser()->getTimeZone()->getName()|strtolower}{/lang}"><p><span class="icon icon16 icon-time"></span> <span>{@TIME_NOW|plainTime}</span></p></li>
-                       {/if}
-                       {event name='navigationItems'}
-               </ul>
-       </nav>*}
-       
-       <footer id="pageFooter" class="footer{if $sidebarOrientation|isset && $sidebar|isset} sidebarOrientation{@$sidebarOrientation|ucfirst}{if $sidebarOrientation == 'right' && $sidebarCollapsed} sidebarCollapsed{/if}{/if}">
+       <footer id="pageFooter" class="footer">
                <div class="layoutBoundary">
+                       {hascontent}
+                               <div class="boxesFooter">
+                                       {content}
+                                               {foreach from=$__wcf->getBoxHandler()->getBoxes('footer') item=box}
+                                                       {@$box}
+                                               {/foreach}
+                                       {/content}
+                               </div>
+                       {/hascontent}
+                       
                        <div class="footerContent">
                                {event name='footerContents'}
                                
index 90a5f6613697152713940eda6445099491818ff9..c43f53daecdb11cbf1988351b43f0888aa53bf56 100644 (file)
@@ -7,39 +7,74 @@
        
        {event name='afterPageHeader'}
        
+       {hascontent}
+               <div class="boxesHeaderBoxes">
+                       <div class="layoutBoundary">
+                               {content}
+                                       {foreach from=$__wcf->getBoxHandler()->getBoxes('headerBoxes') item=box}
+                                               {@$box}
+                                       {/foreach}
+                               {/content}
+                       </div>
+               </div>
+       {/hascontent}
+       
        {include file='pageNavbarTop'}
        
+       {hascontent}
+               <div class="boxesTop">
+                       <div class="layoutBoundary">
+                               {content}
+                                       {foreach from=$__wcf->getBoxHandler()->getBoxes('top') item=box}
+                                               {@$box}
+                                       {/foreach}
+                               {/content}
+                       </div>
+               </div>
+       {/hascontent}
+       
        <section id="main" class="main" role="main">
                <div class="layoutBoundary">
-                       {capture assign='__sidebar'}
-                               {if $sidebar|isset}
-                                       <aside class="sidebar"{if $sidebarOrientation|isset && $sidebarOrientation == 'right'} data-is-open="{if $sidebarCollapsed}false{else}true{/if}" data-sidebar-name="{$sidebarName}"{/if}>
+                       {hascontent}
+                               <aside class="sidebar boxesSidebarLeft">
+                                       {content}
                                                {if MODULE_WCF_AD && $__disableAds|empty}{@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.sidebar.top')}{/if}
                                                
-                                               {event name='sidebarBoxesTop'}
+                                               {event name='boxesSidebarLeftTop'}
                                                
-                                               {@$sidebar}
+                                               {* WCF2.1 Fallback *}
+                                               {if !$sidebar|empty}
+                                                       {if !$sidebarOrientation|isset || $sidebarOrientation == 'left'}
+                                                               {@$sidebar}
+                                                       {/if}   
+                                               {/if}
                                                
-                                               {event name='sidebarBoxesBottom'}
+                                               {if !$sidebarLeft|empty}
+                                                       {@$sidebarLeft}
+                                               {/if}
                                                
-                                               {if MODULE_WCF_AD && $__disableAds|empty}{@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.sidebar.bottom')}{/if}
-                                       </aside>
-                               
-                               {if $sidebarOrientation|isset && $sidebarOrientation == 'right'}
-                                       <script data-relocate="true">
-                                               require(['WoltLab/WCF/Ui/Collapsible/Sidebar'], function(UiCollapsibleSidebar) {
-                                                       UiCollapsibleSidebar.setup();
-                                               });
-                                       </script>
-                               {/if}
-                               {/if}
-                       {/capture}
+                                               {foreach from=$__wcf->getBoxHandler()->getBoxes('sidebarLeft') item=box}
+                                                       {@$box}
+                                               {/foreach}
                        
-                       {if !$sidebarOrientation|isset || $sidebarOrientation == 'left'}
-                               {@$__sidebar}
-                       {/if}
+                                               {event name='boxesSidebarLeftBottom'}
+                       
+                                               {if MODULE_WCF_AD && $__disableAds|empty}{@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.sidebar.bottom')}{/if}
+                                       {/content}
+                               </aside>
+                       {/hascontent}
                        
                        <div id="content" class="content">
                                {if MODULE_WCF_AD && $__disableAds|empty}{@$__wcf->getAdHandler()->getAds('com.woltlab.wcf.header.content')}{/if}
                                
+                               {hascontent}
+                                       <div class="boxesContentTop">
+                                               {content}
+                                                       {foreach from=$__wcf->getBoxHandler()->getBoxes('contentTop') item=box}
+                                                               {@$box}
+                                                       {/foreach}
+                                               {/content}
+                                       </div>
+                               {/hascontent}
+                               
                                {event name='contents'}
index 1b8091f3253b9713e769f16c87c6ad21625146aa..4b1451e7f3e334ba91ac7e2c99edcf5847b544ac 100644 (file)
@@ -1,25 +1,41 @@
-<header id="pageHeader" class="pageHeader">
-       <div>
-               <div class="layoutBoundary">
-                       {include file='pageHeaderLogo'}
-                       
-                       {include file='pageHeaderSearch'}
+<div class="pageHeaderContainer">
+       <header id="pageHeader" class="pageHeader">
+               <div>
+                       <div class="layoutBoundary">
+                               {include file='pageHeaderLogo'}
+                               
+                               {include file='pageHeaderSearch'}
+                               
+                               {include file='pageHeaderMenu'}
+                               
+                               {include file='pageHeaderUser'}
+                       </div>
+               </div>
+               
+               <script data-relocate="true">
+                       var header = elById('pageHeader');
+                       var pageHeaderContainer = elBySel('.pageHeaderContainer');
+                       header.style.setProperty('min-height', header.clientHeight + 'px');
                        
-                       {include file='pageHeaderMenu'}
+                       function stickyHeader() {
+                               header.classList[(document.body.scrollTop > 50) ? 'add' : 'remove']('sticky');
+                               pageHeaderContainer.classList[(document.body.scrollTop > 50) ? 'add' : 'remove']('stickyPageHeader');
+                       }
                        
-                       {include file='pageHeaderUser'}
-               </div>
-       </div>
+                       stickyHeader();
+                       window.addEventListener('scroll', stickyHeader);
+               </script>
+       </header>
        
-       <script data-relocate="true">
-               var header = elById('pageHeader');
-               header.style.setProperty('min-height', header.clientHeight + 'px');
-               
-               function stickyHeader() {
-                       header.classList[(document.body.scrollTop > 50) ? 'add' : 'remove']('sticky');
-               }
-               
-               stickyHeader();
-               window.addEventListener('scroll', stickyHeader);
-       </script>
-</header>
\ No newline at end of file
+       {hascontent}
+               <div class="boxesHero">
+                       <div class="layoutBoundary">
+                               {content}
+                                       {foreach from=$__wcf->getBoxHandler()->getBoxes('hero') item=box}
+                                               {@$box}
+                                       {/foreach}
+                               {/content}
+                       </div>
+               </div>
+       {/hascontent}
+</div>
index e66878db7c2f104750ec61c8d8758db90bd964de..831a65ef4a346b6c987a73c20fb5373f58a94d68 100644 (file)
                <script>
                        var styleRuleMap = {
                                'wcfHeaderBackground': '#spHeader { background-color: VALUE; }',
+                               'wcfHeaderText': '#spHeader { color: VALUE; }',
                                'wcfHeaderLink': '#spHeader a { color: VALUE; }',
                                'wcfHeaderLinkActive': '#spHeader a:hover { color: VALUE; }',
                                'wcfHeaderSearchBoxBackground': '#spSearchBox { background-color: VALUE; }',
index 58bcaf376b55588ff8df061cf88ea34a4a4a7c80..481f03e27b13d82b5edfb1ad1d93650c3701d876 100644 (file)
@@ -443,7 +443,7 @@ class StyleAddForm extends AbstractForm {
                ];
                
                $this->colors = [
-                       'wcfHeader' => ['background', 'link', 'linkActive'],
+                       'wcfHeader' => ['background', 'text', 'link', 'linkActive'],
                        'wcfHeaderSearchBox' => ['background', 'text', 'backgroundActive', 'textActive'],
                        'wcfHeaderMenu' => ['background', 'border', 'link', 'backgroundActive', 'linkActive'],
                        'wcfNavigation' => ['background', 'text', 'link', 'linkActive'],
index d78173cb0222b6eab7ecdd2e9a43a70801ca50bd..f1aed8a92cfb9c92bc040175354daa78fd13a095 100644 (file)
@@ -2,6 +2,7 @@
 namespace wcf\data\box;
 use wcf\data\DatabaseObject;
 use wcf\system\WCF;
+use wcf\util\StringUtil;
 
 /**
  * Represents a box.
@@ -34,7 +35,7 @@ class Box extends DatabaseObject {
         * available box positions
         * @var string[]
         */
-       public static $availablePositions = ['header', 'headerBoxes', 'top', 'sidebarLeft', 'contentTop', 'sidebarRight', 'contentBottom', 'bottom', 'footerBoxes', 'footer'];
+       public static $availablePositions = ['hero', 'headerBoxes', 'top', 'sidebarLeft', 'contentTop', 'sidebarRight', 'contentBottom', 'bottom', 'footerBoxes', 'footer'];
        
        /**
         * Returns true if the active user can delete this box.
@@ -71,6 +72,112 @@ class Box extends DatabaseObject {
                return $content;
        }
        
+       /**
+        * Gets the title for the rendered version of this box.
+        *
+        * @return      string
+        */
+       public function getTitle() {
+               if ($this->boxType == 'system') {
+                       return $this->getController()->getTitle();
+               }
+               else if ($this->boxType == 'menu') {
+                       return $this->getMenu()->getTitle();
+               }
+               else {
+                       $boxContent = $this->getBoxContent();
+                       if ($this->isMultilingual) {
+                               if (isset($boxContent[WCF::getLanguage()->languageID])) return $boxContent[WCF::getLanguage()->languageID]['title'];
+                       }
+                       else {
+                               if (isset($boxContent[0])) return $boxContent[0]['title'];
+                       }
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Gets the content for the rendered version of this box.
+        * 
+        * @return      string
+        */
+       public function getContent() {
+               if ($this->boxType == 'system') {
+                       return $this->getController()->getContent();
+               }
+               else if ($this->boxType == 'menu') {
+                       return $this->getMenu()->getContent();
+               }
+               else {
+                       $boxContent = $this->getBoxContent();
+                       $content = '';
+                       if ($this->isMultilingual) {
+                               if (isset($boxContent[WCF::getLanguage()->languageID])) $content = $boxContent[WCF::getLanguage()->languageID]['content'];
+                       }
+                       else {
+                               if (isset($boxContent[0])) $content = $boxContent[0]['content'];
+                       }
+                       
+                       if ($this->boxType == 'text') {
+                               // @todo parse text
+                               $content = StringUtil::encodeHTML($content);
+                       }
+                       
+                       return $content;
+               }
+               
+               return '';
+       }
+       
+       /**
+        * Returns the rendered version of this box.
+        * 
+        * @return      string
+        */
+       public function __toString() {
+               if (!$this->hasContent()) return ''; 
+               
+               WCF::getTPL()->assign([
+                       'box' => $this
+               ]);
+               return WCF::getTPL()->fetch('__box');
+       }
+       
+       /**
+        * Returns false if this box has no content.
+        * 
+        * @return      boolean
+        */
+       public function hasContent() {
+               if ($this->boxType == 'system') {
+                       return $this->getController()->hasContent();
+               }
+               else if ($this->boxType == 'menu') {
+                       return $this->getMenu()->hasContent();
+               }
+               else {
+                       $boxContent = $this->getBoxContent();
+                       $content = '';
+                       if ($this->isMultilingual) {
+                               if (isset($boxContent[WCF::getLanguage()->languageID])) $content = $boxContent[WCF::getLanguage()->languageID]['content'];
+                       }
+                       else {
+                               if (isset($boxContent[0])) $content = $boxContent[0]['content'];
+                       }
+                       
+                       return ($content != '');
+               }
+       }
+       
+       public function getController() {
+               // @todo
+       }
+       
+       public function getMenu() {
+               // @todo
+       }
+       
        /**
         * Returns the box with the given name.
         *
index 385c5e47975e637f6c1d1281c74a9a0af3950e99..49e25b410c259cb3b4ce859ce53fcce6f01c4bff 100644 (file)
@@ -7,6 +7,7 @@ use wcf\data\package\PackageCache;
 use wcf\data\package\PackageEditor;
 use wcf\system\application\ApplicationHandler;
 use wcf\system\application\IApplication;
+use wcf\system\box\BoxHandler;
 use wcf\system\cache\builder\CoreObjectCacheBuilder;
 use wcf\system\cache\builder\PackageUpdateCacheBuilder;
 use wcf\system\cronjob\CronjobScheduler;
@@ -803,6 +804,15 @@ class WCF {
                return StyleHandler::getInstance();
        }
        
+       /**
+        * Returns box handler.
+        *
+        * @return      BoxHandler
+        */
+       public function getBoxHandler() {
+               return BoxHandler::getInstance();
+       }
+       
        /**
         * Returns number of available updates.
         * 
diff --git a/wcfsetup/install/files/lib/system/box/BoxHandler.class.php b/wcfsetup/install/files/lib/system/box/BoxHandler.class.php
new file mode 100644 (file)
index 0000000..fd4a852
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+namespace wcf\system\box;
+use wcf\data\box\BoxList;
+use wcf\system\SingletonFactory;
+
+/**
+ * Handles boxes.
+ * 
+ * @author     Marcel Werk
+ * @copyright  2001-2015 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.box
+ * @category   Community Framework
+ */
+class BoxHandler extends SingletonFactory {
+       /**
+        * boxes grouped by position
+        * @var array
+        */
+       protected $boxes = [];
+       
+       /**
+        * @inheritDoc
+        */
+       protected function init() {
+               // load box layout for active page
+               $boxList = new BoxList();
+               $boxList->sqlOrderBy = 'showOrder';
+               $boxList->readObjects();
+               foreach ($boxList as $box) {
+                       if (!isset($this->boxes[$box->position])) $this->boxes[$box->position] = [];
+                       $this->boxes[$box->position][] = $box;
+               }
+       }
+       
+       /**
+        * Returns boxes for the given position.
+        * 
+        * @param       string          $position
+        * @return      \wcf\data\box\Box[]
+        */
+       public function getBoxes($position) {
+               if (isset($this->boxes[$position])) {
+                       return $this->boxes[$position];
+               }
+               
+               return [];
+       }
+}
index 1a4f27108ad2bc6a9439ca616ad0f4cd24e58f61..4cabb9d4f307710f62663bb6627a6d3efaf0086c 100644 (file)
@@ -30,7 +30,7 @@ a {
 }
 
 /* COLUMN LAYOUT */
-#pageHeader {
+.pageHeaderContainer {
        flex: 0 0 auto;
        z-index: 100;
 }
index aa1f4660ba69ce0198fbeb70637a13868e3e791d..eec46849053c1327e6269d1f03663e7fe39201c3 100644 (file)
@@ -9,8 +9,9 @@
 .interactiveDropdown { display: none; }
 /* @TODO END */
 
-.pageHeader {
+.pageHeaderContainer {
        background-color: $wcfHeaderBackground;
+       color: $wcfHeaderText;
        
        a {
                color: $wcfHeaderLink;
@@ -19,7 +20,9 @@
                        color: $wcfHeaderLinkActive;
                }
        }
-       
+}
+
+.pageHeader {
        > div > div {
                align-items: center;
                display: flex;
index fc8086c189516a902b737113c09eb38e496ce1f3..6040a46835adcae3a1594fb25d9e548306da338c 100644 (file)
@@ -3,10 +3,13 @@
        will-change: transform;
 }
 
+.pageHeaderContainer.stickyPageHeader {
+       z-index: 300;
+}
+
 #pageHeader.sticky {
        flex-wrap: nowrap;
-       z-index: 300;
-       
+               
        > div {
                background-color: $wcfHeaderBackground; /* @TODO */
                left: 0;
index d7a3ea4e398caa535a272a11e6c59985022c943d..b0a070e1e2caf4668edae7c482256001780924e9 100644 (file)
@@ -1958,6 +1958,7 @@ INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfGapMedi
 INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfGapSmall', '7px');
 INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfGapTiny', '4px');
 INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfHeaderBackground', 'rgba(44, 62, 80, 1)');
+INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfHeaderText', 'rgba(255, 255, 255, 1)');
 INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfHeaderLink', 'rgba(255, 255, 255, 1)');
 INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfHeaderLinkActive', 'rgba(255, 255, 255, 1)');
 INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfHeaderMenuBackground', 'rgba(255, 255, 255, 1)');