Foxtrott has uploaded a new change for review. https://gerrit.wikimedia.org/r/89480
Change subject: Reworking the extension ...................................................................... Reworking the extension * adding a singleton to manage Bootstrap * adding a resource loader module capable of compiling several LESS files in one single context * removing the unused fixes.less Change-Id: Ib69e0a7764dafe7346b73e1eae630ca141819df6 --- A Bootstrap.class.php M Bootstrap.php A ResourceLoaderBootstrapModule.php D fixes.less 4 files changed, 345 insertions(+), 89 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Bootstrap refs/changes/80/89480/1 diff --git a/Bootstrap.class.php b/Bootstrap.class.php new file mode 100644 index 0000000..09277bd --- /dev/null +++ b/Bootstrap.class.php @@ -0,0 +1,213 @@ +<?php +/** + * File holding the Bootstrap class + * + * @copyright (C) 2013, Stephan Gambke + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License, version 3 (or later) + * + * This file is part of the MediaWiki extension Bootstrap. + * The Bootstrap extension 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 Bootstrap extension 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 Bootstrap + */ + +class Bootstrap { + + + static private $bootstrap = null; + static private $moduleDescriptions = array( + 'variables' => array( 'styles' => 'variables' ), + 'mixins' => array( 'styles' => 'mixins' ), + 'normalize' => array( 'styles' => 'normalize' ), + 'print' => array( 'styles' => 'print' ), + 'scaffolding' => array( 'styles' => 'scaffolding' ), + 'type' => array( 'styles' => 'type' ), + 'code' => array( 'styles' => 'code' ), + 'grid' => array( 'styles' => 'grid' ), + 'tables' => array( 'styles' => 'tables' ), + 'forms' => array( 'styles' => 'forms' ), + 'buttons' => array( 'styles' => 'buttons' ), + 'component-animations' => array( 'styles' => 'component-animations' ), + 'glyphicons' => array( 'styles' => 'glyphicons' ), + 'dropdowns' => array( 'styles' => 'dropdowns' ), + 'button-groups' => array( 'styles' => 'button-groups' ), + 'input-groups' => array( 'styles' => 'input-groups' ), + 'navs' => array( 'styles' => 'navs' ), + 'navbar' => array( 'styles' => 'navbar' ), + 'breadcrumbs' => array( 'styles' => 'breadcrumbs' ), + 'pagination' => array( 'styles' => 'pagination' ), + 'pager' => array( 'styles' => 'pager' ), + 'labels' => array( 'styles' => 'labels' ), + 'badges' => array( 'styles' => 'badges' ), + 'jumbotron' => array( 'styles' => 'jumbotron' ), + 'thumbnails' => array( 'styles' => 'thumbnails' ), + 'alerts' => array( 'styles' => 'alerts' ), + 'progress-bars' => array( 'styles' => 'progress-bars' ), + 'media' => array( 'styles' => 'media' ), + 'list-group' => array( 'styles' => 'list-group' ), + 'panels' => array( 'styles' => 'panels' ), + 'wells' => array( 'styles' => 'wells' ), + 'close' => array( 'styles' => 'close' ), + + // Components w/ JavaScript + 'modals' => array( 'styles' => 'modals', 'scripts' => 'bootstrap/js/modal.js' ), + 'tooltip' => array( 'styles' => 'tooltip', 'scripts' => 'bootstrap/js/tooltip.js' ), + 'popovers' => array( 'styles' => 'popovers', 'scripts' => 'bootstrap/js/popover.js', 'dependencies' => 'ext.bootstrap.tooltip' ), + 'carousel' => array( 'styles' => 'carousel', 'scripts' => 'bootstrap/js/carousel.js' ), + + // Utility classes + 'utilities' => array( 'styles' => 'utilities' ), + 'responsive-utilities' => array( 'styles' => 'responsive-utilities' ), + + // JS-only components + 'affix' => array( 'scripts' => 'bootstrap/js/affix.js' ), + 'alert' => array( 'scripts' => 'bootstrap/js/alert.js' ), + 'button' => array( 'scripts' => 'bootstrap/js/button.js' ), + 'collapse' => array( 'scripts' => 'bootstrap/js/collapse.js' ), + 'dropdown' => array( 'scripts' => 'bootstrap/js/dropdown.js' ), + 'scrollspy' => array( 'scripts' => 'bootstrap/js/scrollspy.js' ), + 'tab' => array( 'scripts' => 'bootstrap/js/tab.js' ), + 'transition' => array( 'scripts' => 'bootstrap/js/transition.js' ), + + ); + static private $coreModules = array( 'variables', 'mixins', 'normalize', 'print', 'scaffolding', 'type', 'code', + 'grid', 'tables', 'forms', 'buttons' ); + static private $optionalModules = array( 'component-animations', 'glyphicons', 'dropdowns', 'button-groups', + 'input-groups', 'navs', 'navbar', 'breadcrumbs', 'pagination', 'pager', + 'labels', 'badges', 'jumbotron', 'thumbnails', 'alerts', 'progress-bars', + 'media', 'list-group', 'panels', 'wells', 'close', 'modals', 'tooltip', + 'popovers', 'carousel', 'utilities', 'responsive-utilities', 'affix', + 'alert', 'button', 'collapse', 'dropdown', 'scrollspy', 'tab', 'transition' ); + + /** + * Returns the Bootstrap singleton. + * + * @return Bootstrap + */ + public static function getBootstrap() { + + // if singleton was not yet created + if ( self::$bootstrap === null ) { + self::initializeBootstrap(); + } + + return self::$bootstrap; + } + + /** + * sets up the Bootstrap singleton and does some initialization + */ + protected static function initializeBootstrap() { + + global $wgResourceModules; + + // register resource loader modules for JS components + foreach ( self::$moduleDescriptions as $module => $description ) { + if ( isset( $description[ 'scripts' ] ) ) { + + $wgResourceModules[ 'ext.bootstrap.' . $module ] = array( + 'localBasePath' => $wgResourceModules[ 'ext.bootstrap' ][ 'localBasePath' ], + 'remoteExtPath' => 'Bootstrap', + 'scripts' => $description[ 'scripts' ], + ); + + if ( isset( $description[ 'dependencies' ] ) ) { + $wgResourceModules[ 'ext.bootstrap.' . $module ][ 'dependencies' ] = $description[ 'dependencies' ]; + } + } + } + + self::$bootstrap = new Bootstrap(); + + // add core Bootstrap modules + self::$bootstrap->addBootstrapModule( self::$coreModules ); + } + + /** + * Adds the given Bootstrap module or modules. + * + * @param string|array(string) $modules + */ + public function addBootstrapModule( $modules ) { + + $modules = (array)$modules; + + foreach ( $modules as $module ) { + + // if the module is known + if ( array_key_exists( $module, self::$moduleDescriptions ) ) { + + global $wgResourceModules; + + // add less files to $wgResourceModules + if ( isset( self::$moduleDescriptions[ $module ][ 'styles' ] ) ) { + $wgResourceModules[ 'ext.bootstrap' ][ 'styles' ] = array_merge( $wgResourceModules[ 'ext.bootstrap' ][ 'styles' ], (array)self::$moduleDescriptions[ $module ][ 'styles' ] ); + } + + // ensure loading of js files using dependencies + if ( isset( self::$moduleDescriptions[ $module ][ 'scripts' ] ) ) { + $wgResourceModules[ 'ext.bootstrap' ][ 'dependencies' ][ ] = 'ext.bootstrap.' . $module; + + } + + // prevent adding this module again + unset( self::$moduleDescriptions[ $module ] ); + } + } + + } + + /** + * Adds all bootstrap modules + */ + public function addAllBootstrapModules() { + + $this->addBootstrapModule( self::$optionalModules ); + } + + /** + * @param string $path + * @param string $file + */ + public function addExternalModule( $path, $file ) { + + global $wgResourceModules; + + if ( !in_array( $path, $wgResourceModules[ 'ext.bootstrap' ][ 'paths' ] ) ) { + $wgResourceModules[ 'ext.bootstrap' ][ 'paths' ][ ] = $path; + } + + $wgResourceModules[ 'ext.bootstrap' ][ 'styles' ][ ] = $file; + } + + /** + * @param string $key the LESS variable name + * @param string $value the value to assign to the variable + */ + public function setLessVariable( $key, $value ) { + + $this->setLessVariables( array( $key => $value ) ); + } + + /** + * @param $variables + */ + public function setLessVariables( $variables ) { + + global $wgResourceModules; + $wgResourceModules[ 'ext.bootstrap' ][ 'variables' ] = array_merge( $wgResourceModules[ 'ext.bootstrap' ][ 'variables' ], $variables ); + } + +} diff --git a/Bootstrap.php b/Bootstrap.php index 461e506..b0140b9 100644 --- a/Bootstrap.php +++ b/Bootstrap.php @@ -35,7 +35,11 @@ * @ingroup Bootstrap */ if ( !defined( 'MEDIAWIKI' ) ) { - die( 'This file is part of a MediaWiki extension, it is not a valid entry point.' ); + die( 'This file is part of the MediaWiki extension Bootstrap, it is not a valid entry point.' ); +} + +if ( version_compare( $wgVersion, '1.22', 'lt' ) ) { + die( '<b>Error:</b> This version of <a href="https://www.mediawiki.org/wiki/Extension:Bootstrap">Bootstrap</a> is only compatible with MediaWiki 1.22 or above. You need to upgrade MediaWiki first.' ); } /** @@ -59,62 +63,18 @@ // register message files $wgExtensionMessagesFiles['Bootstrap'] = $dir . '/Bootstrap.i18n.php'; -// register resource modules with the Resource Loader +$wgAutoloadClasses['bootstrap\ResourceLoaderBootstrapModule'] = $dir . '/ResourceLoaderBootstrapModule.php'; +$wgAutoloadClasses['Bootstrap'] = $dir . '/Bootstrap.class.php'; -// module loading all styles -// It is enough to include fixes.less. Everything else is included through this file. -$wgResourceModules['ext.bootstrap.styles'] = array( - 'localBasePath' => $dir, - 'remoteExtPath' => 'Bootstrap', - 'styles' => array( 'fixes.less' ), -); - -// module loading all scripts -$wgResourceModules['ext.bootstrap.scripts'] = array( - 'localBasePath' => $dir, - 'remoteExtPath' => 'Bootstrap', - 'dependencies' => array( ), -); - -$moduleNames = array( - 'affix', - 'alert', - 'button', - 'carousel', - 'collapse', - 'dropdown', - 'modal', - 'popover', - 'scrollspy', - 'tab', - 'tooltip', - 'transition', -); - -$moduleTemplate = array( - 'localBasePath' => $dir, - 'remoteExtPath' => 'Bootstrap', - 'dependencies' => array( 'jquery' ), -); - -// register bootstrap modules with the ResourceLoader -foreach ( $moduleNames as $modName ) { - - $wgResourceModules[ "ext.bootstrap.scripts.$modName" ] = array_merge( $moduleTemplate, array( - 'scripts' => array( "bootstrap/js/$modName.js" ), - ) ); - - $wgResourceModules['ext.bootstrap.scripts']['dependencies'][] = "ext.bootstrap.scripts.$modName"; -} - -// Fix dependencies between modules explicitely -$wgResourceModules['ext.bootstrap.scripts.popover']['dependencies'][] = "ext.bootstrap.scripts.tooltip"; - -// all-including module +// register skeleton resource module with the Resource Loader $wgResourceModules['ext.bootstrap'] = array( 'localBasePath' => $dir, 'remoteExtPath' => 'Bootstrap', - 'dependencies' => array( 'ext.bootstrap.styles', 'ext.bootstrap.scripts' ), + 'class' => 'bootstrap\ResourceLoaderBootstrapModule', + 'styles' => array(), + 'variables' => array(), + 'paths' => array( $dir . '/bootstrap/less' ), + 'dependencies' => array(), ); unset( $dir ); diff --git a/ResourceLoaderBootstrapModule.php b/ResourceLoaderBootstrapModule.php new file mode 100644 index 0000000..3f323ee --- /dev/null +++ b/ResourceLoaderBootstrapModule.php @@ -0,0 +1,119 @@ +<?php +/** + * File holding the Bootstrap class + * + * @copyright (C) 2013, Stephan Gambke + * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License, version 3 (or later) + * + * This file is part of the MediaWiki extension Bootstrap. + * The Bootstrap extension 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 Bootstrap extension 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 Bootstrap + */ + +namespace Bootstrap; + +use ResourceLoader; +use ResourceLoaderContext; +use ResourceLoaderFileModule; + +/** + * ResourceLoader module based on local JavaScript/LESS files. + * + * Different to the behaviour of ResourceLoaderFileModule this module compiles all LESS files in one compile context. + * + * It recognizes the following additional fields in $wgResourceModules: + * * styles: array of LESS file names (with or without extension .less) + * * variables: array of key value pairs representing LESS variables, that will be added to the LESS script after all + * files imports, i.e. that may override any variable set in style files + * * paths: array of paths to search for style files; all these paths together represent one virtual file base and will + * be searched for a style file; this means it is not possible to include two LESS files with the same name + * even if in different paths + * + * @package Bootstrap + */ +class ResourceLoaderBootstrapModule extends ResourceLoaderFileModule { + + protected $variables = array(); + protected $paths = array(); + + public function __construct( $options = array(), $localBasePath = null, $remoteBasePath = null + ) { + + parent::__construct( $options, $localBasePath, $remoteBasePath ); + + if ( isset( $options[ 'variables' ] ) ) { + $this->variables = $options[ 'variables' ]; + } + + if ( isset( $options[ 'paths' ] ) ) { + $this->paths = $options[ 'paths' ]; + } + + } + + /** + * Get the compiled Bootstrap styles + * + * @param ResourceLoaderContext $context + * + * @return array + */ + public function getStyles( ResourceLoaderContext $context ) { + + $compiler = ResourceLoader::getLessCompiler(); + + // prepare a temp file containing all the variables to load + // have to use a temp file for variables because inline variables do not overwrite @import'ed variables even if + // set after the @import (see https://github.com/leafo/lessphp/issues/302 ) + $tmpFile = null; + + if ( !empty( $this->variables ) ) { + + $tmpFile = tempnam( sys_get_temp_dir(), 'php' ); + + $handle = fopen( $tmpFile, 'w' ); + + foreach ( $this->variables as $key => $value ) { + fwrite( $handle, "@$key: $value;\n" ); + } + + fclose( $handle ); + + $this->styles[ ] = basename( $tmpFile ); + $this->paths[ ] = dirname( $tmpFile ); + + } + + // add all + $lessCode = implode( array_map( function ( $module ) { return "@import \"$module\";\n"; }, $this->styles ) ); + + // add additional paths for external files + foreach ( $this->paths as $path ) { + $compiler->addImportDir( $path ); + } + + $styles = $compiler->compile( $lessCode ); + + unlink( $tmpFile ); + + return array( 'all' => $styles ); + } + + public function supportsURLLoading() { + + return false; + } +} diff --git a/fixes.less b/fixes.less deleted file mode 100644 index 32c78ca..0000000 --- a/fixes.less +++ /dev/null @@ -1,36 +0,0 @@ -/*! - * LESS style file importing the regular Bootstrap file and then attaching fixes. - * - * @copyright (C) 2013, Stephan Gambke - * @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License, version 3 (or later) - * - * This file is part of the MediaWiki extension Bootstrap. - * The Bootstrap extension 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 Bootstrap extension 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 Bootstrap - */ - -/* - * first import regular Bootstrap - *********************/ - -@import "bootstrap/less/bootstrap.less"; - - -/* - * then apply fixes - *********************/ - -/* None yet */ -- To view, visit https://gerrit.wikimedia.org/r/89480 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ib69e0a7764dafe7346b73e1eae630ca141819df6 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Bootstrap Gerrit-Branch: master Gerrit-Owner: Foxtrott <[email protected]> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
