362 lines
7.4 KiB
PHP
362 lines
7.4 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
use Kanboard\Core\Translator;
|
||
|
|
|
||
|
|
function concat_files(array $files)
|
||
|
|
{
|
||
|
|
$data = '';
|
||
|
|
foreach ($files as $pattern) {
|
||
|
|
foreach (glob($pattern, GLOB_ERR | GLOB_NOCHECK) as $filename) {
|
||
|
|
echo $filename.PHP_EOL;
|
||
|
|
if (! file_exists($filename)) {
|
||
|
|
die("$filename not found\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
$contents = file_get_contents($filename);
|
||
|
|
if ($contents === false) {
|
||
|
|
die("Unable to read $filename\n");
|
||
|
|
}
|
||
|
|
|
||
|
|
$data .= $contents;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $data;
|
||
|
|
}
|
||
|
|
|
||
|
|
function session_get($key)
|
||
|
|
{
|
||
|
|
return isset($_SESSION[$key]) ? $_SESSION[$key] : null;
|
||
|
|
}
|
||
|
|
|
||
|
|
function session_set($key, $value)
|
||
|
|
{
|
||
|
|
$_SESSION[$key] = $value;
|
||
|
|
}
|
||
|
|
|
||
|
|
function session_remove($key)
|
||
|
|
{
|
||
|
|
unset($_SESSION[$key]);
|
||
|
|
}
|
||
|
|
|
||
|
|
function session_exists($key)
|
||
|
|
{
|
||
|
|
return isset($_SESSION[$key]);
|
||
|
|
}
|
||
|
|
|
||
|
|
function session_is_true($key)
|
||
|
|
{
|
||
|
|
return isset($_SESSION[$key]) && $_SESSION[$key] === true;
|
||
|
|
}
|
||
|
|
|
||
|
|
function session_merge($key, array $value)
|
||
|
|
{
|
||
|
|
$_SESSION[$key] = array_merge($_SESSION[$key], $value);
|
||
|
|
}
|
||
|
|
|
||
|
|
function session_flush()
|
||
|
|
{
|
||
|
|
$_SESSION = [];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Split CSV string
|
||
|
|
*
|
||
|
|
* @param string $str
|
||
|
|
* @return string[]
|
||
|
|
*/
|
||
|
|
function explode_csv_field($str)
|
||
|
|
{
|
||
|
|
$fields = explode(',', $str);
|
||
|
|
array_walk($fields, function (&$value) {
|
||
|
|
$value = trim($value);
|
||
|
|
});
|
||
|
|
return array_filter($fields);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Associate another dict to a dict based on a common key
|
||
|
|
*
|
||
|
|
* @param array $input
|
||
|
|
* @param array $relations
|
||
|
|
* @param string $relation
|
||
|
|
* @param string $column
|
||
|
|
*/
|
||
|
|
function array_merge_relation(array &$input, array &$relations, $relation, $column)
|
||
|
|
{
|
||
|
|
foreach ($input as &$row) {
|
||
|
|
if (isset($row[$column]) && isset($relations[$row[$column]])) {
|
||
|
|
$row[$relation] = $relations[$row[$column]];
|
||
|
|
} else {
|
||
|
|
$row[$relation] = array();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create indexed array from a list of dict
|
||
|
|
*
|
||
|
|
* $input = [
|
||
|
|
* ['k1' => 1, 'k2' => 2], ['k1' => 3, 'k2' => 4], ['k1' => 1, 'k2' => 5]
|
||
|
|
* ]
|
||
|
|
*
|
||
|
|
* array_column_index($input, 'k1') will returns:
|
||
|
|
*
|
||
|
|
* [
|
||
|
|
* 1 => [['k1' => 1, 'k2' => 2], ['k1' => 1, 'k2' => 5]],
|
||
|
|
* 3 => [['k1' => 3, 'k2' => 4]],
|
||
|
|
* ]
|
||
|
|
*
|
||
|
|
* @param array $input
|
||
|
|
* @param string $column
|
||
|
|
* @return array
|
||
|
|
*/
|
||
|
|
function array_column_index(array &$input, $column)
|
||
|
|
{
|
||
|
|
$result = array();
|
||
|
|
|
||
|
|
foreach ($input as &$row) {
|
||
|
|
if (isset($row[$column])) {
|
||
|
|
$result[$row[$column]][] = $row;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Create indexed array from a list of dict with unique values
|
||
|
|
*
|
||
|
|
* $input = [
|
||
|
|
* ['k1' => 1, 'k2' => 2], ['k1' => 3, 'k2' => 4], ['k1' => 1, 'k2' => 5]
|
||
|
|
* ]
|
||
|
|
*
|
||
|
|
* array_column_index_unique($input, 'k1') will returns:
|
||
|
|
*
|
||
|
|
* [
|
||
|
|
* 1 => ['k1' => 1, 'k2' => 2],
|
||
|
|
* 3 => ['k1' => 3, 'k2' => 4],
|
||
|
|
* ]
|
||
|
|
*
|
||
|
|
* @param array $input
|
||
|
|
* @param string $column
|
||
|
|
* @return array
|
||
|
|
*/
|
||
|
|
function array_column_index_unique(array &$input, $column)
|
||
|
|
{
|
||
|
|
$result = array();
|
||
|
|
|
||
|
|
foreach ($input as &$row) {
|
||
|
|
if (isset($row[$column]) && ! isset($result[$row[$column]])) {
|
||
|
|
$result[$row[$column]] = $row;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $result;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Sum all values from a single column in the input array
|
||
|
|
*
|
||
|
|
* $input = [
|
||
|
|
* ['column' => 2], ['column' => 3]
|
||
|
|
* ]
|
||
|
|
*
|
||
|
|
* array_column_sum($input, 'column') returns 5
|
||
|
|
*
|
||
|
|
* @param array $input
|
||
|
|
* @param string $column
|
||
|
|
* @return double
|
||
|
|
*/
|
||
|
|
function array_column_sum(array &$input, $column)
|
||
|
|
{
|
||
|
|
$sum = 0.0;
|
||
|
|
|
||
|
|
foreach ($input as &$row) {
|
||
|
|
if (isset($row[$column])) {
|
||
|
|
$sum += (float) $row[$column];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $sum;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Build version number from git-archive output
|
||
|
|
*
|
||
|
|
* @param string $ref
|
||
|
|
* @param string $commit_hash
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
function build_app_version($ref, $commit_hash)
|
||
|
|
{
|
||
|
|
if ($ref !== '$Format:%d$') {
|
||
|
|
$tag = preg_replace('/\s*\(.*tag:\sv([^,]+).*\)/i', '\1', $ref);
|
||
|
|
|
||
|
|
if (!is_null($tag) && $tag !== $ref) {
|
||
|
|
return $tag;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($commit_hash !== '$Format:%H$') {
|
||
|
|
return 'main.'.$commit_hash;
|
||
|
|
} elseif (file_exists(__DIR__ . '/version.txt')) {
|
||
|
|
return rtrim(file_get_contents(__DIR__ . '/version.txt'));
|
||
|
|
}
|
||
|
|
|
||
|
|
return 'main.unknown_revision';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get upload max size.
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
function get_upload_max_size()
|
||
|
|
{
|
||
|
|
$upload_max_filesize = convert_php_size_to_bytes(ini_get('upload_max_filesize'));
|
||
|
|
$post_max_size = convert_php_size_to_bytes(ini_get('post_max_size'));
|
||
|
|
|
||
|
|
if ($post_max_size == 0) {
|
||
|
|
return $upload_max_filesize;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($upload_max_filesize == 0) {
|
||
|
|
return $post_max_size;
|
||
|
|
}
|
||
|
|
|
||
|
|
return min($post_max_size, $upload_max_filesize);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the number of bytes from PHP size
|
||
|
|
*
|
||
|
|
* @param integer $value PHP size (example: 2M)
|
||
|
|
* @return integer
|
||
|
|
*/
|
||
|
|
function convert_php_size_to_bytes($value)
|
||
|
|
{
|
||
|
|
// Remove the non-unit characters from the size
|
||
|
|
$unit = preg_replace('/[^bkmgtpezy]/i', '', $value);
|
||
|
|
|
||
|
|
// Remove the non-numeric characters from the size
|
||
|
|
$size = preg_replace('/[^0-9\.]/', '', $value);
|
||
|
|
|
||
|
|
switch (strtoupper($unit)) {
|
||
|
|
case 'G':
|
||
|
|
$size *= 1024;
|
||
|
|
// no break
|
||
|
|
case 'M':
|
||
|
|
$size *= 1024;
|
||
|
|
// no break
|
||
|
|
case 'K':
|
||
|
|
$size *= 1024;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $size;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get file extension
|
||
|
|
*
|
||
|
|
* @param $filename
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
function get_file_extension($filename)
|
||
|
|
{
|
||
|
|
return strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Translate a string
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
function t()
|
||
|
|
{
|
||
|
|
return call_user_func_array(array(Translator::getInstance(), 'translate'), func_get_args());
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Translate a string with no HTML escaping
|
||
|
|
*
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
function e()
|
||
|
|
{
|
||
|
|
return call_user_func_array(array(Translator::getInstance(), 'translateNoEscaping'), func_get_args());
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Translate a number
|
||
|
|
*
|
||
|
|
* @param mixed $value
|
||
|
|
* @return string
|
||
|
|
*/
|
||
|
|
function n($value)
|
||
|
|
{
|
||
|
|
return Translator::getInstance()->number($value);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Sanitize a file path
|
||
|
|
*
|
||
|
|
* @param string $path
|
||
|
|
* @return string|false
|
||
|
|
*/
|
||
|
|
function sanitize_path(string $path): string|false
|
||
|
|
{
|
||
|
|
// Handle empty path
|
||
|
|
if (empty($path)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
$dirSeparator = '/';
|
||
|
|
|
||
|
|
// Get Windows drive letter (C:/ or C:\)
|
||
|
|
$driveLetter = '';
|
||
|
|
if (preg_match('/^([a-zA-Z]:)([\/\\\].*)$/', $path, $matches)) {
|
||
|
|
$driveLetter = $matches[1];
|
||
|
|
$path = $matches[2];
|
||
|
|
$dirSeparator = '\\';
|
||
|
|
}
|
||
|
|
|
||
|
|
// If path is not absolute, make it relative to current working directory
|
||
|
|
if ($driveLetter === '' && substr($path, 0, 1) !== '/') {
|
||
|
|
$path = getcwd() . $dirSeparator . $path;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Split path into components
|
||
|
|
$parts = explode($dirSeparator, $path);
|
||
|
|
$resolved = [];
|
||
|
|
|
||
|
|
foreach ($parts as $part) {
|
||
|
|
// Skip empty parts (caused by multiple slashes)
|
||
|
|
if ($part === '' || $part === '.') {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handle parent directory
|
||
|
|
if ($part === '..') {
|
||
|
|
if (count($resolved) > 0) {
|
||
|
|
array_pop($resolved);
|
||
|
|
}
|
||
|
|
// If we're at root and encounter .., ignore it
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Add normal directory/file component
|
||
|
|
$resolved[] = $part;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reconstruct the path
|
||
|
|
$normalized = ($driveLetter !== '' ? $driveLetter . $dirSeparator : $dirSeparator) . implode($dirSeparator, $resolved);
|
||
|
|
|
||
|
|
// Handle root case
|
||
|
|
if ($normalized === $dirSeparator) {
|
||
|
|
return $dirSeparator;
|
||
|
|
}
|
||
|
|
|
||
|
|
return $normalized;
|
||
|
|
}
|