Merge pull request #1426 from aztech-dev/clean-boxes

Clean boxes
This commit is contained in:
Benoît Burnichon
2015-07-08 14:18:03 +02:00
17 changed files with 1381 additions and 839 deletions

View File

@@ -0,0 +1,100 @@
<?php
namespace Alchemy\Phrasea\Core\Connection;
class ConnectionSettings
{
/**
* @var string
*/
protected $host;
/**
* @var int
*/
protected $port;
/**
* @var string
*/
protected $databaseName;
/**
* @var string
*/
protected $user;
/**
* @var string
*/
protected $password;
/**
* @param string $host
* @param int|null $port
* @param string $databaseName
* @param string $user
* @param string $password
*/
public function __construct($host, $port, $databaseName, $user, $password)
{
$this->host = (string) $host;
$this->port = ((int) $port) > 0 ? (int) $port : null;
$this->databaseName = (string) $databaseName;
$this->user = $user;
$this->password = $password;
}
/**
* @return string
*/
public function getHost()
{
return $this->host;
}
/**
* @return int
*/
public function getPort()
{
return $this->port;
}
/**
* @return string
*/
public function getDatabaseName()
{
return $this->databaseName;
}
/**
* @return string
*/
public function getUser()
{
return $this->user;
}
/**
* @return string
*/
public function getPassword()
{
return $this->password;
}
public function toArray()
{
return [
'dbname' => $this->databaseName,
'host' => $this->host,
'port' => $this->port,
'user' => $this->user,
'password' => $this->password
];
}
}

View File

