3a33a3d946a976bc781b22e60dbcb065a0eda895
3 * This core jsScriptLoader class provides the script loader functionality
6 // check if we are being invoked in MediaWiki context or stand alone usage:
7 if ( !defined( 'MEDIAWIKI' ) ){
8 // load noMediaWiki helper:
9 require_once( realpath( dirname( __FILE__
) ) . '/php/noMediaWikiConfig.php' );
11 // run the main action:
12 $myScriptLoader = new jsScriptLoader();
13 // preset request values via normal $_GET operation:
14 $myScriptLoader->doScriptLoader();
16 $wgExtensionMessagesFiles['mwEmbed'] = realpath( dirname( __FILE__
) ) . '/php/mwEmbed.i18n.php';
19 // setup page output hook
20 class jsScriptLoader
{
21 var $jsFileList = array();
23 var $rKey = ''; // the request key
26 var $jsvarurl = false; // if we should include generated js (special class '-')
27 var $doProcReqFlag = true;
29 function doScriptLoader(){
30 global $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgEnableScriptLoaderJsFile, $IP,
31 $wgEnableScriptMinify, $wgUseFileCache;
33 // process the request
34 $this->procRequestVars();
36 // if cache is on and file is present grab it from there:
37 if( $wgUseFileCache && !$this->debug
) {
38 // setup file cache obj:
39 $this->sFileCache
= new simpleFileCache( $this->rKey
);
40 if( $this->sFileCache
->isFileCached() ){
41 // just output headers so we can use php "efficient" readfile
42 $this->outputJsHeaders();
43 $this->sFileCache
->outputFromFileCache();
48 // setup script loader header info
49 $this->jsout
.= 'var mwSlScript = "' . $_SERVER['SCRIPT_NAME'] . '";' . "\n";
50 $this->jsout
.= 'var mwSlGenISODate = "' . date( 'c' ) . '";' ."\n";
51 $this->jsout
.= 'var mwSlURID = "' . $this->urid
. '";' ."\n";
53 // swap in the appropriate language per js_file
54 foreach( $this->jsFileList
as $classKey => $file_name ){
55 // special case: - title classes:
56 if( substr( $classKey, 0, 3 ) == 'WT:' ){
57 // get just the tile part:
58 $title_block = substr( $classKey, 3 );
59 if( $title_block[0] == '-' && strpos( $title_block, '|' ) !== false ){
60 // special case of "-" title with skin
61 $parts = explode( '|', $title_block );
62 $title = array_shift( $parts );
63 foreach( $parts as $tparam ){
64 list( $key, $val ) = explode( '=', $tparam );
65 if( $key == 'useskin' ){
69 // make sure the skin name is valid
70 $skinNames = Skin
::getSkinNames();
71 // get the lower case skin name (array keys)
72 $skinNames = array_keys( $skinNames );
73 if( in_array( strtolower( $skin ), $skinNames ) ){
74 $this->jsout
.= Skin
::generateUserJs( $skin ) . "\n";
79 // it's a wikiTitle append the output of the wikitext:
80 $t = Title
::newFromText( $title_block );
81 $a = new Article( $t );
82 // only get content if the page is not empty:
83 if( $a->getID() !== 0 ){
84 $this->jsout
.= $a->getContent() . "\n";
90 if( trim( $file_name ) != '' ){
91 // if in debug add a comment with the file name:
93 $this->jsout
.= "\n/**
96 $this->jsout
.= ( $this->doProccessJsFile( $file_name ) ) . "\n";
99 // check if we should minify :
100 if( $wgEnableScriptMinify && !$this->debug
){
101 // do the minification and output
102 $this->jsout
= JSMin
::minify( $this->jsout
);
104 // save to the file cache:
105 if( $wgUseFileCache && !$this->debug
) {
106 $status = $this->sFileCache
->saveToFileCache( $this->jsout
);
107 if( $status !== true )
108 $this->error_msg
.= $status;
110 // check for error msg:
111 if( $this->error_msg
!= ''){
112 echo 'alert(\'Error With ScriptLoader.php ::' . str_replace( "\n", '\'+"\n"+'."\n'", $this->error_msg
) . '\');';
113 echo trim( $this->jsout
);
115 // all good lets output cache forever headers:
116 $this->outputJsWithHeaders();
120 function outputJsHeaders(){
121 global $wgJsMimeType;
122 // output js mime type:
123 header( 'Content-type: ' . $wgJsMimeType );
124 header( 'Pragma: public' );
126 // (the point is we never have to revalidate since we should always change the request url based on the svn or article version)
127 $one_year = 60*60*24*365;
128 header( "Expires: " . gmdate( "D, d M Y H:i:s", time() +
$one_year ) . " GM" );
131 function outputJsWithHeaders(){
133 $this->outputJsHeaders();
135 if( wfClientAcceptsGzip() ) {
136 header( 'Content-Encoding: gzip' );
137 echo gzencode( $this->jsout
);
147 * updates the proc Request
149 function procRequestVars(){
150 global $wgContLanguageCode, $wgEnableScriptMinify, $wgJSAutoloadClasses, $wgJSAutoloadLocalClasses, $wgStyleVersion;
153 if( ( isset( $_GET['debug'] ) && $_GET['debug'] == 'true' ) ||
( isset( $wgEnableScriptDebug ) && $wgEnableScriptDebug == true ) ){
157 // set the urid: (be sure to escape it as it goes into our js output)
158 if( isset( $_GET['urid'] ) && $_GET['urid'] !=''){
159 $this->urid
= htmlspecialchars( $_GET['urid'] );
161 // just give it the current style sheet id:
162 // @@todo read the svn version number
163 $this->urid
= $wgStyleVersion;
166 $reqClassList = false;
167 if( isset( $_GET['class'] ) && $_GET['class'] != '' ){
168 $reqClassList = explode( ',', $_GET['class'] );
171 // check for the requested classes
173 // clean the class list and populate jsFileList
174 foreach( $reqClassList as $reqClass ){
175 if( trim( $reqClass ) != '' ){
176 // check for special case '-' class for user generated js
177 if( substr( $reqClass, 0, 3 ) == 'WT:' ){
178 $this->jsFileList
[$reqClass] = true;
179 $this->rKey
.= $reqClass;
180 $this->jsvarurl
= true;
184 $reqClass = ereg_replace("[^A-Za-z0-9_\-\.]", '', $reqClass );
186 if( isset( $wgJSAutoloadLocalClasses[$reqClass] ) ){
187 $this->jsFileList
[$reqClass] = $wgJSAutoloadLocalClasses[$reqClass];
188 $this->rKey
.= $reqClass;
189 } else if( isset( $wgJSAutoloadClasses[$reqClass] ) ) {
190 $this->jsFileList
[$reqClass] = $wgJSAutoloadClasses[$reqClass];
191 $this->rKey
.= $reqClass;
193 $this->error_msg
.= 'Requested class: ' . $reqClass . ' not found' . "\n";
199 // check for requested files if enabled:
200 if( $wgEnableScriptLoaderJsFile ){
201 if( isset( $_GET['files'] ) ){
202 $reqFileList = explode( ',', isset( $_GET['files'] ) );
203 // clean the file list and populate jsFileList
204 foreach( $reqFileList as $reqFile ){
206 $reqFile = str_replace( '../', '', $reqFile );
207 // only allow alphanumeric underscores periods and ending with .js
208 $reqFile = ereg_replace( "[^A-Za-z0-9_\-\/\.]", '', $reqFile );
209 if( substr( $reqFile, -3 ) == '.js' ){
210 // don't add it twice:
211 if( !in_array( $reqFile, $jsFileList ) ) {
212 $this->jsFileList
[] = $IP . $reqFile;
213 $this->rKey
.= $reqFile;
216 $this->error_msg
.= 'Not valid requsted JavaScript file' . "\n";
222 // add the language code to the rKey:
223 $this->rKey
.= '_' . $wgContLanguageCode;
225 // add the unique rid to the rKey
226 $this->rKey
.= $this->urid
;
229 if( $wgEnableScriptMinify ){
230 $this->rKey
.= '_min';
234 function doProccessJsFile( $file_name ){
235 global $IP, $wgEnableScriptLocalization, $IP;
238 $str = @file_get_contents
( "{$IP}/{$file_name}" );
240 if( $str === false ){
241 // @@todo check php error level (don't want to expose paths if errors are hidden)
242 $this->error_msg
.= 'Requested File: ' . htmlspecialchars( $file_name ) . ' could not be read' . "\n";
245 $this->cur_file
= $file_name;
247 // strip out js_log debug lines not much luck with this regExp yet:
248 //if( !$this->debug )
249 // $str = preg_replace('/\n\s*js_log\s*\([^\)]([^;]|\n])*;/', "\n", $str);
252 if( $wgEnableScriptLocalization )
253 $str = preg_replace_callback(
254 '/loadGM\s*\(\s*{(.*)}\s*\)\s*/siU', // @@todo fix: will break down if someone does }) in their msg text
255 array( $this, 'languageMsgReplace' ),
262 function languageMsgReplace( $jvar ){
263 if( !isset( $jvar[1] ) )
266 $jmsg = json_decode( '{' . $jvar[1] . '}', true );
267 // do the language lookup:
269 foreach( $jmsg as $msgKey => $default_en_value ){
270 $jmsg[$msgKey] = wfMsgNoTrans( $msgKey );
272 //return the updated loadGM json with fixed new lines:
273 return 'loadGM( ' . json_encode( $jmsg ) . ')';
275 $this->error_msg
.= "Could not parse JSON language msg in File:\n" .
276 $this->cur_file
. "\n";
278 // could not parse json (throw error?)
283 //a simple version of HTMLFileCache (@@todo abstract shared pieces)
284 class simpleFileCache
{
286 var $filename = null;
289 public function __construct( &$rKey ) {
291 $this->filename
= $this->fileCacheName(); // init name
294 public function fileCacheName() {
296 if( !$this->mFileCache
) {
297 global $wgFileCacheDirectory;
299 $hash = md5( $this->rKey
);
300 # Avoid extension confusion
301 $key = str_replace( '.', '%2E', urlencode( $this->rKey
) );
303 $hash1 = substr( $hash, 0, 1 );
304 $hash2 = substr( $hash, 0, 2 );
305 $this->mFileCache
= "{$wgFileCacheDirectory}/{$subdir}{$hash1}/{$hash2}/{$this->rKey}.js";
308 $this->mFileCache
.= '.gz';
310 wfDebug( " fileCacheName() - {$this->mFileCache}\n" );
312 return $this->mFileCache
;
315 public function isFileCached() {
316 return file_exists( $this->filename
);
319 public function outputFromFileCache(){
322 if( wfClientAcceptsGzip() ) {
323 header( 'Content-Encoding: gzip' );
324 readfile( $this->filename
);
326 /* Send uncompressed (check if fileCache is in compressed state (ends with .gz)
327 * (unlikely to execute this since $wgUseGzip would have created a new file above.. but just in case:
329 if( substr( $this->filename
, -3 ) == '.gz' ){
330 readgzfile( $this->filename
);
332 readfile( $this->filename
);
336 // just output the file
337 readfile( $this->filename
);
343 public function saveToFileCache( &$text ) {
344 global $wgUseFileCache, $wgUseGzip;
345 if( !$wgUseFileCache ) {
346 return 'Error: Called saveToFileCache with $wgUseFileCache off';
348 if( strcmp( $text, '' ) == 0 ) return 'saveToFileCache: empty output file';
350 // check the directories if we could not create them error out:
351 $status = $this->checkCacheDirs();
354 $outputText = gzencode( trim( $text ) );
356 $outputText = trim( $text );
359 if( $status !== true )
361 $f = fopen( $this->filename
, 'w' );
363 fwrite( $f, $outputText );
366 return 'Could not open file for writing. Check your cache directory permissions?';
371 protected function checkCacheDirs() {
372 $mydir2 = substr( $this->filename
, 0, strrpos( $this->filename
, '/' ) ); # subdirectory level 2
373 $mydir1 = substr( $mydir2, 0, strrpos( $mydir2, '/' ) ); # subdirectory level 1
375 if( wfMkdirParents( $mydir1 ) === false ||
wfMkdirParents( $mydir2 ) === false ){
376 return 'Could not create cache directory. Check your cache directory permissions?';