[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Gadgets extension - lets users select custom javascript gadgets 4 * 5 * For more info see http://mediawiki.org/wiki/Extension:Gadgets 6 * 7 * @file 8 * @ingroup Extensions 9 * @author Daniel Kinzler, brightbyte.de 10 * @copyright © 2007 Daniel Kinzler 11 * @license GNU General Public Licence 2.0 or later 12 */ 13 14 15 /** 16 * Wrapper for one gadget. 17 */ 18 class Gadget { 19 /** 20 * Increment this when changing class structure 21 */ 22 const GADGET_CLASS_VERSION = 7; 23 24 private $scripts = array(), 25 $styles = array(), 26 $dependencies = array(), 27 $name, 28 $definition, 29 $resourceLoaded = false, 30 $requiredRights = array(), 31 $requiredSkins = array(), 32 $targets = array( 'desktop' ), 33 $onByDefault = false, 34 $position = 'bottom', 35 $category; 36 37 /** 38 * Creates an instance of this class from definition in MediaWiki:Gadgets-definition 39 * @param $definition String: Gadget definition 40 * @return Gadget|bool Instance of Gadget class or false if $definition is invalid 41 */ 42 public static function newFromDefinition( $definition ) { 43 $m = array(); 44 if ( !preg_match( '/^\*+ *([a-zA-Z](?:[-_:.\w\d ]*[a-zA-Z0-9])?)(\s*\[.*?\])?\s*((\|[^|]*)+)\s*$/', $definition, $m ) ) { 45 return false; 46 } 47 // NOTE: the gadget name is used as part of the name of a form field, 48 // and must follow the rules defined in http://www.w3.org/TR/html4/types.html#type-cdata 49 // Also, title-normalization applies. 50 $gadget = new Gadget(); 51 $gadget->name = trim( str_replace( ' ', '_', $m[1] ) ); 52 // If the name is too long, then RL will throw an MWException when 53 // we try to register the module 54 if ( !ResourceLoader::isValidModuleName( $gadget->getModuleName() ) ) { 55 return false; 56 } 57 $gadget->definition = $definition; 58 $options = trim( $m[2], ' []' ); 59 60 foreach ( preg_split( '/\s*\|\s*/', $options, -1, PREG_SPLIT_NO_EMPTY ) as $option ) { 61 $arr = preg_split( '/\s*=\s*/', $option, 2 ); 62 $option = $arr[0]; 63 if ( isset( $arr[1] ) ) { 64 $params = explode( ',', $arr[1] ); 65 $params = array_map( 'trim', $params ); 66 } else { 67 $params = array(); 68 } 69 70 switch ( $option ) { 71 case 'ResourceLoader': 72 $gadget->resourceLoaded = true; 73 break; 74 case 'dependencies': 75 $gadget->dependencies = $params; 76 break; 77 case 'rights': 78 $gadget->requiredRights = $params; 79 break; 80 case 'skins': 81 $gadget->requiredSkins = $params; 82 break; 83 case 'default': 84 $gadget->onByDefault = true; 85 break; 86 case 'targets': 87 $gadget->targets = $params; 88 break; 89 case 'top': 90 $gadget->position = 'top'; 91 break; 92 } 93 } 94 95 foreach ( preg_split( '/\s*\|\s*/', $m[3], -1, PREG_SPLIT_NO_EMPTY ) as $page ) { 96 $page = "Gadget-$page"; 97 98 if ( preg_match( '/\.js/', $page ) ) { 99 $gadget->scripts[] = $page; 100 } elseif ( preg_match( '/\.css/', $page ) ) { 101 $gadget->styles[] = $page; 102 } 103 } 104 105 return $gadget; 106 } 107 108 /** 109 * @return String: Gadget name 110 */ 111 public function getName() { 112 return $this->name; 113 } 114 115 /** 116 * @return String: Gadget description parsed into HTML 117 */ 118 public function getDescription() { 119 return wfMessage( "gadget-{$this->getName()}" )->parse(); 120 } 121 122 /** 123 * @return String: Wikitext of gadget description 124 */ 125 public function getRawDescription() { 126 return wfMessage( "gadget-{$this->getName()}" )->plain(); 127 } 128 129 /** 130 * @return String: Name of category (aka section) our gadget belongs to. Empty string if none. 131 */ 132 public function getCategory() { 133 return $this->category; 134 } 135 136 /** 137 * @return String: Name of ResourceLoader module for this gadget 138 */ 139 public function getModuleName() { 140 return "ext.gadget.{$this->name}"; 141 } 142 143 /** 144 * Checks whether this gadget is enabled for given user 145 * 146 * @param $user User: user to check against 147 * @return Boolean 148 */ 149 public function isEnabled( $user ) { 150 return (bool)$user->getOption( "gadget-{$this->name}", $this->onByDefault ); 151 } 152 153 /** 154 * Checks whether given user has permissions to use this gadget 155 * 156 * @param $user User: user to check against 157 * @return Boolean 158 */ 159 public function isAllowed( $user ) { 160 return count( array_intersect( $this->requiredRights, $user->getRights() ) ) == count( $this->requiredRights ) 161 && ( !count( $this->requiredSkins ) || in_array( $user->getOption( 'skin' ), $this->requiredSkins ) ); 162 } 163 164 /** 165 * @return Boolean: Whether this gadget is on by default for everyone (but can be disabled in preferences) 166 */ 167 public function isOnByDefault() { 168 return $this->onByDefault; 169 } 170 171 /** 172 * @return Boolean: Whether all of this gadget's JS components support ResourceLoader 173 */ 174 public function supportsResourceLoader() { 175 return $this->resourceLoaded; 176 } 177 178 /** 179 * @return Boolean: Whether this gadget has resources that can be loaded via ResourceLoader 180 */ 181 public function hasModule() { 182 return count( $this->styles ) 183 + ( $this->supportsResourceLoader() ? count( $this->scripts ) : 0 ) 184 > 0; 185 } 186 187 /** 188 * @return String: Definition for this gadget from MediaWiki:gadgets-definition 189 */ 190 public function getDefinition() { 191 return $this->definition; 192 } 193 194 /** 195 * @return Array: Array of pages with JS not prefixed with namespace 196 */ 197 public function getScripts() { 198 return $this->scripts; 199 } 200 201 /** 202 * @return Array: Array of pages with CSS not prefixed with namespace 203 */ 204 public function getStyles() { 205 return $this->styles; 206 } 207 208 /** 209 * @return Array: Array of all of this gadget's resources 210 */ 211 public function getScriptsAndStyles() { 212 return array_merge( $this->scripts, $this->styles ); 213 } 214 215 /** 216 * Returns module for ResourceLoader, see getModuleName() for its name. 217 * If our gadget has no scripts or styles suitable for RL, false will be returned. 218 * @return Mixed: GadgetResourceLoaderModule or false 219 */ 220 public function getModule() { 221 $pages = array(); 222 223 foreach ( $this->styles as $style ) { 224 $pages['MediaWiki:' . $style] = array( 'type' => 'style' ); 225 } 226 227 if ( $this->supportsResourceLoader() ) { 228 foreach ( $this->scripts as $script ) { 229 $pages['MediaWiki:' . $script] = array( 'type' => 'script' ); 230 } 231 } 232 233 if ( !count( $pages ) ) { 234 return null; 235 } 236 237 return new GadgetResourceLoaderModule( $pages, $this->dependencies, $this->targets, $this->position ); 238 } 239 240 /** 241 * Returns list of scripts that don't support ResourceLoader 242 * @return Array 243 */ 244 public function getLegacyScripts() { 245 if ( $this->supportsResourceLoader() ) { 246 return array(); 247 } 248 return $this->scripts; 249 } 250 251 /** 252 * Returns names of resources this gadget depends on 253 * @return Array 254 */ 255 public function getDependencies() { 256 return $this->dependencies; 257 } 258 259 /** 260 * Returns array of permissions required by this gadget 261 * @return Array 262 */ 263 public function getRequiredRights() { 264 return $this->requiredRights; 265 } 266 267 /** 268 * Returns array of skins where this gadget works 269 * @return Array 270 */ 271 public function getRequiredSkins() { 272 return $this->requiredSkins; 273 } 274 275 /** 276 * Returns the position of this Gadget's ResourceLoader module 277 * @return String: 'bottom' or 'top' 278 */ 279 public function getPosition() { 280 return $this->position; 281 } 282 283 /** 284 * Loads and returns a list of all gadgets 285 * @return Mixed: Array of gadgets or false 286 */ 287 public static function loadList() { 288 static $gadgets = null; 289 290 if ( $gadgets !== null ) { 291 return $gadgets; 292 } 293 294 wfProfileIn( __METHOD__ ); 295 $struct = self::loadStructuredList(); 296 297 if ( !$struct ) { 298 $gadgets = $struct; 299 wfProfileOut( __METHOD__ ); 300 return $gadgets; 301 } 302 303 $gadgets = array(); 304 foreach ( $struct as $entries ) { 305 $gadgets = array_merge( $gadgets, $entries ); 306 } 307 wfProfileOut( __METHOD__ ); 308 309 return $gadgets; 310 } 311 312 /** 313 * Checks whether gadget list from cache can be used. 314 * @param $gadgets array 315 * @return Boolean 316 */ 317 private static function isValidList( $gadgets ) { 318 if ( !is_array( $gadgets ) ) { 319 return false; 320 } 321 // Check if we have an array of gadgets 322 // One check is enough 323 /** 324 * @var $g Gadget 325 */ 326 foreach ( $gadgets as $list ) { 327 foreach ( $list as $g ) { 328 return $g instanceof Gadget; 329 } 330 } 331 332 return true; // empty array 333 } 334 335 /** 336 * Loads list of gadgets and returns it as associative array of sections with gadgets 337 * e.g. array( 'sectionnname1' => array( $gadget1, $gadget2 ), 338 * 'sectionnname2' => array( $gadget3 ) ); 339 * @param $forceNewText String: New text of MediaWiki:gadgets-definition. If specified, will 340 * force a purge of cache and recreation of the gadget list. 341 * @return Mixed: Array or false 342 */ 343 public static function loadStructuredList( $forceNewText = null ) { 344 global $wgMemc, $wgGadgetsCaching; 345 346 static $gadgets = null; 347 if ( $gadgets !== null && $forceNewText === null ) { 348 return $gadgets; 349 } 350 351 wfProfileIn( __METHOD__ ); 352 $key = wfMemcKey( 'gadgets-definition', self::GADGET_CLASS_VERSION ); 353 354 if ( $forceNewText === null ) { 355 if ( $wgGadgetsCaching ) { 356 // cached? 357 $gadgets = $wgMemc->get( $key ); 358 if ( self::isValidList( $gadgets ) ) { 359 wfProfileOut( __METHOD__ ); 360 return $gadgets; 361 } 362 } 363 364 $g = wfMessage( "gadgets-definition" )->inContentLanguage(); 365 if ( !$g->exists() ) { 366 $gadgets = false; 367 wfProfileOut( __METHOD__ ); 368 return $gadgets; 369 } 370 $g = $g->plain(); 371 } else { 372 $g = $forceNewText; 373 } 374 375 $gadgets = self::listFromDefinition( $g ); 376 377 if ( !count( $gadgets ) || !$wgGadgetsCaching ) { 378 // Don't cache in case we couldn't find any gadgets. Bug 37228 379 $gadgets = false; 380 wfProfileOut( __METHOD__ ); 381 return $gadgets; 382 } 383 384 // cache for a while. gets purged automatically when MediaWiki:Gadgets-definition is edited 385 $wgMemc->set( $key, $gadgets, 60 * 60 * 24 ); 386 $source = $forceNewText !== null ? 'input text' : 'MediaWiki:Gadgets-definition'; 387 wfDebug( __METHOD__ . ": $source parsed, cache entry $key updated\n" ); 388 wfProfileOut( __METHOD__ ); 389 390 return $gadgets; 391 } 392 393 /** 394 * Generates a structured list of Gadget objects from a definition 395 * 396 * @param $definition 397 * @return array Array( category => Array( name => Gadget ) ) 398 */ 399 private static function listFromDefinition( $definition ) { 400 $definition = preg_replace( '/<!--.*?-->/s', '', $definition ); 401 $lines = preg_split( '/(\r\n|\r|\n)+/', $definition ); 402 403 $gadgets = array(); 404 $section = ''; 405 406 foreach ( $lines as $line ) { 407 $m = array(); 408 if ( preg_match( '/^==+ *([^*:\s|]+?)\s*==+\s*$/', $line, $m ) ) { 409 $section = $m[1]; 410 } else { 411 $gadget = self::newFromDefinition( $line ); 412 if ( $gadget ) { 413 $gadgets[$section][$gadget->getName()] = $gadget; 414 $gadget->category = $section; 415 } 416 } 417 } 418 return $gadgets; 419 } 420 } 421 422 /** 423 * Class representing a list of resources for one gadget 424 */ 425 class GadgetResourceLoaderModule extends ResourceLoaderWikiModule { 426 private $pages, $dependencies; 427 428 /** 429 * Creates an instance of this class 430 * 431 * @param $pages Array: Associative array of pages in ResourceLoaderWikiModule-compatible 432 * format, for example: 433 * array( 434 * 'MediaWiki:Gadget-foo.js' => array( 'type' => 'script' ), 435 * 'MediaWiki:Gadget-foo.css' => array( 'type' => 'style' ), 436 * ) 437 * @param $dependencies Array: Names of resources this module depends on 438 * @param $targets Array: List of targets this module support 439 * @param $position String: 'bottom' or 'top' 440 */ 441 public function __construct( $pages, $dependencies, $targets, $position ) { 442 $this->pages = $pages; 443 $this->dependencies = $dependencies; 444 $this->targets = $targets; 445 $this->position = $position; 446 } 447 448 /** 449 * Overrides the abstract function from ResourceLoaderWikiModule class 450 * @param $context ResourceLoaderContext 451 * @return Array: $pages passed to __construct() 452 */ 453 protected function getPages( ResourceLoaderContext $context ) { 454 return $this->pages; 455 } 456 457 /** 458 * Overrides ResourceLoaderModule::getDependencies() 459 * @return Array: Names of resources this module depends on 460 */ 461 public function getDependencies() { 462 return $this->dependencies; 463 } 464 465 /** 466 * Overrides ResourceLoaderModule::getPosition() 467 * @return String: 'bottom' or 'top' 468 */ 469 public function getPosition() { 470 return $this->position; 471 } 472 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |