--- /dev/null
+<{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}
{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'}
{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'}
-<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>
<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; }',
];
$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'],
namespace wcf\data\box;
use wcf\data\DatabaseObject;
use wcf\system\WCF;
+use wcf\util\StringUtil;
/**
* Represents a box.
* 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.
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.
*
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;
return StyleHandler::getInstance();
}
+ /**
+ * Returns box handler.
+ *
+ * @return BoxHandler
+ */
+ public function getBoxHandler() {
+ return BoxHandler::getInstance();
+ }
+
/**
* Returns number of available updates.
*
--- /dev/null
+<?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 [];
+ }
+}
}
/* COLUMN LAYOUT */
-#pageHeader {
+.pageHeaderContainer {
flex: 0 0 auto;
z-index: 100;
}
.interactiveDropdown { display: none; }
/* @TODO END */
-.pageHeader {
+.pageHeaderContainer {
background-color: $wcfHeaderBackground;
+ color: $wcfHeaderText;
a {
color: $wcfHeaderLink;
color: $wcfHeaderLinkActive;
}
}
-
+}
+
+.pageHeader {
> div > div {
align-items: center;
display: flex;
will-change: transform;
}
+.pageHeaderContainer.stickyPageHeader {
+ z-index: 300;
+}
+
#pageHeader.sticky {
flex-wrap: nowrap;
- z-index: 300;
-
+
> div {
background-color: $wcfHeaderBackground; /* @TODO */
left: 0;
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)');