r76527 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r76526‎ | r76527 | r76528 >
Date:18:19, 11 November 2010
Author:maxsem
Status:ok (Comments)
Tags:
Comment:
Merged branches/Gadgets-work/ to trunk. It now uses ResourceLoader (scripts must be explicitly marked as supporting it to be loaded this way), and in process I've completely rewritten its internal organisation in OOP style. Documentation commits will follow.
Modified paths:
  • /trunk/extensions/Gadgets (modified) (history)
  • /trunk/extensions/Gadgets/Gadgets.php (modified) (history)
  • /trunk/extensions/Gadgets/Gadgets_body.php (added) (history)
  • /trunk/extensions/Gadgets/Gadgets_tests.php (added) (history)
  • /trunk/extensions/Gadgets/SpecialGadgets.php (modified) (history)

Diff [purge]

Index: trunk/extensions/Gadgets/Gadgets_body.php
@@ -0,0 +1,474 @@
 2+<?php
 3+/**
 4+ * Gadgets extension - lets users select custom javascript gadgets
 5+ *
 6+ *
 7+ * For more info see http://mediawiki.org/wiki/Extension:Gadgets
 8+ *
 9+ * @file
 10+ * @ingroup Extensions
 11+ * @author Daniel Kinzler, brightbyte.de
 12+ * @copyright © 2007 Daniel Kinzler
 13+ * @license GNU General Public Licence 2.0 or later
 14+ */
 15+
 16+ class GadgetHooks {
 17+
 18+ /**
 19+ * ArticleSaveComplete hook handler.
 20+ *
 21+ * @param $article Article
 22+ * @param $user User
 23+ * @param $text String: New page text
 24+ */
 25+ public static function articleSaveComplete( $article, $user, $text ) {
 26+ //update cache if MediaWiki:Gadgets-definition was edited
 27+ $title = $article->mTitle;
 28+ if( $title->getNamespace() == NS_MEDIAWIKI && $title->getText() == 'Gadgets-definition' ) {
 29+ Gadget::loadStructuredList( $text );
 30+ }
 31+ return true;
 32+ }
 33+
 34+ /**
 35+ * GetPreferences hook handler.
 36+ * @param $user User
 37+ * @param $preferences Array: Preference descriptions
 38+ */
 39+ public static function getPreferences( $user, &$preferences ) {
 40+ $gadgets = Gadget::loadStructuredList();
 41+ if (!$gadgets) return true;
 42+
 43+ $options = array();
 44+ foreach( $gadgets as $section => $thisSection ) {
 45+ if ( $section !== '' ) {
 46+ $section = wfMsgExt( "gadget-section-$section", 'parseinline' );
 47+ $options[$section] = array();
 48+ $destination = &$options[$section];
 49+ } else {
 50+ $destination = &$options;
 51+ }
 52+ foreach( $thisSection as $gadget ) {
 53+ $gname = $gadget->getName();
 54+ $destination[wfMsgExt( "gadget-$gname", 'parseinline' )] = $gname;
 55+ }
 56+ }
 57+
 58+ $preferences['gadgets-intro'] =
 59+ array(
 60+ 'type' => 'info',
 61+ 'label' => '&#160;',
 62+ 'default' => Xml::tags( 'tr', array(),
 63+ Xml::tags( 'td', array( 'colspan' => 2 ),
 64+ wfMsgExt( 'gadgets-prefstext', 'parse' ) ) ),
 65+ 'section' => 'gadgets',
 66+ 'raw' => 1,
 67+ 'rawrow' => 1,
 68+ );
 69+
 70+ $preferences['gadgets'] =
 71+ array(
 72+ 'type' => 'multiselect',
 73+ 'options' => $options,
 74+ 'section' => 'gadgets',
 75+ 'label' => '&#160;',
 76+ 'prefix' => 'gadget-',
 77+ );
 78+
 79+ return true;
 80+ }
 81+
 82+ /**
 83+ * ResourceLoaderRegisterModules hook handler.
 84+ * @param $resourceLoader ResourceLoader
 85+ */
 86+ public static function registerModules( &$resourceLoader ) {
 87+ $gadgets = Gadget::loadList();
 88+ if ( !$gadgets ) {
 89+ return true;
 90+ }
 91+ foreach ( $gadgets as $g ) {
 92+ $module = $g->getModule();
 93+ if ( $module ) {
 94+ $resourceLoader->register( $g->getModuleName(), $module );
 95+ }
 96+ }
 97+ return true;
 98+ }
 99+
 100+ /**
 101+ * BeforePageDisplay hook handler.
 102+ * @param $out OutputPage
 103+ */
 104+ public static function beforePageDisplay( $out ) {
 105+ global $wgUser;
 106+
 107+ if ( !$wgUser->isLoggedIn() ) return true;
 108+
 109+ wfProfileIn( __METHOD__ );
 110+ //disable all gadgets on critical special pages
 111+ //NOTE: $out->isUserJsAllowed() is tempting, but always fals if $wgAllowUserJs is false.
 112+ // That would disable gadgets on wikis without user JS. Introducing $out->isJsAllowed()
 113+ // may work, but should that really apply also to MediaWiki:common.js? Even on the preference page?
 114+ // See bug 22929 for discussion.
 115+ $title = $out->getTitle();
 116+ if ( $title->isSpecial( 'Preferences' )
 117+ || $title->isSpecial( 'Resetpass' )
 118+ || $title->isSpecial( 'Userlogin' ) )
 119+ {
 120+ wfProfileOut( __METHOD__ );
 121+ return true;
 122+ }
 123+
 124+ $gadgets = Gadget::loadList();
 125+ if ( !$gadgets ) {
 126+ wfProfileOut( __METHOD__ );
 127+ return true;
 128+ }
 129+
 130+ $lb = new LinkBatch();
 131+ $lb->setCaller( __METHOD__ );
 132+ $pages = array();
 133+
 134+ foreach ( $gadgets as $gadget ) {
 135+ if ( $gadget->isEnabled() ) {
 136+ if ( $gadget->hasModule() ) {
 137+ $out->addModules( $gadget->getModuleName() );
 138+ }
 139+ foreach ( $gadget->getLegacyScripts() as $page ) {
 140+ $lb->add( NS_MEDIAWIKI, $page );
 141+ $pages[] = $page;
 142+ }
 143+ }
 144+ }
 145+
 146+ $lb->execute( __METHOD__ );
 147+
 148+ $done = array();
 149+ foreach ( $pages as $page ) {
 150+ if ( isset( $done[$page] ) ) continue;
 151+ $done[$page] = true;
 152+ self::applyScript( $page, $out );
 153+ }
 154+ wfProfileOut( __METHOD__ );
 155+
 156+ return true;
 157+ }
 158+
 159+ /**
 160+ * Adds one legacy script to output.
 161+ *
 162+ * @param $page String: Unprefixed page title
 163+ * @param $out OutputPage
 164+ */
 165+ private static function applyScript( $page, $out ) {
 166+ global $wgJsMimeType;
 167+
 168+ //FIXME: stuff added via $out->addScript appears below usercss and userjs
 169+ // but we'd want it to appear above explicit user stuff, so it can be overwritten.
 170+
 171+ $t = Title::makeTitleSafe( NS_MEDIAWIKI, $page );
 172+ if ( !$t ) continue;
 173+
 174+ $u = $t->getLocalURL( 'action=raw&ctype=' . $wgJsMimeType );
 175+ //switched to addScriptFile call to support scriptLoader
 176+ $out->addScriptFile( $u, $t->getLatestRevID() );
 177+ }
 178+
 179+ /**
 180+ * UnitTestsList hook handler
 181+ * @param $files Array: List of extension test files
 182+ */
 183+ public static function unitTestsList( $files ) {
 184+ $files[] = dirname( __FILE__ ) . '/Gadgets_tests.php';
 185+ return true;
 186+ }
 187+}
 188+
 189+/**
 190+ * Wrapper for one gadget.
 191+ */
 192+class Gadget {
 193+ /**
 194+ * Increment this when changing class structure
 195+ */
 196+ const GADGET_CLASS_VERSION = 1;
 197+
 198+ private $version = self::GADGET_CLASS_VERSION,
 199+ $scripts = array(),
 200+ $styles = array(),
 201+ $name,
 202+ $definition,
 203+ $resourceLoaded = false;
 204+
 205+ /**
 206+ * Creates an instance of this class from definition in MediaWiki:Gadgets-definition
 207+ * @param $definition String: Gadget definition
 208+ * @return Mixed: Instance of Gadget class or false if $definition is invalid
 209+ */
 210+ public static function newFromDefinition( $definition ) {
 211+ if ( !preg_match( '/^\*+ *([a-zA-Z](?:[-_:.\w\d ]*[a-zA-Z0-9])?)(\s*\[.*?\])?\s*((\|[^|]*)+)\s*$/', $definition, $m ) ) {
 212+ return false;
 213+ }
 214+ //NOTE: the gadget name is used as part of the name of a form field,
 215+ // and must follow the rules defined in http://www.w3.org/TR/html4/types.html#type-cdata
 216+ // Also, title-normalization applies.
 217+ $gadget = new Gadget();
 218+ $gadget->name = trim( str_replace(' ', '_', $m[1] ) );
 219+ $gadget->definition = $definition;
 220+ $params = trim( $m[2], ' []' );
 221+ foreach ( preg_split( '/\s*\|\s*/', $params, -1, PREG_SPLIT_NO_EMPTY ) as $option ) {
 222+ if ( $option == 'ResourceLoader' ) $gadget->resourceLoaded = true;
 223+ }
 224+ foreach ( preg_split( '/\s*\|\s*/', $m[3], -1, PREG_SPLIT_NO_EMPTY ) as $page ) {
 225+ $page = "Gadget-$page";
 226+ if ( preg_match( '/\.js/', $page ) ) {
 227+ $gadget->scripts[] = $page;
 228+ } elseif ( preg_match( '/\.css/', $page ) ) {
 229+ $gadget->styles[] = $page;
 230+ }
 231+ }
 232+ return $gadget;
 233+ }
 234+
 235+ /**
 236+ * @return String: Gadget name
 237+ */
 238+ public function getName() {
 239+ return $this->name;
 240+ }
 241+
 242+ /**
 243+ * @return String: Name of ResourceLoader module for this gadget
 244+ */
 245+ public function getModuleName() {
 246+ return "ext.gadget.{$this->name}";
 247+ }
 248+
 249+ /**
 250+ * Checks whether this is an instance of an older version of this class deserialized from cache
 251+ * @return Boolean
 252+ */
 253+ public function isOutdated() {
 254+ return $this->version != GADGET_CLASS_VERSION;
 255+ }
 256+
 257+ /**
 258+ * @return Boolean: Whether this gadget is enabled for current user
 259+ */
 260+ public function isEnabled() {
 261+ global $wgUser;
 262+ return (bool)$wgUser->getOption( "gadget-{$this->name}" );
 263+ }
 264+
 265+ /**
 266+ * @return Boolean: Whether all of this gadget's JS components support ResourceLoader
 267+ */
 268+ public function supportsResourceLoader() {
 269+ return $this->resourceLoaded;
 270+ }
 271+
 272+ /**
 273+ * @return Boolean: Whether this gadget has resources that can be loaded via ResourceLoader
 274+ */
 275+ public function hasModule() {
 276+ return count( $this->styles )
 277+ + ( $this->supportsResourceLoader() ? count( $this->scripts ) : 0 )
 278+ > 0;
 279+ }
 280+
 281+ /**
 282+ * @return String: Definition for this gadget from MediaWiki:gadgets-definition
 283+ */
 284+ public function getDefinition() {
 285+ return $this->definition;
 286+ }
 287+
 288+ /**
 289+ * @return Array: Array of pages with JS not prefixed with namespace
 290+ */
 291+ public function getScripts() {
 292+ return $this->scripts;
 293+ }
 294+
 295+ /**
 296+ * @return Array: Array of pages with CSS not prefixed with namespace
 297+ */
 298+ public function getStyles() {
 299+ return $this->styles;
 300+ }
 301+
 302+ /**
 303+ * @return Array: Array of all of this gadget's resources
 304+ */
 305+ public function getScriptsAndStyles() {
 306+ return array_merge( $this->scripts, $this->styles );
 307+ }
 308+
 309+ /**
 310+ * Returns module for ResourceLoader, see getModuleName() for its name.
 311+ * If our gadget has no scripts or styles suitable for RL, false will be returned.
 312+ * @return Mixed: GadgetResourceLoaderModule or false
 313+ */
 314+ public function getModule() {
 315+ $pages = array();
 316+ foreach( $this->styles as $style ) {
 317+ $pages[$style] = array( 'ns' => NS_MEDIAWIKI, 'type' => 'style' );
 318+ }
 319+ if ( $this->supportsResourceLoader() ) {
 320+ foreach ( $this->scripts as $script ) {
 321+ $pages[$script] = array( 'ns' => NS_MEDIAWIKI, 'type' => 'script' );
 322+ }
 323+ }
 324+ if ( !count( $pages ) ) {
 325+ return null;
 326+ }
 327+ return new GadgetResourceLoaderModule( $pages );
 328+ }
 329+
 330+ /**
 331+ * Returns list of scripts that don't support ResourceLoader
 332+ * @return Array
 333+ */
 334+ public function getLegacyScripts() {
 335+ if ( $this->supportsResourceLoader() ) {
 336+ return array();
 337+ }
 338+ return $this->scripts;
 339+ }
 340+
 341+ /**
 342+ * Loads and returns a list of all gadgets
 343+ * @return Mixed: Array of gadgets or false
 344+ */
 345+ public static function loadList() {
 346+ static $gadgets = null;
 347+
 348+ if ( $gadgets !== null ) return $gadgets;
 349+
 350+ wfProfileIn( __METHOD__ );
 351+ $struct = self::loadStructuredList();
 352+ if ( !$struct ) {
 353+ $gadgets = $struct;
 354+ return $gadgets;
 355+ }
 356+
 357+ $gadgets = array();
 358+ foreach ( $struct as $section => $entries ) {
 359+ $gadgets = array_merge( $gadgets, $entries );
 360+ }
 361+ wfProfileOut( __METHOD__ );
 362+
 363+ return $gadgets;
 364+ }
 365+
 366+ /**
 367+ * Checks whether gadget list from cache can be used.
 368+ * @return Boolean
 369+ */
 370+ private static function isValidList( $gadgets ) {
 371+ if ( !is_array( $gadgets ) ) return false;
 372+ // Check if we have 1) array of gadgets 2) the gadgets are up to date
 373+ // One check is enough
 374+ foreach ( $gadgets as $section => $list ) {
 375+ foreach ( $list as $g ) {
 376+ if ( !( $g instanceof Gadget ) || $g->isOutdated() ) {
 377+ return false;
 378+ } else {
 379+ return true;
 380+ }
 381+ }
 382+ }
 383+ return true; // empty array
 384+ }
 385+
 386+ /**
 387+ * Loads list of gadgets and returns it as associative array of sections with gadgets
 388+ * e.g. array( 'sectionnname1' => array( $gadget1, $gadget2),
 389+ * 'sectionnname2' => array( $gadget3 ) );
 390+ * @param $forceNewText String: New text of MediaWiki:gadgets-sdefinition. If specified, will
 391+ * force a purge of cache and recreation of the gadget list.
 392+ * @return Mixed: Array or false
 393+ */
 394+ public static function loadStructuredList( $forceNewText = null ) {
 395+ global $wgMemc;
 396+
 397+ static $gadgets = null;
 398+ if ( $gadgets !== null && $forceNewText === null ) return $gadgets;
 399+
 400+ wfProfileIn( __METHOD__ );
 401+ $key = wfMemcKey( 'gadgets-definition' );
 402+
 403+ if ( $forceNewText === null ) {
 404+ //cached?
 405+ $gadgets = $wgMemc->get( $key );
 406+ if ( self::isValidList( $gadgets ) ) {
 407+ wfProfileOut( __METHOD__ );
 408+ return $gadgets;
 409+ }
 410+
 411+ $g = wfMsgForContentNoTrans( "gadgets-definition" );
 412+ if ( wfEmptyMsg( "gadgets-definition", $g ) ) {
 413+ $gadgets = false;
 414+ wfProfileOut( __METHOD__ );
 415+ return $gadgets;
 416+ }
 417+ } else {
 418+ $g = $forceNewText;
 419+ }
 420+
 421+ $g = preg_replace( '/<!--.*-->/s', '', $g );
 422+ $g = preg_split( '/(\r\n|\r|\n)+/', $g );
 423+
 424+ $gadgets = array();
 425+ $section = '';
 426+
 427+ foreach ( $g as $line ) {
 428+ if ( preg_match( '/^==+ *([^*:\s|]+?)\s*==+\s*$/', $line, $m ) ) {
 429+ $section = $m[1];
 430+ }
 431+ else {
 432+ $gadget = self::newFromDefinition( $line );
 433+ if ( $gadget ) {
 434+ $gadgets[$section][$gadget->getName()] = $gadget;
 435+ }
 436+ }
 437+ }
 438+
 439+ //cache for a while. gets purged automatically when MediaWiki:Gadgets-definition is edited
 440+ $wgMemc->set( $key, $gadgets, 60*60*24 );
 441+ $source = $forceNewText !== null ? 'input text' : 'MediaWiki:Gadgets-definition';
 442+ wfDebug( __METHOD__ . ": $source parsed, cache entry $key updated\n");
 443+ wfProfileOut( __METHOD__ );
 444+
 445+ return $gadgets;
 446+ }
 447+}
 448+
 449+/**
 450+ * Class representing a list of resources for one gadget
 451+ */
 452+class GadgetResourceLoaderModule extends ResourceLoaderWikiModule {
 453+ private $pages;
 454+
 455+ /**
 456+ * Creates an instance of this class
 457+ * @param $pages Array: Associative array of pages in ResourceLoaderWikiModule-compatible
 458+ * format, for example:
 459+ * array(
 460+ * 'Gadget-foo.js' => array( 'ns' => NS_MEDIAWIKI, 'type' => 'script' ),
 461+ * 'Gadget-foo.css' => array( 'ns' => NS_MEDIAWIKI, 'type' => 'style' ),
 462+ * )
 463+ */
 464+ public function __construct( $pages ) {
 465+ $this->pages = $pages;
 466+ }
 467+
 468+ /**
 469+ * Overrides the abstract function from ResourceLoaderWikiModule class
 470+ * @return Array: $pages passed to __construct()
 471+ */
 472+ protected function getPages( ResourceLoaderContext $context ) {
 473+ return $this->pages;
 474+ }
 475+}