@@ -0,0 +1,600 @@
<?php
namespace Alchemy\Phrasea\Core\Database;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Setup\DoctrineMigrations\AbstractMigration;
use Doctrine\DBAL\Connection;
use vierbergenlars\SemVer\version;
class DatabaseMaintenanceService
{
private static $ormTables = [
'AggregateTokens',
'ApiAccounts',
'ApiApplications',
'ApiLogs',
'ApiOauthCodes',
'ApiOauthRefreshTokens',
'ApiOauthTokens',
'AuthFailures',
'BasketElements',
'Baskets',
'FeedEntries',
'FeedItems',
'FeedPublishers',
'Feeds',
'FeedTokens',
'FtpCredential',
'FtpExportElements',
'FtpExports',
'LazaretAttributes',
'LazaretChecks',
'LazaretFiles',
'LazaretSessions',
'OrderElements',
'Orders',
'Registrations',
'Secrets',
'SessionModules',
'Sessions',
'StoryWZ',
'Tasks',
'UserNotificationSettings',
'UserQueries',
'Users',
'UserSettings',
'UsrAuthProviders',
'UsrListOwners',
'UsrLists',
'UsrListsContent',
'ValidationDatas',
'ValidationParticipants',
'ValidationSessions',
];
private $app;
private $connection;
public function __construct(Application $application, Connection $connection)
{
$this->app = $application;
$this->connection = $connection;
}
public function upgradeDatabase(\base $base, $applyPatches)
{
$recommends = [];
$allTables = [];
$schema = $base->get_schema();
foreach ($schema->tables->table as $table) {
$allTables[(string)$table['name']] = $table;
}
$foundTables = $this->connection->fetchAll("SHOW TABLE STATUS");
foreach ($foundTables as $foundTable) {
$tableName = $foundTable["Name"];
if (isset($allTables[$tableName])) {
$engine = strtolower(trim($allTables[$tableName]->engine));
$ref_engine = strtolower($foundTable['Engine']);
if ($engine != $ref_engine && in_array($engine, ['innodb', 'myisam'])) {
$recommends = $this->alterTableEngine($tableName, $engine, $recommends);
}
$ret = $this->upgradeTable($allTables[$tableName]);
$recommends = array_merge($recommends, $ret);
unset($allTables[$tableName]);
} elseif (!in_array($tableName, self::$ormTables)) {
$recommends[] = [
'message' => 'Une table pourrait etre supprime',
'sql' => 'DROP TABLE ' . $base->get_dbname() . '.`' . $tableName . '`;'
];
}
}
foreach ($allTables as $tableName => $table) {
$this->createTable($table);
}
$current_version = $base->get_version();
if ($applyPatches) {
$this->applyPatches(
$base,
$current_version,
$this->app['phraseanet.version']->getNumber(),
false,
$this->app);
}
return $recommends;
}
/**
* @param $tableName
* @param $engine
* @param $recommends
* @return array
*/
public function alterTableEngine($tableName, $engine, array & $recommends)
{
$sql = 'ALTER TABLE `' . $tableName . '` ENGINE = ' . $engine;
try {
$this->connection->exec($sql);
} catch (\Exception $e) {
$recommends[] = [
'message' => $this->app->trans('Erreur lors de la tentative ; errreur : %message%',
['%message%' => $e->getMessage()]),
'sql' => $sql
];
}
}
/**
* @param \SimpleXMLElement $table
*/
public function createTable(\SimpleXMLElement $table)
{
$field_stmt = $defaults_stmt = [];
$create_stmt = "CREATE TABLE IF NOT EXISTS `" . $table['name'] . "` (";
foreach ($table->fields->field as $field) {
$isnull = trim($field->null) == "" ? "NOT NULL" : "NULL";
if (trim($field->default) != "" && trim($field->default) != "CURRENT_TIMESTAMP") {
$is_default = " default '" . $field->default . "'";
} elseif (trim($field->default) == "CURRENT_TIMESTAMP") {
$is_default = " default " . $field->default;
} else {
$is_default = '';
}
$character_set = '';
if (in_array(strtolower((string)$field->type), ['text', 'longtext', 'mediumtext', 'tinytext'])
|| substr(strtolower((string)$field->type), 0, 7) == 'varchar'
|| in_array(substr(strtolower((string)$field->type), 0, 4), ['char', 'enum'])
) {
$collation = trim((string)$field->collation) != '' ? trim((string)$field->collation) : 'utf8_unicode_ci';
$collations = array_reverse(explode('_', $collation));
$code = array_pop($collations);
$character_set = ' CHARACTER SET ' . $code . ' COLLATE ' . $collation;
}
$field_stmt[] = " `" . $field->name . "` " . $field->type . " "
. $field->extra . " " . $character_set . " "
. $is_default . " " . $isnull . "";
}
if ($table->indexes) {
foreach ($table->indexes->index as $index) {
switch ($index->type) {
case "PRIMARY":
$primary_fields = [];
foreach ($index->fields->field as $field) {
$primary_fields[] = "`" . $field . "`";
}
$field_stmt[] = 'PRIMARY KEY (' . implode(',', $primary_fields) . ')';
break;
case "UNIQUE":
$unique_fields = [];
foreach ($index->fields->field as $field) {
$unique_fields[] = "`" . $field . "`";
}
$field_stmt[] = 'UNIQUE KEY `' . $index->name . '` (' . implode(',', $unique_fields) . ')';
break;
case "INDEX":
$index_fields = [];
foreach ($index->fields->field as $field) {
$index_fields[] = "`" . $field . "`";
}
$field_stmt[] = 'KEY `' . $index->name . '` (' . implode(',', $index_fields) . ')';
break;
}
}
}
if ($table->defaults) {
foreach ($table->defaults->default as $default) {
$params = $dates_values = [];
$nonce = $this->app['random.medium']->generateString(16);
foreach ($default->data as $data) {
$k = trim($data['key']);
if ($k === 'usr_password') {
$data = $this->app['auth.password-encoder']->encodePassword($data, $nonce);
}
if ($k === 'nonce') {
$data = $nonce;
}
$v = trim(str_replace(["\r\n", "\r", "\n", "\t"], '', $data));
if (trim(mb_strtolower($v)) == 'now()') {
$dates_values [$k] = 'NOW()';
} else {
$params[$k] = (trim(mb_strtolower($v)) == 'null' ? null : $v);
}
}
$separator = ((count($params) > 0 && count($dates_values) > 0) ? ', ' : '');
$defaults_stmt[] = [
'sql' =>
'INSERT INTO `' . $table['name'] . '` (' . implode(', ', array_keys($params))
. $separator . implode(', ', array_keys($dates_values)) . ')
VALUES (:' . implode(', :', array_keys($params))
. $separator . implode(', ', array_values($dates_values)) . ') '
,
'params' => $params
];
}
}
$engine = mb_strtolower(trim($table->engine));
$engine = in_array($engine, ['innodb', 'myisam']) ? $engine : 'innodb';
$create_stmt .= implode(',', $field_stmt);
$create_stmt .= ") ENGINE=" . $engine . " CHARACTER SET utf8 COLLATE utf8_unicode_ci;";
$this->connection->exec($create_stmt);
foreach ($defaults_stmt as $def) {
$stmt = $this->connection->prepare($def['sql']);
$stmt->execute($def['params']);
}
unset($stmt);
}
public function upgradeTable(\SimpleXMLElement $table)
{
$correct_table = ['fields' => [], 'indexes' => [], 'collation' => []];
$alter = $alter_pre = $return = [];
foreach ($table->fields->field as $field) {
$expr = trim((string)$field->type);
$_extra = trim((string)$field->extra);
if ($_extra) {
$expr .= ' ' . $_extra;
}
$collation = trim((string)$field->collation) != '' ? trim((string)$field->collation) : 'utf8_unicode_ci';
if (in_array(strtolower((string)$field->type), ['text', 'longtext', 'mediumtext', 'tinytext'])
|| substr(strtolower((string)$field->type), 0, 7) == 'varchar'
|| in_array(substr(strtolower((string)$field->type), 0, 4), ['char', 'enum'])
) {
$collations = array_reverse(explode('_', $collation));
$code = array_pop($collations);
$collation = ' CHARACTER SET ' . $code . ' COLLATE ' . $collation;
$correct_table['collation'][trim((string)$field->name)] = $collation;
$expr .= $collation;
}
$_null = mb_strtolower(trim((string)$field->null));
if (!$_null || $_null == 'no') {
$expr .= ' NOT NULL';
}
$_default = (string)$field->default;
if ($_default && $_default != 'CURRENT_TIMESTAMP') {
$expr .= ' DEFAULT \'' . $_default . '\'';
} elseif ($_default == 'CURRENT_TIMESTAMP') {
$expr .= ' DEFAULT ' . $_default . '';
}
$correct_table['fields'][trim((string)$field->name)] = $expr;
}
if ($table->indexes) {
foreach ($table->indexes->index as $index) {
$i_name = (string)$index->name;
$expr = [];
foreach ($index->fields->field as $field) {
$expr[] = '`' . trim((string)$field) . '`';
}
$expr = implode(', ', $expr);
switch ((string)$index->type) {
case "PRIMARY":
$correct_table['indexes']['PRIMARY'] = 'PRIMARY KEY (' . $expr . ')';
break;
case "UNIQUE":
$correct_table['indexes'][$i_name] = 'UNIQUE KEY `' . $i_name . '` (' . $expr . ')';
break;
case "INDEX":
$correct_table['indexes'][$i_name] = 'KEY `' . $i_name . '` (' . $expr . ')';
break;
}
}
}
$sql = "SHOW FULL FIELDS FROM `" . $table['name'] . "`";
$rs2 = $this->connection->fetchAll($sql);
foreach ($rs2 as $row2) {
$f_name = $row2['Field'];
$expr_found = trim($row2['Type']);
$_extra = $row2['Extra'];
if ($_extra) {
$expr_found .= ' ' . $_extra;
}
$_collation = $row2['Collation'];
$current_collation = '';
if ($_collation) {
$_collation = explode('_', $row2['Collation']);
$expr_found .= $current_collation = ' CHARACTER SET ' . $_collation[0] . ' COLLATE ' . implode('_',
$_collation);
}
$_null = mb_strtolower(trim($row2['Null']));
if (!$_null || $_null == 'no') {
$expr_found .= ' NOT NULL';
}
$_default = $row2['Default'];
if ($_default) {
if (trim($row2['Type']) == 'timestamp' && $_default == 'CURRENT_TIMESTAMP') {
$expr_found .= ' DEFAULT CURRENT_TIMESTAMP';
} else {
$expr_found .= ' DEFAULT \'' . $_default . '\'';
}
}
if (isset($correct_table['fields'][$f_name])) {
if (isset($correct_table['collation'][$f_name]) && $correct_table['collation'][$f_name] != $current_collation) {
$old_type = mb_strtolower(trim($row2['Type']));
$new_type = false;
switch ($old_type) {
case 'text':
$new_type = 'blob';
break;
case 'longtext':
$new_type = 'longblob';
break;
case 'mediumtext':
$new_type = 'mediumblob';
break;
case 'tinytext':
$new_type = 'tinyblob';
break;
default:
if (substr($old_type, 0, 4) == 'char') {
$new_type = 'varbinary(255)';
}
if (substr($old_type, 0, 7) == 'varchar') {
$new_type = 'varbinary(767)';
}
break;
}
if ($new_type) {
$alter_pre[] = "ALTER TABLE `" . $table['name'] . "` CHANGE `$f_name` `$f_name` " . $new_type . "";
}
}
if (strtolower($expr_found) !== strtolower($correct_table['fields'][$f_name])) {
$alter[] = "ALTER TABLE `" . $table['name'] . "` CHANGE `$f_name` `$f_name` " . $correct_table['fields'][$f_name];
}
unset($correct_table['fields'][$f_name]);
} else {
$return[] = [
'message' => 'Un champ pourrait etre supprime',
'sql' => "ALTER TABLE " . $this->connection->getDatabase() . ".`" . $table['name'] . "` DROP `$f_name`;"
];
}
}
foreach ($correct_table['fields'] as $f_name => $expr) {
$alter[] = "ALTER TABLE `" . $table['name'] . "` ADD `$f_name` " . $correct_table['fields'][$f_name];
}
$tIndex = [];
$sql = "SHOW INDEXES FROM `" . $table['name'] . "`";
$rs2 = $this->connection->fetchAll($sql);
foreach ($rs2 as $row2) {
if (!isset($tIndex[$row2['Key_name']])) {
$tIndex[$row2['Key_name']] = ['unique' => ((int)($row2['Non_unique']) == 0), 'columns' => []];
}
$tIndex[$row2['Key_name']]['columns'][(int)($row2['Seq_in_index'])] = $row2['Column_name'];
}
foreach ($tIndex as $kIndex => $vIndex) {
$strColumns = [];
foreach ($vIndex['columns'] as $column) {
$strColumns[] = '`' . $column . '`';
}
$strColumns = '(' . implode(', ', $strColumns) . ')';
if ($kIndex == 'PRIMARY') {
$expr_found = 'PRIMARY KEY ' . $strColumns;
} else {
if ($vIndex['unique']) {
$expr_found = 'UNIQUE KEY `' . $kIndex . '` ' . $strColumns;
} else {
$expr_found = 'KEY `' . $kIndex . '` ' . $strColumns;
}
}
$full_name_index = ($kIndex == 'PRIMARY') ? 'PRIMARY KEY' : ('INDEX `' . $kIndex . '`');
if (isset($correct_table['indexes'][$kIndex])) {
if (mb_strtolower($expr_found) !== mb_strtolower($correct_table['indexes'][$kIndex])) {
$alter[] = 'ALTER TABLE `' . $table['name'] . '` DROP ' . $full_name_index . ', ADD ' . $correct_table['indexes'][$kIndex];
}
unset($correct_table['indexes'][$kIndex]);
} else {
$return[] = [
'message' => 'Un index pourrait etre supprime',
'sql' => 'ALTER TABLE ' . $this->connection->getDatabase() . '.`' . $table['name'] . '` DROP ' . $full_name_index . ';'
];
}
}
foreach ($correct_table['indexes'] as $kIndex => $expr) {
$alter[] = 'ALTER TABLE `' . $table['name'] . '` ADD ' . $expr;
}
foreach ($alter_pre as $a) {
try {
$this->connection->exec($a);
} catch (\Exception $e) {
$return[] = [
'message' => $this->app->trans('Erreur lors de la tentative ; errreur : %message%',
['%message%' => $e->getMessage()]),
'sql' => $a
];
}
}
foreach ($alter as $a) {
try {
$this->connection->exec($a);
} catch (\Exception $e) {
$return[] = [
'message' => $this->app->trans('Erreur lors de la tentative ; errreur : %message%',
['%message%' => $e->getMessage()]),
'sql' => $a
];
}
}
return $return;
}
public function applyPatches(\base $base, $from, $to, $post_process)
{
if (version::eq($from, $to)) {
return true;
}
$list_patches = [];
$iterator = new \DirectoryIterator($this->app['root.path'] . '/lib/classes/patch/');
foreach ($iterator as $fileinfo) {
if (!$fileinfo->isDot()) {
if (substr($fileinfo->getFilename(), 0, 1) == '.') {
continue;
}
$versions = array_reverse(explode('.', $fileinfo->getFilename()));
$classname = 'patch_' . array_pop($versions);
/** @var \patchAbstract $patch */
$patch = new $classname();
if (!in_array($base->get_base_type(), $patch->concern())) {
continue;
}
if (!!$post_process !== !!$patch->require_all_upgrades()) {
continue;
}
// if patch is older than current install
if (version::lte($patch->get_release(), $from)) {
continue;
}
// if patch is new than current target
if (version::gt($patch->get_release(), $to)) {
continue;
}
$n = 0;
do {
$key = $patch->get_release() . '.' . $n;
$n++;
} while (isset($list_patches[$key]));
$list_patches[$key] = $patch;
}
}
uasort($list_patches, function (\patchInterface $patch1, \patchInterface $patch2) {
return version::lt($patch1->get_release(), $patch2->get_release()) ? -1 : 1;
});
$success = true;
// disable mail
$this->app['swiftmailer.transport'] = null;
foreach ($list_patches as $patch) {
// Gets doctrine migrations required for current patch
foreach ($patch->getDoctrineMigrations() as $doctrineVersion) {
/** @var \Doctrine\DBAL\Migrations\Version $version */
$version = $this->app['doctrine-migration.configuration']->getVersion($doctrineVersion);
// Skip if already migrated
if ($version->isMigrated()) {
continue;
}
$migration = $version->getMigration();
// Handle legacy migrations
if ($migration instanceof AbstractMigration) {
// Inject entity manager
$migration->setEntityManager($this->app['orm.em']);
// Execute migration if not marked as migrated and not already applied by an older patch
if (!$migration->isAlreadyApplied()) {
$version->execute('up');
continue;
}
// Or mark it as migrated
$version->markMigrated();
} else {
$version->execute('up');
}
}
if (false === $patch->apply($base, $this->app)) {
$success = false;
}
}
return $success;
}
}

View File

@@ -0,0 +1,126 @@
<?php
namespace Alchemy\Phrasea\Core\Thumbnail;
use Alchemy\Phrasea\Application;
use MediaAlchemyst\Alchemyst;
use MediaAlchemyst\Specification\Image as ImageSpecification;
use MediaVorus\Media\Image;
use MediaVorus\Media\MediaInterface;
use MediaVorus\Media\Video;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\File;
abstract class AbstractThumbnailManager
{
/**
* @var string[]
*/
protected static $allowedMimeTypes = [
'image/gif',
'image/png',
'image/jpeg',
'image/jpg',
'image/pjpeg'
];
/**
* @var Alchemyst
*/
protected $alchemyst;
/**
* @var Application
*/
protected $application;
/**
* @var Filesystem
*/
protected $filesystem;
/**
* @var string
*/
protected $rootPath;
/**
* @param Application $application
* @param Alchemyst $alchemyst
* @param Filesystem $filesystem
* @param $rootPath
*/
public function __construct(Application $application, Alchemyst $alchemyst, Filesystem $filesystem, $rootPath)
{
$this->alchemyst = $alchemyst;
$this->application = $application;
$this->filesystem = $filesystem;
$this->rootPath = $rootPath;
}
protected function validateFileMimeType(File $file)
{
if (!in_array(mb_strtolower($file->getMimeType()), self::$allowedMimeTypes)) {
throw new \InvalidArgumentException('Invalid file format');
}
}
/**
* @param MediaInterface $media
* @param int $maxWidth
* @param int $maxHeight
* @return bool
*/
protected function shouldResize($media, $maxWidth, $maxHeight)
{
if (! $media instanceof Image && ! $media instanceof Video) {
return false;
}
return $media->getWidth() > $maxWidth || $media->getHeight() > $maxHeight;
}
/**
* @param ImageSpecification $imageSpec
* @param int $width
* @param int $height
*/
protected function setSpecificationSize(ImageSpecification $imageSpec, $width, $height)
{
$imageSpec->setResizeMode(ImageSpecification::RESIZE_MODE_INBOUND_FIXEDRATIO);
$imageSpec->setDimensions($width, $height);
}
/**
* @param File $file
* @param $imageSpec
* @return string
* @throws \MediaAlchemyst\Exception\FileNotFoundException
*/
protected function resizeMediaFile(File $file, $imageSpec)
{
$tmp = tempnam(sys_get_temp_dir(), 'tmpdatabox') . '.jpg';
$this->alchemyst->turninto($file->getPathname(), $tmp, $imageSpec);
return $tmp;
}
/**
* @param string $target
* @param string $filename
*/
protected function copyFile($target, $filename)
{
if (is_file($target)) {
$this->filesystem->remove($target);
}
if (null === $target || null === $filename) {
return;
}
$this->filesystem->mkdir(dirname($target), 0750);
$this->filesystem->copy($filename, $target, true);
$this->filesystem->chmod($target, 0760);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Alchemy\Phrasea\Core\Thumbnail;
use Alchemy\Phrasea\Application;
use MediaAlchemyst\Specification\Image as ImageSpecification;
use Symfony\Component\HttpFoundation\File\File;
class CollectionThumbnailManager extends AbstractThumbnailManager implements ThumbnailManager
{
/**
* @param ThumbnailedElement $element
* @param $thumbnailType
* @param File $file
*/
public function setThumbnail(ThumbnailedElement $element, $thumbnailType, File $file = null)
{
$filename = null;
if (!is_null($file)) {
$this->validateFileMimeType($file);
$filename = $this->generateThumbnail($thumbnailType, $file);
}
$logoFile = $this->rootPath . '/config/' . $thumbnailType . '/' . $element->getRootIdentifier();
$custom_path = $this->rootPath . '/www/custom/' . $thumbnailType . '/' . $element->getRootIdentifier();
foreach ([$logoFile, $custom_path] as $target) {
$this->copyFile($target, $filename);
}
$element->updateThumbnail($thumbnailType, $file);
}
/**
* @param $thumbnailType
* @param File $file
* @return string
*/
protected function generateThumbnail($thumbnailType, File $file)
{
$filename = $file->getPathname();
$imageSpec = new ImageSpecification();
if ($thumbnailType === ThumbnailManager::TYPE_LOGO) {
//resize collection logo
$media = $this->application->getMediaFromUri($filename);
if ($this->shouldResize($media, 120, 24)) {
$this->setSpecificationSize($imageSpec, 120, 24);
}
$filename = $this->resizeMediaFile($file, $imageSpec);
return $filename;
} elseif ($thumbnailType === ThumbnailManager::TYPE_PRESENTATION) {
//resize collection logo
$this->setSpecificationSize($imageSpec, 650, 200);
$filename = $this->resizeMediaFile($file, $imageSpec);
return $filename;
}
return $filename;
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace Alchemy\Phrasea\Core\Thumbnail;
use Alchemy\Phrasea\Application;
use MediaAlchemyst\Specification\Image as ImageSpecification;
use Symfony\Component\HttpFoundation\File\File;
class DataboxThumbnailManager extends AbstractThumbnailManager implements ThumbnailManager
{
public function setThumbnail(ThumbnailedElement $element, $thumbnailType, File $file = null)
{
$filename = null;
if ($thumbnailType !== ThumbnailManager::TYPE_PDF) {
throw new \InvalidArgumentException('Unsupported thumbnail type.');
}
if (!is_null($file)) {
$this->validateFileMimeType($file);
$filename = $this->generateThumbnail($file);
}
$logoFile = $this->rootPath . '/config/minilogos/' . $thumbnailType . '_' . $element->getRootIdentifier() . '.jpg';
$custom_path = $this->rootPath . '/www/custom/minilogos/' . $thumbnailType . '_' . $element->getRootIdentifier() . '.jpg';
foreach ([$logoFile, $custom_path] as $target) {
$this->copyFile($target, $filename);
}
$element->updateThumbnail($thumbnailType, $file);
}
/**
* @param File $file
* @return string
*/
protected function generateThumbnail(File $file)
{
$imageSpec = new ImageSpecification();
$this->setSpecificationSize($imageSpec, 120, 35);
$filename = $this->resizeMediaFile($file, $imageSpec);
return $filename;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Alchemy\Phrasea\Core\Thumbnail;
use Symfony\Component\HttpFoundation\File\File;
interface ThumbnailManager
{
const TYPE_LOGO = 'minilogos';
const TYPE_PDF = 'logopdf';
const TYPE_WM = 'wm';
const TYPE_STAMP = 'stamp';
const TYPE_PRESENTATION = 'presentation';
public function setThumbnail(ThumbnailedElement $element, $thumbnailType, File $file = null);
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Alchemy\Phrasea\Core\Thumbnail;
use Symfony\Component\HttpFoundation\File\File;
interface ThumbnailedElement
{
public function getRootIdentifier();
public function updateThumbnail($thumbnailType, File $file = null);
}

View File

@@ -13,14 +13,27 @@ namespace Alchemy\Phrasea\Core;
class Version
{
/**
* @var string
*/
private $number = '4.0.0-alpha.2';
/**
* @var string
*/
private $name = 'Herrerasaurus';
/**
* @return string
*/
public function getNumber()
{
return $this->number;
}
/**
* @return string
*/
public function getName()
{
return $this->name;

View File

@@ -0,0 +1,50 @@
<?php
namespace Alchemy\Phrasea\Core\Version;
use Alchemy\Phrasea\Core\Version;
use Doctrine\DBAL\Connection;
class AppboxVersionRepository implements VersionRepository
{
/**
* @var Connection
*/
private $connection;
/**
* @param Connection $connection
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/**
* @return string
*/
public function getVersion()
{
$result = $this->connection->fetchAssoc('SELECT version FROM sitepreff');
if ($result !== false) {
return $result['version'];
}
return VersionRepository::DEFAULT_VERSION;
}
/**
* @param Version $version
* @return bool
*/
public function saveVersion(Version $version)
{
$statement = $this->connection->executeQuery(
'UPDATE sitepreff SET version = :version WHERE id = 1',
[ ':version' => $version->getNumber() ]
);
return $statement->rowCount() == 1;
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace Alchemy\Phrasea\Core\Version;
use Alchemy\Phrasea\Core\Version;
use Doctrine\DBAL\Connection;
class DataboxVersionRepository implements VersionRepository
{
/**
* @var Connection
*/
private $connection;
/**
* @param Connection $connection
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/**
* @return string
*/
public function getVersion()
{
$result = $this->connection->fetchAssoc('SELECT value AS version FROM pref WHERE prop="version"');
if ($result !== false) {
return $result['version'];
}
return VersionRepository::DEFAULT_VERSION;
}
/**
* @param Version $version
* @return bool
* @throws \Doctrine\DBAL\DBALException
*/
public function saveVersion(Version $version)
{
$this->connection->exec("DELETE FROM pref WHERE prop='version'");
$statement = $this->connection->executeQuery(
'INSERT INTO pref (prop, value, locale, updated_on) VALUES ("version", :version, "", NOW())',
[ ':version' => $version->getNumber() ]
);
return $statement->rowCount() == 1;
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Alchemy\Phrasea\Core\Version;
use Alchemy\Phrasea\Core\Version;
interface VersionRepository
{
const DEFAULT_VERSION = '0.0.0.0';
/**
* @return string
*/
public function getVersion();
/**
* @param Version $version
* @return bool
*/
public function saveVersion(Version $version);
}

View File

@@ -17,7 +17,7 @@ class Setup_Upgrade
{
/**
*
* @var Applications
* @var Application
*/
private $app;
@@ -62,8 +62,8 @@ class Setup_Upgrade
/**
*
* @param type $recommendation
* @param type $command
* @param string $recommendation
* @param string $command
*/
public function addRecommendation($recommendation, $command = null)
{
@@ -81,9 +81,8 @@ class Setup_Upgrade
}
/**
*
*
* @return Setup_Upgrade
* @throws Exception_Setup_CannotWriteLockFile
*/
private function write_lock()
{

View File

@@ -11,6 +11,8 @@
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Configuration\AccessRestriction;
use Alchemy\Phrasea\Core\Connection\ConnectionSettings;
use Alchemy\Phrasea\Core\Version\AppboxVersionRepository;
use Alchemy\Phrasea\Databox\DataboxRepositoryInterface;
use Doctrine\ORM\Tools\SchemaTool;
use MediaAlchemyst\Alchemyst;
@@ -23,176 +25,66 @@ use vierbergenlars\SemVer\version;
class appbox extends base
{
/** @var int */
protected $id;
/**
* constant defining the app type
*/
const BASE_TYPE = self::APPLICATION_BOX;
protected $cache;
protected $connection;
protected $app;
protected $databoxes;
const CACHE_LIST_BASES = 'list_bases';
const CACHE_SBAS_IDS = 'sbas_ids';
/**
* @var int
*/
protected $id;
/**
* @var \databox[]
*/
protected $databoxes;
public function __construct(Application $app)
{
$connexion = $app['conf']->get(['main', 'database']);
parent::__construct($app, $app['db.provider']($connexion));
$connectionConfig = $app['conf']->get(['main', 'database']);
$connection = $app['db.provider']($connectionConfig);
$this->host = $connexion['host'];
$this->port = $connexion['port'];
$this->user = $connexion['user'];
$this->passwd = $connexion['password'];
$this->dbname = $connexion['dbname'];
$connectionSettings = new ConnectionSettings(
$connectionConfig['host'],
$connectionConfig['port'],
$connectionConfig['dbname'],
$connectionConfig['user'],
$connectionConfig['password']
);
$versionRepository = new AppboxVersionRepository($connection);
parent::__construct($app, $connection, $connectionSettings, $versionRepository);
}
public function write_collection_pic(Alchemyst $alchemyst, Filesystem $filesystem, collection $collection, SymfoFile $pathfile = null, $pic_type)
{
$filename = null;
$manager = new \Alchemy\Phrasea\Core\Thumbnail\CollectionThumbnailManager(
$this->app,
$alchemyst,
$filesystem,
$this->app['root.path']
);
if (!is_null($pathfile)) {
if (!in_array(mb_strtolower($pathfile->getMimeType()), ['image/gif', 'image/png', 'image/jpeg', 'image/jpg', 'image/pjpeg'])) {
throw new \InvalidArgumentException('Invalid file format');
}
$filename = $pathfile->getPathname();
if ($pic_type === collection::PIC_LOGO) {
//resize collection logo
$imageSpec = new ImageSpecification();
$media = $this->app->getMediaFromUri($filename);
if ($media->getWidth() > 120 || $media->getHeight() > 24) {
$imageSpec->setResizeMode(ImageSpecification::RESIZE_MODE_INBOUND_FIXEDRATIO);
$imageSpec->setDimensions(120, 24);
}
$tmp = tempnam(sys_get_temp_dir(), 'tmpdatabox') . '.jpg';
try {
$alchemyst->turninto($pathfile->getPathname(), $tmp, $imageSpec);
$filename = $tmp;
} catch (\MediaAlchemyst\Exception $e) {
}
} elseif ($pic_type === collection::PIC_PRESENTATION) {
//resize collection logo
$imageSpec = new ImageSpecification();
$imageSpec->setResizeMode(ImageSpecification::RESIZE_MODE_INBOUND_FIXEDRATIO);
$imageSpec->setDimensions(650, 200);
$tmp = tempnam(sys_get_temp_dir(), 'tmpdatabox') . '.jpg';
try {
$alchemyst->turninto($pathfile->getPathname(), $tmp, $imageSpec);
$filename = $tmp;
} catch (\MediaAlchemyst\Exception $e) {
}
}
}
switch ($pic_type) {
case collection::PIC_WM;
$collection->reset_watermark();
break;
case collection::PIC_LOGO:
case collection::PIC_PRESENTATION:
break;
case collection::PIC_STAMP:
$collection->reset_stamp();
break;
default:
throw new \InvalidArgumentException('unknown pic_type');
break;
}
if ($pic_type == collection::PIC_LOGO) {
$collection->update_logo($pathfile);
}
$file = $this->app['root.path'] . '/config/' . $pic_type . '/' . $collection->get_base_id();
$custom_path = $this->app['root.path'] . '/www/custom/' . $pic_type . '/' . $collection->get_base_id();
foreach ([$file, $custom_path] as $target) {
if (is_file($target)) {
$filesystem->remove($target);
}
if (null === $target || null === $filename) {
continue;
}
$filesystem->mkdir(dirname($target), 0750);
$filesystem->copy($filename, $target, true);
$filesystem->chmod($target, 0760);
}
$manager->setThumbnail($collection, $pic_type, $pathfile);
return $this;
}
public function write_databox_pic(Alchemyst $alchemyst, Filesystem $filesystem, databox $databox, SymfoFile $pathfile = null, $pic_type)
{
$filename = null;
$manager = new \Alchemy\Phrasea\Core\Thumbnail\DataboxThumbnailManager(
$this->app,
$alchemyst,
$filesystem,
$this->app['root.path']
);
if (!is_null($pathfile)) {
if (!in_array(mb_strtolower($pathfile->getMimeType()), ['image/jpeg', 'image/jpg', 'image/pjpeg', 'image/png', 'image/gif'])) {
throw new \InvalidArgumentException('Invalid file format');
}
}
if (!in_array($pic_type, [databox::PIC_PDF])) {
throw new \InvalidArgumentException('unknown pic_type');
}
if ($pathfile) {
$filename = $pathfile->getPathname();
$imageSpec = new ImageSpecification();
$imageSpec->setResizeMode(ImageSpecification::RESIZE_MODE_INBOUND_FIXEDRATIO);
$imageSpec->setDimensions(120, 35);
$tmp = tempnam(sys_get_temp_dir(), 'tmpdatabox') . '.jpg';
try {
$alchemyst->turninto($pathfile->getPathname(), $tmp, $imageSpec);
$filename = $tmp;
} catch (\MediaAlchemyst\Exception $e) {
}
}
$file = $this->app['root.path'] . '/config/minilogos/' . $pic_type . '_' . $databox->get_sbas_id() . '.jpg';
$custom_path = $this->app['root.path'] . '/www/custom/minilogos/' . $pic_type . '_' . $databox->get_sbas_id() . '.jpg';
foreach ([$file, $custom_path] as $target) {
if (is_file($target)) {
$filesystem->remove($target);
}
if (is_null($filename)) {
continue;
}
$filesystem->mkdir(dirname($target));
$filesystem->copy($filename, $target);
$filesystem->chmod($target, 0760);
}
$databox->delete_data_from_cache('printLogo');
$manager->setThumbnail($databox, $pic_type, $pathfile);
return $this;
}
@@ -216,9 +108,8 @@ class appbox extends base
}
/**
*
* @param databox $databox
* @param <type> $boolean
* @param bool $boolean
* @return appbox
*/
public function set_databox_indexable(databox $databox, $boolean)
@@ -237,9 +128,8 @@ class appbox extends base
}
/**
*
* @param databox $databox
* @return <type>
* @return bool
*/
public function is_databox_indexable(databox $databox)
{
@@ -256,8 +146,7 @@ class appbox extends base
}
/**
*
* @return const
* @return string
*/
public function get_base_type()
{

View File

@@ -10,39 +10,79 @@
*/
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Connection\ConnectionSettings;
use Alchemy\Phrasea\Core\Database\DatabaseMaintenanceService;
use Alchemy\Phrasea\Core\Version as PhraseaVersion;
use vierbergenlars\SemVer\version;
use Doctrine\DBAL\Connection;
abstract class base implements cache_cacheableInterface
{
protected $version;
/** @var int */
protected $id;
protected $schema;
protected $dbname;
protected $passwd;
protected $user;
protected $port;
protected $host;
/** @var Connection */
protected $connection;
/** @var Application */
protected $app;
const APPLICATION_BOX = 'APPLICATION_BOX';
const DATA_BOX = 'DATA_BOX';
public function __construct(Application $application, Connection $connection)
/**
* @var string
*/
protected $version;
/**
* @var int
*/
protected $id;
/**
* @var SimpleXMLElement
*/
protected $schema;
/**
* @var ConnectionSettings
*/
protected $connectionSettings;
/**
* @var Connection
*/
protected $connection;
/**
* @var Application
*/
protected $app;
/**
* @var PhraseaVersion\VersionRepository
*/
protected $versionRepository;
/**
* @param Application $application
* @param Connection $connection
* @param ConnectionSettings $connectionSettings
* @param PhraseaVersion\VersionRepository $versionRepository
*/
public function __construct(Application $application,
Connection $connection,
ConnectionSettings $connectionSettings,
PhraseaVersion\VersionRepository $versionRepository)
{
$this->app = $application;
$this->connection = $connection;
$this->connectionSettings = $connectionSettings;
$this->versionRepository = $versionRepository;
}
/**
* @return string
*/
abstract public function get_base_type();
/**
* @return SimpleXMLElement
* @throws Exception
*/
public function get_schema()
{
if ($this->schema) {
@@ -54,32 +94,49 @@ abstract class base implements cache_cacheableInterface
return $this->schema;
}
/**
* @return string
*/
public function get_dbname()
{
return $this->dbname;
return $this->connectionSettings->getDatabaseName();
}
/**
* @return string
*/
public function get_passwd()
{
return $this->passwd;
return $this->connectionSettings->getPassword();
}
/**
* @return string
*/
public function get_user()
{
return $this->user;
return $this->connectionSettings->getUser();
}
/**
* @return int
*/
public function get_port()
{
return $this->port;
return $this->connectionSettings->getPort();
}
/**
* @return string
*/
public function get_host()
{
return $this->host;
return $this->connectionSettings->getHost();
}
/** @return Connection */
/**
* @return Connection
*/
public function get_connection()
{
return $this->connection;
@@ -116,8 +173,10 @@ abstract class base implements cache_cacheableInterface
public function delete_data_from_cache($option = null)
{
$appbox = $this->get_base_type() == self::APPLICATION_BOX ? $this : $this->get_appbox();
if ($option === appbox::CACHE_LIST_BASES) {
$keys = [$this->get_cache_key(appbox::CACHE_LIST_BASES)];
phrasea::reset_sbasDatas($appbox);
phrasea::reset_baseDatas($appbox);
phrasea::clear_sbas_params($this->app);
@@ -126,8 +185,9 @@ abstract class base implements cache_cacheableInterface
}
if (is_array($option)) {
foreach ($option as $key => $value)
foreach ($option as $key => $value) {
$option[$key] = $this->get_cache_key($value);
}
return $this->get_cache()->deleteMulti($option);
} else {
@@ -147,166 +207,32 @@ abstract class base implements cache_cacheableInterface
public function get_version()
{
if ($this->version) {
return $this->version;
if (! $this->version) {
$this->version = $this->versionRepository->getVersion();
}
$version = '0.0.0';
$sql = '';
if ($this->get_base_type() == self::APPLICATION_BOX)
$sql = 'SELECT version FROM sitepreff';
if ($this->get_base_type() == self::DATA_BOX)
$sql = 'SELECT value AS version FROM pref WHERE prop="version" LIMIT 1;';
if ($sql !== '') {
$stmt = $this->get_connection()->prepare($sql);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if ($row)
$version = $row['version'];
}
$this->version = $version;
return $this->version;
}
protected function upgradeDb($apply_patches, Application $app)
{
$recommends = [];
$allTables = [];
$schema = $this->get_schema();
foreach ($schema->tables->table as $table) {
$allTables[(string) $table['name']] = $table;
}
$sql = "SHOW TABLE STATUS";
$stmt = $this->get_connection()->prepare($sql);
$stmt->execute();
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$ORMTables = [
'AggregateTokens',
'ApiAccounts',
'ApiApplications',
'ApiLogs',
'ApiOauthCodes',
'ApiOauthRefreshTokens',
'ApiOauthTokens',
'AuthFailures',
'BasketElements',
'Baskets',
'FeedEntries',
'FeedItems',
'FeedPublishers',
'Feeds',
'FeedTokens',
'FtpCredential',
'FtpExportElements',
'FtpExports',
'LazaretAttributes',
'LazaretChecks',
'LazaretFiles',
'LazaretSessions',
'OrderElements',
'Orders',
'Registrations',
'Secrets',
'SessionModules',
'Sessions',
'StoryWZ',
'Tasks',
'UserNotificationSettings',
'UserQueries',
'Users',
'UserSettings',
'UsrAuthProviders',
'UsrListOwners',
'UsrLists',
'UsrListsContent',
'ValidationDatas',
'ValidationParticipants',
'ValidationSessions',
];
foreach ($rs as $row) {
$tname = $row["Name"];
if (isset($allTables[$tname])) {
$engine = strtolower(trim($allTables[$tname]->engine));
$ref_engine = strtolower($row['Engine']);
if ($engine != $ref_engine && in_array($engine, ['innodb', 'myisam'])) {
$sql = 'ALTER TABLE `' . $tname . '` ENGINE = ' . $engine;
try {
$stmt = $this->get_connection()->prepare($sql);
$stmt->execute();
$stmt->closeCursor();
} catch (\Exception $e) {
$recommends[] = [
'message' => $app->trans('Erreur lors de la tentative ; errreur : %message%', ['%message%' => $e->getMessage()]),
'sql' => $sql
];
}
}
$ret = self::upgradeTable($allTables[$tname]);
$recommends = array_merge($recommends, $ret);
unset($allTables[$tname]);
} elseif ( ! in_array($tname, $ORMTables)) {
$recommends[] = [
'message' => 'Une table pourrait etre supprime',
'sql' => 'DROP TABLE ' . $this->dbname . '.`' . $tname . '`;'
];
}
}
foreach ($allTables as $tname => $table) {
$this->createTable($table);
}
$current_version = $this->get_version();
if ($apply_patches) {
$this->apply_patches($current_version, $app['phraseanet.version']->getNumber(), false, $app);
}
return $recommends;
}
protected function setVersion(PhraseaVersion $version)
{
try {
$sql = '';
if ($this->get_base_type() === self::APPLICATION_BOX)
$sql = 'UPDATE sitepreff SET version = :version WHERE id = 1';
if ($this->get_base_type() === self::DATA_BOX) {
$sql = 'DELETE FROM pref WHERE prop="version"';
$this->get_connection()->query($sql);
$sql = 'INSERT INTO pref (prop, value, locale, updated_on) VALUES ("version", :version, "", NOW())';
}
if ($sql !== '') {
$stmt = $this->get_connection()->prepare($sql);
$stmt->execute([':version' => $version->getNumber()]);
$stmt->closeCursor();
$this->version = $version->getNumber();
return true;
}
try {
return $this->versionRepository->saveVersion($version);
} catch (\Exception $e) {
throw new Exception('Unable to set the database version : '.$e->getMessage());
throw new Exception('Unable to set the database version : ' . $e->getMessage());
}
}
return;
protected function upgradeDb($applyPatches)
{
$service = new DatabaseMaintenanceService($this->app, $this->connection);
return $service->upgradeDatabase($this, $applyPatches);
}
/**
* @return base
* @throws Exception
*/
protected function load_schema()
{
@@ -318,12 +244,13 @@ abstract class base implements cache_cacheableInterface
throw new Exception('Unable to load schema');
}
if ($this->get_base_type() === self::APPLICATION_BOX)
if ($this->get_base_type() === self::APPLICATION_BOX) {
$this->schema = $structure->appbox;
elseif ($this->get_base_type() === self::DATA_BOX)
} elseif ($this->get_base_type() === self::DATA_BOX) {
$this->schema = $structure->databox;
else
} else {
throw new Exception('Unknown schema type');
}
return $this;
}
@@ -335,8 +262,10 @@ abstract class base implements cache_cacheableInterface
{
$this->load_schema();
$service = new DatabaseMaintenanceService($this->app, $this->connection);
foreach ($this->get_schema()->tables->table as $table) {
$this->createTable($table);
$service->createTable($table);
}
$this->setVersion($this->app['phraseanet.version']);
@@ -344,444 +273,10 @@ abstract class base implements cache_cacheableInterface
return $this;
}
/**
* @param SimpleXMLElement $table
* @return base
*/
protected function createTable(SimpleXMLElement $table)
public function apply_patches($from, $to, $post_process, Application $app)
{
$field_stmt = $defaults_stmt = [];
$service = new DatabaseMaintenanceService($this->app, $this->connection);
$create_stmt = "CREATE TABLE IF NOT EXISTS `" . $table['name'] . "` (";
foreach ($table->fields->field as $field) {
$isnull = trim($field->null) == "" ? "NOT NULL" : "NULL";
if (trim($field->default) != "" && trim($field->default) != "CURRENT_TIMESTAMP")
$is_default = " default '" . $field->default . "'";
elseif (trim($field->default) == "CURRENT_TIMESTAMP")
$is_default = " default " . $field->default;
else
$is_default = '';
$character_set = '';
if (in_array(strtolower((string) $field->type), ['text', 'longtext', 'mediumtext', 'tinytext'])
|| substr(strtolower((string) $field->type), 0, 7) == 'varchar'
|| in_array(substr(strtolower((string) $field->type), 0, 4), ['char', 'enum'])) {
$collation = trim((string) $field->collation) != '' ? trim((string) $field->collation) : 'utf8_unicode_ci';
$collations = array_reverse(explode('_', $collation));
$code = array_pop($collations);
$character_set = ' CHARACTER SET ' . $code . ' COLLATE ' . $collation;
}
$field_stmt[] = " `" . $field->name . "` " . $field->type . " "
. $field->extra . " " . $character_set . " "
. $is_default . " " . $isnull . "";
}
if ($table->indexes) {
foreach ($table->indexes->index as $index) {
switch ($index->type) {
case "PRIMARY":
$primary_fields = [];
foreach ($index->fields->field as $field) {
$primary_fields[] = "`" . $field . "`";
}
$field_stmt[] = 'PRIMARY KEY (' . implode(',', $primary_fields) . ')';
break;
case "UNIQUE":
$unique_fields = [];
foreach ($index->fields->field as $field) {
$unique_fields[] = "`" . $field . "`";
}
$field_stmt[] = 'UNIQUE KEY `' . $index->name . '` (' . implode(',', $unique_fields) . ')';
break;
case "INDEX":
$index_fields = [];
foreach ($index->fields->field as $field) {
$index_fields[] = "`" . $field . "`";
}
$field_stmt[] = 'KEY `' . $index->name . '` (' . implode(',', $index_fields) . ')';
break;
}
}
}
if ($table->defaults) {
foreach ($table->defaults->default as $default) {
$k = $v = $params = $dates_values = [];
$nonce = $this->app['random.medium']->generateString(16);
foreach ($default->data as $data) {
$k = trim($data['key']);
if ($k === 'usr_password')
$data = $this->app['auth.password-encoder']->encodePassword($data, $nonce);
if ($k === 'nonce')
$data = $nonce;
$v = trim(str_replace(["\r\n", "\r", "\n", "\t"], '', $data));
if (trim(mb_strtolower($v)) == 'now()')
$dates_values [$k] = 'NOW()';
else
$params[$k] = (trim(mb_strtolower($v)) == 'null' ? null : $v);
}
$separator = ((count($params) > 0 && count($dates_values) > 0) ? ', ' : '');
$defaults_stmt[] = [
'sql' =>
'INSERT INTO `' . $table['name'] . '` (' . implode(', ', array_keys($params))
. $separator . implode(', ', array_keys($dates_values)) . ')
VALUES (:' . implode(', :', array_keys($params))
. $separator . implode(', ', array_values($dates_values)) . ') '
, 'params' => $params
];
}
}
$engine = mb_strtolower(trim($table->engine));
$engine = in_array($engine, ['innodb', 'myisam']) ? $engine : 'innodb';
$create_stmt .= implode(',', $field_stmt);
$create_stmt .= ") ENGINE=" . $engine . " CHARACTER SET utf8 COLLATE utf8_unicode_ci;";
$stmt = $this->get_connection()->prepare($create_stmt);
$stmt->execute();
$stmt->closeCursor();
foreach ($defaults_stmt as $def) {
$stmt = $this->get_connection()->prepare($def['sql']);
$stmt->execute($def['params']);
$stmt->closeCursor();
}
unset($stmt);
return $this;
}
protected function upgradeTable(SimpleXMLElement $table)
{
$correct_table = ['fields' => [], 'indexes' => [], 'collation' => []];
$alter = $alter_pre = $return = [];
foreach ($table->fields->field as $field) {
$expr = trim((string) $field->type);
$_extra = trim((string) $field->extra);
if ($_extra)
$expr .= ' ' . $_extra;
$collation = trim((string) $field->collation) != '' ? trim((string) $field->collation) : 'utf8_unicode_ci';
if (in_array(strtolower((string) $field->type), ['text', 'longtext', 'mediumtext', 'tinytext'])
|| substr(strtolower((string) $field->type), 0, 7) == 'varchar'
|| in_array(substr(strtolower((string) $field->type), 0, 4), ['char', 'enum'])) {
$collations = array_reverse(explode('_', $collation));
$code = array_pop($collations);
$collation = ' CHARACTER SET ' . $code . ' COLLATE ' . $collation;
$correct_table['collation'][trim((string) $field->name)] = $collation;
$expr .= $collation;
}
$_null = mb_strtolower(trim((string) $field->null));
if ( ! $_null || $_null == 'no')
$expr .= ' NOT NULL';
$_default = (string) $field->default;
if ($_default && $_default != 'CURRENT_TIMESTAMP')
$expr .= ' DEFAULT \'' . $_default . '\'';
elseif ($_default == 'CURRENT_TIMESTAMP')
$expr .= ' DEFAULT ' . $_default . '';
$correct_table['fields'][trim((string) $field->name)] = $expr;
}
if ($table->indexes) {
foreach ($table->indexes->index as $index) {
$i_name = (string) $index->name;
$expr = [];
foreach ($index->fields->field as $field)
$expr[] = '`' . trim((string) $field) . '`';
$expr = implode(', ', $expr);
switch ((string) $index->type) {
case "PRIMARY":
$correct_table['indexes']['PRIMARY'] = 'PRIMARY KEY (' . $expr . ')';
break;
case "UNIQUE":
$correct_table['indexes'][$i_name] = 'UNIQUE KEY `' . $i_name . '` (' . $expr . ')';
break;
case "INDEX":
$correct_table['indexes'][$i_name] = 'KEY `' . $i_name . '` (' . $expr . ')';
break;
}
}
}
$sql = "SHOW FULL FIELDS FROM `" . $table['name'] . "`";
$stmt = $this->get_connection()->prepare($sql);
$stmt->execute();
$rs2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
foreach ($rs2 as $row2) {
$f_name = $row2['Field'];
$expr_found = trim($row2['Type']);
$_extra = $row2['Extra'];
if ($_extra)
$expr_found .= ' ' . $_extra;
$_collation = $row2['Collation'];
$current_collation = '';
if ($_collation) {
$_collation = explode('_', $row2['Collation']);
$expr_found .= $current_collation = ' CHARACTER SET ' . $_collation[0] . ' COLLATE ' . implode('_', $_collation);
}
$_null = mb_strtolower(trim($row2['Null']));
if ( ! $_null || $_null == 'no')
$expr_found .= ' NOT NULL';
$_default = $row2['Default'];
if ($_default) {
if (trim($row2['Type']) == 'timestamp' && $_default == 'CURRENT_TIMESTAMP')
$expr_found .= ' DEFAULT CURRENT_TIMESTAMP';
else
$expr_found .= ' DEFAULT \'' . $_default . '\'';
}
if (isset($correct_table['fields'][$f_name])) {
if (isset($correct_table['collation'][$f_name]) && $correct_table['collation'][$f_name] != $current_collation) {
$old_type = mb_strtolower(trim($row2['Type']));
$new_type = false;
switch ($old_type) {
case 'text':
$new_type = 'blob';
break;
case 'longtext':
$new_type = 'longblob';
break;
case 'mediumtext':
$new_type = 'mediumblob';
break;
case 'tinytext':
$new_type = 'tinyblob';
break;
default:
if (substr($old_type, 0, 4) == 'char')
$new_type = 'varbinary(255)';
if (substr($old_type, 0, 7) == 'varchar')
$new_type = 'varbinary(767)';
break;
}
if ($new_type) {
$alter_pre[] = "ALTER TABLE `" . $table['name'] . "` CHANGE `$f_name` `$f_name` " . $new_type . "";
}
}
if (strtolower($expr_found) !== strtolower($correct_table['fields'][$f_name])) {
$alter[] = "ALTER TABLE `" . $table['name'] . "` CHANGE `$f_name` `$f_name` " . $correct_table['fields'][$f_name];
}
unset($correct_table['fields'][$f_name]);
} else {
$return[] = [
'message' => 'Un champ pourrait etre supprime',
'sql' => "ALTER TABLE " . $this->dbname . ".`" . $table['name'] . "` DROP `$f_name`;"
];
}
}
foreach ($correct_table['fields'] as $f_name => $expr) {
$alter[] = "ALTER TABLE `" . $table['name'] . "` ADD `$f_name` " . $correct_table['fields'][$f_name];
}
$tIndex = [];
$sql = "SHOW INDEXES FROM `" . $table['name'] . "`";
$stmt = $this->get_connection()->prepare($sql);
$stmt->execute();
$rs2 = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
foreach ($rs2 as $row2) {
if ( ! isset($tIndex[$row2['Key_name']]))
$tIndex[$row2['Key_name']] = ['unique' => ((int) ($row2['Non_unique']) == 0), 'columns' => []];
$tIndex[$row2['Key_name']]['columns'][(int) ($row2['Seq_in_index'])] = $row2['Column_name'];
}
foreach ($tIndex as $kIndex => $vIndex) {
$strColumns = [];
foreach ($vIndex['columns'] as $column)
$strColumns[] = '`' . $column . '`';
$strColumns = '(' . implode(', ', $strColumns) . ')';
if ($kIndex == 'PRIMARY')
$expr_found = 'PRIMARY KEY ' . $strColumns;
else {
if ($vIndex['unique'])
$expr_found = 'UNIQUE KEY `' . $kIndex . '` ' . $strColumns;
else
$expr_found = 'KEY `' . $kIndex . '` ' . $strColumns;
}
$full_name_index = ($kIndex == 'PRIMARY') ? 'PRIMARY KEY' : ('INDEX `' . $kIndex . '`');
if (isset($correct_table['indexes'][$kIndex])) {
if (mb_strtolower($expr_found) !== mb_strtolower($correct_table['indexes'][$kIndex])) {
$alter[] = 'ALTER TABLE `' . $table['name'] . '` DROP ' . $full_name_index . ', ADD ' . $correct_table['indexes'][$kIndex];
}
unset($correct_table['indexes'][$kIndex]);
} else {
$return[] = [
'message' => 'Un index pourrait etre supprime',
'sql' => 'ALTER TABLE ' . $this->dbname . '.`' . $table['name'] . '` DROP ' . $full_name_index . ';'
];
}
}
foreach ($correct_table['indexes'] as $kIndex => $expr)
$alter[] = 'ALTER TABLE `' . $table['name'] . '` ADD ' . $expr;
foreach ($alter_pre as $a) {
try {
$stmt = $this->get_connection()->prepare($a);
$stmt->execute();
$stmt->closeCursor();
} catch (\Exception $e) {
$return[] = [
'message' => $this->app->trans('Erreur lors de la tentative ; errreur : %message%', ['%message%' => $e->getMessage()]),
'sql' => $a
];
}
}
foreach ($alter as $a) {
try {
$stmt = $this->get_connection()->prepare($a);
$stmt->execute();
$stmt->closeCursor();
} catch (\Exception $e) {
$return[] = [
'message' => $this->app->trans('Erreur lors de la tentative ; errreur : %message%', ['%message%' => $e->getMessage()]),
'sql' => $a
];
}
}
return $return;
}
protected function apply_patches($from, $to, $post_process, Application $app)
{
if (version::eq($from, $to)) {
return true;
}
$list_patches = [];
$iterator = new DirectoryIterator($this->app['root.path'] . '/lib/classes/patch/');
foreach ($iterator as $fileinfo) {
if ( ! $fileinfo->isDot()) {
if (substr($fileinfo->getFilename(), 0, 1) == '.')
continue;
$versions = array_reverse(explode('.', $fileinfo->getFilename()));
$classname = 'patch_' . array_pop($versions);
$patch = new $classname();
if ( ! in_array($this->get_base_type(), $patch->concern()))
continue;
if ( ! ! $post_process !== ! ! $patch->require_all_upgrades())
continue;
// if patch is older than current install
if (version::lte($patch->get_release(), $from)) {
continue;
}
// if patch is new than current target
if (version::gt($patch->get_release(), $to)) {
continue;
}
$n = 0;
do {
$key = $patch->get_release() . '.' . $n;
$n ++;
} while (isset($list_patches[$key]));
$list_patches[$key] = $patch;
}
}
uasort($list_patches, function (\patchInterface $patch1, \patchInterface $patch2) {
return version::lt($patch1->get_release(), $patch2->get_release()) ? -1 : 1;
});
$success = true;
// disable mail
$app['swiftmailer.transport'] = null;
foreach ($list_patches as $patch) {
// Gets doctrine migrations required for current patch
foreach ($patch->getDoctrineMigrations() as $doctrineVersion) {
/** @var \Doctrine\DBAL\Migrations\Version $version */
$version = $app['doctrine-migration.configuration']->getVersion($doctrineVersion);
// Skip if already migrated
if ($version->isMigrated()) {
continue;
}
$migration = $version->getMigration();
// Handle legacy migrations
if ($migration instanceof \Alchemy\Phrasea\Setup\DoctrineMigrations\AbstractMigration) {
// Inject entity manager
$migration->setEntityManager($app['orm.em']);
// Execute migration if not marked as migrated and not already applied by an older patch
if (!$migration->isAlreadyApplied()) {
$version->execute('up');
continue;
}
// Or mark it as migrated
$version->markMigrated();
} else {
$version->execute('up');
}
}
if (false === $patch->apply($this, $app)) {
$success = false;
}
}
return $success;
return $service->applyPatches($this, $from, $to, $post_process, $app);
}
}

View File

@@ -14,11 +14,14 @@ use Alchemy\Phrasea\Core\Event\Collection\CollectionEvent;
use Alchemy\Phrasea\Core\Event\Collection\CollectionEvents;
use Alchemy\Phrasea\Core\Event\Collection\CreatedEvent;
use Alchemy\Phrasea\Core\Event\Collection\NameChangedEvent;
use Alchemy\Phrasea\Core\Thumbnail\ThumbnailedElement;
use Alchemy\Phrasea\Core\Thumbnail\ThumbnailManager;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
use Alchemy\Phrasea\Model\Entities\User;
use Doctrine\DBAL\Driver\Connection;
use Symfony\Component\HttpFoundation\File\File;
class collection implements cache_cacheableInterface
class collection implements cache_cacheableInterface, ThumbnailedElement
{
protected $base_id;
protected $sbas_id;
@@ -227,6 +230,31 @@ class collection implements cache_cacheableInterface
return $this->databox->get_connection();
}
public function getRootIdentifier()
{
return $this->base_id;
}
public function updateThumbnail($thumbnailType, File $file = null)
{
switch ($thumbnailType) {
case ThumbnailManager::TYPE_WM;
$this->reset_watermark();
break;
case ThumbnailManager::TYPE_LOGO:
$this->update_logo($file);
break;
case ThumbnailManager::TYPE_PRESENTATION:
break;
case ThumbnailManager::TYPE_STAMP:
$this->reset_stamp();
break;
default:
throw new \InvalidArgumentException('Unsupported thumbnail type.');
}
}
public function set_public_presentation($publi)
{
if (in_array($publi, ['none', 'wm', 'stamp'])) {

View File

@@ -10,6 +10,8 @@
*/
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Connection\ConnectionSettings;
use Alchemy\Phrasea\Core\Version\DataboxVersionRepository;
use Alchemy\Phrasea\Databox\DataboxFieldRepositoryInterface;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Exception\InvalidArgumentException;
@@ -18,22 +20,34 @@ use Alchemy\Phrasea\Status\StatusStructureFactory;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Statement;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Translation\TranslatorInterface;
use Alchemy\Phrasea\Core\PhraseaTokens;
class databox extends base
class databox extends base implements \Alchemy\Phrasea\Core\Thumbnail\ThumbnailedElement
{
/** @var int */
protected $id;
/** @var string */
protected $structure;
const BASE_TYPE = self::DATA_BOX;
const CACHE_META_STRUCT = 'meta_struct';
const CACHE_THESAURUS = 'thesaurus';
const CACHE_COLLECTIONS = 'collections';
const CACHE_STRUCTURE = 'structure';
const PIC_PDF = 'logopdf';
/** @var array */
protected static $_xpath_thesaurus = [];
/** @var array */
protected static $_dom_thesaurus = [];
/** @var array */
protected static $_thesaurus = [];
/** @var SimpleXMLElement */
protected static $_sxml_thesaurus = [];
/** @var int */
protected $id;
/** @var string */
protected $structure;
/** @var array */
protected $_xpath_structure;
/** @var DOMDocument */
@@ -46,17 +60,8 @@ class databox extends base
protected $meta_struct;
/** @var databox_subdefsStructure */
protected $subdef_struct;
/** @var SimpleXMLElement */
protected static $_sxml_thesaurus = [];
const BASE_TYPE = self::DATA_BOX;
const CACHE_META_STRUCT = 'meta_struct';
const CACHE_THESAURUS = 'thesaurus';
const CACHE_COLLECTIONS = 'collections';
const CACHE_STRUCTURE = 'structure';
const PIC_PDF = 'logopdf';
protected $cache;
/** @var string[] */
private $labels = [];
private $ord;
private $viewname;
@@ -73,33 +78,33 @@ class databox extends base
$this->id = $sbas_id;
$connection_params = phrasea::sbas_params($app);
$connectionConfigs = phrasea::sbas_params($app);
if (! isset($connection_params[$sbas_id])) {
if (! isset($connectionConfigs[$sbas_id])) {
throw new NotFoundHttpException(sprintf('databox %d not found', $sbas_id));
}
$params = [
'host' => $connection_params[$sbas_id]['host'],
'port' => $connection_params[$sbas_id]['port'],
'user' => $connection_params[$sbas_id]['user'],
'password' => $connection_params[$sbas_id]['pwd'],
'dbname' => $connection_params[$sbas_id]['dbname'],
];
parent::__construct($app, $app['db.provider']($params));
$connectionConfig = $connectionConfigs[$sbas_id];
$connection = $app['db.provider']($connectionConfig);
$this->host = $params['host'];
$this->port = $params['port'];
$this->user = $params['user'];
$this->passwd = $params['password'];
$this->dbname = $params['dbname'];
$connectionSettings = new ConnectionSettings(
$connectionConfig['host'],
$connectionConfig['port'],
$connectionConfig['dbname'],
$connectionConfig['user'],
$connectionConfig['password']
);
$versionRepository = new DataboxVersionRepository($connection);
parent::__construct($app, $connection, $connectionSettings, $versionRepository);
$this->loadFromRow($row);
}
public function get_viewname()
{
return $this->viewname ? : $this->dbname;
return $this->viewname ? : $this->connectionSettings->getDatabaseName();
}
public function set_viewname($viewname)
@@ -131,6 +136,16 @@ class databox extends base
return $this->app->getApplicationBox();
}
public function getRootIdentifier()
{
return $this->get_sbas_id();
}
public function updateThumbnail($thumbnailType, File $file = null)
{
$this->delete_data_from_cache('printLogo');
}
/**
* @return collection[]
*/
@@ -481,11 +496,11 @@ class databox extends base
$password = $connection->getPassword();
$params = [
':host' => $host
, ':port' => $port
, ':dbname' => $dbname
, ':user' => $user
, ':password' => $password
':host' => $host,
':port' => $port,
':dbname' => $dbname,
':user' => $user,
':password' => $password
];
/** @var appbox $appbox */
@@ -519,6 +534,7 @@ class databox extends base
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
if ($row) {
$ord = $row['ord'] + 1;
}
@@ -571,6 +587,7 @@ class databox extends base
'password' => $password,
'dbname' => $dbname,
]);
$conn->connect();
$conn = $app->getApplicationBox()->get_connection();
@@ -586,12 +603,12 @@ class databox extends base
VALUES (null, :ord, :host, :port, :dbname, "MYSQL", :user, :password)';
$stmt = $conn->prepare($sql);
$stmt->execute([
':ord' => $ord
, ':host' => $host
, ':port' => $port
, ':dbname' => $dbname
, ':user' => $user
, ':password' => $password
':ord' => $ord,
':host' => $host,
':port' => $port,
':dbname' => $dbname,
':user' => $user,
':password' => $password
]);
$stmt->closeCursor();
@@ -665,8 +682,6 @@ class databox extends base
$n = 0;
$comp = $year . DIRECTORY_SEPARATOR . $month . DIRECTORY_SEPARATOR . $day . DIRECTORY_SEPARATOR;
$pathout = $repository_path . $comp;
while (($pathout = $repository_path . $comp . self::addZeros($n)) && is_dir($pathout) && iterator_count(new \DirectoryIterator($pathout)) > 100) {
$n ++;
}
@@ -690,36 +705,37 @@ class databox extends base
private static function addZeros($n, $length = 5)
{
while (strlen($n) < $length) {
$n = '0' . $n;
}
return $n;
return str_pad($n, $length, '0');
}
public function get_serialized_server_info()
{
return sprintf("%s@%s:%s (MySQL %s)", $this->dbname, $this->host, $this->port, $this->get_connection()->getWrappedConnection()->getAttribute(\PDO::ATTR_SERVER_VERSION));
return sprintf("%s@%s:%s (MySQL %s)",
$this->connectionSettings->getDatabaseName(),
$this->connectionSettings->getHost(),
$this->connectionSettings->getPort(),
$this->get_connection()->getWrappedConnection()->getAttribute(\PDO::ATTR_SERVER_VERSION)
);
}
public static function get_available_dcfields()
{
return [
databox_Field_DCESAbstract::Contributor => new databox_Field_DCES_Contributor()
, databox_Field_DCESAbstract::Coverage => new databox_Field_DCES_Coverage()
, databox_Field_DCESAbstract::Creator => new databox_Field_DCES_Creator()
, databox_Field_DCESAbstract::Date => new databox_Field_DCES_Date()
, databox_Field_DCESAbstract::Description => new databox_Field_DCES_Description()
, databox_Field_DCESAbstract::Format => new databox_Field_DCES_Format()
, databox_Field_DCESAbstract::Identifier => new databox_Field_DCES_Identifier()
, databox_Field_DCESAbstract::Language => new databox_Field_DCES_Language()
, databox_Field_DCESAbstract::Publisher => new databox_Field_DCES_Publisher()
, databox_Field_DCESAbstract::Relation => new databox_Field_DCES_Relation
, databox_Field_DCESAbstract::Rights => new databox_Field_DCES_Rights
, databox_Field_DCESAbstract::Source => new databox_Field_DCES_Source
, databox_Field_DCESAbstract::Subject => new databox_Field_DCES_Subject()
, databox_Field_DCESAbstract::Title => new databox_Field_DCES_Title()
, databox_Field_DCESAbstract::Type => new databox_Field_DCES_Type()
databox_Field_DCESAbstract::Contributor => new databox_Field_DCES_Contributor(),
databox_Field_DCESAbstract::Coverage => new databox_Field_DCES_Coverage(),
databox_Field_DCESAbstract::Creator => new databox_Field_DCES_Creator(),
databox_Field_DCESAbstract::Date => new databox_Field_DCES_Date(),
databox_Field_DCESAbstract::Description => new databox_Field_DCES_Description(),
databox_Field_DCESAbstract::Format => new databox_Field_DCES_Format(),
databox_Field_DCESAbstract::Identifier => new databox_Field_DCES_Identifier(),
databox_Field_DCESAbstract::Language => new databox_Field_DCES_Language(),
databox_Field_DCESAbstract::Publisher => new databox_Field_DCES_Publisher(),
databox_Field_DCESAbstract::Relation => new databox_Field_DCES_Relation(),
databox_Field_DCESAbstract::Rights => new databox_Field_DCES_Rights(),
databox_Field_DCESAbstract::Source => new databox_Field_DCES_Source(),
databox_Field_DCESAbstract::Subject => new databox_Field_DCES_Subject(),
databox_Field_DCESAbstract::Title => new databox_Field_DCES_Title(),
databox_Field_DCESAbstract::Type => new databox_Field_DCES_Type()
];
}
@@ -872,7 +888,7 @@ class databox extends base
$contents = str_replace(
["{{basename}}", "{{datapathnoweb}}"]
, [$this->dbname, rtrim($path_doc, '/').'/']
, [$this->connectionSettings->getDatabaseName(), rtrim($path_doc, '/').'/']
, $contents
);
@@ -1001,8 +1017,8 @@ class databox extends base
/**
*
* @param <type> $sbas_id
* @return <type>
* @param int $sbas_id
* @return string
*/
public static function getPrintLogo($sbas_id)
{

View File

@@ -58,7 +58,7 @@ class phrasea
self::$_sbas_params = [];
$sql = 'SELECT sbas_id, host, port, user, pwd, dbname FROM sbas';
$sql = 'SELECT sbas_id, host, port, user, pwd as password, dbname FROM sbas';
$stmt = $app->getApplicationBox()->get_connection()->prepare($sql);
$stmt->execute();
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);