From fc7369835258467bf97eb64f184b93691f9a9fd5 Mon Sep 17 00:00:00 2001 From: Yaco Date: Thu, 4 Jun 2020 11:01:00 -0300 Subject: first commit --- www/wiki/skins/chameleon/src/ChameleonTemplate.php | 117 +++++++++ www/wiki/skins/chameleon/src/ComponentFactory.php | 265 ++++++++++++++++++++ www/wiki/skins/chameleon/src/Components/Cell.php | 57 +++++ .../skins/chameleon/src/Components/Component.php | 222 +++++++++++++++++ .../skins/chameleon/src/Components/Container.php | 60 +++++ .../skins/chameleon/src/Components/ContentBody.php | 218 ++++++++++++++++ .../chameleon/src/Components/ContentHeader.php | 76 ++++++ .../skins/chameleon/src/Components/FooterIcons.php | 73 ++++++ .../skins/chameleon/src/Components/FooterInfo.php | 83 +++++++ .../chameleon/src/Components/FooterPlaces.php | 64 +++++ www/wiki/skins/chameleon/src/Components/Grid.php | 59 +++++ www/wiki/skins/chameleon/src/Components/Html.php | 61 +++++ .../skins/chameleon/src/Components/LastmodInfo.php | 85 +++++++ .../skins/chameleon/src/Components/LegalInfo.php | 85 +++++++ www/wiki/skins/chameleon/src/Components/Logo.php | 98 ++++++++ .../skins/chameleon/src/Components/MainContent.php | 215 ++++++++++++++++ www/wiki/skins/chameleon/src/Components/Menu.php | 96 ++++++++ .../src/Components/Modifications/HideFor.php | 71 ++++++ .../src/Components/Modifications/Modification.php | 174 +++++++++++++ .../src/Components/Modifications/ShowOnlyFor.php | 71 ++++++ .../src/Components/Modifications/Sticky.php | 51 ++++ .../skins/chameleon/src/Components/NavMenu.php | 221 +++++++++++++++++ .../chameleon/src/Components/NavbarHorizontal.php | 256 +++++++++++++++++++ .../src/Components/NavbarHorizontal/Logo.php | 54 ++++ .../src/Components/NavbarHorizontal/Menu.php | 51 ++++ .../src/Components/NavbarHorizontal/NavMenu.php | 51 ++++ .../src/Components/NavbarHorizontal/PageTools.php | 159 ++++++++++++ .../NavbarHorizontal/PageToolsAdaptable.php | 209 ++++++++++++++++ .../Components/NavbarHorizontal/PersonalTools.php | 117 +++++++++ .../src/Components/NavbarHorizontal/SearchBar.php | 55 +++++ .../chameleon/src/Components/NewtalkNotifier.php | 57 +++++ .../skins/chameleon/src/Components/PageTools.php | 267 ++++++++++++++++++++ .../chameleon/src/Components/PersonalTools.php | 82 +++++++ www/wiki/skins/chameleon/src/Components/Row.php | 45 ++++ .../skins/chameleon/src/Components/SearchBar.php | 144 +++++++++++ www/wiki/skins/chameleon/src/Components/Silent.php | 51 ++++ .../skins/chameleon/src/Components/SiteNotice.php | 59 +++++ .../skins/chameleon/src/Components/Structure.php | 96 ++++++++ .../chameleon/src/Components/ToolbarHorizontal.php | 142 +++++++++++ .../skins/chameleon/src/Hooks/SetupAfterCache.php | 242 ++++++++++++++++++ www/wiki/skins/chameleon/src/IdRegistry.php | 122 +++++++++ www/wiki/skins/chameleon/src/Menu/Menu.php | 118 +++++++++ www/wiki/skins/chameleon/src/Menu/MenuFactory.php | 91 +++++++ .../skins/chameleon/src/Menu/MenuFromLines.php | 273 +++++++++++++++++++++ www/wiki/skins/chameleon/src/PermissionsHelper.php | 195 +++++++++++++++ www/wiki/skins/chameleon/src/SkinChameleon.php | 122 +++++++++ 46 files changed, 5580 insertions(+) create mode 100644 www/wiki/skins/chameleon/src/ChameleonTemplate.php create mode 100644 www/wiki/skins/chameleon/src/ComponentFactory.php create mode 100644 www/wiki/skins/chameleon/src/Components/Cell.php create mode 100644 www/wiki/skins/chameleon/src/Components/Component.php create mode 100644 www/wiki/skins/chameleon/src/Components/Container.php create mode 100644 www/wiki/skins/chameleon/src/Components/ContentBody.php create mode 100644 www/wiki/skins/chameleon/src/Components/ContentHeader.php create mode 100644 www/wiki/skins/chameleon/src/Components/FooterIcons.php create mode 100644 www/wiki/skins/chameleon/src/Components/FooterInfo.php create mode 100644 www/wiki/skins/chameleon/src/Components/FooterPlaces.php create mode 100644 www/wiki/skins/chameleon/src/Components/Grid.php create mode 100644 www/wiki/skins/chameleon/src/Components/Html.php create mode 100644 www/wiki/skins/chameleon/src/Components/LastmodInfo.php create mode 100644 www/wiki/skins/chameleon/src/Components/LegalInfo.php create mode 100644 www/wiki/skins/chameleon/src/Components/Logo.php create mode 100644 www/wiki/skins/chameleon/src/Components/MainContent.php create mode 100644 www/wiki/skins/chameleon/src/Components/Menu.php create mode 100644 www/wiki/skins/chameleon/src/Components/Modifications/HideFor.php create mode 100644 www/wiki/skins/chameleon/src/Components/Modifications/Modification.php create mode 100644 www/wiki/skins/chameleon/src/Components/Modifications/ShowOnlyFor.php create mode 100644 www/wiki/skins/chameleon/src/Components/Modifications/Sticky.php create mode 100644 www/wiki/skins/chameleon/src/Components/NavMenu.php create mode 100644 www/wiki/skins/chameleon/src/Components/NavbarHorizontal.php create mode 100644 www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Logo.php create mode 100644 www/wiki/skins/chameleon/src/Components/NavbarHorizontal/Menu.php create mode 100644 www/wiki/skins/chameleon/src/Components/NavbarHorizontal/NavMenu.php create mode 100644 www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageTools.php create mode 100644 www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PageToolsAdaptable.php create mode 100644 www/wiki/skins/chameleon/src/Components/NavbarHorizontal/PersonalTools.php create mode 100644 www/wiki/skins/chameleon/src/Components/NavbarHorizontal/SearchBar.php create mode 100644 www/wiki/skins/chameleon/src/Components/NewtalkNotifier.php create mode 100644 www/wiki/skins/chameleon/src/Components/PageTools.php create mode 100644 www/wiki/skins/chameleon/src/Components/PersonalTools.php create mode 100644 www/wiki/skins/chameleon/src/Components/Row.php create mode 100644 www/wiki/skins/chameleon/src/Components/SearchBar.php create mode 100644 www/wiki/skins/chameleon/src/Components/Silent.php create mode 100644 www/wiki/skins/chameleon/src/Components/SiteNotice.php create mode 100644 www/wiki/skins/chameleon/src/Components/Structure.php create mode 100644 www/wiki/skins/chameleon/src/Components/ToolbarHorizontal.php create mode 100644 www/wiki/skins/chameleon/src/Hooks/SetupAfterCache.php create mode 100644 www/wiki/skins/chameleon/src/IdRegistry.php create mode 100644 www/wiki/skins/chameleon/src/Menu/Menu.php create mode 100644 www/wiki/skins/chameleon/src/Menu/MenuFactory.php create mode 100644 www/wiki/skins/chameleon/src/Menu/MenuFromLines.php create mode 100644 www/wiki/skins/chameleon/src/PermissionsHelper.php create mode 100644 www/wiki/skins/chameleon/src/SkinChameleon.php (limited to 'www/wiki/skins/chameleon/src') 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 @@ +. + * + * @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 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 "\n"; + + } + + /** + * 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 @@ +. + * + * @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 @@ +. + * + * @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 @@ +. + * + * @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 @@ +. + * + * @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 ) . ''; + + 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 @@ +. + * + * @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() . '' . + $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() . '
$idRegistry->getId( 'mw-js-message' ), + 'style' => 'display:none;' + ) + ) . $skintemplate->get( 'userlangattributes' ) . '>
'; + + // $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() . '' . + $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub', 'class' => 'small' ), $skintemplate->get( 'subtitle' ) ); + $ret .= '
'; + + } + + if ( $skintemplate->get( 'undelete' ) ) { + // TODO: should not use class 'small', better use class 'contentSub2' and do styling in a less file + $ret .= + $this->indent() . '' . + $this->indent() . $idRegistry->element( 'div', array( 'id' => 'contentSub2', 'class' => 'small' ), $skintemplate->get( 'undelete' ) ); + $ret .= '
'; + + } + + + $ret .= $this->buildContentBody(); + $ret .= $this->buildCategoryLinks(); + + $ret .= $this->indent( -1 ) . ''; + // END content + + return $ret; + } + + /** + * @return string + */ + protected function buildContentHeader() { + + $skintemplate = $this->getSkinTemplate(); + $idRegistry = IdRegistry::getRegistry(); + + $ret = $this->indent() . '
' . + + $this->indent( 1 ) . '' . + $this->indent() . $idRegistry->element( 'h1', array( 'id' => 'firstHeading', 'class' => 'firstHeading' ), $skintemplate->get( 'title' ) ) . + + $this->indent() . '' . + $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() . '' . + $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() . '' . + $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() . '' . $skintemplate->getMsg( 'jumptonavigation' )->escaped() . '' . + $skintemplate->getMsg( 'comma-separator' )->escaped() . '' . $skintemplate->getMsg( 'jumptosearch' )->escaped() . '' + ); + + $ret .= $this->indent( -1 ) . '
'; + return $ret; + } + + /** + * @return string + */ + protected function buildContentBody() { + return $this->indent() . IdRegistry::getRegistry()->element( 'div', array( 'id' => 'bodyContent' ), + $this->indent( 1 ) . '' . "\n" . + $this->indent() . $this->getSkinTemplate()->get( 'bodytext' ) . + $this->indent() . '' . + $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() . '' . + $this->indent() . $this->getSkinTemplate()->get( 'catlinks' ); + } + + /** + * @return string + */ + protected function buildDataAfterContent() { + + $skinTemplate = $this->getSkinTemplate(); + + if ( $skinTemplate->get( 'dataAfterContent' ) ) { + return + $this->indent() . '' . + $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 @@ +. + * + * @file + * @ingroup Chameleon + */ + +namespace Skins\Chameleon\Components; +use Skins\Chameleon\ChameleonTemplate; +use Skins\Chameleon\IdRegistry; + +/** + * The FooterIcons class. + * + * A inline list containing icons: ' . $this->indent( - 1 ) . ''; + } + + /** + * @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 @@ +. + * + * @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: ' . "\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 @@ +. + * + * @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 @@ +. + * + * @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 '\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 @@ +. + * + * @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 '\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 @@ +. + * + * @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() . '' . + $this->indent() . '' . "\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 @@ +. + * + * @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() . '' . $this->indent() . '