\ No newline at end of file
Property changes on: trunk/extensions/Gadgets/Gadgets_body.php
___________________________________________________________________
Added: svn:eol-style
1476 + native
Added: svn:keywords
2477 + LastChangedDate LastChangedRevision
Index: trunk/extensions/Gadgets/Gadgets.php
@@ -17,224 +17,32 @@
1818 die( 1 );
1919 }
2020
 21+if ( version_compare( $wgVersion, '1.17alpha', '<' ) ) {
 22+ die( "This version of Extension:Gadgets requires MediaWiki 1.17+\n" );
 23+}
 24+
2125 $wgExtensionCredits['other'][] = array(
2226 'path' => __FILE__,
2327 'name' => 'Gadgets',
24 - 'author' => 'Daniel Kinzler',
 28+ 'author' => array( 'Daniel Kinzler', 'Max Semenik' ),
2529 'url' => 'http://mediawiki.org/wiki/Extension:Gadgets',
2630 'descriptionmsg' => 'gadgets-desc',
2731 );
2832
29 -$wgHooks['GetPreferences'][] = 'wfGadgetsGetPreferences';
30 -$wgHooks['BeforePageDisplay'][] = 'wfGadgetsBeforePageDisplay';
31 -$wgHooks['ArticleSaveComplete'][] = 'wfGadgetsArticleSaveComplete';
 33+$wgHooks['ArticleSaveComplete'][] = 'GadgetHooks::articleSaveComplete';
 34+$wgHooks['BeforePageDisplay'][] = 'GadgetHooks::beforePageDisplay';
 35+$wgHooks['GetPreferences'][] = 'GadgetHooks::getPreferences';
 36+$wgHooks['ResourceLoaderRegisterModules'][] = 'GadgetHooks::registerModules';
 37+$wgHooks['UnitTestsList'][] = 'GadgetHooks::unitTestsList';
