Merge pull request #13839 from nijel/scripts

Remove get_scripts wrapper to download javascript
This commit is contained in:
Michal Čihař 2017-11-29 17:05:44 +01:00 committed by GitHub
commit 7ec4059dbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 56 additions and 282 deletions

2
.gitignore vendored
View File

@ -32,8 +32,6 @@ web.config
/twig-templates/
# Backups
*~
# Javascript line counts
/js/line_counts.php
# API documentation
/apidoc/
/doc/linkcheck/

View File

@ -686,7 +686,7 @@ A list of files and corresponding functionality which degrade gracefully when re
* :file:`./locale/` folder, or unused subfolders (interface translations)
* Any unused themes in :file:`./themes/`
* :file:`./js/vendor/jquery/src/` (included for licensing reasons)
* :file:`./js/line_counts.php`
* :file:`./js/line_counts.php` (removed in phpMyAdmin 4.8)
* :file:`./doc/` (documentation)
* :file:`./setup/` (setup script)
* :file:`./examples/`

View File

@ -534,9 +534,10 @@ var AJAX = {
_scriptsToBeLoaded: [],
/**
* @var array _scriptsToBeFired The list of files for which
* to fire the onload event
* to fire the onload and unload events
*/
_scriptsToBeFired: [],
_scriptsCompleted: false,
/**
* Records that a file has been downloaded
*
@ -572,7 +573,7 @@ var AJAX = {
self._scripts = [];
self._scriptsVersion = PMA_commonParams.get('PMA_VERSION');
}
self._scriptsToBeLoaded = [];
self._scriptsCompleted = false;
self._scriptsToBeFired = [];
for (var i in files) {
self._scriptsToBeLoaded.push(files[i].name);
@ -580,64 +581,60 @@ var AJAX = {
self._scriptsToBeFired.push(files[i].name);
}
}
// Generate a request string
var request = [];
var needRequest = false;
for (var index in self._scriptsToBeLoaded) {
var script = self._scriptsToBeLoaded[index];
for (var i in files) {
var script = files[i].name;
// Only for scripts that we don't already have
if ($.inArray(script, self._scripts) === -1) {
needRequest = true;
this.add(script);
request.push('scripts%5B%5D=' + script);
if (request.length >= 10) {
// Download scripts in chunks
this.appendScript(request);
request = [];
needRequest = false;
}
this.appendScript(script, callback);
} else {
self.done(script, callback);
}
}
request.push('call_done=1');
request.push('v=' + encodeURIComponent(PMA_commonParams.get('PMA_VERSION')));
// Download the composite js file, if necessary
if (needRequest) {
this.appendScript(request);
} else {
self.done(callback);
}
// Trigger callback if there is nothing to load
self.done(null, callback);
},
/**
* Called whenever all files are loaded
*
* @return void
*/
done: function (callback) {
if ($.isFunction(callback)) {
callback();
}
done: function (script, callback) {
if (typeof ErrorReport !== 'undefined') {
ErrorReport.wrap_global_functions();
}
for (var i in this._scriptsToBeFired) {
AJAX.fireOnload(this._scriptsToBeFired[i]);
if ($.inArray(script, this._scriptsToBeFired)) {
AJAX.fireOnload(script);
}
if ($.inArray(script, this._scriptsToBeLoaded)) {
this._scriptsToBeLoaded.splice($.inArray(script, this._scriptsToBeLoaded), 1);
}
if (script === null) {
this._scriptsCompleted = true;
}
/* We need to wait for last signal (with null) or last script load */
AJAX.active = (this._scriptsToBeLoaded.length > 0) || ! this._scriptsCompleted;
/* Run callback on last script */
if (! AJAX.active && $.isFunction(callback)) {
callback();
}
AJAX.active = false;
},
/**
* Appends a script element to the head to load the scripts
*
* @return void
*/
appendScript: function (request) {
appendScript: function (name, callback) {
var head = document.head || document.getElementsByTagName('head')[0];
var script = document.createElement('script');
var self = this;
request.push('call_done=1');
request.push('v=' + encodeURIComponent(PMA_commonParams.get('PMA_VERSION')));
script.type = 'text/javascript';
script.src = 'js/get_scripts.js.php?' + request.join('&');
script.src = 'js/' + name + '?' + 'v=' + encodeURIComponent(PMA_commonParams.get('PMA_VERSION'));
script.async = false;
script.onload = function () {
self.done(name, callback);
};
head.appendChild(script);
},
/**

View File

@ -1,72 +0,0 @@
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* Concatenates several js files to reduce the number of
* http requests sent to the server
*
* @package PhpMyAdmin
*/
if (! isset($_GET['scripts'])) {
die('Missing parameter');
}
if (!defined('TESTSUITE')) {
chdir('..');
// Close session early as we won't write anything there
session_write_close();
// Send correct type
header('Content-Type: text/javascript; charset=UTF-8');
// Enable browser cache for 1 hour
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 3600) . ' GMT');
// When a token is not presented, even though whitelisted arrays are removed
// in PMA_removeRequestVars(). This is a workaround for that.
$_GET['scripts'] = json_encode($_GET['scripts']);
// Avoid loading the full common.inc.php because this would add many
// non-js-compatible stuff like DOCTYPE
define('PMA_MINIMUM_COMMON', true);
define('PMA_PATH_TO_BASEDIR', '../');
require_once './libraries/common.inc.php';
}
$buffer = PhpMyAdmin\OutputBuffering::getInstance();
$buffer->start();
if (!defined('TESTSUITE')) {
register_shutdown_function(
function () {
echo PhpMyAdmin\OutputBuffering::getInstance()->getContents();
}
);
}
$_GET['scripts'] = json_decode($_GET['scripts']);
if (! empty($_GET['scripts']) && is_array($_GET['scripts'])) {
// Only up to 10 scripts as this is what we generate
foreach (array_slice($_GET['scripts'], 0, 10) as $script) {
// Sanitise filename
$script_name = 'js';
$path = explode("/", $script);
foreach ($path as $filename) {
// Allow alphanumeric, "." and "-" chars only, no files starting
// with .
if (preg_match("@^[\w][\w\.-]+$@", $filename)) {
$script_name .= DIRECTORY_SEPARATOR . $filename;
}
}
// Output file contents
if (preg_match("@\.js$@", $script_name) && is_readable($script_name)) {
readfile($script_name);
echo ";\n\n";
}
}
}
if (isset($_GET['call_done'])) {
echo "AJAX.scriptHandler.done();";
}

View File

@ -119,6 +119,7 @@ class DatabaseStructureController extends DatabaseController
array(
'db_structure.js',
'tbl_change.js',
'vendor/jquery/jquery.validate.js',
'vendor/jquery/jquery-ui-timepicker-addon.js'
)
);

View File

@ -201,6 +201,7 @@ class TableSearchController extends TableController
'sql.js',
'tbl_select.js',
'tbl_change.js',
'vendor/jquery/jquery.validate.js',
'vendor/jquery/jquery-ui-timepicker-addon.js',
'vendor/jquery/jquery.uitablefilter.js',
'gis_data_editor.js',
@ -239,6 +240,7 @@ class TableSearchController extends TableController
'vendor/jqplot/plugins/jqplot.highlighter.js',
'vendor/jqplot/plugins/jqplot.cursor.js',
'vendor/jquery/jquery-ui-timepicker-addon.js',
'vendor/jquery/jquery.validate.js',
'tbl_zoom_plot_jqplot.js',
'tbl_change.js',
)

