Initial commit: CloudOps infrastructure platform

This commit is contained in:
root
2026-04-09 19:58:57 +02:00
commit 1166a52f26
7762 changed files with 839452 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace <namespace>;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class <className> extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'table_name';
protected function preUpAssertions(): void
{
// Please add an assertion for every SQL you define in the `up()` method.
// The order does matter! Examples:
/*
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName()),
"Table {$this->getPrefixedTableName()} already exists"
);
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName())->hasIndex('index_name'),
'Index index_name already exists'
);
*/
}
public function up(Schema $schema): void
{
// Add queries to modify the database schema. Examples:
/**
$table = $schema->getTable($this->getPrefixedTableName());
$table->addColumn('column_a', Types::DATETIME_IMMUTABLE)->setNotnull(false);
*/
/**
$this->addSql("CREATE INDEX index_a ON {$this->getPrefixedTableName()} (column_a, column_b);");
*/
}
public function down(Schema $schema): void
{
// If it makes sense for your migration, undo what was done in the up() method here.
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
/**
* This migration must run first otherwise the pre-up assertions for other migrations will fail on M5.
*/
final class Version020230615115326 extends AbstractMauticMigration
{
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE `{$this->prefix}message_channels` CHANGE `properties` `properties` JSON NOT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}emails` CHANGE `headers` `headers` JSON NOT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}form_fields` CHANGE `conditions` `conditions` JSON DEFAULT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}form_fields` CHANGE `validation` `validation` JSON DEFAULT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}dynamic_content` CHANGE `utm_tags` `utm_tags` JSON DEFAULT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}sms_message_stats` CHANGE `details` `details` JSON NOT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}sync_object_mapping` CHANGE `internal_storage` `internal_storage` JSON NOT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}imports` CHANGE `properties` `properties` JSON DEFAULT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}lead_event_log` CHANGE `properties` `properties` JSON DEFAULT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}reports` CHANGE `settings` `settings` JSON DEFAULT NULL COMMENT '(DC2Type:json)'");
$this->addSql("ALTER TABLE `{$this->prefix}tweet_stats` CHANGE `response_details` `response_details` JSON DEFAULT NULL COMMENT '(DC2Type:json)'");
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20190326190241 extends AbstractMauticMigration
{
/**
* @throws SkipMigration
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function preUp(Schema $schema): void
{
if ($schema->getTable("{$this->prefix}campaign_events")->hasColumn('failed_count')) {
throw new SkipMigration('Schema includes this migration');
}
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}campaign_events ADD failed_count INT NOT NULL;");
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
class Version20190410143658 extends AbstractMauticMigration
{
/**
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function preUp(Schema $schema): void
{
$newIndexName = $this->getNewIndexName();
$tableName = $this->getTableName();
$table = $schema->getTable($tableName);
if (true === $table->hasIndex($newIndexName)) {
throw new SkipMigration('Schema includes this migration');
}
}
/**
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function up(Schema $schema): void
{
$newIndexName = $this->getNewIndexName();
$tableName = $this->getTableName();
$oldIndexName = $this->getOldIndexName($tableName);
$this->addSql("ALTER TABLE {$tableName} ADD INDEX {$newIndexName} (lead_id, channel, reason);");
if ($schema->getTable($tableName)->hasIndex($oldIndexName)) {
$this->addSql("ALTER TABLE {$tableName} DROP INDEX {$oldIndexName};");
}
}
/**
* @param string $tableName
*
* @return string
*/
private function getOldIndexName($tableName)
{
return $this->generatePropertyName($tableName, 'idx', ['lead_id']);
}
/**
* @return string
*/
private function getNewIndexName()
{
return "{$this->prefix}leadid_reason_channel";
}
/**
* @return string
*/
private function getTableName()
{
return "{$this->prefix}lead_donotcontact";
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
use Mautic\LeadBundle\Field\Helper\IndexHelper;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20190524124819 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable("{$this->prefix}lead_fields")->hasColumn('is_index');
}, sprintf('Schema includes this migration'));
}
public function up(Schema $schema): void
{
$this->addSql("
ALTER TABLE {$this->prefix}lead_fields
ADD `is_index` TINYINT(1) DEFAULT 0 NOT NULL,
ADD `char_length_limit` INT(3) NULL;
");
$this->addSql("
UPDATE {$this->prefix}lead_fields
SET `char_length_limit` = 255
WHERE `type` IN ('text', 'select', 'multiselect', 'phone', 'url', 'email')
AND `char_length_limit` IS NULL;
");
$indexHelper = $this->container->get(IndexHelper::class);
$indexedColumns = implode("', '", $indexHelper->getIndexedColumnNames());
$this->addSql("
UPDATE {$this->prefix}lead_fields
SET `is_index` = TRUE
WHERE `alias` IN ('{$indexedColumns}');
");
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
class Version20191106152509 extends AbstractMauticMigration
{
public function up(Schema $schema): void
{
$this->addSql("
UPDATE {$this->prefix}lead_fields
SET `char_length_limit` = NULL
WHERE `type` NOT IN ('text', 'select', 'multiselect', 'phone', 'url', 'email')
AND `char_length_limit` IS NOT NULL;
");
}
}

View File

@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20200415135706 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
if ($schema->getTable("{$this->prefix}form_fields")->hasColumn('mapped_object')) {
throw new SkipMigration('Schema includes this migration');
}
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}form_fields
ADD mapped_object VARCHAR(191) DEFAULT NULL,
ADD mapped_field VARCHAR(191) DEFAULT NULL");
// All field that starts with company belongs to the company object.
// Except the company field itself that belongs to the contact (lead) object.
$this->addSql("UPDATE {$this->prefix}form_fields
SET mapped_object = CASE
WHEN lead_field LIKE 'company%' AND lead_field != 'company' THEN 'company'
ELSE 'contact'
END, mapped_field = lead_field
WHERE lead_field IS NOT NULL");
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
/**
* Migration for removing online status.
*/
class Version20200513162918 extends AbstractMauticMigration
{
/**
* @throws SkipMigration
* @throws SchemaException
*/
public function preUp(Schema $schema): void
{
if ($schema->getTable("{$this->prefix}email_copies")->hasColumn('body_text')) {
throw new SkipMigration("The body_text column has already been added to the {$this->prefix}email_copies table.");
}
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}email_copies ADD COLUMN `body_text` LONGTEXT NULL DEFAULT NULL AFTER `body`");
}
public function down(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}email_copies DROP COLUMN `body_text`");
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\TextType;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20200917152259 extends AbstractMauticMigration
{
/**
* @var string
*/
private $table = 'lead_fields';
/**
* @throws SkipMigration
*/
public function preUp(Schema $schema): void
{
if ($schema->getTable($this->getTableName())->getColumn('default_value')->getType() instanceof TextType) {
throw new SkipMigration('default_value is already the correct type.');
}
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->getTableName()} MODIFY `default_value` LONGTEXT NULL DEFAULT NULL");
}
public function down(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->getTableName()} MODIFY `default_value` VARCHAR(191) NULL DEFAULT NULL");
}
private function getTableName(): string
{
return $this->prefix.$this->table;
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20201007003589 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
if ($schema->getTable($this->prefix.'lead_fields')->hasColumn('column_is_not_removed')) {
throw new SkipMigration('Schema does not need this migration');
}
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}lead_fields ADD column_is_not_removed TINYINT(1) NOT NULL DEFAULT 0");
}
public function down(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}lead_fields DROP column_is_not_removed");
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20201019100000 extends AbstractMauticMigration
{
protected const TABLE_NAME = 'emails_draft';
/**
* @throws SkipMigration
*/
public function preUp(Schema $schema): void
{
if ($schema->hasTable($this->getPrefixedTableName())) {
throw new SkipMigration(sprintf('Table %s already exists. Skipping migration', $this->getPrefixedTableName()));
}
}
public function up(Schema $schema): void
{
$emailsTable = $schema->getTable($this->getPrefixedTableName('emails'));
$idColumn = $emailsTable->getColumn('id');
$idDataType = $idColumn->getUnsigned() ? 'UNSIGNED' : 'SIGNED';
$fkName = $this->generatePropertyName('emails_draft', 'fk', ['email_id']);
$ukName = $this->generatePropertyName('emails_draft', 'uniq', ['email_id']);
$this->addSql(
"CREATE TABLE `{$this->getPrefixedTableName()}` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`email_id` int(10) {$idDataType} NOT NULL,
`html` longtext,
`template` varchar(191) DEFAULT NULL,
`public_preview` tinyint(1) DEFAULT 1 NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `{$ukName}` (`email_id`),
CONSTRAINT `{$fkName}` FOREIGN KEY (`email_id`) REFERENCES `{$this->getPrefixedTableName('emails')}` (`id`)
)DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB ROW_FORMAT = DYNAMIC;"
);
}
public function down(Schema $schema): void
{
$schema->dropTable($this->getPrefixedTableName());
}
}

View File

@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Doctrine\ORM\EntityManagerInterface;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
use Mautic\CoreBundle\Helper\EmojiHelper;
use Mautic\DynamicContentBundle\Entity\DynamicContent;
use Mautic\EmailBundle\Entity\Copy;
use Mautic\EmailBundle\Entity\Email;
final class Version20201026101117 extends AbstractMauticMigration
{
/**
* @throws SkipMigration
*/
public function preUp(Schema $schema): void
{
$table = $schema->getTable($this->prefix.'emails');
if ('utf8mb4' === $table->getColumn('subject')->getPlatformOption('charset')) {
throw new SkipMigration('Schema includes this migration');
}
}
public function up(Schema $schema): void
{
// Note: all these columns are type of LONGTEXT.
$tables = [
'emails' => ['subject', 'custom_html', 'plain_text', 'name'],
'email_copies' => ['subject', 'body', 'body_text'],
];
foreach ($tables as $table => $columns) {
foreach ($columns as $column) {
$this->addSql("ALTER TABLE {$this->prefix}{$table} CHANGE {$column} {$column} LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
}
}
}
public function postUp(Schema $schema): void
{
$this->convertEmailsEmojies();
$this->convertEmailCopiesEmojies();
$this->convertDynamicContentEmojies();
}
private function convertEmailsEmojies(): void
{
$this->iterateOverAllEntities(
Email::class,
function (Email $email) {
$email->setName(EmojiHelper::toEmoji($email->getName(), 'short'));
$email->setSubject(EmojiHelper::toEmoji($email->getSubject(), 'short'));
$email->setCustomHtml(EmojiHelper::toEmoji($email->getCustomHtml(), 'short'));
$email->setPlainText(EmojiHelper::toEmoji($email->getPlainText(), 'short'));
}
);
}
private function convertEmailCopiesEmojies(): void
{
$this->iterateOverAllEntities(
Copy::class,
function (Copy $emailCopy) {
$emailCopy->setSubject(EmojiHelper::toEmoji($emailCopy->getSubject(), 'short'));
$emailCopy->setBody(EmojiHelper::toEmoji($emailCopy->getBody(), 'short'));
$emailCopy->setBodyText(EmojiHelper::toEmoji($emailCopy->getBodyText(), 'short'));
}
);
}
private function convertDynamicContentEmojies(): void
{
$this->iterateOverAllEntities(
DynamicContent::class,
function (DynamicContent $dynamicContent) {
$dynamicContent->setDescription(EmojiHelper::toEmoji($dynamicContent->getDescription(), 'short'));
}
);
}
private function iterateOverAllEntities(string $entityClass, callable $entityModifier): void
{
$entityManager = $this->container->get('doctrine.orm.entity_manager');
\assert($entityManager instanceof EntityManagerInterface);
$batchSize = 50;
$i = 1;
$q = $entityManager->createQuery("SELECT t from {$entityClass} t");
$iterableResult = $q->toIterable();
foreach ($iterableResult as $row) {
$entityModifier($row[0]);
$entityManager->persist($row[0]);
if (0 === ($i % $batchSize)) {
$entityManager->flush();
$entityManager->clear();
}
++$i;
}
$entityManager->flush();
}
}

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
use Mautic\PageBundle\Entity\PageDraft;
final class Version20201029001005 extends AbstractMauticMigration
{
/**
* @throws SkipMigration
*/
public function preUp(Schema $schema): void
{
if ($schema->hasTable($this->getPrefixedTableName(PageDraft::TABLE_NAME))) {
throw new SkipMigration(sprintf('Table %s already exists. Skipping migration', $this->getPrefixedTableName(PageDraft::TABLE_NAME)));
}
}
public function up(Schema $schema): void
{
$idDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'pages', 'id');
$fkName = $this->generatePropertyName('pages_draft', 'fk', ['page_id']);
$ukName = $this->generatePropertyName('pages_draft', 'uniq', ['page_id']);
$this->addSql(
sprintf(
'CREATE TABLE `%s` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`page_id` int(10) %s NOT NULL,
`html` longtext,
`template` varchar(191) DEFAULT NULL,
`public_preview` tinyint(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`),
UNIQUE KEY `%s` (`page_id`),
CONSTRAINT `%s` FOREIGN KEY (`page_id`) REFERENCES `%s` (`id`)
)DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB ROW_FORMAT = DYNAMIC;',
$this->getPrefixedTableName(PageDraft::TABLE_NAME),
$idDataType,
$ukName,
$fkName,
$this->getPrefixedTableName('pages')
)
);
}
public function down(Schema $schema): void
{
$schema->dropTable($this->getPrefixedTableName(PageDraft::TABLE_NAME));
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20210112162046 extends AbstractMauticMigration
{
protected const TABLE_NAME = 'sync_object_mapping';
private const INDEX_NAME = 'integration_integration_object_name_last_sync_date';
public function preUp(Schema $schema): void
{
$this->skipIf(
$this->indexExists($schema),
sprintf('Index `%s` already exists. Skipping the migration', static::INDEX_NAME)
);
}
public function up(Schema $schema): void
{
$this->addSql(sprintf(
'ALTER TABLE `%s` ADD INDEX `%s` (`integration`, `internal_object_name`, `last_sync_date`);',
$this->getPrefixedTableName(),
static::INDEX_NAME
));
}
public function preDown(Schema $schema): void
{
$this->skipIf(
!$this->indexExists($schema),
sprintf('Index `%s` doesn\'t exist. Skipping reverting the migration', static::INDEX_NAME)
);
}
public function down(Schema $schema): void
{
$this->addSql(sprintf(
'ALTER TABLE `%s` DROP INDEX `%s`;',
$this->getPrefixedTableName(),
static::INDEX_NAME
));
}
private function indexExists(Schema $schema): bool
{
return $schema->getTable($this->getPrefixedTableName())->hasIndex(static::INDEX_NAME);
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20210115065034 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
if ($schema->getTable($this->prefix.'emails')->hasColumn('preheader_text')) {
throw new SkipMigration('The emails table already includes the preheader_text column');
}
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}emails ADD `preheader_text` VARCHAR(130) NULL AFTER `subject`");
}
public function down(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}emails DROP COLUMN `preheader_text`");
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20210211081531 extends AbstractMauticMigration
{
private string $uuidColumn = 'uuid';
/**
* @var string[]
*/
private array $tableList = [
'assets',
'campaign_events',
'campaigns',
'categories',
'dynamic_content',
'emails',
'focus',
'form_actions',
'form_fields',
'forms',
'lead_fields',
'lead_lists',
'lead_tags',
'message_channels',
'messages',
'pages',
'point_trigger_events',
'point_triggers',
'points',
'push_notifications',
'reports',
'stages',
'sms_messages',
'monitoring',
];
public function preUp(Schema $schema): void
{
foreach ($this->tableList as $key => $table) {
if ($schema->getTable($this->prefix.$table)->hasColumn($this->uuidColumn)) {
unset($this->tableList[$key]);
}
}
}
public function up(Schema $schema): void
{
if (0 === count($this->tableList)) {
return;
}
$statements = [];
foreach ($this->tableList as $table) {
// When we migrate to MySql 8.0.13+,
// the below commented statement would suffice for settings default values using expression
// ALTER TABLE `{$this->prefix}{$table}` ALTER `{$this->uuidColumn}` SET DEFAULT bin_to_uuid(UUID());
$statements[] = "ALTER TABLE `{$this->prefix}{$table}` ADD COLUMN `{$this->uuidColumn}` char(36) default NULL;";
$statements[] = "UPDATE `{$this->prefix}{$table}` SET `{$this->uuidColumn}` = UUID() WHERE `{$this->uuidColumn}` IS NULL;";
}
$batchSql = implode(' ', $statements);
$this->addSql($batchSql);
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CampaignBundle\Entity\Campaign;
use Mautic\CampaignBundle\Entity\Event;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20210217115150 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
if ($schema->getTable($this->getPrefixedTableName(Campaign::TABLE_NAME))->hasColumn('deleted')
&& $schema->getTable($this->getPrefixedTableName(Event::TABLE_NAME))->hasColumn('deleted')
) {
throw new SkipMigration('Deleted column already added in tables');
}
}
public function up(Schema $schema): void
{
$schema->getTable($this->getPrefixedTableName(Campaign::TABLE_NAME))
->addColumn('deleted', Types::DATETIME_MUTABLE, ['notnull' => false]);
$schema->getTable($this->getPrefixedTableName(Event::TABLE_NAME))
->addColumn('deleted', Types::DATETIME_MUTABLE, ['notnull' => false]);
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
use Mautic\PageBundle\Entity\Page;
final class Version20210330075323 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$tableName = $this->getPrefixedTableName(Page::TABLE_NAME);
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($tableName)->hasColumn('public_preview'),
sprintf('Column %s already exists in table %s', 'public_preview', $tableName)
);
}
public function up(Schema $schema): void
{
$tableName = $this->getPrefixedTableName(Page::TABLE_NAME);
$this->addSql("ALTER TABLE {$tableName} ADD public_preview TINYINT(1) DEFAULT 1 NOT NULL;");
}
public function down(Schema $schema): void
{
$tableName = $this->getPrefixedTableName(Page::TABLE_NAME);
$this->addSql("ALTER TABLE {$tableName} DROP public_preview;");
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20210420113309 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getTableName())->hasIndex($this->getIndexName()),
"Index {$this->getIndexName()} already exists"
);
}
public function up(Schema $schema): void
{
$this->addSql("CREATE INDEX {$this->getIndexName()} ON {$this->getTableName()} (alias)");
}
public function down(Schema $schema): void
{
$this->addSql("DROP INDEX {$this->getIndexName()} ON {$this->getTableName()}");
}
private function getTableName(): string
{
return "{$this->prefix}lead_lists";
}
private function getIndexName(): string
{
return "{$this->prefix}lead_list_alias";
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20210818090322 extends PreUpAssertionMigration
{
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable("{$this->prefix}roles")->hasColumn('uuid');
}, sprintf('Column %s already exists', 'uuid'));
}
public function up(Schema $schema): void
{
$statements = [];
$statements[] = "ALTER TABLE `{$this->prefix}roles` ADD COLUMN `uuid` char(36) default NULL;";
$statements[] = "UPDATE `{$this->prefix}roles` SET `uuid` = UUID() WHERE `uuid` IS NULL;";
$batchSql = implode(' ', $statements);
$this->addSql($batchSql);
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20210819201726 extends PreUpAssertionMigration
{
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable("{$this->prefix}permissions")->hasColumn('uuid');
}, sprintf('Column %s already exists', 'uuid'));
}
public function up(Schema $schema): void
{
$statements = [];
$statements[] = "ALTER TABLE `{$this->prefix}permissions` ADD COLUMN `uuid` char(36) default NULL;";
$statements[] = "UPDATE `{$this->prefix}permissions` SET `uuid` = UUID() WHERE `uuid` IS NULL;";
$batchSql = implode(' ', $statements);
$this->addSql($batchSql);
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
use Mautic\LeadBundle\Field\Helper\IndexHelper;
final class Version20211020092759 extends PreUpAssertionMigration
{
private const TABLE = 'leads';
protected function preUpAssertions(): void
{
$this->skipAssertion(
function (Schema $schema) {
$table = $schema->getTable($this->getPrefixedTableName(self::TABLE));
return count($table->getIndexes()) >= IndexHelper::MAX_COUNT_ALLOWED || $table->hasIndex($this->getIndexName());
},
"Index {$this->getIndexName()} cannot be created because the {$this->getPrefixedTableName(self::TABLE)} has hit the table index limit or the index already exists"
);
}
public function up(Schema $schema): void
{
$this->addSql("CREATE INDEX {$this->getIndexName()} ON {$this->getPrefixedTableName(self::TABLE)} (date_modified)");
}
private function getIndexName(): string
{
return $this->prefix.'lead_date_modified';
}
}

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20211020114811 extends PreUpAssertionMigration
{
private const COMPANIES_TABLE = 'companies';
private const SYNC_OBJECT_MAPPING_TABLE = 'sync_object_mapping';
private const INDEX_COMPANY_MATCH = MAUTIC_TABLE_PREFIX.'company_match';
private const INDEX_INTEGRATION_OBJECT = MAUTIC_TABLE_PREFIX.'integration_object';
private const INDEX_INTEGRATION_REFERENCE = MAUTIC_TABLE_PREFIX.'integration_reference';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => !$schema->getTable($this->getPrefixedTableName(self::COMPANIES_TABLE))->hasIndex(self::INDEX_COMPANY_MATCH),
sprintf('The index %s does not exist in the %s table.', self::INDEX_COMPANY_MATCH, $this->getPrefixedTableName(self::COMPANIES_TABLE))
);
$this->skipAssertion(
fn (Schema $schema) => !$schema->getTable($this->getPrefixedTableName(self::SYNC_OBJECT_MAPPING_TABLE))->hasIndex(self::INDEX_INTEGRATION_OBJECT),
sprintf('The index %s does not exist in the %s table.', self::INDEX_INTEGRATION_OBJECT, $this->getPrefixedTableName(self::SYNC_OBJECT_MAPPING_TABLE))
);
$this->skipAssertion(
fn (Schema $schema) => !$schema->getTable($this->getPrefixedTableName(self::SYNC_OBJECT_MAPPING_TABLE))->hasIndex(self::INDEX_INTEGRATION_REFERENCE),
sprintf('The index %s does not exist in the %s table.', self::INDEX_INTEGRATION_REFERENCE, $this->getPrefixedTableName(self::SYNC_OBJECT_MAPPING_TABLE))
);
$this->skipAssertion(
fn () => empty($this->getTables()),
'No tables require character set conversion.'
);
}
public function up(Schema $schema): void
{
$this->addSql('SET FOREIGN_KEY_CHECKS=0;');
/*
* The index key prefix length limit is 3072 bytes for InnoDB tables.
* In utf8mb4, 1 char uses 4 bytes
* So if we are creating multiple columns index for 4 columns of varchar(255) then it will take 255*4*4 = 4080.
* 4080 bytes is more than max allowed limit 3072 bytes.
* So it was not allowing to convert charset for particular tables and was showing error
* "Specified key was too long; max key length is 3072 bytes"
*/
$dropIndexQuery = 'DROP INDEX %s ON %s';
$this->addSql(
sprintf(
$dropIndexQuery,
self::INDEX_COMPANY_MATCH,
$this->getPrefixedTableName(self::COMPANIES_TABLE)
)
);
$this->addSql(
sprintf(
'CREATE INDEX %s ON %s(`companyname`(191),`companycity`(191),`companycountry`(191),`companystate`(191))',
self::INDEX_COMPANY_MATCH,
$this->getPrefixedTableName(self::COMPANIES_TABLE)
)
);
$this->addSql(
sprintf(
$dropIndexQuery,
self::INDEX_INTEGRATION_OBJECT,
$this->getPrefixedTableName(self::SYNC_OBJECT_MAPPING_TABLE)
)
);
$this->addSql(
sprintf(
'CREATE INDEX %s ON %s(`integration`(191),`integration_object_name`(191),`integration_object_id`(191), `integration_reference_id`(191))',
self::INDEX_INTEGRATION_OBJECT,
$this->getPrefixedTableName(self::SYNC_OBJECT_MAPPING_TABLE)
)
);
$this->addSql(
sprintf(
$dropIndexQuery,
self::INDEX_INTEGRATION_REFERENCE,
$this->getPrefixedTableName(self::SYNC_OBJECT_MAPPING_TABLE)
)
);
$this->addSql(
sprintf(
'CREATE INDEX %s ON %s(`integration`(191),`integration_object_name`(191), `integration_reference_id`(191), `integration_object_id`(191))',
self::INDEX_INTEGRATION_REFERENCE,
$this->getPrefixedTableName(self::SYNC_OBJECT_MAPPING_TABLE)
)
);
$tables = $this->getTables();
foreach ($tables as $table) {
$tableName = $table['TABLE_NAME'];
$this->addSql(
sprintf(
'ALTER TABLE %s CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;',
$tableName)
);
}
$this->addSql('SET FOREIGN_KEY_CHECKS=1;');
}
/**
* @return mixed[]
*
* @throws Exception
*/
private function getTables(): array
{
$stmt = $this->connection->executeQuery(
"SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES AS T
INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY AS C
ON (C.collation_name = T.table_collation) WHERE
T.TABLE_SCHEMA = '{$this->connection->getDatabase()}' AND
(C.CHARACTER_SET_NAME != 'utf8mb4' OR C.COLLATION_NAME != 'utf8mb4_unicode_ci')"
);
return $stmt->fetchAllAssociative();
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20211020142629 extends AbstractMauticMigration
{
public function up(Schema $schema): void
{
$this->addSql("UPDATE {$this->getPrefixedTableName('leads')} SET date_modified = date_added WHERE date_modified IS NULL;");
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20211026152443 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getTableName())->hasIndex($this->getIndexName()),
"Index {$this->getIndexName()} already exists"
);
}
public function up(Schema $schema): void
{
$this->addSql("CREATE INDEX {$this->getIndexName()} ON {$this->getTableName()} (`object`, `field_order`, `is_published`)");
}
public function down(Schema $schema): void
{
$this->addSql("DROP INDEX {$this->getIndexName()} ON {$this->getTableName()}");
}
private function getTableName(): string
{
return "{$this->prefix}lead_fields";
}
private function getIndexName(): string
{
return "{$this->prefix}idx_object_field_order_is_published";
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20211026153057 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getTableName())->hasIndex($this->getIndexName()),
"Index {$this->getIndexName()} already exists"
);
}
public function up(Schema $schema): void
{
$this->addSql("CREATE INDEX {$this->getIndexName()} ON {$this->getTableName()} (`lead_id`, `date_added`)");
}
public function down(Schema $schema): void
{
$this->addSql("DROP INDEX {$this->getIndexName()} ON {$this->getTableName()}");
}
private function getTableName(): string
{
return "{$this->prefix}lead_frequencyrules";
}
private function getIndexName(): string
{
return "{$this->prefix}idx_frequency_date_added";
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\BooleanType;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20211110070503 extends PreUpAssertionMigration
{
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable("{$this->prefix}campaigns")->getColumn('allow_restart')->getType() instanceof BooleanType;
}, 'Column already in BOOLEAN type');
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE `{$this->prefix}campaigns` MODIFY `allow_restart` BOOLEAN NOT NULL;");
}
}