3238
3339 $dir = dirname(__FILE__) . '/';
3440 $wgExtensionMessagesFiles['Gadgets'] = $dir . 'Gadgets.i18n.php';
3541 $wgExtensionAliasesFiles['Gadgets'] = $dir . 'Gadgets.alias.php';
 42+
 43+$wgAutoloadClasses['Gadget'] = $dir . 'Gadgets_body.php';
 44+$wgAutoloadClasses['GadgetHooks'] = $dir . 'Gadgets_body.php';
 45+$wgAutoloadClasses['GadgetsResourceLoaderModule'] = $dir . 'Gadgets_body.php';
3646 $wgAutoloadClasses['SpecialGadgets'] = $dir . 'SpecialGadgets.php';
 47+
3748 $wgSpecialPages['Gadgets'] = 'SpecialGadgets';
3849 $wgSpecialPageGroups['Gadgets'] = 'wiki';
39 -
40 -function wfGadgetsArticleSaveComplete( $article, $user, $text ) {
41 - //update cache if MediaWiki:Gadgets-definition was edited
42 - $title = $article->mTitle;
43 - if( $title->getNamespace() == NS_MEDIAWIKI && $title->getText() == 'Gadgets-definition' ) {
44 - wfLoadGadgetsStructured( $text );
45 - }
46 - return true;
47 -}
48 -
49 -function wfLoadGadgets() {
50 - static $gadgets = null;
51 -
52 - if ( $gadgets !== null ) {
53 - return $gadgets;
54 - }
55 -
56 - $struct = wfLoadGadgetsStructured();
57 - if ( !$struct ) {
58 - $gadgets = $struct;
59 - return $gadgets;
60 - }
61 -
62 - $gadgets = array();
63 - foreach ( $struct as $entries ) {
64 - $gadgets = array_merge( $gadgets, $entries );
65 - }
66 -
67 - return $gadgets;
68 -}
69 -
70 -function wfLoadGadgetsStructured( $forceNewText = null ) {
71 - global $wgMemc;
72 -
73 - static $gadgets = null;
74 - if ( $gadgets !== null && $forceNewText === null ) {
75 - return $gadgets;
76 - }
77 -
78 - $key = wfMemcKey( 'gadgets-definition' );
79 -
80 - if ( $forceNewText === null ) {
81 - //cached?
82 - $gadgets = $wgMemc->get( $key );
83 - if ( is_array($gadgets) ) return $gadgets;
84 -
85 - $g = wfMsgForContentNoTrans( "gadgets-definition" );
86 - if ( wfEmptyMsg( "gadgets-definition", $g ) ) {
87 - $gadgets = false;
88 - return $gadgets;
89 - }
90 - } else {
91 - $g = $forceNewText;
92 - }
93 -
94 - $g = preg_replace( '/<!--.*-->/s', '', $g );
95 - $g = preg_split( '/(\r\n|\r|\n)+/', $g );
96 -
97 - $gadgets = array();
98 - $section = '';
99 -
100 - foreach ( $g as $line ) {
101 - if ( preg_match( '/^==+ *([^*:\s|]+?)\s*==+\s*$/', $line, $m ) ) {
102 - $section = $m[1];
103 - }
104 - else if ( preg_match( '/^\*+ *([a-zA-Z](?:[-_:.\w\d ]*[a-zA-Z0-9])?)\s*((\|[^|]*)+)\s*$/', $line, $m ) ) {
105 - //NOTE: the gadget name is used as part of the name of a form field,
106 - // and must follow the rules defined in http://www.w3.org/TR/html4/types.html#type-cdata
107 - // Also, title-normalization applies.
108 - $name = str_replace(' ', '_', $m[1] );
109 -
110 - $code = preg_split( '/\s*\|\s*/', $m[2], -1, PREG_SPLIT_NO_EMPTY );
111 -
112 - if ( $code ) {
113 - $gadgets[$section][$name] = $code;
114 - }
115 - }
116 - }
117 -
118 - //cache for a while. gets purged automatically when MediaWiki:Gadgets-definition is edited
119 - $wgMemc->set( $key, $gadgets, 60*60*24 );
120 - $source = $forceNewText !== null ? 'input text' : 'MediaWiki:Gadgets-definition';
121 - wfDebug( __METHOD__ . ": $source parsed, cache entry $key updated\n");
122 -
123 - return $gadgets;
124 -}
125 -
126 -function wfGadgetsGetPreferences( $user, &$preferences ) {
127 - $gadgets = wfLoadGadgetsStructured();
128 - if (!$gadgets) {
129 - return true;
130 - }
131 -
132 - $options = array();
133 - foreach( $gadgets as $section => $thisSection ) {
134 - if ( $section !== '' ) {
135 - $section = wfMsgExt( "gadget-section-$section", 'parseinline' );
136 - $options[$section] = array();
137 - $destination = &$options[$section];
138 - } else {
139 - $destination = &$options;
140 - }
141 - foreach( array_keys( $thisSection ) as $gname ) {
142 - $destination[wfMsgExt( "gadget-$gname", 'parseinline' )] = $gname;
143 - }
144 - }
145 -
146 - $preferences['gadgets-intro'] =
147 - array(
148 - 'type' => 'info',
149 - 'label' => '&#160;',
150 - 'default' => Xml::tags( 'tr', array(),
151 - Xml::tags( 'td', array( 'colspan' => 2 ),
152 - wfMsgExt( 'gadgets-prefstext', 'parse' ) ) ),
153 - 'section' => 'gadgets',
154 - 'raw' => 1,
155 - 'rawrow' => 1,
156 - );
157 -
158 - $preferences['gadgets'] =
159 - array(
160 - 'type' => 'multiselect',
161 - 'options' => $options,
162 - 'section' => 'gadgets',
163 - 'label' => '&#160;',
164 - 'prefix' => 'gadget-',
165 - );
166 -
167 - return true;
168 -}
169 -
170 -function wfGadgetsBeforePageDisplay( $out ) {
171 - global $wgUser;
172 - if ( !$wgUser->isLoggedIn() ) {
173 - return true;
174 - }
175 -
176 - //disable all gadgets on critical special pages
177 - //NOTE: $out->isUserJsAllowed() is tempting, but always fals if $wgAllowUserJs is false.
178 - // That would disable gadgets on wikis without user JS. Introducing $out->isJsAllowed()
179 - // may work, but should that really apply also to MediaWiki:common.js? Even on the preference page?
180 - // See bug 22929 for discussion.
181 - $title = $out->getTitle();
182 - if ( $title->isSpecial( 'Preferences' )
183 - || $title->isSpecial( 'Resetpass' )
184 - || $title->isSpecial( 'Userlogin' ) ) {
185 - return true;
186 - }
187 -
188 - $gadgets = wfLoadGadgets();
189 - if ( !$gadgets ) {
190 - return true;
191 - }
192 -
193 - $lb = new LinkBatch();
194 - $lb->setCaller( __METHOD__ );
195 - $pages = array();
196 -
197 - foreach ( $gadgets as $gname => $id ) {
198 - $tname = "gadget-$gname";
199 - if ( $wgUser->getOption( $tname ) ) {
200 - foreach ( $id as $page ) {
201 - $lb->add( NS_MEDIAWIKI, "Gadget-$page" );
202 - $pages[] = $page;
203 - }
204 - }
205 - }
206 -
207 - $lb->execute( __METHOD__ );
208 -
209 - $done = array();
210 - foreach ( $pages as $page ) {
211 - if ( isset( $done[$page] ) ) {
212 - continue;
213 - }
214 - $done[$page] = true;
215 - wfApplyGadgetCode( $page, $out );
216 - }
217 -
218 - return true;
219 -}
220 -
221 -function wfApplyGadgetCode( $page, $out ) {
222 - global $wgJsMimeType;
223 -
224 - //FIXME: stuff added via $out->addScript appears below usercss and userjs in the head tag.
225 - // but we'd want it to appear above explicit user stuff, so it can be overwritten.
226 -
227 - $t = Title::makeTitleSafe( NS_MEDIAWIKI, "Gadget-$page" );
228 - if ( !$t ) {
229 - return;
230 - }
231 -
232 - if ( preg_match( '/\.js/', $page ) ) {
233 - $u = $t->getLocalURL( 'action=raw&ctype=' . $wgJsMimeType );
234 - //switched to addScriptFile call to support scriptLoader
235 - $out->addScriptFile( $u, $t->getLatestRevID() );
236 - } elseif ( preg_match( '/\.css/', $page ) ) {
237 - $u = $t->getLocalURL( 'action=raw&ctype=text/css&' . $t->getLatestRevID() );
238 - $out->addScript( Html::linkedStyle( $u ) );
239 - }
240 -}
241 -
Index: trunk/extensions/Gadgets/SpecialGadgets.php
@@ -51,7 +51,7 @@
5252 $wgOut->setPagetitle( wfMsg( "gadgets-title" ) );
5353 $wgOut->addWikiMsg( 'gadgets-pagetext' );
5454
55 - $gadgets = wfLoadGadgetsStructured();
 55+ $gadgets = Gadget::loadStructuredList();