View File

@ -188,89 +188,6 @@ class ErrorReport
return $response;
}
/**
* Returns number of lines in given javascript file.
*
* @param string $filename javascript filename
*
* @return Number of lines
*
* @todo Should gracefully handle non existing files
*/
public static function countLines($filename)
{
/**
* The generated file that contains the line numbers for the js files
* If you change any of the js files you can run the scripts/line-counts.sh
*/
if (is_readable('js/line_counts.php')) {
include_once 'js/line_counts.php';
}
global $LINE_COUNT;
if (defined('LINE_COUNTS')) {
return $LINE_COUNT[$filename];
}
// ensure that the file is inside the phpMyAdmin folder
$depath = 1;
foreach (explode('/', $filename) as $part) {
if ($part == '..') {
$depath--;
} elseif ($part != '.' || $part === '') {
$depath++;
}
if ($depath < 0) {
return 0;
}
}
$linecount = 0;
$handle = fopen('./js/' . $filename, 'r');
while (!feof($handle)) {
$line = fgets($handle);
if ($line === false) {
break;
}
$linecount++;
}
fclose($handle);
return $linecount;
}
/**
* returns the translated line number and the file name from the cumulative line
* number and an array of files
*
* uses the $LINE_COUNT global array of file names and line numbers
*
* @param array $filenames list of files in order of concatenation
* @param Integer $cumulative_number the cumulative line number in the
* concatenated files
*
* @return array the filename and line number
* Returns two variables in an array:
* - A String $filename the filename where the requested cumulative number
* exists
* - Integer $linenumber the translated line number in the returned file
*/
public static function getLineNumber(array $filenames, $cumulative_number)
{
$cumulative_sum = 0;
foreach ($filenames as $filename) {
$filecount = self::countLines($filename);
if ($cumulative_number <= $cumulative_sum + $filecount + 2) {
$linenumber = $cumulative_number - $cumulative_sum;
break;
}
$cumulative_sum += $filecount + 2;
}
if (! isset($filename)) {
$filename = '';
}
return array($filename, $linenumber);
}
/**
* translates the cumulative line numbers in the stack trace as well as sanitize
* urls and trim long lines in the context
@ -287,19 +204,10 @@ class ErrorReport
$line = mb_substr($line, 0, 75) . "//...";
}
}
if (preg_match("<js/get_scripts.js.php\?(.*)>", $level["url"], $matches)) {
parse_str($matches[1], $vars);
List($file_name, $line_number) = self::getLineNumber(
$vars["scripts"], $level["line"]
);
$level["filename"] = $file_name;
$level["line"] = $line_number;
} else {
unset($level["context"]);
List($uri, $script_name) = self::sanitizeUrl($level["url"]);
$level["uri"] = $uri;
$level["scriptname"] = $script_name;
}
unset($level["context"]);
List($uri, $script_name) = self::sanitizeUrl($level["url"]);
$level["uri"] = $uri;
$level["scriptname"] = $script_name;
unset($level["url"]);
}
unset($level);

View File

@ -184,7 +184,7 @@ class Header
// Here would not be a good place to add CodeMirror because
// the user preferences have not been merged at this point
$this->_scripts->addFile('messages.php', false, array('l' => $GLOBALS['lang']));
$this->_scripts->addFile('messages.php', array('l' => $GLOBALS['lang']));
// Append the theme id to this url to invalidate
// the cache on a theme change. Though this might be
// unavailable for fatal errors.

View File

@ -1871,6 +1871,9 @@ class InsertEdit
$response = Response::getInstance();
$header = $response->getHeader();
$scripts = $header->getScripts();
$scripts->addFile('vendor/jquery/jquery-ui-timepicker-addon.js');
$scripts->addFile('vendor/jquery/jquery.validate.js');
$scripts->addFile('vendor/jquery/additional-methods.js');
$scripts->addFile('tbl_change.js');
if (!defined('TESTSUITE')) {
include 'tbl_change.php';

View File

@ -44,42 +44,19 @@ class Scripts
*/
private function _includeFiles(array $files)
{
$first = [];
$result = [];
$scripts = array();
$separator = Url::getArgSeparator();
$result = '';
foreach ($files as $value) {
if (strpos($value['filename'], ".php") !== false) {
$file_name = $value['filename'] . Url::getCommon($value['params'] + array('v' => PMA_VERSION));
if ($value['before_statics'] === true) {
$first[]
= "<script data-cfasync='false' type='text/javascript' "
. "src='js/" . $file_name . "'></script>";
} else {
$result[] = "<script data-cfasync='false' "
. "type='text/javascript' src='js/" . $file_name
. "'></script>";
}
$result .= "<script data-cfasync='false' "
. "type='text/javascript' src='js/" . $file_name
. "'></script>\n";
} else {
$scripts[] = "scripts%5B%5D=" . $value['filename'];
$result .= '<script data-cfasync="false" type="text/javascript" src="js/'
. $value['filename'] . '?' . Header::getVersionParameter() . '"></script>' . "\n";
}
}
$separator = Url::getArgSeparator();
// Using chunks of 10 files to avoid too long URLs
// as some servers are set to 512 bytes URL limit
$script_chunks = array_chunk($scripts, 10);
foreach ($script_chunks as $script_chunk) {
$url = 'js/get_scripts.js.php?'
. implode($separator, $script_chunk)
. $separator . Header::getVersionParameter();
$result[] = sprintf(
'<script data-cfasync="false" type="text/javascript" src="%s">' .
'</script>',
htmlspecialchars($url)
);
}
return implode("\n", $first) . implode("\n", $result);
return $result;
}
/**
@ -97,15 +74,12 @@ class Scripts
* Adds a new file to the list of scripts
*
* @param string $filename The name of the file to include
* @param bool $before_statics Whether this dynamic script should be
* included before the static ones
* @param array $params Additional parameters to pass to the file
*
* @return void
*/
public function addFile(
$filename,
$before_statics = false,
array $params = array()
) {
$hash = md5($filename);
@ -118,7 +92,6 @@ class Scripts
'has_onload' => $has_onload,
'filename' => $filename,
'params' => $params,
'before_statics' => $before_statics
);
}

