Index: branches/mikeb/phase3/docs/hooks.txt |
— | — | @@ -214,9 +214,11 @@ |
215 | 215 | # ... |
216 | 216 | function protect() { |
217 | 217 | global $wgUser; |
218 | | - if (wfRunHooks('ArticleProtect', array(&$this, &$wgUser))) { |
| 218 | + if (wfRunHooks('ArticleProtect', array(&$this, |
| 219 | + &$wgUser))) { |
219 | 220 | # protect the article |
220 | | - wfRunHooks('ArticleProtectComplete', array(&$this, &$wgUser)); |
| 221 | + wfRunHooks('ArticleProtectComplete', |
| 222 | + array(&$this, &$wgUser)); |
221 | 223 | } |
222 | 224 | } |
223 | 225 | } |
— | — | @@ -304,7 +306,8 @@ |
305 | 307 | $create: Whether or not the restoration caused the page to be created |
306 | 308 | (i.e. it didn't exist before) |
307 | 309 | |
308 | | -'ArticleViewHeader': Before the parser cache is about to be tried for article viewing. |
| 310 | +'ArticleViewHeader': Before the parser cache is about to be tried for article |
| 311 | +viewing. |
309 | 312 | &$pcache: whether to try the parser cache or not |
310 | 313 | &$outputDone: whether the output for this page finished or not |
311 | 314 | |
— | — | @@ -372,7 +375,8 @@ |
373 | 376 | saved, that is before insertNewArticle() is called |
374 | 377 | &$editpage_Obj: the current EditPage object |
375 | 378 | |
376 | | -'EditFormPreloadText': Allows population of the edit form when creating new pages |
| 379 | +'EditFormPreloadText': Allows population of the edit form when creating new |
| 380 | +pages |
377 | 381 | &$text: Text to preload with |
378 | 382 | &$title: Title object representing the page being created |
379 | 383 | |
— | — | @@ -387,9 +391,12 @@ |
388 | 392 | $section: Section being edited |
389 | 393 | &$error: Error message to return |
390 | 394 | |
391 | | -Return false to halt editing; you'll need to handle error messages, etc. yourself. |
392 | | -Alternatively, modifying $error and returning true will cause the contents of $error |
393 | | -to be echoed at the top of the edit form as wikitext. Return true without altering |
| 395 | +Return false to halt editing; you'll need to handle error messages, etc. |
| 396 | +yourself. |
| 397 | +Alternatively, modifying $error and returning true will cause the contents of |
| 398 | +$error |
| 399 | +to be echoed at the top of the edit form as wikitext. Return true without |
| 400 | +altering |
394 | 401 | $error to allow the edit to proceed. |
395 | 402 | |
396 | 403 | 'EditSectionLink': Override the return value of Linker::editSectionLink() |
— | — | @@ -399,7 +406,8 @@ |
400 | 407 | $link: Default link |
401 | 408 | $result: Result (alter this to override the generated links) |
402 | 409 | |
403 | | -'EditSectionLinkForOther': Override the return value of Linker::editSectionLinkForOther() |
| 410 | +'EditSectionLinkForOther': Override the return value of |
| 411 | +Linker::editSectionLinkForOther() |
404 | 412 | $skin: Skin rendering the UI |
405 | 413 | $title: Title being linked to |
406 | 414 | $section: Section to link to |
— | — | @@ -425,10 +433,12 @@ |
426 | 434 | $subject: subject of the mail |
427 | 435 | $text: text of the mail |
428 | 436 | |
429 | | -'FetchChangesList': When fetching the ChangesList derivative for a particular user |
| 437 | +'FetchChangesList': When fetching the ChangesList derivative for a particular |
| 438 | +user |
430 | 439 | &$user: User the list is being fetched for |
431 | 440 | &$skin: Skin object to be used with the list |
432 | | -&$list: List object (defaults to NULL, change it to an object instance and return |
| 441 | +&$list: List object (defaults to NULL, change it to an object instance and |
| 442 | +return |
433 | 443 | false override the list derivative used) |
434 | 444 | |
435 | 445 | 'FileUpload': When a file upload occurs |
— | — | @@ -449,21 +459,26 @@ |
450 | 460 | $url: string value as output (out parameter, can modify) |
451 | 461 | $query: query options passed to Title::getFullURL() |
452 | 462 | |
453 | | -'ImageOpenShowImageInlineBefore': Call potential extension just before showing the image on an image page |
| 463 | +'ImageOpenShowImageInlineBefore': Call potential extension just before showing |
| 464 | +the image on an image page |
454 | 465 | $imagePage: ImagePage object ($this) |
455 | 466 | $output: $wgOut |
456 | 467 | |
457 | | -'InternalParseBeforeLinks': during Parser's internalParse method before links but |
| 468 | +'InternalParseBeforeLinks': during Parser's internalParse method before links |
| 469 | +but |
458 | 470 | after noinclude/includeonly/onlyinclude and other processing. |
459 | 471 | &$this: Parser object |
460 | 472 | &$text: string containing partially parsed text |
461 | 473 | &$this->mStripState: Parser's internal StripState object |
462 | 474 | |
463 | | -'LoginAuthenticateAudit': a login attempt for a valid user account either succeeded or failed. |
464 | | - No return data is accepted; this hook is for auditing only. |
| 475 | +'LoginAuthenticateAudit': a login attempt for a valid user account either |
| 476 | +succeeded or failed. |
| 477 | + No return data is accepted; this hook is for auditing |
| 478 | + only. |
465 | 479 | $user: the User object being authenticated against |
466 | 480 | $password: the password being submitted and found wanting |
467 | | -$retval: a LoginForm class constant with authenticateUserData() return value (SUCCESS, WRONG_PASS, etc) |
| 481 | +$retval: a LoginForm class constant with authenticateUserData() return value |
| 482 | +(SUCCESS, WRONG_PASS, etc) |
468 | 483 | |
469 | 484 | 'LogPageValidTypes': action being logged. DEPRECATED: Use $wgLogTypes |
470 | 485 | &$type: array of strings |
— | — | @@ -471,10 +486,12 @@ |
472 | 487 | 'LogPageLogName': name of the logging page(s). DEPRECATED: Use $wgLogNames |
473 | 488 | &$typeText: array of strings |
474 | 489 | |
475 | | -'LogPageLogHeader': strings used by wfMsg as a header. DEPRECATED: Use $wgLogHeaders |
| 490 | +'LogPageLogHeader': strings used by wfMsg as a header. DEPRECATED: Use |
| 491 | +$wgLogHeaders |
476 | 492 | &$headerText: array of strings |
477 | 493 | |
478 | | -'LogPageActionText': strings used by wfMsg as a header. DEPRECATED: Use $wgLogActions |
| 494 | +'LogPageActionText': strings used by wfMsg as a header. DEPRECATED: Use |
| 495 | +$wgLogActions |
479 | 496 | &$actionText: array of strings |
480 | 497 | |
481 | 498 | 'MarkPatrolled': before an edit is marked patrolled |
— | — | @@ -494,6 +511,10 @@ |
495 | 512 | $errmsg: error message, in HTML (string). Nonempty indicates failure |
496 | 513 | of rendering the formula |
497 | 514 | |
| 515 | +'MimeMagicRegisterPlugins': Tell the MIME detection module to consider a |
| 516 | +plugin for more accurate identification of particular MIME type(s). |
| 517 | +This hook should do nothing more than instantiate MimePlugin(s). |
| 518 | + |
498 | 519 | 'OutputPageBeforeHTML': a page has been processed by the parser and |
499 | 520 | the resulting HTML is about to be displayed. |
500 | 521 | $parserOutput: the parserOutput (object) that corresponds to the page |
— | — | @@ -529,7 +550,8 @@ |
530 | 551 | Change $result and return false to give a definitive answer, otherwise |
531 | 552 | the built-in rate limiting checks are used, if enabled. |
532 | 553 | |
533 | | -'PreferencesUserInformationPanel': Add HTML bits to user information list in preferences form |
| 554 | +'PreferencesUserInformationPanel': Add HTML bits to user information list in |
| 555 | +preferences form |
534 | 556 | $form : PreferencesForm object |
535 | 557 | &$html : HTML to append to |
536 | 558 | |
— | — | @@ -552,7 +574,8 @@ |
553 | 575 | &$siteNotice: HTML sitenotice |
554 | 576 | Alter the contents of $siteNotice to add to/alter the sitenotice/anonnotice. |
555 | 577 | |
556 | | -'SkinTemplateOutputPageBeforeExec': Before SkinTemplate::outputPage() starts page output |
| 578 | +'SkinTemplateOutputPageBeforeExec': Before SkinTemplate::outputPage() starts |
| 579 | +page output |
557 | 580 | &$sktemplate: SkinTemplate object |
558 | 581 | &$tpl: Template engine object |
559 | 582 | |
— | — | @@ -643,7 +666,9 @@ |
644 | 667 | 'SkinTemplateContentActions': after building the $content_action array right |
645 | 668 | before returning it, see Content_action.php in |
646 | 669 | the extensions/examples/ directory |
647 | | - ( http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/examples/ ) |
| 670 | + ( |
| 671 | + http://svn.wikimedia.org/viewvc/mediawiki/trunk/ex |
| 672 | + tensions/examples/ ) |
648 | 673 | for a demonstration of how to use this hook. |
649 | 674 | $content_actions: The array of content actions |
650 | 675 | |
Index: branches/mikeb/phase3/includes/MimeMagic.php |
— | — | @@ -75,7 +75,8 @@ |
76 | 76 | * Implements functions related to mime types such as detection and mapping to |
77 | 77 | * file extension. |
78 | 78 | * |
79 | | - * Instances of this class are stateles, there only needs to be one global instance |
| 79 | + * Instances of this class are stateles, there only needs to be one global |
| 80 | + instance |
80 | 81 | * of MimeMagic. Please use MimeMagic::singleton() to get that instance. |
81 | 82 | */ |
82 | 83 | class MimeMagic { |
— | — | @@ -98,21 +99,62 @@ |
99 | 100 | */ |
100 | 101 | var $mExtToMime= NULL; |
101 | 102 | |
| 103 | + /** |
| 104 | + * Multidimensional array indexed by MIME type, then subtype, |
| 105 | + * containing among other things references to any appropriate MimePlugins. |
| 106 | + */ |
| 107 | + private $pluginsByType; |
| 108 | + |
| 109 | + /** |
| 110 | + * Multidimensional array indexed by file extension, containing |
| 111 | + * references to any appropriate MimePlugins. |
| 112 | + */ |
| 113 | + private $pluginsByExt; |
| 114 | + |
| 115 | + /** |
| 116 | + * flat list of extensions with reliable detection. Generation is |
| 117 | + * quite expensive, so this list is computed once and cached here. |
| 118 | + */ |
| 119 | + private $recognizableExtensions; |
| 120 | + |
| 121 | + /** |
| 122 | + * Mime Type strings from plugins (same format as MM_WELL_KNOWN_MIME_TYPES) |
| 123 | + * are aggregated here |
| 124 | + */ |
| 125 | + private $pluginMimeTypes; |
| 126 | + |
| 127 | + /** |
| 128 | + * Mime Info strings from plugins (same format as MM_WELL_KNOWN_MIME_INFO) |
| 129 | + * are aggregated here |
| 130 | + */ |
| 131 | + private $pluginMimeInfo; |
| 132 | + |
102 | 133 | /** The singleton instance |
103 | 134 | */ |
104 | 135 | private static $instance; |
105 | 136 | |
106 | | - /** Initializes the MimeMagic object. This is called by MimeMagic::singleton(). |
| 137 | + /** Initializes the MimeMagic object. Declared private; use |
| 138 | + * MimeMagic::singleton() to obtain the instance in client code. |
107 | 139 | * |
108 | | - * This constructor parses the mime.types and mime.info files and build internal mappings. |
| 140 | + * This constructor parses the mime.types and mime.info files and build |
| 141 | + * internal mappings. |
| 142 | + * |
109 | 143 | */ |
110 | | - function __construct() { |
| 144 | + private function __construct() { |
| 145 | + global $wgMimeTypeFile, $IP, $wgHooks; |
| 146 | + |
| 147 | + # Add hooks to register plugins included in the main distribution |
| 148 | + $wgHooks['MimeMagicRegisterPlugins'][] = 'AVMimePlugin'; |
| 149 | + |
| 150 | + $this->pluginMimeTypes = $this->pluginMimeInfo = ''; |
| 151 | + $this->pluginsByType = array(); |
| 152 | + //Ask for special detection plugins. Return value doesn't matter. |
| 153 | + wfRunHooks('MimeMagicRegisterPlugins', array($this)); |
| 154 | + |
111 | 155 | /* |
112 | 156 | * --- load mime.types --- |
113 | 157 | */ |
114 | 158 | |
115 | | - global $wgMimeTypeFile, $IP; |
116 | | - |
117 | 159 | $types = MM_WELL_KNOWN_MIME_TYPES; |
118 | 160 | |
119 | 161 | if ( $wgMimeTypeFile == 'includes/mime.types' ) { |
— | — | @@ -120,18 +162,24 @@ |
121 | 163 | } |
122 | 164 | |
123 | 165 | if ( $wgMimeTypeFile ) { |
124 | | - if ( is_file( $wgMimeTypeFile ) and is_readable( $wgMimeTypeFile ) ) { |
125 | | - wfDebug( __METHOD__.": loading mime types from $wgMimeTypeFile\n" ); |
| 166 | + if ( is_file( $wgMimeTypeFile ) and is_readable( $wgMimeTypeFile ) ) |
| 167 | + { |
| 168 | + wfDebug( __METHOD__.": loading mime types from |
| 169 | + $wgMimeTypeFile\n" ); |
126 | 170 | $types .= "\n"; |
127 | 171 | $types .= file_get_contents( $wgMimeTypeFile ); |
128 | 172 | } else { |
129 | | - wfDebug( __METHOD__.": can't load mime types from $wgMimeTypeFile\n" ); |
| 173 | + wfDebug( __METHOD__.": can't load mime types from $wgMimeTypeFile. Proceeding with buit-in & plugin types only.\n" ); |
130 | 174 | } |
131 | 175 | } else { |
132 | | - wfDebug( __METHOD__.": no mime types file defined, using build-ins only.\n" ); |
| 176 | + wfDebug( __METHOD__.": no mime types file defined, using built-in & plugin types only.\n" ); |
133 | 177 | } |
134 | 178 | |
135 | | - $types = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), "\n", $types ); |
| 179 | + $types .= $this->pluginMimeTypes; //will start with newline or be empty |
| 180 | + unset($this->pluginMimeTypes); //no longer needed |
| 181 | + |
| 182 | + $types = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), |
| 183 | + "\n", $types ); |
136 | 184 | $types = str_replace( "\t", " ", $types ); |
137 | 185 | |
138 | 186 | $this->mMimeToExt = array(); |
— | — | @@ -178,16 +226,16 @@ |
179 | 227 | /* |
180 | 228 | * --- load mime.info --- |
181 | 229 | */ |
| 230 | + $info = MM_WELL_KNOWN_MIME_INFO; |
182 | 231 | |
183 | 232 | global $wgMimeInfoFile; |
184 | 233 | if ( $wgMimeInfoFile == 'includes/mime.info' ) { |
185 | 234 | $wgMimeInfoFile = "$IP/$wgMimeInfoFile"; |
186 | 235 | } |
187 | 236 | |
188 | | - $info = MM_WELL_KNOWN_MIME_INFO; |
189 | | - |
190 | 237 | if ( $wgMimeInfoFile ) { |
191 | | - if ( is_file( $wgMimeInfoFile ) and is_readable( $wgMimeInfoFile ) ) { |
| 238 | + if ( is_file( $wgMimeInfoFile ) and is_readable( $wgMimeInfoFile ) ) |
| 239 | + { |
192 | 240 | wfDebug( __METHOD__.": loading mime info from $wgMimeInfoFile\n" ); |
193 | 241 | $info .= "\n"; |
194 | 242 | $info .= file_get_contents( $wgMimeInfoFile ); |
— | — | @@ -195,10 +243,14 @@ |
196 | 244 | wfDebug(__METHOD__.": can't load mime info from $wgMimeInfoFile\n"); |
197 | 245 | } |
198 | 246 | } else { |
199 | | - wfDebug(__METHOD__.": no mime info file defined, using build-ins only.\n"); |
| 247 | + wfDebug(__METHOD__.": no mime info file defined, using built-in & plugin types only.\n"); |
200 | 248 | } |
201 | 249 | |
202 | | - $info = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), "\n", $info); |
| 250 | + $info .= $this->pluginMimeInfo; //appended here so plugins get final say |
| 251 | + unset($this->pluginMimeInfo); |
| 252 | + |
| 253 | + $info = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), |
| 254 | + "\n", $info); |
203 | 255 | $info = str_replace( "\t", " ", $info ); |
204 | 256 | |
205 | 257 | $this->mMimeTypeAliases = array(); |
— | — | @@ -218,8 +270,9 @@ |
219 | 271 | #print "processing MIME INFO line $s<br>"; |
220 | 272 | |
221 | 273 | $match = array(); |
222 | | - if ( preg_match( '!\[\s*(\w+)\s*\]!', $s, $match ) ) { |
223 | | - $s = preg_replace( '!\[\s*(\w+)\s*\]!', '', $s ); |
| 274 | + $exp = '!\s*\[\s*(\w+)\s*\]!'; |
| 275 | + if ( preg_match($exp, $s, $match ) ) { |
| 276 | + $s = preg_replace($exp, '', $s ); |
224 | 277 | $mtype = trim( strtoupper( $match[1] ) ); |
225 | 278 | } else { |
226 | 279 | $mtype = MEDIATYPE_UNKNOWN; |
— | — | @@ -247,6 +300,31 @@ |
248 | 301 | } |
249 | 302 | } |
250 | 303 | |
| 304 | + /* Build index of plugins by extension. Plugins can influence the |
| 305 | + * extensions they are indexed under by implementing |
| 306 | + * MimePlugin->mimeTypes() |
| 307 | + */ |
| 308 | + $this->pluginsByExt = array(); |
| 309 | + foreach($this->pluginsByType AS $type => $subtypes) |
| 310 | + { |
| 311 | + foreach($subtypes AS $subtype => $detectors) |
| 312 | + { |
| 313 | + foreach($detectors AS $detector) |
| 314 | + { |
| 315 | + if($detector[1]) //if authoritative |
| 316 | + { |
| 317 | + $exts = $this->getExtensionsForType("$type/$subtype"); |
| 318 | + |
| 319 | + foreach(explode(' ', $exts) AS $ext) |
| 320 | + { |
| 321 | + $ext = trim($ext); if(empty($ext)) continue; |
| 322 | + $this->pluginsByExt[$ext][] = $detector; |
| 323 | + } |
| 324 | + } |
| 325 | + } |
| 326 | + } |
| 327 | + } |
| 328 | + |
251 | 329 | } |
252 | 330 | |
253 | 331 | /** |
— | — | @@ -256,23 +334,89 @@ |
257 | 335 | if ( !isset( self::$instance ) ) { |
258 | 336 | self::$instance = new MimeMagic; |
259 | 337 | } |
| 338 | + |
260 | 339 | return self::$instance; |
261 | 340 | } |
262 | 341 | |
| 342 | + /** |
| 343 | + * Populates private plugin arrays by MIME type (and subtype). This method should only be |
| 344 | + * called by MimePlugin::registerContentType |
| 345 | + */ |
| 346 | + public function registerPluginByContentType(MimePlugin $detector, $type, |
| 347 | + $subtype, $authoritative) |
| 348 | + { |
| 349 | + $this->pluginsByType[$type][$subtype][] = array($detector, |
| 350 | + $authoritative); |
| 351 | + } |
| 352 | + |
| 353 | + /** |
| 354 | + * Intended to be called by plugins during their construction/registration |
| 355 | + * process. By allowing plugins to append MIME type information to the |
| 356 | + * built-in well-known types, complete support for a new MIME type can be |
| 357 | + * assured by simply installing a plugin for it, without admins having to worry |
| 358 | + * about the contents of their mime.types or mime.info files as well. |
| 359 | + * |
| 360 | + * @todo Move loading code in constructor to a helper method, make it more |
| 361 | + * verbose about formatting errors, and use it to validate typesStrings on a |
| 362 | + * plugin by plugin basis. |
| 363 | + * |
| 364 | + * @param string typesString The mime types and extensions supported by this |
| 365 | + * plugin, in the format of a mime.types file. (See MM_WELL_KNOWN_MIME_TYPES |
| 366 | + * for an example.) |
| 367 | + */ |
| 368 | + public function addMimeTypes(&$typesString) |
| 369 | + { |
| 370 | + $this->pluginMimeTypes .= "\n" . $typesString; |
| 371 | + } |
| 372 | + |
| 373 | + /** |
| 374 | + * Intended to be called by plugins. |
| 375 | + * |
| 376 | + * @param string infoString Mime types supported by this plugin and their |
| 377 | + * associated media types, in the format of a mime.info file. (See |
| 378 | + * MM_WELL_KNOWN_MIME_INFO for an example.) |
| 379 | + */ |
| 380 | + public function addMimeInfo(&$infoString) |
| 381 | + { |
| 382 | + $this->pluginMimeInfo .= "\n" . $infoString; |
| 383 | + } |
| 384 | + |
263 | 385 | /** returns a list of file extensions for a given mime type |
264 | 386 | * as a space separated string. |
265 | 387 | */ |
266 | 388 | function getExtensionsForType( $mime ) { |
267 | | - $mime = strtolower( $mime ); |
| 389 | + if(func_num_args() == 1) |
| 390 | + { |
| 391 | + $mime = strtolower( func_get_arg(0) ); |
| 392 | + $parts = MimeMagic::splitMimeString($mime); |
| 393 | + } else { |
| 394 | + $mime = strtolower(func_get_arg(0) . "/" . func_get_arg(1)); |
| 395 | + $parts[0] = strtolower(func_get_arg(0)); |
| 396 | + $parts[1] = strtolower(func_get_arg(1)); |
| 397 | + } |
268 | 398 | |
269 | | - $r = @$this->mMimeToExt[$mime]; |
| 399 | + if($parts[1] === '*') |
| 400 | + { |
| 401 | + //lookup all main types matching parts[0] |
| 402 | + $r = ''; |
| 403 | + foreach($this->mMimeToExt AS $mimecheck => $exts) |
| 404 | + { |
| 405 | + $p = MimeMagic::splitMimeString($mimecheck); |
| 406 | + if(strcmp($p[0], $parts[0]) === 0) |
| 407 | + { |
| 408 | + $r .= ' ' . $exts; |
| 409 | + } |
| 410 | + } |
| 411 | + } else { |
| 412 | + $r = @$this->mMimeToExt[$mime]; |
270 | 413 | |
271 | | - if ( @!$r and isset( $this->mMimeTypeAliases[$mime] ) ) { |
272 | | - $mime = $this->mMimeTypeAliases[$mime]; |
273 | | - $r = @$this->mMimeToExt[$mime]; |
| 414 | + if ( @!$r and isset( $this->mMimeTypeAliases[$mime] ) ) { |
| 415 | + $mime = $this->mMimeTypeAliases[$mime]; |
| 416 | + $r = @$this->mMimeToExt[$mime]; |
| 417 | + } |
274 | 418 | } |
275 | 419 | |
276 | | - return $r; |
| 420 | + return trim($r); |
277 | 421 | } |
278 | 422 | |
279 | 423 | /** returns a list of mime types for a given file extension |
— | — | @@ -286,7 +430,8 @@ |
287 | 431 | } |
288 | 432 | |
289 | 433 | /** returns a single mime type for a given file extension. |
290 | | - * This is always the first type from the list returned by getTypesForExtension($ext). |
| 434 | + * This is always the first type from the list returned by |
| 435 | + getTypesForExtension($ext). |
291 | 436 | */ |
292 | 437 | function guessTypesForExtension( $ext ) { |
293 | 438 | $m = $this->getTypesForExtension( $ext ); |
— | — | @@ -345,27 +490,49 @@ |
346 | 491 | * invalid uploads; if we can't identify the type we won't |
347 | 492 | * be able to say if it's invalid. |
348 | 493 | * |
349 | | - * @todo Be more accurate when using fancy mime detector plugins; |
350 | | - * right now this is the bare minimum getimagesize() list. |
351 | 494 | * @return bool |
352 | 495 | */ |
353 | 496 | function isRecognizableExtension( $extension ) { |
354 | | - static $types = array( |
355 | | - 'gif', 'jpeg', 'jpg', 'png', 'swf', 'psd', |
356 | | - 'bmp', 'tiff', 'tif', 'jpc', 'jp2', |
357 | | - 'jpx', 'jb2', 'swc', 'iff', 'wbmp', |
358 | | - 'xbm', 'djvu' |
359 | | - ); |
360 | | - return in_array( strtolower( $extension ), $types ); |
| 497 | + if(! isset($this->recognizableExtensions)) |
| 498 | + { |
| 499 | + $pluginTypes = array(); |
| 500 | + //go over plugins by extension and add authoritative ones |
| 501 | + foreach($this->pluginsByExt AS $ext => $detectors) |
| 502 | + { |
| 503 | + foreach($detectors AS $detector) |
| 504 | + { |
| 505 | + if($detector[1]) |
| 506 | + { |
| 507 | + $pluginTypes[] = $ext; |
| 508 | + break; |
| 509 | + } |
| 510 | + } |
| 511 | + } |
| 512 | + |
| 513 | + $this->recognizableExtensions = array_merge(array( |
| 514 | + 'gif', 'jpeg', 'jpg', 'png', 'swf', 'psd', |
| 515 | + 'bmp', 'tiff', 'tif', 'jpc', 'jp2', |
| 516 | + 'jpx', 'jb2', 'swc', 'iff', 'wbmp', |
| 517 | + 'xbm', 'djvu' |
| 518 | + ), |
| 519 | + $pluginTypes |
| 520 | + ); |
| 521 | + } |
| 522 | + |
| 523 | + return in_array( strtolower( $extension ), $this->recognizableExtensions ); |
361 | 524 | } |
362 | 525 | |
363 | 526 | |
364 | | - /** mime type detection. This uses detectMimeType to detect the mime type of the file, |
365 | | - * but applies additional checks to determine some well known file formats that may be missed |
366 | | - * or misinterpreter by the default mime detection (namely xml based formats like XHTML or SVG). |
| 527 | + /** mime type detection. This uses detectMimeType to detect the mime type of |
| 528 | + the file, |
| 529 | + * but applies additional checks to determine some well known file formats |
| 530 | + that may be missed |
| 531 | + * or misinterpreter by the default mime detection (namely xml based formats |
| 532 | + like XHTML or SVG). |
367 | 533 | * |
368 | 534 | * @param string $file The file to check |
369 | | - * @param mixed $ext The file extension, or true to extract it from the filename. |
| 535 | + * @param mixed $ext The file extension, or true to extract it from the |
| 536 | + filename. |
370 | 537 | * Set it to false to ignore the extension. |
371 | 538 | * |
372 | 539 | * @return string the mime type of $file |
— | — | @@ -405,9 +572,11 @@ |
406 | 573 | $xml_type = "ASCII"; |
407 | 574 | } elseif ( substr( $head, 0, 8 ) == "\xef\xbb\xbf<?xml") { |
408 | 575 | $xml_type = "UTF-8"; |
409 | | - } elseif ( substr( $head, 0, 10 ) == "\xfe\xff\x00<\x00?\x00x\x00m\x00l" ) { |
| 576 | + } elseif ( substr( $head, 0, 10 ) == |
| 577 | + "\xfe\xff\x00<\x00?\x00x\x00m\x00l" ) { |
410 | 578 | $xml_type = "UTF-16BE"; |
411 | | - } elseif ( substr( $head, 0, 10 ) == "\xff\xfe<\x00?\x00x\x00m\x00l\x00") { |
| 579 | + } elseif ( substr( $head, 0, 10 ) == |
| 580 | + "\xff\xfe<\x00?\x00x\x00m\x00l\x00") { |
412 | 581 | $xml_type = "UTF-16LE"; |
413 | 582 | } |
414 | 583 | |
— | — | @@ -420,10 +589,12 @@ |
421 | 590 | $doctype = ""; |
422 | 591 | $tag = ""; |
423 | 592 | |
424 | | - if ( preg_match( '%<!DOCTYPE\s+[\w-]+\s+PUBLIC\s+["'."'".'"](.*?)["'."'".'"].*>%sim', |
425 | | - $head, $match ) ) { |
426 | | - $doctype = $match[1]; |
427 | | - } |
| 593 | + if ( |
| 594 | + preg_match( '%<!DOCTYPE\s+[\w-]+\s+PUBLIC\s+["'."'".'"](.*?)["'."'".'"].*>%sim', |
| 595 | + $head, $match ) ) |
| 596 | + { |
| 597 | + $doctype = $match[1]; |
| 598 | + } |
428 | 599 | if ( preg_match( '%<(\w+).*>%sim', $head, $match ) ) { |
429 | 600 | $tag = $match[1]; |
430 | 601 | } |
— | — | @@ -495,6 +666,34 @@ |
496 | 667 | |
497 | 668 | } |
498 | 669 | |
| 670 | + //run plugins |
| 671 | + //if aliased, fetch plugins for both types |
| 672 | + //remove plugins registered as both |
| 673 | + //getContentType |
| 674 | + //re-run final result through aliases |
| 675 | + |
| 676 | + $plugins = $this->getPluginsForType($mime); |
| 677 | + |
| 678 | + if(isset($this->mMimeTypeAliases[$mime] )) |
| 679 | + { |
| 680 | + $mime2 = $this->mMimeTypeAliases[$mime]; |
| 681 | + $plugins = array_merge((array) $plugins, (array) $this->getPluginsForType($mime2)); |
| 682 | + MimeMagic::killPluginDupes($plugins); |
| 683 | + } |
| 684 | + |
| 685 | + foreach($plugins AS $detector) |
| 686 | + { |
| 687 | + $plugin = $detector[0]; |
| 688 | + $authoritative = $detector[1]; |
| 689 | + |
| 690 | + $pMime = $plugin->getContentType($file); |
| 691 | + if(strpos($pMime, 'unknown/unknown') === false || $authoritative) |
| 692 | + { |
| 693 | + $mime = $pMime; |
| 694 | + break; |
| 695 | + } |
| 696 | + } |
| 697 | + |
499 | 698 | if ( isset( $this->mMimeTypeAliases[$mime] ) ) { |
500 | 699 | $mime = $this->mMimeTypeAliases[$mime]; |
501 | 700 | } |
— | — | @@ -503,16 +702,92 @@ |
504 | 703 | return $mime; |
505 | 704 | } |
506 | 705 | |
507 | | - /** Internal mime type detection, please use guessMimeType() for application code instead. |
508 | | - * Detection is done using an external program, if $wgMimeDetectorCommand is set. |
509 | | - * Otherwise, the fileinfo extension and mime_content_type are tried (in this order), if they are available. |
510 | | - * If the dections fails and $ext is not false, the mime type is guessed from the file extension, using |
| 706 | + private static function killPluginDupes(&$plugins) |
| 707 | + { |
| 708 | + $out = array(); |
| 709 | + $existingPlugins = array(); |
| 710 | + |
| 711 | + foreach($plugins AS $newDetector) |
| 712 | + { |
| 713 | + if(! in_array($newDetector[0], $existingPlugins)) |
| 714 | + { |
| 715 | + $out[] = $newDetector; |
| 716 | + $existingPlugins[] = $newDetector[0]; |
| 717 | + } |
| 718 | + } |
| 719 | + |
| 720 | + $plugins = $out; |
| 721 | + } |
| 722 | + |
| 723 | + public static function splitMimeString($type) |
| 724 | + { |
| 725 | + //split $type into type and subtype |
| 726 | + $subtype = '*'; |
| 727 | + |
| 728 | + $slashPos = strpos($type, '/'); |
| 729 | + if($slashPos > 0) |
| 730 | + { |
| 731 | + # assume the presence of a slash indicates this is a type/subtype |
| 732 | + if(strlen($type) - 1 === $slashPos) |
| 733 | + { |
| 734 | + #..or maybe not. Just throw out the ending / |
| 735 | + $type = substr($type, 0, -1); |
| 736 | + } else { |
| 737 | + $subtype = substr($type, $slashPos + 1); |
| 738 | + $type = substr($type, 0, $slashPos); |
| 739 | + } |
| 740 | + } |
| 741 | + |
| 742 | + return array($type, $subtype); |
| 743 | + } |
| 744 | + |
| 745 | + private function getPluginsForType($mime) |
| 746 | + { |
| 747 | + $type = MimeMagic::splitMimeString($mime); |
| 748 | + |
| 749 | + if($type[1] != '*') |
| 750 | + { |
| 751 | + $plugins = $this->pluginsByType[ $type[0] ][ $type[1] ]; |
| 752 | + $plugins = array_merge((array) $plugins, (array) $this->pluginsByType[ $type[0] ][ '*' ]); |
| 753 | + } else { |
| 754 | + /* no subtype specified...spit out all plugins from all subtypes. |
| 755 | + * this code is currently unused (and untested), but included for |
| 756 | + * completeness. |
| 757 | + */ |
| 758 | + $plugins = array(); |
| 759 | + foreach($this->pluginsByType[ $type[0] ] AS $subtype) |
| 760 | + { |
| 761 | + foreach($subtype AS $detectors) |
| 762 | + { |
| 763 | + foreach($detectors AS $detector) |
| 764 | + { |
| 765 | + $plugins[] = $detector; |
| 766 | + } |
| 767 | + } |
| 768 | + } |
| 769 | + } |
| 770 | + |
| 771 | + return $plugins; |
| 772 | + } |
| 773 | + |
| 774 | + |
| 775 | + /** Internal mime type detection, please use guessMimeType() for application |
| 776 | + code instead. |
| 777 | + * Detection is done using an external program, if $wgMimeDetectorCommand is |
| 778 | + set. |
| 779 | + * Otherwise, the fileinfo extension and mime_content_type are tried (in this |
| 780 | + order), if they are available. |
| 781 | + * If the dections fails and $ext is not false, the mime type is guessed from |
| 782 | + the file extension, using |
511 | 783 | * guessTypesForExtension. |
512 | | - * If the mime type is still unknown, getimagesize is used to detect the mime type if the file is an image. |
513 | | - * If no mime type can be determined, this function returns "unknown/unknown". |
| 784 | + * If the mime type is still unknown, getimagesize is used to detect the mime |
| 785 | + type if the file is an image. |
| 786 | + * If no mime type can be determined, this function returns |
| 787 | + "unknown/unknown". |
514 | 788 | * |
515 | 789 | * @param string $file The file to check |
516 | | - * @param mixed $ext The file extension, or true to extract it from the filename. |
| 790 | + * @param mixed $ext The file extension, or true to extract it from the |
| 791 | + filename. |
517 | 792 | * Set it to false to ignore the extension. |
518 | 793 | * |
519 | 794 | * @return string the mime type of $file |
— | — | @@ -525,7 +800,8 @@ |
526 | 801 | if ( $wgMimeDetectorCommand ) { |
527 | 802 | $fn = wfEscapeShellArg( $file ); |
528 | 803 | $m = `$wgMimeDetectorCommand $fn`; |
529 | | - } elseif ( function_exists( "finfo_open" ) && function_exists( "finfo_file" ) ) { |
| 804 | + } elseif ( function_exists( "finfo_open" ) && function_exists( |
| 805 | + "finfo_file" ) ) { |
530 | 806 | |
531 | 807 | # This required the fileinfo extension by PECL, |
532 | 808 | # see http://pecl.php.net/package/fileinfo |
— | — | @@ -537,13 +813,15 @@ |
538 | 814 | # If you may need to load the fileinfo extension at runtime, set |
539 | 815 | # $wgLoadFileinfoExtension in LocalSettings.php |
540 | 816 | |
541 | | - $mime_magic_resource = finfo_open(FILEINFO_MIME); /* return mime type ala mimetype extension */ |
| 817 | + $mime_magic_resource = finfo_open(FILEINFO_MIME); /* return mime |
| 818 | + type ala mimetype extension */ |
542 | 819 | |
543 | 820 | if ($mime_magic_resource) { |
544 | 821 | $m = finfo_file( $mime_magic_resource, $file ); |
545 | 822 | finfo_close( $mime_magic_resource ); |
546 | 823 | } else { |
547 | | - wfDebug( __METHOD__.": finfo_open failed on ".FILEINFO_MIME."!\n" ); |
| 824 | + wfDebug( __METHOD__.": finfo_open failed on |
| 825 | + ".FILEINFO_MIME."!\n" ); |
548 | 826 | } |
549 | 827 | } elseif ( function_exists( "mime_content_type" ) ) { |
550 | 828 | |
— | — | @@ -559,10 +837,12 @@ |
560 | 838 | $m = mime_content_type($file); |
561 | 839 | |
562 | 840 | if ( $m == 'text/plain' ) { |
563 | | - // mime_content_type sometimes considers DJVU files to be text/plain. |
| 841 | + // mime_content_type sometimes considers DJVU files to be |
| 842 | + text/plain. |
564 | 843 | $deja = new DjVuImage( $file ); |
565 | 844 | if( $deja->isValid() ) { |
566 | | - wfDebug( __METHOD__.": (re)detected $file as image/vnd.djvu\n" ); |
| 845 | + wfDebug( __METHOD__.": (re)detected $file as |
| 846 | + image/vnd.djvu\n" ); |
567 | 847 | $m = 'image/vnd.djvu'; |
568 | 848 | } |
569 | 849 | } |
— | — | @@ -653,27 +933,31 @@ |
654 | 934 | } |
655 | 935 | |
656 | 936 | /** |
657 | | - * Determine the media type code for a file, using its mime type, name and possibly |
658 | | - * its contents. |
| 937 | + * Determine the media type code for a file, using its mime type, name and |
| 938 | + * possibly its contents. |
659 | 939 | * |
660 | 940 | * This function relies on the findMediaType(), mapping extensions and mime |
661 | 941 | * types to media types. |
662 | 942 | * |
663 | | - * @todo analyse file if need be |
664 | 943 | * @todo look at multiple extension, separately and together. |
665 | 944 | * |
666 | | - * @param string $path full path to the image file, in case we have to look at the contents |
667 | | - * (if null, only the mime type is used to determine the media type code). |
668 | | - * @param string $mime mime type. If null it will be guessed using guessMimeType. |
| 945 | + * @param string $path full path to the image file, in case we have to look |
| 946 | + * at the contents |
| 947 | + * (if null, only the mime type is used to determine the media type code). |
669 | 948 | * |
670 | | - * @return (int?string?) a value to be used with the MEDIATYPE_xxx constants. |
| 949 | + * @param string $mime mime type. If null it will be guessed using |
| 950 | + * guessMimeType. |
| 951 | + * |
| 952 | + * @return string a value to be used with the MEDIATYPE_xxx constants. |
671 | 953 | */ |
672 | 954 | function getMediaType( $path = NULL, $mime = NULL ) { |
673 | 955 | if( !$mime && !$path ) return MEDIATYPE_UNKNOWN; |
674 | 956 | |
675 | 957 | # If mime type is unknown, guess it |
676 | 958 | if( !$mime ) $mime = $this->guessMimeType( $path, false ); |
| 959 | + if(strpos($mime, 'unknown/unknown') === 0) return MEDIATYPE_UNKNOWN; |
677 | 960 | |
| 961 | + /* obsoleted by plugins |
678 | 962 | # Special code for ogg - detect if it's video (theora), |
679 | 963 | # else label it as sound. |
680 | 964 | if( $mime == "application/ogg" && file_exists( $path ) ) { |
— | — | @@ -688,41 +972,55 @@ |
689 | 973 | |
690 | 974 | # This is an UGLY HACK, file should be parsed correctly |
691 | 975 | if ( strpos( $head, 'theora' ) !== false ) return MEDIATYPE_VIDEO; |
692 | | - elseif ( strpos( $head, 'vorbis' ) !== false ) return MEDIATYPE_AUDIO; |
| 976 | + elseif ( strpos( $head, 'vorbis' ) !== false ) return |
| 977 | + MEDIATYPE_AUDIO; |
693 | 978 | elseif ( strpos( $head, 'flac' ) !== false ) return MEDIATYPE_AUDIO; |
694 | | - elseif ( strpos( $head, 'speex' ) !== false ) return MEDIATYPE_AUDIO; |
| 979 | + elseif ( strpos( $head, 'speex' ) !== false ) return |
| 980 | + MEDIATYPE_AUDIO; |
695 | 981 | else return MEDIATYPE_MULTIMEDIA; |
696 | 982 | } |
| 983 | + */ |
697 | 984 | |
| 985 | + $type = MEDIATYPE_UNKNOWN; |
698 | 986 | # check for entry for full mime type |
699 | | - if( $mime ) { |
700 | | - $type = $this->findMediaType( $mime ); |
701 | | - if( $type !== MEDIATYPE_UNKNOWN ) return $type; |
702 | | - } |
| 987 | + $type = $this->findMediaType( $mime ); |
| 988 | + if($type == MEDIATYPE_UNKNOWN) |
| 989 | + { |
| 990 | + # Check for entry for file extension |
| 991 | + $e = NULL; |
| 992 | + if ( $path ) { |
| 993 | + $i = strrpos( $path, '.' ); |
| 994 | + $e = strtolower( $i ? substr( $path, $i + 1 ) : '' ); |
703 | 995 | |
704 | | - # Check for entry for file extension |
705 | | - $e = NULL; |
706 | | - if ( $path ) { |
707 | | - $i = strrpos( $path, '.' ); |
708 | | - $e = strtolower( $i ? substr( $path, $i + 1 ) : '' ); |
| 996 | + # TODO: look at multi-extension if this fails, parse from full path |
709 | 997 | |
710 | | - # TODO: look at multi-extension if this fails, parse from full path |
711 | | - |
712 | | - $type = $this->findMediaType( '.' . $e ); |
713 | | - if ( $type !== MEDIATYPE_UNKNOWN ) return $type; |
714 | | - } |
715 | | - |
716 | | - # Check major mime type |
717 | | - if( $mime ) { |
| 998 | + $type = $this->findMediaType( '.' . $e ); |
| 999 | + } |
| 1000 | + } else if($type == MEDIATYPE_UNKNOWN) |
| 1001 | + { |
| 1002 | + # Check major mime type |
718 | 1003 | $i = strpos( $mime, '/' ); |
719 | 1004 | if( $i !== false ) { |
720 | 1005 | $major = substr( $mime, 0, $i ); |
721 | 1006 | $type = $this->findMediaType( $major ); |
722 | | - if( $type !== MEDIATYPE_UNKNOWN ) return $type; |
723 | 1007 | } |
724 | 1008 | } |
725 | 1009 | |
726 | | - if( !$type ) $type = MEDIATYPE_UNKNOWN; |
| 1010 | + if( $type == MEDIATYPE_MULTIMEDIA && file_exists($path)) |
| 1011 | + { |
| 1012 | + /* check if there's a registered plugin that wants to make the |
| 1013 | + * determination by examination of contents |
| 1014 | + */ |
| 1015 | + $detectors = $this->getPluginsForType($mime); |
| 1016 | + foreach($detectors AS $detector) |
| 1017 | + { |
| 1018 | + if($detector[1]) //assume non-authoritative plugins won't know |
| 1019 | + { |
| 1020 | + $t = $detector[0]->getMediaType($path, $mime); |
| 1021 | + if($t) return $t; |
| 1022 | + } |
| 1023 | + } |
| 1024 | + } |
727 | 1025 | |
728 | 1026 | return $type; |
729 | 1027 | } |
— | — | @@ -731,6 +1029,8 @@ |
732 | 1030 | * File extensions are represented by a string starting with a dot (.) to |
733 | 1031 | * distinguish them from mime types. |
734 | 1032 | * |
| 1033 | + * @todo make this function match "major" mime types, getMediaType seems to |
| 1034 | + * think it can use it in this capacity. |
735 | 1035 | * This funktion relies on the mapping defined by $this->mMediaTypes |
736 | 1036 | * @access private |
737 | 1037 | */ |
— | — | @@ -750,6 +1050,7 @@ |
751 | 1051 | } |
752 | 1052 | |
753 | 1053 | foreach ( $m as $mime ) { |
| 1054 | + $mime = trim($mime); if(empty($mime)) continue; |
754 | 1055 | foreach ( $this->mMediaTypes as $type => $codes ) { |
755 | 1056 | if ( in_array($mime, $codes, true ) ) { |
756 | 1057 | return $type; |
Index: branches/mikeb/phase3/includes/JobQueue.php |
— | — | @@ -63,7 +63,8 @@ |
64 | 64 | |
65 | 65 | if ($affected == 0) { |
66 | 66 | wfProfileOut( __METHOD__ ); |
67 | | - return false; |
| 67 | + //but there may still be other jobs of $type we can claim... |
| 68 | + return self::pop_type($type); |
68 | 69 | } |
69 | 70 | |
70 | 71 | $namespace = $row->job_namespace; |
— | — | @@ -180,6 +181,12 @@ |
181 | 182 | return new EmaillingJob($params); |
182 | 183 | case 'enotifNotify': |
183 | 184 | return new EnotifNotifyJob($title, $params); |
| 185 | + case 'AudioRecode': case 'VideoRecode': |
| 186 | + $class = $command . 'Job'; |
| 187 | + if(class_exists($class, true)) |
| 188 | + { |
| 189 | + return new $class($title, $params, $id); |
| 190 | + } |
184 | 191 | default: |
185 | 192 | throw new MWException( "Invalid job command \"$command\"" ); |
186 | 193 | } |
Index: branches/mikeb/phase3/includes/SpecialUpload.php |
— | — | @@ -4,7 +4,6 @@ |
5 | 5 | * @addtogroup SpecialPage |
6 | 6 | */ |
7 | 7 | |
8 | | - |
9 | 8 | /** |
10 | 9 | * Entry point |
11 | 10 | */ |
— | — | @@ -262,6 +261,14 @@ |
263 | 262 | function processUpload() { |
264 | 263 | global $wgUser, $wgOut; |
265 | 264 | |
| 265 | + /* |
| 266 | + * Add audio and video checking. Probably there is a better place to put |
| 267 | + * these. |
| 268 | + */ |
| 269 | + global $wgHooks; |
| 270 | + $wgHooks['UploadVerification'][] = new AudioUploadHandler; |
| 271 | + $wgHooks['UploadVerification'][] = new VideoUploadHandler; |
| 272 | + |
266 | 273 | if( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) |
267 | 274 | { |
268 | 275 | wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file." ); |
— | — | @@ -372,7 +379,7 @@ |
373 | 380 | */ |
374 | 381 | $error = ''; |
375 | 382 | if( !wfRunHooks( 'UploadVerification', |
376 | | - array( $this->mDestName, $this->mTempPath, &$error ) ) ) { |
| 383 | + array( $this->mDestName, $this->mTempPath, &$error, $finalExt ) ) ) { |
377 | 384 | return $this->uploadError( $error ); |
378 | 385 | } |
379 | 386 | } |
— | — | @@ -384,6 +391,17 @@ |
385 | 392 | if ( ! $this->mIgnoreWarning ) { |
386 | 393 | $warning = ''; |
387 | 394 | |
| 395 | + /* if upload hooks wrote to $error, report as warning. |
| 396 | + * This isn't perfect in that if multiple UploadVerification hooks |
| 397 | + * made warnings, only the last one will show. As if that's really |
| 398 | + * ever going to happen. Easy fix is to have the hooks append to an |
| 399 | + * array instead. |
| 400 | + */ |
| 401 | + if(strlen($error)) |
| 402 | + { |
| 403 | + $warning .= "<li>$error</li>"; |
| 404 | + } |
| 405 | + |
388 | 406 | global $wgCapitalLinks; |
389 | 407 | if( $wgCapitalLinks ) { |
390 | 408 | $filtered = ucfirst( $filtered ); |
— | — | @@ -920,7 +938,6 @@ |
921 | 939 | #magically determine mime type |
922 | 940 | $magic=& MimeMagic::singleton(); |
923 | 941 | $mime= $magic->guessMimeType($tmpfile,false); |
924 | | - |
925 | 942 | #check mime type, if desired |
926 | 943 | global $wgVerifyMimeType; |
927 | 944 | if ($wgVerifyMimeType) { |
Index: branches/mikeb/phase3/includes/Hooks.php |
— | — | @@ -105,7 +105,36 @@ |
106 | 106 | } elseif ( false !== ( $pos = strpos( $func, '::' ) ) ) { |
107 | 107 | $callback = array( substr( $func, 0, $pos ), substr( $func, $pos + 2 ) ); |
108 | 108 | } else { |
109 | | - $callback = $func; |
| 109 | + if(function_exists($func)) |
| 110 | + { |
| 111 | + $callback = $func; |
| 112 | + } else { |
| 113 | + //see if it's really a class (without a static method specified) |
| 114 | + //I will document this feature if there aren't objections to it |
| 115 | + if(class_exists($func, true)) |
| 116 | + { |
| 117 | + //see if it has an on[Event] method |
| 118 | + if(in_array("on$event", get_class_methods($func))) |
| 119 | + { |
| 120 | + //see if it's static & callable |
| 121 | + $method = new ReflectionMethod($func, "on$event"); |
| 122 | + if($method->isStatic() && $method->isPublic()) |
| 123 | + { |
| 124 | + $callback = array($func, "on$event"); |
| 125 | + } else { |
| 126 | + throw new MWException("Method on$event in class $func must be public and static to be used in hooks for $event\n"); |
| 127 | + } |
| 128 | + } else { |
| 129 | + throw new MWException("$func is a class, not a function in hooks for $event. Define $func::on$event or see docs/hooks.txt for more usage information."); |
| 130 | + } |
| 131 | + } else { |
| 132 | + /*past behavior was to try to call it anyway...slightly |
| 133 | + * worried throwing an exception instead might break |
| 134 | + * something |
| 135 | + */ |
| 136 | + throw new MWException("No function or class \"$func\" in hooks for $event."); |
| 137 | + } |
| 138 | + } |
110 | 139 | } |
111 | 140 | |
112 | 141 | /* Call the hook. */ |
— | — | @@ -126,4 +155,5 @@ |
127 | 156 | |
128 | 157 | return true; |
129 | 158 | } |
| 159 | + |
130 | 160 | ?> |
Index: branches/mikeb/phase3/includes/media/Generic.php |
— | — | @@ -23,8 +23,13 @@ |
24 | 24 | static function getHandler( $type ) { |
25 | 25 | global $wgMediaHandlers; |
26 | 26 | if ( !isset( $wgMediaHandlers[$type] ) ) { |
27 | | - wfDebug( __METHOD__ . ": no handler found for $type.\n"); |
28 | | - return false; |
| 27 | + if( !isset( $wgMediaHandlers[$mediaType] )) |
| 28 | + { |
| 29 | + wfDebug( __METHOD__ . ": no handler found for $type.\n"); |
| 30 | + return false; |
| 31 | + } else { |
| 32 | + $type = $mediaType; |
| 33 | + } |
29 | 34 | } |
30 | 35 | $class = $wgMediaHandlers[$type]; |
31 | 36 | if ( !isset( self::$handlers[$class] ) ) { |
Index: branches/mikeb/phase3/includes/media/AV.php |
— | — | @@ -0,0 +1,477 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | +* Currently this file contains a set of classes (inheriting from |
| 5 | +* AVUploadHandler) to be used with the UploadVerification hook for precision |
| 6 | +* audio and video upload verification. These upload handler classes utilize the |
| 7 | +* various AVInspector classes also included in this file. I have implemented |
| 8 | +* AVInspectors powered by ffmpeg-php and MPlayer, as well as a composite |
| 9 | +* class that implements its methods in terms of the other AVInspectors. |
| 10 | +* |
| 11 | +* For audio file verification, ffmpeg is completely suitable, and so the |
| 12 | +* AudioUploadHandler only uses FfmpegAVInspector. However, ffmpeg occasionally |
| 13 | +* fails to validate a file that MPlayer can decode, and in this event the |
| 14 | +* composite class will enlist the help of MPlayerAVInspector as well. Thus, |
| 15 | +* VideoUploadHandler works directly with CompositeAVInspector. |
| 16 | +* |
| 17 | +* Since the tools powering the AVInspector classes also provide ready access to |
| 18 | +* metadata, I will also be calling on these classes to extract information like |
| 19 | +* (chronological) file length and bitrates. Current design has this metadata |
| 20 | +* extraction taking place in media handlers, not upload verification handlers, |
| 21 | +* so for now I'm having AVInspectors cache the results of file examinations, |
| 22 | +* expecting that media handlers will request metadata information later as part |
| 23 | +* of the same http request. |
| 24 | +*/ |
| 25 | + |
| 26 | +interface AVInspector |
| 27 | +{ |
| 28 | + function hasVideoStream(); |
| 29 | + |
| 30 | + function hasAudioStream(); |
| 31 | + |
| 32 | + function hasDecodableVideoStream(); |
| 33 | + |
| 34 | + function hasDecodableAudioStream(); |
| 35 | + |
| 36 | + function getFrameWidth(); |
| 37 | + |
| 38 | + function getFrameHeight(); |
| 39 | + |
| 40 | + /* |
| 41 | + * These last three methods can be implemented in terms of the others, and I |
| 42 | + * have done so in AVInspectorHelper. |
| 43 | + */ |
| 44 | + function isDecodable(); |
| 45 | + |
| 46 | + function isAV(); |
| 47 | + |
| 48 | + function isAudio(); |
| 49 | +} |
| 50 | + |
| 51 | +abstract class AVInspectorHelper implements AVInspector |
| 52 | +{ |
| 53 | + /* |
| 54 | + * Shortcut method that passes good audio, or video with or without audio. |
| 55 | + * Use has(Decodable)AudioStream for that. |
| 56 | + */ |
| 57 | + public function isDecodable() |
| 58 | + { |
| 59 | + if($this->isAudio()) |
| 60 | + { |
| 61 | + return $this->hasDecodableAudioStream(); |
| 62 | + } else { |
| 63 | + return $this->hasDecodableVideoStream(); |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + public function isAV() |
| 68 | + { |
| 69 | + return $this->hasAudioStream() || $this->hasVideoStream(); |
| 70 | + } |
| 71 | + |
| 72 | + public function isAudio() |
| 73 | + { |
| 74 | + return $this->isAV() && ! $this->hasVideoStream(); |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +class CompositeAVInspector extends AVInspectorHelper implements AVInspector |
| 79 | +{ |
| 80 | + private $inspectors; |
| 81 | + private $file; |
| 82 | + |
| 83 | + public function __construct($file) |
| 84 | + { |
| 85 | + $this->file = $file; |
| 86 | + |
| 87 | + /* inspectors are given as classnames only...instantiating |
| 88 | + * now would cause all inspectors to run, defeating the purpose of the |
| 89 | + * composite class. |
| 90 | + */ |
| 91 | + $this->inspectors = array(); |
| 92 | + |
| 93 | + // see if we have access to the ffmpeg api... |
| 94 | + $extension = "ffmpeg"; |
| 95 | + $extension_soname = $extension . "." . PHP_SHLIB_SUFFIX; |
| 96 | + // load extension |
| 97 | + if(!extension_loaded($extension)) { |
| 98 | + @dl($extension_soname); |
| 99 | + } |
| 100 | + |
| 101 | + // if we can use the ffmpeg api, make an inspector based on it |
| 102 | + if(class_exists('ffmpeg_movie')) |
| 103 | + $this->inspectors[] = "FfmpegAVInspector"; |
| 104 | + |
| 105 | + // todo: scan for midentify |
| 106 | + $this->inspectors[] = "MPlayerAVInspector"; |
| 107 | + } |
| 108 | + |
| 109 | + /* the below duplication is necessary for the parser to think we've |
| 110 | + * implemented the interface. Otherwise, using __call would work. |
| 111 | + */ |
| 112 | + function hasVideoStream() |
| 113 | + { |
| 114 | + return $this->composite(__FUNCTION__); |
| 115 | + } |
| 116 | + |
| 117 | + function hasAudioStream() |
| 118 | + { |
| 119 | + return $this->composite(__FUNCTION__); |
| 120 | + } |
| 121 | + |
| 122 | + function hasDecodableVideoStream() |
| 123 | + { |
| 124 | + return $this->composite(__FUNCTION__); |
| 125 | + } |
| 126 | + |
| 127 | + function hasDecodableAudioStream() |
| 128 | + { |
| 129 | + return $this->composite(__FUNCTION__); |
| 130 | + } |
| 131 | + |
| 132 | + function getFrameWidth() |
| 133 | + { |
| 134 | + return $this->composite(__FUNCTION__); |
| 135 | + } |
| 136 | + |
| 137 | + function getFrameHeight() |
| 138 | + { |
| 139 | + return $this->composite(__FUNCTION__); |
| 140 | + } |
| 141 | + |
| 142 | + private function composite($method) |
| 143 | + { |
| 144 | + foreach($this->inspectors AS &$inspector) |
| 145 | + { |
| 146 | + if(is_string($inspector)) $inspector = new $inspector($this->file); |
| 147 | + $value = $inspector->$method(); |
| 148 | + |
| 149 | + echo(get_class($inspector) . " sets $method as "); |
| 150 | + if($value === true) echo 'true'; else if($value === false) echo 'false'; else echo $value; |
| 151 | + echo "<br>\n"; |
| 152 | + |
| 153 | + if((bool) $value) |
| 154 | + { |
| 155 | + return $value; |
| 156 | + } |
| 157 | + } |
| 158 | + return false; |
| 159 | + } |
| 160 | +} |
| 161 | + |
| 162 | +class MPlayerAVInspector extends AVInspectorHelper implements AVInspector |
| 163 | +{ |
| 164 | + /* Since current code involves upload handlers validating through this |
| 165 | + * class, and media handlers extracting metadata from this class, results |
| 166 | + * of file inspections are cached here by path. This sucks, it should really |
| 167 | + * just all be done in the upload handler, and have the media handler do |
| 168 | + * presentation only...but nobody wants to discuss this. |
| 169 | + */ |
| 170 | + private static $info; |
| 171 | + private $path; |
| 172 | + |
| 173 | + public function __construct($path) |
| 174 | + { |
| 175 | + $path = escapeshellarg($path); |
| 176 | + $this->path = $path; |
| 177 | + if(! is_array(self::$info[$path])) |
| 178 | + { |
| 179 | + exec("/data/mplayer-checkout-2007-06-08/TOOLS/midentify $path", $output); |
| 180 | + |
| 181 | + self::$info[$path] = array(); |
| 182 | + foreach($output AS $line) |
| 183 | + { |
| 184 | + $p = strpos($line, '='); |
| 185 | + $key = substr($line, 0, $p); |
| 186 | + $value = substr($line, $p+1); |
| 187 | + self::$info[$path][$key] = $value; |
| 188 | + } |
| 189 | + } |
| 190 | + } |
| 191 | + |
| 192 | + /* enables some shorthand for most AVInspector methods. |
| 193 | + * Exact vaules can still be acquired through $this->info |
| 194 | + */ |
| 195 | + private function __get($attribute) |
| 196 | + { |
| 197 | + if($attribute != 'info') |
| 198 | + { |
| 199 | + $value = trim(self::$info[$this->path]['ID_' . strtoupper($attribute)]); |
| 200 | + if(strlen($value)) |
| 201 | + { |
| 202 | + return true; |
| 203 | + } else { |
| 204 | + return false; |
| 205 | + } |
| 206 | + } else { |
| 207 | + return self::$info[$this->path]; |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + function hasVideoStream() |
| 212 | + { |
| 213 | + return $this->video_format; |
| 214 | + } |
| 215 | + |
| 216 | + function hasAudioStream() |
| 217 | + { |
| 218 | + return $this->audio_id; |
| 219 | + } |
| 220 | + |
| 221 | + function hasDecodableVideoStream() |
| 222 | + { |
| 223 | + return $this->video_codec; |
| 224 | + } |
| 225 | + |
| 226 | + function hasDecodableAudioStream() |
| 227 | + { |
| 228 | + return $this->audio_codec; |
| 229 | + } |
| 230 | + |
| 231 | + function getFrameWidth() |
| 232 | + { |
| 233 | + return $this->info['ID_VIDEO_WIDTH']; |
| 234 | + } |
| 235 | + |
| 236 | + function getFrameHeight() |
| 237 | + { |
| 238 | + return $this->info['ID_VIDEO_HEIGHT']; |
| 239 | + } |
| 240 | +} |
| 241 | + |
| 242 | +class FfmpegAVInspector extends AVInspectorHelper implements AVInspector |
| 243 | +{ |
| 244 | + private static $cache; |
| 245 | + private $path; |
| 246 | + |
| 247 | + /* used to be private $movie; cache was added later. |
| 248 | + * The __get magic method was the easy way to make it still |
| 249 | + * work. |
| 250 | + */ |
| 251 | + public function __get($name) |
| 252 | + { |
| 253 | + return self::$cache[$this->path]; |
| 254 | + } |
| 255 | + |
| 256 | + public function __construct($file) |
| 257 | + { |
| 258 | + $this->path = $file; |
| 259 | + if(! is_object(self::$cache[$file])) |
| 260 | + { |
| 261 | + // see if we have access to the ffmpeg api... |
| 262 | + $extension = "ffmpeg"; |
| 263 | + $extension_soname = $extension . "." . PHP_SHLIB_SUFFIX; |
| 264 | + // load extension |
| 265 | + if(!extension_loaded($extension)) { |
| 266 | + @dl($extension_soname); |
| 267 | + } |
| 268 | + |
| 269 | + self::$cache[$file] = @new ffmpeg_movie($file); |
| 270 | + |
| 271 | + /* if ffmpeg can't decypher this file, we don't even get an object. |
| 272 | + * The below little hack prevents all the member methods from having to |
| 273 | + * check for this conidtion. |
| 274 | + */ |
| 275 | + if(! is_object(self::$cache[$file])) |
| 276 | + { |
| 277 | + self::$cache[$file] = new FalseClass(); |
| 278 | + //no anonymous classes in php :( |
| 279 | + } |
| 280 | + } |
| 281 | + } |
| 282 | + |
| 283 | + public function hasVideoStream() |
| 284 | + { |
| 285 | + return $this->movie->getFrameCount() > 1; |
| 286 | + } |
| 287 | + |
| 288 | + public function hasAudioStream() |
| 289 | + { |
| 290 | + return $this->movie->hasAudio(); |
| 291 | + } |
| 292 | + |
| 293 | + public function hasDecodableVideoStream() |
| 294 | + { |
| 295 | + //for now, do this by attempting to render the first frame. |
| 296 | + //There may be a less expensive way...getVideoCodec, but not sure |
| 297 | + $frame = $this->movie->getFrame(1); |
| 298 | + return $frame !== false && $frame->getWidth() && $frame->getHeight(); |
| 299 | + } |
| 300 | + |
| 301 | + public function hasDecodableAudioStream() |
| 302 | + { |
| 303 | + return $this->hasAudioStream() && (bool) $this->movie->getAudioCodec() && (bool) $this->movie->getAudioSampleRate(); |
| 304 | + } |
| 305 | + |
| 306 | + |
| 307 | + public function getFrameWidth() |
| 308 | + { |
| 309 | + return $this->movie->getFrameWidth(); |
| 310 | + } |
| 311 | + |
| 312 | + public function getFrameHeight() |
| 313 | + { |
| 314 | + return $this->movie->getFrameHeight(); |
| 315 | + } |
| 316 | +} |
| 317 | + |
| 318 | +class FalseClass |
| 319 | +{ |
| 320 | + public function __call($name, $args) |
| 321 | + { |
| 322 | + return false; |
| 323 | + } |
| 324 | +} |
| 325 | + |
| 326 | +abstract class AVUploadHandler |
| 327 | +{ |
| 328 | + protected $inspector; |
| 329 | + |
| 330 | + public function __construct() |
| 331 | + { |
| 332 | + $this->inspector = "CompositeAVInspector"; |
| 333 | + } |
| 334 | + |
| 335 | + public abstract function getSupportedExtensions(); |
| 336 | + |
| 337 | + public function onUploadVerification($useless, $path, &$errorRef, $ext) |
| 338 | + { |
| 339 | + $exts = $this->getSupportedExtensions(); |
| 340 | + if(!in_array($ext, $exts)) return true; |
| 341 | + /*SUCKS! This way, everything else doesn't matter if the user misnames a |
| 342 | + * file. We need *exclusively* upload handlers by extension. |
| 343 | + */ |
| 344 | + |
| 345 | + /*Ultimately uploading should be rewritten so that |
| 346 | + * this code only gets called if MimeMagic says the extension must |
| 347 | + * validate as an audio or video file. |
| 348 | + */ |
| 349 | + |
| 350 | + $start = microtime(true); |
| 351 | + |
| 352 | + $inspector = new $this->inspector($path); |
| 353 | + |
| 354 | + // todo whatever needs to happen to make errors multilingual. |
| 355 | + // some debug & performance output is left for now for your reference, |
| 356 | + // should you choose to run this. |
| 357 | + if(! $inspector->isAV()) |
| 358 | + { |
| 359 | + $errorRef .= "<br />This audio or video file is unrecognized or corrupt."; |
| 360 | + echo ("AV upload checking done in " . (microtime(true) - $start)); |
| 361 | + return false; |
| 362 | + } |
| 363 | + |
| 364 | + if(! $inspector->isDecodable()) |
| 365 | + { |
| 366 | + $errorRef = "This file cannot be prepared for the WikiMedia player. You will be unable to add it to article pages. It will be available for direct download only."; |
| 367 | + echo ("AV upload checking done in " . (microtime(true) - $start)); |
| 368 | + return true; |
| 369 | + } |
| 370 | + |
| 371 | + if(! $inspector->hasDecodableAudioStream()) |
| 372 | + { |
| 373 | + $errorRef = "The audio component of this video file is in an unrecognized format. No sound will be available in the WikiMedia player."; |
| 374 | + echo ("AV upload checking done in " . (microtime(true) - $start)); |
| 375 | + return true; |
| 376 | + } |
| 377 | + |
| 378 | + //also could easily add warnings for too small/to large frame size or |
| 379 | + //other metadata attributs here. |
| 380 | + |
| 381 | + echo ("AV upload passed all checks in " . (microtime(true) - $start)); |
| 382 | + return true; |
| 383 | + } |
| 384 | +} |
| 385 | + |
| 386 | +class VideoUploadHandler extends AVUploadHandler |
| 387 | +{ |
| 388 | + public function getSupportedExtensions() |
| 389 | + { |
| 390 | + $mime = MimeMagic::singleton(); |
| 391 | + $mimeKnownVideoExts = $mime->getExtensionsForType('video'); |
| 392 | + |
| 393 | + $others = "avi mov asf yuv mp4 mpeg"; |
| 394 | + |
| 395 | + $out = array_merge( |
| 396 | + explode(' ', $mimeKnownAudioExts), |
| 397 | + explode(' ', $mimeKnownVideoExts), |
| 398 | + explode(' ', $others) |
| 399 | + ); |
| 400 | + |
| 401 | + return $out; |
| 402 | + } |
| 403 | +} |
| 404 | + |
| 405 | +class AudioUploadHandler extends AVUploadHandler |
| 406 | +{ |
| 407 | + public function __construct() |
| 408 | + { |
| 409 | + $this->inspector = "FfmpegAVInspector"; |
| 410 | + } |
| 411 | + |
| 412 | + public function getSupportedExtensions() |
| 413 | + { |
| 414 | + $mime = MimeMagic::singleton(); |
| 415 | + return explode(' ', $mime->getExtensionsForType('audio')); |
| 416 | + } |
| 417 | +} |
| 418 | + |
| 419 | + |
| 420 | + |
| 421 | + |
| 422 | + |
| 423 | + |
| 424 | + |
| 425 | + |
| 426 | + |
| 427 | + |
| 428 | + |
| 429 | + |
| 430 | +/** |
| 431 | +* This is just a stub class I was using to test my work making MimeMagic more |
| 432 | +* modular. See my versions of MimeMagic.php and includes/media/MimePlugin.php. |
| 433 | +*/ |
| 434 | + |
| 435 | +class AVMimePlugin extends MimePlugin |
| 436 | +{ |
| 437 | + public static function onMimeMagicRegisterPlugins($core) |
| 438 | + { |
| 439 | + /*make one of itself..it'd be slick if a universal implementation of |
| 440 | + * this method could be made in the superclass, but alas...php bug |
| 441 | + * #30423... |
| 442 | + */ |
| 443 | + new self($core); |
| 444 | + } |
| 445 | + |
| 446 | + protected function __construct($core) |
| 447 | + { |
| 448 | + parent::__construct($core); |
| 449 | + |
| 450 | + //$this->registerContentType('video', true); |
| 451 | + } |
| 452 | + |
| 453 | + public function getContentType($file) |
| 454 | + { |
| 455 | + return 'unknown/unknown'; |
| 456 | + } |
| 457 | + |
| 458 | + protected function mimeTypes() |
| 459 | + { |
| 460 | + //return 'video/x-ms-asf asf asx'; |
| 461 | + } |
| 462 | + |
| 463 | + protected function mimeInfo() |
| 464 | + { |
| 465 | + //return 'video/x-ms-asf [VIDEO]'; |
| 466 | + } |
| 467 | + |
| 468 | + public function getMediaType($file, $mime) |
| 469 | + { |
| 470 | + if($mime == 'application/ogg') |
| 471 | + { |
| 472 | + return MEDIATYPE_VIDEO; |
| 473 | + } else { |
| 474 | + return false; |
| 475 | + } |
| 476 | + } |
| 477 | +} |
| 478 | +?> |
\ No newline at end of file |
Property changes on: branches/mikeb/phase3/includes/media/AV.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 479 | + native |
Index: branches/mikeb/phase3/includes/media/MimePlugin.php |
— | — | @@ -0,0 +1,140 @@ |
| 2 | +<?php |
| 3 | +/** Comments should be inserted here ;) */ |
| 4 | + |
| 5 | +abstract class MimePlugin |
| 6 | +{ |
| 7 | + /** some convenience named constants |
| 8 | + */ |
| 9 | + const EXAMINE_UNKNOWN = true; |
| 10 | + const IGNORE_UNKNOWN = false; |
| 11 | + |
| 12 | + private $unknownFileSafe; |
| 13 | + |
| 14 | + /** |
| 15 | + * The MimeMagic instance. Plugins need to reference MimeMagic during their |
| 16 | + * construction, and because this occurs *within MimeMagic's own |
| 17 | + * constructor*, using MimeMagic::singleton() would just create a new |
| 18 | + * MimeMagic instance (and create an endless loop.) |
| 19 | + */ |
| 20 | + protected $MagicCore; |
| 21 | + |
| 22 | + protected function __construct(MimeMagic $core, $arbitraryFileSafe = MimePlugin::EXAMINE_UNKNOWN) |
| 23 | + { |
| 24 | + $this->MagicCore = $core; |
| 25 | + $this->MagicCore->addMimeTypes($this->mimeTypes()); |
| 26 | + $this->MagicCore->addMimeInfo($this->mimeInfo()); |
| 27 | + |
| 28 | + if($arbitraryFileSafe) |
| 29 | + { |
| 30 | + $this->registerContentType('unknown/unknown'); |
| 31 | + } |
| 32 | + } |
| 33 | + |
| 34 | + /** |
| 35 | + * Register this plugin for a MIME content-type. |
| 36 | + * This should be called as many times as necessary in your child class |
| 37 | + * constructor. |
| 38 | + * |
| 39 | + * You can register for a precise content-type (ex. audio/mpeg), or |
| 40 | + * for all media in a main media-type group (ex. video). See |
| 41 | + * http://www.iana.org/assignments/media-types/ for the possibilities. |
| 42 | + * |
| 43 | + * MimeMagic uses this information to decide what plugins to seek help from. |
| 44 | + * There are two reasons for you to register a given content type X: |
| 45 | + * 1) This plugin targets content type X. In general, |
| 46 | + * $authoritativeRejector should be true for any such types. If you |
| 47 | + * register as authoritative for an entire media-type group, be sure |
| 48 | + * your plugin really can identify every subtype (that your wiki cares |
| 49 | + * about, anyway.) |
| 50 | + * 2) If MimeMagic is known to misidentify the plugin's target type as X. For |
| 51 | + * example, .asf files are usually guessed as application/octet-stream. |
| 52 | + * Thus, if your plugin targets .asf files, you should also register it |
| 53 | + * for application/octet-stream non-authoritatively. (Actually, |
| 54 | + * application/octet stream is aliased to unknown/unknown by default and |
| 55 | + * so EXAMINE_UNKNOWN plugins will get used anyway, but the example |
| 56 | + * still serves to illustrate.) |
| 57 | + * There is no shortcut to register your plugin for all content types, and |
| 58 | + * please don't do it. This defeats the purpose of a plugin, and |
| 59 | + * really attempts to replace Magic Mime checking by thoroughly processing |
| 60 | + * all files as all file types, a generally silly idea. |
| 61 | + * |
| 62 | + * @param $type string A content type or type/subtype pair. |
| 63 | + * @param $authoritativeRejector boolean Whether this plugin's inability to |
| 64 | + * identify a particular file as $type really means it is not $type. |
| 65 | + */ |
| 66 | + protected final function registerContentType($type, |
| 67 | + $authoritativeRejector = false) |
| 68 | + { |
| 69 | + $type = MimeMagic::splitMimeString($type); |
| 70 | + |
| 71 | + $this->MagicCore->registerPluginByContentType($this, $type[0], $type[1], $authoritativeRejector); |
| 72 | + } |
| 73 | + |
| 74 | + /** |
| 75 | + * Attempt to identify the MIME content-type of $file. |
| 76 | + * |
| 77 | + * MimeMagic will call this in the event that |
| 78 | + * A) It has no clue about $file and this plugin was constructed with |
| 79 | + * MimePlugin::EXAMINE_UNKNOWN. |
| 80 | + * B) It guessed $file to be a type that this plugin registered itself for. |
| 81 | + * |
| 82 | + * The rationale behind case B) is to correct wrong guesses by MimeMagic. |
| 83 | + * This can happen in two ways. MimeMagic can: |
| 84 | + * 1) Misidentify a file as of a type this plugin specializes in (and for which this plugin registered itself |
| 85 | + * authoritative), when the file is in fact something else. This plugin probably cannot determine what the file |
| 86 | + * really is, but knows (by attempting to actually render as an image, |
| 87 | + * say) that it is not what MimeMagic guessed. In this case, the return |
| 88 | + * value should be 'unknown/unknown', which will override the Magic guess |
| 89 | + * (and give any other EXAMINE_UNKNOWN plugins a crack at it) |
| 90 | + * 2) Misidentify a file as of a type this plugin does not specialize in, when the file is in fact of a type this plugin can identify. This means that as part |
| 91 | + * of creating your plugin, you should determine if MimeMagic is known to |
| 92 | + * misidentify your target media as something else, and register the |
| 93 | + * plugin non-authoritatively for those type(s) as necessary. When registered non-authoritatively, returning unknown/unknown will not override actual identifications from MimeMagic. |
| 94 | + * In all cases, if this method returns any valid mime content-type string |
| 95 | + * other than unknown/unknown, all further processing stops and that string |
| 96 | + * will be the final type reported by MimeMagic. |
| 97 | + */ |
| 98 | + public abstract function getContentType($file); |
| 99 | + |
| 100 | + /** |
| 101 | + * @return string typesString The mime types and extensions supported by this |
| 102 | + * plugin, in the format of a mime.types file. (See MM_WELL_KNOWN_MIME_TYPES |
| 103 | + * in MimeMagic.php for an example.) |
| 104 | + */ |
| 105 | + protected abstract function mimeTypes(); |
| 106 | + |
| 107 | + /** |
| 108 | + * @return string infoString The mime types supported by this plugin and |
| 109 | + * their associated media types, in the format of a mime.info file. (See |
| 110 | + * MM_WELL_KNOWN_MIME_INFO for an example.) |
| 111 | + */ |
| 112 | + protected abstract function mimeInfo(); |
| 113 | + |
| 114 | + /** |
| 115 | + * Possibly classify $file as one of the MEDIATYPE constants. |
| 116 | + * |
| 117 | + * Usually there is no need for a plugin to explicitly declare this; |
| 118 | + * for most mime types MimeMagic can already figure it out. However, a few |
| 119 | + * types (the classic example being application/ogg) do not in themselves |
| 120 | + * necessitate one particular MEDIATYPE. If your plugin will be registered |
| 121 | + * for such a type and is capable of parsing the file's contents to make this |
| 122 | + * distinction, please override this method and do so. |
| 123 | + * |
| 124 | + * Because the assumption is that plugins are smarter than MimeMagic for the |
| 125 | + * specialized types they were written for, this method will get called for |
| 126 | + * *every* content-type the plugin is registered for, whether MimeMagic |
| 127 | + * thinks it could classify it or not. To delegate this responsibility back |
| 128 | + * to MimeMagic, just return false if $mime is not of a type that needs |
| 129 | + * special parsing of the file's contents. |
| 130 | + * |
| 131 | + * @param string $file the file to classify |
| 132 | + * @param string $mime MimeMagic's guess for $file (including help from |
| 133 | + * plugins) |
| 134 | + * @return mixed a MEDIATYPE constant, or false to let MimeMagic guess. |
| 135 | + */ |
| 136 | + public function getMediaType($file, $mime) |
| 137 | + { |
| 138 | + return false; |
| 139 | + } |
| 140 | +} |
| 141 | +?> |
\ No newline at end of file |
Property changes on: branches/mikeb/phase3/includes/media/MimePlugin.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 142 | + native |
Index: branches/mikeb/phase3/includes/AutoLoader.php |
— | — | @@ -266,13 +266,21 @@ |
267 | 267 | 'UnregisteredLocalFile' => 'includes/filerepo/UnregisteredLocalFile.php', |
268 | 268 | |
269 | 269 | # Media |
| 270 | + 'AudioHandler' => 'includes/media/AV.php', |
| 271 | + 'AudioUploadHandler' => 'includes/media/AV.php', |
270 | 272 | 'BitmapHandler' => 'includes/media/Bitmap.php', |
271 | 273 | 'BmpHandler' => 'includes/media/BMP.php', |
272 | 274 | 'DjVuHandler' => 'includes/media/DjVu.php', |
273 | 275 | 'MediaHandler' => 'includes/media/Generic.php', |
274 | 276 | 'ImageHandler' => 'includes/media/Generic.php', |
275 | 277 | 'SvgHandler' => 'includes/media/SVG.php', |
| 278 | + 'VideoHandler' => 'includes/media/AV.php', |
| 279 | + 'VideoUploadHandler' => 'includes/media/AV.php', |
276 | 280 | |
| 281 | + # MIME plugins |
| 282 | + 'AVMimePlugin' => 'includes/media/AV.php', |
| 283 | + 'MimePlugin' => 'includes/media/MimePlugin.php', |
| 284 | + |
277 | 285 | # Normal |
278 | 286 | 'UtfNormal' => 'includes/normal/UtfNormal.php', |
279 | 287 | |
Index: branches/mikeb/phase3/includes/DefaultSettings.php |
— | — | @@ -1557,6 +1557,8 @@ |
1558 | 1558 | 'image/x-ms-bmp' => 'BmpHandler', |
1559 | 1559 | 'image/svg+xml' => 'SvgHandler', |
1560 | 1560 | 'image/vnd.djvu' => 'DjVuHandler', |
| 1561 | + MEDIATYPE_VIDEO => 'VideoHandler', |
| 1562 | + MEDIATYPE_AUDIO => 'AudioHandler' |
1561 | 1563 | ); |
1562 | 1564 | |
1563 | 1565 | |
— | — | @@ -1646,7 +1648,19 @@ |
1647 | 1649 | /** Obsolete, always true, kept for compatibility with extensions */ |
1648 | 1650 | $wgUseImageResize = true; |
1649 | 1651 | |
| 1652 | +/** |
| 1653 | + * Available bitrates at which audio uploads should be streamable. |
| 1654 | + * Leave empty to skip making ogg vorbis recodes of audio uploads. |
| 1655 | + */ |
| 1656 | + $wgAudioRecoding = array(); |
1650 | 1657 | |
| 1658 | +/** |
| 1659 | +* Available widths in pixels for streamable video, and associated bitrates. |
| 1660 | +* Index is integer pixel width, value is integer bitrate. |
| 1661 | +* Leave empty to skip making ogg theora recodes of video uploads. |
| 1662 | +*/ |
| 1663 | + $wgVideoRecoding = array(320 => 500); |
| 1664 | + |
1651 | 1665 | /** Set $wgCommandLineMode if it's not set already, to avoid notices */ |
1652 | 1666 | if( !isset( $wgCommandLineMode ) ) { |
1653 | 1667 | $wgCommandLineMode = false; |