r22233 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r22232‎ | r22233 | r22234 >
Date:20:22, 17 May 2007
Author:magnusmanske
Status:old
Tags:
Comment:
New extension, wanted by commons; might have some display problems in IE
Modified paths:
  • /trunk/extensions/MiniPreview (added) (history)
  • /trunk/extensions/MiniPreview/MiniPreview.css (added) (history)
  • /trunk/extensions/MiniPreview/MiniPreview.i18n.php (added) (history)
  • /trunk/extensions/MiniPreview/MiniPreview.php (added) (history)

Diff [purge]

Index: trunk/extensions/MiniPreview/MiniPreview.i18n.php
@@ -0,0 +1,17 @@
 2+<?php
 3+
 4+$messages['minipreview-files_in_category'] = '$1 total files in this category.';
 5+$messages['minipreview-files_in_gallery'] = '$1 total files in this gallery.';
 6+$messages['minipreview-no_more_files_here'] = 'No more files in this direction.';
 7+$messages['minipreview-ignore_categories'] = '
 8+The following is a list of category names or name beginnings. These categories will
 9+not be displayed by MiniPreview. For example, "* PD-" will not display all categories
 10+that start with "PD-", such as "PD-old".
 11+* GFDL-
 12+* GFDL
 13+* CC-
 14+* PD-
 15+* Self
 16+' ;
 17+
 18+?>
