Index: NEWS.txt =================================================================== --- NEWS.txt (Revision 7262) +++ NEWS.txt (Arbeitskopie) @@ -18,6 +18,12 @@ 5. Color palettes become ExtJS ColorPalettes 6. The following dialogue windows become ExtJS windows: acronym/abbreviation, table/row/column/cell properties, foreground/background color, emoticon. + * Automatic version-numbers of CSS and JS files to avoid caching problems: This feature provides automatic numbering of CSS and JS files using the files modified timestamp. This way the file reference will change when a CSS or JS files is changed, and by this the browser and proxy will re-cache the file. Can be configured to include the timestamp within the the filename (before .ext) or as a parameter to the file (default). + If versioning is done inside the filename (by setting TYPO3_CONF_VARS[BE][versionNumberInFilename] to true) you need this line as the first rewrite rule in .htaccess: + # Rule for versioned static files (see TYPO3_CONF_VARS[BE][versionNumberInFilename]) + RewriteRule ^(.+)\.(\d+)\.(php|js|css|png|jpg|gif)$ $1.$3 [L] + Developers can use this API for versioning of files in the own backend mods, by calling t3lib_div::createVersionNumberedFilename or using the core API for including files in the page renderer class. + Backend ======= Index: t3lib/config_default.php =================================================================== --- t3lib/config_default.php (Revision 7262) +++ t3lib/config_default.php (Arbeitskopie) @@ -257,6 +257,7 @@ 'flexformForceCDATA' => 0, // Boolean: If set, will add CDATA to Flexform XML. Some versions of libxml have a bug that causes HTML entities to be stripped from any XML content and this setting will avoid the bug by adding CDATA. 'explicitConfirmationOfTranslation' => FALSE, // If set, then the diff-data of localized records is not saved automatically when updated but requires that a translator clicks the special finish_translation/save/close button that becomes available. 'elementVersioningOnly' => FALSE, // If true, only element versioning is allowed in the backend. This is recommended for new installations of TYPO3 4.2+ since "page" and "branch" versioning types are known for the drawbacks of loosing ids and "element" type versions supports moving now. + 'versionNumberInFilename' => FALSE, // Boolean. If true, included CSS and JS files will have the timestamp embedded in the filename, ie. filename.1269312081.js. This will make browsers and proxies reload the files if they change (thus avoiding caching issues). IMPORTANT: this feature requires this .htaccess rule to work: RewriteRule ^(.+)\.(\d+)\.(php|js|css|png|jpg|gif)$ $1.$3 [L]. If false the filemtime will be appended as a query-string. 'AJAX' => array( // array of key-value pairs for a unified use of AJAX calls in the TYPO3 backend. Keys are the unique ajaxIDs where the value will be resolved to call a method in an object. See ajax.php and the classes/class.typo3ajax.php for more information. 'SC_alt_db_navframe::expandCollapse' => 'typo3/alt_db_navframe.php:SC_alt_db_navframe->ajaxExpandCollapse', 'SC_alt_file_navframe::expandCollapse' => 'typo3/alt_file_navframe.php:SC_alt_file_navframe->ajaxExpandCollapse', @@ -337,6 +338,7 @@ 'eID_include' => array(), // Array of key/value pairs where key is "tx_[ext]_[optional suffix]" and value is relative filename of class to include. Key is used as "?eID=" for index_ts.php to include the code file which renders the page from that point. (Useful for functionality that requires a low initialization footprint, eg. frontend ajax applications) 'disableNoCacheParameter' => FALSE, // Boolean. If set, the no_cache request parameter will become ineffective. This is currently still an experimental feature and will require a website only with plugins that don't use this parameter. However, using "&no_cache=1" should be avoided anyway because there are better ways to disable caching for a certain part of the website (see COA_INT/USER_INT documentation in TSref). 'workspacePreviewLogoutTemplate' => '', // If set, points to an HTML file relative to the TYPO3_site root which will be read and outputted as template for this message. Example: fileadmin/templates/template_workspace_preview_logout.html. Inside you can put the marker %1$s to insert the URL to go back to. Use this in Go back... links + 'versionNumberInFilename' => '', // String. Set this to include a timestamp based version to added CSS and JS filenames on the rendered page. Set to 'embed' will have the timestamp embedded in the filename, ie. filename.1269312081.js. This will make browsers and proxies reload the files if they change (thus avoiding caching issues). IMPORTANT: 'embed' requires this .htaccess rule to work: RewriteRule ^(.+)\.(\d+)\.(php|js|css|png|jpg|gif)$ $1.$3 [L]. If 'querystring' the filemtime will be appended as a query-string. Default setting of '' will turn off this functionality. 'XCLASS' => array(), // See 'Inside TYPO3' document for more information. ), 'MODS' => array( // Backend Module Configuration (obsolete, make extension instead) Index: t3lib/class.t3lib_div.php =================================================================== --- t3lib/class.t3lib_div.php (Revision 7262) +++ t3lib/class.t3lib_div.php (Arbeitskopie) @@ -3343,16 +3343,77 @@ } + /** + * Function for static version numbers on files, based on the filemtime + * + * This will make the filename automatically change when a file is + * changed, and by that re-cached by the browser. If the file does not + * exist physically the original file passed to the function is + * returned without the timestamp. + * + * Behaviour is influenced by the setting + * TYPO3_CONF_VARS[TYPO3_MODE][versionNumberInFilename] + * = true (BE) / "embed" (FE) : modify filename + * = false (BE) / "queryString" (FE) : add timestamp as parameter + * + * @param string $file Relative path to file including all potential query parameters (not htmlspecialchared yet) + * @param boolean $forceQueryString If settings would suggest to embed in filename, this parameter allows us to force the versioning to occur in the query string. This is needed for scriptaculous.js which cannot have a different filename in order to load its modules (?load=...) + * @return Relative path with version filename including the timestamp + * @author Lars Houmark + */ + public static function createVersionNumberedFilename($file, $forceQueryString = FALSE) { + $lookupFile = explode('?', $file); + $path = self::resolveBackPath(self::dirname(PATH_thisScript) .'/'. $lookupFile[0]); + if (TYPO3_MODE == 'FE') { + $mode = strtolower($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename']); + if ($mode === 'embed') { + $mode = TRUE; + } else if ($mode === 'querystring') { + $mode = FALSE; + } else { + $doNothing = TRUE; + } + } else { + $mode = $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['versionNumberInFilename']; + } + if (! file_exists($path) || $doNothing) { + // File not found, return filename unaltered + $fullName = $file; + } else if (! $mode || $forceQueryString) { + // If use of .htaccess rule is not configured, + // we use the default query-string method + if ($lookupFile[1]) { + $separator = '&'; + } else { + $separator = '?'; + } + $fullName = $file . $separator . filemtime($path); + } else { + // Change the filename + $name = explode('.', $lookupFile[0]); + $extension = array_pop($name); + + array_push($name, filemtime($path), $extension); + $fullName = implode('.', $name); + // append potential query string + $fullName .= $lookupFile[1] ? '?' . $lookupFile[1] : ''; + } + return $fullName; + } + + + + /************************* * * DEBUG helper FUNCTIONS Index: t3lib/class.t3lib_pagerenderer.php =================================================================== --- t3lib/class.t3lib_pagerenderer.php (Revision 7262) +++ t3lib/class.t3lib_pagerenderer.php (Arbeitskopie) @@ -1000,8 +1000,9 @@ if (count($this->cssFiles)) { foreach ($this->cssFiles as $file => $properties) { - $file = htmlspecialchars(t3lib_div::resolveBackPath($file)); - $tag = ''; + $file = t3lib_div::resolveBackPath($file); + $file = t3lib_div::createVersionNumberedFilename($file); + $tag = ''; if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) { $tag = str_replace('|', $tag, $properties['allWrap']); } @@ -1028,10 +1029,9 @@ if (count($this->jsLibs)) { foreach ($this->jsLibs as $name => $properties) { - $properties['file'] = htmlspecialchars( - t3lib_div::resolveBackPath($properties['file']) - ); - $tag = ''; + $properties['file'] = t3lib_div::resolveBackPath($properties['file']); + $properties['file'] = t3lib_div::createVersionNumberedFilename($properties['file']); + $tag = ''; if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) { $tag = str_replace('|', $tag, $properties['allWrap']); } @@ -1054,8 +1054,9 @@ if (count($this->jsFiles)) { foreach ($this->jsFiles as $file => $properties) { - $file = htmlspecialchars(t3lib_div::resolveBackPath($file)); - $tag = ''; + $file = t3lib_div::resolveBackPath($file); + $file = t3lib_div::createVersionNumberedFilename($file); + $tag = ''; if ($properties['allWrap'] && strpos($properties['allWrap'], '|') !== FALSE) { $tag = str_replace('|', $tag, $properties['allWrap']); } @@ -1162,7 +1163,7 @@ $out = ''; if ($this->addPrototype) { - $out .= '' . chr(10); + $out .= '' . chr(10); unset($this->jsFiles[$this->backPath . 'contrib/prototype/prototype.js']); } @@ -1181,22 +1182,21 @@ if (count($mods)) { $moduleLoadString = '?load=' . implode(',', $mods); } - - $out .= '' . chr(10); + $out .= '' . chr(10); unset($this->jsFiles[$this->backPath . 'contrib/scriptaculous/scriptaculous.js' . $moduleLoadString]); } // include extCore if ($this->addExtCore) { - $out .= '' . chr(10); + $out .= '' . chr(10); unset($this->jsFiles[$this->backPath . 'contrib/extjs/ext-core' . ($this->enableExtCoreDebug ? '-debug' : '') . '.js']); } // include extJS if ($this->addExtJS) { // use the base adapter all the time - $out .= '' . chr(10); - $out .= '' . chr(10); + $out .= '' . chr(10); + $out .= '' . chr(10); // add extJS localization $localeMap = $this->csConvObj->isoArray; // load standard ISO mapping and modify for use with ExtJS @@ -1211,7 +1211,7 @@ // TODO autoconvert file from UTF8 to current BE charset if necessary!!!! $extJsLocaleFile = 'contrib/extjs/locale/ext-lang-' . $extJsLang . '.js'; if (file_exists(PATH_typo3 . $extJsLocaleFile)) { - $out .= '' . chr(10); + $out .= '' . chr(10); } Index: typo3/alt_main.php =================================================================== --- typo3/alt_main.php (Revision 7262) +++ typo3/alt_main.php (Arbeitskopie) @@ -498,8 +498,8 @@ $this->generateJScode(); $GLOBALS['TBE_TEMPLATE']->JScode= ' - - + + '; $GLOBALS['TBE_TEMPLATE']->JScode.=$GLOBALS['TBE_TEMPLATE']->wrapScriptTags($this->mainJScode); Index: typo3/sysext/t3editor/classes/class.tx_t3editor.php =================================================================== --- typo3/sysext/t3editor/classes/class.tx_t3editor.php (Revision 7262) +++ typo3/sysext/t3editor/classes/class.tx_t3editor.php (Arbeitskopie) @@ -126,9 +126,9 @@ // include editor-css $content .= ''; // include editor-js-lib Index: typo3/sysext/cms/tslib/class.tslib_content.php =================================================================== --- typo3/sysext/cms/tslib/class.tslib_content.php (Revision 7262) +++ typo3/sysext/cms/tslib/class.tslib_content.php (Arbeitskopie) @@ -2205,7 +2205,7 @@ if ($conf['REQ']) { $validateForm=' onsubmit="return validateForm(\''.$formname.'\',\''.implode(',',$fieldlist).'\','.t3lib_div::quoteJSvalue($conf['goodMess']).','.t3lib_div::quoteJSvalue($conf['badMess']).','.t3lib_div::quoteJSvalue($conf['emailMess']).')"'; - $GLOBALS['TSFE']->additionalHeaderData['JSFormValidate'] = ''; + $GLOBALS['TSFE']->additionalHeaderData['JSFormValidate'] = ''; } else $validateForm=''; // Create form tag: @@ -6989,7 +6989,7 @@ /*]]>*/ '; - $GLOBALS['TSFE']->additionalHeaderData['JSincludeFormupdate']=''; + $GLOBALS['TSFE']->additionalHeaderData['JSincludeFormupdate']=''; return $JSPart; } Index: typo3/sysext/cms/layout/db_layout.php =================================================================== --- typo3/sysext/cms/layout/db_layout.php (Revision 7262) +++ typo3/sysext/cms/layout/db_layout.php (Arbeitskopie) @@ -426,7 +426,7 @@ $this->doc->setModuleTemplate('templates/db_layout.html'); // JavaScript: - $this->doc->JScode = ''; + $this->doc->JScode = ''; $this->doc->JScode.= $this->doc->wrapScriptTags(' if (top.fsMod) top.fsMod.recentIds["web"] = '.intval($this->id).'; if (top.fsMod) top.fsMod.navFrameHighlightedID["web"] = "pages'.intval($this->id).'_"+top.fsMod.currentBank; '.intval($this->id).'; Index: typo3/sysext/recycler/mod1/index.php =================================================================== --- typo3/sysext/recycler/mod1/index.php (Revision 7262) +++ typo3/sysext/recycler/mod1/index.php (Arbeitskopie) @@ -158,7 +158,7 @@ */ protected function loadStylesheet($fileName) { $fileName = t3lib_div::resolveBackPath($this->doc->backPath . $fileName); - $this->doc->JScode.= "\t" . '' . "\n"; + $this->doc->JScode.= "\t" . '' . "\n"; } /** @@ -169,7 +169,7 @@ */ protected function loadJavaScript($fileName) { $fileName = t3lib_div::resolveBackPath($this->doc->backPath . $fileName); - $this->doc->JScode.= "\t" . '' . "\n"; + $this->doc->JScode.= "\t" . '' . "\n"; } /**