diff options
author | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
---|---|---|
committer | Yaco <franco@reevo.org> | 2020-06-04 11:01:00 -0300 |
commit | fc7369835258467bf97eb64f184b93691f9a9fd5 (patch) | |
tree | daabd60089d2dd76d9f5fb416b005fbe159c799d /www/wiki/skins/chameleon/src |
first commit
Diffstat (limited to 'www/wiki/skins/chameleon/src')
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']; + } +} |