\ No newline at end of file
Index: trunk/extensions/MiniPreview/MiniPreview.php
@@ -0,0 +1,429 @@
 2+<?php
 3+
 4+/**
 5+ * MiniPreview displays, next to an image, small previews
 6+ * of other images in the same categories or displayed on the same pages
 7+ *
 8+ * @addtogroup Extensions
 9+ * @author Magnus Manske
 10+ * @copyright � 2007 Magnus Manske
 11+ * @licence GNU General Public Licence 2.0 or later
 12+ */
 13+
 14+if( !defined( 'MEDIAWIKI' ) ) {
 15+ echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
 16+ die( 1 );
 17+}
 18+
 19+# YOU CAN REDEFINE THESE VARIABLES IN YOUR LocalSettings.php TO ALTER THE BEHAVIOUR OF MiniPreview
 20+
 21+$wgMiniPreviewEnabled = true; # Main switch
 22+$wgMiniPreviewExtPath = '/extensions/MiniPreview';
 23+$wgMiniPreviewShowCategories = true; # Look for images in the same categories
 24+$wgMiniPreviewShowGalleries = true; # Look for images in the same galleries (Galleries = Main namespace pages on commons)
 25+$wgMiniPreviewGalleryNamespace = 0; # Default : Main namespace (for commons)
 26+$wgMiniPreviewThumbnailSize = 75; # Last/next thumbnails will be $wgMiniPreviewThumbnailSize pixel squared
 27+$wgMiniPreviewMaxCategories = 10; # Maximum number of categories shown
 28+$wgMiniPreviewMaxGalleries = 10; # Maximum number of galleries shown
 29+$wgMiniPreviewMaxTotal = 15; # Maximum number of categories and galleries shown
 30+
 31+
 32+
 33+# HERE THE SCARY SOFTWARE BEGINS
 34+
 35+$wgMiniPreviewMessagesInitialized = false;
 36+
 37+/**
 38+ * Register extension setup hook and credits
 39+ */
 40+$wgExtensionFunctions[] = 'efMiniPreview';
 41+$wgExtensionCredits['parserhook'][] = array(
 42+ 'name' => 'MiniPreview',
 43+ 'author' => 'Magnus Manske',
 44+ 'url' => 'http://www.mediawiki.org/wiki/Extension:MiniPreview',
 45+ 'description' => 'MiniPreview displays, next to an image, small previews of other images in the same categories or displayed on the same pages',
 46+);
 47+
 48+#$messages['minipreview_total_files_in_gallery']= '';
 49+
 50+$wgHooks['LoadAllMessages'][] = 'efInjectMiniPreviewMessages';
 51+$wgHooks['ImageOpenShowImageInlineBefore'][] = 'efMiniPreviewShow';
 52+
 53+
 54+function efMiniPreview () {
 55+ # Not sure why this is necessary...
 56+}
 57+
 58+function efMiniPreviewShow ( &$imagePage, &$output ) {
 59+ global $wgMiniPreviewEnabled, $wgMiniPreviewGalleryNamespace;
 60+ global $wgMiniPreviewThumbnailSize, $wgMiniPreviewExtPath, $wgScriptPath;
 61+ global $wgMiniPreviewShowCategories, $wgMiniPreviewShowGalleries;
 62+ global $wgMiniPreviewMaxCategories, $wgMiniPreviewMaxGalleries, $wgMiniPreviewMaxTotal;
 63+
 64+ if ( !$wgMiniPreviewEnabled ) return ;
 65+
 66+ # Register CSS
 67+ $output->addLink(
 68+ array(
 69+ 'rel' => 'stylesheet',
 70+ 'type' => 'text/css',
 71+ 'href' => $wgScriptPath . $wgMiniPreviewExtPath . '/MiniPreview.css'
 72+ ) ) ;
 73+
 74+ $previews = array(); # Will contain objects, each having $entry[0] as BEFORE and $entry[1] as AFTER file
 75+
 76+ # Get categories to ignore
 77+ $ignore_categories = array() ;
 78+ $data = wfMiniPreviewMessage ( 'minipreview-ignore_categories' ) ;
 79+ $data = explode ( "\n" , $data ) ;
 80+ foreach ( $data AS $line ) {
 81+ if ( substr ( $line , 0 , 1 ) != '*' AND substr ( $line , 0 , 1 ) != '#' ) continue ;
 82+ $line = trim ( substr ( $line , 1 ) ) ;
 83+ $ignore_categories[] = $line;
 84+ }
 85+
 86+
 87+ # Run categories
 88+ if ( $wgMiniPreviewShowCategories ) {
 89+ $categories_timestamps = wfMiniPreviewGetImageCategories( $imagePage );
 90+ $cnt = 0;
 91+ foreach ( $categories_timestamps AS $category => $timestamp ) {
 92+ $use_this = true ;
 93+ foreach ( $ignore_categories AS $ic ) {
 94+ if ( substr ( ucfirst($category) , 0 , strlen ( $ic ) ) != $ic ) continue ;
 95+ $use_this = false ;
 96+ break ;
 97+ }
 98+ if ( !$use_this ) continue ; # Ignore this category
 99+ if ( $cnt == $wgMiniPreviewMaxCategories or $cnt == $wgMiniPreviewMaxTotal ) break ; # Too many categories
 100+ $cnt++;
 101+ $previews[] = wfMiniPreviewGetPreviewForCategory ( $category , $timestamp , $imagePage );
 102+ }
 103+ }
 104+
 105+ # Run galleries
 106+ if ( $wgMiniPreviewShowGalleries ) {
 107+ $cnt = 0;
 108+ $galleries = wfMiniPreviewGetImageGalleries( $imagePage );
 109+ foreach ( $galleries AS $id => $title ) {
 110+ if ( $cnt == $wgMiniPreviewMaxGalleries or count ( $previews ) == $wgMiniPreviewMaxTotal ) break; # Too many galleries
 111+ $cnt++;
 112+ $previews[] = wfMiniPreviewGetPreviewForGallery ( $id , $title , $imagePage );
 113+ }
 114+ }
 115+
 116+ # Get image info
 117+ $image_titles = array() ;
 118+ foreach ( $previews AS $p ) {
 119+ if ( $p->entry[0]->id != 0 ) $image_titles[$p->entry[0]->title] = $p->entry[0]->title ;
 120+ if ( $p->entry[1]->id != 0 ) $image_titles[$p->entry[1]->title] = $p->entry[1]->title ;
 121+ }
 122+ $image_data = array();
 123+ wfMiniPreviewGetImageData ( $image_titles , &$image_data ) ;
 124+
 125+ # Output
 126+ $mainwidth = ( $wgMiniPreviewThumbnailSize + 2 ) * 3 ;
 127+ $html .= wfOpenElement( 'div', array (
 128+ 'id' => 'MiniPreview',
 129+ 'class' => 'MiniPreview_main',
 130+ 'style' => "float:right;clear:right" ) ) ;
 131+
 132+ if ( count ( $previews ) == 0 ) { # No results
 133+ $html .= "No categories or galleries!" ;
 134+ } else {
 135+ $last_type = $previews[0]->from_category;
 136+ foreach ( $previews AS $p ) {
 137+ if ( $last_type != $p->from_category ) $html .= "<div class='MiniPreviewSeparator'>&nbsp;</div>" ; # Visually separate categories from galleries
 138+ $last_type = $p->from_category;
 139+ $nsid = $p->from_category ? 14 : $wgMiniPreviewGalleryNamespace ;
 140+ $ns = Namespace::getCanonicalName ( $nsid ) . ':' ;
 141+ $t = Title::newFromDBkey( $ns . $p->source_title );
 142+ $mode = ( $p->from_category ? "category" : "gallery" ) ;
 143+
 144+ $html .= wfOpenElement( 'div', array( 'class' => 'MiniPreview_'.$mode , 'width' => '100%' ) );
 145+ $html .= wfElement( 'a' , array ( 'href' => $t->getLocalURL() ) , $t->getText() );
 146+ $html .= wfOpenElement( 'table' , array( 'border' => '0' , 'cellpadding' => '0' , 'cellspacing' => '0' )); # CSS is just not up to this yet...
 147+ $html .= wfOpenElement( 'tr' );
 148+ $html .= wfMiniPreviewGetThumbnail ( $p->entry[0] , $image_data ) ;
 149+ $html .= wfMiniPreviewGetThumbnail ( $p->entry[1] , $image_data ) ;
 150+
 151+ $html .= wfOpenElement( 'td' );
 152+ $html .= wfOpenElement( 'div', array(
 153+ 'style' => "width:{$wgMiniPreviewThumbnailSize}px;height:{$wgMiniPreviewThumbnailSize}px;",
 154+ 'class' => 'MiniPreview_count' ));
 155+ $html .= wfMiniPreviewMessage ( 'minipreview-files_in_'.$mode , array("<b>".$p->image_count."</b>") );
 156+ $html .= wfCloseElement ( "div" ) ;
 157+ $html .= wfCloseElement ( "td" ) ;
 158+ $html .= wfCloseElement ( "tr" ) ;
 159+ $html .= wfCloseElement ( "table" ) ;
 160+ $html .= wfCloseElement ( "div" ) ;
 161+ }
 162+ }
 163+ $html .= wfCloseElement ( "div" ) ;
 164+ $output->addHTML( $html );
 165+}
 166+
 167+function wfMiniPreviewGetThumbnail ( $entry , &$image_data ) {
 168+ global $wgMiniPreviewThumbnailSize;
 169+
 170+ $divclass = ( $entry->id == 0 ) ? 'MiniPreview_no_thumb' : 'MiniPreview_thumb' ;
 171+
 172+ $ret = '';
 173+ $ret .= wfOpenElement( 'td' );
 174+ $ret .= wfOpenElement( 'div', array(
 175+ 'style' => "width:{$wgMiniPreviewThumbnailSize}px;height:{$wgMiniPreviewThumbnailSize}px;overflow:hidden",
 176+ 'class' => $divclass ));
 177+
 178+ if ( $entry->id != 0 ) { # Existing file
 179+ $w = $image_data[$entry->title]->getWidth();
 180+ $h = $image_data[$entry->title]->getHeight();
 181+
 182+ $style = "position:relative;overflow:hidden;" ;
 183+ $tw = $wgMiniPreviewThumbnailSize ;
 184+ if ( $w > 0 and $h > 0 ) {
 185+ $th = $tw / $w * $h ;
 186+ if ( $th < $wgMiniPreviewThumbnailSize ) {
 187+ $th = $wgMiniPreviewThumbnailSize;
 188+ $tw = $th / $h * $w;
 189+ $o = -( $th - $wgMiniPreviewThumbnailSize ) / 2;
 190+ $style .= "left:" . floor($o) . "px;";
 191+ }
 192+ if ( $th > $wgMiniPreviewThumbnailSize ) {
 193+ $o = -( $th - $wgMiniPreviewThumbnailSize ) / 2 ;
 194+ $style .= "top:" . floor($o) . "px;";
 195+ }
 196+ } else {
 197+ $default_icon_width = 120 ; # HARDCODED?
 198+ $o = -( $default_icon_width - $wgMiniPreviewThumbnailSize ) / 2 ;
 199+ $style .= "top:" . floor($o) . "px;";
 200+ $style .= "left:" . floor($o) . "px;";
 201+ }
 202+
 203+ $thumb_url = $image_data[$entry->title]->createThumb( $tw );
 204+ $image_url = $image_data[$entry->title]->getTitle()->getLocalURL();
 205+ $image_name = $image_data[$entry->title]->getTitle()->getText();
 206+
 207+ $ret .= wfOpenElement ( 'a' , array ( 'href' => $image_url ));
 208+ $ret .= wfElement ( 'img' , array ( 'border' => '0', 'style' => $style, 'src' => $thumb_url , 'alt' => $image_name ));
 209+ $ret .= wfCloseElement ( 'a' );
 210+ } else { # No such file
 211+ $ret .= wfMiniPreviewMessage ( 'minipreview-no_more_files_here' ) ;
 212+ }
 213+ $ret .= wfCloseElement ( "div" ) ;
 214+ $ret .= wfCloseElement ( "td" ) ;
 215+ return $ret;
 216+}
 217+
 218+# Finds all categories the $imagePage belongs to
 219+function wfMiniPreviewGetImageCategories ( &$imagePage ) {
 220+ $dbr = wfGetDB( DB_SLAVE );
 221+ $res = $dbr->select(
 222+ array( 'page', 'categorylinks' ),
 223+ array( 'cl_to', 'cl_timestamp' ),
 224+ array(
 225+ 'cl_from = page_id',
 226+ 'page_namespace' => '6', # Image page only
 227+ 'page_title' => $imagePage->mTitle->getDBKey() )
 228+ );
 229+
 230+ $ret = array();
 231+ while( $x = $dbr->fetchObject ( $res ) ) {
 232+ $ret[$x->cl_to] = $x->cl_timestamp;
 233+ }
 234+ $dbr->freeResult( $res );
 235+ return $ret;
 236+}
 237+
 238+# Finds all gallery pages the $imagePage belongs to
 239+function wfMiniPreviewGetImageGalleries ( &$imagePage ) {
 240+ global $wgMiniPreviewGalleryNamespace;
 241+ $dbr = wfGetDB( DB_SLAVE );
 242+ $res = $dbr->select(
 243+ array( 'page', 'imagelinks' ),
 244+ array( 'il_from' , 'page_title' ),
 245+ array(
 246+ 'page_id = il_from',
 247+ 'page_namespace' => $wgMiniPreviewGalleryNamespace,
 248+ 'il_to' => $imagePage->mTitle->getDBKey() )
 249+ );
 250+
 251+ $ret = array();
 252+ while( $x = $dbr->fetchObject ( $res ) ) {
 253+ $ret[$x->il_from] = $x->page_title ;
 254+ }
 255+ $dbr->freeResult( $res );
 256+ return $ret;
 257+}
 258+
 259+# For a $category and a $timestamp, find the images immediately before and after that timestamp
 260+# in that category, according to timestamp
 261+function wfMiniPreviewGetPreviewForCategory ( $category , $timestamp , $imagePage ) {
 262+ $ret = '';
 263+ $ret->from_category = true;
 264+ $ret->from_gallery = false;
 265+ $ret->source_title = $category;
 266+ $ret->entry = array();
 267+
 268+ $dbr = wfGetDB( DB_SLAVE );
 269+
 270+ # Get the image BEFORE the current one (according to timestamp)
 271+ $res = $dbr->select(
 272+ array( 'page', 'categorylinks' ),
 273+ array( 'page_title', 'page_id'),
 274+ array(
 275+ 'cl_to' => $category , # Same category as current one
 276+ 'cl_timestamp < ' . $dbr->addQuotes( $timestamp ), # Timestamp is before the current one
 277+ 'page_id = cl_from',
 278+ 'page_namespace=6' # Images only
 279+ ),
 280+ __METHOD__, #????
 281+ array( 'ORDER BY' => 'cl_timestamp DESC' )
 282+ );
 283+ if( $x = $dbr->fetchObject ( $res ) ) {
 284+ $ret->entry[0]->title = $x->page_title ;
 285+ $ret->entry[0]->id = $x->page_id ;
 286+ } else $ret->entry[0]->id = 0 ;
 287+ $dbr->freeResult( $res );
 288+
 289+ # Get the image AFTER the current one (according to timestamp)
 290+ $res = $dbr->select(
 291+ array( 'page', 'categorylinks' ),
 292+ array( 'page_id', 'page_title' ),
 293+ array(
 294+ 'cl_to' => $category , # Same category as current one
 295+ 'cl_timestamp > ' . $dbr->addQuotes( $timestamp ), # Timestamp is after the current one
 296+ 'page_id = cl_from',
 297+ 'page_namespace=6' # Images only
 298+ ),
 299+ __METHOD__, #????
 300+ array( 'ORDER BY' => 'cl_timestamp')
 301+ );
 302+ if( $x = $dbr->fetchObject ( $res ) ) {
 303+ $ret->entry[1]->title = $x->page_title ;
 304+ $ret->entry[1]->id = $x->page_id ;
 305+ } else $ret->entry[1]->id = 0 ;
 306+ $dbr->freeResult( $res );
 307+
 308+ # Get total image count for this category
 309+ $res = $dbr->select(
 310+ array( 'page', 'categorylinks' ),
 311+ array( 'count(*) AS cnt' ),
 312+ array(
 313+ 'cl_to' => $category,
 314+ 'page_id = cl_from',
 315+ 'page_namespace=6' # Images only
 316+ )
 317+ );
 318+ if( $x = $dbr->fetchObject ( $res ) ) $ret->image_count = $x->cnt;
 319+ else $ret->image_count = 0;
 320+
 321+ return $ret;
 322+}
 323+
 324+# For a $id and a $title, find the images immediately before and after that title
 325+# on that gallery page, according to image title
 326+function wfMiniPreviewGetPreviewForGallery ( $id , $title , $imagePage ) {
 327+ $ret = '';
 328+ $ret->from_category = false;
 329+ $ret->from_gallery = true;
 330+ $ret->source_title = $title;
 331+ $ret->entry = array();
 332+
 333+ $dbr = wfGetDB( DB_SLAVE );
 334+
 335+ # Get the image BEFORE the current one (according to title)
 336+ $res = $dbr->select(
 337+ array( 'page', 'imagelinks' ),
 338+ array( 'il_from', 'page_id', 'max(il_to) AS maxto' ),
 339+ array(
 340+ 'il_from' => $id, # Same page
 341+ 'il_to < ' . $dbr->addQuotes( $imagePage->getTitle()->getDBKey() ), # Title comes before the current one
 342+ 'page_title = il_to',
 343+ 'page_namespace=6' # Images only
 344+ ),
 345+ __METHOD__, #????
 346+ array( 'GROUP BY' => 'il_from')
 347+ );
 348+ if( $x = $dbr->fetchObject ( $res ) ) {
 349+ $ret->entry[0]->title .= $x->maxto ;
 350+ $ret->entry[0]->id .= $x->page_id ;
 351+ } else $ret->entry[0]->id = 0 ;
 352+ $dbr->freeResult( $res );
 353+
 354+ # Get the image AFTER the current one (according to title)
 355+ $res = $dbr->select(
 356+ array( 'page', 'imagelinks' ),
 357+ array( 'il_from', 'page_id', 'min(il_to) AS minto' ),
 358+ array(
 359+ 'il_from' => $id, # Same page
 360+ 'il_to > ' . $dbr->addQuotes( $imagePage->mTitle->getDBKey() ), # Title comes after the current one
 361+ 'page_title = il_to',
 362+ 'page_namespace=6' # Images only
 363+ ),
 364+ __METHOD__, #????
 365+ array( 'GROUP BY' => 'il_from')
 366+ );
 367+ if( $x = $dbr->fetchObject ( $res ) ) {
 368+ $ret->entry[1]->title .= $x->minto ;
 369+ $ret->entry[1]->id .= $x->page_id ;
 370+ } else $ret->entry[1]->id = 0 ;
 371+ $dbr->freeResult( $res );
 372+
 373+ # Get total image count for this category
 374+ $res = $dbr->select(
 375+ array( 'imagelinks' ),
 376+ array( 'count(*) AS cnt' ),
 377+ array( 'il_from' => $id )
 378+ );
 379+ if( $x = $dbr->fetchObject ( $res ) ) $ret->image_count = $x->cnt;
 380+ else $ret->image_count = 0;
 381+
 382+ return $ret ;
 383+}
 384+
 385+# For given ids (page_id!) of images, find all image (and page) data
 386+function wfMiniPreviewGetImageData ( $image_titles , &$image_data ) {
 387+ $image_data = array(); # Paranoia
 388+ foreach ( $image_titles AS $i ) {
 389+ $image_data[$i] = Image::newFromName( $i );
 390+ $image_data[$i]->load();
 391+ }
 392+}
 393+
 394+/**
 395+* inject messages used by MiniPreview into the message cache
 396+*/
 397+function efInjectMiniPreviewMessages() {
 398+ wfMiniPreviewMessage(false);
 399+ return true;
 400+}
 401+
 402+function wfMiniPreviewLoadMessages() {
 403+ global $wgLang;
 404+ $messages = array();
 405+
 406+ $f= dirname( __FILE__ ) . '/MiniPreview.i18n.php';
 407+ include( $f );
 408+
 409+ $f= dirname( __FILE__ ) . '/MiniPreview.i18n.' . $wgLang->getCode() . '.php';
 410+ if ( file_exists( $f ) ) include( $f );
 411+
 412+ return $messages;
 413+}
 414+
 415+function wfMiniPreviewMessage( $msg , $args = array() ) {
 416+ global $wgMiniPreviewMessagesInitialized;
 417+ $wgMiniPreviewMessagesInitialized = false;
 418+ global $wgMessageCache;
 419+ if ( !$initialized ) {
 420+ $wgMessageCache->addMessages( wfMiniPreviewLoadMessages() );
 421+ $initialized = true;
 422+ }
 423+ if ( $msg === false ) {
 424+ return null;
 425+ }
 426+ return wfMsgReal( $msg, $args );
 427+}
 428+
 429+
 430+?>
