MediaWiki
REL1_24
|
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 }