View File

@ -1,33 +0,0 @@
#!/bin/bash
# Do not run as CGI
if [ -n "$GATEWAY_INTERFACE" ] ; then
echo 'Can not invoke as CGI!'
exit 1
fi
cat > js/line_counts.php <<EOF
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* An autogenerated file that stores the line counts of javascript files
*
* @package PhpMyAdmin
*/
if (! defined('PHPMYADMIN')) {
exit;
}
define('LINE_COUNTS', true);
\$LINE_COUNT = array();
EOF
php_code=""
for file in `find js -name '*.js'` ; do
lc=`wc -l $file | sed 's/\([0-9]*\).*/\1/'`
file=${file:3}
entry="\$LINE_COUNT['$file'] = $lc;"
php_code="$php_code\n$entry"
done
echo -e $php_code >> js/line_counts.php

View File

@ -454,6 +454,9 @@ if (! empty($return_to_sql_query)) {
$GLOBALS['sql_query'] = $return_to_sql_query;
}
$scripts->addFile('vendor/jquery/jquery-ui-timepicker-addon.js');
$scripts->addFile('vendor/jquery/jquery.validate.js');
$scripts->addFile('vendor/jquery/additional-methods.js');
$scripts->addFile('tbl_change.js');
$active_page = $goto_include;