\ No newline at end of file
Index: trunk/extensions/MiniPreview/MiniPreview.css
@@ -0,0 +1,66 @@
 2+/*
 3+ * Stylesheet for the MiniPreview extension
 4+ *
 5+ * @package MediaWiki
 6+ * @subpackage Extensions
 7+ * @author Magnus Manske
 8+ * @copyright � 2007 Magnus Manske
 9+ * @licence GNU General Public Licence 2.0 or later
 10+*/
 11+
 12+
 13+/* The main box */
 14+.MiniPreview_main {
 15+ border:2px solid #888888;
 16+ background-color:#DDDDDD;
 17+ padding:2px;
 18+}
 19+
 20+/* One "line", category */
 21+.MiniPreview_category {
 22+ background-color:#DDDDFF;
 23+ padding:1px;
 24+}
 25+
 26+/* One "line", gallery */
 27+.MiniPreview_gallery {
 28+ background-color:#DDFFDD;
 29+ padding:1px;
 30+}
 31+
 32+/* Style for an existing thumbnail/icon */
 33+.MiniPreview_thumb {
 34+ color:#888888;
 35+ text-align:center;
 36+ background-color:#CCCCCC;
 37+ margin:1px;
 38+}
 39+
 40+/* Style for the text box if no file exists in that direction */
 41+.MiniPreview_no_thumb {
 42+ color:#888888;
 43+ text-align:center;
 44+ font-size:80%;
 45+ background-color:#EEEEEE;
 46+ margin:1px;
 47+ vertial-align:middle;
 48+}
 49+
 50+
 51+/* The third "count thumbnail" */
 52+.MiniPreview_count {
 53+ color:#666666;
 54+ text-align:center;
 55+ font-size:80%;
 56+ background-color:#CCCCCC;
 57+ margin:1px;
 58+ vertial-align:middle;
 59+}
 60+
 61+.MiniPreviewSeparator {
 62+ height:2px;
 63+ background-color:#888888;
 64+ width:100%;
 65+ margin-top:2px;
 66+ margin-bottom:2px;
 67+}