Initial import
This commit is contained in:
commit
1fca98aea3
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
set_include_path
|
||||||
|
(
|
||||||
|
get_include_path ()
|
||||||
|
.PATH_SEPARATOR.__DIR__.'/package/usr/share/php'
|
||||||
|
);
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,9 @@
|
||||||
|
Package: php-vn-lib
|
||||||
|
Version: 1.0-5
|
||||||
|
Architecture: all
|
||||||
|
Maintainer: Juan Ferrer Toribio <juan@verdnatura.es>
|
||||||
|
Depends: php5-mysql
|
||||||
|
Section: misc
|
||||||
|
Priority: optional
|
||||||
|
Description: PHP libraries
|
||||||
|
PHP libraries.
|
|
@ -0,0 +1,222 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Vn\Db;
|
||||||
|
|
||||||
|
require_once ('vn/sql/sql.php');
|
||||||
|
require_once ('vn/db/exception.php');
|
||||||
|
|
||||||
|
use Vn\Sql\Render;
|
||||||
|
|
||||||
|
class Conn
|
||||||
|
{
|
||||||
|
private $conn = NULL;
|
||||||
|
private $isOpen = FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a new connection to the database.
|
||||||
|
*
|
||||||
|
* @param string $host The host name
|
||||||
|
* @param string $user The user name to authenticate
|
||||||
|
* @param string $pass The user password
|
||||||
|
* @param string $name The default schema name
|
||||||
|
*
|
||||||
|
* @return boolean %TRUE on success, %FALSE otherwise
|
||||||
|
**/
|
||||||
|
function open ($host, $user, $pass, $name)
|
||||||
|
{
|
||||||
|
$conn = $this->conn = mysqli_init ();
|
||||||
|
$conn->options (MYSQLI_READ_DEFAULT_FILE, __DIR__.'/my.cnf');
|
||||||
|
@$conn->real_connect ($host, $user, $pass, $name);
|
||||||
|
|
||||||
|
if (mysqli_connect_errno ())
|
||||||
|
{
|
||||||
|
sleep (1);
|
||||||
|
throw new Exception (mysqli_connect_errno (), mysqli_connect_error ());
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->isOpen = TRUE;
|
||||||
|
$this->query ('SET CHARACTER SET utf8');
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the current connection, if it's closed does nothing.
|
||||||
|
**/
|
||||||
|
function close ()
|
||||||
|
{
|
||||||
|
if ($this->isOpen)
|
||||||
|
$this->conn->close ();
|
||||||
|
|
||||||
|
$this->isOpen = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the connection is open.
|
||||||
|
*
|
||||||
|
* @return boolean %TRUE if connection is open, %FALSE otherwise
|
||||||
|
**/
|
||||||
|
function isOpen ()
|
||||||
|
{
|
||||||
|
return $this->isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the query and gets it's first row.
|
||||||
|
*
|
||||||
|
* @param string $query The SQL query
|
||||||
|
* @param mixed[] $params The query parameters
|
||||||
|
*
|
||||||
|
* @return mixed[] An associative array with the first row, %NULL if error
|
||||||
|
**/
|
||||||
|
function getRow ($query, $params = NULL)
|
||||||
|
{
|
||||||
|
$result = $this->query ($query, $params);
|
||||||
|
|
||||||
|
if ($result && ($row = $result->fetch_assoc ()))
|
||||||
|
return $row;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the query and gets the first value of the first row.
|
||||||
|
*
|
||||||
|
* @param string $query The SQL query
|
||||||
|
* @param mixed[] $params The query parameters
|
||||||
|
*
|
||||||
|
* @return mixed The value or %NULL if error
|
||||||
|
**/
|
||||||
|
function getValue ($query, $params = NULL)
|
||||||
|
{
|
||||||
|
$result = $this->query ($query, $params);
|
||||||
|
|
||||||
|
if ($result && ($row = $result->fetch_row ()))
|
||||||
|
return $row[0];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the query stored in the specified file and gets the result.
|
||||||
|
*
|
||||||
|
* @param string $query The SQL query
|
||||||
|
* @param mixed[] $params The query parameters
|
||||||
|
*
|
||||||
|
* @return mixed The value or %NULL if error
|
||||||
|
**/
|
||||||
|
function queryFromFile ($file, $params = NULL)
|
||||||
|
{
|
||||||
|
$query = file_get_contents ($file);
|
||||||
|
|
||||||
|
if ($query)
|
||||||
|
return $this->query ($query, $params);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the query and gets the result.
|
||||||
|
*
|
||||||
|
* @param string $query The SQL query
|
||||||
|
* @param mixed[] $params The query parameters
|
||||||
|
*
|
||||||
|
* @return mixed The value or %NULL if error
|
||||||
|
**/
|
||||||
|
function query ($query, $params = NULL)
|
||||||
|
{
|
||||||
|
$result = $this->conn->query (Render::toString ($query, $params));
|
||||||
|
|
||||||
|
if (!$result)
|
||||||
|
$this->checkError ();
|
||||||
|
else
|
||||||
|
while ($this->moreResults ())
|
||||||
|
$this->nextResult ();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute multiple queries separated by semicolons.
|
||||||
|
*
|
||||||
|
* @param string $query The SQL query
|
||||||
|
* @param mixed[] $params The query parameters
|
||||||
|
*
|
||||||
|
* @return mixed The value or %NULL if error
|
||||||
|
**/
|
||||||
|
function multiQuery ($query, $params = NULL)
|
||||||
|
{
|
||||||
|
$success = $this->conn->multi_query (Render::toString ($query, $params));
|
||||||
|
|
||||||
|
if (!$success)
|
||||||
|
$this->checkError ();
|
||||||
|
|
||||||
|
return $success;
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeResult ()
|
||||||
|
{
|
||||||
|
$result = $this->conn->store_result ();
|
||||||
|
|
||||||
|
if (!$result)
|
||||||
|
$this->checkError ();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function moreResults ()
|
||||||
|
{
|
||||||
|
return $this->conn->more_results ();
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextResult ()
|
||||||
|
{
|
||||||
|
return $this->conn->next_result ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkError ()
|
||||||
|
{
|
||||||
|
if ($this->conn->errno)
|
||||||
|
{
|
||||||
|
$code = $this->conn->errno;
|
||||||
|
$message = $this->conn->error;
|
||||||
|
|
||||||
|
$sql = 'SELECT es_es message, 2000 + id AS id '.
|
||||||
|
'FROM sql_error WHERE code = @err';
|
||||||
|
|
||||||
|
if ($code == 1305 && ($row = $this->getRow ($sql)))
|
||||||
|
throw new Exception ($row['id'], $row['message']);
|
||||||
|
else
|
||||||
|
throw new Exception ($code, $message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkWarnings ()
|
||||||
|
{
|
||||||
|
if ($this->conn->warning_count > 0)
|
||||||
|
if ($result = $this->conn->query ('SHOW WARNINGS'))
|
||||||
|
{
|
||||||
|
$warnings = [];
|
||||||
|
$sql = 'SELECT es_es message, 3000 + id AS id '.
|
||||||
|
'FROM sql_warning WHERE code = @warn';
|
||||||
|
|
||||||
|
while ($row = $result->fetch_assoc ())
|
||||||
|
{
|
||||||
|
if ($row['Code'] == 1265
|
||||||
|
&& ($warning = $this->getRow ($sql)))
|
||||||
|
{
|
||||||
|
$row['Code'] = $warning['id'];
|
||||||
|
$row['Message'] = $warning['message'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$warnings[] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $warnings;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once ('vn/sql/sql.php');
|
||||||
|
require_once ('vn/db/conn.php');
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Vn\Db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to store information about database errors.
|
||||||
|
**/
|
||||||
|
class Exception extends \Exception
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $code The code of message
|
||||||
|
* @param string $message The message string
|
||||||
|
**/
|
||||||
|
function __construct ($code, $message)
|
||||||
|
{
|
||||||
|
parent::__construct ($message, $code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,2 @@
|
||||||
|
[client]
|
||||||
|
enable-cleartext-plugin = 1
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once ('vn/lib/type.php');
|
||||||
|
require_once ('vn/lib/locale.php');
|
||||||
|
require_once ('vn/lib/log.php');
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,112 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
use Vn\Lib\Locale;
|
||||||
|
|
||||||
|
function i($stringId)
|
||||||
|
{
|
||||||
|
echo Locale::getString ($stringId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function s($stringId)
|
||||||
|
{
|
||||||
|
return Locale::getString ($stringId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Vn\Lib
|
||||||
|
{
|
||||||
|
class Locale
|
||||||
|
{
|
||||||
|
static $localeSet = FALSE;
|
||||||
|
static $locale = 'en';
|
||||||
|
static $strings = [];
|
||||||
|
static $paths = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the locale.
|
||||||
|
*
|
||||||
|
* @param string $locale The locale with 2 digits format, or %NULL to
|
||||||
|
* set the default
|
||||||
|
**/
|
||||||
|
static function set ($locale = NULL)
|
||||||
|
{
|
||||||
|
if (!isset ($locale))
|
||||||
|
$locale = self::$locale;
|
||||||
|
|
||||||
|
self::$locale = $locale;
|
||||||
|
setlocale (LC_ALL, $locale);
|
||||||
|
self::$localeSet = TRUE;
|
||||||
|
|
||||||
|
foreach (self::$paths as $path)
|
||||||
|
self::loadFile ($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the locale.
|
||||||
|
*
|
||||||
|
* @return string The locale with 2 digits format
|
||||||
|
**/
|
||||||
|
static function get ()
|
||||||
|
{
|
||||||
|
return self::$locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the translation of a string, if no translation is found the same
|
||||||
|
* string is returned.
|
||||||
|
*
|
||||||
|
* @param string $stringId The string to translate
|
||||||
|
* @return string The translated string
|
||||||
|
**/
|
||||||
|
static function getString ($stringId)
|
||||||
|
{
|
||||||
|
if (isset (self::$strings[$stringId]))
|
||||||
|
return self::$strings[$stringId];
|
||||||
|
else
|
||||||
|
return $stringId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a path where a JSON file with translations is located.
|
||||||
|
*
|
||||||
|
* @param string $path The JSON file path
|
||||||
|
**/
|
||||||
|
static function addPath ($path)
|
||||||
|
{
|
||||||
|
self::$paths[$path] = TRUE;
|
||||||
|
|
||||||
|
if (self::$localeSet)
|
||||||
|
self::loadFile ($path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads translations from a JSON file.
|
||||||
|
*
|
||||||
|
* @param string $path The JSON file path
|
||||||
|
**/
|
||||||
|
static function loadFile ($path)
|
||||||
|
{
|
||||||
|
$file = stream_resolve_include_path ('locale/'. self::$locale .'/'. $path .'.json');
|
||||||
|
|
||||||
|
if (file_exists ($file)
|
||||||
|
&& ($jsonString = file_get_contents ($file)))
|
||||||
|
self::addTranslations (json_decode ($jsonString, TRUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a set of translations to the database.
|
||||||
|
*
|
||||||
|
* @param array $strings Associative array of every string and its
|
||||||
|
* translation
|
||||||
|
**/
|
||||||
|
static function addTranslations ($strings)
|
||||||
|
{
|
||||||
|
foreach ($strings as $string => &$translation)
|
||||||
|
self::$strings[$string] = &$translation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Vn\Lib;
|
||||||
|
|
||||||
|
class Log
|
||||||
|
{
|
||||||
|
private static $fd = NULL;
|
||||||
|
private static $count;
|
||||||
|
private static $file;
|
||||||
|
|
||||||
|
static function init ($file)
|
||||||
|
{
|
||||||
|
self::close ();
|
||||||
|
self::$file = $file;
|
||||||
|
self::$fd = fopen ($file, 'a');
|
||||||
|
self::rename ();
|
||||||
|
set_error_handler ('Vn\Lib\Log::phpHandler', E_ALL);
|
||||||
|
}
|
||||||
|
static function phpHandler ($no, $str, $file, $line, $context)
|
||||||
|
{
|
||||||
|
self::write ('PHP: %s:%d: %s', $file, $line, $str);
|
||||||
|
|
||||||
|
switch ($no)
|
||||||
|
{
|
||||||
|
case E_ERROR:
|
||||||
|
case E_PARSE:
|
||||||
|
case E_CORE_ERROR:
|
||||||
|
case E_COMPILE_ERROR:
|
||||||
|
self::write ('PHP: Could not continue, exiting.');
|
||||||
|
self::close ();
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
static function close ()
|
||||||
|
{
|
||||||
|
if (self::$fd != NULL)
|
||||||
|
{
|
||||||
|
fclose (self::$fd);
|
||||||
|
self::$fd = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static function write ()
|
||||||
|
{
|
||||||
|
if (self::$fd == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (self::$count > 5000)
|
||||||
|
self::rename ();
|
||||||
|
|
||||||
|
self::$count += fprintf (self::$fd, "%s: %s\n"
|
||||||
|
,strftime ('%Y-%m-%d %T')
|
||||||
|
,call_user_func_array ('sprintf', func_get_args ())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
static private function rename ()
|
||||||
|
{
|
||||||
|
if (filesize (self::$file) > 1000000)
|
||||||
|
{
|
||||||
|
self::close ();
|
||||||
|
$rename = self::$file.'.1';
|
||||||
|
|
||||||
|
if (file_exists ($rename))
|
||||||
|
unlink ($rename);
|
||||||
|
|
||||||
|
rename (self::$file, $rename);
|
||||||
|
self::$fd = fopen ($file, 'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$n = -1;
|
||||||
|
define ('TYPE_NULL' , ++$n);
|
||||||
|
define ('TYPE_INTEGER' , ++$n);
|
||||||
|
define ('TYPE_TIME' , ++$n);
|
||||||
|
define ('TYPE_DATE' , ++$n);
|
||||||
|
define ('TYPE_DATE_TIME', ++$n);
|
||||||
|
define ('TYPE_STRING' , ++$n);
|
||||||
|
define ('TYPE_DOUBLE' , ++$n);
|
||||||
|
define ('TYPE_BOOLEAN' , ++$n);
|
||||||
|
define ('TYPE_OBJECT' , ++$n);
|
||||||
|
|
||||||
|
class Time extends DateTime {}
|
||||||
|
|
||||||
|
class Date extends DateTime {}
|
||||||
|
|
||||||
|
function get_type ($value)
|
||||||
|
{
|
||||||
|
if (is_int ($value))
|
||||||
|
return TYPE_INTEGER;
|
||||||
|
elseif (is_float ($value))
|
||||||
|
return TYPE_DOUBLE;
|
||||||
|
elseif (is_string ($value))
|
||||||
|
return TYPE_STRING;
|
||||||
|
elseif (is_bool ($value))
|
||||||
|
return TYPE_BOOLEAN;
|
||||||
|
elseif (is_object ($value))
|
||||||
|
{
|
||||||
|
if ($value instanceof Time)
|
||||||
|
return TYPE_TIME;
|
||||||
|
elseif ($value instanceof Date)
|
||||||
|
return TYPE_DATE;
|
||||||
|
elseif ($value instanceof DateTime)
|
||||||
|
return TYPE_DATE_TIME;
|
||||||
|
else
|
||||||
|
return TYPE_OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TYPE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_type (& $value, $type)
|
||||||
|
{
|
||||||
|
switch ($type)
|
||||||
|
{
|
||||||
|
case TYPE_INTEGER:
|
||||||
|
settype ($value, 'integer');
|
||||||
|
break;
|
||||||
|
case TYPE_DOUBLE:
|
||||||
|
settype ($value, 'float');
|
||||||
|
break;
|
||||||
|
case TYPE_STRING:
|
||||||
|
settype ($value, 'string');
|
||||||
|
break;
|
||||||
|
case TYPE_BOOLEAN:
|
||||||
|
settype ($value, 'boolean');
|
||||||
|
break;
|
||||||
|
case TYPE_NULL:
|
||||||
|
$value = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Vn\Sql;
|
||||||
|
|
||||||
|
require_once ('vn/lib/lib.php');
|
||||||
|
require_once ('vn/sql/value.php');
|
||||||
|
|
||||||
|
class Render
|
||||||
|
{
|
||||||
|
static function toString (&$query, &$paramsMap = NULL)
|
||||||
|
{
|
||||||
|
if (isset ($paramsMap) && is_array ($paramsMap) && count ($paramsMap) > 0)
|
||||||
|
{
|
||||||
|
$i = 0;
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
foreach ($paramsMap as $key => $value)
|
||||||
|
$params[$key] = (new Value ($value))->render ();
|
||||||
|
|
||||||
|
$replaceFunc = function ($matches) use (&$params, &$i)
|
||||||
|
{
|
||||||
|
$key = substr ($matches[0], 1);
|
||||||
|
|
||||||
|
if (strlen ($key) == 0)
|
||||||
|
$key = $i++;
|
||||||
|
if (isset ($params[$key]))
|
||||||
|
return $params[$key];
|
||||||
|
|
||||||
|
return '#'. $key;
|
||||||
|
};
|
||||||
|
|
||||||
|
return preg_replace_callback ('/#\w*/', $replaceFunc, $query);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function printf ($arg)
|
||||||
|
{
|
||||||
|
$count = count ($arg);
|
||||||
|
|
||||||
|
if ($count > 1)
|
||||||
|
{
|
||||||
|
for ($n = 1; $n < $count; $n++)
|
||||||
|
{
|
||||||
|
$obj = new Value ($arg[$n]);
|
||||||
|
$arg[$n] = $obj->render ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return call_user_func_array ('sprintf', $arg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return $arg[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once ('vn/lib/lib.php');
|
||||||
|
require_once ('vn/sql/render.php');
|
||||||
|
require_once ('vn/sql/value.php');
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Vn\Sql;
|
||||||
|
|
||||||
|
require_once ('vn/lib/lib.php');
|
||||||
|
|
||||||
|
class Value
|
||||||
|
{
|
||||||
|
var $value;
|
||||||
|
|
||||||
|
function __construct ($value)
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function render ()
|
||||||
|
{
|
||||||
|
$value = $this->value;
|
||||||
|
|
||||||
|
if ($value === NULL)
|
||||||
|
return 'NULL';
|
||||||
|
|
||||||
|
switch (get_type ($value))
|
||||||
|
{
|
||||||
|
case TYPE_STRING:
|
||||||
|
return '\'' . addslashes ($value) . '\'';
|
||||||
|
case TYPE_DATE:
|
||||||
|
return strftime ('\'%Y-%m-%d\'', $value->getTimestamp ());
|
||||||
|
case TYPE_TIME:
|
||||||
|
return strftime ('\'%T\'', $value->getTimestamp ());
|
||||||
|
case TYPE_DATE_TIME:
|
||||||
|
return strftime ('\'%Y-%m-%d %T\'', $value->getTimestamp ());
|
||||||
|
case TYPE_BOOLEAN:
|
||||||
|
return ($value) ? 'TRUE' : 'FALSE';
|
||||||
|
default:
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
Loading…
Reference in New Issue