5656 if ( !$gadgets ) return;
5757
5858 $lang = "";
@@ -84,17 +84,17 @@
8585 $wgOut->addHTML( Html::rawElement( 'h2', array(), $ttext . $lnk ) . "\n" );
8686 }
8787
88 - foreach ( $entries as $gname => $code ) {
89 - $t = Title::makeTitleSafe( NS_MEDIAWIKI, "Gadget-$gname$lang" );
 88+ foreach ( $entries as $gadget ) {
 89+ $t = Title::makeTitleSafe( NS_MEDIAWIKI, "Gadget-{$gadget->getName()}$lang" );
9090 if ( !$t ) continue;
9191
9292 $links = array();
9393 if ( $editInterfaceAllowed ) {
9494 $links[] = $skin->link( $t, wfMsgHTML( 'edit' ), array(), array( 'action' => 'edit' ) );
9595 }
96 - $links[] = $skin->link( $this->getTitle( "export/$gname" ), wfMsgHtml( 'gadgets-export' ) );
 96+ $links[] = $skin->link( $this->getTitle( "export/{$gadget->getName()}" ), wfMsgHtml( 'gadgets-export' ) );
9797
98 - $ttext = wfMsgExt( "gadget-$gname", $msgOpt );
 98+ $ttext = wfMsgExt( "gadget-{$gadget->getName()}", $msgOpt );
9999
100100 if( !$listOpen ) {
101101 $listOpen = true;
@@ -107,8 +107,8 @@
108108 );
109109
110110 $lnk = array();
111 - foreach ( $code as $codePage ) {
112 - $t = Title::makeTitleSafe( NS_MEDIAWIKI, "Gadget-$codePage" );
 111+ foreach ( $gadget->getScriptsAndStyles() as $codePage ) {
 112+ $t = Title::makeTitleSafe( NS_MEDIAWIKI, $codePage );
113113 if ( !$t ) continue;
114114
115115 $lnk[] = $skin->link( $t, htmlspecialchars( $t->getText() ) );
@@ -130,20 +130,20 @@
131131 public function showExportForm( $gadget ) {
132132 global $wgOut, $wgScript;
133133
134 - $gadgets = wfLoadGadgets();
 134+ $gadgets = Gadget::loadList();
135135 if ( !isset( $gadgets[$gadget] ) ) {
136136 $wgOut->showErrorPage( 'error', 'gadgets-not-found', array( $gadget ) );
137137 return;
138138 }
139139
140 - $ourDefinition = "* $gadget|" . implode('|', $gadgets[$gadget] );
 140+ $g = $gadgets[$gadget];
141141 $this->setHeaders();
142142 $wgOut->setPagetitle( wfMsg( "gadgets-export-title" ) );
143 - $wgOut->addWikiMsg( 'gadgets-export-text', $gadget, $ourDefinition );
 143+ $wgOut->addWikiMsg( 'gadgets-export-text', $gadget, $g->getDefinition() );
144144
145145 $exportList = "MediaWiki:gadget-$gadget\n";
146 - foreach ( $gadgets[$gadget] as $page ) {
147 - $exportList .= "MediaWiki:gadget-$page\n";
 146+ foreach ( $g->getScriptsAndStyles() as $page ) {
 147+ $exportList .= "MediaWiki:$page\n";
148148 }
149149
150150 $wgOut->addHTML( Html::openElement( 'form', array( 'method' => 'GET', 'action' => $wgScript ) )
Index: trunk/extensions/Gadgets/Gadgets_tests.php
@@ -0,0 +1,39 @@
 2+<?php
 3+
 4+/**
 5+ * @group Gadgets
 6+ */
 7+class GadgetsTest extends PHPUnit_Framework_TestCase {
 8+
 9+ private function create( $line ) {
 10+ $g = Gadget::newFromDefinition( $line );
 11+ // assertInstanceOf() is available since PHPUnit 3.5
 12+ $this->assertEquals( 'Gadget', get_class( $g ) );
 13+ return $g;
 14+ }
 15+
 16+ function testInvalidLines() {
 17+ $this->assertFalse( Gadget::newFromDefinition( '' ) );
 18+ $this->assertFalse( Gadget::newFromDefinition( '<foo|bar>' ) );
 19+ }
 20+
 21+ function testSimpleCases() {
 22+ $g = $this->create( '* foo bar| foo.css|foo.js|foo.bar' );
 23+ $this->assertEquals( 'foo_bar', $g->getName() );
 24+ $this->assertEquals( 'ext.gadget.foo_bar', $g->getModuleName() );
 25+ $this->assertEquals( array( 'Gadget-foo.js' ), $g->getScripts() );
 26+ $this->assertEquals( array( 'Gadget-foo.css' ), $g->getStyles() );
 27+ $this->assertEquals( array( 'Gadget-foo.js', 'Gadget-foo.css' ),
 28+ $g->getScriptsAndStyles() );
 29+ $this->assertEquals( array( 'Gadget-foo.js' ), $g->getLegacyScripts() );
 30+ $this->assertFalse( $g->supportsResourceLoader() );
 31+ $this->assertTrue( $g->hasModule() );
 32+ }
 33+
 34+ function testRLtag() {
 35+ $g = $this->create( '*foo [ResourceLoader]|foo.js|foo.css' );
 36+ $this->assertEquals( 'foo', $g->getName() );
 37+ $this->assertTrue( $g->supportsResourceLoader() );
 38+ $this->assertEquals(0, count( $g->getLegacyScripts() ) );
 39+ }
 40+}
\ No newline at end of file
Property changes on: trunk/extensions/Gadgets/Gadgets_tests.php
___________________________________________________________________
Added: svn:eol-style
141 + native
Property changes on: trunk/extensions/Gadgets
___________________________________________________________________
Added: svn:mergeinfo
242 Merged /branches/Gadgets-work:r73145-76526

Follow-up revisions

RevisionCommit summaryAuthorDate
r76528Follow-up r76527: updated readmemaxsem18:46, 11 November 2010
r76535Stupid, stupid /me (ping r76527)maxsem19:10, 11 November 2010

Comments

#Comment by Reedy (talk | contribs)   22:47, 7 January 2011

Same as r70719, seems to work fine on my SVN HEAD dev wiki

Status & tagging log