* @copyright 2018, Maxence Lange * @license GNU AGPL version 3 or any later version * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ namespace OCA\Social\Migration; use Closure; use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Types\Type; use Exception; use OCP\DB\ISchemaWrapper; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\SimpleMigrationStep; /** * Class Version0002Date20190226000001 * * @package OCA\Social\Migration */ class Version0002Date20190622000001 extends SimpleMigrationStep { /** @var IDBConnection */ private $connection; /** * @param IDBConnection $connection */ public function __construct(IDBConnection $connection) { $this->connection = $connection; } /** * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` * @param array $options * * @return ISchemaWrapper * @throws SchemaException */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options ): ISchemaWrapper { /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); if (!$schema->hasTable('social_a2_stream')) { return $schema; } $table = $schema->getTable('social_a2_stream'); if (!$table->hasColumn('details')) { $table->addColumn( 'details', Type::TEXT, [ 'notnull' => false ] ); } return $schema; } /** * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` * @param array $options * * @throws Exception */ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); $qb = $this->connection->getQueryBuilder(); $qb->delete('social_a2_stream'); $expr = $qb->expr(); $qb->where($expr->eq('type', $qb->createNamedParameter('Announce'))); $qb->execute(); $this->check( $schema, 'id', 'social_server_follows', 'social_a2_follows', 'accepted', 'boolean' ); $this->check( $schema, 'id', 'social_server_notes', 'social_a2_stream', 'local', 'boolean' ); $this->check( $schema, 'id', 'social_cache_actors', 'social_a2_cache_actors', 'local', 'boolean' ); $this->check( $schema, 'id', 'social_cache_documents', 'social_a2_cache_documts', 'public', 'boolean' ); $this->check( $schema, 'id', 'social_server_actors', 'social_a2_actors', 'creation', 'datetime' ); $this->check( $schema, 'id', 'social_server_follows', 'social_a2_follows', 'creation', 'datetime' ); $this->check( $schema, 'id', 'social_server_notes', 'social_a2_stream', 'creation', 'datetime' ); $this->check( $schema, 'id', 'social_cache_actors', 'social_a2_cache_actors', 'creation', 'datetime' ); $this->check( $schema, 'id', 'social_cache_documents', 'social_a2_cache_documts', 'creation', 'datetime' ); $this->check( $schema, 'id', 'social_cache_documents', 'social_a2_cache_documts', 'caching', 'datetime' ); // $this->check($schema, 'id', 'social_request_queue', 'social_a2_request_queue','last', 'datetime'); // $this->check($schema, 'id', 'social_queue_stream', 'social_a2_stream_queue', 'last', 'datetime'); $this->check( $schema, 'id', 'social_server_notes', 'social_a2_stream', 'published_time', 'datetime' ); } private function check( ISchemaWrapper $schema, string $prim, string $source, string $dest, string $field, string $type ) { if (!$schema->hasTable($source)) { return; } $qb = $this->connection->getQueryBuilder(); $qb->select($prim, $field) ->from($source); $cursor = $qb->execute(); while ($data = $cursor->fetch()) { $this->fixMigration($dest, $prim, $field, $type, $data); } $cursor->closeCursor(); } /** * @param string $k * @param array $arr * @param string $default * * @return string */ private function get(string $k, array $arr, string $default = ''): string { if ($arr === null) { return $default; } if (!array_key_exists($k, $arr)) { $subs = explode('.', $k, 2); if (sizeof($subs) > 1) { if (!array_key_exists($subs[0], $arr)) { return $default; } $r = $arr[$subs[0]]; if (!is_array($r)) { return $default; } return $this->get($subs[1], $r, $default); } else { return $default; } } if ($arr[$k] === null || !is_string($arr[$k]) && (!is_int($arr[$k]))) { return $default; } return (string)$arr[$k]; } /** * @param string $k * @param array $arr * @param bool $default * * @return bool */ protected function getBool(string $k, array $arr, bool $default = false): bool { if ($arr === null) { return $default; } if (!array_key_exists($k, $arr)) { $subs = explode('.', $k, 2); if (sizeof($subs) > 1) { if (!array_key_exists($subs[0], $arr)) { return $default; } return $this->getBool($subs[1], $arr[$subs[0]], $default); } else { return $default; } } if ($arr[$k] === null) { return $default; } if (is_bool($arr[$k])) { return $arr[$k]; } if ($arr[$k] === '1') { return true; } if ($arr[$k] === '0') { return false; } return $default; } // // // /** // * @param string $table // * @param array $fields // * @param array $data // * // * @throws Exception // */ // private function insertInto(string $table, array $fields, array $data) { // $insert = $this->connection->getQueryBuilder(); // $insert->insert($table); // // $datetimeFields = [ // 'creation', // 'last', // 'caching', // 'published_time' // ]; // // $booleanFields = [ // 'local', // 'public', // 'accepted', // 'hidden_on_timeline' // ]; // // foreach ($fields as $field) { // $value = $this->get($field, $data, ''); // if ($field === 'id_prim' // && $value === '' // && $this->get('id', $data, '') !== '') { // $value = hash('sha512', $this->get('id', $data, '')); // } // // if (in_array($field, $datetimeFields) && $value === '') { // $insert->setValue( // $field, // $insert->createNamedParameter(new DateTime('now'), IQueryBuilder::PARAM_DATE) // ); // } else if (in_array($field, $booleanFields) && $value === '') { // $insert->setValue( // $field, $insert->createNamedParameter('0') // ); // } else { // $insert->setValue( // $field, $insert->createNamedParameter($value) // ); // } // } // // try { // $insert->execute(); // } catch (UniqueConstraintViolationException $e) { // } // } /** * @param string $dest * @param string $prim * @param string $field * @param string $type * @param array $old */ private function fixMigration( string $dest, string $prim, string $field, string $type, array $old ) { // foreach ($fields as $field) { // $value = $this->get($field, $data, ''); // if ($field === 'id_prim' // && $value === '' // && $this->get('id', $data, '') !== '') { // $value = hash('sha512', $this->get('id', $data, '')); // } $current = $this->connection->getQueryBuilder(); $current->select($field) ->from($dest); $cursor = $current->execute(); $data = $cursor->fetch(); $cursor->closeCursor(); if ($data === false) { return; } switch ($type) { case 'datetime': $oldValue = $this->get($field, $old); $oldId = $this->get($prim, $old); $currValue = $this->get($field, $data, ''); if ($currValue !== '') { return; } if ($oldValue === '') { $oldValue = null; } $update = $this->connection->getQueryBuilder(); $update->update($dest); $update->set($field, $update->createNamedParameter($oldValue)); $expr = $update->expr(); $update->where($expr->eq($prim, $update->createNamedParameter($oldId))); try { $update->execute(); } catch (Exception $e) { } break; case 'boolean': $oldValue = $this->getBool($field, $old); $oldId = $this->get($prim, $old); $update = $this->connection->getQueryBuilder(); $update->update($dest); $update->set($field, $update->createNamedParameter(($oldValue) ? '1' : '0')); $expr = $update->expr(); $update->where($expr->eq($prim, $update->createNamedParameter($oldId))); $update->execute(); break; } } }