MediaWiki  REL1_24
ResourceLoaderWikiModule.php
Go to the documentation of this file.
00001 <?php
00032 abstract class ResourceLoaderWikiModule extends ResourceLoaderModule {
00033 
00034     /* Protected Members */
00035 
00036     # Origin is user-supplied code
00037     protected $origin = self::ORIGIN_USER_SITEWIDE;
00038 
00039     // In-object cache for title info
00040     protected $titleInfo = array();
00041 
00042     /* Abstract Protected Methods */
00043 
00060     abstract protected function getPages( ResourceLoaderContext $context );
00061 
00062     /* Protected Methods */
00063 
00075     protected function getDB() {
00076         return wfGetDB( DB_SLAVE );
00077     }
00078 
00083     protected function getContent( $title ) {
00084         if ( !$title->isCssJsSubpage() && !$title->isCssOrJsPage() ) {
00085             return null;
00086         }
00087         $revision = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
00088         if ( !$revision ) {
00089             return null;
00090         }
00091 
00092         $content = $revision->getContent( Revision::RAW );
00093 
00094         if ( !$content ) {
00095             wfDebugLog( 'resourceloader', __METHOD__ . ': failed to load content of JS/CSS page!' );
00096             return null;
00097         }
00098 
00099         if ( $content->isSupportedFormat( CONTENT_FORMAT_JAVASCRIPT ) ) {
00100             return $content->serialize( CONTENT_FORMAT_JAVASCRIPT );
00101         } elseif ( $content->isSupportedFormat( CONTENT_FORMAT_CSS ) ) {
00102             return $content->serialize( CONTENT_FORMAT_CSS );
00103         } else {
00104             wfDebugLog( 'resourceloader', __METHOD__ . ": bad content model {$content->getModel()} for JS/CSS page!" );
00105             return null;
00106         }
00107     }
00108 
00109     /* Methods */
00110 
00115     public function getScript( ResourceLoaderContext $context ) {
00116         $scripts = '';
00117         foreach ( $this->getPages( $context ) as $titleText => $options ) {
00118             if ( $options['type'] !== 'script' ) {
00119                 continue;
00120             }
00121             $title = Title::newFromText( $titleText );
00122             if ( !$title || $title->isRedirect() ) {
00123                 continue;
00124             }
00125             $script = $this->getContent( $title );
00126             if ( strval( $script ) !== '' ) {
00127                 $script = $this->validateScriptFile( $titleText, $script );
00128                 $scripts .= ResourceLoader::makeComment( $titleText ) . $script . "\n";
00129             }
00130         }
00131         return $scripts;
00132     }
00133 
00138     public function getStyles( ResourceLoaderContext $context ) {
00139         $styles = array();
00140         foreach ( $this->getPages( $context ) as $titleText => $options ) {
00141             if ( $options['type'] !== 'style' ) {
00142                 continue;
00143             }
00144             $title = Title::newFromText( $titleText );
00145             if ( !$title || $title->isRedirect() ) {
00146                 continue;
00147             }
00148             $media = isset( $options['media'] ) ? $options['media'] : 'all';
00149             $style = $this->getContent( $title );
00150             if ( strval( $style ) === '' ) {
00151                 continue;
00152             }
00153             if ( $this->getFlip( $context ) ) {
00154                 $style = CSSJanus::transform( $style, true, false );
00155             }
00156             $style = CSSMin::remap( $style, false, $this->getConfig()->get( 'ScriptPath' ), true );
00157             if ( !isset( $styles[$media] ) ) {
00158                 $styles[$media] = array();
00159             }
00160             $style = ResourceLoader::makeComment( $titleText ) . $style;
00161             $styles[$media][] = $style;
00162         }
00163         return $styles;
00164     }
00165 
00170     public function getModifiedTime( ResourceLoaderContext $context ) {
00171         $modifiedTime = 1; // wfTimestamp() interprets 0 as "now"
00172         $titleInfo = $this->getTitleInfo( $context );
00173         if ( count( $titleInfo ) ) {
00174             $mtimes = array_map( function( $value ) {
00175                 return $value['timestamp'];
00176             }, $titleInfo );
00177             $modifiedTime = max( $modifiedTime, max( $mtimes ) );
00178         }
00179         $modifiedTime = max(
00180             $modifiedTime,
00181             $this->getMsgBlobMtime( $context->getLanguage() ),
00182             $this->getDefinitionMtime( $context )
00183         );
00184         return $modifiedTime;
00185     }
00186 
00193     public function getDefinitionSummary( ResourceLoaderContext $context ) {
00194         return array(
00195             'class' => get_class( $this ),
00196             'pages' => $this->getPages( $context ),
00197         );
00198     }
00199 
00204     public function isKnownEmpty( ResourceLoaderContext $context ) {
00205         $titleInfo = $this->getTitleInfo( $context );
00206         // Bug 68488: For modules in the "user" group, we should actually
00207         // check that the pages are empty (page_len == 0), but for other
00208         // groups, just check the pages exist so that we don't end up
00209         // caching temporarily-blank pages without the appropriate
00210         // <script> or <link> tag.
00211         if ( $this->getGroup() !== 'user' ) {
00212             return count( $titleInfo ) === 0;
00213         }
00214 
00215         foreach ( $titleInfo as $info ) {
00216             if ( $info['length'] !== 0 ) {
00217                 // At least one non-0-lenth page, not empty
00218                 return false;
00219             }
00220         }
00221 
00222         // All pages are 0-length, so it's empty
00223         return true;
00224     }
00225 
00233     protected function getTitleInfo( ResourceLoaderContext $context ) {
00234         $dbr = $this->getDB();
00235         if ( !$dbr ) {
00236             // We're dealing with a subclass that doesn't have a DB
00237             return array();
00238         }
00239 
00240         $hash = $context->getHash();
00241         if ( isset( $this->titleInfo[$hash] ) ) {
00242             return $this->titleInfo[$hash];
00243         }
00244 
00245         $this->titleInfo[$hash] = array();
00246         $batch = new LinkBatch;
00247         foreach ( $this->getPages( $context ) as $titleText => $options ) {
00248             $batch->addObj( Title::newFromText( $titleText ) );
00249         }
00250 
00251         if ( !$batch->isEmpty() ) {
00252             $res = $dbr->select( 'page',
00253                 array( 'page_namespace', 'page_title', 'page_touched', 'page_len' ),
00254                 $batch->constructSet( 'page', $dbr ),
00255                 __METHOD__
00256             );
00257             foreach ( $res as $row ) {
00258                 $title = Title::makeTitle( $row->page_namespace, $row->page_title );
00259                 $this->titleInfo[$hash][$title->getPrefixedDBkey()] = array(
00260                     'timestamp' => wfTimestamp( TS_UNIX, $row->page_touched ),
00261                     'length' => $row->page_len,
00262                 );
00263             }
00264         }
00265         return $this->titleInfo[$hash];
00266     }
00267 }