*/ class RandomImage { private $parser = null; private $width = false; private $float = false; private $caption = ''; private $choices = array(); private $categories = array(); private $nocategories = array(); /** * Constructor * * @param Parser $parser Parent parser * @param array $options Initial options * @param string $caption Caption text */ public function __construct( $parser, $options, $caption ) { $this->parser = $parser; $this->caption = $caption; $this->setOptions( $options ); } /** * Extract applicable options from tag attributes * * @param array $options Tag attributes */ protected function setOptions( $options ) { if( isset( $options['size'] ) ) { $size = intval( $options['size'] ); if( $size > 0 ) $this->width = $size; } if( isset( $options['float'] ) ) { $float = strtolower( $options['float'] ); // TODO: Use magic words instead if( in_array( $float, array( 'left', 'right', 'center' ) ) ) $this->float = $float; } if( isset( $options['choices'] ) ) { $choices = explode( '|', $options['choices'] ); if( count( $choices ) > 0 ) $this->choices = $choices; } if( isset( $options['categories'] ) ) { $categories = explode( '|', $options['categories'] ); if( count( $categories ) > 0 ) $this->categories = $categories; } if( isset( $options['nocategories'] ) ) { $nocategories = explode( '|', $options['nocategories'] ); if( count( $nocategories ) > 0 ) $this->nocategories = $nocategories; } } /** * Render a random image * * @return string */ public function render() { $title = $this->pickImage(); if( $title instanceof Title && $this->imageExists( $title ) ) { return $this->parser->recursiveTagParse( $this->buildMarkup( $title ) ); } return ''; } /** * Does the specified image exist? * * This is a wrapper around the new File/FileRepo mechanism from * 1.10, to avoid breaking compatibility with older versions for * no good reason * * @param Title $title Title of the image * @return bool */ protected function imageExists( $title ) { $file = function_exists( 'wfFindFile' ) ? wfFindFile( $title ) : new Image( $title ); return is_object( $file ) && $file->exists(); } /** * Prepare image markup for the given image * * @param Title $title Title of the image to render * @return string */ protected function buildMarkup( $title ) { $parts[] = $title->getPrefixedText(); $parts[] = 'thumb'; if( $this->width !== false ) $parts[] = "{$this->width}px"; if( $this->float ) $parts[] = $this->float; $parts[] = $this->getCaption( $title ); return '[[' . implode( '|', $parts ) . ']]'; } /** * Locate and remove the "magnify" icon in the image HTML * * @param string $html Image HTML * @return string */ protected function removeMagnifier( $html ) { $doc = DOMDocument::loadHTML( $html ); $xpath = new DOMXPath( $doc ); foreach( $xpath->query( '//div[@class="magnify"]' ) as $mag ) $mag->parentNode->removeChild( $mag ); return preg_replace( '!<\?xml[^?]*\?>!', '', $doc->saveXml() ); } /** * Obtain caption text for a given image * * @param Title $title Image page to take caption from * @return string */ protected function getCaption( $title ) { if( !$this->caption ) { if( $title->exists() ) { $text = Revision::newFromTitle( $title )->getText(); if( preg_match( '!(.*?)!i', $text, $matches ) ) { $this->caption = $matches[1]; } elseif( preg_match( "/Opis\s\=\s(.*)\s\|\s/i", $text, $matches ) ) { $this->caption = $matches[1]; } elseif( preg_match( "!^(.*?)\n!i", $text, $matches ) ) { $this->caption = $matches[1]; } else { if($text) { $this->caption = $text; } else { $this->caption=' '; } } } else { $this->caption = ' '; } } return $this->caption; } /** * Select a random image * * @return Title */ protected function pickImage() { if( count( $this->choices ) > 0 ) { return $this->pickFromChoices(); } else { $pick = $this->pickFromDatabase(); if( !$pick instanceof Title ) $pick = $this->pickFromDatabase(); return $pick; } } /** * Select a random image from the choices given * * @return Title */ protected function pickFromChoices() { $name = count( $this->choices ) > 1 ? $this->choices[ array_rand( $this->choices ) ] : $this->choices[0]; return Title::makeTitleSafe( NS_IMAGE, $name ); } /** * Select a random image from the database * * @return Title */ protected function pickFromDatabase() { wfProfileIn( __METHOD__ ); $dbr = wfGetDB( DB_SLAVE ); list( $table, $conds, $opts ) = $this->getExtraSelectOptions( $dbr ); list( $table2, $conds2 ) = $this->getCategoriesSelectOptions( $dbr ); $res = $dbr->select( array_merge(array_values($table2),array_values($table)), array( 'page_namespace', 'page_title', ), array( 'page_namespace' => NS_IMAGE, 'page_is_redirect' => 0, 'page_random > ' . $dbr->addQuotes( wfRandom() ), ) + $conds + $conds2, __METHOD__, array( 'ORDER BY' => 'page_random', 'LIMIT' => 1, ) + $opts ); wfProfileOut( __METHOD__ ); if( $dbr->numRows( $res ) > 0 ) { $row = $dbr->fetchObject( $res ); $dbr->freeResult( $res ); return Title::makeTitleSafe( $row->page_namespace, $row->page_title ); } return null; } protected function getCategoriesSelectOptions( $dbr ) { $tables=array(); $conds=array(); if($this->categories || $this->nocategories){ $tables=array('categorylinks'); $conds=array(100=>'page_id=cl_from'); } if($this->categories){ $conds+=array( 101=>"cl_to IN ('".implode("','",$this->categories)."')", ); } if($this->nocategories){ $conds+=array( 201=>"page_id NOT IN (SELECT page_id FROM categorylinks, page WHERE page_namespace = '6' AND cl_from=page_id AND (cl_to IN ('".implode("','",$this->nocategories)."')))", ); } return array($tables,$conds); } /** * Get various options for database selection * * @param Database $dbr Database being queried * @return array */ protected function getExtraSelectOptions( $dbr ) { global $wgRandomImageStrict; if( $wgRandomImageStrict ) { list( $image, $page ) = $dbr->tableNamesN( 'image', 'page' ); $ind = $dbr->useIndexClause( 'page_random' ); return array( array( "{$page} {$ind} LEFT JOIN {$image} ON img_name = page_title", ), array( 'img_major_mime' => 'image', ), array(), ); } else { return array( array('page'), array(), array( 'USE INDEX' => 'page_random', ), ); } } /** * Parser hook callback * * @param string $input Tag input * @param array $args Tag attributes * @param Parser $parser Parent parser * @return string */ public static function renderHook( $input, $args, $parser ) { global $wgRandomImageNoCache; if( $wgRandomImageNoCache ) $parser->disableCache(); $random = new RandomImage( $parser, $args, $input ); return $random->render(); } /** * Strip tags out of page text * * @param Parser $parser Calling parser * @param string $text Page text * @return bool */ public static function stripHook( $parser, &$text ) { $text = preg_replace( '!!i', '', $text ); return true; } }