summaryrefslogtreecommitdiff
path: root/www/wiki/skins/chameleon/src
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
committerYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
commitfc7369835258467bf97eb64f184b93691f9a9fd5 (patch)
treedaabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/skins/chameleon/src
first commit
Diffstat (limited to 'www/wiki/skins/chameleon/src')
-rw-r--r--www/wiki/skins/chameleon/src/ChameleonTemplate.php117
-rw-r--r--www/wiki/skins/chameleon/src/ComponentFactory.php265
-rw-r--r--www/wiki/skins/chameleon/src/Components/Cell.php57
-rw-r--r--www/wiki/skins/chameleon/src/Components/Component.php222
-rw-r--r--www/wiki/skins/chameleon/src/Components/Container.php60
-rw-r--r--www/wiki/skins/chameleon/src/Components/ContentBody.php218
-rw-r--r--www/wiki/skins/chameleon/src/Components/ContentHeader.php76
-rw-r--r--www/wiki/skins/chameleon/src/Components/FooterIcons.php73
-rw-r--r--www/wiki/skins/chameleon/src/Components/FooterInfo.php83
-rw-r--r--www/wiki/skins/chameleon/src/Components/FooterPlaces.php64
-rw-r--r--www/wiki/skins/chameleon/src/Components/Grid.php59
-rw-r--r--www/wiki/skins/chameleon/src/Components/Html.php61
-rw-r--r--www/wiki/skins/chameleon/src/Components/LastmodInfo.php85
-rw-r--r--www/wiki/skins/chameleon/src/Components/LegalInfo.php85
-rw-r--r--www/wiki/skins/chameleon/src/Components/Logo.php98
-rw-r--r--www/wiki/skins/chameleon/src/Components/MainContent.php215
-rw-r--r--www/wiki/skins/chameleon/src/Components/Menu.php96
-rw-r--r--www/wiki/skins/chameleon/src/Components/Modifications/HideFor.php71
-rw-r--r--www/wiki/skins/chameleon/src/Components/Modifications/Modification.php174
-rw-r--r--www/wiki/skins/chameleon/src/Components/Modifications/ShowOnlyFor.php71
-rw-r--r--www/wiki/skins/chameleon/src/Components/Modifications/Sticky.php51
-rw-r--r--www/wiki/skins/chameleon/src/Components/NavMenu.php221
-rw-r--r--www/wiki/skins/chameleon/src/Components/NavbarHorizontal.php256
-rw-r--r--www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Logo.php54
-rw-r--r--www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Menu.php51
-rw-r--r--www/wiki/skins/chameleon/src/Components/NavbarHorizontal/NavMenu.php51
-rw-r--r--www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageTools.php159
-rw-r--r--www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageToolsAdaptable.php209
-rw-r--r--www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PersonalTools.php117
-rw-r--r--www/wiki/skins/chameleon/src/Components/NavbarHorizontal/SearchBar.php55
-rw-r--r--www/wiki/skins/chameleon/src/Components/NewtalkNotifier.php57
-rw-r--r--www/wiki/skins/chameleon/src/Components/PageTools.php267
-rw-r--r--www/wiki/skins/chameleon/src/Components/PersonalTools.php82
-rw-r--r--www/wiki/skins/chameleon/src/Components/Row.php45
-rw-r--r--www/wiki/skins/chameleon/src/Components/SearchBar.php144
-rw-r--r--www/wiki/skins/chameleon/src/Components/Silent.php51
-rw-r--r--www/wiki/skins/chameleon/src/Components/SiteNotice.php59
-rw-r--r--www/wiki/skins/chameleon/src/Components/Structure.php96
-rw-r--r--www/wiki/skins/chameleon/src/Components/ToolbarHorizontal.php142
-rw-r--r--www/wiki/skins/chameleon/src/Hooks/SetupAfterCache.php242
-rw-r--r--www/wiki/skins/chameleon/src/IdRegistry.php122
-rw-r--r--www/wiki/skins/chameleon/src/Menu/Menu.php118
-rw-r--r--www/wiki/skins/chameleon/src/Menu/MenuFactory.php91
-rw-r--r--www/wiki/skins/chameleon/src/Menu/MenuFromLines.php273
-rw-r--r--www/wiki/skins/chameleon/src/PermissionsHelper.php195
-rw-r--r--www/wiki/skins/chameleon/src/SkinChameleon.php122
46 files changed, 5580 insertions, 0 deletions
diff --git a/www/wiki/skins/chameleon/src/ChameleonTemplate.php b/www/wiki/skins/chameleon/src/ChameleonTemplate.php
new file mode 100644
index 00000000..fbcca0ae
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/ChameleonTemplate.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * File holding the ChameleonTemplate class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2016, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon;
+
+use BaseTemplate;
+use SkinChameleon;
+
+/**
+ * BaseTemplate class for the Chameleon skin
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class ChameleonTemplate extends BaseTemplate {
+
+ /**
+ * Outputs the entire contents of the page
+ */
+ public function execute() {
+
+ // output the head element
+ // The headelement defines the <body> tag itself, it shouldn't be included in the html text
+ // To add attributes or classes to the body tag override addToBodyAttributes() in SkinChameleon
+ $this->html( 'headelement' );
+ echo $this->getSkin()->getComponentFactory()->getRootComponent()->getHtml();
+ $this->printTrail();
+ echo "</body>\n</html>";
+
+ }
+
+ /**
+ * Overrides method in parent class that is unprotected against non-existent indexes in $this->data
+ *
+ * @param string $key
+ *
+ * @return string|void
+ */
+ public function html( $key ) {
+ echo $this->get( $key );
+ }
+
+ /**
+ * Get the Skin object related to this object
+ *
+ * @return SkinChameleon
+ */
+ public function getSkin() {
+ return parent::getSkin();
+ }
+
+ /**
+ * @param \DOMElement $description
+ * @param int $indent
+ * @param string $htmlClassAttribute
+ *
+ * @deprecated since 1.6. Use getSkin()->getComponentFactory()->getComponent()
+ *
+ * @throws \MWException
+ * @return \Skins\Chameleon\Components\Container
+ */
+ public function getComponent( \DOMElement $description, $indent = 0, $htmlClassAttribute = '' ) {
+ return $this->getSkin()->getComponentFactory()->getComponent( $description, $indent, $htmlClassAttribute );
+ }
+
+ /**
+ * Generates a list item for a navigation, portlet, portal, sidebar... list
+ *
+ * Overrides the parent function to ensure ids are unique.
+ *
+ * @param $key string, usually a key from the list you are generating this link from.
+ * @param $item array, of list item data containing some of a specific set of keys.
+ *
+ * The "id" and "class" keys will be used as attributes for the list item,
+ * if "active" contains a value of true a "active" class will also be appended to class.
+ *
+ * @param $options array
+ *
+ * @return string
+ */
+ public function makeListItem( $key, $item, $options = array() ) {
+
+ foreach ( array( 'id', 'single-id' ) as $attrib ) {
+
+ if ( isset ( $item[ $attrib ] ) ) {
+ $item[ $attrib ] = IdRegistry::getRegistry()->getId( $item[ $attrib ], $this );
+ }
+
+ }
+
+ return parent::makeListItem( $key, $item, $options );
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/ComponentFactory.php b/www/wiki/skins/chameleon/src/ComponentFactory.php
new file mode 100644
index 00000000..f0e2d05a
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/ComponentFactory.php
@@ -0,0 +1,265 @@
+<?php
+/**
+ * File containing the ComponentFactory class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon;
+
+use DOMDocument;
+use DOMElement;
+use MWException;
+use RuntimeException;
+use Skins\Chameleon\Components\Component;
+use Skins\Chameleon\Components\Container;
+
+/**
+ * Class ComponentFactory
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class ComponentFactory {
+
+ // the root component of the page; should be of type Container
+ private $mRootComponent = null;
+
+ private $layoutFile;
+ private $skinTemplate;
+
+ const NAMESPACE_HIERARCHY = 'Skins\\Chameleon\\Components';
+
+ /**
+ * @param string $layoutFileName
+ */
+ public function __construct( $layoutFileName ) {
+ $this->setLayoutFile( $layoutFileName );
+ }
+
+ /**
+ * @return Container
+ * @throws MWException
+ */
+ public function getRootComponent() {
+
+ if ( $this->mRootComponent === null ) {
+
+ $doc = new DOMDocument();
+
+ $doc->load( $this->getLayoutFile() );
+
+ $doc->normalizeDocument();
+
+ $roots = $doc->getElementsByTagName( 'structure' );
+
+ if ( $roots->length > 0 ) {
+
+ $this->mRootComponent = $this->getComponent( $roots->item( 0 ) );
+
+ } else {
+ // TODO: catch other errors, e.g. malformed XML
+ throw new MWException( sprintf( '%s: XML description is missing an element: structure.', $this->getLayoutFile() ) );
+ }
+ }
+
+ return $this->mRootComponent;
+
+ }
+
+ /**
+ * @return string
+ */
+ protected function getLayoutFile() {
+
+ return $this->layoutFile;
+ }
+
+ /**
+ * @param string $fileName
+ */
+ public function setLayoutFile( $fileName ) {
+
+ $fileName = $this->sanitizeFileName( $fileName );
+
+ if ( !is_readable( $fileName ) ) {
+ throw new RuntimeException( "Expected an accessible {$fileName} layout file" );
+ }
+
+ $this->layoutFile = $fileName;
+ }
+
+ /**
+ * @param DOMElement $description
+ * @param int $indent
+ * @param string $htmlClassAttribute
+ *
+ * @throws MWException
+ * @return \Skins\Chameleon\Components\Container
+ */
+ public function getComponent( DOMElement $description, $indent = 0, $htmlClassAttribute = '' ) {
+
+ $className = $this->getComponentClassName( $description );
+ $component = new $className( $this->getSkinTemplate(), $description, $indent, $htmlClassAttribute );
+
+ $children = $description->childNodes;
+
+ foreach ( $children as $child ) {
+ if ( $child instanceof DOMElement && strtolower( $child->nodeName ) === 'modification' ) {
+ $component = $this->getModifiedComponent( $child, $component );
+ }
+ }
+
+ return $component;
+ }
+
+ /**
+ * @param DOMElement $description
+ *
+ * @return string
+ * @throws MWException
+ * @since 1.1
+ */
+ protected function getComponentClassName( DOMElement $description ) {
+
+ $className = $this->mapDescriptionToClassName( $description );
+
+ if ( !class_exists( $className ) || !is_subclass_of( $className, self::NAMESPACE_HIERARCHY . '\\Component' ) ) {
+ throw new MWException( sprintf( '%s (line %d): Invalid component type: %s.', $this->getLayoutFile(), $description->getLineNo(), $description->getAttribute( 'type' ) ) );
+ }
+
+ return $className;
+ }
+
+ /**
+ * @param DOMElement $description
+ *
+ * @return string
+ * @throws MWException
+ */
+ protected function mapDescriptionToClassName( DOMElement $description ) {
+
+ $nodeName = strtolower( $description->nodeName );
+
+ $mapOfComponentsToClassNames = array(
+ 'structure' => 'Structure',
+ 'grid' => 'Grid',
+ 'row' => 'Row',
+ 'cell' => 'Cell',
+ 'modification' => 'Silent',
+ );
+
+ if ( array_key_exists( $nodeName, $mapOfComponentsToClassNames ) ) {
+ return self::NAMESPACE_HIERARCHY . '\\' . $mapOfComponentsToClassNames[ $nodeName ];
+ }
+
+ if ( $nodeName === 'component' ) {
+ return $this->mapComponentDescriptionToClassName( $description );
+ }
+
+ throw new MWException( sprintf( '%s (line %d): XML element not allowed here: %s.', $this->getLayoutFile(), $description->getLineNo(), $description->nodeName ) );
+
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSkinTemplate() {
+ return $this->skinTemplate;
+ }
+
+ /**
+ * @param ChameleonTemplate $skinTemplate
+ */
+ public function setSkinTemplate( ChameleonTemplate $skinTemplate ) {
+ $this->skinTemplate = $skinTemplate;
+ }
+
+ /**
+ * @param DOMElement $description
+ * @param Component $component
+ *
+ * @return mixed
+ * @throws MWException
+ */
+ protected function getModifiedComponent( DOMElement $description, Component $component ) {
+
+ if ( !$description->hasAttribute( 'type' ) ) {
+ throw new MWException( sprintf( '%s (line %d): Modification element missing an attribute: type.', $this->getLayoutFile(), $description->getLineNo() ) );
+ }
+
+ $className = 'Skins\\Chameleon\\Components\\Modifications\\' . $description->getAttribute( 'type' );
+
+ if ( !class_exists( $className ) || !is_subclass_of( $className, 'Skins\\Chameleon\\Components\\Modifications\\Modification' ) ) {
+ throw new MWException( sprintf( '%s (line %d): Invalid modification type: %s.', $this->getLayoutFile(), $description->getLineNo(), $description->getAttribute( 'type' ) ) );
+ }
+
+ return new $className( $component, $description );
+
+ }
+
+ /**
+ * @param string $fileName
+ *
+ * @return string
+ */
+ public function sanitizeFileName( $fileName ) {
+ return str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, $fileName );
+ }
+
+ /**
+ * @param DOMElement $description
+ * @return string
+ */
+ protected function mapComponentDescriptionToClassName( DOMElement $description ) {
+
+ if ( $description->hasAttribute( 'type' ) ) {
+ $className = $description->getAttribute( 'type' );
+ $parent = $description->parentNode;
+
+ if ( $parent instanceof DOMElement && $parent->hasAttribute( 'type' ) ) {
+ $fullClassName = join(
+ '\\',
+ array(
+ self::NAMESPACE_HIERARCHY,
+ $parent->getAttribute( 'type' ),
+ $className
+ )
+ );
+
+ if ( class_exists( $fullClassName ) ) {
+ return $fullClassName;
+ }
+ }
+
+ $chameleonClassName = join( '\\', array( self::NAMESPACE_HIERARCHY, $className ) );
+ if ( !class_exists( $chameleonClassName ) ) {
+ return $className;
+ }
+
+ return $chameleonClassName;
+ }
+
+ return self::NAMESPACE_HIERARCHY . 'Container';
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Cell.php b/www/wiki/skins/chameleon/src/Components/Cell.php
new file mode 100644
index 00000000..f46c8813
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Cell.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * File holding the Cell class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Skins\Chameleon\ChameleonTemplate;
+
+/**
+ * The Cell class.
+ *
+ * @ingroup Skins
+ */
+class Cell extends Container {
+
+ public function __construct( ChameleonTemplate $template, \DOMElement $domElement = null, $indent = 0 ) {
+
+ if ( !is_null( $domElement ) ) {
+
+ $span = $domElement->getAttribute( 'span' );
+
+ if ( ( !is_int( $span ) && !ctype_digit( $span ) ) || ( $span < 1 ) || ( $span > 12 ) ) {
+ $span = '12';
+ }
+
+ } else {
+ $span = '12';
+ }
+
+ parent::__construct( $template, $domElement, $indent );
+
+ $this->addClasses( "col-lg-$span" );
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Component.php b/www/wiki/skins/chameleon/src/Components/Component.php
new file mode 100644
index 00000000..e6dc51c1
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Component.php
@@ -0,0 +1,222 @@
+<?php
+/**
+ * File containing the Component class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use SkinChameleon;
+use Skins\Chameleon\ChameleonTemplate;
+
+/**
+ * Component class
+ *
+ * This is the base class of all components.
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+abstract class Component {
+
+ private $mSkinTemplate;
+ private $mIndent = 0;
+ private $mClasses = array();
+ private $mDomElement = null;
+
+ /**
+ * @param ChameleonTemplate $template
+ * @param \DOMElement|null $domElement
+ * @param int $indent
+ */
+ public function __construct( ChameleonTemplate $template, \DOMElement $domElement = null, $indent = 0 ) {
+
+ $this->mSkinTemplate = $template;
+ $this->mIndent = (int) $indent;
+ $this->mDomElement = $domElement;
+
+ if ( $domElement !== null ) {
+ $this->addClasses( $domElement->getAttribute( 'class' ) );
+ }
+ }
+
+ /**
+ * Sets the class string that should be assigned to the top-level html element of this component
+ *
+ * @param string | array | null $classes
+ *
+ */
+ public function setClasses( $classes ) {
+
+ $this->mClasses = array();
+ $this->addClasses( $classes );
+
+ }
+
+ /**
+ * Adds the given class to the class string that should be assigned to the top-level html element of this component
+ *
+ * @param string | array | null $classes
+ *
+ * @return string | array
+ */
+ public function addClasses( $classes ) {
+
+ $classesArray = $this->transformClassesToArray( $classes );
+
+ if ( !empty( $classesArray ) ) {
+ $classesArray = array_combine( $classesArray, $classesArray );
+ $this->mClasses = array_merge( $this->mClasses, $classesArray );
+ }
+ }
+
+ /**
+ * @param string | array | null $classes
+ *
+ * @return array
+ * @throws \MWException
+ */
+ protected function transformClassesToArray ( $classes ) {
+
+ if ( empty( $classes ) ) {
+ return array();
+ } elseif ( is_array( $classes )) {
+ return $classes;
+ } elseif ( is_string( $classes ) ) {
+ return explode( ' ', $classes );
+ } else {
+ throw new \MWException( __METHOD__ . ': Expected String or Array; ' . getType( $classes ) . ' given.' );
+ }
+
+ }
+
+ /**
+ * @return ChameleonTemplate
+ */
+ public function getSkinTemplate() {
+
+ return $this->mSkinTemplate;
+ }
+
+ /**
+ * @since 1.1
+ * @return SkinChameleon
+ */
+ public function getSkin() {
+
+ return $this->mSkinTemplate->getSkin();
+ }
+
+ /**
+ * Returns the current indentation level
+ *
+ * @return int
+ */
+ public function getIndent() {
+
+ return $this->mIndent;
+ }
+
+ /**
+ * Returns the class string that should be assigned to the top-level html element of this component
+ *
+ * @return string
+ */
+ public function getClassString() {
+
+ return implode( ' ', $this->mClasses );
+ }
+
+ /**
+ * Removes the given class from the class string that should be assigned to the top-level html element of this component
+ *
+ * @param string | array | null $classes
+ *
+ * @return string
+ */
+ public function removeClasses( $classes ) {
+
+ $classesArray = $this->transformClassesToArray( $classes );
+
+ $this->mClasses = array_diff( $this->mClasses, $classesArray );
+ }
+
+ /**
+ * Returns the DOMElement from the description XML file associated with this element.
+ *
+ * @return \DOMElement
+ */
+ public function getDomElement() {
+ return $this->mDomElement;
+ }
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ abstract public function getHtml();
+
+ /**
+ * @return string[] the resource loader modules needed by this component
+ */
+ public function getResourceLoaderModules() {
+ return array();
+ }
+
+ /**
+ * Adds $indent to (or subtracts from if negative) the current indentation level.
+ * Inserts a new line and a number of tabs according to the new indentation level.
+ *
+ * @param int $indent
+ * @return string
+ * @throws \MWException
+ */
+ protected function indent( $indent = 0 ) {
+
+ $this->mIndent += (int) $indent;
+
+ if ( $this->mIndent < 0 ) {
+ throw new \MWException('Attempted HTML indentation of ' .$this->mIndent );
+ }
+
+ return "\n" . str_repeat( "\t", $this->mIndent );
+ }
+
+ /**
+ * @param string $attributeName
+ * @param null | string $default
+ * @return null | string
+ */
+ protected function getAttribute( $attributeName, $default = null ) {
+
+ $element = $this->getDomElement();
+
+ if ( $element !== null && $element->hasAttribute( $attributeName ) ) {
+ return $element->getAttribute( $attributeName );
+ }
+
+ return $default;
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Container.php b/www/wiki/skins/chameleon/src/Components/Container.php
new file mode 100644
index 00000000..4aaa1675
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Container.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * File holding the Container class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+/**
+ * The Container class.
+ *
+ * It will wrap its content elements in a DIV.
+ *
+ * Supported attributes:
+ * - class
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class Container extends Structure {
+
+ /**
+ * Builds the HTML code for the main container
+ *
+ * @return String the HTML code
+ */
+ public function getHtml(){
+
+ $ret = $this->indent() . \Html::openElement( 'div', array( 'class' => $this->getClassString() ) );
+ $this->indent( 1 );
+
+ $ret .= parent::getHtml();
+
+ $ret .= $this->indent( -1 ) . '</div>';
+
+ return $ret;
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/ContentBody.php b/www/wiki/skins/chameleon/src/Components/ContentBody.php
new file mode 100644
index 00000000..336b8172
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/ContentBody.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ * File holding the MainContent class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2016, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The MainContent class.
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class ContentBody extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $skintemplate = $this->getSkinTemplate();
+ $idRegistry = IdRegistry::getRegistry();
+
+ // START content
+ $ret =
+ $this->indent() . '<!-- start the content area -->' .
+ $this->indent() . $idRegistry->openElement( 'div',
+ array( 'id' => 'content', 'class' => 'mw-body ' . $this->getClassString() )
+ ) .
+
+ $idRegistry->element( 'a', array( 'id' => 'top' ) ) .
+ $this->indent(1) . $idRegistry->element( 'div', array( 'id' => 'mw-indicators', 'class' => 'mw-indicators', ), $this->buildMwIndicators() ) .
+
+ $this->indent() . '<div ' . \Html::expandAttributes( array(
+ 'id' => $idRegistry->getId( 'mw-js-message' ),
+ 'style' => 'display:none;'
+ )
+ ) . $skintemplate->get( 'userlangattributes' ) . '></div>';
+
+ // $ret .= $this->buildContentHeader();
+
+ if ( $skintemplate->get( 'subtitle' ) ) {
+
+ // TODO: should not use class 'small', better use class 'contentSub' and do styling in a less file
+ $ret .=
+ $this->indent() . '<!-- subtitle line; used for various things like the subpage hierarchy -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub', 'class' => 'small' ), $skintemplate->get( 'subtitle' ) );
+ $ret .= '</br>';
+
+ }
+
+ if ( $skintemplate->get( 'undelete' ) ) {
+ // TODO: should not use class 'small', better use class 'contentSub2' and do styling in a less file
+ $ret .=
+ $this->indent() . '<!-- undelete message -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub2', 'class' => 'small' ), $skintemplate->get( 'undelete' ) );
+ $ret .= '</br>';
+
+ }
+
+
+ $ret .= $this->buildContentBody();
+ $ret .= $this->buildCategoryLinks();
+
+ $ret .= $this->indent( -1 ) . '</div>';
+ // END content
+
+ return $ret;
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildContentHeader() {
+
+ $skintemplate = $this->getSkinTemplate();
+ $idRegistry = IdRegistry::getRegistry();
+
+ $ret = $this->indent() . '<div class ="contentHeader">' .
+
+ $this->indent( 1 ) . '<!-- title of the page -->' .
+ $this->indent() . $idRegistry->element( 'h1', array( 'id' => 'firstHeading', 'class' => 'firstHeading' ), $skintemplate->get( 'title' ) ) .
+
+ $this->indent() . '<!-- tagline; usually goes something like "From WikiName" primary purpose of this seems to be for printing to identify the source of the content -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id'=> 'siteSub' ), $skintemplate->getMsg( 'tagline' )->escaped() );
+
+ if ( $skintemplate->get( 'subtitle' ) ) {
+
+ // TODO: should not use class 'small', better use class 'contentSub' and do styling in a less file
+ $ret .=
+ $this->indent() . '<!-- subtitle line; used for various things like the subpage hierarchy -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub', 'class' => 'small' ), $skintemplate->get( 'subtitle' ) );
+
+ }
+
+ if ( $skintemplate->get( 'undelete' ) ) {
+ // TODO: should not use class 'small', better use class 'contentSub2' and do styling in a less file
+ $ret .=
+ $this->indent() . '<!-- undelete message -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub2', 'class' => 'small' ), $skintemplate->get( 'undelete' ) );
+ }
+
+ // TODO: Do we need this? Seems to be an accessibility thing. It's used
+ // in vector to jump to the nav which is at the bottom of the document,
+ // but our nav is usually at the top
+ $ret .= $idRegistry->element( 'div', array( 'id' => 'jump-to-nav', 'class' => 'mw-jump' ),
+ $skintemplate->getMsg( 'jumpto' )->escaped() . '<a href="#mw-navigation">' . $skintemplate->getMsg( 'jumptonavigation' )->escaped() . '</a>' .
+ $skintemplate->getMsg( 'comma-separator' )->escaped() . '<a href="#p-search">' . $skintemplate->getMsg( 'jumptosearch' )->escaped() . '</a>'
+ );
+
+ $ret .= $this->indent( -1 ) . '</div>';
+ return $ret;
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildContentBody() {
+ return $this->indent() . IdRegistry::getRegistry()->element( 'div', array( 'id' => 'bodyContent' ),
+ $this->indent( 1 ) . '<!-- body text -->' . "\n" .
+ $this->indent() . $this->getSkinTemplate()->get( 'bodytext' ) .
+ $this->indent() . '<!-- end body text -->' .
+ $this->buildDataAfterContent() .
+ $this->indent( -1 )
+ );
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildCategoryLinks() {
+ // TODO: Category links should be a separate component, but
+ // * dataAfterContent should come after the the category links.
+ // * only one extension is known to use it dataAfterContent and it is geared specifically towards MonoBook
+ // => provide an attribute hideCatLinks for the XML and -if present- hide category links and assume somebody knows what they are doing
+ return
+ $this->indent() . '<!-- category links -->' .
+ $this->indent() . $this->getSkinTemplate()->get( 'catlinks' );
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildDataAfterContent() {
+
+ $skinTemplate = $this->getSkinTemplate();
+
+ if ( $skinTemplate->get( 'dataAfterContent' ) ) {
+ return
+ $this->indent() . '<!-- data blocks which should go somewhere after the body text, but not before the catlinks block-->' .
+ $this->indent() . $skinTemplate->get( 'dataAfterContent' );
+ }
+
+ return '';
+ }
+
+ /**
+ * @return string
+ */
+ private function buildMwIndicators() {
+
+ $idRegistry = IdRegistry::getRegistry();
+ $indicators = $this->getSkinTemplate()->get( 'indicators' );
+
+ if ( !is_array( $indicators ) || count( $indicators ) === 0 ) {
+ return '';
+ }
+
+ $this->indent( 1 );
+
+ $ret = '';
+
+ foreach ( $indicators as $id => $content ) {
+ $id = \Sanitizer::escapeId( "mw-indicator-$id" );
+
+ $ret .=
+ $this->indent() .
+ $idRegistry->element( 'div',
+ array(
+ 'id' => $id,
+ 'class' => "mw-indicator $id",
+ ),
+ $content
+ );
+ }
+
+ $ret .= $this->indent( -1 );
+
+ return $ret;
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/ContentHeader.php b/www/wiki/skins/chameleon/src/Components/ContentHeader.php
new file mode 100644
index 00000000..724f60da
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/ContentHeader.php
@@ -0,0 +1,76 @@
+<?php
+/**
+ * File holding the FooterIcons class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Chameleon
+ */
+
+namespace Skins\Chameleon\Components;
+use Skins\Chameleon\ChameleonTemplate;
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The FooterIcons class.
+ *
+ * A inline list containing icons: <ul id="footer-icons" >
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class ContentHeader extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+ global $bannerImage;
+ $skintemplate = $this->getSkinTemplate();
+ $idRegistry = IdRegistry::getRegistry();
+
+ if ($bannerImage) {
+ $ret = $this->indent() . '<div class ="contentHeader bannerimage"><div class="container"><div class="row"><div class="col-lg-12">';
+ } else {
+ $ret = $this->indent() . '<div class ="contentHeader"><div class="container"><div class="row"><div class="col-lg-12">';
+ }
+ $ret .= $this->indent( 1 ) . '<!-- title of the page -->' .
+ $this->indent() . $idRegistry->element( 'h1', array( 'id' => 'firstHeading', 'class' => 'firstHeading' ), $skintemplate->get( 'title' ) ) .
+
+ $this->indent() . '<!-- tagline; usually goes something like "From WikiName" primary purpose of this seems to be for printing to identify the source of the content -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id'=> 'siteSub' ), $skintemplate->getMsg( 'tagline' )->escaped() );
+
+
+ // TODO: Do we need this? Seems to be an accessibility thing. It's used
+ // in vector to jump to the nav which is at the bottom of the document,
+ // but our nav is usually at the top
+ $ret .= $idRegistry->element( 'div', array( 'id' => 'jump-to-nav', 'class' => 'mw-jump' ),
+ $skintemplate->getMsg( 'jumpto' )->escaped() . '<a href="#mw-navigation">' . $skintemplate->getMsg( 'jumptonavigation' )->escaped() . '</a>' .
+ $skintemplate->getMsg( 'comma-separator' )->escaped() . '<a href="#p-search">' . $skintemplate->getMsg( 'jumptosearch' )->escaped() . '</a>'
+ );
+
+ $ret .= $this->indent( -1 ) . '</div></div></div></div>';
+ return $ret;
+
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/FooterIcons.php b/www/wiki/skins/chameleon/src/Components/FooterIcons.php
new file mode 100644
index 00000000..30994304
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/FooterIcons.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * File holding the FooterIcons class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Chameleon
+ */
+
+namespace Skins\Chameleon\Components;
+
+/**
+ * The FooterIcons class.
+ *
+ * A inline list containing icons: <ul id="footer-icons" >
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class FooterIcons extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $ret = '';
+ $icons = $this->getSkinTemplate()->getFooterIcons( 'icononly' );
+
+ if ( $icons !== null && count( $icons ) > 0 ) {
+
+ $ret = $this->indent() . '<!-- footer icons -->' .
+ $this->indent() . '<ul class="list-inline pull-right footer-icons ' . $this->getClassString() . '" id="footer-icons" >';
+
+ $this->indent( 1 );
+ foreach ( $icons as $blockName => $footerIcons ) {
+
+ $ret .= $this->indent() . '<!-- ' . htmlspecialchars( $blockName ) . ' -->';
+
+ foreach ( $footerIcons as $icon ) {
+ $ret .= $this->indent() . '<li>' .
+ $this->getSkinTemplate()->getSkin()->makeFooterIcon( $icon ) . '</li>';
+ }
+
+ }
+
+ $ret .= $this->indent( -1 ) . '</ul>' . "\n";
+ }
+
+ return $ret;
+
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/FooterInfo.php b/www/wiki/skins/chameleon/src/Components/FooterInfo.php
new file mode 100644
index 00000000..2d43279d
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/FooterInfo.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * File holding the FooterInfo class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Chameleon
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Skins\Chameleon\ChameleonTemplate;
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The FooterInfo class.
+ *
+ * An list of footer items (last modified time, view count, number of watching users, credits, copyright)
+ * Does not include so called places (about, privacy policy, and disclaimer links). They need to be added to the page elsewhere.
+ *
+ * This is an unstyled unordered list: <ul id="footer-info" >
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class FooterInfo extends Component {
+
+ public function __construct( ChameleonTemplate $template, \DOMElement $domElement = null, $indent = 0 ) {
+ parent::__construct( $template, $domElement , $indent );
+ $this->addClasses( 'list-unstyled small' );
+ }
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $ret = $this->indent() . '<!-- footer links -->' .
+ $this->indent() .
+ \Html::openElement( 'ul', array(
+ 'class' => 'footer-info ' . $this->getClassString(),
+ 'id' => IdRegistry::getRegistry()->getId( 'footer-info' ),
+ )
+ );
+
+ $footerlinks = $this->getSkinTemplate()->getFooterLinks();
+ $this->indent( 1 );
+ foreach ( $footerlinks as $category => $links ) {
+ if ( $category !== 'places' ) {
+
+ $ret .= $this->indent() . '<!-- ' . htmlspecialchars( $category ) . ' -->';
+ foreach ( $links as $key ) {
+ $ret .= $this->indent() . '<li>' . $this->getSkinTemplate()->get( $key ) . '</li>';
+ }
+
+ }
+ }
+
+ $ret .= $this->indent( -1 ) . '</ul>' . "\n";
+
+ return $ret;
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/FooterPlaces.php b/www/wiki/skins/chameleon/src/Components/FooterPlaces.php
new file mode 100644
index 00000000..470633ae
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/FooterPlaces.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * File holding the FooterPlaces class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+/**
+ * The FooterInfo class.
+ *
+ * A inline list containing links to places (about, privacy policy, and disclaimer links): <ul id="footer-places">
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class FooterPlaces extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $ret = null;
+ $footerlinks = $this->getSkinTemplate()->getFooterLinks();
+
+ if ( array_key_exists( 'places', $footerlinks ) ) {
+
+ $ret = $this->indent() . '<!-- places -->' .
+ $this->indent() . '<ul class="list-inline footer-places ' . $this->getClassString() . '" id="footer-places">';
+
+ $this->indent( 1 );
+ foreach ( $footerlinks[ 'places' ] as $key ) {
+ $ret .= $this->indent() . '<li><small>' . $this->getSkinTemplate()->get( $key ) . '</small></li>';
+ }
+ $ret .= $this->indent( -1 ) . '</ul>' . "\n";
+ }
+
+ return $ret;
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Grid.php b/www/wiki/skins/chameleon/src/Components/Grid.php
new file mode 100644
index 00000000..79c1472c
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Grid.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * File holding the Grid class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Skins\Chameleon\ChameleonTemplate;
+
+/**
+ * The Grid class.
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class Grid extends Container {
+
+ const ATTR_MODE = 'mode';
+ const MODE_FIXEDWIDTH = 'fixedwidth';
+ const MODE_FLUID = 'fluid';
+
+ public function __construct( ChameleonTemplate $template, \DOMElement $domElement = null, $indent = 0 ) {
+
+ parent::__construct( $template, $domElement, $indent );
+
+ if( $this->isFluidMode() ) {
+ $this->addClasses( 'container-fluid' );
+ } else {
+ $this->addClasses( 'container' );
+ }
+ }
+
+ protected function isFluidMode() {
+ return $this->getAttribute( self::ATTR_MODE, self::MODE_FIXEDWIDTH ) === self::MODE_FLUID;
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/skins/chameleon/src/Components/Html.php b/www/wiki/skins/chameleon/src/Components/Html.php
new file mode 100644
index 00000000..9297669e
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Html.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * File holding the Html class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+/**
+ * The Html class.
+ *
+ * This component allows insertion of raw HTML into the page.
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class Html extends Component {
+
+ /**
+ * Builds the HTML code for the main container
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $ret = '';
+
+ if ( $this->getDomElement() !== null ) {
+
+ $dom = $this->getDomElement()->ownerDocument;
+
+ foreach ( $this->getDomElement()->childNodes as $node ) {
+ $ret .= $dom->saveHTML( $node );
+ }
+ }
+
+ return $ret;
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/LastmodInfo.php b/www/wiki/skins/chameleon/src/Components/LastmodInfo.php
new file mode 100644
index 00000000..29ef8d4c
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/LastmodInfo.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * File holding the LastmodInfo class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Chameleon
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Skins\Chameleon\ChameleonTemplate;
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The LastmodInfo class.
+ *
+ * An list of footer items (last modified time, view count, number of watching users, credits, copyright)
+ * Does not include so called places (about, privacy policy, and disclaimer links). They need to be added to the page elsewhere.
+ *
+ * This is an unstyled unordered list: <ul id="footer-info" >
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class LastmodInfo extends Component {
+
+ public function __construct( ChameleonTemplate $template, \DOMElement $domElement = null, $indent = 0 ) {
+ parent::__construct( $template, $domElement , $indent );
+ $this->addClasses( 'list-unstyled small' );
+ }
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $ret = $this->indent() . '<!-- footer links -->' .
+ $this->indent() .
+ \Html::openElement( 'ul', array(
+ 'class' => 'lastmod-info ' . $this->getClassString(),
+ 'id' => IdRegistry::getRegistry()->getId( 'lastmod-info' ),
+ )
+ );
+
+ $footerlinks = $this->getSkinTemplate()->getFooterLinks();
+ $this->indent( 1 );
+ foreach ( $footerlinks as $category => $links ) {
+ if ( $category !== 'places' ) {
+
+ $ret .= $this->indent() . '<!-- ' . htmlspecialchars( $category ) . ' -->';
+ foreach ( $links as $key ) {
+ if ( $key !== 'copyright' ) {
+ $ret .= $this->indent() . '<li>' . $this->getSkinTemplate()->get( $key ) . '</li>';
+ }
+ }
+
+ }
+ }
+
+ $ret .= $this->indent( -1 ) . '</ul>' . "\n";
+
+ return $ret;
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/LegalInfo.php b/www/wiki/skins/chameleon/src/Components/LegalInfo.php
new file mode 100644
index 00000000..c302b624
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/LegalInfo.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * File holding the LegalInfo class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Chameleon
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Skins\Chameleon\ChameleonTemplate;
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The LegalInfo class.
+ *
+ * An list of footer items (last modified time, view count, number of watching users, credits, copyright)
+ * Does not include so called places (about, privacy policy, and disclaimer links). They need to be added to the page elsewhere.
+ *
+ * This is an unstyled unordered list: <ul id="footer-info" >
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class LegalInfo extends Component {
+
+ public function __construct( ChameleonTemplate $template, \DOMElement $domElement = null, $indent = 0 ) {
+ parent::__construct( $template, $domElement , $indent );
+ $this->addClasses( 'list-unstyled small' );
+ }
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $ret = $this->indent() . '<!-- footer links -->' .
+ $this->indent() .
+ \Html::openElement( 'ul', array(
+ 'class' => 'footer-legal-info ' . $this->getClassString(),
+ 'id' => IdRegistry::getRegistry()->getId( 'footer-legal-info' ),
+ )
+ );
+
+ $footerlinks = $this->getSkinTemplate()->getFooterLinks();
+ $this->indent( 1 );
+ foreach ( $footerlinks as $category => $links ) {
+ if ( $category !== 'places' ) {
+
+ $ret .= $this->indent() . '<!-- ' . htmlspecialchars( $category ) . ' -->';
+ foreach ( $links as $key ) {
+ if ( $key == 'copyright' ) {
+ $ret .= $this->indent() . '<li>' . $this->getSkinTemplate()->get( $key ) . '</li>';
+ }
+ }
+
+ }
+ }
+
+ $ret .= $this->indent( -1 ) . '</ul>' . "\n";
+
+ return $ret;
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Logo.php b/www/wiki/skins/chameleon/src/Components/Logo.php
new file mode 100644
index 00000000..be592ed2
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Logo.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * File holding the Logo class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2016, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Linker;
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The Logo class.
+ *
+ * The logo image as a link to the wiki main page wrapped in a div: <div id="p-logo" role="banner">
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class Logo extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $attribs = NULL;
+ if ( $this->addLink() ) {
+ $attribs = array_merge(
+ array( 'href' => $this->getSkinTemplate()->data[ 'nav_urls' ][ 'mainpage' ][ 'href' ] ),
+ Linker::tooltipAndAccesskeyAttribs( 'p-logo' )
+ );
+ }
+
+ $contents = \Html::element( 'img',
+ array(
+ 'src' => $this->getSkinTemplate()->data[ 'logopath' ],
+ 'alt' => $this->getSkinTemplate()->data[ 'sitename' ],
+ )
+ );
+
+ return
+ $this->indent() . '<!-- logo and main page link -->' .
+ $this->indent() . \Html::openElement( 'div',
+ array(
+ 'id' => IdRegistry::getRegistry()->getId( 'p-logo' ),
+ 'class' => 'p-logo ' . $this->getClassString(),
+ 'role' => 'banner'
+ )
+ ) .
+ $this->indent( 1 ) . \Html::rawElement( 'a', $attribs, $contents ) .
+ $this->indent( -1 ) . '</div>' . "\n";
+ }
+
+ /**
+ * Return true if addLink attribute is unset or set to 'yes' in the Logo
+ * component description. Clicking on the logo should redirect to Main Page
+ * in that case. Else the logo should just display an inactive image.
+ *
+ * @return bool
+ */
+ private function addLink() {
+ if ( $this->getDomElement() === null ) {
+ return true;
+ }
+
+ $addLink = $this->getDomElement()->getAttribute( 'addLink' );
+
+ if ( $addLink === '' ) {
+ return true;
+ }
+
+ return filter_var( $addLink, FILTER_VALIDATE_BOOLEAN );
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/MainContent.php b/www/wiki/skins/chameleon/src/Components/MainContent.php
new file mode 100644
index 00000000..c732bd24
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/MainContent.php
@@ -0,0 +1,215 @@
+<?php
+/**
+ * File holding the MainContent class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2016, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The MainContent class.
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class MainContent extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $skintemplate = $this->getSkinTemplate();
+ $idRegistry = IdRegistry::getRegistry();
+
+ // START content
+ $ret =
+ $this->indent() . '<!-- start the content area -->' .
+ $this->indent() . $idRegistry->openElement( 'div',
+ array( 'id' => 'content', 'class' => 'mw-body ' . $this->getClassString() )
+ ) .
+
+ $idRegistry->element( 'a', array( 'id' => 'top' ) ) .
+ $this->indent(1) . $idRegistry->element( 'div', array( 'id' => 'mw-indicators', 'class' => 'mw-indicators', ), $this->buildMwIndicators() ) .
+
+ $this->indent() . '<div ' . \Html::expandAttributes( array(
+ 'id' => $idRegistry->getId( 'mw-js-message' ),
+ 'style' => 'display:none;'
+ )
+ ) . $skintemplate->get( 'userlangattributes' ) . '></div>';
+
+ $ret .= $this->buildContentHeader();
+
+ // if ( $skintemplate->get( 'subtitle' ) ) {
+
+ // TODO: should not use class 'small', better use class 'contentSub' and do styling in a less file
+ $ret .=
+ $this->indent() . '<!-- subtitle line; used for various things like the subpage hierarchy -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub', 'class' => 'small' ), $skintemplate->get( 'subtitle' ) );
+
+ // }
+
+ if ( $skintemplate->get( 'undelete' ) ) {
+ // TODO: should not use class 'small', better use class 'contentSub2' and do styling in a less file
+ $ret .=
+ $this->indent() . '<!-- undelete message -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub2', 'class' => 'small' ), $skintemplate->get( 'undelete' ) );
+ }
+
+
+ $ret .= $this->buildContentBody();
+ $ret .= $this->buildCategoryLinks();
+
+ $ret .= $this->indent( -1 ) . '</div>';
+ // END content
+
+ return $ret;
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildContentHeader() {
+
+ $skintemplate = $this->getSkinTemplate();
+ $idRegistry = IdRegistry::getRegistry();
+
+ $ret = $this->indent() . '<div class ="contentHeader">' .
+
+ $this->indent( 1 ) . '<!-- title of the page -->' .
+ $this->indent() . $idRegistry->element( 'h1', array( 'id' => 'firstHeading', 'class' => 'firstHeading' ), $skintemplate->get( 'title' ) ) .
+
+ $this->indent() . '<!-- tagline; usually goes something like "From WikiName" primary purpose of this seems to be for printing to identify the source of the content -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id'=> 'siteSub' ), $skintemplate->getMsg( 'tagline' )->escaped() );
+
+ if ( $skintemplate->get( 'subtitle' ) ) {
+
+ // TODO: should not use class 'small', better use class 'contentSub' and do styling in a less file
+ $ret .=
+ $this->indent() . '<!-- subtitle line; used for various things like the subpage hierarchy -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub', 'class' => 'small' ), $skintemplate->get( 'subtitle' ) );
+
+ }
+
+ if ( $skintemplate->get( 'undelete' ) ) {
+ // TODO: should not use class 'small', better use class 'contentSub2' and do styling in a less file
+ $ret .=
+ $this->indent() . '<!-- undelete message -->' .
+ $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub2', 'class' => 'small' ), $skintemplate->get( 'undelete' ) );
+ }
+
+ // TODO: Do we need this? Seems to be an accessibility thing. It's used
+ // in vector to jump to the nav which is at the bottom of the document,
+ // but our nav is usually at the top
+ $ret .= $idRegistry->element( 'div', array( 'id' => 'jump-to-nav', 'class' => 'mw-jump' ),
+ $skintemplate->getMsg( 'jumpto' )->escaped() . '<a href="#mw-navigation">' . $skintemplate->getMsg( 'jumptonavigation' )->escaped() . '</a>' .
+ $skintemplate->getMsg( 'comma-separator' )->escaped() . '<a href="#p-search">' . $skintemplate->getMsg( 'jumptosearch' )->escaped() . '</a>'
+ );
+
+ $ret .= $this->indent( -1 ) . '</div>';
+ return $ret;
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildContentBody() {
+ return $this->indent() . IdRegistry::getRegistry()->element( 'div', array( 'id' => 'bodyContent' ),
+ $this->indent( 1 ) . '<!-- body text -->' . "\n" .
+ $this->indent() . $this->getSkinTemplate()->get( 'bodytext' ) .
+ $this->indent() . '<!-- end body text -->' .
+ $this->buildDataAfterContent() .
+ $this->indent( -1 )
+ );
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildCategoryLinks() {
+ // TODO: Category links should be a separate component, but
+ // * dataAfterContent should come after the the category links.
+ // * only one extension is known to use it dataAfterContent and it is geared specifically towards MonoBook
+ // => provide an attribute hideCatLinks for the XML and -if present- hide category links and assume somebody knows what they are doing
+ return
+ $this->indent() . '<!-- category links -->' .
+ $this->indent() . $this->getSkinTemplate()->get( 'catlinks' );
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildDataAfterContent() {
+
+ $skinTemplate = $this->getSkinTemplate();
+
+ if ( $skinTemplate->get( 'dataAfterContent' ) ) {
+ return
+ $this->indent() . '<!-- data blocks which should go somewhere after the body text, but not before the catlinks block-->' .
+ $this->indent() . $skinTemplate->get( 'dataAfterContent' );
+ }
+
+ return '';
+ }
+
+ /**
+ * @return string
+ */
+ private function buildMwIndicators() {
+
+ $idRegistry = IdRegistry::getRegistry();
+ $indicators = $this->getSkinTemplate()->get( 'indicators' );
+
+ if ( !is_array( $indicators ) || count( $indicators ) === 0 ) {
+ return '';
+ }
+
+ $this->indent( 1 );
+
+ $ret = '';
+
+ foreach ( $indicators as $id => $content ) {
+ $id = \Sanitizer::escapeId( "mw-indicator-$id" );
+
+ $ret .=
+ $this->indent() .
+ $idRegistry->element( 'div',
+ array(
+ 'id' => $id,
+ 'class' => "mw-indicator $id",
+ ),
+ $content
+ );
+ }
+
+ $ret .= $this->indent( -1 );
+
+ return $ret;
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Menu.php b/www/wiki/skins/chameleon/src/Components/Menu.php
new file mode 100644
index 00000000..86ae5831
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Menu.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * File holding the Menu component class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Sanitizer;
+use Skins\Chameleon\Menu\MenuFactory;
+
+/**
+ * Class Menu
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class Menu extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ if ( $this->getDomElement() === null ) {
+ return '';
+ }
+
+ $menu = $this->getMenu();
+
+ $menu->setMenuItemFormatter( function ( $href, $text, $depth, $subitems ) {
+ $href = Sanitizer::cleanUrl( $href );
+ $text = htmlspecialchars( $text );
+ if ( $depth === 1 && !empty( $subitems ) ) {
+ return "<li class=\"dropdown\"><a class=\"dropdown-toggle\" href=\"#\" data-toggle=\"dropdown\">$text<b class=\"caret\"></b></a>$subitems</li>";
+ } else {
+ return "<li><a href=\"$href\">$text</a>$subitems</li>";
+ }
+ } );
+
+ $menu->setItemListFormatter( function ( $rawItemsHtml, $depth ) {
+ if ( $depth === 0 ) {
+ return $rawItemsHtml;
+ } elseif ( $depth === 1 ) {
+ return "<ul class=\"dropdown-menu\">$rawItemsHtml</ul>";
+ } else {
+ return "<ul>$rawItemsHtml</ul>";
+ }
+
+ } );
+
+ return $menu->getHtml();
+ }
+
+ /**
+ * @return \Skins\Chameleon\Menu\Menu
+ */
+ public function getMenu() {
+
+ $domElement = $this->getDomElement();
+ $msgKey = $domElement->getAttribute( 'message' );
+
+ $menuFactory = new MenuFactory();
+
+ if ( empty( $msgKey ) ) {
+ return $menuFactory->getMenuFromMessageText( $domElement->textContent );
+ } else {
+ return $menuFactory->getMenuFromMessage( $msgKey );
+
+ }
+
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Modifications/HideFor.php b/www/wiki/skins/chameleon/src/Components/Modifications/HideFor.php
new file mode 100644
index 00000000..58d78a6c
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Modifications/HideFor.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * File containing the HideFor class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\Modifications;
+use Skins\Chameleon\Components\Silent;
+use Skins\Chameleon\PermissionsHelper;
+
+/**
+ * HideFor class
+ *
+ * @author Stephan Gambke
+ * @since 1.1
+ * @ingroup Skins
+ */
+class HideFor extends Modification {
+
+ private $permissionsHelper;
+
+ /**
+ * This method checks if the restriction is applicable and if necessary
+ * replaces the decorated component by a Silent component
+ */
+ protected function applyModification() {
+ if ( $this->isHidden() ) {
+ $c = $this->getComponent();
+ $this->setComponent( new Silent( $c->getSkinTemplate(), $c->getDomElement(), $c->getIndent() ) );
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ private function isHidden() {
+ $p = $this->getPermissionsHelper();
+ return $p->userHasGroup( 'group' ) && $p->userHasPermission( 'permission' ) && $p->pageIsInNamespace( 'namespace' ) && $p->pageHasCategory( 'category' );
+ }
+
+ /**
+ * @return PermissionsHelper
+ */
+ private function getPermissionsHelper() {
+ if ( $this->permissionsHelper === null ) {
+ $this->permissionsHelper = new PermissionsHelper( $this->getSkinTemplate()->getSkin(), $this->getDomElementOfModification(), true );
+ }
+
+ return $this->permissionsHelper;
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Modifications/Modification.php b/www/wiki/skins/chameleon/src/Components/Modifications/Modification.php
new file mode 100644
index 00000000..5fd34b19
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Modifications/Modification.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * File containing the Modification class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\Modifications;
+
+use Skins\Chameleon\ChameleonTemplate;
+use Skins\Chameleon\Components\Component;
+
+/**
+ * Modification class
+ *
+ * This is the abstract base class of all modifications.
+ *
+ * Follows the Decorator pattern (Decorator role).
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+abstract class Modification extends Component {
+
+ private $component = null;
+
+ /**
+ * @param Component $component
+ * @param \DOMElement $domElement
+ */
+ public function __construct( Component $component, \DOMElement $domElement = null ) {
+
+ $this->component = $component;
+ parent::__construct( $component->getSkinTemplate(), $domElement, $component->getIndent() );
+ }
+
+ /**
+ * This method should apply any modifications to the decorated component
+ * available from the getComponent() method.
+ */
+ abstract protected function applyModification();
+
+ /**
+ * @return \DOMElement|null
+ */
+ public function getDomElementOfModification() {
+ return parent::getDomElement();
+ }
+
+ /**
+ * @return \DOMElement
+ */
+ public function getDomElementOfComponent() {
+ return $this->getDomElement();
+ }
+
+ /**
+ * Sets the class string that should be assigned to the top-level html element of this component
+ *
+ * @param string | array | null $classes
+ *
+ */
+ public function setClasses( $classes ) {
+ $this->getComponent()->setClasses( $classes );
+ }
+
+ /**
+ * @return Component
+ */
+ public function getComponent() {
+ return $this->component;
+ }
+
+ /**
+ * @param Component $component
+ * @since 1.1
+ */
+ protected function setComponent( Component $component ) {
+ $this->component = $component;
+ }
+
+ /**
+ * Adds the given class to the class string that should be assigned to the top-level html element of this component
+ *
+ * @param string | array | null $classes
+ *
+ * @return string | array
+ */
+ public function addClasses( $classes ) {
+ $this->getComponent()->addClasses( $classes );
+ }
+
+ /**
+ * @return ChameleonTemplate
+ */
+ public function getSkinTemplate() {
+ return $this->getComponent()->getSkinTemplate();
+ }
+
+ /**
+ * Returns the current indentation level
+ *
+ * @return int
+ */
+ public function getIndent() {
+ return $this->getComponent()->getIndent();
+ }
+
+ /**
+ * Returns the class string that should be assigned to the top-level html element of this component
+ *
+ * @return string
+ */
+ public function getClassString() {
+ return $this->getComponent()->getClassString();
+ }
+
+ /**
+ * Removes the given class from the class string that should be assigned to the top-level html element of this component
+ *
+ * @param string | array | null $classes
+ *
+ * @return string
+ */
+ public function removeClasses( $classes ) {
+ $this->getComponent()->removeClasses( $classes );
+ }
+
+ /**
+ * Returns the DOMElement from the description XML file associated with this element.
+ *
+ * @return \DOMElement
+ */
+ public function getDomElement() {
+ return $this->getComponent()->getDomElement();
+ }
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+ $this->applyModification();
+ return $this->getComponent()->getHtml();
+ }
+
+ /**
+ * @return array the resource loader modules needed by this component
+ */
+ public function getResourceLoaderModules() {
+ return $this->getComponent()->getResourceLoaderModules();
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Modifications/ShowOnlyFor.php b/www/wiki/skins/chameleon/src/Components/Modifications/ShowOnlyFor.php
new file mode 100644
index 00000000..7c137d81
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Modifications/ShowOnlyFor.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * File containing the ShowOnlyFor class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\Modifications;
+use Skins\Chameleon\Components\Silent;
+use Skins\Chameleon\PermissionsHelper;
+
+/**
+ * ShowOnlyFor class
+ *
+ * @author Stephan Gambke
+ * @since 1.1
+ * @ingroup Skins
+ */
+class ShowOnlyFor extends Modification {
+
+ private $permissionsHelper;
+
+ /**
+ * This method checks if the restriction is applicable and if necessary
+ * replaces the decorated component by a Silent component
+ */
+ protected function applyModification() {
+ if ( ! $this->isShown() ) {
+ $c = $this->getComponent();
+ $this->setComponent( new Silent( $c->getSkinTemplate(), $c->getDomElement(), $c->getIndent() ) );
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ private function isShown() {
+ $p = $this->getPermissionsHelper();
+ return $p->userHasGroup( 'group' ) || $p->userHasPermission( 'permission' ) || $p->pageIsInNamespace( 'namespace' ) || $p->pageHasCategory( 'category' );
+ }
+
+ /**
+ * @return PermissionsHelper
+ */
+ private function getPermissionsHelper() {
+ if ( $this->permissionsHelper === null ) {
+ $this->permissionsHelper = new PermissionsHelper( $this->getSkinTemplate()->getSkin(), $this->getDomElementOfModification(), false );
+ }
+
+ return $this->permissionsHelper;
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Modifications/Sticky.php b/www/wiki/skins/chameleon/src/Components/Modifications/Sticky.php
new file mode 100644
index 00000000..cbac2107
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Modifications/Sticky.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * File containing the Sticky class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\Modifications;
+
+/**
+ * Class Sticky
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class Sticky extends Modification {
+
+ protected function applyModification() {
+ $this->getComponent()->addClasses( 'sticky' );
+ }
+
+ /**
+ * @return string[] the resource loader modules needed by this component
+ */
+ public function getResourceLoaderModules() {
+ $modules = parent::getResourceLoaderModules();
+ $modules[] = 'skin.chameleon.jquery-sticky';
+ return $modules;
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/NavMenu.php b/www/wiki/skins/chameleon/src/Components/NavMenu.php
new file mode 100644
index 00000000..65b9aa8f
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NavMenu.php
@@ -0,0 +1,221 @@
+<?php
+/**
+ * File holding the NavMenu class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Linker;
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The NavMenu class.
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class NavMenu extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return string the HTML code
+ */
+ public function getHtml() {
+
+ $ret = '';
+
+ $sidebar = $this->getSkinTemplate()->getSidebar( array(
+ 'search' => false, 'toolbox' => $this->showTools(), 'languages' => $this->showLanguages()
+ )
+ );
+
+ $flatten = $this->getMenusToBeFlattened();
+
+ // create a dropdown for each sidebar box
+ foreach ( $sidebar as $menuName => $menuDescription ) {
+ $ret .= $this->getDropdownForNavMenu( $menuName, $menuDescription, array_search( $menuName, $flatten ) !== false );
+ }
+
+ return $ret;
+ }
+
+ /**
+ * @return bool
+ */
+ private function showLanguages() {
+ return $this->getDomElement() !== null &&
+ filter_var( $this->getDomElement()->getAttribute( 'showLanguages' ), FILTER_VALIDATE_BOOLEAN );
+ }
+
+ /**
+ * @return bool
+ */
+ private function showTools() {
+ return $this->getDomElement() !== null &&
+ filter_var( $this->getDomElement()->getAttribute( 'showTools' ), FILTER_VALIDATE_BOOLEAN );
+ }
+
+ /**
+ * Create a single dropdown
+ *
+ * @param string $menuName
+ * @param mixed[] $menuDescription
+ * @param bool $flatten
+ *
+ * @return string
+ */
+ protected function getDropdownForNavMenu( $menuName, $menuDescription, $flatten = false ) {
+
+ // open list item containing the dropdown
+ $ret = $this->indent() . '<!-- ' . $menuName . ' -->';
+
+ if ( $flatten ) {
+
+ $ret .= $this->buildMenuItemsForDropdownMenu( $menuDescription );
+
+ } elseif ( !$this->hasSubmenuItems( $menuDescription ) ) {
+
+ $ret .= $this->buildDropdownMenuStub( $menuDescription );
+
+ } else {
+
+ $ret .= $this->buildDropdownOpeningTags( $menuDescription ) .
+ $this->buildMenuItemsForDropdownMenu( $menuDescription, 2 ) .
+ $this->buildDropdownClosingTags();
+
+
+ }
+
+ return $ret;
+ }
+
+ /**
+ * @param mixed[] $menuDescription
+ * @param int $indent
+ *
+ * @return string
+ */
+ protected function buildMenuItemsForDropdownMenu( $menuDescription, $indent = 0 ) {
+
+ // build the list of submenu items
+ if ( $this->hasSubmenuItems( $menuDescription ) ) {
+
+ $menuitems = '';
+ $this->indent( $indent );
+
+ foreach ( $menuDescription['content'] as $key => $item ) {
+ $menuitems .= $this->indent() . $this->getSkinTemplate()->makeListItem( $key, $item );
+ }
+
+ $this->indent( - $indent );
+
+ return $menuitems;
+
+ } else {
+ return $this->indent() . '<!-- empty -->';
+ }
+ }
+
+ /**
+ * @param mixed[] $menuDescription
+ *
+ * @return bool
+ */
+ protected function hasSubmenuItems( $menuDescription ) {
+ return is_array( $menuDescription['content'] ) && count( $menuDescription['content'] ) > 0;
+ }
+
+ /**
+ * @param mixed[] $menuDescription
+ *
+ * @return string
+ */
+ protected function buildDropdownMenuStub( $menuDescription ) {
+ return
+ $this->indent() . \Html::rawElement( 'li',
+ array(
+ 'class' => '',
+ 'title' => Linker::titleAttrib( $menuDescription[ 'id' ] )
+ ),
+ '<a href="#">' . htmlspecialchars( $menuDescription[ 'header' ] ) . '</a>'
+ );
+ }
+
+ /**
+ * @param mixed[] $menuDescription
+ *
+ * @return string
+ */
+ protected function buildDropdownOpeningTags( $menuDescription ) {
+ // open list item containing the dropdown
+ $ret = $this->indent() . \Html::openElement( 'li',
+ array(
+ 'class' => 'dropdown',
+ 'title' => Linker::titleAttrib( $menuDescription['id'] ),
+ ) );
+
+ // add the dropdown toggle
+ $ret .= $this->indent( 1 ) . '<a href="#" class="dropdown-toggle" data-toggle="dropdown">' .
+ htmlspecialchars( $menuDescription['header'] ) . ' <b class="caret"></b></a>';
+
+ // open list of dropdown menu items
+ $ret .= $this->indent() .
+ $this->indent() . \Html::openElement( 'ul',
+ array(
+ 'class' => 'dropdown-menu ' . $menuDescription[ 'id' ],
+ 'id' => IdRegistry::getRegistry()->getId( $menuDescription[ 'id' ] ),
+ )
+ );
+ return $ret;
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildDropdownClosingTags() {
+ return $this->indent() . '</ul>' . $this->indent( - 1 ) . '</li>';
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getMenusToBeFlattened() {
+ $msg = \Message::newFromKey( 'skin-chameleon-navmenu-flatten' );
+
+ if ( $msg->exists() ) {
+ $flatten = array_map( 'trim', explode( ';', $msg->plain() ) );
+ } elseif ( $this->getDomElement() !== null ) {
+ $flatten =
+ array_map( 'trim',
+ explode( ';', $this->getDomElement()->getAttribute( 'flatten' ) ) );
+ } else {
+ $flatten = array();
+ }
+
+ return $flatten;
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/NavbarHorizontal.php b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal.php
new file mode 100644
index 00000000..3694382a
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal.php
@@ -0,0 +1,256 @@
+<?php
+/**
+ * File holding the NavbarHorizontal class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use DOMElement;
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The NavbarHorizontal class.
+ *
+ * A horizontal navbar containing the sidebar items.
+ * Does not include standard items (toolbox, search, language links). They need
+ * to be added to the page elsewhere
+ *
+ * The navbar is a list of lists wrapped in a nav element: <nav
+ * role="navigation" id="p-navbar" >
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class NavbarHorizontal extends Component {
+
+ private $mHtml = null;
+ private $htmlId = null;
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ if ( $this->mHtml === null ) {
+ $this->buildHtml();
+ }
+
+ return $this->mHtml;
+ }
+
+ /**
+ *
+ */
+ protected function buildHtml() {
+
+ if ( $this->getDomElement() === null ) {
+ $this->mHtml = '';
+ return;
+ }
+
+ $this->mHtml =
+ $this->buildFixedNavBarIfRequested() .
+ $this->buildNavBarOpeningTags() .
+ $this->buildNavBarComponents() .
+ $this->buildNavBarClosingTags();
+ }
+
+ /**
+ *
+ */
+ protected function buildFixedNavBarIfRequested() {
+ // if a fixed navbar is requested
+ if ( filter_var( $this->getDomElement()->getAttribute( 'fixed' ), FILTER_VALIDATE_BOOLEAN ) === true ||
+ $this->getDomElement()->getAttribute( 'position' ) === 'fixed'
+ ) {
+
+ // first build the actual navbar and set a class so it will be fixed
+ $this->getDomElement()->setAttribute( 'fixed', '0' );
+ $this->getDomElement()->setAttribute( 'position', '' );
+
+ $realNav = new self( $this->getSkinTemplate(), $this->getDomElement(), $this->getIndent() );
+ $realNav->setClasses( $this->getClassString() . ' navbar-fixed-top' );
+
+ // then add an invisible copy of the nav bar that will act as a spacer
+ $this->addClasses( 'navbar-static-top invisible' );
+
+ return $realNav->getHtml();
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildNavBarOpeningTags() {
+ $openingTags =
+ $this->indent() . '<!-- navigation bar -->' .
+ $this->indent() . \Html::openElement( 'nav', array(
+ 'class' => 'navbar navbar-default p-navbar ' . $this->getClassString(),
+ 'role' => 'navigation',
+ 'id' => $this->getHtmlId()
+ )
+ ) .
+ $this->indent( 1 ) . '<div class="container">';
+
+ $this->indent( 1 );
+
+ return $openingTags;
+ }
+
+ /**
+ * @return string
+ */
+ private function getHtmlId() {
+ if ( $this->htmlId === null ) {
+ $this->htmlId = IdRegistry::getRegistry()->getId( 'mw-navigation' );
+ }
+ return $this->htmlId;
+ }
+
+ /**
+ *
+ */
+ protected function buildNavBarComponents() {
+
+ $elements = $this->buildNavBarElementsFromDomTree();
+
+ if ( !empty( $elements[ 'right' ] ) ) {
+
+ $elements[ 'left' ][ ] =
+ $this->indent( 1 ) . '<div class="navbar-right-aligned">' .
+ implode( $elements[ 'right' ] ) .
+ $this->indent() . '</div> <!-- navbar-right-aligned -->';
+
+ $this->indent( -1 );
+ }
+
+ return
+ $this->buildHead( $elements[ 'head' ] ) .
+ $this->buildTail( $elements[ 'left' ] );
+ }
+
+ /**
+ * @return string[][]
+ */
+ protected function buildNavBarElementsFromDomTree() {
+
+ $elements = array(
+ 'head' => array(),
+ 'left' => array(),
+ 'right' => array(),
+ );
+
+ /** @var \DOMElement[] $children */
+ $children = $this->getDomElement()->hasChildNodes() ? $this->getDomElement()->childNodes : array();
+
+ // add components
+ foreach ( $children as $node ) {
+ $this->buildAndCollectNavBarElementFromDomElement( $node, $elements );
+ }
+ return $elements;
+ }
+
+ /**
+ * @param DOMElement $node
+ * @param $elements
+ */
+ protected function buildAndCollectNavBarElementFromDomElement( $node, &$elements ) {
+
+ if ( $node instanceof DOMElement && $node->tagName === 'component' && $node->hasAttribute( 'type' ) ) {
+
+ $position = $node->getAttribute( 'position' );
+
+ if ( !array_key_exists( $position, $elements ) ) {
+ $position = 'left';
+ }
+
+ $indentation = ( $position === 'right' ) ? 2 : 1;
+
+ $this->indent( $indentation );
+ $html = $this->buildNavBarElementFromDomElement( $node );
+ $this->indent( -$indentation );
+
+ $elements[ $position ][ ] = $html;
+
+ // } else {
+ // TODO: Warning? Error?
+ }
+ }
+
+ /**
+ * @param \DomElement $node
+ *
+ * @return string
+ */
+ protected function buildNavBarElementFromDomElement( $node ) {
+ return $this->getSkin()->getComponentFactory()->getComponent( $node, $this->getIndent() )->getHtml();
+ }
+
+ /**
+ * @param string[] $headElements
+ *
+ * @return string
+ */
+ protected function buildHead( $headElements ) {
+
+ $head =
+ $this->indent() . "<div class=\"navbar-header\">\n" .
+ $this->indent( 1 ) . "<button type=\"button\" class=\"navbar-toggle collapsed\" data-toggle=\"collapse\" data-target=\"#" . $this->getHtmlId() . "-collapse\">" .
+ $this->indent( 1 ) . "<span class=\"sr-only\">Toggle navigation</span>" .
+ $this->indent() . str_repeat( "<span class=\"icon-bar\"></span>", 3 ) .
+ $this->indent( -1 ) . "</button>\n" .
+ implode( '', $headElements ) . "\n" .
+ $this->indent( -1 ) . "</div>\n";
+
+ return $head;
+ }
+
+ /**
+ * @param string[] $tailElements
+ *
+ * @return string
+ */
+ protected function buildTail( $tailElements ) {
+
+ return
+ $this->indent() . '<div class="collapse navbar-collapse" id="' . $this->getHtmlId() . '-collapse">' .
+ implode( '', $tailElements ) .
+ $this->indent() . '</div><!-- /.navbar-collapse -->';
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildNavBarClosingTags() {
+ return
+ $this->indent( -1 ) . '</div>' .
+ $this->indent( -1 ) . '</nav>' . "\n";
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Logo.php b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Logo.php
new file mode 100644
index 00000000..d4e42823
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Logo.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * File holding the NavbarHorizontal\Logo class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\NavbarHorizontal;
+
+use Skins\Chameleon\Components\Component;
+use Skins\Chameleon\Components\Logo as GenLogo;
+
+/**
+ * The NavbarHorizontal\Logo class.
+ *
+ * Provides a Logo component to be included in a NavbarHorizontal component.
+ *
+ * @author Stephan Gambke
+ * @since 1.6
+ * @ingroup Skins
+ */
+class Logo extends Component {
+
+ /**
+ * @return String
+ */
+ public function getHtml() {
+
+ $logo = new GenLogo( $this->getSkinTemplate(), $this->getDomElement(), $this->getIndent() );
+ $logo->addClasses( 'navbar-brand' );
+
+ return $logo->getHtml();
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Menu.php b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Menu.php
new file mode 100644
index 00000000..e47dca25
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Menu.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * File holding the NavbarHorizontal\Menu class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\NavbarHorizontal;
+
+use Skins\Chameleon\Components\Component;
+use Skins\Chameleon\Components\Menu as GenMenu;
+
+/**
+ * The NavbarHorizontal\Logo class.
+ *
+ * Provides a Menu component to be included in a NavbarHorizontal component.
+ *
+ * @author Stephan Gambke
+ * @since 1.6
+ * @ingroup Skins
+ */
+class Menu extends Component {
+
+ /**
+ * @return String
+ */
+ public function getHtml() {
+ $menu = new GenMenu( $this->getSkinTemplate(), $this->getDomElement(), $this->getIndent() );;
+ return '<ul class="nav navbar-nav">' . $menu->getHtml() . "</ul>\n";
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/NavMenu.php b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/NavMenu.php
new file mode 100644
index 00000000..1cc15c28
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/NavMenu.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * File holding the NavbarHorizontal\NavMenu class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\NavbarHorizontal;
+
+use Skins\Chameleon\Components\Component;
+use Skins\Chameleon\Components\NavMenu as GenNavMenu;
+
+/**
+ * The NavbarHorizontal\NavMenu class.
+ *
+ * Provides a NavMenu component to be included in a NavbarHorizontal component.
+ *
+ * @author Stephan Gambke
+ * @since 1.6
+ * @ingroup Skins
+ */
+class NavMenu extends Component {
+
+ /**
+ * @return String
+ */
+ public function getHtml() {
+ $navMenu = new GenNavMenu( $this->getSkinTemplate(), $this->getDomElement(), $this->getIndent() );;
+ return '<ul class="nav navbar-nav">' . $navMenu->getHtml() . "</ul>\n";
+ }
+
+} \ No newline at end of file
diff --git a/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageTools.php b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageTools.php
new file mode 100644
index 00000000..58776077
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageTools.php
@@ -0,0 +1,159 @@
+<?php
+/**
+ * File holding the NavbarHorizontal\PageTools class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\NavbarHorizontal;
+
+use Skins\Chameleon\Components\Component;
+use Skins\Chameleon\Components\PageTools as GenPageTools;
+
+/**
+ * The NavbarHorizontal\PageTools class.
+ *
+ * Provides a PageTools component to be included in a NavbarHorizontal component.
+ *
+ * @author Stephan Gambke
+ * @since 1.6
+ * @ingroup Skins
+ */
+class PageTools extends Component {
+
+ /**
+ * @return String
+ */
+ public function getHtml() {
+
+ $ret = '';
+
+ $pageTools = new GenPageTools( $this->getSkinTemplate(), $this->getDomElement(), $this->getIndent() + 1 );
+
+ $pageTools->setFlat( true );
+ $pageTools->removeClasses( 'text-center list-inline' );
+ $pageTools->addClasses( 'dropdown-menu' );
+
+ $editLinkHtml = $this->getEditLinkHtml( $pageTools );
+
+ $pageToolsHtml = $pageTools->getHtml();
+
+ if ( $editLinkHtml || $pageToolsHtml ) {
+ $ret =
+ $this->indent() . '<!-- page tools -->' .
+ $this->indent() . '<ul class="navbar-tools navbar-nav" >';
+
+ if ( $editLinkHtml !== '' ) {
+ $ret .= $this->indent( 1 ) . $editLinkHtml;
+ }
+
+ if ( $pageToolsHtml !== '' ) {
+ $ret .=
+ $this->indent( $editLinkHtml !== '' ? 0 : 1 ) . '<li class="navbar-tools-tools dropdown">' .
+ $this->indent( 1 ) . '<a data-toggle="dropdown" class="dropdown-toggle" href="#" title="' . $this->getSkinTemplate()->getMsg( 'specialpages-group-pagetools' )->text() . '" ><span>...</span></a>' .
+ $pageToolsHtml .
+ $this->indent( -1 ) . '</li>';
+ }
+ $ret .=
+ $this->indent( $editLinkHtml !== '' ? 0 : -1 ) . '</ul>' . "\n";
+ }
+
+ return $ret;
+ }
+
+ /**
+ * @param GenPageTools $pageTools
+ * @return string
+ */
+ protected function getEditLinkHtml( $pageTools ) {
+
+ $pageToolsStructure = $pageTools->getPageToolsStructure();
+
+ if ( ! array_key_exists( 'views', $pageToolsStructure ) ) {
+ return '';
+ }
+
+ foreach ( $this->getReplaceableEditActionIds() as $id ) {
+
+ if ( array_key_exists( $id, $pageToolsStructure[ 'views' ] ) ) {
+ return $this->getLinkAndRemoveFromPageToolStructure( $pageTools, $id );
+ }
+ }
+
+ return '';
+ }
+
+ /**
+ * @param GenPageTools $pageTools
+ * @param string $editActionId
+ *
+ * @return string
+ */
+ protected function getLinkAndRemoveFromPageToolStructure( $pageTools, $editActionId ) {
+
+ $pageToolsStructure = $pageTools->getPageToolsStructure();
+ $editActionStructure = $pageToolsStructure[ 'views' ][ $editActionId ];
+
+ $editActionStructure[ 'text' ] = '';
+
+ if ( array_key_exists( 'class', $editActionStructure ) ) {
+ $editActionStructure[ 'class' ] .= ' navbar-tools-tools';
+ } else {
+ $editActionStructure[ 'class' ] = 'navbar-tools-tools';
+ }
+
+ $options = array (
+ 'text-wrapper' => array(
+ 'tag' => 'span',
+ 'attributes' => array('class' => 'glyphicon glyphicon-pencil',)
+ ),
+ );
+
+ $editLinkHtml = $this->getSkinTemplate()->makeListItem(
+ $editActionId,
+ $editActionStructure,
+ $options
+ );
+
+ $pageTools->setRedundant( $editActionId );
+
+ return $editLinkHtml;
+ }
+
+ /**
+ * @return string[]
+ */
+ protected function getReplaceableEditActionIds() {
+
+ $editActionIds = array( 've-edit', 'edit' );
+
+ if ( array_key_exists( 'sfgRenameEditTabs', $GLOBALS ) && $GLOBALS[ 'sfgRenameEditTabs' ] === true ||
+ array_key_exists( 'wgPageFormsRenameEditTabs', $GLOBALS ) && $GLOBALS[ 'wgPageFormsRenameEditTabs' ] === true ) {
+
+ $editActionIds = array_merge( array( 'formedit', 'form_edit' ), $editActionIds );
+ }
+
+ return $editActionIds;
+ }
+
+
+} \ No newline at end of file
diff --git a/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageToolsAdaptable.php b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageToolsAdaptable.php
new file mode 100644
index 00000000..e82e2ada
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageToolsAdaptable.php
@@ -0,0 +1,209 @@
+<?php
+/**
+ * File holding the NavbarHorizontal\PageToolsAdaptable class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+
+namespace Skins\Chameleon\Components\NavbarHorizontal;
+
+use Skins\Chameleon\Components\PageTools as GenPageTools;
+
+/**
+ * The NavbarHorizontal\PageToolsAdaptable class.
+ *
+ * Provides an adaptable PageTools component to be included in a NavbarHorizontal component.
+ *
+ * @author Tobias Oetterer
+ * @since 1.6
+ * @ingroup Skins
+ */
+class PageToolsAdaptable extends PageTools
+{
+
+ const GLYPH_ICON_UNKNOWN_ACTION = 'asterisk';
+
+ /**
+ * @var string[]
+ */
+ private $mShowActions = null;
+
+ /**
+ * @var string[]
+ */
+ private $mValidActionsToShow = null;
+
+ /**
+ * @var array
+ */
+ private static $sGlyphIconForAction = array(
+ 'delete' => 'trash',
+ 'edit' => 'edit',
+ 'formedit' => 'list-alt',
+ 'history' => 'education',
+ 'move' => 'share-alt',
+ 'protect' => 'folder-close',
+ 'purge' => 'repeat',
+ 'undelete' => 'road',
+ 'unprotect' => 'folder-open',
+ 'unwatch' => 'star',
+ 've-edit' => 'pencil',
+ 'view' => 'eye-open',
+ 'watch' => 'star-empty',
+ );
+
+ /**
+ * @param string $action
+ * @param string $fallback
+ * @return null|string
+ */
+ public static function getGlyphIconForAction( $action, $fallback = null ) {
+ if ( isset( self::$sGlyphIconForAction[$action] ) ) {
+ return self::$sGlyphIconForAction[$action];
+ }
+ return $fallback !== null ? $fallback : self::GLYPH_ICON_UNKNOWN_ACTION;
+ }
+
+ /**
+ * @param string $icon
+ * @param string $action
+ */
+ public static function setGlyphIconForAction( $icon, $action ) {
+ if ( is_string( $icon ) && $icon && is_string( $action ) && $action ) {
+ self::$sGlyphIconForAction[$action] = $icon;
+ }
+ }
+
+ /**
+ * @param GenPageTools $pageTools
+ * @return string
+ * @throws \MWException
+ */
+ protected function getEditLinkHtml( $pageTools ) {
+
+ $pageToolsStructure = $pageTools->getPageToolsStructure();
+ if ( !array_key_exists( 'views', $pageToolsStructure ) ) {
+ return '';
+ }
+
+ $items = array();
+
+ $showActions = $this->getShowActions( $pageTools );
+
+ foreach ( $showActions as $actionId ) {
+
+ if ( array_key_exists( $actionId, $pageToolsStructure['views'] ) ) {
+ $items[] = $this->getLinkAndRemoveFromPageToolStructure( $pageTools, $actionId );
+ }
+ }
+
+ return implode(
+ $this->indent() . '</ul>' . $this->indent() . '<ul class="navbar-tools navbar-nav" >' . $this->indent() . "\t",
+ $items
+ );
+ }
+
+ /**
+ * @param string $action
+ * @return string
+ */
+ protected function getGlyphIconClassFor( $action ) {
+ return 'glyphicon glyphicon-' . self::getGlyphIconForAction( $action );
+ }
+
+ /**
+ * @param GenPageTools $pageTools
+ * @param string $editActionId
+ *
+ * @return string
+ */
+ protected function getLinkAndRemoveFromPageToolStructure( $pageTools, $editActionId ) {
+
+ $pageToolsStructure = $pageTools->getPageToolsStructure();
+ $editActionStructure = $pageToolsStructure['views'][$editActionId];
+
+ $editActionStructure['text'] = '';
+
+ if ( array_key_exists( 'class', $editActionStructure ) ) {
+ $editActionStructure['class'] .= ' navbar-tools-tools';
+ } else {
+ $editActionStructure['class'] = 'navbar-tools-tools';
+ }
+
+ $options = array(
+ 'text-wrapper' => array(
+ 'tag' => 'span',
+ 'attributes' => array( 'class' => $this->getGlyphIconClassFor( $editActionId ), ),
+ ),
+ );
+
+ $editLinkHtml = $this->getSkinTemplate()->makeListItem(
+ $editActionId,
+ $editActionStructure,
+ $options
+ );
+
+ $pageTools->setRedundant( $editActionId );
+
+ return $editLinkHtml;
+ }
+
+ /**
+ * @param @param GenPageTools $pageTools
+ * @return string[]|null
+ */
+ protected function getShowActions( $pageTools ) {
+ if ( $this->mShowActions !== null ) {
+ return $this->mShowActions;
+ }
+ $showActions = array();
+
+ $showAttributesString = $this->getDomElement() !== null ? $this->getDomElement()->getAttribute( 'show' ) : '';
+
+ if ( $showAttributesString != '' ) {
+ foreach ( explode( ',', $showAttributesString ) as $requestedShowAction ) {
+ if ( in_array( $requestedShowAction, $this->getValidActionsToShow( $pageTools ) ) ) {
+ $showActions[] = $requestedShowAction;
+ }
+ }
+ }
+ return $this->mShowActions = $showActions;
+ }
+
+ /**
+ * @param GenPageTools $pageTools
+ * @return string[]
+ */
+ protected function getValidActionsToShow( $pageTools ) {
+ if ( $this->mValidActionsToShow !== null ) {
+ return $this->mValidActionsToShow;
+ }
+ $pageToolsStructure = $pageTools->getPageToolsStructure();
+ $validActionsToShow = array();
+
+ foreach ( $pageToolsStructure as $group => $groupStructure ) {
+ $validActionsToShow = array_merge( $validActionsToShow, array_keys( $groupStructure ) );
+ }
+ return $this->mValidActionsToShow = $validActionsToShow;
+ }
+} \ No newline at end of file
diff --git a/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PersonalTools.php b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PersonalTools.php
new file mode 100644
index 00000000..540e10d0
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PersonalTools.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * File holding the NavbarHorizontal\PersonalTools class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\NavbarHorizontal;
+
+use Hooks;
+use Skins\Chameleon\Components\Component;
+
+/**
+ * The NavbarHorizontal\PersonalTools class.
+ *
+ * Provides a PersonalTools component to be included in a NavbarHorizontal component.
+ *
+ * @author Stephan Gambke
+ * @since 1.6
+ * @ingroup Skins
+ */
+class PersonalTools extends Component {
+
+ /**
+ * @return String
+ */
+ public function getHtml() {
+
+ $user = $this->getSkinTemplate()->getSkin()->getUser();
+
+ if ( $user->isLoggedIn() ) {
+ $toolsClass = 'navbar-userloggedin';
+ $toolsLinkText = $this->getSkinTemplate()->getMsg( 'chameleon-loggedin' )->params( $user->getName() )->text();
+ } else {
+ $toolsClass = 'navbar-usernotloggedin';
+ $toolsLinkText = $this->getSkinTemplate()->getMsg( 'chameleon-notloggedin' )->text();
+ }
+
+ $linkText = '<span class="glyphicon glyphicon-user"></span>';
+ \Hooks::run('ChameleonNavbarHorizontalPersonalToolsLinkText', array( &$linkText, $this->getSkin() ) );
+
+ // start personal tools element
+ $ret =
+ $this->indent() . '<!-- personal tools -->' .
+ $this->indent() . '<ul class="'.$this->getClassString().' navbar-tools navbar-nav" >' .
+ $this->indent( 1 ) . '<li class="dropdown navbar-tools-tools">' .
+ $this->indent( 1 ) . '<a class="dropdown-toggle ' . $toolsClass . '" href="#" data-toggle="dropdown" title="' . $toolsLinkText . '" >' . $linkText . '</a>' .
+ $this->indent() . '<ul class="p-personal-tools dropdown-menu dropdown-menu-right" >';
+
+ $this->indent( 1 );
+
+ // add personal tools (links to user page, user talk, prefs, ...)
+ foreach ( $this->getSkinTemplate()->getPersonalTools() as $key => $item ) {
+ $ret .= $this->indent() . $this->getSkinTemplate()->makeListItem( $key, $item );
+ }
+
+ $ret .=
+ $this->indent( -1 ) . '</ul>' .
+ $this->indent( -1 ) . '</li>';
+
+ // if the user is logged in, add the newtalk notifier
+ if ( $user->isLoggedIn() ) {
+
+ $newMessagesAlert = '';
+ $newtalks = $user->getNewMessageLinks();
+ $out = $this->getSkinTemplate()->getSkin()->getOutput();
+
+ // Allow extensions to disable the new messages alert;
+ // since we do not display the link text, we ignore the actual value returned in $newMessagesAlert
+ if ( Hooks::run( 'GetNewMessagesAlert', array( &$newMessagesAlert, $newtalks, $user, $out ) ) ) {
+
+ if ( count( $user->getNewMessageLinks() ) > 0 ) {
+ $newtalkClass = 'navbar-newtalk-available';
+ $newtalkLinkText = $this->getSkinTemplate()->getMsg( 'chameleon-newmessages' )->text();
+ } else {
+ $newtalkClass = 'navbar-newtalk-not-available';
+ $newtalkLinkText = $this->getSkinTemplate()->getMsg( 'chameleon-nonewmessages' )->text();
+ }
+
+ $linkText = '<span class="glyphicon glyphicon-envelope"></span>';
+ \Hooks::run('ChameleonNavbarHorizontalNewTalkLinkText', array( &$linkText, $this->getSkin() ) );
+
+ $ret .= $this->indent() . '<li class="navbar-newtalk-notifier">' .
+ $this->indent( 1 ) . '<a class="dropdown-toggle ' . $newtalkClass . '" title="' .
+ $newtalkLinkText . '" href="' . $user->getTalkPage()->getLinkURL( 'redirect=no' ) . '">' . $linkText . '</a>' .
+ $this->indent( -1 ) . '</li>';
+
+ }
+
+ }
+
+ $ret .= $this->indent( -1 ) . '</ul>' . "\n";
+
+ return $ret;
+ }
+
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/SearchBar.php b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/SearchBar.php
new file mode 100644
index 00000000..d665b2d9
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NavbarHorizontal/SearchBar.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * File holding the NavbarHorizontal\PersonalTools class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components\NavbarHorizontal;
+
+use Skins\Chameleon\Components\Component;
+use Skins\Chameleon\Components\SearchBar as GenericSearchBar;
+
+/**
+ * The NavbarHorizontal\PersonalTools class.
+ *
+ * Provides a SearchBar component to be included in a NavbarHorizontal component.
+ *
+ * @author Stephan Gambke
+ * @since 1.6
+ * @ingroup Skins
+ */
+class SearchBar extends Component {
+
+ /**
+ * @return String
+ */
+ public function getHtml() {
+
+ $search = new GenericSearchBar( $this->getSkinTemplate(), $this->getDomElement(), $this->getIndent() );
+ $search->addClasses( 'navbar-form' );
+
+ return $search->getHtml();
+ }
+
+
+} \ No newline at end of file
diff --git a/www/wiki/skins/chameleon/src/Components/NewtalkNotifier.php b/www/wiki/skins/chameleon/src/Components/NewtalkNotifier.php
new file mode 100644
index 00000000..d9f6cf76
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/NewtalkNotifier.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * File holding the NewtalkNotifier class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+/**
+ * The NewtalkNotifier class.
+ *
+ * A message to a user about new messages on their talkpage
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class NewtalkNotifier extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $data = $this->getSkinTemplate()->data;
+
+ if ( array_key_exists( 'newtalk', $data ) && $data[ 'newtalk' ] ) {
+ return $this->indent() . '<!-- message to a user about new messages on their talkpage -->' .
+ $this->indent() . '<span class="usermessage ' . $this->getClassString() . '">' . $this->getSkinTemplate()->data[ 'newtalk' ] . '</span>';
+ } else {
+ return '';
+ }
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/PageTools.php b/www/wiki/skins/chameleon/src/Components/PageTools.php
new file mode 100644
index 00000000..56ca3a0d
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/PageTools.php
@@ -0,0 +1,267 @@
+<?php
+/**
+ * File holding the PageTools class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Action;
+use MWNamespace;
+use Skins\Chameleon\ChameleonTemplate;
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The PageTools class.
+ *
+ * A unordered list containing content navigation links (Page, Discussion,
+ * Edit, History, Move, ...)
+ *
+ * The tab list is a list of lists: '<ul id="p-contentnavigation">
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class PageTools extends Component {
+
+ private $mFlat = false;
+ private $mPageToolsStructure = null;
+
+ /**
+ * @param ChameleonTemplate $template
+ * @param \DOMElement|null $domElement
+ * @param int $indent
+ */
+ public function __construct( ChameleonTemplate $template, \DOMElement $domElement = null, $indent = 0 ) {
+
+ parent::__construct( $template, $domElement, $indent );
+
+ // add classes for the normal case where the page tools are displayed as a first class element;
+ // these classes should be removed if the page tools are part of another element, e.g. nav bar
+ $this->addClasses( 'list-inline text-center' );
+ }
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return string the HTML code
+ */
+ public function getHtml() {
+
+ $contentNavigation = $this->getPageToolsStructure();
+
+ if ( $this->hideSelectedNamespace() ) {
+ unset( $contentNavigation[ 'namespaces' ][ $this->getNamespaceKey() ] );
+ }
+
+ $ret = '';
+
+ $this->indent( 2 );
+ foreach ( $contentNavigation as $category => $tabsDescription ) {
+ $ret .= $this->buildTabGroup( $category, $tabsDescription );
+ }
+ $this->indent( -2 );
+
+ if ( $ret !== '' ) {
+ $ret =
+ $this->indent( 1 ) . '<!-- Content navigation -->' .
+ $this->indent() . \Html::openElement( 'ul',
+ array(
+ 'class' => 'p-contentnavigation ' . $this->getClassString(),
+ 'id' => IdRegistry::getRegistry()->getId( 'p-contentnavigation' ),
+ ) ) .
+ $ret .
+ $this->indent() . '</ul>';
+ }
+
+ return $ret;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function &getPageToolsStructure() {
+ if ( $this->mPageToolsStructure === null ) {
+ $this->mPageToolsStructure = $this->getSkinTemplate()->get( 'content_navigation' , null );
+ }
+ return $this->mPageToolsStructure;
+ }
+
+ /**
+ * @return bool
+ */
+ protected function hideSelectedNamespace() {
+ return
+ $this->getDomElement() !== null &&
+ filter_var( $this->getDomElement()->getAttribute( 'hideSelectedNameSpace' ), FILTER_VALIDATE_BOOLEAN ) &&
+ Action::getActionName( $this->getSkin() ) === 'view';
+ }
+
+ /**
+ * Generate strings used for xml 'id' names in tabs
+ *
+ * Stolen from MW's Title::getNamespaceKey()
+ *
+ * Difference: This function here reports the actual namespace while the
+ * one in Title reports the subject namespace, i.e. no talk namespaces
+ *
+ * @return string
+ */
+ public function getNamespaceKey() {
+ global $wgContLang;
+
+ // Gets the subject namespace if this title
+ $namespace = $this->getSkinTemplate()->getSkin()->getTitle()->getNamespace();
+
+ // Checks if canonical namespace name exists for namespace
+ if ( MWNamespace::exists( $this->getSkinTemplate()->getSkin()->getTitle()->getNamespace() ) ) {
+ // Uses canonical namespace name
+ $namespaceKey = MWNamespace::getCanonicalName( $namespace );
+ } else {
+ // Uses text of namespace
+ $namespaceKey = $this->getSkinTemplate()->getSkin()->getTitle()->getNsText();
+ }
+
+ // Makes namespace key lowercase
+ $namespaceKey = $wgContLang->lc( $namespaceKey );
+ // Uses main
+ if ( $namespaceKey == '' ) {
+ $namespaceKey = 'main';
+ }
+ // Changes file to image for backwards compatibility
+ if ( $namespaceKey == 'file' ) {
+ $namespaceKey = 'image';
+ }
+ return $namespaceKey;
+ }
+
+ /**
+ * @param string $category
+ * @param mixed[][] $tabsDescription
+ *
+ * @return string
+ */
+ protected function buildTabGroup( $category, $tabsDescription ) {
+ // TODO: visually group all links of one category (e.g. some space between categories)
+
+ if ( empty( $tabsDescription ) ) {
+ return '';
+ }
+
+ $ret = $this->indent() . '<!-- ' . $category . ' -->';
+
+ if ( !$this->mFlat ) {
+ $ret .= $this->buildTabGroupOpeningTags( $category );
+
+ }
+
+ foreach ( $tabsDescription as $key => $tabDescription ) {
+ $ret .= $this->buildTab( $tabDescription, $key );
+ }
+
+ if ( !$this->mFlat ) {
+ $ret .= $this->buildTabGroupClosingTags();
+ }
+ return $ret;
+ }
+
+ /**
+ * @param string $category
+ *
+ * @return string
+ */
+ protected function buildTabGroupOpeningTags( $category ) {
+ // output the name of the current category (e.g. 'namespaces', 'views', ...)
+ $ret = $this->indent() .
+ \Html::openElement( 'li', array( 'id' => IdRegistry::getRegistry()->getId( 'p-' . $category ) ) ) .
+ $this->indent( 1 ) . '<ul class="list-inline" >';
+
+ $this->indent( 1 );
+ return $ret;
+ }
+
+ /**
+ * @param mixed[] $tabDescription
+ * @param string $key
+ *
+ * @return string
+ */
+ protected function buildTab( $tabDescription, $key ) {
+
+ // skip redundant links (i.e. the 'view' link)
+ // TODO: make this dependent on an option
+ if ( array_key_exists( 'redundant', $tabDescription ) && $tabDescription[ 'redundant' ] === true ) {
+ return '';
+ }
+
+ // apply a link class if specified, e.g. for the currently active namespace
+ $options = array();
+ if ( array_key_exists( 'class', $tabDescription ) ) {
+ $options[ 'link-class' ] = $tabDescription[ 'class' ];
+ }
+
+ return $this->indent() . $this->getSkinTemplate()->makeListItem( $key, $tabDescription, $options );
+
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildTabGroupClosingTags() {
+ return $this->indent( -1 ) . '</ul>' .
+ $this->indent( -1 ) . '</li>';
+ }
+
+ /**
+ * Set the page tool menu to have submenus or not
+ *
+ * @param boolean $flat
+ */
+ public function setFlat( $flat ) {
+ $this->mFlat = $flat;
+ }
+
+ /**
+ * Set the page tool menu to have submenus or not
+ *
+ * @param string|string[] $tools
+ */
+ public function setRedundant( $tools ) {
+ if ( is_string( $tools ) ) {
+ $tools = array( $tools );
+ }
+
+ $pageToolsStructure = &$this->getPageToolsStructure();
+
+ foreach ( $tools as $tool ) {
+ foreach ( $pageToolsStructure as $group => $groupStructure ) {
+ if ( array_key_exists( $tool, $groupStructure ) ) {
+ $pageToolsStructure[ $group ][ $tool ][ 'redundant' ] = true;
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/PersonalTools.php b/www/wiki/skins/chameleon/src/Components/PersonalTools.php
new file mode 100644
index 00000000..fed5a695
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/PersonalTools.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * File holding the PersonalTools class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+/**
+ * The PersonalTools class.
+ *
+ * An unordered list of personal tools: <ul id="p-personal" >...
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class PersonalTools extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $ret = $this->indent() . '<!-- personal tools -->' .
+ $this->indent() . '<div class="p-personal ' . $this->getClassString() . '" id="p-personal" >';
+
+ $ret .= $this->indent( 1 ) . '<ul class="p-personal-tools list-inline pull-right" >';
+
+ $this->indent( 1 );
+
+ // add personal tools (links to user page, user talk, prefs, ...)
+ foreach ( $this->getSkinTemplate()->getPersonalTools() as $key => $item ) {
+ $ret .= $this->indent() . $this->getSkinTemplate()->makeListItem( $key, $item );
+ }
+
+ $ret .= $this->indent( -1 ) . '</ul>' .
+ $this->indent( -1 ) . '</div>' . "\n" .
+ $this->getNewtalkNotifier();
+
+ return $ret;
+ }
+
+ /**
+ * @return string
+ */
+ private function getNewtalkNotifier() {
+
+ if ( $this->getDomElement() !== null && filter_var( $this->getDomElement()->getAttribute( 'hideNewtalkNotifier' ), FILTER_VALIDATE_BOOLEAN ) ) {
+ return '';
+ }
+
+ // include message to a user about new messages on their talkpage
+ $newtalkNotifier = new NewtalkNotifier( $this->getSkinTemplate(), null, $this->getIndent() + 2 );
+
+ return $this->indent() . '<div class="newtalk-notifier pull-right">' . $newtalkNotifier->getHtml() .
+ $this->indent() . '</div>';
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Row.php b/www/wiki/skins/chameleon/src/Components/Row.php
new file mode 100644
index 00000000..70675934
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Row.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * File holding the Row class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Skins\Chameleon\ChameleonTemplate;
+
+/**
+ * The Row class.
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class Row extends Container {
+
+ public function __construct( ChameleonTemplate $template, \DOMElement $domElement = null, $indent = 0 ) {
+
+ parent::__construct( $template, $domElement, $indent );
+ $this->addClasses( 'row' );
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/SearchBar.php b/www/wiki/skins/chameleon/src/Components/SearchBar.php
new file mode 100644
index 00000000..89937975
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/SearchBar.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * File holding the SearchBar class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use \Linker;
+use Skins\Chameleon\IdRegistry;
+
+/**
+ * The SearchBar class.
+ *
+ * The search form wrapped in a div: <div id="p-search" role="search" >
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class SearchBar extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return string
+ */
+ public function getHtml() {
+
+ $ret = $this->indent() . '<!-- search form -->' .
+
+ $this->indent() . '<div ' . \Html::expandAttributes( array(
+ 'id' => IdRegistry::getRegistry()->getId( 'p-search' ),
+ 'class' => 'p-search ' . $this->getClassString(),
+ 'role' => 'search',
+ )
+ ) . Linker::tooltip( 'p-search' ) . '>' .
+
+ $this->indent( 1 ) . '<form ' . \Html::expandAttributes( array(
+ 'id' => IdRegistry::getRegistry()->getId( 'searchform' ),
+ 'class' => 'mw-search form-inline',
+ )
+ ) . ' action="' . $this->getSkinTemplate()->data[ 'wgScript' ] . '">' .
+
+ $this->indent( 1 ) . '<input type="hidden" name="title" value="' . $this->getSkinTemplate()->data[ 'searchtitle' ] . '" />' .
+ $this->indent() . '<div class="input-group">' .
+ $this->indent( 1 ) . $this->getSkinTemplate()->makeSearchInput( array( 'id' => IdRegistry::getRegistry()->getId( 'searchInput' ), 'type' => 'text', 'class' => 'form-control' ) ) .
+ $this->indent() . '<div class="input-group-btn">';
+
+ $this->indent( 1 );
+
+ $ret .=
+ $this->getGoButton() .
+ $this->getSearchButton() .
+ $this->indent( -1 ) . '</div>' .
+ $this->indent( -1 ) . '</div>' .
+ $this->indent( -1 ) . '</form>' .
+ $this->indent( -1 ) . '</div>' . "\n";
+
+ return $ret;
+ }
+
+ /**
+ * @return string
+ */
+ private function getGoButton() {
+
+ $valueAttr = 'searcharticle';
+ $idAttr = 'searchGoButton';
+ $nameAttr = 'go';
+ $glyphicon = ( $this->getAttribute( 'buttons' ) === 'go' ? 'search' : 'share-alt' );
+
+ return $this->getButton( 'go', $valueAttr, $idAttr, $nameAttr, $glyphicon );
+ }
+
+ /**
+ * @return string
+ */
+ private function getSearchButton() {
+
+ $valueAttr = 'searchbutton';
+ $idAttr = 'mw-searchButton';
+ $nameAttr = 'fulltext';
+ $glyphicon = 'search';
+
+ return $this->getButton( 'search', $valueAttr, $idAttr, $nameAttr, $glyphicon );
+ }
+
+ /**
+ * @param $valueAttr
+ * @param $idAttr
+ * @param $nameAttr
+ * @param $glyphicon
+ * @return string
+ */
+ private function getButton( $button, $valueAttr, $idAttr, $nameAttr, $glyphicon ) {
+
+ if ( $this->shouldShowButton( $button ) ) {
+
+ $buttonAttrs = array(
+ 'value' => $this->getSkinTemplate()->getSkin()->msg( $valueAttr ),
+ 'id' => IdRegistry::getRegistry()->getId( $idAttr ),
+ 'name' => $nameAttr,
+ 'type' => 'submit',
+ 'class' => $idAttr . ' btn btn-default'
+ );
+
+ $buttonAttrs = array_merge(
+ $buttonAttrs,
+ Linker::tooltipAndAccesskeyAttribs( "search-$nameAttr" )
+ );
+
+ return $this->indent() . \Html::rawElement( 'button', $buttonAttrs, '<span class="glyphicon glyphicon-' . $glyphicon . '"></span>' );
+ }
+
+ return '';
+ }
+
+ private function shouldShowButton( $button ) {
+ $buttonsAttribute = $this->getAttribute( 'buttons' );
+ return $button === 'go' && $buttonsAttribute !== 'search' ||
+ $button === 'search' && $buttonsAttribute !== 'go';
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Silent.php b/www/wiki/skins/chameleon/src/Components/Silent.php
new file mode 100644
index 00000000..10726328
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Silent.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * File holding the Silent component class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+
+/**
+ * The Silent class.
+ *
+ * This component may be used as a placeholder during development.
+ *
+ * It is also used internally.
+ *
+ * @author Stephan Gambke
+ * @since 1.1
+ * @ingroup Skins
+ */
+class Silent extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+ return '';
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Components/SiteNotice.php b/www/wiki/skins/chameleon/src/Components/SiteNotice.php
new file mode 100644
index 00000000..1b1b53e6
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/SiteNotice.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * File holding the SiteNotice class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+/**
+ * The SiteNotice class.
+ *
+ * A simple div containing the site notice text: <div id="siteNotice" >
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class SiteNotice extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $data = $this->getSkinTemplate()->data;
+
+ if ( array_key_exists( 'sitenotice', $data ) && $data[ 'sitenotice' ] ) {
+
+ return $this->indent() . '<!-- sitenotice -->' .
+ $this->indent() . '<div id="siteNotice" class="siteNotice ' . $this->getClassString() . '" >' . $data[ 'sitenotice' ] . '</div>'
+ . "\n";
+ } else {
+ return "\n";
+ }
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/Structure.php b/www/wiki/skins/chameleon/src/Components/Structure.php
new file mode 100644
index 00000000..dee8a39e
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/Structure.php
@@ -0,0 +1,96 @@
+<?php
+/**
+ * File holding the Structure class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+use DOMElement;
+
+/**
+ * The Structure class.
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class Structure extends Component {
+
+ private $subcomponents;
+
+ /**
+ * Builds the HTML code for the component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml(){
+ $ret = '';
+
+ foreach ( $this->getSubcomponents() as $component ) {
+ $ret .= $component->getHtml();
+ }
+
+ return $ret;
+ }
+
+ /**
+ * @return string[] the resource loader modules needed by this component
+ */
+ public function getResourceLoaderModules() {
+ $modules = array();
+
+ foreach ( $this->getSubcomponents() as $component ) {
+ $modules = array_merge( $modules, $component->getResourceLoaderModules() );
+ }
+
+ return $modules;
+ }
+
+ /**
+ * @return Component[]
+ */
+ protected function getSubcomponents() {
+
+ if ( !isset ( $this->subcomponents ) ) {
+
+ $this->subcomponents = array();
+
+ $domElement = $this->getDomElement();
+
+ if ( $domElement !== null && $domElement instanceof DOMElement ) {
+
+ $children = $this->getDomElement()->childNodes;
+
+ foreach ( $children as $child ) {
+ if ( $child instanceof DOMElement ) {
+ $this->subcomponents[] = $this->getSkin()->getComponentFactory()->getComponent( $child, $this->getIndent() + 1 );
+ }
+ }
+
+ }
+ }
+
+ return $this->subcomponents;
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Components/ToolbarHorizontal.php b/www/wiki/skins/chameleon/src/Components/ToolbarHorizontal.php
new file mode 100644
index 00000000..255cec14
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Components/ToolbarHorizontal.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * File containing the ToolbarHorizontal class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2016, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Components;
+
+use Hooks;
+use Linker;
+
+/**
+ * ToolbarHorizontal class
+ *
+ * A horizontal toolbar containing standard sidebar items (toolbox, language links).
+ *
+ * The toolbar is an unordered list in a nav element: <nav role="navigation" id="p-tb" >
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class ToolbarHorizontal extends Component {
+
+ /**
+ * Builds the HTML code for this component
+ *
+ * @return String the HTML code
+ */
+ public function getHtml() {
+
+ $skinTemplate = $this->getSkinTemplate();
+
+ $ret = $this->indent() . '<!-- ' . htmlspecialchars( $skinTemplate->getMsg( 'toolbox' )->text() ) . '-->' .
+ $this->indent() . '<nav class="navbar navbar-default p-tb ' . $this->getClassString() . '" id="p-tb" ' . Linker::tooltip( 'p-tb' ) . ' >' .
+ $this->indent( 1 ) . '<ul class="nav navbar-nav small">';
+
+ $this->indent( 1 );
+
+ // insert toolbox items
+ if ( !$this->hideTools() ) {
+ $ret .= $this->addTools( $skinTemplate );
+ }
+
+ // insert language links
+ if ( !$this->hideLanguages() ) {
+ $ret .= $this->addLanguageLinks( $skinTemplate );
+ }
+
+ $ret .= $this->indent( -1 ) . '</ul>' .
+ $this->indent( -1 ) . '</nav>' . "\n";
+
+ return $ret;
+ }
+
+ /**
+ * @param $skinTemplate
+ * @return string
+ * @throws \FatalError
+ * @throws \MWException
+ */
+ private function addTools( $skinTemplate ) {
+
+ $ret = '';
+
+ // TODO: Do we need to care of dropdown menus here? E.g. RSS feeds? See SkinTemplateToolboxEnd.php:1485
+ foreach ( $skinTemplate->getToolbox() as $key => $tbitem ) {
+ $ret .= $this->indent() . $skinTemplate->makeListItem( $key, $tbitem );
+ }
+
+ ob_start();
+ // We pass an extra 'true' at the end so extensions using BaseTemplateToolbox
+ // can abort and avoid outputting double toolbox links
+ Hooks::run( 'SkinTemplateToolboxEnd', array( &$skinTemplate, true ) );
+ $ret .= $this->indent() . ob_get_contents();
+ ob_end_clean();
+ return $ret;
+ }
+
+ /**
+ * @param $skinTemplate
+ * @return string
+ * @throws \MWException
+ */
+ private function addLanguageLinks( $skinTemplate ) {
+
+ $ret = '';
+
+ if ( array_key_exists( 'language_urls', $skinTemplate->data ) && $skinTemplate->data[ 'language_urls' ] ) {
+
+ $ret .= $this->indent() . '<li class="dropdown dropup p-lang" id="p-lang" ' . Linker::tooltip( 'p-lang' ) . ' >' .
+ $this->indent( 1 ) . '<a href="#" data-target="#" class="dropdown-toggle" data-toggle="dropdown">' .
+ htmlspecialchars( $skinTemplate->getMsg( 'otherlanguages' )->text() ) . ' <b class="caret"></b>' . '</a>' .
+ $this->indent() . '<ul class="dropdown-menu" >';
+
+ $this->indent( 1 );
+ foreach ( $skinTemplate->data[ 'language_urls' ] as $key => $langlink ) {
+ $ret .= $this->indent() . $skinTemplate->makeListItem( $key, $langlink, array( 'link-class' => 'small' ) );
+ }
+
+ $ret .= $this->indent( -1 ) . '</ul>' .
+ $this->indent( -1 ) . '</li>';
+ }
+
+ return $ret;
+ }
+
+ /**
+ * @return bool
+ */
+ private function hideTools() {
+ return $this->getDomElement() !== null && filter_var( $this->getDomElement()->getAttribute( 'hideTools' ), FILTER_VALIDATE_BOOLEAN );
+ }
+
+ /**
+ * @return bool
+ */
+ private function hideLanguages() {
+ return $this->getDomElement() !== null && filter_var( $this->getDomElement()->getAttribute( 'hideLanguages' ), FILTER_VALIDATE_BOOLEAN );
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Hooks/SetupAfterCache.php b/www/wiki/skins/chameleon/src/Hooks/SetupAfterCache.php
new file mode 100644
index 00000000..138c5faf
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Hooks/SetupAfterCache.php
@@ -0,0 +1,242 @@
+<?php
+/**
+ * File containing the BeforeInitialize class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2016, Stephan Gambke, mwjames
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Hooks;
+
+use Bootstrap\BootstrapManager;
+use RuntimeException;
+
+/**
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/SetupAfterCache
+ *
+ * @since 1.0
+ *
+ * @author mwjames
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class SetupAfterCache {
+
+ protected $bootstrapManager = null;
+ protected $configuration = array();
+ protected $request;
+
+ /**
+ * @since 1.0
+ *
+ * @param BootstrapManager $bootstrapManager
+ * @param array $configuration
+ * @param \WebRequest $request
+ */
+ public function __construct( BootstrapManager $bootstrapManager, array &$configuration, \WebRequest $request ) {
+ $this->bootstrapManager = $bootstrapManager;
+ $this->configuration = &$configuration;
+ $this->request = $request;
+ }
+
+ /**
+ * @since 1.0
+ *
+ * @return self
+ */
+ public function process() {
+
+ $this->setInstallPaths();
+ $this->addLateSettings();
+ $this->registerCommonBootstrapModules();
+ $this->registerExternalLessModules();
+ $this->registerExternalLessVariables();
+
+ return $this;
+ }
+
+ /**
+ * @since 1.0
+ *
+ * @param array $configuration
+ */
+ public function adjustConfiguration( array &$configuration ) {
+
+ foreach ( $this->configuration as $key => $value ) {
+ $configuration[ $key ] = $value;
+ }
+ }
+
+ /**
+ * Set local and remote base path of the Chameleon skin
+ */
+ protected function setInstallPaths() {
+
+ $this->configuration[ 'chameleonLocalPath' ] = $this->configuration['wgStyleDirectory'] . '/chameleon';
+ $this->configuration[ 'chameleonRemotePath' ] = $this->configuration['wgStylePath'] . '/chameleon';
+ }
+
+ protected function addLateSettings() {
+
+ $this->addChameleonToVisualEditorSupportedSkins();
+ $this->addResourceModules();
+ $this->setLayoutFile();
+ }
+
+ protected function registerCommonBootstrapModules() {
+
+ $this->bootstrapManager->addAllBootstrapModules();
+
+ if ( file_exists( $this->configuration[ 'wgStyleDirectory' ] . '/common/shared.css' ) ) { // MW < 1.24
+ $this->bootstrapManager->addExternalModule(
+ $this->configuration[ 'wgStyleDirectory' ] . '/common/shared.css',
+ $this->configuration[ 'wgStylePath' ] . '/common/'
+ );
+ } else {
+ if ( file_exists( $this->configuration[ 'IP' ] . '/resources/src/mediawiki.legacy/shared.css' ) ) { // MW >= 1.24
+ $this->bootstrapManager->addExternalModule(
+ $this->configuration[ 'IP' ] . '/resources/src/mediawiki.legacy/shared.css',
+ $this->configuration[ 'wgScriptPath' ] . '/resources/src/mediawiki.legacy/'
+ );
+ }
+ }
+
+ $this->bootstrapManager->addExternalModule(
+ $this->configuration[ 'chameleonLocalPath' ] . '/resources/styles/core.less',
+ $this->configuration[ 'chameleonRemotePath' ] . '/resources/styles/'
+ );
+ }
+
+ protected function registerExternalLessModules() {
+
+ if ( $this->hasConfigurationOfTypeArray( 'egChameleonExternalStyleModules' ) ) {
+
+ foreach ( $this->configuration[ 'egChameleonExternalStyleModules' ] as $localFile => $remotePath ) {
+
+ list( $localFile, $remotePath ) = $this->matchAssociativeElement( $localFile, $remotePath );
+
+ $this->bootstrapManager->addExternalModule(
+ $this->isReadableFile( $localFile ),
+ $remotePath
+ );
+ }
+ }
+ }
+
+ protected function registerExternalLessVariables() {
+
+ if ( $this->hasConfigurationOfTypeArray( 'egChameleonExternalLessVariables' ) ) {
+
+ foreach ( $this->configuration[ 'egChameleonExternalLessVariables' ] as $key => $value ) {
+ $this->bootstrapManager->setLessVariable( $key, $value );
+ }
+ }
+ }
+
+ /**
+ * @param $id
+ * @return bool
+ */
+ private function hasConfiguration( $id ) {
+ return isset( $this->configuration[ $id ] );
+ }
+
+ /**
+ * @param string $id
+ * @return bool
+ */
+ private function hasConfigurationOfTypeArray( $id ) {
+ return $this->hasConfiguration( $id ) && is_array( $this->configuration[ $id ] );
+ }
+
+ /**
+ * @param $localFile
+ * @param $remotePath
+ * @return array
+ */
+ private function matchAssociativeElement( $localFile, $remotePath ) {
+
+ if ( is_integer( $localFile ) ) {
+ return array( $remotePath, '' );
+ }
+
+ return array( $localFile, $remotePath );
+ }
+
+ /**
+ * @param string $file
+ * @return string
+ */
+ private function isReadableFile( $file ) {
+
+ if ( is_readable( $file ) ) {
+ return $file;
+ }
+
+ throw new RuntimeException( "Expected an accessible {$file} file" );
+ }
+
+ protected function addChameleonToVisualEditorSupportedSkins() {
+
+ // if Visual Editor is installed and there is a setting to enable or disable it
+ if ( $this->hasConfiguration( 'wgVisualEditorSupportedSkins' ) && $this->hasConfiguration( 'egChameleonEnableVisualEditor' ) ) {
+
+ // if VE should be enabled
+ if ( $this->configuration[ 'egChameleonEnableVisualEditor' ] === true ) {
+
+ // if Chameleon is not yet in the list of VE-enabled skins
+ if ( !in_array( 'chameleon', $this->configuration[ 'wgVisualEditorSupportedSkins' ] ) ) {
+ $this->configuration[ 'wgVisualEditorSupportedSkins' ][] = 'chameleon';
+ }
+
+ } else {
+ // remove all entries of Chameleon from the list of VE-enabled skins
+ $this->configuration[ 'wgVisualEditorSupportedSkins' ] = array_diff(
+ $this->configuration[ 'wgVisualEditorSupportedSkins' ],
+ array( 'chameleon' )
+ );
+ }
+ }
+ }
+
+ protected function addResourceModules() {
+ $this->configuration[ 'wgResourceModules' ][ 'skin.chameleon.jquery-sticky' ] = array(
+ 'localBasePath' => $this->configuration[ 'chameleonLocalPath' ] . '/resources/js',
+ 'remoteBasePath' => $this->configuration[ 'chameleonRemotePath' ] . '/resources/js',
+ 'group' => 'skin.chameleon',
+ 'skinScripts' => array( 'chameleon' => array( 'sticky-kit/jquery.sticky-kit.js', 'Components/Modifications/sticky.js' ) )
+ );
+ }
+
+ protected function setLayoutFile() {
+
+ $layout = $this->request->getVal( 'uselayout' );
+
+ if ( $layout !== null &&
+ $this->hasConfigurationOfTypeArray( 'egChameleonAvailableLayoutFiles' ) &&
+ array_key_exists( $layout, $this->configuration[ 'egChameleonAvailableLayoutFiles' ] ) ) {
+
+ $this->configuration[ 'egChameleonLayoutFile' ] = $this->configuration[ 'egChameleonAvailableLayoutFiles' ][ $layout ];
+ }
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/IdRegistry.php b/www/wiki/skins/chameleon/src/IdRegistry.php
new file mode 100644
index 00000000..11a880d4
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/IdRegistry.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * File holding the IdRegistry class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2015, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon;
+
+/**
+ * Class IdRegistry provides a registry and access methods to ensure each id is only used once per HTML page.
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class IdRegistry {
+
+ private static $sInstance;
+ private $mRegistry = array();
+
+ /**
+ * @return IdRegistry
+ */
+ public static function getRegistry() {
+
+ if ( self::$sInstance === null ) {
+ self::$sInstance = new IdRegistry();
+ }
+
+ return self::$sInstance;
+
+ }
+
+ /**
+ * Returns the opening tag of an HTML element in a string.
+ *
+ * The advantage over Html::openElement is that any id attribute is ensured to be unique.
+ *
+ * @param string $tag
+ * @param array $attributes
+ *
+ * @return string
+ */
+ public function openElement( $tag, $attributes = array() ) {
+
+ if ( is_array( $attributes ) && isset( $attributes[ 'id' ] ) ) {
+ $attributes[ 'id' ] = $this->getId( $attributes[ 'id' ] );
+ }
+
+ return \Html::openElement( $tag, $attributes );
+ }
+
+ /**
+ * @param null|string $id
+ * @param null|mixed $component
+ * @return string
+ */
+ public function getId( $id = null, $component = null ) {
+
+ if ( empty( $id ) ) {
+
+ // no specific id requested, just return a unique string
+ return base_convert( uniqid(), 16, 36 );
+
+ } elseif ( array_key_exists( $id, $this->mRegistry ) ) {
+
+ // specific id requested, but already in use
+ // return a string derived from the id and a unique string
+ $key = "$id-" . base_convert( uniqid(), 16, 36 );
+ $this->mRegistry[ $id ][ $key ] = $component;
+ return $key;
+
+ } else {
+
+ // specific id requested that is not yet in use
+ // return the id
+ $this->mRegistry[ $id ][ $id ] = $component;
+ return $id;
+
+ }
+ }
+
+ /**
+ * Returns an HTML element in a string. The contents are NOT escaped.
+ *
+ * The advantage over Html::rawElement is that any id attribute is ensured to be unique.
+ *
+ * @param string $tag
+ * @param array $attributes
+ * @param string $contents
+ *
+ * @return string
+ */
+ public function element( $tag, $attributes = array(), $contents = '' ) {
+
+ if ( is_array( $attributes ) && isset( $attributes[ 'id' ] ) ) {
+ $attributes[ 'id' ] = $this->getId( $attributes[ 'id' ] );
+ }
+
+ return \Html::rawElement( $tag, $attributes, $contents );
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Menu/Menu.php b/www/wiki/skins/chameleon/src/Menu/Menu.php
new file mode 100644
index 00000000..78ae3f76
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Menu/Menu.php
@@ -0,0 +1,118 @@
+<?php
+/**
+ * File holding the abstract Menu class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Menu;
+
+/**
+ * Class Menu
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+abstract class Menu {
+
+ private $menuItemFormatter = null;
+ private $itemListFormatter = null;
+
+ abstract public function getHtml();
+
+ /**
+ * @param string $href
+ * @param string $text
+ * @param int $depth
+ * @param string $subitems
+ *
+ * @return string
+ */
+ protected function getHtmlForMenuItem( $href, $text, $depth, $subitems ) {
+ return call_user_func( $this->getMenuItemFormatter(), $href, $text, $depth, $subitems );
+ }
+
+ /**
+ * @return callable
+ */
+ public function getMenuItemFormatter() {
+
+ if ( $this->menuItemFormatter === null ) {
+
+ $this->setMenuItemFormatter( function ( $href, $text, $depth, $subitems ) {
+ $href = \Sanitizer::cleanUrl( $href );
+ $text = htmlspecialchars( $text );
+ $indent = str_repeat( "\t", 2 * $depth );
+
+ if ( $subitems !== '' ) {
+ return "$indent<li>\n$indent\t<a href=\"$href\">$text</a>\n$subitems$indent</li>\n";
+ } else {
+ return "$indent<li><a href=\"$href\">$text</a></li>\n";
+ }
+ } );
+
+ }
+
+ return $this->menuItemFormatter;
+ }
+
+ /**
+ * @param callable $menuItemFormatter
+ */
+ public function setMenuItemFormatter( $menuItemFormatter ) {
+ $this->menuItemFormatter = $menuItemFormatter;
+ }
+
+ /**
+ * @param string $rawItemsHtml
+ * @param int $depth
+ *
+ * @return string
+ */
+ protected function getHtmlForMenuItemList( $rawItemsHtml, $depth ) {
+ return call_user_func( $this->getItemListFormatter(), $rawItemsHtml, $depth );
+ }
+
+ /**
+ * @return callable
+ */
+ public function getItemListFormatter() {
+
+ if ( $this->itemListFormatter === null ) {
+ $this->setItemListFormatter( function ( $rawItemsHtml, $depth ) {
+ $indent = str_repeat( "\t", 2 * $depth + 1 );
+ return "$indent<ul>\n$rawItemsHtml$indent</ul>\n";
+ } );
+ }
+
+ return $this->itemListFormatter;
+ }
+
+ /**
+ * @param callable $itemListFormatter
+ */
+ public function setItemListFormatter( $itemListFormatter ) {
+ $this->itemListFormatter = $itemListFormatter;
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/Menu/MenuFactory.php b/www/wiki/skins/chameleon/src/Menu/MenuFactory.php
new file mode 100644
index 00000000..b1f71779
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Menu/MenuFactory.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * File holding the MenuFactory class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2017, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Menu;
+use Message;
+
+/**
+ * Class MenuFactory
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class MenuFactory {
+
+ /**
+ * @param Message|string|string[] $message
+ * @param bool $forContent
+ *
+ * @throws \MWException
+ *
+ * @return Menu
+ */
+ public function getMenuFromMessage( $message, $forContent = false ) {
+
+ if ( is_string( $message ) || is_array( $message ) ) {
+ $message = Message::newFromKey( $message );
+ }
+
+ $this->assert( $message instanceof Message, 'String, array of strings or Message object expected.', $message );
+
+ if ( $forContent ) {
+ $message = $message->inContentLanguage();
+ }
+
+ if ( !$message->exists() ) {
+ return $this->getMenuFromMessageText( '', $forContent );
+ }
+
+ return $this->getMenuFromMessageText( $message->text(), $forContent );
+ }
+
+ /**
+ * @param string $text
+ * @param bool $forContent
+ *
+ * @return Menu
+ * @throws \MWException
+ */
+ public function getMenuFromMessageText( $text, $forContent = false ) {
+
+ $this->assert( is_string( $text ), 'String expected.', $text );
+
+ $lines = explode( "\n", trim( $text ) );
+
+ return new MenuFromLines( $lines, $forContent );
+ }
+
+ /**
+ * @param $message
+ * @throws \MWException
+ */
+ protected function assert( $condition, $message, $target ) {
+ if ( !$condition ) {
+ throw new \MWException( $message . ' Got ' . (is_object( $target ) ? get_class( $target ) : gettype( $target )) . '.' );
+ }
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/Menu/MenuFromLines.php b/www/wiki/skins/chameleon/src/Menu/MenuFromLines.php
new file mode 100644
index 00000000..ea55fabc
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/Menu/MenuFromLines.php
@@ -0,0 +1,273 @@
+<?php
+/**
+ * File holding the MenuFromLines class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2015, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon\Menu;
+
+use Title;
+
+/**
+ * Class MenuFromLines
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class MenuFromLines extends Menu {
+
+ private $lines = null;
+ private $inContentLanguage = false;
+ private $menuItemData = null;
+
+ private $needsParse = true;
+
+ /** @var Menu[] */
+ private $children = array();
+ private $html = null;
+
+ /**
+ * @param string[] $lines
+ * @param bool $inContentLanguage
+ * @param null|string[] $itemData
+ */
+ public function __construct( &$lines, $inContentLanguage = false, $itemData = null ) {
+
+ $this->lines = &$lines;
+ $this->inContentLanguage = $inContentLanguage;
+
+ if ( $itemData !== null ) {
+ $this->menuItemData = $itemData;
+ } else {
+ $this->menuItemData = array(
+ 'text' => '',
+ 'href' => '#',
+ 'depth' => 0
+ );
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getHtml() {
+
+ if ( $this->html === null ) {
+
+ $this->parseLines();
+ $this->html = $this->buildHtml();
+
+ }
+
+ return $this->html;
+ }
+
+ /**
+ * @return string[]|null
+ */
+ public function parseLines() {
+
+ if ( !$this->needsParse ) {
+ return null;
+ }
+
+ $this->needsParse = false;
+
+ $line = $this->getNextLine();
+ $subItemData = $this->parseOneLine( $line );
+
+ while ( $subItemData !== null && $subItemData[ 'depth' ] > $this->menuItemData[ 'depth' ] ) {
+
+ $subItemData = $this->createChildAndParseNextLine( $subItemData );
+
+ }
+
+ return $subItemData;
+ }
+
+ /**
+ * @return string
+ */
+ protected function getNextLine() {
+ $line = '';
+
+ while ( count( $this->lines ) > 0 && empty( $line ) ) {
+ $line = trim( array_shift( $this->lines ) );
+ };
+ return $line;
+ }
+
+ /**
+ * Will return an array of the form
+ * array(
+ * 'text' => $text, // link text
+ * 'href' => $href, // parsed link target
+ * 'depth' => $depth
+ * );
+ *
+ * @param string $rawLine
+ *
+ * @return array
+ */
+ protected function parseOneLine( $rawLine ) {
+
+ if ( empty( $rawLine ) ) {
+ return null;
+ }
+
+ list( $depth, $linkDescription ) = $this->extractDepthAndLine( $rawLine );
+ list( $href, $text ) = $this->extractHrefAndLinkText( $linkDescription );
+
+ return array(
+ 'text' => $text,
+ 'href' => $href,
+ 'depth' => $depth
+ );
+ }
+
+ /**
+ * @param string $rawLine
+ *
+ * @return array
+ */
+ protected function extractDepthAndLine( $rawLine ) {
+
+ $matches = array();
+ preg_match( '/(\**)(.*)/', ltrim( $rawLine ), $matches );
+
+ $depth = strlen( $matches[ 1 ] );
+ $line = $matches[ 2 ];
+
+ return array( $depth, $line );
+ }
+
+ /**
+ * @param $linkDescription
+ *
+ * @return array
+ */
+ protected function extractHrefAndLinkText( $linkDescription ) {
+
+ $linkAttributes = array_map( 'trim', explode( '|', $linkDescription, 2 ) );
+
+ $linkTarget = trim( trim( $linkAttributes[ 0 ], '[]' ) );
+ $linkTarget = $this->getTextFromMessageName( $linkTarget );
+ $href = $this->getHrefForTarget( $linkTarget );
+
+ $linkDescription = count( $linkAttributes ) > 1 ? $linkAttributes[ 1 ] : '';
+ $text = $linkDescription === '' ? $linkTarget : $this->getTextFromMessageName( $linkDescription );
+
+ return array( $href, $text );
+ }
+
+ /**
+ * @param string $messageName
+ *
+ * @return string
+ */
+ protected function getTextFromMessageName( $messageName ) {
+ $msgObj = $this->inContentLanguage ? wfMessage( $messageName )->inContentLanguage() : wfMessage( $messageName );
+ $messageText = ( $msgObj->isDisabled() ? $messageName : trim( $msgObj->inContentLanguage()->text() ) );
+ return $messageText;
+ }
+
+ /**
+ * @param string $linkTarget
+ *
+ * @return string
+ * @throws \MWException
+ */
+ protected function getHrefForTarget( $linkTarget ) {
+
+ if ( empty( $linkTarget ) ) {
+ return '#';
+ } elseif ( preg_match( '/^(?:' . wfUrlProtocols() . ')/', $linkTarget ) || $linkTarget[ 0 ] === '#' ) {
+ return $linkTarget;
+ } else {
+ return $this->getHrefForWikiPage( $linkTarget );
+ }
+ }
+
+ /**
+ * @param string $linkTarget
+ *
+ * @return string
+ * @throws \MWException
+ */
+ protected function getHrefForWikiPage( $linkTarget ) {
+ $title = Title::newFromText( $linkTarget );
+
+ if ( $title instanceof Title ) {
+ return $title->fixSpecialName()->getLocalURL();
+ }
+
+ return '#';
+ }
+
+ /**
+ * @param string[] $subItemData
+ *
+ * @return null|string[]
+ */
+ protected function createChildAndParseNextLine( $subItemData ) {
+ $child = new self( $this->lines, $this->inContentLanguage, $subItemData );
+ $child->setMenuItemFormatter( $this->getMenuItemFormatter() );
+ $child->setItemListFormatter( $this->getItemListFormatter() );
+ $subItemData = $child->parseLines();
+ $this->children[ ] = $child;
+ return $subItemData;
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildHtml() {
+
+ $submenuHtml = $this->buildSubmenuHtml();
+
+ if ( $this->menuItemData[ 'text' ] !== '' ) {
+ return $this->getHtmlForMenuItem( $this->menuItemData[ 'href' ], $this->menuItemData[ 'text' ], $this->menuItemData[ 'depth' ], $submenuHtml );
+ } else {
+ return $submenuHtml;
+ }
+ }
+
+ /**
+ * @return string
+ */
+ protected function buildSubmenuHtml() {
+
+ if ( empty( $this->children ) ) {
+ return '';
+ }
+
+ $itemList = '';
+ foreach ( $this->children as $child ) {
+ $itemList .= $child->getHtml();
+ }
+
+ return $this->getHtmlForMenuItemList( $itemList, $this->menuItemData[ 'depth' ] );
+ }
+
+}
diff --git a/www/wiki/skins/chameleon/src/PermissionsHelper.php b/www/wiki/skins/chameleon/src/PermissionsHelper.php
new file mode 100644
index 00000000..3d895c98
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/PermissionsHelper.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * File containing the PermissionsHelper class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2014, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+
+namespace Skins\Chameleon;
+
+use DOMElement;
+
+/**
+ * PermissionsHelper class
+ *
+ * @author Stephan Gambke
+ * @since 1.1
+ * @ingroup Skins
+ */
+class PermissionsHelper {
+
+ private $domElement;
+ private $skin;
+ private $default;
+
+ /**
+ * @param \SkinChameleon $skin
+ * @param DOMElement $domElement
+ * @param bool $default
+ */
+ public function __construct( \SkinChameleon $skin, DOMElement $domElement = null, $default = false ) {
+ $this->skin = $skin;
+ $this->domElement = $domElement;
+ $this->default = $default;
+ }
+
+ /**
+ * @since 1.1
+ *
+ * @param string $attributeNameInDomElement
+ *
+ * @return bool
+ */
+ public function userHasGroup( $attributeNameInDomElement ) {
+
+ return $this->userHas( 'group', $attributeNameInDomElement );
+ }
+
+ /**
+ * @param string $attributeOfUser
+ * @param string $attributeNameInDomElement
+ *
+ * @throws \MWException
+ * @return bool
+ */
+ protected function userHas( $attributeOfUser, $attributeNameInDomElement ) {
+
+ $user = $this->skin->getUser();
+ $attributeAccessors = array(
+ 'group' => array( $user, 'getEffectiveGroups' ),
+ 'permission' => array( $user, 'getRights' ),
+ );
+
+ if ( !array_key_exists( $attributeOfUser, $attributeAccessors ) ) {
+ throw new \MWException( sprintf( 'Unknown permission: %s', $attributeOfUser ) );
+ }
+
+ if ( !$this->hasAttribute( $attributeNameInDomElement ) ) {
+ return $this->default;
+ }
+
+ $expectedValues = $this->getValueListFromAttribute( $attributeNameInDomElement );
+ $observedValues = call_user_func( $attributeAccessors[ $attributeOfUser ] );
+ $effectiveValues = array_intersect( $expectedValues, $observedValues );
+
+ return !empty( $effectiveValues );
+ }
+
+ /**
+ * @since 1.1
+ *
+ * @param string $attributeNameInDomElement
+ *
+ * @return bool
+ */
+ public function hasAttribute( $attributeNameInDomElement ) {
+ return $this->domElement !== null && $this->domElement->hasAttribute( $attributeNameInDomElement );
+ }
+
+ /**
+ * @param string $attributeName
+ *
+ * @return string[]
+ */
+ protected function getValueListFromAttribute( $attributeName ) {
+ return $this->domElement === null ? array() : array_map( 'trim', explode( ',', $this->domElement->getAttribute( $attributeName ) ) );
+
+ }
+
+ /**
+ * @since 1.1
+ *
+ * @param string $attributeNameInDomElement
+ *
+ * @return bool
+ */
+ public function userHasPermission( $attributeNameInDomElement ) {
+
+ return $this->userHas( 'permission', $attributeNameInDomElement );
+ }
+
+ /**
+ * @since 1.1
+ *
+ * @param string $attributeNameInDomElement
+ *
+ * @return bool
+ */
+ public function pageIsInNamespace( $attributeNameInDomElement ) {
+
+ if ( !$this->hasAttribute( $attributeNameInDomElement ) ) {
+ return $this->default;
+ }
+
+ $expectedNamespaces = array_map( array( $this, 'getNamespaceNumberFromDefinedConstantName' ), $this->getValueListFromAttribute( $attributeNameInDomElement ) );
+ $pageNamespace = $this->skin->getTitle()->getNamespace();
+
+ return in_array( $pageNamespace, $expectedNamespaces );
+ }
+
+ /**
+ * @since 1.1
+ *
+ * AGREGADO PARA REEVO!
+ *
+ * @param string $attributeNameInDomElement
+ *
+ * @return bool
+ */
+ public function pageHasCategory( $attributeNameInDomElement ) {
+
+ if ( !$this->hasAttribute( $attributeNameInDomElement ) ) {
+ return $this->default;
+ }
+
+ $expectedCategories = $this->getValueListFromAttribute( $attributeNameInDomElement );
+
+ $pagename = $this->skin->getTitle();
+ $cats = $this->skin->getSkin()->getCategoryLinks();
+ preg_match_all('/<\s*a[^>]*>(.*?)<\s*\/\s*a>/', $cats, $output_array);
+ $pageCategory = $output_array[1];
+ $match = array_intersect($pageCategory,$expectedCategories);
+ $final = reset($match);
+ if ($match) {
+ // error_log('Se encontro una categoria en la página '.$pagename.' que aplica una regla de Modification en Chamaleon');
+ $pageCategory = $final;
+ }
+
+ return in_array( $pageCategory, $expectedCategories );
+ }
+
+
+
+ /**
+ * @param null|string $value
+ *
+ * @return int
+ */
+ protected function getNamespaceNumberFromDefinedConstantName( $value ) {
+ $constants = get_defined_constants();
+ if ( !is_null( $value ) && array_key_exists( $value, $constants ) ) {
+ $value = $constants[ $value ];
+ }
+
+ return is_int( $value ) ? $value : -1;
+ }
+}
diff --git a/www/wiki/skins/chameleon/src/SkinChameleon.php b/www/wiki/skins/chameleon/src/SkinChameleon.php
new file mode 100644
index 00000000..a898450d
--- /dev/null
+++ b/www/wiki/skins/chameleon/src/SkinChameleon.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * File holding the SkinChameleon class
+ *
+ * This file is part of the MediaWiki skin Chameleon.
+ *
+ * @copyright 2013 - 2016, Stephan Gambke
+ * @license GNU General Public License, version 3 (or any later version)
+ *
+ * The Chameleon skin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * The Chameleon skin is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @file
+ * @ingroup Skins
+ */
+use Skins\Chameleon\ComponentFactory;
+
+/**
+ * SkinTemplate class for the Chameleon skin
+ *
+ * @author Stephan Gambke
+ * @since 1.0
+ * @ingroup Skins
+ */
+class SkinChameleon extends SkinTemplate {
+
+ public $skinname = 'chameleon';
+ public $stylename = 'chameleon';
+ public $template = '\Skins\Chameleon\ChameleonTemplate';
+ public $useHeadElement = true;
+
+ private $componentFactory;
+
+ /**
+ * @param $out OutputPage object
+ */
+ public function setupSkinUserCss( OutputPage $out ) {
+
+ // load Bootstrap styles
+ $out->addModuleStyles(
+ array(
+ 'ext.bootstrap.styles'
+ )
+ );
+ }
+
+ /**
+ * @param \OutputPage $out
+ */
+ public function initPage( OutputPage $out ) {
+
+ parent::initPage( $out );
+
+ // Enable responsive behaviour on mobile browsers
+ $out->addMeta( 'viewport', 'width=device-width, initial-scale=1.0' );
+ }
+
+ /**
+ * @return QuickTemplate
+ */
+ protected function setupTemplateForOutput() {
+
+ $tpl = parent::setupTemplateForOutput();
+
+ $this->getComponentFactory()->setSkinTemplate( $tpl );
+
+ $tpl->set( 'skin', $this );
+ $this->addSkinModulesToOutput();
+
+ return $tpl;
+ }
+
+ /**
+ * @return ComponentFactory
+ */
+ public function getComponentFactory() {
+
+ if ( ! isset( $this->componentFactory ) ) {
+ $this->componentFactory = new \Skins\Chameleon\ComponentFactory(
+ $this->getLayoutFilePath()
+ );
+ }
+
+ return $this->componentFactory;
+ }
+
+ public function addSkinModulesToOutput() {
+ // load Bootstrap scripts
+ $out = $this->getOutput();
+ $out->addModules( array( 'ext.bootstrap.scripts' ) );
+ $out->addModules( $this->getComponentFactory()->getRootComponent()->getResourceLoaderModules() );
+
+ }
+
+ /**
+ * @param Title $title
+ * @return string
+ */
+ public function getPageClasses( $title ) {
+ $layoutFilePath = $this->getLayoutFilePath();
+ $layoutName = Sanitizer::escapeClass( 'layout-' . basename( $layoutFilePath, '.xml' ) );
+ return implode( ' ', array( parent::getPageClasses( $title ), $layoutName ) );
+ }
+
+ /**
+ * Template method that can be overridden by subclasses
+ * @return string Path to layout file
+ */
+ protected function getLayoutFilePath() {
+ return $GLOBALS['egChameleonLayoutFile'];
+ }
+}