View File

@@ -0,0 +1,137 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\ORM\EntityManagerInterface;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
use Mautic\CoreBundle\Factory\ModelFactory;
use Mautic\UserBundle\Entity\Permission;
use Mautic\UserBundle\Entity\Role;
use Mautic\UserBundle\Model\RoleModel;
final class Version20211209022550 extends AbstractMauticMigration
{
public function postUp(Schema $schema): void
{
/** @var RoleModel $model */
$model = $this->container->get(ModelFactory::class)->getModel('user.role');
// Get all non admin roles.
$roles = $model->getEntities([
'orderBy' => 'r.id',
'orderByDir' => 'ASC',
'filter' => [
'where' => [
[
'col' => 'r.isAdmin',
'expr' => 'eq',
'val' => 0,
],
],
],
]);
/** @var Role $role */
foreach ($roles as $role) {
$rawPermissions = $role->getRawPermissions();
if (empty($rawPermissions)) {
continue;
}
$leadPermission = $rawPermissions['lead:leads'] ?? [];
$listPermission = $rawPermissions['lead:lists'] ?? [];
if (empty($leadPermission) && empty($listPermission)) {
continue;
}
// Map all leads permission to list.
$newPermissions = $leadPermission;
if (!in_array('full', $newPermissions)) {
// If lead has viewown permission, then add create permission for list.
if (in_array('viewown', $leadPermission)) {
$newPermissions[] = 'create';
}
// Add the list related permission.
foreach ($listPermission as $perm) {
$newPermissions[] = $perm;
}
}
$perms = array_unique($newPermissions);
$rawPermissions['lead:lists'] = $perms;
$bit = $this->getPermissionBitwise($perms);
// We have to get the segment permission to update the bitwise value.
// The rest of the permission will stay as-is.
$this->setBitwise($role, $bit, $rawPermissions);
}
}
/**
* @param string[] $perms
*/
private function getPermissionBitwise(array $perms): int
{
$permBitwise = [
'viewown' => 2,
'viewother' => 4,
'editown' => 8,
'editother' => 16,
'create' => 32,
'deleteown' => 64,
'deleteother' => 128,
'full' => 1024,
];
$bit = 0;
foreach ($perms as $perm) {
$bit += $permBitwise[$perm];
}
return $bit;
}
/**
* @param mixed[] $rawPermissions
*/
private function setBitwise(Role $role, int $bit, array $rawPermissions): void
{
$entityManager = $this->container->get('doctrine.orm.entity_manager');
\assert($entityManager instanceof EntityManagerInterface);
$isPresent = false;
/** @var Permission $permission */
foreach ($role->getPermissions()->getIterator() as $permission) {
if ('lists' !== $permission->getName()) {
continue;
}
$isPresent = true;
$permission->setBitwise($bit);
$entityManager->persist($permission);
break;
}
if (!$isPresent) {
$permission = new Permission();
$permission->setBundle('lead');
$permission->setName('lists');
$permission->setBitwise($bit);
$entityManager->persist($permission);
$role->addPermission($permission);
}
$role->setRawPermissions($rawPermissions);
$entityManager->persist($role);
$entityManager->flush();
}
}

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20220208032455 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
$sql = sprintf('select id from %s%s limit 1', $this->prefix, 'forms');
$recordCount = $this->connection->executeQuery($sql)->fetchOne();
return !(bool) $recordCount;
}, 'Migration is not required.');
}
public function up(Schema $schema): void
{
$formFields = $this->getFormFields();
foreach ($formFields as $formId => $formField) {
$tableName = $this->prefix.'form_results_'.$formId.'_'.$formField['alias'];
// Get table schema
$columns = $this->getTableSchema($schema, $tableName);
if (empty($columns)) {
continue;
}
$fields = $formField['fields'];
$deleteFields = array_diff($columns, $fields);
$dropColumns = array_map(function (string $column) {
return sprintf('DROP COLUMN %s', $column);
}, $deleteFields);
if ($dropColumns) {
$this->addSql(sprintf('ALTER TABLE %s %s', $tableName, implode(', ', $dropColumns)));
}
}
}
/**
* @return array<int, array<string, string|array<int, string>>>
*/
private function getFormFields(): array
{
$sqlString = sprintf('SELECT f.id as form_id, f.alias as form_name, ff.id as field_id, ff.alias as column_name FROM %s%s f JOIN %s%s ff on ff.form_id = f.id', $this->prefix, 'forms', $this->prefix, 'form_fields');
$results = $this->connection->executeQuery($sqlString)->fetchAllAssociative();
$formFields = [];
foreach ($results as $row) {
$formId = $row['form_id'];
$formFields[$formId]['alias'] = $row['form_name'];
$formFields[$formId]['fields'][$row['field_id']] = $row['column_name'];
}
return $formFields;
}
/**
* @return string[]
*/
private function getTableSchema(Schema $schema, string $tableName): array
{
try {
$columns = $schema->getTable($tableName)->getColumns();
unset($columns['submission_id'], $columns['form_id']);
return array_keys($columns);
} catch (\Exception $e) {
return [];
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20220216161028 extends AbstractMauticMigration
{
public function up(Schema $schema): void
{
$oldAndNewValues = [
$this->connection->quote('Val d\'Oise') => $this->connection->quote('Val-d\'Oise'),
$this->connection->quote('Réunion') => $this->connection->quote('La Réunion'),
];
foreach ($oldAndNewValues as $old => $new) {
$this->addSql("UPDATE `{$this->prefix}leads` SET `state` = {$new} WHERE `state` = {$old}");
$this->addSql("UPDATE `{$this->prefix}companies` SET `companystate` = {$new} WHERE `companystate` = {$old}");
}
$filterOldAndNewValues = [
$this->connection->quote('s:6:"filter";s:10:"Val d\'Oise";') => $this->connection->quote('s:6:"filter";s:10:"Val-d\'Oise";'),
$this->connection->quote('s:6:"filter";s:8:"Réunion";') => $this->connection->quote('s:6:"filter";s:11:"La Réunion";'),
];
foreach ($filterOldAndNewValues as $old => $new) {
$this->addSql("UPDATE `{$this->prefix}dynamic_content` SET `filters` = REPLACE(`filters`, {$old}, {$new})");
$this->addSql("UPDATE `{$this->prefix}lead_lists` SET `filters` = REPLACE(`filters`, {$old}, {$new})");
$this->addSql("UPDATE `{$this->prefix}emails` SET `dynamic_content` = REPLACE(`dynamic_content`, {$old}, {$new})");
}
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\StringType;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20220223072252 extends PreUpAssertionMigration
{
private const TABLE = 'campaign_events';
protected function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getPrefixedTableName(self::TABLE))->getColumn('channel_id')->getType() instanceof StringType;
}, 'Column already in Varchar type');
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE `{$this->getPrefixedTableName(self::TABLE)}` MODIFY `channel_id` VARCHAR(64) DEFAULT NULL;");
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\Table;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
class Version20220429091934 extends PreUpAssertionMigration
{
private const SIGNED = 'SIGNED';
private const UNSIGNED = 'UNSIGNED';
protected function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->hasTable("{$this->prefix}contact_export_scheduler");
}, sprintf('Table %s already exists', "{$this->prefix}contact_export_scheduler"));
}
public function up(Schema $schema): void
{
$userIdFK = $this->generatePropertyName('users', 'fk', ['user_id']);
$contactExportSchedulerTableName = "{$this->prefix}contact_export_scheduler";
$usersTableName = "{$this->prefix}users";
$usersIdDataType = $this->getColumnDataType($schema->getTable($usersTableName), 'id');
$this->addSql(
"# Creating table {$this->prefix}contact_export_scheduler
# ------------------------------------------------------------
CREATE TABLE {$contactExportSchedulerTableName} (
id INT AUTO_INCREMENT NOT NULL,
user_id INT {$usersIdDataType} NOT NULL,
scheduled_datetime DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)',
data LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)',
PRIMARY KEY(id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;"
);
$this->addSql("ALTER TABLE {$contactExportSchedulerTableName} ADD CONSTRAINT {$userIdFK} FOREIGN KEY (user_id) REFERENCES $usersTableName (id)");
}
public function down(Schema $schema): void
{
$this->addSql(
"# Dropping table {$this->prefix}contact_export_scheduler
# ------------------------------------------------------------
DROP TABLE {$this->prefix}contact_export_scheduler"
);
}
/**
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
private function getColumnDataType(Table $table, string $columnName): string
{
$column = $table->getColumn($columnName);
return $column->getUnsigned() ? self::UNSIGNED : self::SIGNED;
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20220722074516 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
if ($schema->getTable($this->getTableName())->hasColumn('deduplicate')) {
throw new SkipMigration("The deduplicate column has already been added to the {$this->getTableName()} table.");
}
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->getTableName()} ADD deduplicate VARCHAR(32) DEFAULT NULL");
$this->addSql("CREATE INDEX deduplicate_date_added ON {$this->getTableName()} (deduplicate, date_added)");
}
public function down(Schema $schema): void
{
$this->addSql("DROP INDEX deduplicate_date_added ON {$this->getTableName()}");
$this->addSql("ALTER TABLE {$this->getTableName()} DROP deduplicate");
}
private function getTableName(): string
{
return $this->prefix.'notifications';
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20221010121758 extends AbstractMauticMigration
{
public function up(Schema $schema): void
{
$this->addSql("UPDATE `{$this->prefix}leads` SET `state` = 'Uttarakhand' WHERE `state` = 'Uttaranchal'");
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20221014061125 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
if ($schema->getTable($this->getTableName())->hasIndex($this->getIndexName())) {
throw new SkipMigration(sprintf('Index %s already exists', $this->getIndexName()));
}
}
public function up(Schema $schema): void
{
$this->addSql(sprintf('CREATE INDEX %s ON %s (webhook_id, date_added)', $this->getIndexName(), $this->getTableName()));
}
private function getTableName(): string
{
return "{$this->prefix}webhook_logs";
}
private function getIndexName(): string
{
return "{$this->prefix}webhook_id_date_added";
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20221014081742 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
$table = $schema->getTable($this->getTableName());
if ($table->hasColumn('payload_compressed')) {
throw new SkipMigration('Schema includes this migration');
}
}
public function up(Schema $schema): void
{
$this->addSql(sprintf('ALTER TABLE %s ADD payload_compressed MEDIUMBLOB DEFAULT NULL, CHANGE payload payload LONGTEXT DEFAULT NULL', $this->getTableName()));
}
private function getTableName(): string
{
return $this->prefix.'webhook_queue';
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
use Mautic\PluginBundle\Helper\IntegrationHelper;
use Mautic\SmsBundle\Form\Type\ConfigType;
final class Version20221128145933 extends AbstractMauticMigration
{
/**
* @throws SkipMigration
*/
public function preUp(Schema $schema): void
{
/** @var IntegrationHelper $integrationHelper */
$integrationHelper = $this->container->get(IntegrationHelper::class);
$integration = $integrationHelper->getIntegrationObject('Twilio');
$settings = $integration->getIntegrationSettings()->getFeatureSettings();
if (empty($settings['disable_trackable_urls'])) {
throw new SkipMigration('Schema includes this migration');
}
}
public function up(Schema $schema): void
{
$confFile = \Mautic\CoreBundle\Loader\ParameterLoader::getLocalConfigFile(__DIR__.'/../');
if (!file_exists($confFile)) {
return;
}
require $confFile;
$parameters[ConfigType::SMS_DISABLE_TRACKABLE_URLS] = 1;
// Write updated config to local.php
$result = file_put_contents($confFile, "<?php\n".'$parameters = '.var_export($parameters, true).';');
if (false === $result) {
throw new \Exception(sprintf("Couldn't update configuration file with enabled %s", ConfigType::SMS_DISABLE_TRACKABLE_URLS));
}
}
}

