diff --git a/.github/.readthedocs.yaml b/.github/.readthedocs.yaml
new file mode 100644
index 0000000000..9753f83314
--- /dev/null
+++ b/.github/.readthedocs.yaml
@@ -0,0 +1,25 @@
+# Read the Docs configuration file for Sphinx projects
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+build:
+ os: ubuntu-24.04
+ tools:
+ python: "3.12"
+ jobs:
+ post_create_environment:
+ - python -m pip install sphinx-rtd-theme
+
+# Build documentation in the "doc/" directory with Sphinx
+sphinx:
+ configuration: doc/conf.py
+ # Fail on all warnings to avoid broken references
+ # fail_on_warning: true
+
+# Optionally build your docs in additional formats such as PDF and ePub
+formats:
+ - pdf
+ - epub
+ - htmlzip
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 8dcb4ec5f9..7cfde02b77 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -63,15 +63,15 @@ jobs:
test-php:
name: Test on PHP ${{ matrix.php-version }}, ${{ matrix.composer-dependency }} and ${{ matrix.os }}
runs-on: ${{ matrix.os }}
- continue-on-error: ${{ matrix.php-version == '8.4' }}
+ continue-on-error: ${{ matrix.php-version == '8.5' }}
strategy:
matrix:
- php-version: ['8.2', '8.3']
+ php-version: ['8.2', '8.3', '8.4']
os: [ubuntu-latest]
composer-dependency: [locked, highest]
php-extensions: ['mbstring, iconv, mysqli, zip, gd, bz2']
include:
- - php-version: '8.4'
+ - php-version: '8.5'
os: ubuntu-latest
composer-dependency: locked
composer-options: '--ignore-platform-req=php+'
diff --git a/docs/conf.py b/docs/conf.py
index d79a969eac..c09030aba8 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -44,7 +44,7 @@ master_doc = 'index'
# General information about the project.
project = u'phpMyAdmin'
-copyright = u'2012 - 2023, The phpMyAdmin devel team'
+copyright = u'2012 - 2024, The phpMyAdmin devel team'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -100,6 +100,16 @@ pygments_style = 'sphinx'
# a list of builtin themes.
html_theme = 'default'
+# See: https://docs.readthedocs.io/en/stable/reference/environment-variables.html#envvar-READTHEDOCS
+on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
+
+if on_rtd: # only import and set the theme if we're building docs on readthedocs
+ try:
+ import sphinx_rtd_theme
+ html_theme = 'sphinx_rtd_theme'
+ except:
+ html_theme = 'default'
+
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 7ffdca2d1f..5a0fd25b84 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -8529,7 +8529,7 @@ parameters:
-
message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#'
identifier: foreach.nonIterable
- count: 2
+ count: 1
path: src/Error/ErrorReport.php
-
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
index 11c9a484e7..6c0eeffd62 100644
--- a/psalm-baseline.xml
+++ b/psalm-baseline.xml
@@ -5281,6 +5281,7 @@
+
diff --git a/resources/js/src/sql.ts b/resources/js/src/sql.ts
index eda96b949b..21b619cf14 100644
--- a/resources/js/src/sql.ts
+++ b/resources/js/src/sql.ts
@@ -5,7 +5,7 @@ import { Navigation } from './modules/navigation.ts';
import { CommonParams } from './modules/common.ts';
import highlightSql from './modules/sql-highlight.ts';
import { ajaxRemoveMessage, ajaxShowMessage } from './modules/ajax-message.ts';
-import { escapeHtml } from './modules/functions/escape.ts';
+import { escapeBacktick, escapeHtml } from './modules/functions/escape.ts';
import refreshMainContent from './modules/functions/refreshMainContent.ts';
import isStorageSupported from './modules/functions/isStorageSupported.ts';
@@ -320,7 +320,7 @@ const insertQuery = function (queryType) {
// @ts-ignore
var myListBox = document.sqlform.dummy;
// @ts-ignore
- table = document.sqlform.table.value;
+ table = escapeBacktick(document.sqlform.table.value);
if (myListBox.options.length > 0) {
sqlBoxLocked = true;
diff --git a/resources/js/src/table/gis_visualization.ts b/resources/js/src/table/gis_visualization.ts
index 3530245920..7a1cc869a1 100644
--- a/resources/js/src/table/gis_visualization.ts
+++ b/resources/js/src/table/gis_visualization.ts
@@ -387,7 +387,7 @@ class SvgVisualization extends GisVisualization {
$('#tooltip').remove();
const target = event.target as SVGElement;
- const contents = target.getAttribute('name').trim();
+ const contents = target.getAttribute('data-label').trim();
if (contents === '') {
return;
}
diff --git a/resources/templates/table/insert/column_row.twig b/resources/templates/table/insert/column_row.twig
index 5f1d86ce67..2b1e960924 100644
--- a/resources/templates/table/insert/column_row.twig
+++ b/resources/templates/table/insert/column_row.twig
@@ -55,7 +55,7 @@
{% elseif (longtext_double_textarea and 'longtext' in column.pmaType) or 'json' in column.pmaType or 'text' in column.pmaType %}
{{ backup_field|raw }}
@@ -98,7 +98,7 @@
{{ backup_field|raw }}
diff --git a/src/Error/ErrorReport.php b/src/Error/ErrorReport.php
index 7a7444bbaf..05ebb88cee 100644
--- a/src/Error/ErrorReport.php
+++ b/src/Error/ErrorReport.php
@@ -226,12 +226,14 @@ class ErrorReport
private function translateStacktrace(array $stack): array
{
foreach ($stack as &$level) {
- foreach ($level['context'] as &$line) {
- if (mb_strlen($line) <= 80) {
- continue;
- }
+ if (is_array($level['context'])) {
+ foreach ($level['context'] as &$line) {
+ if (mb_strlen($line) <= 80) {
+ continue;
+ }
- $line = mb_substr($line, 0, 75) . '//...';
+ $line = mb_substr($line, 0, 75) . '//...';
+ }
}
[$uri, $scriptName] = $this->sanitizeUrl($level['url']);
diff --git a/src/Gis/GisLineString.php b/src/Gis/GisLineString.php
index bb58d85ee4..4816a85099 100644
--- a/src/Gis/GisLineString.php
+++ b/src/Gis/GisLineString.php
@@ -167,7 +167,7 @@ class GisLineString extends GisGeometry
public function prepareRowAsSvg(string $spatial, string $label, array $color, ScaleData $scaleData): string
{
$lineOptions = [
- 'name' => $label,
+ 'data-label' => $label,
'id' => $label . $this->getRandomId(),
'class' => 'linestring vector',
'fill' => 'none',
diff --git a/src/Gis/GisMultiLineString.php b/src/Gis/GisMultiLineString.php
index 214215ba6f..50dfc53782 100644
--- a/src/Gis/GisMultiLineString.php
+++ b/src/Gis/GisMultiLineString.php
@@ -188,7 +188,7 @@ class GisMultiLineString extends GisGeometry
public function prepareRowAsSvg(string $spatial, string $label, array $color, ScaleData $scaleData): string
{
$lineOptions = [
- 'name' => $label,
+ 'data-label' => $label,
'class' => 'linestring vector',
'fill' => 'none',
'stroke' => sprintf('#%02x%02x%02x', ...$color),
diff --git a/src/Gis/GisMultiPoint.php b/src/Gis/GisMultiPoint.php
index 2aa374eff8..2fb02121c1 100644
--- a/src/Gis/GisMultiPoint.php
+++ b/src/Gis/GisMultiPoint.php
@@ -169,7 +169,7 @@ class GisMultiPoint extends GisGeometry
public function prepareRowAsSvg(string $spatial, string $label, array $color, ScaleData $scaleData): string
{
$pointOptions = [
- 'name' => $label,
+ 'data-label' => $label,
'class' => 'multipoint vector',
'fill' => 'white',
'stroke' => sprintf('#%02x%02x%02x', ...$color),
diff --git a/src/Gis/GisMultiPolygon.php b/src/Gis/GisMultiPolygon.php
index 25fda11fa7..3de2cf8f8e 100644
--- a/src/Gis/GisMultiPolygon.php
+++ b/src/Gis/GisMultiPolygon.php
@@ -194,7 +194,7 @@ class GisMultiPolygon extends GisGeometry
public function prepareRowAsSvg(string $spatial, string $label, array $color, ScaleData $scaleData): string
{
$polygonOptions = [
- 'name' => $label,
+ 'data-label' => $label,
'class' => 'multipolygon vector',
'stroke' => 'black',
'stroke-width' => 0.5,
diff --git a/src/Gis/GisPoint.php b/src/Gis/GisPoint.php
index 1771dcc4ed..43bb6221a3 100644
--- a/src/Gis/GisPoint.php
+++ b/src/Gis/GisPoint.php
@@ -160,7 +160,7 @@ class GisPoint extends GisGeometry
public function prepareRowAsSvg(string $spatial, string $label, array $color, ScaleData $scaleData): string
{
$pointOptions = [
- 'name' => $label,
+ 'data-label' => $label,
'id' => $label . $this->getRandomId(),
'class' => 'point vector',
'fill' => 'white',
diff --git a/src/Gis/GisPolygon.php b/src/Gis/GisPolygon.php
index a844d1fced..c44bc0afa5 100644
--- a/src/Gis/GisPolygon.php
+++ b/src/Gis/GisPolygon.php
@@ -163,7 +163,7 @@ class GisPolygon extends GisGeometry
public function prepareRowAsSvg(string $spatial, string $label, array $color, ScaleData $scaleData): string
{
$polygonOptions = [
- 'name' => $label,
+ 'data-label' => $label,
'id' => $label . $this->getRandomId(),
'class' => 'polygon vector',
'stroke' => 'black',
diff --git a/src/Gis/GisVisualization.php b/src/Gis/GisVisualization.php
index 0feef33ef8..815a692da0 100644
--- a/src/Gis/GisVisualization.php
+++ b/src/Gis/GisVisualization.php
@@ -19,6 +19,7 @@ use TCPDF;
use function assert;
use function count;
+use function htmlspecialchars;
use function is_string;
use function max;
use function mb_strlen;
@@ -508,7 +509,7 @@ class GisVisualization
$label = trim((string) ($row[$this->labelColumn] ?? ''));
if ($format === 'svg') {
- $svg .= $gisObj->prepareRowAsSvg($wkt, $label, $color, $scaleData);
+ $svg .= $gisObj->prepareRowAsSvg($wkt, htmlspecialchars($label), $color, $scaleData);
} elseif ($format === 'png') {
assert($renderer instanceof ImageWrapper);
$gisObj->prepareRowAsPng($wkt, $label, $color, $scaleData, $renderer);
diff --git a/src/InsertEdit.php b/src/InsertEdit.php
index 82e02752a4..2a947f643d 100644
--- a/src/InsertEdit.php
+++ b/src/InsertEdit.php
@@ -387,7 +387,7 @@ class InsertEdit
* @todo clarify the meaning of the "textfield" class and explain
* why character columns have the "char" class instead
*/
- $theClass = 'char charField';
+ $theClass = 'charField';
$textAreaRows = $this->config->settings['CharTextareaRows'];
$textareaCols = $this->config->settings['CharTextareaCols'];
$extractedColumnspec = Util::extractColumnSpec($column->type);
diff --git a/tests/unit/Gis/GisGeometryCollectionTest.php b/tests/unit/Gis/GisGeometryCollectionTest.php
index 3ffd7d3d9d..8a7a3e3ac3 100644
--- a/tests/unit/Gis/GisGeometryCollectionTest.php
+++ b/tests/unit/Gis/GisGeometryCollectionTest.php
@@ -380,7 +380,7 @@ class GisGeometryCollectionTest extends GisGeomTestCase
[176, 46, 224],
new ScaleData(offsetX: 12, offsetY: 69, scale: 2, height: 150),
'',
],
diff --git a/tests/unit/Gis/GisLineStringTest.php b/tests/unit/Gis/GisLineStringTest.php
index 080cb42395..9105043bc6 100644
--- a/tests/unit/Gis/GisLineStringTest.php
+++ b/tests/unit/Gis/GisLineStringTest.php
@@ -251,7 +251,7 @@ class GisLineStringTest extends GisGeomTestCase
[176, 46, 224],
new ScaleData(offsetX: 12, offsetY: 69, scale: 2, height: 150),
'',
],
];
diff --git a/tests/unit/Gis/GisMultiLineStringTest.php b/tests/unit/Gis/GisMultiLineStringTest.php
index 7bbfeed640..409f3233a3 100644
--- a/tests/unit/Gis/GisMultiLineStringTest.php
+++ b/tests/unit/Gis/GisMultiLineStringTest.php
@@ -258,10 +258,10 @@ class GisMultiLineStringTest extends GisGeomTestCase
'svg',
[176, 46, 224],
new ScaleData(offsetX: 12, offsetY: 69, scale: 2, height: 150),
- '',
],
];
diff --git a/tests/unit/Gis/GisMultiPointTest.php b/tests/unit/Gis/GisMultiPointTest.php
index 5cf2ce6804..db59bcf948 100644
--- a/tests/unit/Gis/GisMultiPointTest.php
+++ b/tests/unit/Gis/GisMultiPointTest.php
@@ -237,15 +237,15 @@ class GisMultiPointTest extends GisGeomTestCase
'svg',
[176, 46, 224],
new ScaleData(offsetX: 12, offsetY: 69, scale: 2, height: 150),
- '',
],
diff --git a/tests/unit/Gis/GisMultiPolygonTest.php b/tests/unit/Gis/GisMultiPolygonTest.php
index d1cf239a40..403ec510b9 100644
--- a/tests/unit/Gis/GisMultiPolygonTest.php
+++ b/tests/unit/Gis/GisMultiPolygonTest.php
@@ -349,10 +349,12 @@ class GisMultiPolygonTest extends GisGeomTestCase
[176, 46, 224],
new ScaleData(offsetX: -50, offsetY: -50, scale: 2, height: 400),
'',
],
];
diff --git a/tests/unit/Gis/GisPolygonTest.php b/tests/unit/Gis/GisPolygonTest.php
index d3bcd8fe80..54c73d7ec4 100644
--- a/tests/unit/Gis/GisPolygonTest.php
+++ b/tests/unit/Gis/GisPolygonTest.php
@@ -254,7 +254,8 @@ class GisPolygonTest extends GisGeomTestCase
'svg',
[176, 46, 224],
new ScaleData(offsetX: 12, offsetY: 69, scale: 2, height: 150),
- '',
],
diff --git a/tests/unit/InsertEditTest.php b/tests/unit/InsertEditTest.php
index e139257d11..ca7953deb6 100644
--- a/tests/unit/InsertEditTest.php
+++ b/tests/unit/InsertEditTest.php
@@ -611,7 +611,7 @@ class InsertEditTest extends AbstractTestCase
$result = $this->parseString($result);
self::assertStringContainsString(
- '