gl-website-deployer/admin/phpMyAdmin/libraries/classes/Transformations.php
2024-11-19 08:02:04 +01:00

491 lines
16 KiB
PHP

<?php
/**
* Set of functions used with the relation and pdf feature
*
* This file also provides basic functions to use in other plugins!
* These are declared in the 'GLOBAL Plugin functions' section
*
* Please use short and expressive names.
* For now, special characters which aren't allowed in
* filenames or functions should not be used.
*
* Please provide a comment for your function,
* what it does and what parameters are available.
*/
declare(strict_types=1);
namespace PhpMyAdmin;
use PhpMyAdmin\ConfigStorage\Relation;
use PhpMyAdmin\Plugins\TransformationsInterface;
use function array_shift;
use function class_exists;
use function closedir;
use function count;
use function explode;
use function ltrim;
use function mb_strtolower;
use function mb_substr;
use function opendir;
use function preg_match;
use function preg_replace;
use function readdir;
use function rtrim;
use function sort;
use function str_contains;
use function str_replace;
use function stripslashes;
use function strlen;
use function trim;
use function ucfirst;
use function ucwords;
/**
* Transformations class
*/
class Transformations
{
/**
* Returns array of options from string with options separated by comma,
* removes quotes
*
* <code>
* getOptions("'option ,, quoted',abd,'2,3',");
* // array {
* // 'option ,, quoted',
* // 'abc',
* // '2,3',
* // '',
* // }
* </code>
*
* @param string $optionString comma separated options
*
* @return array options
*/
public function getOptions($optionString)
{
if (strlen($optionString) === 0) {
return [];
}
$transformOptions = explode(',', $optionString);
$result = [];
while (($option = array_shift($transformOptions)) !== null) {
$trimmed = trim($option);
if (strlen($trimmed) > 1 && $trimmed[0] == "'" && $trimmed[strlen($trimmed) - 1] == "'") {
// '...'
$option = mb_substr($trimmed, 1, -1);
} elseif (isset($trimmed[0]) && $trimmed[0] == "'") {
// '...,
$trimmed = ltrim($option);
$rtrimmed = '';
while (($option = array_shift($transformOptions)) !== null) {
// ...,
$trimmed .= ',' . $option;
$rtrimmed = rtrim($trimmed);
if ($rtrimmed[strlen($rtrimmed) - 1] == "'") {
// ,...'
break;
}
}
$option = mb_substr($rtrimmed, 1, -1);
}
$result[] = stripslashes($option);
}
return $result;
}
/**
* Gets all available MIME-types
*
* @return array array[mimetype], array[transformation]
*
* @staticvar array $stack
*/
public function getAvailableMimeTypes(): array
{
static $stack = null;
if ($stack !== null) {
return $stack;
}
$stack = [];
$sub_dirs = [
'Input/' => 'input_',
'Output/' => '',
'' => '',
];
foreach ($sub_dirs as $sd => $prefix) {
$handle = opendir(ROOT_PATH . 'libraries/classes/Plugins/Transformations/' . $sd);
if (! $handle) {
$stack[$prefix . 'transformation'] = [];
$stack[$prefix . 'transformation_file'] = [];
continue;
}
$filestack = [];
while ($file = readdir($handle)) {
// Ignore hidden files
if ($file[0] === '.') {
continue;
}
// Ignore old plugins (.class in filename)
if (str_contains($file, '.class')) {
continue;
}
$filestack[] = $file;
}
closedir($handle);
sort($filestack);
foreach ($filestack as $file) {
if (preg_match('|^[^.].*_.*_.*\.php$|', $file)) {
// File contains transformation functions.
$parts = explode('_', str_replace('.php', '', $file));
$mimetype = $parts[0] . '/' . $parts[1];
$stack['mimetype'][$mimetype] = $mimetype;
$stack[$prefix . 'transformation'][] = $mimetype . ': ' . $parts[2];
$stack[$prefix . 'transformation_file'][] = $sd . $file;
if ($sd === '') {
$stack['input_transformation'][] = $mimetype . ': ' . $parts[2];
$stack['input_transformation_file'][] = $sd . $file;
}
} elseif (preg_match('|^[^.].*\.php$|', $file)) {
// File is a plain mimetype, no functions.
$base = str_replace('.php', '', $file);
if ($base !== 'global') {
$mimetype = str_replace('_', '/', $base);
$stack['mimetype'][$mimetype] = $mimetype;
$stack['empty_mimetype'][$mimetype] = $mimetype;
}
}
}
}
return $stack;
}
/**
* Returns the class name of the transformation
*
* @param string $filename transformation file name
*
* @return string the class name of transformation
*/
public function getClassName($filename)
{
// get the transformation class name
$class_name = explode('.php', $filename);
$class_name = 'PhpMyAdmin\\' . str_replace('/', '\\', mb_substr($class_name[0], 18));
return $class_name;
}
/**
* Returns the description of the transformation
*
* @param string $file transformation file
*
* @return string the description of the transformation
*/
public function getDescription($file)
{
$include_file = 'libraries/classes/Plugins/Transformations/' . $file;
/** @psalm-var class-string<TransformationsInterface> $class_name */
$class_name = $this->getClassName($include_file);
if (class_exists($class_name)) {
return $class_name::getInfo();
}
return '';
}
/**
* Returns the name of the transformation
*
* @param string $file transformation file
*
* @return string the name of the transformation
*/
public function getName($file)
{
$include_file = 'libraries/classes/Plugins/Transformations/' . $file;
/** @psalm-var class-string<TransformationsInterface> $class_name */
$class_name = $this->getClassName($include_file);
if (class_exists($class_name)) {
return $class_name::getName();
}
return '';
}
/**
* Fixups old MIME or transformation name to new one
*
* - applies some hardcoded fixups
* - adds spaces after _ and numbers
* - capitalizes words
* - removes back spaces
*
* @param string $value Value to fixup
*
* @return string
*/
public function fixUpMime($value)
{
$value = str_replace(
[
'jpeg',
'png',
],
[
'JPEG',
'PNG',
],
$value
);
return str_replace(
' ',
'',
ucwords(
(string) preg_replace('/([0-9_]+)/', '$1 ', $value)
)
);
}
/**
* Gets the mimetypes for all columns of a table
*
* @param string $db the name of the db to check for
* @param string $table the name of the table to check for
* @param bool $strict whether to include only results having a mimetype set
* @param bool $fullName whether to use full column names as the key
*
* @return array|null [field_name][field_key] = field_value
*/
public function getMime($db, $table, $strict = false, $fullName = false)
{
global $dbi;
$relation = new Relation($dbi);
$browserTransformationFeature = $relation->getRelationParameters()->browserTransformationFeature;
if ($browserTransformationFeature === null) {
return null;
}
$com_qry = '';
if ($fullName) {
$com_qry .= 'SELECT CONCAT(`db_name`, \'.\', `table_name`, \'.\', `column_name`) AS column_name, ';
} else {
$com_qry = 'SELECT `column_name`, ';
}
$com_qry .= '`mimetype`, '
. '`transformation`, '
. '`transformation_options`, '
. '`input_transformation`, '
. '`input_transformation_options`'
. ' FROM ' . Util::backquote($browserTransformationFeature->database) . '.'
. Util::backquote($browserTransformationFeature->columnInfo)
. ' WHERE `db_name` = \'' . $dbi->escapeString($db) . '\''
. ' AND `table_name` = \'' . $dbi->escapeString($table) . '\''
. ' AND ( `mimetype` != \'\'' . (! $strict ?
' OR `transformation` != \'\''
. ' OR `transformation_options` != \'\''
. ' OR `input_transformation` != \'\''
. ' OR `input_transformation_options` != \'\'' : '') . ')';
$result = $dbi->fetchResult($com_qry, 'column_name', null, DatabaseInterface::CONNECT_CONTROL);
foreach ($result as $column => $values) {
// convert mimetype to new format (f.e. Text_Plain, etc)
$values['mimetype'] = $this->fixUpMime($values['mimetype']);
// For transformation of form
// output/image_jpeg__inline.inc.php
// extract dir part.
$dir = explode('/', $values['transformation']);
$subdir = '';
if (count($dir) === 2) {
$subdir = ucfirst($dir[0]) . '/';
$values['transformation'] = $dir[1];
}
$values['transformation'] = $this->fixUpMime($values['transformation']);
$values['transformation'] = $subdir . $values['transformation'];
$result[$column] = $values;
}
return $result;
}
/**
* Set a single mimetype to a certain value.
*
* @param string $db the name of the db
* @param string $table the name of the table
* @param string $key the name of the column
* @param string $mimetype the mimetype of the column
* @param string $transformation the transformation of the column
* @param string $transformationOpts the transformation options of the column
* @param string $inputTransform the input transformation of the column
* @param string $inputTransformOpts the input transformation options of the column
* @param bool $forcedelete force delete, will erase any existing
* comments for this column
*/
public function setMime(
$db,
$table,
$key,
$mimetype,
$transformation,
$transformationOpts,
$inputTransform,
$inputTransformOpts,
$forcedelete = false
): bool {
global $dbi;
$relation = new Relation($dbi);
$browserTransformationFeature = $relation->getRelationParameters()->browserTransformationFeature;
if ($browserTransformationFeature === null) {
return false;
}
// lowercase mimetype & transformation
$mimetype = mb_strtolower($mimetype);
$transformation = mb_strtolower($transformation);
// Do we have any parameter to set?
$has_value = (
strlen($mimetype) > 0 ||
strlen($transformation) > 0 ||
strlen($transformationOpts) > 0 ||
strlen($inputTransform) > 0 ||
strlen($inputTransformOpts) > 0
);
$test_qry = '
SELECT `mimetype`,
`comment`
FROM ' . Util::backquote($browserTransformationFeature->database) . '.'
. Util::backquote($browserTransformationFeature->columnInfo) . '
WHERE `db_name` = \'' . $dbi->escapeString($db) . '\'
AND `table_name` = \'' . $dbi->escapeString($table) . '\'
AND `column_name` = \'' . $dbi->escapeString($key) . '\'';
$test_rs = $dbi->queryAsControlUser($test_qry);
if ($test_rs->numRows() > 0) {
$row = $test_rs->fetchAssoc();
if (! $forcedelete && ($has_value || strlen($row['comment']) > 0)) {
$upd_query = 'UPDATE '
. Util::backquote($browserTransformationFeature->database) . '.'
. Util::backquote($browserTransformationFeature->columnInfo)
. ' SET '
. '`mimetype` = \''
. $dbi->escapeString($mimetype) . '\', '
. '`transformation` = \''
. $dbi->escapeString($transformation) . '\', '
. '`transformation_options` = \''
. $dbi->escapeString($transformationOpts) . '\', '
. '`input_transformation` = \''
. $dbi->escapeString($inputTransform) . '\', '
. '`input_transformation_options` = \''
. $dbi->escapeString($inputTransformOpts) . '\'';
} else {
$upd_query = 'DELETE FROM '
. Util::backquote($browserTransformationFeature->database)
. '.' . Util::backquote($browserTransformationFeature->columnInfo);
}
$upd_query .= '
WHERE `db_name` = \'' . $dbi->escapeString($db) . '\'
AND `table_name` = \'' . $dbi->escapeString($table)
. '\'
AND `column_name` = \'' . $dbi->escapeString($key)
. '\'';
} elseif ($has_value) {
$upd_query = 'INSERT INTO '
. Util::backquote($browserTransformationFeature->database)
. '.' . Util::backquote($browserTransformationFeature->columnInfo)
. ' (db_name, table_name, column_name, mimetype, '
. 'transformation, transformation_options, '
. 'input_transformation, input_transformation_options) '
. ' VALUES('
. '\'' . $dbi->escapeString($db) . '\','
. '\'' . $dbi->escapeString($table) . '\','
. '\'' . $dbi->escapeString($key) . '\','
. '\'' . $dbi->escapeString($mimetype) . '\','
. '\'' . $dbi->escapeString($transformation) . '\','
. '\'' . $dbi->escapeString($transformationOpts) . '\','
. '\'' . $dbi->escapeString($inputTransform) . '\','
. '\'' . $dbi->escapeString($inputTransformOpts) . '\')';
}
if (isset($upd_query)) {
return (bool) $dbi->queryAsControlUser($upd_query);
}
return false;
}
/**
* GLOBAL Plugin functions
*/
/**
* Delete related transformation details
* after deleting database. table or column
*
* @param string $db Database name
* @param string $table Table name
* @param string $column Column name
*/
public function clear($db, $table = '', $column = ''): bool
{
global $dbi;
$relation = new Relation($dbi);
$browserTransformationFeature = $relation->getRelationParameters()->browserTransformationFeature;
if ($browserTransformationFeature === null) {
return false;
}
$delete_sql = 'DELETE FROM '
. Util::backquote($browserTransformationFeature->database) . '.'
. Util::backquote($browserTransformationFeature->columnInfo)
. ' WHERE ';
if (($column != '') && ($table != '')) {
$delete_sql .= '`db_name` = \'' . $db . '\' AND '
. '`table_name` = \'' . $table . '\' AND '
. '`column_name` = \'' . $column . '\' ';
} elseif ($table != '') {
$delete_sql .= '`db_name` = \'' . $db . '\' AND '
. '`table_name` = \'' . $table . '\' ';
} else {
$delete_sql .= '`db_name` = \'' . $db . '\' ';
}
return (bool) $dbi->tryQuery($delete_sql);
}
}