View File

@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20230131133732 extends AbstractMauticMigration
{
/**
* @throws SkipMigration
* @throws SchemaException
*/
public function preUp(Schema $schema): void
{
if ($schema->getTable("{$this->prefix}lead_lists")->hasColumn('last_built_time')) {
throw new SkipMigration("The last_built_time column has already been added to the {$this->prefix}lead_lists table.");
}
}
private function getTable(): string
{
return $this->prefix.'lead_lists';
}
public function up(Schema $schema): void
{
$table = $this->getTable();
$this->addSql(sprintf('ALTER TABLE `%s` ADD COLUMN `last_built_time` FLOAT NULL DEFAULT NULL AFTER `last_built_date`', $table));
}
public function down(Schema $schema): void
{
$this->addSql(sprintf('ALTER TABLE `%s` DROP COLUMN `last_built_time`', $this->getTable()));
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20230307083702 extends AbstractMauticMigration
{
public function up(Schema $schema): void
{
$old = $this->connection->quote('Swaziland');
$new = $this->connection->quote('Eswatini');
$this->addSql("UPDATE `{$this->prefix}leads` SET `country` = {$new} WHERE `country` = {$old}");
$this->addSql("UPDATE `{$this->prefix}companies` SET `companycountry` = {$new} WHERE `companycountry` = {$old}");
$old = $this->connection->quote('s:6:"filter";s:9:"Swaziland";');
$new = $this->connection->quote('s:6:"filter";s:8:"Eswatini";');
$this->addSql("UPDATE `{$this->prefix}dynamic_content` SET `filters` = REPLACE(`filters`, {$old}, {$new})");
$this->addSql("UPDATE `{$this->prefix}lead_lists` SET `filters` = REPLACE(`filters`, {$old}, {$new})");
$this->addSql("UPDATE `{$this->prefix}emails` SET `dynamic_content` = REPLACE(`dynamic_content`, {$old}, {$new})");
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20230311195347 extends AbstractMauticMigration
{
public const BATCH_SIZE = 1000;
public function up(Schema $schema): void
{
$tableName = MAUTIC_TABLE_PREFIX.'integration_entity';
$columnName = 'integration';
$value = 'Pipedrive';
$connection = $this->connection;
$rowCount = self::BATCH_SIZE;
while ($rowCount) {
$sql = "DELETE FROM $tableName WHERE $columnName = :value LIMIT ".self::BATCH_SIZE;
$rowCount = $connection->executeStatement($sql, ['value' => $value]);
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20230321133733 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
foreach (['utm_campaign', 'utm_content', 'utm_medium', 'utm_source', 'utm_term'] as $column) {
$this->skipAssertion(fn (Schema $schema) => $schema->getTable("{$this->prefix}asset_downloads")->hasColumn($column), "Column {$this->prefix}asset_downloads.{$column} already exists");
}
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}asset_downloads ADD utm_campaign VARCHAR(191) DEFAULT NULL, ADD utm_content VARCHAR(191) DEFAULT NULL, ADD utm_medium VARCHAR(191) DEFAULT NULL, ADD utm_source VARCHAR(191) DEFAULT NULL, ADD utm_term VARCHAR(191) DEFAULT
NULL;");
}
public function down(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}asset_downloads DROP COLUMN utm_campaign, utm_content, utm_medium, utm_source, utm_term");
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20230424083829 extends AbstractMauticMigration
{
/**
* @throws SkipMigration
* @throws SchemaException
*/
public function preUp(Schema $schema): void
{
$table = $schema->getTable($this->prefix.'focus');
if ($table->hasIndex($this->prefix.'focus_name')) {
throw new SkipMigration('Schema includes this migration');
}
}
public function up(Schema $schema): void
{
$this->addSql('CREATE INDEX '.$this->prefix.'focus_name ON '.$this->prefix.'focus (name)');
}
public function down(Schema $schema): void
{
$this->addSql('DROP INDEX '.$this->prefix.'focus_name ON '.$this->prefix.'focus');
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20230506112314 extends PreUpAssertionMigration
{
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())->hasIndex($this->getIndexName());
}, sprintf('The index "%s" has already been added to the table "%s".', $this->getIndexName(), $this->getTableName()));
}
public function up(Schema $schema): void
{
$this->addSql(sprintf('CREATE INDEX %s ON %s (date_added)', $this->getIndexName(), $this->getTableName()));
}
public function down(Schema $schema): void
{
$this->addSql("DROP INDEX {$this->getIndexName()} ON {$this->getTableName()}");
}
private function getTableName(): string
{
return "{$this->prefix}lead_donotcontact";
}
private function getIndexName(): string
{
return "{$this->prefix}dnc_date_added";
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20230506113422 extends PreUpAssertionMigration
{
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())->hasIndex($this->getIndexName());
}, sprintf('The index "%s" has already been added to the table "%s".', $this->getIndexName(), $this->getTableName()));
}
public function up(Schema $schema): void
{
$this->addSql(sprintf('CREATE INDEX %s ON %s (date_sent)', $this->getIndexName(), $this->getTableName()));
}
private function getTableName(): string
{
return "{$this->prefix}dynamic_content_stats";
}
private function getIndexName(): string
{
return "{$this->prefix}stat_dynamic_content_date_sent";
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20230506113627 extends PreUpAssertionMigration
{
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())->hasIndex($this->getIndexName());
}, sprintf('The index "%s" has already been added to the table "%s".', $this->getIndexName(), $this->getTableName()));
}
public function up(Schema $schema): void
{
$this->addSql(sprintf('CREATE INDEX %s ON %s (date_published)', $this->getIndexName(), $this->getTableName()));
}
private function getTableName(): string
{
return "{$this->prefix}message_queue";
}
private function getIndexName(): string
{
return "{$this->prefix}message_queue_date_published";
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20230506113731 extends PreUpAssertionMigration
{
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())->hasIndex($this->getIndexName());
}, sprintf('The index "%s" has already been added to the table "%s".', $this->getIndexName(), $this->getTableName()));
}
public function up(Schema $schema): void
{
$this->addSql(sprintf('CREATE INDEX %s ON %s (date_added)', $this->getIndexName(), $this->getTableName()));
}
private function getTableName(): string
{
return "{$this->prefix}lead_stages_change_log";
}
private function getIndexName(): string
{
return "{$this->prefix}lead_stages_change_log_date_added";
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20230506113812 extends PreUpAssertionMigration
{
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())->hasIndex($this->getIndexName());
}, sprintf('The index "%s" has already been added to the table "%s".', $this->getIndexName(), $this->getTableName()));
}
public function up(Schema $schema): void
{
$this->addSql(sprintf('CREATE INDEX %s ON %s (date_added)', $this->getIndexName(), $this->getTableName()));
}
private function getTableName(): string
{
return "{$this->prefix}lead_utmtags";
}
private function getIndexName(): string
{
return "{$this->prefix}utm_date_added";
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20230519154448 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => !$schema->hasTable("{$this->prefix}plugin_crm_pipedrive_owners"),
"Table {$this->prefix}plugin_crm_pipedrive_owners was already removed"
);
}
public function up(Schema $schema): void
{
$schema->dropTable("{$this->prefix}plugin_crm_pipedrive_owners");
}
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20230525202700 extends AbstractMauticMigration
{
public function up(Schema $schema): void
{
$oldAndNewValues = [
'Niederosterreich' => 'Niederösterreich',
'Oberosterreich' => 'Oberösterreich',
'Geneva' => 'Genève',
'Graubunden' => 'Graubünden',
'Neuchatel' => 'Neuchâtel',
'Sankt Gallen' => 'St. Gallen',
'Zurich' => 'Zürich',
'Baden-Wuerttemberg' => 'Baden-Württemberg',
'Thueringen' => 'Thüringen',
];
foreach ($oldAndNewValues as $old => $new) {
$this->addSql("UPDATE `{$this->prefix}leads` SET `state` = '{$new}' WHERE `state` = '{$old}'");
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20230606111852 extends PreUpAssertionMigration
{
public const OLD_STRING = 'Connect a &quot;Send Email&quot; action to the top of this decision.';
public const NEW_STRING = 'Connect a Send Email action to the top of this decision.';
protected const TABLE_NAME = 'campaign_events';
protected function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
$sql = sprintf("select id from %s where properties like '%s' limit 1", $this->getPrefixedTableName(), '%'.self::OLD_STRING.'%');
$recordCount = $this->connection->executeQuery($sql)->fetchAllAssociative();
return !$recordCount;
}, 'Migration is not required.');
}
public function up(Schema $schema): void
{
$sql = sprintf("select id, properties from %s where properties like '%s'", $this->getPrefixedTableName(), '%'.self::OLD_STRING.'%');
$results = $this->connection->executeQuery($sql)->fetchAllAssociative();
$updatedRecords = 0;
foreach ($results as $row) {
$propertiesArray = unserialize($row['properties']);
$propertiesArray['settings']['description'] = str_replace(self::OLD_STRING, self::NEW_STRING, $propertiesArray['settings']['description']);
$propertiesString = serialize($propertiesArray);
$sql = sprintf('UPDATE %s SET properties = :properties where id = :id', $this->getPrefixedTableName());
$stmt = $this->connection->prepare($sql);
$stmt->bindValue('properties', $propertiesString, \PDO::PARAM_STR);
$stmt->bindValue('id', $row['id'], \PDO::PARAM_INT);
$updatedRecords += $stmt->executeStatement();
}
$this->write(sprintf('<comment>%s record(s) have been updated successfully.</comment>', $updatedRecords));
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Configurator\Configurator;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
use Mautic\CoreBundle\Helper\Dsn\Dsn;
final class Version20230615101328 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$configurator = $this->getConfigurator();
$this->skipAssertion(
fn () => !$configurator->isFileWritable(),
'The local.php file is not writable. Skipping the email configuration migration.'
);
$this->skipAssertion(
fn () => array_key_exists('mailer_dsn', $configurator->getParameters()),
'The mailer_dsn parameter is already set. Skipping the email configuration migration.'
);
}
public function up(Schema $schema): void
{
$configurator = $this->getConfigurator();
$parameters = $configurator->getParameters();
$dsn = new Dsn(
$parameters['mailer_transport'] ?? 'smtp',
$parameters['mailer_host'] ?? 'localhost',
$parameters['mailer_user'] ?? null,
$parameters['mailer_password'] ?? null,
(int) ($parameters['mailer_port'] ?? 25),
);
$parameters['mailer_dsn'] = str_replace('%', '%%', (string) $dsn);
$configurator->mergeParameters($parameters);
$configurator->write();
}
private function getConfigurator(): Configurator
{
return $this->container->get(Configurator::class);
}
}

