[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/extensions/Gadgets/ -> Gadgets_body.php (source)

   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  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1