77use PhpMyAdmin \Controllers \AbstractController ;
88use PhpMyAdmin \Core ;
99use PhpMyAdmin \DatabaseInterface ;
10+ use PhpMyAdmin \FieldMetadata ;
1011use PhpMyAdmin \Gis \GisVisualization ;
1112use PhpMyAdmin \Html \Generator ;
1213use PhpMyAdmin \Http \ServerRequest ;
1718use PhpMyAdmin \Util ;
1819
1920use function __ ;
20- use function array_merge ;
21+ use function in_array ;
2122use function is_array ;
23+ use function is_string ;
2224
2325/**
2426 * Handles creation of the GIS visualizations.
2527 */
2628final class GisVisualizationController extends AbstractController
2729{
28- private GisVisualization $ visualization ;
29-
3030 public function __construct (
3131 ResponseRenderer $ response ,
3232 Template $ template ,
@@ -37,8 +37,6 @@ public function __construct(
3737
3838 public function __invoke (ServerRequest $ request ): void
3939 {
40- $ GLOBALS ['urlParams ' ] ??= null ;
41- $ GLOBALS ['errorUrl ' ] ??= null ;
4240 $ this ->checkParameters (['db ' ]);
4341
4442 $ GLOBALS ['errorUrl ' ] = Util::getScriptNameForOption ($ GLOBALS ['cfg ' ]['DefaultTabDatabase ' ], 'database ' );
@@ -49,17 +47,10 @@ public function __invoke(ServerRequest $request): void
4947 }
5048
5149 // SQL query for retrieving GIS data
52- $ sqlQuery = '' ;
53- if (isset ($ _GET ['sql_query ' ], $ _GET ['sql_signature ' ])) {
54- if (Core::checkSqlQuerySignature ($ _GET ['sql_query ' ], $ _GET ['sql_signature ' ])) {
55- $ sqlQuery = $ _GET ['sql_query ' ];
56- }
57- } elseif (isset ($ _POST ['sql_query ' ])) {
58- $ sqlQuery = $ _POST ['sql_query ' ];
59- }
50+ $ sqlQuery = $ this ->getSqlQuery ();
6051
6152 // Throw error if no sql query is set
62- if ($ sqlQuery == '' ) {
53+ if ($ sqlQuery === null ) {
6354 $ this ->response ->setRequestStatus (false );
6455 $ this ->response ->addHTML (
6556 Message::error (__ ('No SQL query was set to fetch data. ' ))->getDisplay (),
@@ -68,129 +59,180 @@ public function __invoke(ServerRequest $request): void
6859 return ;
6960 }
7061
71- // Execute the query and return the result
72- $ result = $ this ->dbi ->tryQuery ($ sqlQuery );
73- // Get the meta data of results
74- $ meta = [];
75- if ($ result !== false ) {
76- $ meta = $ this ->dbi ->getFieldsMeta ($ result );
77- }
62+ $ meta = $ this ->getColumnMeta ($ sqlQuery );
7863
7964 // Find the candidate fields for label column and spatial column
8065 $ labelCandidates = [];
8166 $ spatialCandidates = [];
8267 foreach ($ meta as $ column_meta ) {
68+ if ($ column_meta ->name === '' ) {
69+ continue ;
70+ }
71+
8372 if ($ column_meta ->isMappedTypeGeometry ) {
8473 $ spatialCandidates [] = $ column_meta ->name ;
8574 } else {
8675 $ labelCandidates [] = $ column_meta ->name ;
8776 }
8877 }
8978
90- // Get settings if any posted
91- $ visualizationSettings = [];
92- // Download as PNG/SVG/PDF use _GET and the normal form uses _POST
93- if (isset ($ _POST ['visualizationSettings ' ]) && is_array ($ _POST ['visualizationSettings ' ])) {
94- $ visualizationSettings = $ _POST ['visualizationSettings ' ];
95- } elseif (isset ($ _GET ['visualizationSettings ' ]) && is_array ($ _GET ['visualizationSettings ' ])) {
96- $ visualizationSettings = $ _GET ['visualizationSettings ' ];
97- }
98-
99- // Check mysql version
100- $ visualizationSettings ['mysqlVersion ' ] = $ this ->dbi ->getVersion ();
101- $ visualizationSettings ['isMariaDB ' ] = $ this ->dbi ->isMariaDB ();
79+ if ($ spatialCandidates === []) {
80+ $ this ->response ->setRequestStatus (false );
81+ $ this ->response ->addHTML (
82+ Message::error (__ ('No spatial column found for this SQL query. ' ))->getDisplay (),
83+ );
10284
103- if (! isset ($ visualizationSettings ['labelColumn ' ]) && isset ($ labelCandidates [0 ])) {
104- $ visualizationSettings ['labelColumn ' ] = '' ;
85+ return ;
10586 }
10687
107- // If spatial column is not set, use first geometric column as spatial column
108- if (! isset ( $ visualizationSettings [ ' spatialColumn ' ])) {
109- $ visualizationSettings ['spatialColumn ' ] = $ spatialCandidates [ 0 ] ;
110- }
88+ // Get settings if any posted
89+ $ visualizationSettings = $ this -> getVisualizationSettings ( $ spatialCandidates , $ labelCandidates );
90+ $ visualizationSettings ['width ' ] = 600 ;
91+ $ visualizationSettings [ ' height ' ] = 450 ;
11192
112- // Download as PNG/SVG/PDF use _GET and the normal form uses _POST
113- // Convert geometric columns from bytes to text.
114- $ pos = (int ) ($ _POST ['pos ' ] ?? $ _GET ['pos ' ] ?? $ _SESSION ['tmpval ' ]['pos ' ]);
115- if (isset ($ _POST ['session_max_rows ' ]) || isset ($ _GET ['session_max_rows ' ])) {
116- $ rows = (int ) ($ _POST ['session_max_rows ' ] ?? $ _GET ['session_max_rows ' ]);
117- } else {
118- if ($ _SESSION ['tmpval ' ]['max_rows ' ] !== 'all ' ) {
119- $ rows = (int ) $ _SESSION ['tmpval ' ]['max_rows ' ];
120- } else {
121- $ rows = (int ) $ GLOBALS ['cfg ' ]['MaxRows ' ];
122- }
123- }
93+ $ rows = $ this ->getRows ();
94+ $ pos = $ this ->getPos ();
12495
125- $ this -> visualization = GisVisualization::get ($ sqlQuery , $ visualizationSettings , $ rows , $ pos );
96+ $ visualization = GisVisualization::get ($ sqlQuery , $ visualizationSettings , $ rows , $ pos );
12697
12798 if (isset ($ _GET ['saveToFile ' ])) {
128- $ this ->saveToFile ($ visualizationSettings ['spatialColumn ' ], $ _GET ['fileFormat ' ]);
99+ $ this ->response ->disable ();
100+ $ filename = $ visualization ->getSpatialColumn ();
101+ $ visualization ->toFile ($ filename , $ _GET ['fileFormat ' ]);
129102
130103 return ;
131104 }
132105
133106 $ this ->addScriptFiles (['vendor/openlayers/OpenLayers.js ' , 'table/gis_visualization.js ' ]);
134107
135108 // If all the rows contain SRID, use OpenStreetMaps on the initial loading.
136- if (! isset ($ _POST ['displayVisualization ' ])) {
137- if ($ this ->visualization ->hasSrid ()) {
138- $ visualizationSettings ['choice ' ] = 'useBaseLayer ' ;
139- } else {
140- unset($ visualizationSettings ['choice ' ]);
141- }
142- }
143-
144- $ this ->visualization ->setUserSpecifiedSettings ($ visualizationSettings );
145- foreach ($ this ->visualization ->getSettings () as $ setting => $ val ) {
146- if (isset ($ visualizationSettings [$ setting ])) {
147- continue ;
148- }
149-
150- $ visualizationSettings [$ setting ] = $ val ;
151- }
109+ $ useBaseLayer = isset ($ _POST ['redraw ' ]) ? isset ($ _POST ['useBaseLayer ' ]) : $ visualization ->hasSrid ();
152110
153111 /**
154112 * Displays the page
155113 */
156- $ GLOBALS ['urlParams ' ]['goto ' ] = Util::getScriptNameForOption ($ GLOBALS ['cfg ' ]['DefaultTabDatabase ' ], 'database ' );
157- $ GLOBALS ['urlParams ' ]['back ' ] = Url::getFromRoute ('/sql ' );
158- $ GLOBALS ['urlParams ' ]['sql_query ' ] = $ sqlQuery ;
159- $ GLOBALS ['urlParams ' ]['sql_signature ' ] = Core::signSqlQuery ($ sqlQuery );
160- $ downloadUrl = Url::getFromRoute ('/table/gis-visualization ' , array_merge (
161- $ GLOBALS ['urlParams ' ],
162- [
163- 'saveToFile ' => true ,
164- 'session_max_rows ' => $ rows ,
165- 'pos ' => $ pos ,
166- 'visualizationSettings[spatialColumn] ' => $ visualizationSettings ['spatialColumn ' ],
167- 'visualizationSettings[labelColumn] ' => $ visualizationSettings ['labelColumn ' ] ?? null ,
168- ],
169- ));
114+ $ urlParams = $ GLOBALS ['urlParams ' ] ?? [];
115+ $ urlParams ['goto ' ] = Util::getScriptNameForOption ($ GLOBALS ['cfg ' ]['DefaultTabDatabase ' ], 'database ' );
116+ $ urlParams ['back ' ] = Url::getFromRoute ('/sql ' );
117+ $ urlParams ['sql_query ' ] = $ sqlQuery ;
118+ $ urlParams ['sql_signature ' ] = Core::signSqlQuery ($ sqlQuery );
119+ $ downloadParams = [
120+ 'saveToFile ' => true ,
121+ 'session_max_rows ' => $ visualization ->getRows (),
122+ 'pos ' => $ visualization ->getPos (),
123+ 'visualizationSettings[spatialColumn] ' => $ visualization ->getSpatialColumn (),
124+ 'visualizationSettings[labelColumn] ' => $ visualization ->getLabelColumn (),
125+ ];
126+ $ downloadUrl = Url::getFromRoute ('/table/gis-visualization ' , $ downloadParams + $ urlParams );
170127
171128 $ startAndNumberOfRowsFieldset = Generator::getStartAndNumberOfRowsFieldsetData ($ sqlQuery );
172129
173130 $ html = $ this ->template ->render ('table/gis_visualization/gis_visualization ' , [
174- 'url_params ' => $ GLOBALS [ ' urlParams ' ] ,
131+ 'url_params ' => $ urlParams ,
175132 'download_url ' => $ downloadUrl ,
176133 'label_candidates ' => $ labelCandidates ,
177134 'spatial_candidates ' => $ spatialCandidates ,
178- 'visualization_settings ' => $ visualizationSettings ,
135+ 'spatialColumn ' => $ visualization ->getSpatialColumn (),
136+ 'labelColumn ' => $ visualization ->getLabelColumn (),
137+ 'width ' => $ visualization ->getWidth (),
138+ 'height ' => $ visualization ->getHeight (),
179139 'start_and_number_of_rows_fieldset ' => $ startAndNumberOfRowsFieldset ,
180- 'visualization ' => $ this ->visualization ->toImage ('svg ' ),
181- 'draw_ol ' => $ this ->visualization ->asOl (),
140+ 'useBaseLayer ' => $ useBaseLayer ,
141+ 'visualization ' => $ visualization ->asSVG (),
142+ 'draw_ol ' => $ visualization ->asOl (),
182143 ]);
183144
184145 $ this ->response ->addHTML ($ html );
185146 }
186147
187148 /**
188- * @param string $filename File name
189- * @param string $format Save format
149+ * Reads the sql query from POST or GET
150+ *
151+ * @psalm-return non-empty-string|null
152+ */
153+ private function getSqlQuery (): string |null
154+ {
155+ $ getQuery = $ _GET ['sql_query ' ] ?? null ;
156+ $ getSignature = $ _GET ['sql_signature ' ] ?? null ;
157+ $ postQuery = $ _POST ['sql_query ' ] ?? null ;
158+
159+ $ sqlQuery = null ;
160+ if (is_string ($ getQuery ) && is_string ($ getSignature )) {
161+ if (Core::checkSqlQuerySignature ($ getQuery , $ getSignature )) {
162+ $ sqlQuery = $ getQuery ;
163+ }
164+ } elseif (is_string ($ postQuery )) {
165+ $ sqlQuery = $ postQuery ;
166+ }
167+
168+ return $ sqlQuery === '' ? null : $ sqlQuery ;
169+ }
170+
171+ /**
172+ * @param string[] $spatialCandidates
173+ * @param string[] $labelCandidates
174+ * @psalm-param non-empty-list<non-empty-string> $spatialCandidates
175+ * @psalm-param list<non-empty-string> $labelCandidates
176+ *
177+ * @return mixed[];
178+ * @psalm-return array{spatialColumn:non-empty-string,labelColumn?:non-empty-string}
190179 */
191- private function saveToFile (string $ filename , string $ format ): void
180+ private function getVisualizationSettings (array $ spatialCandidates , array $ labelCandidates ): array
181+ {
182+ $ settingsIn = [];
183+ // Download as PNG/SVG/PDF use _GET and the normal form uses _POST
184+ if (is_array ($ _POST ['visualizationSettings ' ] ?? null )) {
185+ /** @var mixed[] $settingsIn */
186+ $ settingsIn = $ _POST ['visualizationSettings ' ];
187+ } elseif (is_array ($ _GET ['visualizationSettings ' ] ?? null )) {
188+ /** @var mixed[] $settingsIn */
189+ $ settingsIn = $ _GET ['visualizationSettings ' ];
190+ }
191+
192+ $ settings = [];
193+ if (
194+ isset ($ settingsIn ['labelColumn ' ]) &&
195+ in_array ($ settingsIn ['labelColumn ' ], $ labelCandidates , true )
196+ ) {
197+ $ settings ['labelColumn ' ] = $ settingsIn ['labelColumn ' ];
198+ }
199+
200+ // If spatial column is not set, use first geometric column as spatial column
201+ $ spatialColumnValid = isset ($ settingsIn ['spatialColumn ' ]) &&
202+ in_array ($ settingsIn ['spatialColumn ' ], $ spatialCandidates , true );
203+ $ settings ['spatialColumn ' ] = $ spatialColumnValid ? $ settingsIn ['spatialColumn ' ] : $ spatialCandidates [0 ];
204+
205+ return $ settings ;
206+ }
207+
208+ private function getPos (): int
192209 {
193- $ this ->response ->disable ();
194- $ this ->visualization ->toFile ($ filename , $ format );
210+ // Download as PNG/SVG/PDF use _GET and the normal form uses _POST
211+ return (int ) ($ _POST ['pos ' ] ?? $ _GET ['pos ' ] ?? $ _SESSION ['tmpval ' ]['pos ' ]);
212+ }
213+
214+ private function getRows (): int
215+ {
216+ if (isset ($ _POST ['session_max_rows ' ]) || isset ($ _GET ['session_max_rows ' ])) {
217+ return (int ) ($ _POST ['session_max_rows ' ] ?? $ _GET ['session_max_rows ' ]);
218+ }
219+
220+ if ($ _SESSION ['tmpval ' ]['max_rows ' ] === 'all ' ) {
221+ return (int ) $ GLOBALS ['cfg ' ]['MaxRows ' ];
222+ }
223+
224+ return (int ) $ _SESSION ['tmpval ' ]['max_rows ' ];
225+ }
226+
227+ /**
228+ * Execute the query and return the result
229+ *
230+ * @return FieldMetadata[]
231+ */
232+ private function getColumnMeta (string $ sqlQuery ): array
233+ {
234+ $ result = $ this ->dbi ->tryQuery ($ sqlQuery );
235+
236+ return $ result === false ? [] : $ this ->dbi ->getFieldsMeta ($ result );
195237 }
196238}
0 commit comments