View File

@@ -0,0 +1,201 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\BigIntType;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
use Mautic\PointBundle\Entity\Group;
use Mautic\PointBundle\Entity\GroupContactScore;
final class Version20230621074925 extends PreUpAssertionMigration
{
private string $groupTableName;
private string $contactScoreTableName;
private string $contactTableName;
private string $pointsTableName;
private string $pointTriggersTableName;
private string $leadPointsChangeLogTableName;
private string $contactScoreContactFk;
private string $contactScoreGroupFk;
private string $pointsGroupFk;
private string $pointTriggersGroupFk;
private string $leadPointsChangeLogGroupFk;
private function initTableNames(): void
{
$this->groupTableName = $this->generateTableName(Group::TABLE_NAME);
$this->contactScoreTableName = $this->generateTableName(GroupContactScore::TABLE_NAME);
$this->contactTableName = $this->generateTableName('leads');
$this->pointsTableName = $this->generateTableName('points');
$this->pointTriggersTableName = $this->generateTableName('point_triggers');
$this->leadPointsChangeLogTableName = $this->generateTableName('lead_points_change_log');
$this->contactScoreContactFk = $this->generatePropertyName($this->contactScoreTableName, 'fk', ['contact_id']);
$this->contactScoreGroupFk = $this->generatePropertyName($this->contactScoreTableName, 'fk', ['group_id']);
$this->pointsGroupFk = $this->generatePropertyName($this->pointsTableName, 'fk', ['group_id']);
$this->pointTriggersGroupFk = $this->generatePropertyName($this->pointTriggersTableName, 'fk', ['group_id']);
$this->leadPointsChangeLogGroupFk = $this->generatePropertyName($this->leadPointsChangeLogTableName, 'fk', ['group_id']);
}
protected function preUpAssertions(): void
{
$this->initTableNames();
$this->assertTableDoesNotExist($this->groupTableName);
$this->assertTableDoesNotExist($this->contactScoreTableName);
$this->assertColumnDoesNotExist($this->pointsTableName, 'group_id');
$this->assertColumnDoesNotExist($this->pointTriggersTableName, 'group_id');
$this->assertColumnDoesNotExist($this->leadPointsChangeLogTableName, 'group_id');
$this->assertForeignKeyDoesNotExist($this->contactScoreTableName, $this->contactScoreContactFk);
$this->assertForeignKeyDoesNotExist($this->contactScoreTableName, $this->contactScoreGroupFk);
$this->assertForeignKeyDoesNotExist($this->pointsTableName, $this->pointsGroupFk);
$this->assertForeignKeyDoesNotExist($this->pointTriggersTableName, $this->pointTriggersGroupFk);
$this->assertForeignKeyDoesNotExist($this->leadPointsChangeLogTableName, $this->leadPointsChangeLogGroupFk);
}
public function up(Schema $schema): void
{
$this->initTableNames();
$this->addSql("CREATE TABLE `{$this->groupTableName}`
(
`id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
`is_published` TINYINT(1) NOT NULL,
`date_added` DATETIME DEFAULT NULL,
`created_by` INT DEFAULT NULL,
`created_by_user` VARCHAR(191) DEFAULT NULL,
`date_modified` DATETIME DEFAULT NULL,
`modified_by` INT DEFAULT NULL,
`modified_by_user` VARCHAR(191) DEFAULT NULL,
`checked_out` DATETIME DEFAULT NULL,
`checked_out_by` INT DEFAULT NULL,
`checked_out_by_user` VARCHAR(191) DEFAULT NULL,
`name` VARCHAR(191) NOT NULL,
`description` LONGTEXT DEFAULT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4
COLLATE `utf8mb4_unicode_ci`
ENGINE = InnoDB
ROW_FORMAT = DYNAMIC;");
$this->addSql("CREATE TABLE `{$this->contactScoreTableName}`
(
`contact_id` {$this->getContactIdColumnType($schema)} NOT NULL,
`group_id` INT UNSIGNED NOT NULL,
`score` INT NOT NULL,
PRIMARY KEY (`contact_id`, `group_id`)
) DEFAULT CHARACTER SET utf8mb4
COLLATE `utf8mb4_unicode_ci`
ENGINE = InnoDB
ROW_FORMAT = DYNAMIC;");
$this->addSql("ALTER TABLE `{$this->pointsTableName}` ADD group_id INT UNSIGNED DEFAULT NULL");
$this->addSql("ALTER TABLE `{$this->pointTriggersTableName}` ADD group_id INT UNSIGNED DEFAULT NULL");
$this->addSql("ALTER TABLE `{$this->leadPointsChangeLogTableName}` ADD group_id INT UNSIGNED DEFAULT NULL");
$this->addSql("ALTER TABLE `{$this->contactScoreTableName}` ADD CONSTRAINT `{$this->contactScoreContactFk}` FOREIGN KEY (`contact_id`) REFERENCES `{$this->contactTableName}` (`id`) ON DELETE CASCADE");
$this->addSql("ALTER TABLE `{$this->contactScoreTableName}` ADD CONSTRAINT `{$this->contactScoreGroupFk}` FOREIGN KEY (`group_id`) REFERENCES `{$this->groupTableName}` (`id`) ON DELETE CASCADE");
$this->addSql("ALTER TABLE `{$this->pointsTableName}` ADD CONSTRAINT `{$this->pointsGroupFk}` FOREIGN KEY (`group_id`) REFERENCES `{$this->groupTableName}` (`id`) ON DELETE CASCADE");
$this->addSql("ALTER TABLE `{$this->pointTriggersTableName}` ADD CONSTRAINT `{$this->pointTriggersGroupFk}` FOREIGN KEY (`group_id`) REFERENCES `{$this->groupTableName}` (`id`) ON DELETE CASCADE");
$this->addSql("ALTER TABLE `{$this->leadPointsChangeLogTableName}` ADD CONSTRAINT `{$this->leadPointsChangeLogGroupFk}` FOREIGN KEY (`group_id`) REFERENCES `{$this->groupTableName}` (`id`) ON DELETE CASCADE");
}
public function down(Schema $schema): void
{
$this->initTableNames();
if ($schema->hasTable($this->contactScoreTableName)) {
$contactScoreTable = $schema->getTable($this->contactScoreTableName);
if ($contactScoreTable->hasForeignKey($this->contactScoreContactFk)) {
$this->addSql("ALTER TABLE `{$this->contactScoreTableName}` DROP FOREIGN KEY `{$this->contactScoreContactFk}`");
}
if ($contactScoreTable->hasForeignKey($this->contactScoreGroupFk)) {
$this->addSql("ALTER TABLE `{$this->contactScoreTableName}` DROP FOREIGN KEY `{$this->contactScoreGroupFk}`");
}
}
$pointsTable = $schema->getTable($this->pointsTableName);
$pointTriggersTable = $schema->getTable($this->pointTriggersTableName);
$leadPointsChangeLogTable = $schema->getTable($this->leadPointsChangeLogTableName);
if ($pointsTable->hasForeignKey($this->pointsGroupFk)) {
$this->addSql("ALTER TABLE `{$this->pointsTableName}` DROP FOREIGN KEY `{$this->pointsGroupFk}`");
}
if ($pointTriggersTable->hasForeignKey($this->pointTriggersGroupFk)) {
$this->addSql("ALTER TABLE `{$this->pointTriggersTableName}` DROP FOREIGN KEY `{$this->pointTriggersGroupFk}`");
}
if ($leadPointsChangeLogTable->hasForeignKey($this->leadPointsChangeLogGroupFk)) {
$this->addSql("ALTER TABLE `{$this->leadPointsChangeLogTableName}` DROP FOREIGN KEY `{$this->leadPointsChangeLogGroupFk}`");
}
if ($pointsTable->hasColumn('group_id')) {
$this->addSql("ALTER TABLE `{$this->pointsTableName}` DROP group_id");
}
if ($pointTriggersTable->hasColumn('group_id')) {
$this->addSql("ALTER TABLE `{$this->pointTriggersTableName}` DROP group_id");
}
if ($leadPointsChangeLogTable->hasColumn('group_id')) {
$this->addSql("ALTER TABLE `{$this->leadPointsChangeLogTableName}` DROP group_id");
}
if ($schema->hasTable($this->contactScoreTableName)) {
$this->addSql("DROP TABLE {$this->contactScoreTableName}");
}
if ($schema->hasTable($this->groupTableName)) {
$this->addSql("DROP TABLE {$this->groupTableName}");
}
}
private function generateTableName(string $tableName): string
{
return "{$this->prefix}$tableName";
}
private function assertTableDoesNotExist(string $tableName): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable("{$tableName}"),
"Table {$tableName} already exists"
);
}
private function assertColumnDoesNotExist(string $tableName, string $columnName): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable("{$tableName}")->hasColumn($columnName),
"Column {$tableName}.{$columnName} already exists"
);
}
private function assertForeignKeyDoesNotExist(string $tableName, string $fkName): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable("{$tableName}")->hasForeignKey($fkName),
"Foreign key {$fkName} already exists in {$tableName} table"
);
}
private function getContactIdColumnType(Schema $schema): string
{
$contactTable = $schema->getTable($this->contactTableName);
$contactIdColumn = $contactTable->getColumn('id');
return $contactIdColumn->getType() instanceof BigIntType ? 'BIGINT UNSIGNED' : 'INT';
}
}

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Configurator\Configurator;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20230627140512 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
$configurator = $this->getConfigurator();
$this->skipIf(
!$configurator->isFileWritable(),
'The local.php file is not writable. Skipping the migration. Replace the usages of "%kernel.root_dir%" in your local.config file with "%kernel.project_dir%/app".'
);
$this->skipIf(
!str_contains($configurator->render(), '%kernel.root_dir%'),
'The deprecated %kernel.root_dir% is unused. Your local.php file is just fine. Skipping the migration.'
);
}
public function up(Schema $schema): void
{
$configurator = $this->getConfigurator();
$configurator->mergeParameters(
array_map(
function ($value) {
if (is_string($value) && str_contains($value, '%kernel.root_dir%/..')) {
return str_replace('%kernel.root_dir%/..', '%kernel.project_dir%', $value);
}
if (is_string($value) && str_contains($value, '%kernel.root_dir%')) {
return str_replace('%kernel.root_dir%', '%kernel.project_dir%/app', $value);
}
return $value;
},
$configurator->getParameters()
)
);
$configurator->write();
}
private function getConfigurator(): Configurator
{
return $this->container->get(Configurator::class);
}
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20230927055621 extends AbstractMauticMigration
{
public function up(Schema $schema): void
{
$table = $schema->getTable($this->prefix.'emails');
if (!$table->hasColumn('continue_sending')) {
$this->addSql("ALTER TABLE {$this->prefix}emails ADD `continue_sending` TINYINT(1) DEFAULT 0 NOT NULL");
$this->addSql("UPDATE {$this->prefix}emails SET `continue_sending`= 1 WHERE publish_up IS NOT NULL");
}
}
public function down(Schema $schema): void
{
$table = $schema->getTable($this->prefix.'emails');
if ($table->hasColumn('continue_sending')) {
$this->addSql("ALTER TABLE {$this->prefix}emails DROP continue_sending");
}
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20231110103625 extends AbstractMauticMigration
{
protected const TABLE_NAME = 'roles';
public function up(Schema $schema): void
{
$sql = sprintf('SELECT id, readable_permissions FROM %s WHERE is_admin != 1', $this->getPrefixedTableName());
$results = $this->connection->executeQuery($sql)->fetchAllAssociative();
$updatedRecords = 0;
$addPermissions = [];
foreach ($results as $row) {
$permissionsArray = unserialize($row['readable_permissions']);
// Add permissions if not exists
$permissionsToAdd = ['lead:export', 'form:export', 'report:export'];
foreach ($permissionsToAdd as $permission) {
if (!isset($permissionsArray[$permission])) {
$permissionsArray[$permission] = ['enable'];
$addPermissions[$row['id']][$permission] = 1024;
}
}
$permissionsString = serialize($permissionsArray);
$updateSql = sprintf('UPDATE %s SET readable_permissions = :permissions WHERE id = :id', $this->getPrefixedTableName());
$stmt = $this->connection->prepare($updateSql);
$stmt->bindValue('permissions', $permissionsString, \PDO::PARAM_STR);
$stmt->bindValue('id', $row['id'], \PDO::PARAM_INT);
$updatedRecords += $stmt->executeStatement();
foreach ($addPermissions as $roleId => $permissionsToAdd) {
foreach ($permissionsToAdd as $permissionToAdd => $bitwise) {
$sql = sprintf('INSERT IGNORE INTO %s (role_id, bundle, name, bitwise) VALUES (:role_id, :bundle, :name, :bitwise)', $this->prefix.'permissions');
$stmt = $this->connection->prepare($sql);
$permissionArray = explode(':', $permissionToAdd);
$stmt->bindValue('role_id', $row['id'], \PDO::PARAM_INT);
$stmt->bindValue('bundle', $permissionArray[0], \PDO::PARAM_STR);
$stmt->bindValue('name', $permissionArray[1], \PDO::PARAM_STR);
$stmt->bindValue('bitwise', $bitwise, \PDO::PARAM_INT);
$stmt->executeStatement();
}
}
}
$this->write(sprintf('<comment>%s record(s) have been updated successfully.</comment>', $updatedRecords));
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
use Mautic\LeadBundle\Entity\ListLead;
final class Version20231205094436 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())->hasIndex($this->getIndexName());
}, sprintf('Index %s already exists', $this->getIndexName()));
}
public function up(Schema $schema): void
{
$this->addSql(sprintf(
'ALTER TABLE %s ADD INDEX %s (lead_id, leadlist_id, manually_removed)',
$this->getTableName(),
$this->getIndexName()
));
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE '.$this->getTableName().' DROP INDEX '.$this->getIndexName());
}
private function getTableName(): string
{
return $this->prefix.ListLead::TABLE_NAME;
}
private function getIndexName(): string
{
return "{$this->prefix}lead_id_lists_id_removed";
}
}

View File

@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20231206152313 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())->hasIndex($this->getSentIndexName());
}, sprintf('Index %s already exists', $this->getSentIndexName()));
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())->hasIndex($this->getIsReadIndexName());
}, sprintf('Index %s already exists', $this->getIsReadIndexName()));
}
public function up(Schema $schema): void
{
$this->addSql(sprintf('ALTER TABLE %s ADD INDEX %s (lead_id, date_sent), ADD INDEX %s (email_id, is_read)', $this->getTableName(), $this->getSentIndexName(), $this->getIsReadIndexName()));
}
public function postUp(Schema $schema): void
{
$this->dropIndex(['lead_id']);
$this->dropIndex(['email_id']);
}
private function getTableName(): string
{
return "{$this->prefix}email_stats";
}
private function getSentIndexName(): string
{
return "{$this->prefix}stat_email_lead_id_date_sent";
}
private function getIsReadIndexName(): string
{
return "{$this->prefix}stat_email_email_id_is_read";
}
/**
* @param string[] $columnNames
*/
private function dropIndex(array $columnNames): void
{
try {
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $this->generatePropertyName('email_stats', 'idx', $columnNames), $this->getTableName()));
} catch (\Throwable) {
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\Exception\SkipMigration;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
class Version20231207235400 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
$table = $schema->getTable($this->prefix.'forms');
if ($table->hasColumn('lang')) {
throw new SkipMigration('Schema includes this migration');
}
}
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE '.$this->prefix.'forms ADD lang VARCHAR(191) DEFAULT NULL');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE '.$this->prefix.'forms DROP lang');
}
}

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20240226114528 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->hasTable($this->getTableName());
}, sprintf('Table %s already exists', $this->getTableName()));
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())
->hasForeignKey($this->getForeignKeyName('email_id'));
}, sprintf('Foreign key %s already exists', $this->getForeignKeyName('email_id')));
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable($this->getTableName())
->hasForeignKey($this->getForeignKeyName('leadlist_id'));
}, sprintf('Foreign key %s already exists', $this->getForeignKeyName('leadlist_id')));
}
public function up(Schema $schema): void
{
$tableName = $this->getTableName();
$this->addSql(sprintf('
CREATE TABLE %s (
email_id INT %s NOT NULL,
leadlist_id INT %s NOT NULL,
INDEX %s (email_id),
INDEX %s (leadlist_id),
PRIMARY KEY(email_id, leadlist_id)
) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB ROW_FORMAT = DYNAMIC',
$tableName,
$this->getIntegerRange($schema, 'emails'),
$this->getIntegerRange($schema, 'lead_lists'),
$this->getIndexName('email_id'),
$this->getIndexName('leadlist_id')
));
$this->addSql(sprintf('ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (email_id) REFERENCES %semails (id) ON DELETE CASCADE',
$tableName,
$this->getForeignKeyName('email_id'),
$this->prefix
));
$this->addSql(sprintf('ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (leadlist_id) REFERENCES %slead_lists (id) ON DELETE CASCADE',
$tableName,
$this->getForeignKeyName('leadlist_id'),
$this->prefix
));
}
private function getTableName(): string
{
return "{$this->prefix}{$this->getBareTableName()}";
}
private function getBareTableName(): string
{
return 'email_list_excluded';
}
private function getIndexName(string $column): string
{
return $this->generatePropertyName($this->getBareTableName(), 'idx', [$column]);
}
private function getForeignKeyName(string $column): string
{
return $this->generatePropertyName($this->getBareTableName(), 'fk', [$column]);
}
private function getIntegerRange(Schema $schema, string $bareTableName): string
{
$unsigned = $schema->getTable("{$this->prefix}{$bareTableName}")
->getColumn('id')
->getUnsigned();
return $unsigned ? 'UNSIGNED' : '';
}
}

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20240228132306 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'webhook_queue';
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
$table = $schema->getTable($this->getPrefixedTableName());
return !$table->hasColumn('payload') && $table->hasColumn('retries') && $table->hasColumn('date_modified');
}, sprintf('No need to run migration on %s columns already exist.', $this->getPrefixedTableName()));
}
public function up(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName());
if ($table->hasColumn('payload')) {
$table->dropColumn('payload');
}
if (!$table->hasColumn('retries')) {
$table->addColumn('retries', Types::SMALLINT)
->setUnsigned(true)
->setDefault(0);
}
if (!$table->hasColumn('date_modified')) {
$table->addColumn('date_modified', Types::DATETIME_IMMUTABLE)
->setNotnull(false);
}
}
public function down(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName());
if (!$table->hasColumn('payload')) {
$table->addColumn('payload', Types::TEXT)
->setNotnull(false);
}
if ($table->hasColumn('retries')) {
$table->dropColumn('retries');
}
if ($table->hasColumn('date_modified')) {
$table->dropColumn('date_modified');
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20240320081612 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable("{$this->prefix}campaign_events")->hasColumn('trigger_window'),
'Column trigger_window already exists'
);
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}campaign_events ADD COLUMN `trigger_window` INT NULL DEFAULT NULL AFTER `trigger_restricted_dow`");
}
public function down(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}campaign_events DROP COLUMN `trigger_window`");
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20240416112112 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
return $schema->getTable("{$this->prefix}campaigns")->hasColumn('version');
}, sprintf('Column %s already exists', 'version'));
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}campaigns ADD version INT UNSIGNED DEFAULT 1 NOT NULL");
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\BigIntType;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20240607092418 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'webhook_queue';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName(self::TABLE_NAME))->getColumn('id')->getType() instanceof BigIntType,
sprintf('Index %s already exists', 'index_name')
);
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->getPrefixedTableName(self::TABLE_NAME)} CHANGE id id BIGINT UNSIGNED AUTO_INCREMENT NOT NULL");
}
public function down(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->getPrefixedTableName(self::TABLE_NAME)} CHANGE id id INT UNSIGNED AUTO_INCREMENT NOT NULL");
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20240611103824 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable("{$this->prefix}bundle_grapesjsbuilder")->hasColumn('draft_custom_mjml'),
'Column draft_custom_mjml already exists'
);
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->prefix}bundle_grapesjsbuilder ADD draft_custom_mjml ".Types::TEXT);
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20240704164714 extends AbstractMauticMigration
{
protected const TABLE_NAME = 'lead_fields';
public function up(Schema $schema): void
{
$this->addSql(
"UPDATE {$this->getPrefixedTableName()}
SET type = 'html',
properties = 'a:0:{}'
WHERE type = 'textarea' AND properties LIKE '%\"allowHtml\";s:1:\"1\"%'"
);
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
final class Version20240708153845 extends AbstractMauticMigration
{
private string $emailStatsTableName;
protected string $emailsTableName;
public function preUp(Schema $schema): void
{
$this->emailStatsTableName = $this->prefix.'email_stats';
$this->emailsTableName = $this->prefix.'emails';
}
public function up(Schema $schema): void
{
$sql = sprintf('SELECT id, read_count FROM %s', $this->emailsTableName);
$emailsResult = $this->connection->executeQuery($sql)->fetchAllAssociative();
foreach ($emailsResult as $email) {
$totalCountResult = $this->connection
->executeQuery(
"SELECT email_id, SUM(is_read) as total_read_count
FROM {$this->emailStatsTableName}
WHERE email_id = :email_id
GROUP BY email_id",
['email_id' => $email['id']]
)
->fetchAssociative();
if (is_array($totalCountResult) && $email['id'] === $totalCountResult['email_id'] && (int) $email['read_count'] < $totalCountResult['total_read_count']) {
$this
->addSql(
"UPDATE {$this->emailsTableName}
SET read_count = '{$totalCountResult['total_read_count']}'
WHERE id = '{$email['id']}'"
);
}
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20240725105507 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
}
public function up(Schema $schema): void
{
$this->addSql("UPDATE {$this->prefix}leads SET `country` = 'Türkiye' WHERE `country` = 'Turkey'");
$this->addSql("UPDATE {$this->prefix}companies SET `companycountry` = 'Türkiye' WHERE `companycountry` = 'Turkey'");
}
public function down(Schema $schema): void
{
$this->addSql("UPDATE {$this->prefix}leads SET `country` = 'Turkey' WHERE `country` = 'Türkiye'");
$this->addSql("UPDATE {$this->prefix}companies SET `companycountry` = 'Turkey' WHERE `companycountry` = 'Türkiye'");
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20241004132307 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'webhooks';
public function preUpAssertions(): void
{
$this->skipAssertion(function (Schema $schema) {
$table = $schema->getTable($this->getPrefixedTableName());
return $table->hasColumn('marked_unhealthy_at') && $table->hasColumn('unhealthy_since') && $table->hasColumn('last_notification_sent_at');
}, sprintf('Columns already exists in %s', $this->getPrefixedTableName()));
}
public function up(Schema $schema): void
{
$webhook = $schema->getTable($this->getPrefixedTableName());
if (!$webhook->hasColumn('marked_unhealthy_at')) {
$webhook->addColumn('marked_unhealthy_at', Types::DATETIME_IMMUTABLE)
->setNotnull(false);
}
if (!$webhook->hasColumn('unhealthy_since')) {
$webhook->addColumn('unhealthy_since', Types::DATETIME_IMMUTABLE)
->setNotnull(false);
}
if (!$webhook->hasColumn('last_notification_sent_at')) {
$webhook->addColumn('last_notification_sent_at', Types::DATETIME_IMMUTABLE)
->setNotnull(false);
}
}
public function down(Schema $schema): void
{
$webhook = $schema->getTable($this->getPrefixedTableName());
if ($webhook->hasColumn('marked_unhealthy_at')) {
$webhook->dropColumn('marked_unhealthy_at');
}
if ($webhook->hasColumn('unhealthy_since')) {
$webhook->dropColumn('unhealthy_since');
}
if ($webhook->hasColumn('last_notification_sent_at')) {
$webhook->dropColumn('last_notification_sent_at');
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20241212090146 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'sync_object_mapping';
private string $indexName = MAUTIC_TABLE_PREFIX.'internal_object_id_idx';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => !$schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME))
|| $schema->getTable($this->getPrefixedTableName(self::TABLE_NAME))->hasIndex($this->indexName),
"Table {$this->getPrefixedTableName(self::TABLE_NAME)} does not exist or the index {$this->indexName} already exists."
);
}
public function up(Schema $schema): void
{
$this->addSql("CREATE INDEX {$this->indexName} ON {$this->getPrefixedTableName(self::TABLE_NAME)} (internal_object_id);");
}
public function down(Schema $schema): void
{
$this->addSql("DROP INDEX {$this->indexName} ON {$this->getPrefixedTableName(self::TABLE_NAME)};");
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20241227065658 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'form_fields';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName())->hasColumn('is_read_only'),
sprintf('Column %s already exists', 'is_read_only')
);
}
public function up(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName());
$table->addColumn('is_read_only', Types::BOOLEAN)->setDefault(false);
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250127092202 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'dynamic_content';
private const COLUMN_NAME = 'type';
protected function preUpAssertions(): void
{
$this->skipAssertion(fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName(self::TABLE_NAME))->hasColumn(self::COLUMN_NAME),
sprintf('Column %s already exists in table %s',
self::COLUMN_NAME, $this->getPrefixedTableName(self::TABLE_NAME))
);
}
public function up(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName(self::TABLE_NAME));
$table->addColumn(self::COLUMN_NAME, Types::STRING, ['default' => 'html', 'length' => 10]);
}
public function down(Schema $schema): void
{
$schema->getTable($this->getPrefixedTableName(self::TABLE_NAME))->dropColumn(self::COLUMN_NAME);
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250207035735 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'lead_fields';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => ($column = $schema->getTable($this->getPrefixedTableName())->getColumn('is_short_visible'))
&& false === $column->getDefault(),
sprintf('Column %s already has a default set', 'is_short_visible')
);
}
public function up(Schema $schema): void
{
// Update the table schema
$table = $schema->getTable($this->getPrefixedTableName());
$table->getColumn('is_short_visible')->setDefault(false)->setNotnull(true);
// Update the existing records.
$this->connection->executeStatement(sprintf('UPDATE %s SET is_short_visible = FALSE WHERE is_short_visible IS NULL', $table->getName()));
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250402103942 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'form_fields';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName(self::TABLE_NAME))->hasColumn('field_width'),
'The field field_width already exists'
);
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE {$this->getPrefixedTableName(self::TABLE_NAME)} ADD field_width VARCHAR(50) DEFAULT '100%' NOT NULL");
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE {$this->getPrefixedTableName(self::TABLE_NAME)} DROP field_width');
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
use Mautic\ProjectBundle\Entity\Project;
final class Version20250409150440 extends PreUpAssertionMigration
{
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(Project::TABLE_NAME)),
'Table '.Project::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$projectsTable = $schema->createTable($this->getPrefixedTableName(Project::TABLE_NAME));
$projectsTable->addColumn('id', Types::INTEGER, ['autoincrement' => true, 'unsigned' => true]);
$projectsTable->addColumn('name', Types::STRING, ['length' => 191]);
$projectsTable->addColumn('description', Types::TEXT, ['notnull' => false]);
$projectsTable->addColumn('properties', Types::JSON, ['notnull' => true]);
$projectsTable->addColumn('uuid', Types::GUID, ['notnull' => false]);
$projectsTable->addColumn('is_published', Types::BOOLEAN, ['notnull' => true]);
$projectsTable->addColumn('checked_out', Types::DATETIME_MUTABLE, ['notnull' => false]);
$projectsTable->addColumn('checked_out_by', Types::INTEGER, ['notnull' => false]);
$projectsTable->addColumn('checked_out_by_user', Types::STRING, ['length' => 191, 'notnull' => false]);
$projectsTable->addColumn('date_added', Types::DATETIME_MUTABLE, ['notnull' => false]);
$projectsTable->addColumn('created_by', Types::INTEGER, ['notnull' => false]);
$projectsTable->addColumn('created_by_user', Types::STRING, ['length' => 191, 'notnull' => false]);
$projectsTable->addColumn('date_modified', Types::DATETIME_MUTABLE, ['notnull' => false]);
$projectsTable->addColumn('modified_by', Types::INTEGER, ['notnull' => false]);
$projectsTable->addColumn('modified_by_user', Types::STRING, ['length' => 191, 'notnull' => false]);
$projectsTable->setPrimaryKey(['id']);
$projectsTable->addIndex(['name'], $this->prefix.'project_name');
}
public function down(Schema $schema): void
{
$schema->dropTable($this->getPrefixedTableName(Project::TABLE_NAME));
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250409150450 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'lead_list_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$leadListIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'lead_lists', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'lead_list_projects_xref');
$table->addColumn('leadlist_id', 'integer', ['unsigned' => 'UNSIGNED' === $leadListIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['leadlist_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'lead_lists', ['leadlist_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('lead_list_projects_xref', 'idx', ['leadlist_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'lead_list_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'lead_list_projects_xref');
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250415142826 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'campaign_lead_event_log';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName(self::TABLE_NAME))->hasIndex("{$this->prefix}idx_scheduled_events"),
'Index idx_scheduled_events already exists'
);
}
public function up(Schema $schema): void
{
$this->addSql("CREATE INDEX {$this->prefix}idx_scheduled_events ON {$this->getPrefixedTableName(self::TABLE_NAME)} (is_scheduled, event_id, trigger_date);");
}
public function down(Schema $schema): void
{
$this->addSql("DROP INDEX {$this->prefix}idx_scheduled_events ON {$this->getPrefixedTableName(self::TABLE_NAME)};");
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250424092044 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'campaign_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'campaigns', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'campaign_projects_xref');
$table->addColumn('campaign_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['campaign_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'campaigns', ['campaign_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('campaign_projects_xref', 'idx', ['campaign_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'campaign_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'campaign_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250425125026 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'email_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'emails', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'email_projects_xref');
$table->addColumn('email_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['email_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'emails', ['email_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('email_projects_xref', 'idx', ['email_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'email_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'email_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250425134835 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'sms_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'sms_messages', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'sms_projects_xref');
$table->addColumn('sms_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['sms_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'sms_messages', ['sms_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('sms_projects_xref', 'idx', ['sms_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'sms_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'sms_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250428071743 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'form_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'forms', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'form_projects_xref');
$table->addColumn('form_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['form_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'forms', ['form_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('form_projects_xref', 'idx', ['form_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'form_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'form_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250429133744 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'message_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'messages', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'message_projects_xref');
$table->addColumn('message_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['message_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'messages', ['message_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('message_projects_xref', 'idx', ['message_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'message_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'message_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250430104345 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'focus_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'focus', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'focus_projects_xref');
$table->addColumn('focus_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['focus_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'focus', ['focus_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('focus_projects_xref', 'idx', ['focus_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'focus_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'focus_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250430113559 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'asset_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'assets', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'asset_projects_xref');
$table->addColumn('asset_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['asset_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'assets', ['asset_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('asset_projects_xref', 'idx', ['asset_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'asset_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'asset_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250430135003 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'page_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'pages', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'page_projects_xref');
$table->addColumn('page_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['page_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'pages', ['page_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('page_projects_xref', 'idx', ['page_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'page_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'page_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250502071717 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'company_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'companies', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'company_projects_xref');
$table->addColumn('company_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['company_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'companies', ['company_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('company_projects_xref', 'idx', ['company_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'company_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'company_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250618122722 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'dynamic_content_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'dynamic_content', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'dynamic_content_projects_xref');
$table->addColumn('dynamic_content_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['dynamic_content_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'dynamic_content', ['dynamic_content_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('dynamic_content_projects_xref', 'idx', ['dynamic_content_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'dynamic_content_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'dynamic_content_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250618122908 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'stage_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'stages', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'stage_projects_xref');
$table->addColumn('stage_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['stage_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'stages', ['stage_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('stage_projects_xref', 'idx', ['stage_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'stage_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'stage_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250618134002 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'point_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'points', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'point_projects_xref');
$table->addColumn('point_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['point_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'points', ['point_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('point_projects_xref', 'idx', ['point_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'point_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'point_projects_xref');
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250618141525 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'point_trigger_projects_xref';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->hasTable($this->getPrefixedTableName(self::TABLE_NAME)),
'Table '.self::TABLE_NAME.' already exists'
);
}
public function up(Schema $schema): void
{
$targetIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'point_triggers', 'id');
$projectIdDataType = $this->getColumnTypeSignedOrUnsigned($schema, 'projects', 'id');
$table = $schema->createTable($this->prefix.'point_trigger_projects_xref');
$table->addColumn('point_trigger_id', 'integer', ['unsigned' => 'UNSIGNED' === $targetIdDataType, 'notnull' => true]);
$table->addColumn('project_id', 'integer', ['unsigned' => 'UNSIGNED' === $projectIdDataType, 'notnull' => true]);
$table->setPrimaryKey(['point_trigger_id', 'project_id']);
$table->addForeignKeyConstraint($this->prefix.'point_triggers', ['point_trigger_id'], ['id'], ['onDelete' => 'CASCADE']);
$table->addForeignKeyConstraint($this->prefix.'projects', ['project_id'], ['id'], ['onDelete' => 'CASCADE']);
}
public function postUp(Schema $schema): void
{
$index = $this->generatePropertyName('point_trigger_projects_xref', 'idx', ['point_trigger_id']);
$this->connection->executeStatement(sprintf('DROP INDEX %s ON %s', $index, $this->prefix.'point_trigger_projects_xref'));
}
public function down(Schema $schema): void
{
$schema->dropTable($this->prefix.'point_trigger_projects_xref');
}
}

View File

@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
use Mautic\LeadBundle\Segment\OperatorOptions;
class Version20250804003400 extends AbstractMauticMigration
{
private string $leadListsTable;
public function preUp(Schema $schema): void
{
$this->leadListsTable = $this->prefix.'lead_lists';
}
public function up(Schema $schema): void
{
$sql = 'SELECT id, filters FROM '.$this->leadListsTable.' WHERE filters LIKE "%multiselect%"';
$listsWithMultiselect = $this->connection->executeQuery($sql)->fetchAllAssociative();
foreach ($listsWithMultiselect as $listData) {
$filters = unserialize($listData['filters'], ['allowed_classes' => false]);
$changed = false;
foreach ($filters as $index => $filter) {
if ('multiselect' !== $filter['type']) {
continue;
}
if (OperatorOptions::INCLUDING_ANY === $filter['operator']) {
$filters[$index]['operator'] = OperatorOptions::INCLUDING_ALL;
$changed = true;
} elseif (OperatorOptions::EXCLUDING_ANY === $filter['operator']) {
$filters[$index]['operator'] = OperatorOptions::EXCLUDING_ALL;
$changed = true;
}
}
if ($changed) {
$this->addSql(
'UPDATE '.$this->leadListsTable.' SET filters = :filters WHERE id = :id',
[
'filters' => serialize($filters),
'id' => $listData['id'],
]
);
}
}
}
public function preDown(Schema $schema): void
{
$this->leadListsTable = $this->prefix.'lead_lists';
}
public function down(Schema $schema): void
{
$sql = 'SELECT id, filters FROM '.$this->leadListsTable.' WHERE filters LIKE "%multiselect%"';
$listsWithMultiselect = $this->connection->executeQuery($sql)->fetchAllAssociative();
foreach ($listsWithMultiselect as $listData) {
$filters = unserialize($listData['filters'], ['allowed_classes' => false]);
$changed = false;
foreach ($filters as $index => $filter) {
if ('multiselect' !== $filter['type']) {
continue;
}
if (OperatorOptions::INCLUDING_ALL === $filter['operator']) {
$filters[$index]['operator'] = OperatorOptions::INCLUDING_ANY;
$changed = true;
} elseif (OperatorOptions::EXCLUDING_ALL === $filter['operator']) {
$filters[$index]['operator'] = OperatorOptions::EXCLUDING_ANY;
$changed = true;
}
}
if ($changed) {
$this->addSql(
'UPDATE '.$this->leadListsTable.' SET filters = :filters WHERE id = :id',
[
'filters' => serialize($filters),
'id' => $listData['id'],
]
);
}
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
use Mautic\PointBundle\Entity\Group;
final class Version20250805095503 extends PreUpAssertionMigration
{
protected const TABLE_NAME = Group::TABLE_NAME;
public function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName())->hasColumn('uuid'),
'Column uuid already exists'
);
}
public function up(Schema $schema): void
{
$this->addSql("ALTER TABLE `{$this->getPrefixedTableName()}` ADD `uuid` CHAR(36) NULL DEFAULT NULL");
$this->addSql("UPDATE `{$this->getPrefixedTableName()}` SET `uuid` = UUID() WHERE `uuid` IS NULL");
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Mautic\CampaignBundle\Entity\LeadEventLog;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
use Mautic\CoreBundle\Entity\OptimisticLockInterface;
final class Version20250806085552 extends PreUpAssertionMigration
{
protected const TABLE_NAME = LeadEventLog::TABLE_NAME;
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName())->hasColumn('version'),
"Table {$this->getPrefixedTableName()} already has 'version' column"
);
}
public function up(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName());
$table->addColumn('version', Types::INTEGER)
->setUnsigned(true)
->setDefault(OptimisticLockInterface::INITIAL_VERSION);
}
public function down(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName());
$table->dropColumn('version');
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250828070131 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'sms_messages';
protected function preUpAssertions(): void
{
$column = 'translation_parent_id';
$this->skipAssertion(fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName())->hasColumn($column), "Column {$this->prefix}sms_messages.{$column} already exists");
}
public function up(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName());
// translation_parent_id
if (!$table->hasColumn('translation_parent_id')) {
$table->addColumn('translation_parent_id', 'integer', [
'unsigned' => true,
'notnull' => false,
]);
$table->addIndex(['translation_parent_id'], 'IDX_SMS_TRANSLATION_PARENT');
$table->addForeignKeyConstraint(
$table,
['translation_parent_id'],
['id'],
['onDelete' => 'CASCADE'],
'FK_SMS_TRANSLATION_PARENT'
);
}
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE '.$this->getPrefixedTableName().' DROP FOREIGN KEY FK_SMS_TRANSLATION_PARENT');
$this->addSql('DROP INDEX IDX_SMS_TRANSLATION_PARENT ON '.$this->getPrefixedTableName());
$this->addSql('ALTER TABLE '.$this->getPrefixedTableName().' DROP COLUMN translation_parent_id');
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250923114835 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'campaign_lead_event_log';
protected function preUpAssertions(): void
{
$this->skipAssertion(
fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName())->hasColumn('date_queued'),
"Table {$this->getPrefixedTableName()} already has 'date_queued' column"
);
}
public function up(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName());
$table->addColumn('date_queued', Types::DATETIME_MUTABLE)->setNotnull(false);
}
public function down(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName());
$table->dropColumn('date_queued');
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\PreUpAssertionMigration;
final class Version20250923135527 extends PreUpAssertionMigration
{
protected const TABLE_NAME = 'push_notifications';
private const COLUMN_NAME = 'translation_parent_id';
protected function preUpAssertions(): void
{
$this->skipAssertion(fn (Schema $schema) => $schema->getTable($this->getPrefixedTableName())->hasColumn(self::COLUMN_NAME), sprintf('Column %s.%s already exists.', $this->getPrefixedTableName(), self::COLUMN_NAME));
}
public function up(Schema $schema): void
{
$table = $schema->getTable($this->getPrefixedTableName());
if (!$table->hasColumn(self::COLUMN_NAME)) {
$table->addColumn(self::COLUMN_NAME, 'integer', [
'unsigned' => true,
'notnull' => false,
]);
$table->addIndex([self::COLUMN_NAME], 'IDX_PUSH_NOTIFICATIONS_TRANSLATION_PARENT');
$table->addForeignKeyConstraint(
$table,
[self::COLUMN_NAME],
['id'],
['onDelete' => 'CASCADE'],
'FK_PUSH_NOTIFICATIONS_TRANSLATION_PARENT'
);
}
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE '.$this->getPrefixedTableName().' DROP FOREIGN KEY FK_PUSH_NOTIFICATIONS_TRANSLATION_PARENT');
$this->addSql('DROP INDEX IDX_PUSH_NOTIFICATIONS_TRANSLATION_PARENT ON '.$this->getPrefixedTableName());
$this->addSql('ALTER TABLE '.$this->getPrefixedTableName().' DROP COLUMN translation_parent_id');
}
}

View File

@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Mautic\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Mautic\CoreBundle\Doctrine\AbstractMauticMigration;
use Mautic\CoreBundle\Helper\PathsHelper;
/**
* Move config files that contain local config to a folder outside the application data.
*/
final class Versionzz20230929183000 extends AbstractMauticMigration
{
public function preUp(Schema $schema): void
{
[$appConfigDir] = $this->getConfigDirs();
$matches = glob($appConfigDir.'/*local.php');
$this->skipIf(
0 == count($matches),
'There are no local config files to migrate. Skipping the migration.'
);
}
public function up(Schema $schema): void
{
$pathsHelper = $this->container->get(PathsHelper::class);
$appConfigDir = $pathsHelper->getRootPath().'/app/config';
$localConfigDir = $pathsHelper->getVendorRootPath().'/config';
$matches = glob($appConfigDir.'/*local.php');
foreach ($matches as $file) {
rename($file, $localConfigDir.'/'.pathinfo($file, PATHINFO_BASENAME));
}
}
/**
* @return string[]
*/
public function getConfigDirs(): array
{
$pathsHelper = $this->container->get(PathsHelper::class);
$appConfigDir = $pathsHelper->getRootPath().'/app/config';
$localConfigDir = $pathsHelper->getVendorRootPath().'/config';
return [$appConfigDir, $localConfigDir];
}
}