View File

@ -1842,7 +1842,7 @@ class InsertEditTest extends TestCase
->setMethods(array('addFile'))
->getMock();
$scriptsMock->expects($this->once())
$scriptsMock->expects($this->exactly(4))
->method('addFile');
$headerMock = $this->getMockBuilder('PhpMyAdmin\Header')

View File

@ -77,8 +77,7 @@ class ScriptsTest extends PmaTestCase
{
$this->assertEquals(
'<script data-cfasync="false" type="text/javascript" '
. 'src="js/get_scripts.js.php?'
. 'scripts%5B%5D=common.js&amp;v=' . PMA_VERSION . '"></script>',
. 'src="js/common.js?v=' . PMA_VERSION . '"></script>' . "\n",
$this->_callPrivateFunction(
'_includeFiles',
array(
@ -105,8 +104,7 @@ class ScriptsTest extends PmaTestCase
$this->assertRegExp(
'@<script data-cfasync="false" type="text/javascript" '
. 'src="js/get_scripts.js.php\\?'
. 'scripts%5B%5D=common.js&amp;v=' . PMA_VERSION . '"></script>'
. 'src="js/common.js\?v=' . PMA_VERSION . '"></script>' . "\n"
. '<script data-cfasync="false" type="text/'
. 'javascript">// <!\\[CDATA\\[' . "\n"
. 'AJAX.scriptHandler.add\\("common.js",1\\);' . "\n"
@ -180,7 +178,6 @@ $(function() {});
$hash => array(
'has_onload' => 1,
'filename' => 'common.js',
'before_statics' => false,
'params' => array(),
)
);
@ -209,13 +206,11 @@ $(function() {});
'd7716810d825f4b55d18727c3ccb24e6' => array(
'has_onload' => 1,
'filename' => 'common.js',
'before_statics' => false,
'params' => array(),
),
'347a57484fcd6ea6d8a125e6e1d31f78' => array(
'has_onload' => 1,
'filename' => 'sql.js',
'before_statics' => false,
'params' => array(),
),
);

View File

@ -68,7 +68,6 @@ class FilesTest extends TestCase
return array(
array('js/whitelist.php', 'var PMA_gotoWhitelist'),
array('js/messages.php', 'var PMA_messages = new Array();'),
array('js/get_scripts.js.php', 'var AJAX'),
);
}
}