first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-06-08 17:09:23 -04:00
commit df3a033196
17887 changed files with 8637778 additions and 0 deletions
@@ -0,0 +1,142 @@
<?php
/**
* @file classes/migration/upgrade/OJSv3_3_0UpgradeMigration.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class OJSv3_3_0UpgradeMigration
*
* @brief Describe database table structures.
*/
namespace APP\migration\upgrade;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class OJSv3_3_0UpgradeMigration extends \PKP\migration\upgrade\PKPv3_3_0UpgradeMigration
{
protected function getSubmissionPath(): string
{
return 'articles';
}
protected function getContextPath(): string
{
return 'journals';
}
protected function getContextTable(): string
{
return 'journals';
}
protected function getContextKeyField(): string
{
return 'journal_id';
}
protected function getContextSettingsTable(): string
{
return 'journal_settings';
}
protected function getSectionTable(): string
{
return 'sections';
}
protected function getSerializedSettings(): array
{
return [
'site_settings' => [
'enableBulkEmails',
'installedLocales',
'pageHeaderTitleImage',
'sidebar',
'styleSheet',
'supportedLocales',
],
'journal_settings' => [
'disableBulkEmailUserGroups',
'favicon',
'homepageImage',
'pageHeaderLogoImage',
'sidebar',
'styleSheet',
'submissionChecklist',
'supportedFormLocales',
'supportedLocales',
'supportedSubmissionLocales',
'enablePublisherId',
'journalThumbnail',
],
'publication_settings' => [
'categoryIds',
'coverImage',
'disciplines',
'keywords',
'languages',
'subjects',
'supportingAgencies',
]
];
}
/**
* Run the migrations.
*/
public function up(): void
{
parent::up();
// pkp/pkp-lib#6807 Make sure all submission/issue last modification dates are set
DB::statement('UPDATE issues SET last_modified = date_published WHERE last_modified IS NULL');
// Delete the old MODS34 filters
DB::statement("DELETE FROM filters WHERE class_name='plugins.metadata.mods34.filter.Mods34SchemaArticleAdapter'");
DB::statement("DELETE FROM filter_groups WHERE symbolic IN ('article=>mods34', 'mods34=>article')");
// Delete mEDRA dependencies
DB::statement("DELETE FROM filters WHERE class_name IN ('plugins.importexport.medra.filter.IssueMedraXmlFilter', 'plugins.importexport.medra.filter.ArticleMedraXmlFilter', 'plugins.importexport.medra.filter.GalleyMedraXmlFilter')");
DB::statement("DELETE FROM filter_groups WHERE symbolic IN ('issue=>medra-xml', 'article=>medra-xml', 'galley=>medra-xml')");
DB::statement("DELETE FROM scheduled_tasks WHERE class_name='plugins.importexport.medra.MedraInfoSender'");
DB::statement("DELETE FROM versions WHERE product_type='plugins.importexport' AND product='medra'");
}
/**
* Complete specific submission file migrations
*
* The main submission file migration is done in
* PKPv3_3_0UpgradeMigration and that migration must
* be run before this one.
*/
protected function migrateSubmissionFiles()
{
parent::migrateSubmissionFiles();
Schema::table('publication_galleys', function (Blueprint $table) {
$table->renameColumn('file_id', 'submission_file_id');
});
DB::statement('UPDATE publication_galleys SET submission_file_id = NULL WHERE submission_file_id = 0');
// pkp/pkp-lib#6616 Delete publication_galleys entries that correspond to nonexistent submission_files
$orphanedIds = DB::table('publication_galleys AS pg')
->leftJoin('submission_files AS sf', 'pg.submission_file_id', '=', 'sf.submission_file_id')
->whereNull('sf.submission_file_id')
->whereNotNull('pg.submission_file_id')
->pluck('pg.submission_file_id', 'pg.galley_id');
foreach ($orphanedIds as $galleyId => $submissionFileId) {
error_log("Removing orphaned publication_galleys entry ID {$galleyId} with submission_file_id {$submissionFileId}");
DB::table('publication_galleys')->where('galley_id', '=', $galleyId)->delete();
}
Schema::table('publication_galleys', function (Blueprint $table) {
$table->bigInteger('submission_file_id')->nullable()->unsigned()->change();
$table->foreign('submission_file_id')->references('submission_file_id')->on('submission_files');
});
}
}
@@ -0,0 +1,62 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I3573_AddPrimaryKeys.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I3573_AddPrimaryKeys.php
*
* @brief Add primary keys to tables that do not have them, to better support database replication.
*
*/
namespace APP\migration\upgrade\v3_4_0;
class I3573_AddPrimaryKeys extends \PKP\migration\upgrade\v3_4_0\I3573_AddPrimaryKeys
{
public static function getKeyNames(): array
{
return array_merge(parent::getKeyNames(), [
'issue_galley_settings' => 'issue_galley_setting_id',
'issue_settings' => 'issue_setting_id',
'journal_settings' => 'journal_setting_id',
'publication_galley_settings' => 'publication_galley_setting_id',
'section_settings' => 'section_setting_id',
'subscription_type_settings' => 'subscription_type_setting_id',
'usage_stats_unique_item_requests_temporary_records' => 'usage_stats_temp_item_id',
'metrics_context' => 'metrics_context_id',
'metrics_counter_submission_institution_daily' => 'metrics_counter_submission_institution_daily_id',
'metrics_counter_submission_daily' => 'metrics_counter_submission_daily_id',
'metrics_submission' => 'metrics_submission_id',
'usage_stats_unique_item_investigations_temporary_records' => 'usage_stats_temp_unique_item_id',
'metrics_counter_submission_monthly' => 'metrics_counter_submission_monthly_id',
'usage_stats_total_temporary_records' => 'usage_stats_temp_total_id',
'usage_stats_institution_temporary_records' => 'usage_stats_temp_institution_id',
'metrics_submission_geo_daily' => 'metrics_submission_geo_daily_id',
'metrics_counter_submission_institution_monthly' => 'metrics_counter_submission_institution_monthly_id',
'metrics_issue' => 'metrics_issue_id',
'metrics_submission_geo_monthly' => 'metrics_submission_geo_monthly_id',
'custom_section_orders' => 'custom_section_order_id',
'custom_issue_orders' => 'custom_issue_order_id',
'funder_settings' => 'funder_setting_id', // PLUGIN
'funder_award_settings' => 'funder_award_setting_id', // PLUGIN
]);
}
public static function getIndexData(): array
{
return array_merge(parent::getIndexData(), [
'journal_settings' => ['journal_settings_pkey', ['journal_id', 'locale', 'setting_name'], 'journal_settings_unique', true],
'section_settings' => ['section_settings_pkey', ['section_id', 'locale', 'setting_name'], 'section_settings_unique', true],
'issue_settings' => ['issue_settings_pkey', ['issue_id', 'locale', 'setting_name'], 'issue_settings_unique', true],
'issue_galley_settings' => ['issue_galley_settings_pkey', ['galley_id', 'locale', 'setting_name'], 'issue_galley_settings_unique', true],
'custom_issue_orders' => ['custom_issue_orders_pkey', ['issue_id'], 'custom_issue_orders_unique', true],
'custom_section_orders' => ['custom_section_orders_pkey', ['issue_id', 'section_id'], 'custom_section_orders_unique', true],
'publication_galley_settings' => ['publication_galley_settings_pkey', ['galley_id', 'locale', 'setting_name'], 'publication_galley_settings_unique', true],
'subscription_type_settings' => ['subscription_type_settings_pkey', ['type_id', 'locale', 'setting_name'], 'subscription_type_settings_unique', true],
]);
}
}
@@ -0,0 +1,45 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I4235_OAISetSpec.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I4235_OAISetSpec
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
use PKP\oai\OAIUtils;
class I4235_OAISetSpec extends \PKP\migration\Migration
{
/**
* Run the migration.
*/
public function up(): void
{
// pkp/pkp-lib/issues/4235 Improve OAI-PMH set spec compliance
// Convert stored setSpec strings to valid format
$setSpecs = DB::table('data_object_tombstones')->select('set_spec')->distinct()->get()->toArray();
foreach ($setSpecs as $row) {
$a = preg_split('/:/', $row->set_spec);
if (count($a) == 2) {
[$journalSpec, $sectionSpec] = $a;
$new = OAIUtils::toValidSetSpec(urldecode($journalSpec)) . ':' . OAIUtils::toValidSetSpec(urldecode($sectionSpec));
DB::table('data_object_tombstones')->where('set_spec', $row->set_spec)->update(['set_spec' => $new]);
}
}
}
/**
* Reverse the downgrades
*/
public function down(): void
{
// The old format is not recoverable since some characters might have been stripped
}
}
@@ -0,0 +1,49 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I5716_EmailTemplateAssignments.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2000-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I5716_EmailTemplateAssignments
*
* @brief Refactors relationship between Mailables and Email Templates
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Collection;
use PKP\mail\mailables\DiscussionCopyediting;
use PKP\mail\mailables\DiscussionProduction;
use PKP\mail\mailables\DiscussionReview;
use PKP\mail\mailables\DiscussionSubmission;
class I5716_EmailTemplateAssignments extends \PKP\migration\upgrade\v3_4_0\I5716_EmailTemplateAssignments
{
protected function getContextTable(): string
{
return 'journals';
}
protected function getContextSettingsTable(): string
{
return 'journal_settings';
}
protected function getContextIdColumn(): string
{
return 'journal_id';
}
protected function getDiscussionTemplates(): Collection
{
return collect([
DiscussionSubmission::getEmailTemplateKey(),
DiscussionReview::getEmailTemplateKey(),
DiscussionCopyediting::getEmailTemplateKey(),
DiscussionProduction::getEmailTemplateKey(),
]);
}
}
@@ -0,0 +1,91 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6091_AddFilterNamespaces.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6091_AddFilterNamespaces
*
* @brief Describe upgrade/downgrade operations for introducing namespaces to the built-in set of filters.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
class I6091_AddFilterNamespaces extends \PKP\migration\Migration
{
public const FILTER_RENAME_MAP = [
// Application filters
'plugins.importexport.doaj.filter.DOAJXmlFilter' => 'APP\plugins\importexport\doaj\filter\DOAJXmlFilter',
'plugins.generic.datacite.filter.DataciteXmlFilter' => 'APP\plugins\generic\datacite\filter\DataciteXmlFilter',
'plugins.importexport.native.filter.ArticleNativeXmlFilter' => 'APP\plugins\importexport\native\filter\ArticleNativeXmlFilter',
'plugins.importexport.native.filter.NativeXmlArticleFilter' => 'APP\plugins\importexport\native\filter\NativeXmlArticleFilter',
'plugins.importexport.native.filter.IssueNativeXmlFilter' => 'APP\plugins\importexport\native\filter\IssueNativeXmlFilter',
'plugins.importexport.native.filter.NativeXmlIssueFilter' => 'APP\plugins\importexport\native\filter\NativeXmlIssueFilter',
'plugins.importexport.native.filter.IssueGalleyNativeXmlFilter' => 'APP\plugins\importexport\native\filter\IssueGalleyNativeXmlFilter',
'plugins.importexport.native.filter.NativeXmlIssueGalleyFilter' => 'APP\plugins\importexport\native\filter\NativeXmlIssueGalleyFilter',
'plugins.importexport.native.filter.AuthorNativeXmlFilter' => 'APP\plugins\importexport\native\filter\AuthorNativeXmlFilter',
'plugins.importexport.native.filter.NativeXmlAuthorFilter' => 'APP\plugins\importexport\native\filter\NativeXmlAuthorFilter',
'plugins.importexport.native.filter.NativeXmlArticleFileFilter' => 'APP\plugins\importexport\native\filter\NativeXmlArticleFileFilter',
'plugins.importexport.native.filter.ArticleGalleyNativeXmlFilter' => 'APP\plugins\importexport\native\filter\ArticleGalleyNativeXmlFilter',
'plugins.importexport.native.filter.NativeXmlArticleGalleyFilter' => 'APP\plugins\importexport\native\filter\NativeXmlArticleGalleyFilter',
'plugins.importexport.native.filter.PublicationNativeXmlFilter' => 'APP\plugins\importexport\native\filter\PublicationNativeXmlFilter',
'plugins.importexport.native.filter.NativeXmlPublicationFilter' => 'APP\plugins\importexport\native\filter\NativeXmlPublicationFilter',
'plugins.importexport.doaj.filter.DOAJJsonFilter' => 'APP\plugins\importexport\doaj\filter\DOAJJsonFilter',
'plugins.importexport.pubmed.filter.ArticlePubMedXmlFilter' => 'APP\plugins\importexport\pubmed\filter\ArticlePubMedXmlFilter',
'plugins.metadata.dc11.filter.Dc11SchemaArticleAdapter' => 'APP\plugins\metadata\dc11\filter\Dc11SchemaArticleAdapter',
'plugins.generic.crossref.filter.IssueCrossrefXmlFilter' => 'APP\plugins\generic\crossref\filter\IssueCrossrefXmlFilter',
'plugins.generic.crossref.filter.ArticleCrossrefXmlFilter' => 'APP\plugins\generic\crossref\filter\ArticleCrossrefXmlFilter',
// pkp-lib filters
'lib.pkp.plugins.importexport.users.filter.PKPUserUserXmlFilter' => 'PKP\plugins\importexport\users\filter\PKPUserUserXmlFilter',
'lib.pkp.plugins.importexport.users.filter.UserXmlPKPUserFilter' => 'PKP\plugins\importexport\users\filter\UserXmlPKPUserFilter',
'lib.pkp.plugins.importexport.users.filter.UserGroupNativeXmlFilter' => 'PKP\plugins\importexport\users\filter\UserGroupNativeXmlFilter',
'lib.pkp.plugins.importexport.users.filter.NativeXmlUserGroupFilter' => 'PKP\plugins\importexport\users\filter\NativeXmlUserGroupFilter',
'lib.pkp.plugins.importexport.native.filter.SubmissionFileNativeXmlFilter' => 'PKP\plugins\importexport\native\filter\SubmissionFileNativeXmlFilter',
];
public const TASK_RENAME_MAP = [
'lib.pkp.classes.task.ReviewReminder' => 'PKP\task\ReviewReminder',
'lib.pkp.classes.task.StatisticsReport' => 'PKP\task\StatisticsReport',
'classes.tasks.SubscriptionExpiryReminder' => 'APP\tasks\SubscriptionExpiryReminder',
'lib.pkp.classes.task.DepositDois' => 'PKP\task\DepositDois',
'lib.pkp.classes.task.RemoveUnvalidatedExpiredUsers' => 'PKP\task\RemoveUnvalidatedExpiredUsers',
'lib.pkp.classes.task.EditorialReminders' => 'PKP\task\EditorialReminders',
'lib.pkp.classes.task.UpdateIPGeoDB' => 'PKP\task\UpdateIPGeoDB',
'classes.tasks.UsageStatsLoader' => 'APP\tasks\UsageStatsLoader',
'plugins.importexport.doaj.DOAJInfoSender' => 'APP\plugins\importexport\doaj\DOAJInfoSender',
];
/**
* Run the migration.
*/
public function up(): void
{
foreach (self::FILTER_RENAME_MAP as $oldName => $newName) {
DB::statement('UPDATE filters SET class_name = ? WHERE class_name = ?', [$newName, $oldName]);
}
foreach (self::TASK_RENAME_MAP as $oldName => $newName) {
DB::statement('UPDATE scheduled_tasks SET class_name = ? WHERE class_name = ?', [$newName, $oldName]);
}
DB::statement('UPDATE filter_groups SET output_type=? WHERE output_type = ?', ['metadata::APP\plugins\metadata\dc11\schema\Dc11Schema(ARTICLE)', 'metadata::plugins.metadata.dc11.schema.Dc11Schema(ARTICLE)']);
}
/**
* Reverse the downgrades
*/
public function down(): void
{
foreach (self::FILTER_RENAME_MAP as $oldName => $newName) {
DB::statement('UPDATE filters SET class_name = ? WHERE class_name = ?', [$oldName, $newName]);
}
foreach (self::TASK_RENAME_MAP as $oldName => $newName) {
DB::statement('UPDATE scheduled_tasks SET class_name = ? WHERE class_name = ?', [$oldName, $newName]);
}
DB::statement('UPDATE filter_groups SET output_type=? WHERE output_type = ?', ['metadata::plugins.metadata.dc11.schema.Dc11Schema(ARTICLE)', 'metadata::APP\plugins\metadata\dc11\schema\Dc11Schema(ARTICLE)']);
}
}
@@ -0,0 +1,144 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6093_AddForeignKeys.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6093_AddForeignKeys
*
* @brief Describe upgrade/downgrade operations for introducing foreign key definitions to existing database relationships.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class I6093_AddForeignKeys extends \PKP\migration\upgrade\v3_4_0\I6093_AddForeignKeys
{
protected function getContextTable(): string
{
return 'journals';
}
protected function getContextKeyField(): string
{
return 'journal_id';
}
protected function getContextSettingsTable(): string
{
return 'journal_settings';
}
public function up(): void
{
parent::up();
Schema::table('sections', function (Blueprint $table) {
$table->foreign('review_form_id', 'sections_review_form_id')->references('review_form_id')->on('review_forms')->onDelete('set null');
$table->index(['review_form_id'], 'sections_review_form_id');
$table->foreign('journal_id', 'sections_journal_id')->references('journal_id')->on('journals')->onDelete('cascade');
});
Schema::table('section_settings', function (Blueprint $table) {
$table->foreign('section_id', 'section_settings_section_id')->references('section_id')->on('sections')->onDelete('cascade');
});
Schema::table('issues', function (Blueprint $table) {
$table->foreign('journal_id', 'issues_journal_id')->references('journal_id')->on('journals')->onDelete('cascade');
});
Schema::table('issue_settings', function (Blueprint $table) {
$table->foreign('issue_id', 'issue_settings_issue_id')->references('issue_id')->on('issues')->onDelete('cascade');
});
Schema::table('issue_files', function (Blueprint $table) {
$table->foreign('issue_id', 'issue_files_issue_id')->references('issue_id')->on('issues')->onDelete('cascade');
});
Schema::table('issue_galleys', function (Blueprint $table) {
$table->foreign('issue_id', 'issue_galleys_issue_id')->references('issue_id')->on('issues')->onDelete('cascade');
$table->foreign('file_id', 'issue_galleys_file_id')->references('file_id')->on('issue_files')->onDelete('cascade');
$table->index(['file_id'], 'issue_galleys_file_id');
});
Schema::table('issue_galley_settings', function (Blueprint $table) {
$table->foreign('galley_id', 'issue_galleys_settings_galley_id')->references('galley_id')->on('issue_galleys')->onDelete('cascade');
});
Schema::table('custom_issue_orders', function (Blueprint $table) {
$table->foreign('issue_id', 'custom_issue_orders_issue_id')->references('issue_id')->on('issues')->onDelete('cascade');
$table->index(['issue_id'], 'custom_issue_orders_issue_id');
$table->foreign('journal_id', 'custom_issue_orders_journal_id')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['journal_id'], 'custom_issue_orders_journal_id');
});
Schema::table('custom_section_orders', function (Blueprint $table) {
$table->foreign('issue_id', 'custom_section_orders_issue_id')->references('issue_id')->on('issues')->onDelete('cascade');
$table->index(['issue_id'], 'custom_section_orders_issue_id');
$table->foreign('section_id', 'custom_section_orders_section_id')->references('section_id')->on('sections')->onDelete('cascade');
$table->index(['section_id'], 'custom_section_orders_section_id');
});
Schema::table('publications', function (Blueprint $table) {
$table->foreign('section_id', 'publications_section_id')->references('section_id')->on('sections')->onDelete('set null');
$table->foreign('submission_id', 'publications_submission_id')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->foreign('primary_contact_id', 'publications_primary_contact_id')->references('author_id')->on('authors')->onDelete('set null');
$table->index(['primary_contact_id'], 'publications_primary_contact_id');
});
// Attempt to drop the previous foreign key, which doesn't have the cascade rule
if (DB::getDoctrineSchemaManager()->introspectTable('publication_galleys')->hasForeignKey('publication_galleys_submission_file_id_foreign')) {
Schema::table('publication_galleys', fn (Blueprint $table) => $table->dropForeign('publication_galleys_submission_file_id_foreign'));
}
Schema::table('publication_galleys', function (Blueprint $table) {
$table->foreign('publication_id', 'publication_galleys_publication_id')->references('publication_id')->on('publications')->onDelete('cascade');
$table->index(['submission_file_id'], 'publication_galleys_submission_file_id');
});
Schema::table('publication_galley_settings', function (Blueprint $table) {
$table->foreign('galley_id', 'publication_galley_settings_galley_id')->references('galley_id')->on('publication_galleys')->onDelete('cascade');
});
Schema::table('subscription_types', function (Blueprint $table) {
$table->foreign('journal_id', 'subscription_types_journal_id')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['journal_id'], 'subscription_types_journal_id');
});
Schema::table('subscription_type_settings', function (Blueprint $table) {
$table->foreign('type_id', 'subscription_type_settings_type_id')->references('type_id')->on('subscription_types')->onDelete('cascade');
});
Schema::table('subscriptions', function (Blueprint $table) {
$table->foreign('journal_id', 'subscriptions_journal_id')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['journal_id'], 'subscriptions_journal_id');
$table->foreign('user_id', 'subscriptions_user_id')->references('user_id')->on('users')->onDelete('cascade');
$table->index(['user_id'], 'subscriptions_user_id');
$table->foreign('type_id', 'subscriptions_type_id')->references('type_id')->on('subscription_types')->onDelete('cascade');
$table->index(['type_id'], 'subscriptions_type_id');
});
Schema::table('institutional_subscriptions', function (Blueprint $table) {
$table->foreign('subscription_id', 'institutional_subscriptions_subscription_id')->references('subscription_id')->on('subscriptions')->onDelete('cascade');
});
Schema::table('completed_payments', function (Blueprint $table) {
$table->foreign('context_id', 'completed_payments_context_id')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'completed_payments_context_id');
$table->foreign('user_id', 'completed_payments_user_id')->references('user_id')->on('users')->onDelete('set null');
$table->index(['user_id'], 'completed_payments_user_id');
});
Schema::table('journals', function (Blueprint $table) {
$table->index(['current_issue_id'], 'journals_issue_id');
});
}
}
@@ -0,0 +1,29 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6241_RequiredGenres.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6241_RequiredGenres
*
* @brief Set a required file genre for this app.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
class I6241_RequiredGenres extends \PKP\migration\upgrade\v3_4_0\I6241_RequiredGenres
{
public function up(): void
{
parent::up();
DB::table('genres')
->where('entry_key', 'SUBMISSION') // "Article Text" from genres.xml
->update(['required' => 1]);
}
}
@@ -0,0 +1,29 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6306_EnableCategories.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2000-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6306_EnableCategories
*/
namespace APP\migration\upgrade\v3_4_0;
class I6306_EnableCategories extends \PKP\migration\upgrade\v3_4_0\I6306_EnableCategories
{
protected function getContextTable(): string
{
return 'journals';
}
protected function getContextSettingsTable(): string
{
return 'journal_settings';
}
protected function getContextIdColumn(): string
{
return 'journal_id';
}
}
@@ -0,0 +1,27 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6782_CleanOldMetrics.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6782_CleanOldMetrics
*
* @brief Clean the old metrics:
* delete migrated entries with the metric type ojs::counter from the DB table metrics,
* move back the orphaned metrics from the temporary metrics_tmp,
* rename or delete the DB table metrics,
* delete DB table usage_stats_temporary_records.
*/
namespace APP\migration\upgrade\v3_4_0;
class I6782_CleanOldMetrics extends \PKP\migration\upgrade\v3_4_0\I6782_CleanOldMetrics
{
protected function getMetricType(): string
{
return 'ojs::counter';
}
}
@@ -0,0 +1,378 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6782_CreateNewMetricsTables.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6782_CreateNewMetricsTables
*
* @brief Describe database table structures.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema as Schema;
use PKP\install\DowngradeNotSupportedException;
use PKP\migration\Migration;
class I6782_CreateNewMetricsTables extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('metrics_context', function (Blueprint $table) {
$table->bigIncrements('metrics_context_id');
$table->string('load_id', 255);
$table->index(['load_id'], 'metrics_context_load_id');
$table->bigInteger('context_id');
$table->foreign('context_id')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'metrics_context_context_id');
$table->date('date');
$table->integer('metric');
});
Schema::create('metrics_submission', function (Blueprint $table) {
$table->bigIncrements('metrics_submission_id');
$table->string('load_id', 255);
$table->index(['load_id'], 'ms_load_id');
$table->bigInteger('context_id');
$table->foreign('context_id')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'metrics_submission_context_id');
$table->bigInteger('submission_id');
$table->foreign('submission_id')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'metrics_submission_submission_id');
$table->bigInteger('representation_id')->nullable();
$table->foreign('representation_id')->references('galley_id')->on('publication_galleys')->onDelete('cascade');
$table->index(['representation_id'], 'metrics_submission_representation_id');
$table->bigInteger('submission_file_id')->unsigned()->nullable();
$table->foreign('submission_file_id')->references('submission_file_id')->on('submission_files')->onDelete('cascade');
$table->index(['submission_file_id'], 'metrics_submission_submission_file_id');
$table->bigInteger('file_type')->nullable();
$table->bigInteger('assoc_type');
$table->date('date');
$table->integer('metric');
$table->index(['context_id', 'submission_id', 'assoc_type', 'file_type'], 'ms_context_id_submission_id_assoc_type_file_type');
});
Schema::create('metrics_issue', function (Blueprint $table) {
$table->bigIncrements('metrics_issue_id');
$table->string('load_id', 255);
$table->index(['load_id'], 'metrics_issue_load_id');
$table->bigInteger('context_id');
$table->foreign('context_id')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'metrics_issue_context_id');
$table->bigInteger('issue_id');
$table->foreign('issue_id')->references('issue_id')->on('issues')->onDelete('cascade');
$table->index(['issue_id'], 'metrics_issue_issue_id');
$table->bigInteger('issue_galley_id')->nullable();
$table->foreign('issue_galley_id')->references('galley_id')->on('issue_galleys')->onDelete('cascade');
$table->index(['issue_galley_id'], 'metrics_issue_issue_galley_id');
$table->date('date');
$table->integer('metric');
$table->index(['context_id', 'issue_id'], 'metrics_issue_context_id_issue_id');
});
Schema::create('metrics_counter_submission_daily', function (Blueprint $table) {
$table->bigIncrements('metrics_counter_submission_daily_id');
$table->string('load_id', 255);
$table->index(['load_id'], 'msd_load_id');
$table->bigInteger('context_id');
$table->foreign('context_id', 'msd_context_id_foreign')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'metrics_counter_submission_daily_context_id');
$table->bigInteger('submission_id');
$table->foreign('submission_id', 'msd_submission_id_foreign')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'metrics_counter_submission_daily_submission_id');
$table->date('date');
$table->integer('metric_investigations');
$table->integer('metric_investigations_unique');
$table->integer('metric_requests');
$table->integer('metric_requests_unique');
$table->index(['context_id', 'submission_id'], 'msd_context_id_submission_id');
$table->unique(['load_id', 'context_id', 'submission_id', 'date'], 'msd_uc_load_id_context_id_submission_id_date');
});
Schema::create('metrics_counter_submission_monthly', function (Blueprint $table) {
$table->bigIncrements('metrics_counter_submission_monthly_id');
$table->bigInteger('context_id');
$table->foreign('context_id', 'msm_context_id_foreign')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'metrics_counter_submission_monthly_context_id');
$table->bigInteger('submission_id');
$table->foreign('submission_id', 'msm_submission_id_foreign')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'metrics_counter_submission_monthly_submission_id');
$table->integer('month');
$table->integer('metric_investigations');
$table->integer('metric_investigations_unique');
$table->integer('metric_requests');
$table->integer('metric_requests_unique');
$table->index(['context_id', 'submission_id'], 'msm_context_id_submission_id');
$table->unique(['context_id', 'submission_id', 'month'], 'msm_uc_context_id_submission_id_month');
});
Schema::create('metrics_counter_submission_institution_daily', function (Blueprint $table) {
$table->bigIncrements('metrics_counter_submission_institution_daily_id');
$table->string('load_id', 255);
$table->index(['load_id'], 'msid_load_id');
$table->bigInteger('context_id');
$table->foreign('context_id', 'msid_context_id_foreign')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'metrics_counter_submission_institution_daily_context_id');
$table->bigInteger('submission_id');
$table->foreign('submission_id', 'msid_submission_id_foreign')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'metrics_counter_submission_institution_daily_submission_id');
$table->bigInteger('institution_id');
$table->foreign('institution_id', 'msid_institution_id_foreign')->references('institution_id')->on('institutions')->onDelete('cascade');
$table->index(['institution_id'], 'metrics_counter_submission_institution_daily_institution_id');
$table->date('date');
$table->integer('metric_investigations');
$table->integer('metric_investigations_unique');
$table->integer('metric_requests');
$table->integer('metric_requests_unique');
$table->index(['context_id', 'submission_id'], 'msid_context_id_submission_id');
$table->unique(['load_id', 'context_id', 'submission_id', 'institution_id', 'date'], 'msid_uc_load_id_context_id_submission_id_institution_id_date');
});
Schema::create('metrics_counter_submission_institution_monthly', function (Blueprint $table) {
$table->bigIncrements('metrics_counter_submission_institution_monthly_id');
$table->bigInteger('context_id');
$table->foreign('context_id', 'msim_context_id_foreign')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'metrics_counter_submission_institution_monthly_context_id');
$table->bigInteger('submission_id');
$table->foreign('submission_id', 'msim_submission_id_foreign')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'metrics_counter_submission_institution_monthly_submission_id');
$table->bigInteger('institution_id');
$table->foreign('institution_id', 'msim_institution_id_foreign')->references('institution_id')->on('institutions')->onDelete('cascade');
$table->index(['institution_id'], 'metrics_counter_submission_institution_monthly_institution_id');
$table->integer('month');
$table->integer('metric_investigations');
$table->integer('metric_investigations_unique');
$table->integer('metric_requests');
$table->integer('metric_requests_unique');
$table->index(['context_id', 'submission_id'], 'msim_context_id_submission_id');
$table->unique(['context_id', 'submission_id', 'institution_id', 'month'], 'msim_uc_context_id_submission_id_institution_id_month');
});
Schema::create('metrics_submission_geo_daily', function (Blueprint $table) {
$table->bigIncrements('metrics_submission_geo_daily_id');
$table->string('load_id', 255);
$table->index(['load_id'], 'msgd_load_id');
$table->bigInteger('context_id');
$table->foreign('context_id', 'msgd_context_id_foreign')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'metrics_submission_geo_daily_context_id');
$table->bigInteger('submission_id');
$table->foreign('submission_id', 'msgd_submission_id_foreign')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'metrics_submission_geo_daily_submission_id');
$table->string('country', 2)->default('');
$table->string('region', 3)->default('');
$table->string('city', 255)->default('');
$table->date('date');
$table->integer('metric');
$table->integer('metric_unique');
$table->index(['context_id', 'submission_id'], 'msgd_context_id_submission_id');
$table->unique(['load_id', 'context_id', 'submission_id', 'country', 'region', 'city', 'date'], 'msgd_uc_load_context_submission_c_r_c_date');
});
Schema::create('metrics_submission_geo_monthly', function (Blueprint $table) {
$table->bigIncrements('metrics_submission_geo_monthly_id');
$table->bigInteger('context_id');
$table->foreign('context_id', 'msgm_context_id_foreign')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'metrics_submission_geo_monthly_context_id');
$table->bigInteger('submission_id');
$table->foreign('submission_id', 'msgm_submission_id_foreign')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'metrics_submission_geo_monthly_submission_id');
$table->string('country', 2)->default('');
$table->string('region', 3)->default('');
$table->string('city', 255)->default('');
$table->integer('month');
$table->integer('metric');
$table->integer('metric_unique');
$table->index(['context_id', 'submission_id'], 'msgm_context_id_submission_id');
$table->unique(['context_id', 'submission_id', 'country', 'region', 'city', 'month'], 'msgm_uc_context_submission_c_r_c_month');
});
// Usage stats total item temporary records
Schema::create('usage_stats_total_temporary_records', function (Blueprint $table) {
$table->bigIncrements('usage_stats_temp_total_id');
$table->dateTime('date', $precision = 0);
$table->string('ip', 255);
$table->string('user_agent', 255);
$table->bigInteger('line_number');
$table->string('canonical_url', 255);
$table->bigInteger('issue_id')->nullable();
$table->foreign('issue_id', 'ust_issue_id_foreign')->references('issue_id')->on('issues')->onDelete('cascade');
$table->index(['issue_id'], 'usage_stats_total_temporary_records_issue_id');
$table->bigInteger('issue_galley_id')->nullable();
$table->foreign('issue_galley_id', 'ust_issue_galley_id_foreign')->references('galley_id')->on('issue_galleys')->onDelete('cascade');
$table->index(['issue_galley_id'], 'usage_stats_total_temporary_records_issue_galley_id');
$table->bigInteger('context_id');
$table->foreign('context_id', 'ust_context_id_foreign')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'usage_stats_total_temporary_records_context_id');
$table->bigInteger('submission_id')->nullable();
$table->foreign('submission_id', 'ust_submission_id_foreign')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'usage_stats_total_temporary_records_submission_id');
$table->bigInteger('representation_id')->nullable();
$table->foreign('representation_id', 'ust_representation_id_foreign')->references('galley_id')->on('publication_galleys')->onDelete('cascade');
$table->index(['representation_id'], 'usage_stats_total_temporary_records_representation_id');
$table->bigInteger('submission_file_id')->unsigned()->nullable();
$table->foreign('submission_file_id', 'ust_submission_file_id_foreign')->references('submission_file_id')->on('submission_files')->onDelete('cascade');
$table->index(['submission_file_id'], 'usage_stats_total_temporary_records_submission_file_id');
$table->bigInteger('assoc_type');
$table->smallInteger('file_type')->nullable();
$table->string('country', 2)->default('');
$table->string('region', 3)->default('');
$table->string('city', 255)->default('');
$table->string('load_id', 255);
});
// Usage stats unique item investigations temporary records
// No need to consider issue_id and issue_galley_id here because
// investigations are only relevant/calculated on submission level.
Schema::create('usage_stats_unique_item_investigations_temporary_records', function (Blueprint $table) {
$table->bigIncrements('usage_stats_temp_unique_item_id');
$table->dateTime('date', $precision = 0);
$table->string('ip', 255);
$table->string('user_agent', 255);
$table->bigInteger('line_number');
$table->bigInteger('context_id');
$table->foreign('context_id', 'usii_context_id_foreign')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'usii_context_id');
$table->bigInteger('submission_id');
$table->foreign('submission_id', 'usii_submission_id_foreign')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'usii_submission_id');
$table->bigInteger('representation_id')->nullable();
$table->foreign('representation_id', 'usii_representation_id_foreign')->references('galley_id')->on('publication_galleys')->onDelete('cascade');
$table->index(['representation_id'], 'usii_representation_id');
$table->bigInteger('submission_file_id')->unsigned()->nullable();
$table->foreign('submission_file_id', 'usii_submission_file_id_foreign')->references('submission_file_id')->on('submission_files')->onDelete('cascade');
$table->index(['submission_file_id'], 'usii_submission_file_id');
$table->bigInteger('assoc_type');
$table->smallInteger('file_type')->nullable();
$table->string('country', 2)->default('');
$table->string('region', 3)->default('');
$table->string('city', 255)->default('');
$table->string('load_id', 255);
});
// Usage stats unique item requests temporary records
// No need to consider issue_id and issue_galley_id here because
// requests are only relevant/calculated on submission level.
Schema::create('usage_stats_unique_item_requests_temporary_records', function (Blueprint $table) {
$table->bigIncrements('usage_stats_temp_item_id');
$table->dateTime('date', $precision = 0);
$table->string('ip', 255);
$table->string('user_agent', 255);
$table->bigInteger('line_number');
$table->bigInteger('context_id');
$table->foreign('context_id', 'usir_context_id_foreign')->references('journal_id')->on('journals')->onDelete('cascade');
$table->index(['context_id'], 'usir_context_id');
$table->bigInteger('submission_id');
$table->foreign('submission_id', 'usir_submission_id_foreign')->references('submission_id')->on('submissions')->onDelete('cascade');
$table->index(['submission_id'], 'usir_submission_id');
$table->bigInteger('representation_id')->nullable();
$table->foreign('representation_id', 'usir_representation_id_foreign')->references('galley_id')->on('publication_galleys')->onDelete('cascade');
$table->index(['representation_id'], 'usir_representation_id');
$table->bigInteger('submission_file_id')->unsigned()->nullable();
$table->foreign('submission_file_id', 'usir_submission_file_id_foreign')->references('submission_file_id')->on('submission_files')->onDelete('cascade');
$table->index(['submission_file_id'], 'usir_submission_file_id');
$table->bigInteger('assoc_type');
$table->smallInteger('file_type')->nullable();
$table->string('country', 2)->default('');
$table->string('region', 3)->default('');
$table->string('city', 255)->default('');
$table->string('load_id', 255);
});
// Usage stats institution temporary records
// This table is needed because of data normalization
Schema::create('usage_stats_institution_temporary_records', function (Blueprint $table) {
$table->bigIncrements('usage_stats_temp_institution_id');
$table->string('load_id', 255);
$table->bigInteger('line_number');
$table->bigInteger('institution_id');
$table->foreign('institution_id', 'usi_institution_id_foreign')->references('institution_id')->on('institutions')->onDelete('cascade');
$table->index(['institution_id'], 'usi_institution_id');
$table->unique(['load_id', 'line_number', 'institution_id'], 'usitr_load_id_line_number_institution_id');
});
}
/**
* Reverse the migration
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,30 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6782_MetricsContext.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6782_MetricsContext
*
* @brief Migrate context stats data from the old DB table metrics into the new DB table metrics_context.
*/
namespace APP\migration\upgrade\v3_4_0;
class I6782_MetricsContext extends \PKP\migration\upgrade\v3_4_0\I6782_MetricsContext
{
private const ASSOC_TYPE_CONTEXT = 0x0000100;
protected function getMetricType(): string
{
return 'ojs::counter';
}
protected function getContextAssocType(): int
{
return self::ASSOC_TYPE_CONTEXT;
}
}
@@ -0,0 +1,23 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6782_MetricsGeo.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6782_MetricsGeo
*
* @brief Migrate submission stats Geo data from the old DB table metrics into the new DB table metrics_submission_geo_daily, then aggregate monthly.
*/
namespace APP\migration\upgrade\v3_4_0;
class I6782_MetricsGeo extends \PKP\migration\upgrade\v3_4_0\I6782_MetricsGeo
{
protected function getMetricType(): string
{
return 'ojs::counter';
}
}
@@ -0,0 +1,62 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6782_MetricsIssue.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6782_MetricsIssue
*
* @brief Migrate issue stats data from the old DB table metrics into the new DB table metrics_issue.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
use PKP\config\Config;
use PKP\install\DowngradeNotSupportedException;
use PKP\migration\Migration;
class I6782_MetricsIssue extends Migration
{
private const ASSOC_TYPE_ISSUE = 0x0000103;
private const ASSOC_TYPE_ISSUE_GALLEY = 0x0000105;
/**
* Run the migration.
*/
public function up(): void
{
$dayFormatSql = "DATE_FORMAT(STR_TO_DATE(m.day, '%Y%m%d'), '%Y-%m-%d')";
if (substr(Config::getVar('database', 'driver'), 0, strlen('postgres')) === 'postgres') {
$dayFormatSql = "to_date(m.day, 'YYYYMMDD')";
}
// The not existing foreign keys should already be moved to the metrics_tmp in I6782_OrphanedMetrics
// Migrate issue metrics; consider issue TOCs and galley files
$selectIssueMetrics = DB::table('metrics as m')
->select(DB::raw("m.load_id, m.context_id, m.assoc_id, null, {$dayFormatSql}, m.metric"))
->where('m.assoc_type', '=', self::ASSOC_TYPE_ISSUE)
->where('m.metric_type', '=', 'ojs::counter');
DB::table('metrics_issue')->insertUsing(['load_id', 'context_id', 'issue_id', 'issue_galley_id', 'date', 'metric'], $selectIssueMetrics);
$selectIssueGalleyMetrics = DB::table('metrics as m')
->join('issue_galleys as ig', 'ig.galley_id', '=', 'm.assoc_id')
->select(DB::raw("m.load_id, m.context_id, ig.issue_id, m.assoc_id, {$dayFormatSql}, m.metric"))
->where('m.assoc_type', '=', self::ASSOC_TYPE_ISSUE_GALLEY)
->where('m.metric_type', '=', 'ojs::counter');
DB::table('metrics_issue')->insertUsing(['load_id', 'context_id', 'issue_id', 'issue_galley_id', 'date', 'metric'], $selectIssueGalleyMetrics);
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,68 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6782_MetricsSubmission.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6782_MetricsSubmission
*
* @brief Migrate submissions stats data from the old DB table metrics into the new DB table metrics_submission.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
use PKP\config\Config;
use PKP\install\DowngradeNotSupportedException;
use PKP\migration\Migration;
class I6782_MetricsSubmission extends Migration
{
private const ASSOC_TYPE_SUBMISSION = 0x0100009;
private const ASSOC_TYPE_SUBMISSION_FILE = 0x0000203;
private const ASSOC_TYPE_SUBMISSION_FILE_COUNTER_OTHER = 0x0000213;
/**
* Run the migration.
*/
public function up(): void
{
$dayFormatSql = "DATE_FORMAT(STR_TO_DATE(m.day, '%Y%m%d'), '%Y-%m-%d')";
if (substr(Config::getVar('database', 'driver'), 0, strlen('postgres')) === 'postgres') {
$dayFormatSql = "to_date(m.day, 'YYYYMMDD')";
}
// The not existing foreign keys should already be moved to the metrics_tmp in I6782_OrphanedMetrics
// Migrate submission metrics; consider abstracts, galley and supp files
$selectSubmissionMetrics = DB::table('metrics as m')
->select(DB::raw("m.load_id, m.context_id, m.assoc_id, null, null, null, m.assoc_type, {$dayFormatSql}, m.metric"))
->where('m.assoc_type', '=', self::ASSOC_TYPE_SUBMISSION)
->where('m.metric_type', '=', 'ojs::counter');
DB::table('metrics_submission')->insertUsing(['load_id', 'context_id', 'submission_id', 'representation_id', 'submission_file_id', 'file_type', 'assoc_type', 'date', 'metric'], $selectSubmissionMetrics);
$selectSubmissionFileMetrics = DB::table('metrics as m')
->select(DB::raw("m.load_id, m.context_id, m.submission_id, m.representation_id, m.assoc_id, m.file_type, m.assoc_type, {$dayFormatSql}, m.metric"))
->where('m.assoc_type', '=', self::ASSOC_TYPE_SUBMISSION_FILE)
->where('m.metric_type', '=', 'ojs::counter');
DB::table('metrics_submission')->insertUsing(['load_id', 'context_id', 'submission_id', 'representation_id', 'submission_file_id', 'file_type', 'assoc_type', 'date', 'metric'], $selectSubmissionFileMetrics);
$selectSubmissionSuppFileMetrics = DB::table('metrics as m')
->select(DB::raw("m.load_id, m.context_id, m.submission_id, m.representation_id, m.assoc_id, m.file_type, m.assoc_type, {$dayFormatSql}, m.metric"))
->where('m.assoc_type', '=', self::ASSOC_TYPE_SUBMISSION_FILE_COUNTER_OTHER)
->where('m.metric_type', '=', 'ojs::counter');
DB::table('metrics_submission')->insertUsing(['load_id', 'context_id', 'submission_id', 'representation_id', 'submission_file_id', 'file_type', 'assoc_type', 'date', 'metric'], $selectSubmissionSuppFileMetrics);
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,94 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6782_OrphanedMetrics.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6782_OrphanedMetrics
*
* @brief Migrate metrics data from objects that do not exist any more and from assoc types that are not considered in the upgrade into the temporary table.
* These entries will be copied back and stay in the table metrics_old, s. I6782_CleanOldMetrics.
* Consider only metric_type ojs::counter here, because these entries will be removed during the upgrade.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class I6782_OrphanedMetrics extends \PKP\migration\upgrade\v3_4_0\I6782_OrphanedMetrics
{
private const ASSOC_TYPE_CONTEXT = 0x0000100;
private const ASSOC_TYPE_ISSUE = 0x0000103;
private const ASSOC_TYPE_ISSUE_GALLEY = 0x0000105;
protected function getMetricType(): string
{
return 'ojs::counter';
}
protected function getContextAssocType(): int
{
return self::ASSOC_TYPE_CONTEXT;
}
protected function getContextTable(): string
{
return 'journals';
}
protected function getContextKeyField(): string
{
return 'journal_id';
}
protected function getRepresentationTable(): string
{
return 'publication_galleys';
}
protected function getRepresentationKeyField(): string
{
return 'galley_id';
}
protected function getAssocTypesToMigrate(): array
{
return array_merge(
[
self::ASSOC_TYPE_CONTEXT,
self::ASSOC_TYPE_ISSUE,
self::ASSOC_TYPE_ISSUE_GALLEY,
],
parent::getAssocTypesToMigrate()
);
}
/**
* Run the migration.
*
* assoc_object_type, assoc_object_id, and pkp_section_id will not be considered here, because they are not relevant for the migration
*/
public function up(): void
{
parent::up();
$metricsColumns = Schema::getColumnListing('metrics_tmp');
// Metrics issue IDs
// as m.assoc_id
$orphanedIds = DB::table('metrics AS m')->leftJoin('issues AS i', 'm.assoc_id', '=', 'i.issue_id')->where('m.assoc_type', '=', self::ASSOC_TYPE_ISSUE)->whereNull('i.issue_id')->distinct()->pluck('m.assoc_id');
$orphandedIssues = DB::table('metrics')->select($metricsColumns)->where('assoc_type', '=', self::ASSOC_TYPE_ISSUE)->whereIn('assoc_id', $orphanedIds)->where('metric_type', '=', $this->getMetricType());
DB::table('metrics_tmp')->insertUsing($metricsColumns, $orphandedIssues);
DB::table('metrics')->where('assoc_type', '=', self::ASSOC_TYPE_ISSUE)->whereIn('assoc_id', $orphanedIds)->delete();
// Clean orphaned metrics issue galley IDs
$orphanedIds = DB::table('metrics AS m')->leftJoin('issue_galleys AS ig', 'm.assoc_id', '=', 'ig.galley_id')->where('m.assoc_type', '=', self::ASSOC_TYPE_ISSUE_GALLEY)->whereNull('ig.galley_id')->distinct()->pluck('m.assoc_id');
$orphandedIssuesGalleys = DB::table('metrics')->select($metricsColumns)->where('assoc_type', '=', self::ASSOC_TYPE_ISSUE_GALLEY)->whereIn('assoc_id', $orphanedIds)->where('metric_type', '=', $this->getMetricType());
DB::table('metrics_tmp')->insertUsing($metricsColumns, $orphandedIssuesGalleys);
DB::table('metrics')->where('assoc_type', '=', self::ASSOC_TYPE_ISSUE_GALLEY)->whereIn('assoc_id', $orphanedIds)->delete();
}
}
@@ -0,0 +1,62 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6782_RemovePlugins.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6782_RemovePlugins
*
* @brief Remove the usageStats and views report plugin.
*
* This script has to be called after I6782_Metrics, i.e. after usageStats plugin settings were successfully migrated.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
use PKP\install\DowngradeNotSupportedException;
use PKP\migration\Migration;
class I6782_RemovePlugins extends Migration
{
/**
* Run the migration.
*/
public function up(): void
{
// Remove usageStats plugin and views report plugin
// Differently to versionDao->disableVersion, we will remove the entry from the table 'versions' and 'plugin_settings'
// because the plugins cannot be used any more
DB::table('versions')
->where('product_type', '=', 'plugins.generic')
->where('product', '=', 'usageStats')
->delete();
DB::table('plugin_settings')
->where('plugin_name', '=', 'usagestatsplugin')
->delete();
DB::table('versions')
->where('product_type', '=', 'plugins.reports')
->where('product', '=', 'views')
->delete();
// It is not needed to remove usageStats plugin scheduled task from the Acron plugin, because
// PKPAcronPlugin function _parseCrontab() will be called at the end of update, that
// will overwrite the old crontab setting.
// Remove the old scheduled task from the table scheduled_tasks
DB::table('scheduled_tasks')->where('class_name', '=', 'plugins.generic.usageStats.UsageStatsLoader')->delete();
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,42 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6807_SetLastModified.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6807_SetLastModified
*
* @brief Update last modification dates where they are not yet set.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
class I6807_SetLastModified extends \PKP\migration\Migration
{
/**
* Run the migration.
*/
public function up(): void
{
// pkp/pkp-lib#6807 Make sure all submission/issue last modification dates are set
DB::statement('UPDATE issues SET last_modified = date_published WHERE last_modified IS NULL');
DB::statement('UPDATE submissions SET last_modified = NOW() WHERE last_modified IS NULL');
}
/**
* Reverse the downgrades
*/
public function down(): void
{
// We don't have the data to downgrade and downgrades are unwanted here anyway.
}
}
if (!PKP_STRICT_MODE) {
class_alias('\APP\migration\upgrade\v3_4_0\I6807_SetLastModified', '\I6807_SetLastModified');
}
@@ -0,0 +1,89 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I6895_Institutions.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I6895_Institutions
*
* @brief Migrate institution data from subscriptions into the new institution data model.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use PKP\install\DowngradeNotSupportedException;
use PKP\migration\Migration;
class I6895_Institutions extends Migration
{
/**
* Run the migration.
*/
public function up(): void
{
// Requires that institution tables are already there
// Add the new column institution_id to the table institutional_subscriptions
Schema::table('institutional_subscriptions', function (Blueprint $table) {
$table->bigInteger('institution_id')->default(0);
});
// pkp/pkp-lib#6895 Migrate all institutions from institutional subscriptions into new databases
$institutionalSubscriptions = DB::table('institutional_subscriptions AS i')
->select('i.institutional_subscription_id', 'i.subscription_id', 'i.institution_name', 's.journal_id', 'j.primary_locale')
->join('subscriptions AS s', 's.subscription_id', '=', 'i.subscription_id')
->join('journals AS j', 'j.journal_id', '=', 's.journal_id')
->get();
foreach ($institutionalSubscriptions as $institutionalSubscription) {
$institutionId = DB::table('institutions')->insertGetId(['context_id' => $institutionalSubscription->journal_id], 'institution_id');
if ($institutionId) {
DB::table('institution_settings')->insert(['institution_id' => $institutionId, 'setting_name' => 'name', 'setting_value' => $institutionalSubscription->institution_name, 'locale' => $institutionalSubscription->primary_locale]);
$affected = DB::table('institutional_subscriptions')
->where('institutional_subscription_id', $institutionalSubscription->institutional_subscription_id)
->update(['institution_id' => $institutionId]);
// Get IP ranges
$ipRanges = DB::table('institutional_subscription_ip')
->select('ip_string', 'ip_start', 'ip_end')
->where('subscription_id', '=', $institutionalSubscription->subscription_id)
->get();
foreach ($ipRanges as $ipRange) {
DB::table('institution_ip')->insert(['institution_id' => $institutionId, 'ip_string' => $ipRange->ip_string, 'ip_start' => $ipRange->ip_start, 'ip_end' => $ipRange->ip_end]);
}
}
}
// Drop the table institutional_subscription_ip
Schema::drop('institutional_subscription_ip');
// Drop column institution_name form institutional_subscriptions
Schema::table('institutional_subscriptions', function (Blueprint $table) {
$table->dropColumn('institution_name');
});
// Create the foreign key constraint (now that the values are correct and match the IDs in the parent table)
Schema::table('institutional_subscriptions', function (Blueprint $table) {
$table->foreign('institution_id')->references('institution_id')->on('institutions')->onDelete('cascade');
$table->index(['institution_id'], 'institutional_subscriptions_institution_id');
});
DB::statement('ALTER TABLE institutional_subscriptions ALTER COLUMN institution_id DROP DEFAULT');
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,794 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7014_DoiMigration.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7014_DoiMigration
*
* @brief Describe upgrade/downgrade operations for DB table dois.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use PKP\doi\Doi;
use PKP\install\DowngradeNotSupportedException;
use PKP\migration\upgrade\v3_4_0\PKPI7014_DoiMigration;
class I7014_DoiMigration extends PKPI7014_DoiMigration
{
/**
* Run the migrations.
*/
public function up(): void
{
parent::up();
// Add doiId to issue
Schema::table('issues', function (Blueprint $table) {
$table->bigInteger('doi_id')->nullable();
$table->foreign('doi_id')->references('doi_id')->on('dois')->nullOnDelete();
$table->index(['doi_id'], 'issues_doi_id');
});
// Add doiId to galley
Schema::table('publication_galleys', function (Blueprint $table) {
$table->bigInteger('doi_id')->nullable();
$table->foreign('doi_id')->references('doi_id')->on('dois')->nullOnDelete();
$table->index(['doi_id'], 'publication_galleys_doi_id');
});
$this->migrateExistingDataUp();
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
protected function migrateExistingDataUp(): void
{
parent::migrateExistingDataUp();
// Find all existing DOIs, move to new DOI objects and add foreign key for pub object
$this->_migrateGalleyDoisUp();
$this->_migrateIssueDoisUp();
$this->_migrateCrossrefSettingsToContext();
$this->_migrateDataciteSettingsToContext();
}
/**
* Transfer over batch_id, failedMsg and settings, filter info for importExport to generic plugin transfer
*/
private function _migrateCrossrefSettingsToContext()
{
// ===== Filters ===== //
DB::table('filters')
->where('class_name', '=', 'plugins.importexport.crossref.filter.IssueCrossrefXmlFilter')
->update(['class_name' => 'plugins.generic.crossref.filter.IssueCrossrefXmlFilter']);
DB::table('filters')
->where('class_name', '=', 'plugins.importexport.crossref.filter.ArticleCrossrefXmlFilter')
->update(['class_name' => 'plugins.generic.crossref.filter.ArticleCrossrefXmlFilter']);
// ===== Submissions Statuses & Settings ===== //
// 1. Get submissions with Crossref info
$publicationDois = DB::table('publications', 'p')
->select(['p.submission_id', 'p.doi_id'])
->whereNotNull('p.doi_id')
->get()
->map(function ($item) {
return [
'submission_id' => $item->submission_id,
'doi_id' => $item->doi_id
];
});
$galleyDois = DB::table('publication_galleys', 'pg')
->leftJoin('publications as p', 'pg.publication_id', '=', 'p.publication_id')
->select(['p.submission_id', 'pg.doi_id'])
->whereNotNull('pg.doi_id')
->get()
->map(function ($item) {
return [
'submission_id' => $item->submission_id,
'doi_id' => $item->doi_id
];
});
// 2. Get all DOIs possibly associated with submission (publications and galleys)
$doisBySubmission = $publicationDois->concat($galleyDois)
->mapToGroups(function ($item) {
return [$item['submission_id'] => $item['doi_id']];
})
->map(function ($item) {
return ['doiIds' => $item->all()];
})->toArray();
// 3. Apply batchId and failedMsg to all DOIs
DB::table('submissions', 's')
->leftJoin('submission_settings as ss', 's.submission_id', '=', 'ss.submission_id')
->whereIn('ss.setting_name', ['crossref::registeredDoi', 'crossref::status', 'crossref::batchId', 'crossref::failedMsg'])
->select(['ss.submission_id', 'ss.setting_name', 'ss.setting_value'])
->get()
->each(function ($item) use (&$doisBySubmission) {
if (!isset($doisBySubmission[$item->submission_id])) {
return;
}
switch ($item->setting_name) {
case 'crossref::registeredDoi':
$doisBySubmission[$item->submission_id]['crossref::registeredDoi'] = $item->setting_value;
return;
case 'crossref::status':
$status = Doi::STATUS_ERROR;
$registrationAgency = null;
if (in_array($item->setting_value, ['found', 'registered', 'markedRegistered'])) {
$status = Doi::STATUS_REGISTERED;
if ($item->setting_value == 'registered') {
$registrationAgency = 'CrossrefExportPlugin';
}
}
$doisBySubmission[$item->submission_id]['status'] = $status;
if ($registrationAgency !== null) {
$doisBySubmission[$item->submission_id]['registrationAgency'] = $registrationAgency;
}
return;
case 'crossref::batchId':
$doisBySubmission[$item->submission_id]['crossrefplugin_batchId'] = $item->setting_value;
return;
case 'crossref::failedMsg':
$doisBySubmission[$item->submission_id]['crossrefplugin_failedMsg'] = $item->setting_value;
return;
}
});
// 4. Apply status to all DOIs
$doiSettingInserts = [];
$doiStatusUpdates = [];
foreach ($doisBySubmission as $item) {
$doiIds = $item['doiIds'];
foreach ($doiIds as $doiId) {
// Settings
if (isset($item['crossrefplugin_batchId'])) {
$doiSettingInserts[] = [
'doi_id' => $doiId,
'setting_name' => 'crossrefplugin_batchId',
'setting_value' => $item['crossrefplugin_batchId'],
];
}
if (isset($item['crossrefplugin_failedMsg'])) {
$doiSettingInserts[] = [
'doi_id' => $doiId,
'setting_name' => 'crossrefplugin_failedMsg',
'setting_value' => $item['crossrefplugin_failedMsg'],
];
}
// Status
if (isset($item['status'])) {
$doiStatusUpdates[$doiId] = ['status' => $item['status']];
} elseif (isset($item['crossref::registeredDoi'])) {
$doiStatusUpdates[$doiId] = ['status' => 3];
}
if (isset($item['registrationAgency'])) {
$doiSettingInserts[] = [
'doi_id' => $doiId,
'setting_name' => 'registrationAgency',
'setting_value' => $item['registrationAgency']
];
}
}
}
foreach (array_chunk($doiSettingInserts, 100) as $chunkedInserts) {
DB::table('doi_settings')
->insert($chunkedInserts);
}
foreach ($doiStatusUpdates as $doiId => $doiStatusUpdate) {
DB::table('dois')
->where('doi_id', '=', $doiId)
->update($doiStatusUpdate);
}
// 5. Clean up old settings
DB::table('submission_settings')
->whereIn('setting_name', ['crossref::registeredDoi', 'crossref::status', 'crossref::batchId'])
->delete();
// ===== Issue Statuses & Settings ===== //
// 1. Get issues with Crossref-related info
$issueData = DB::table('issues', 'i')
->leftJoin('issue_settings as iss', 'i.issue_id', '=', 'iss.issue_id')
->whereIn('iss.setting_name', ['crossref::registeredDoi', 'crossref::status', 'crossref::batchId', 'crossref::failedMsg'])
->select(['i.issue_id', 'i.doi_id', 'iss.setting_name', 'iss.setting_value'])
->get()
->reduce(function ($carry, $item) {
if (!isset($carry[$item->issue_id])) {
$carry[$item->issue_id] = [
'doi_id' => $item->doi_id
];
}
$carry[$item->issue_id][$item->setting_name] = $item->setting_value;
return $carry;
}, []);
// 2. Map settings/status insert statements
$inserts = [];
$statuses = [];
foreach ($issueData as $item) {
// Settings
if (isset($item['crossref::batchId'])) {
$inserts[] = [
'doi_id' => $item['doi_id'],
'setting_name' => 'crossrefplugin_batchId',
'setting_value' => $item['crossref::batchId'],
];
}
if (isset($item['crossref::failedMsg'])) {
$inserts[] = [
'doi_id' => $item['doi_id'],
'setting_name' => 'crossrefplugin_failedMsg',
'setting_value' => $item['crossref::failedMsg'],
];
}
// Status
if (isset($item['crossref::status'])) {
$status = Doi::STATUS_ERROR;
$registrationAgency = null;
if (in_array($item['crossref::status'], ['found', 'registered', 'markedRegistered'])) {
$status = Doi::STATUS_REGISTERED;
if ($item['crossref::status'] === 'registered') {
$registrationAgency = 'CrossrefExportPlugin';
}
} elseif (isset($item['crossref::registeredDoi'])) {
$status = Doi::STATUS_REGISTERED;
}
$statuses[$item['doi_id']] = ['status' => $status];
if ($registrationAgency !== null) {
$inserts[] = [
'doi_id' => $item['doi_id'],
'setting_name' => 'registrationAgency',
'setting_value' => $registrationAgency
];
}
}
}
// 3. Insert updated settings/statuses
foreach (array_chunk($inserts, 100) as $chunkedInserts) {
DB::table('doi_settings')
->insert($chunkedInserts);
}
foreach ($statuses as $doiId => $insert) {
DB::table('dois')
->where('doi_id', '=', $doiId)
->update($insert);
}
// 4. Clean up old settings
DB::table('issue_settings')
->whereIn('setting_name', ['crossref::registeredDoi', 'crossref::status', 'crossref::batchId', 'crossref::failedMsg'])
->delete();
// ===== General cleanup ===== //
// If any Crossref settings are configured, assume plugin is in use and enable
$contextsWithPluginEnabled = DB::table('journals')
->whereIn('journal_id', function (Builder $q) {
$q->select('context_id')
->from('plugin_settings')
->where('plugin_name', '=', 'crossrefexportplugin');
})
->select(['journal_id'])
->get();
$contextsWithPluginEnabled->each(function ($item) {
DB::table('plugin_settings')
->insert(
[
'plugin_name' => 'crossrefplugin',
'context_id' => $item->journal_id,
'setting_name' => 'enabled',
'setting_value' => 1,
'setting_type' => 'bool'
]
);
});
// Enable automatic DOI deposit if configured
$contextsWithAutomaticDeposit = DB::table('journals')
->whereIn('journal_id', function (Builder $q) {
$q->select(['context_id'])
->from('plugin_settings')
->where('plugin_name', '=', 'crossrefexportplugin')
->where('setting_name', '=', 'automaticRegistration')
->where('setting_value', '=', 1) ;
})
->select(['journal_id'])
->get();
$contextsWithAutomaticDeposit->each(function ($item) {
DB::table('journal_settings')
->upsert(
[
'journal_id' => $item->journal_id,
'setting_name' => 'automaticDoiDeposit',
'setting_value' => 1
],
['journal_id', 'locale', 'setting_name'],
['setting_value']
);
});
DB::table('plugin_settings')
->where('plugin_name', '=', 'crossrefexportplugin')
->where('setting_name', '=', 'automaticRegistration')
->delete();
// Update no-longer-in-use version for importExport plugin
DB::table('versions')
->where('product_type', '=', 'plugins.importexport')
->where('product', '=', 'crossref')
->delete();
// Remove scheduled task
DB::table('scheduled_tasks')
->where('class_name', '=', 'plugins.importexport.crossref.CrossrefInfoSender')
->delete();
}
/**
* Transfer over settings, filter info for importExport to generic plugin transfer
*/
private function _migrateDataciteSettingsToContext()
{
// ===== Filters ===== //
DB::table('filters')
->where('class_name', '=', 'plugins.importexport.datacite.filter.DataciteXmlFilter')
->update(['class_name' => 'plugins.generic.datacite.filter.DataciteXmlFilter']);
// ===== Issues Statuses & Settings ===== //
// 1. Get issues with Datacite-related info
$issueData = DB::table('issues', 'i')
->leftJoin('issue_settings as iss', 'i.issue_id', '=', 'iss.issue_id')
->whereIn('iss.setting_name', ['datacite::registeredDoi', 'datacite::status'])
->select(['i.issue_id', 'i.doi_id', 'iss.setting_name', 'iss.setting_value'])
->get()
->reduce(function ($carry, $item) {
if (!isset($carry[$item->issue_id])) {
$carry[$item->issue_id] = [
'doi_id' => $item->doi_id
];
}
$carry[$item->issue_id][$item->setting_name] = $item->setting_value;
return $carry;
}, []);
// 2. Map statuses insert statements
$statuses = [];
$registrationAgencies = [];
foreach ($issueData as $item) {
// Status
if (isset($item['datacite::status'])) {
$status = Doi::STATUS_ERROR;
$registrationAgency = null;
if (in_array($item['datacite::status'], ['found', 'registered', 'markedRegistered'])) {
if ($item['datacite::status'] === 'registered') {
$registrationAgency = 'DataciteExportPlugin';
}
$status = Doi::STATUS_REGISTERED;
} elseif (isset($item['datacite::registeredDoi'])) {
$status = Doi::STATUS_REGISTERED;
}
$statuses[$item['doi_id']] = ['status' => $status];
$registrationAgencies[$item['doi_id']] = $registrationAgency;
}
}
// 3. Insert updated statuses
foreach ($statuses as $doiId => $insert) {
DB::table('dois')
->where('doi_id', '=', $doiId)
->update($insert);
}
foreach ($registrationAgencies as $doiId => $agency) {
if ($agency === null) {
continue;
}
DB::table('doi_settings')
->insert([
'doi_id' => $doiId,
'setting_name' => 'registrationAgency',
'setting_value' => $agency
]);
}
// 4. Clean up old settings
DB::table('issue_settings')
->whereIn('setting_name', ['datacite::registeredDoi', 'datacite::status'])
->delete();
// ===== Publications Statuses & Settings ===== //
// 1. Get publications with Datacite-related info
$publicationData = DB::table('submissions', 's')
->leftJoin('submission_settings as ss', 's.submission_id', '=', 'ss.submission_id')
->leftJoin('publications as p', 's.current_publication_id', '=', 'p.publication_id')
->whereIn('ss.setting_name', ['datacite::registeredDoi', 'datacite::status'])
->select(['p.publication_id', 'p.doi_id', 'ss.setting_name', 'ss.setting_value'])
->get()
->reduce(function ($carry, $item) {
if (!isset($carry[$item->publication_id])) {
$carry[$item->publication_id] = [
'doi_id' => $item->doi_id
];
}
$carry[$item->publication_id][$item->setting_name] = $item->setting_value;
return $carry;
}, []);
// 2. Map statuses insert statements
$statuses = [];
$registrationAgencies = [];
foreach ($publicationData as $item) {
// Status
if (isset($item['datacite::status'])) {
$status = Doi::STATUS_ERROR;
$registrationAgency = null;
if (in_array($item['datacite::status'], ['found', 'registered', 'markedRegistered'])) {
if ($item['datacite::status'] === 'registered') {
$registrationAgency = 'DataciteExportPlugin';
}
$status = Doi::STATUS_REGISTERED;
} elseif (isset($item['datacite::registeredDoi'])) {
$status = Doi::STATUS_REGISTERED;
}
$statuses[$item['doi_id']] = ['status' => $status];
$registrationAgencies[$item['doi_id']] = $registrationAgency;
}
}
// 3. Insert updated statuses
foreach ($statuses as $doiId => $insert) {
DB::table('dois')
->where('doi_id', '=', $doiId)
->update($insert);
}
foreach ($registrationAgencies as $doiId => $agency) {
if ($agency === null) {
continue;
}
DB::table('doi_settings')
->insert([
'doi_id' => $doiId,
'setting_name' => 'registrationAgency',
'setting_value' => $agency
]);
}
// 4. Clean up old settings
DB::table('publication_settings')
->whereIn('setting_name', ['datacite::registeredDoi', 'datacite::status'])
->delete();
// ===== Galleys Statuses & Settings ===== //
// 1. Get galleys with Datacite-related info
$galleyData = DB::table('publication_galleys', 'pg')
->leftJoin('publication_galley_settings as pgs', 'pg.galley_id', '=', 'pgs.galley_id')
->whereIn('pgs.setting_name', ['datacite::registeredDoi', 'datacite::status'])
->select(['pg.galley_id', 'pg.doi_id', 'pgs.setting_name', 'pgs.setting_value'])
->get()
->reduce(function ($carry, $item) {
if (!isset($carry[$item->galley_id])) {
$carry[$item->galley_id] = [
'doi_id' => $item->doi_id
];
}
$carry[$item->galley_id][$item->setting_name] = $item->setting_value;
return $carry;
}, []);
// 2. Map statuses insert statements
$statuses = [];
$registrationAgencies = [];
foreach ($galleyData as $item) {
// Status
if (isset($item['datacite::status'])) {
$status = Doi::STATUS_ERROR;
$registrationAgency = null;
if (in_array($item['datacite::status'], ['found', 'registered', 'markedRegistered'])) {
if ($item['datacite::status'] === 'registered') {
$registrationAgency = 'DataciteExportPlugin';
}
$status = Doi::STATUS_REGISTERED;
} elseif (isset($item['datacite::registeredDoi'])) {
$status = Doi::STATUS_REGISTERED;
}
$statuses[$item['doi_id']] = ['status' => $status];
$registrationAgencies[$item['doi_id']] = $registrationAgency;
}
}
// 3. Insert updated statuses
foreach ($statuses as $doiId => $insert) {
DB::table('dois')
->where('doi_id', '=', $doiId)
->update($insert);
}
foreach ($registrationAgencies as $doiId => $agency) {
if ($agency === null) {
continue;
}
DB::table('doi_settings')
->insert([
'doi_id' => $doiId,
'setting_name' => 'registrationAgency',
'setting_value' => $agency
]);
}
// 4. Clean up old settings
DB::table('publication_galley_settings')
->whereIn('setting_name', ['datacite::registeredDoi', 'datacite::status'])
->delete();
// ===== General cleanup ===== //
// If any Datacite settings are configured, assume plugin is in use and enable
$contextsWithPluginEnabled = DB::table('journals')
->whereIn('journal_id', function (Builder $q) {
$q->select('context_id')
->from('plugin_settings')
->where('plugin_name', '=', 'dataciteexportplugin');
})
->select(['journal_id'])
->get();
$contextsWithPluginEnabled->each(function ($item) {
DB::table('plugin_settings')
->insert(
[
'plugin_name' => 'dataciteplugin',
'context_id' => $item->journal_id,
'setting_name' => 'enabled',
'setting_value' => 1,
'setting_type' => 'bool'
]
);
});
// Enable automatic DOI deposit if configured
$contextsWithAutomaticDeposit = DB::table('journals')
->whereIn('journal_id', function (Builder $q) {
$q->select(['context_id'])
->from('plugin_settings')
->where('plugin_name', '=', 'dataciteexportplugin')
->where('setting_name', '=', 'automaticRegistration')
->where('setting_value', '=', 1) ;
})
->select(['journal_id'])
->get();
$contextsWithAutomaticDeposit->each(function ($item) {
DB::table('journal_settings')
->upsert(
[
'journal_id' => $item->journal_id,
'setting_name' => 'automaticDoiDeposit',
'setting_value' => 1
],
['journal_id', 'locale', 'setting_name'],
['setting_value']
);
});
DB::table('plugin_settings')
->where('plugin_name', '=', 'dataciteexportplugin')
->where('setting_name', '=', 'automaticRegistration')
->delete();
// Update no-longer-in-use version for importExport plugin
DB::table('versions')
->where('product_type', '=', 'plugins.importexport')
->where('product', '=', 'datacite')
->delete();
// Remove scheduled task
DB::table('scheduled_tasks')
->where('class_name', '=', 'plugins.importexport.datacite.DataciteInfoSender')
->delete();
}
/**
* Move galley DOIs from publication_galley_settings table to DOI objects
*/
private function _migrateGalleyDoisUp(): void
{
$q = DB::table('submissions', 's')
->select(['s.context_id', 'pg.galley_id', 'pg.doi_id', 'pgss.setting_name', 'pgss.setting_value'])
->leftJoin('publications as p', 'p.submission_id', '=', 's.submission_id')
->leftJoin('publication_galleys as pg', 'pg.publication_id', '=', 'p.publication_id')
->leftJoin('publication_galley_settings as pgss', 'pgss.galley_id', '=', 'pg.galley_id')
->where('pgss.setting_name', '=', 'pub-id::doi');
$q->chunkById(1000, function ($items) {
foreach ($items as $item) {
// Double-check to ensure a DOI object does not already exist for galley
if ($item->doi_id === null) {
$doiId = $this->_addDoi($item->context_id, $item->setting_value);
// Add association to newly created DOI to galley
DB::table('publication_galleys')
->where('galley_id', '=', $item->galley_id)
->update(['doi_id' => $doiId]);
} else {
// Otherwise update existing DOI object
$this->_updateDoi($item->doi_id, $item->context_id, $item->setting_value);
}
}
}, 'pg.galley_id', 'galley_id');
}
/**
* Move issue DOIs from issue_settings table to DOI objects
*/
private function _migrateIssueDoisUp(): void
{
$q = DB::table('issues', 'i')
->select(['i.issue_id', 'i.journal_id', 'i.doi_id', 'iss.setting_name', 'iss.setting_value'])
->leftJoin('issue_settings as iss', 'iss.issue_id', '=', 'i.issue_id')
->where('iss.setting_name', '=', 'pub-id::doi');
$q->chunkById(1000, function ($items) {
foreach ($items as $item) {
// Double-check to ensure a DOI object does not already exist for issue
if ($item->doi_id === null) {
$doiId = $this->_addDoi($item->journal_id, $item->setting_value);
// Add association to newly created DOI to issue
DB::table('issues')
->where('issue_id', '=', $item->issue_id)
->update(['doi_id' => $doiId]);
} else {
// Otherwise update existing DOI object
$this->_updateDoi($item->doi_id, $item->journal_id, $item->setting_value);
}
}
}, 'i.issue_id', 'issue_id');
}
/**
* Gets app-specific context table name, e.g. journals
*
*/
protected function getContextTable(): string
{
return 'journals';
}
/**
* Gets app-specific context_id column, e.g. journal_id
*/
protected function getContextIdColumn(): string
{
return 'journal_id';
}
/**
* Gets app-specific context settings table, e.g. journal_settings
*/
protected function getContextSettingsTable(): string
{
return 'journal_settings';
}
/**
* Adds app-specific suffix patterns to data collector stdClass
*/
protected function addSuffixPatternsData(\stdClass $data): \stdClass
{
$data->doiIssueSuffixPattern = [];
return $data;
}
/**
* Add suffix pattern settings from DB into reducer's data
*/
protected function insertSuffixPatternsData(\stdClass $carry, \stdClass $item): \stdClass
{
switch ($item->setting_name) {
case 'doiIssueSuffixPattern':
$carry->doiIssueSuffixPattern[] = [
$this->getContextIdColumn() => $item->context_id,
'setting_name' => $item->setting_name,
'setting_value' => $item->setting_value,
];
return $carry;
default:
return $carry;
}
}
/**
* Add insert-ready statements for all applicable suffix pattern items
*/
protected function prepareSuffixPatternsForInsert(\stdClass $processedData, array $insertData): array
{
foreach ($processedData->doiIssueSuffixPattern as $item) {
$insertData[] = $item;
}
return $insertData;
}
/**
* Add app-specific enabled DOI types for insert into DB
*/
protected function insertEnabledDoiTypes(\stdClass $carry, \stdClass $item): \stdClass
{
if ($item->setting_name === 'enableIssueDoi') {
if (!isset($carry->enabledDoiTypes[$item->context_id])) {
$carry->enabledDoiTypes[$item->context_id] = [
$this->getContextIdColumn() => $item->context_id,
'setting_name' => 'enabledDoiTypes',
'setting_value' => [],
];
}
if ($item->setting_value === '1') {
$carry->enabledDoiTypes[$item->context_id]['setting_value'][] = 'issue';
}
}
return $carry;
}
/**
* Get an array with the keys for each suffix pattern type
*/
protected function getSuffixPatternNames(): array
{
return ['doiPublicationSuffixPattern', 'doiRepresentationSuffixPattern', 'doiIssueSuffixPattern'];
}
/**
* Returns the default pattern for the given suffix pattern type
*/
protected function getSuffixPatternValue(string $suffixPatternName): string
{
$pattern = '';
switch ($suffixPatternName) {
case 'doiPublicationSuffixPattern':
$pattern = '%j.v%vi%i.%a';
break;
case 'doiRepresentationSuffixPattern':
$pattern = '%j.v%vi%i.%a.g%g';
break;
case 'doiIssueSuffixPattern':
$pattern = '%j.v%vi%i';
break;
}
return $pattern;
}
}
if (!PKP_STRICT_MODE) {
class_alias('\APP\migration\upgrade\v3_4_0\I7014_DoiMigration', '\I7014_DoiMigration');
}
@@ -0,0 +1,44 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7128_SectionEntityDAORefactor.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7128_SectionEntityDAORefactor
*
* @brief Remove deprecated setting_type requirement after converting the section DAO to use new repository pattern
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use PKP\install\DowngradeNotSupportedException;
class I7128_SectionEntityDAORefactor extends \PKP\migration\Migration
{
/**
* Run the migration.
*/
public function up(): void
{
if (Schema::hasColumn('section_settings', 'setting_type')) {
Schema::table('section_settings', function (Blueprint $table) {
$table->dropColumn('setting_type');
});
}
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,110 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7129_IssueEntityDAORefactor.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7129_IssueEntityDAORefactor
*
* @brief Convert issue DAO to use new repository pattern
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class I7129_IssueEntityDAORefactor extends \PKP\migration\Migration
{
/**
* Run the migration.
*/
public function up(): void
{
// Remove deprecated setting_type requirement
Schema::table('issue_settings', function (Blueprint $table) {
$table->string('setting_type', 6)->nullable()->change();
});
// Move current issue status from Issue to Journal
Schema::table('journals', function (Blueprint $table) {
$table->bigInteger('current_issue_id')->nullable()->default(null);
$table->foreign('current_issue_id')->references('issue_id')->on('issues')->onDelete('set null');
$table->index(['current_issue_id'], 'journals_current_issue_id');
});
$this->transferCurrentStatusToJournal();
Schema::table('issues', function (Blueprint $table) {
$table->dropColumn('current');
});
}
/**
* Reverse the downgrades
*/
public function down(): void
{
// Restore deprecated setting_type requirement
Schema::table('issue_settings', function (Blueprint $table) {
$table->string('setting_type', 6)->change();
});
// Move current issue status back to Issue from Journal
Schema::table('issues', function (Blueprint $table) {
$table->smallInteger('current')->default(0);
});
$this->transferCurrentStatusToIssue();
Schema::table('journals', function (Blueprint $table) {
$table->dropForeign('journals_current_issue_id_foreign');
$table->dropColumn('current_issue_id');
});
}
/**
* Transfers current issue status from Issue to Journal for each journal
*/
private function transferCurrentStatusToJournal()
{
$contexts = DB::table('journals', 'j')
->select('j.journal_id')
->get();
foreach ($contexts as $context) {
$currentIssue = DB::table('issues', 'i')
->select('i.issue_id')
->where('i.journal_id', '=', $context->journal_id)
->where('i.current', '=', 1)
->get()
->first();
if ($currentIssue !== null) {
DB::table('journals', 'j')
->where('j.journal_id', '=', $context->journal_id)
->update(['current_issue_id' => $currentIssue->issue_id]);
}
}
}
// Transfer current issue status from Journal to Issue for each journal
private function transferCurrentStatusToIssue()
{
$contexts = DB::table('journals', 'j')
->select('j.current_issue_id')
->get();
foreach ($contexts as $context) {
if ($context->current_issue_id != null) {
DB::table('issues', 'i')
->where('i.issue_id', '=', $context->current_issue_id)
->update(['i.current' => 1]);
}
}
}
}
if (!PKP_STRICT_MODE) {
class_alias('\APP\migration\upgrade\v3_4_0\I7129_IssueEntityDAORefactor', '\I7129_IssueEntityDAORefactor');
}
@@ -0,0 +1,71 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7186_OpenAccessNotification.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7186_OpenAccessNotification
*
* @brief Migrate the user's open access subscription setting from OJS 2.4.8 to
* the notification subscriptions table.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use PKP\install\DowngradeNotSupportedException;
use PKP\migration\Migration;
class I7186_OpenAccessNotification extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
$userIds = DB::table('users')
->whereNotIn('user_id', function (Builder $query) {
$query->select('user_id')
->from('user_settings')
->where('setting_name', 'openAccessNotification')
->where('setting_value', '1');
})
->pluck('user_id');
$contextIds = DB::table('journals')->pluck('journal_id');
$rows = [];
foreach ($userIds as $userId) {
foreach ($contextIds as $contextId) {
$rows[] = [
'setting_name' => 'blocked_emailed_notification',
'setting_value' => 50331659, // Notification::NOTIFICATION_TYPE_OPEN_ACCESS
'user_id' => $userId,
'context' => $contextId,
'setting_type' => 'int',
];
}
}
DB::table('notification_subscription_settings')->insert($rows);
DB::table('user_settings')
->where('setting_name', 'openAccessNotification')
->where('setting_value', '1')
->delete();
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,59 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7190_RemoveOrphanFilters.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7190_RemoveOrphanFilters
*
* @brief Remove old filters which have been left behind
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use PKP\install\DowngradeNotSupportedException;
class I7190_RemoveOrphanFilters extends \PKP\migration\Migration
{
/**
* @inheritDoc
*/
public function up(): void
{
DB::table('filter_groups')
->whereNotIn(
'symbolic',
[
'mods34=>mods34-xml',
'SubmissionArtworkFile=>native-xml',
'SupplementaryFile=>native-xml',
'native-xml=>SubmissionArtworkFile',
'native-xml=>SupplementaryFile'
]
)
->delete();
DB::table('filters')
->whereNotExists(
fn (Builder $query) => $query
->from('filter_groups', 'fg')
->whereColumn('fg.filter_group_id', '=', 'filters.filter_group_id')
)
->delete();
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,22 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7191_EditorAssignments.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7191_EditorAssignments
*
* @brief Update the subeditor_submission_group table to accomodate new editor assignment settings
*/
namespace APP\migration\upgrade\v3_4_0;
class I7191_EditorAssignments extends \PKP\migration\upgrade\v3_4_0\I7191_EditorAssignments
{
protected string $sectionDb = 'sections';
protected string $sectionIdColumn = 'section_id';
protected string $contextColumn = 'journal_id';
}
@@ -0,0 +1,22 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7191_InstallSubmissionHelpDefaults.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7191_InstallSubmissionHelpDefaults
*
* @brief Migrate the submissionChecklist setting from an array to a HTML string
*/
namespace APP\migration\upgrade\v3_4_0;
class I7191_InstallSubmissionHelpDefaults extends \PKP\migration\upgrade\v3_4_0\I7191_InstallSubmissionHelpDefaults
{
protected string $CONTEXT_TABLE = 'journals';
protected string $CONTEXT_SETTINGS_TABLE = 'journal_settings';
protected string $CONTEXT_COLUMN = 'journal_id';
}
@@ -0,0 +1,21 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7191_SubmissionChecklistMigration.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7191_SubmissionChecklistMigration
*
* @brief Migrate the submissionChecklist setting from an array to a HTML string
*/
namespace APP\migration\upgrade\v3_4_0;
class I7191_SubmissionChecklistMigration extends \PKP\migration\upgrade\v3_4_0\I7191_SubmissionChecklistMigration
{
protected string $CONTEXT_SETTINGS_TABLE = 'journal_settings';
protected string $CONTEXT_COLUMN = 'journal_id';
}
@@ -0,0 +1,36 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7264_UpdateEmailTemplates.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7264_UpdateEmailTemplates
*
* @brief Describe upgrade/downgrade operations for DB table email_templates.
*/
namespace APP\migration\upgrade\v3_4_0;
class I7264_UpdateEmailTemplates extends \PKP\migration\upgrade\v3_4_0\I7264_UpdateEmailTemplates
{
protected function oldNewVariablesMap(): array
{
$oldNewVariablesMap = parent::oldNewVariablesMap();
array_walk_recursive($oldNewVariablesMap, function (&$newVariable, $oldVariable) {
if ($newVariable === 'contextName') {
$newVariable = 'journalName';
} elseif ($newVariable === 'contextUrl') {
$newVariable = 'journalUrl';
} elseif ($newVariable === 'contextSignature') {
$newVariable = 'journalSignature';
} elseif ($newVariable === 'contextAcronym') {
$newVariable = 'journalAcronym';
}
});
return $oldNewVariablesMap;
}
}
@@ -0,0 +1,108 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7265_EditorialDecisions.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2000-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7265_EditorialDecisions
*
* @brief Database migrations for editorial decision refactor.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
class I7265_EditorialDecisions extends \PKP\migration\upgrade\v3_4_0\I7265_EditorialDecisions
{
/**
* Run the migrations.
*/
public function up(): void
{
parent::up();
$this->upNewDecisions();
}
/**
* Reverse the migrations.
*/
public function down(): void
{
parent::down();
$this->downNewDecisions();
}
/**
* Change decisions taken in submission stage
*
* APP\decision\Decision::ACCEPT = 1
* APP\decision\Decision::REVERT_DECLINE = 17
*
* When these decisions have been recorded in
* the submission stage they must become become:
*
* APP\decision\Decision::SKIP_EXTERNAL_REVIEW = 19
* APP\decision\Decision::REVERT_INITIAL_DECLINE = 18
*
* In 3.3 and earlier, the decision constants were global
* and named:
*
* Decision::ACCEPT
* Decision::REVERT_DECLINE
*/
public function upNewDecisions()
{
DB::table('edit_decisions')
->where('stage_id', '=', 1) // WORKFLOW_STAGE_ID_SUBMISSION
->where('decision', '=', 1) // APP\decision\Decision::ACCEPT
->update([
'decision' => 19, // APP\decision\Decision::SKIP_EXTERNAL_REVIEW
]);
DB::table('edit_decisions')
->where('stage_id', '=', 1) // WORKFLOW_STAGE_ID_SUBMISSION
->where('decision', '=', 17) // APP\decision\Decision::REVERT_DECLINE
->update([
'decision' => 18, // APP\decision\Decision::REVERT_INITIAL_DECLINE
]);
}
/**
* Reverse the decision type changes
*
* @see self::upNewSubmissionDecisions()
*/
public function downNewDecisions()
{
DB::table('edit_decisions')
->where('stage_id', '=', 1) // WORKFLOW_STAGE_ID_SUBMISSION
->where('decision', '=', 19) // APP\decision\Decision::ACCEPT
->update([
'decision' => 1, // APP\decision\Decision::ACCEPT
]);
DB::table('edit_decisions')
->where('stage_id', '=', 1) // WORKFLOW_STAGE_ID_SUBMISSION
->where('decision', '=', 18) // APP\decision\Decision::REVERT_INITIAL_DECLINE
->update([
'decision' => 17, // APP\decision\Decision::REVERT_DECLINE
]);
}
protected function getContextTable(): string
{
return 'journals';
}
protected function getContextSettingsTable(): string
{
return 'journal_settings';
}
protected function getContextIdColumn(): string
{
return 'journal_id';
}
}
@@ -0,0 +1,42 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7470_FixDeprecatedFileStage.php
*
* Copyright (c) 2023 Simon Fraser University
* Copyright (c) 2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7470_FixDeprecatedFileStage.php
*
* @brief Redirect deprecated file stages that remained after the OJS 2 > 3 migration.
*
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
use PKP\install\DowngradeNotSupportedException;
use PKP\migration\Migration;
class I7470_FixDeprecatedFileStage extends Migration
{
public function up(): void
{
DB::table('submission_files')
// From SUBMISSION_FILE_FAIR_COPY
->where('file_stage', 7)
// To \PKP\submissionFile::SUBMISSION_FILE_FINAL
->update(['file_stage' => 6]);
DB::table('submission_files')
// From SUBMISSION_FILE_EDITOR
->where('file_stage', 8)
// To \PKP\submissionFile::SUBMISSION_FILE_COPYEDIT
->update(['file_stage' => 9]);
}
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,50 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7513_DoiSettings.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7513_DoiSettings
*
* @brief Database migrations for DOI settings refactor.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
class I7513_DoiSettings extends \PKP\migration\Migration
{
/**
* @inheritDoc
*/
public function up(): void
{
DB::table('plugin_settings')
->where('plugin_name', '=', 'crossrefexportplugin')
->update(['plugin_name' => 'crossrefplugin']);
DB::table('plugin_settings')
->where('plugin_name', '=', 'dataciteexportplugin')
->update(['plugin_name' => 'dataciteplugin']);
}
/**
* @inheritDoc
*/
public function down(): void
{
DB::table('plugin_settings')
->where('plugin_name', '=', 'crossrefplugin')
->whereNot('setting_name', '=', 'enabled')
->update(['plugin_name' => 'crossrefexportplugin']);
DB::table('plugin_settings')
->where('plugin_name', '=', 'dataciteplugin')
->whereNot('setting_name', '=', 'enabled')
->update(['plugin_name' => 'dataciteexportplugin']);
}
}
@@ -0,0 +1,41 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7520_IssueGalleyLabelLength.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7520_IssueGalleyLabelLength
*
* @brief This migration increases the length of the issue galley label column in the database
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class I7520_IssueGalleyLabelLength extends \PKP\migration\Migration
{
/**
* Run the migration.
*/
public function up(): void
{
Schema::table('issue_galleys', function (Blueprint $table) {
$table->string('label', 255)->nullable()->change();
});
}
/**
* Reverse the downgrades
*/
public function down(): void
{
Schema::table('issue_galleys', function (Blueprint $table) {
$table->string('label', 32)->nullable()->change();
});
}
}
@@ -0,0 +1,48 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7596_RemoveNonExpiring.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7596_RemoveNonExpiring
*
* @brief Remove the subscription non_expiring column if it exists.
* By OJS 3.3.0-x the non-expiring state of the subscription was determined by
* the NULL status of the duration column, but not all code had been updated to
* reflect this. Issue 7596 converts the outstanding cases to check the
* duration column instead, and this migration removes the superfluous column.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class I7596_RemoveNonExpiring extends \PKP\migration\Migration
{
/**
* Run the migration.
*/
public function up(): void
{
// Installations that began with OJS 3.3.0 will not have this column.
// Older installations will.
if (Schema::hasColumn('subscription_types', 'non_expiring')) {
Schema::table('subscription_types', function (Blueprint $table) {
$table->dropColumn('non_expiring');
});
}
}
/**
* Reverse the downgrades
*/
public function down(): void
{
// Regardless of whether the column existed before the migration was
// executed, OJS 3.3.x did not use it and it should not be re-added.
}
}
@@ -0,0 +1,148 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7725_DecisionConstantsUpdate.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2000-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7725_DecisionConstantsUpdate
*
* @brief Editorial decision constant sync up across all application
*
* @see https://github.com/pkp/pkp-lib/issues/7725
*/
namespace APP\migration\upgrade\v3_4_0;
class I7725_DecisionConstantsUpdate extends \PKP\migration\upgrade\v3_4_0\I7725_DecisionConstantsUpdate
{
/**
* Get the decisions constants mappings
*
*/
public function getDecisionMappings(): array
{
return [
// \PKP\decision\Decision::ACCEPT
[
'stage_id' => [WORKFLOW_STAGE_ID_EDITING],
'current_value' => 1,
'updated_value' => 2,
],
// \PKP\decision\Decision::EXTERNAL_REVIEW
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 8,
'updated_value' => 3,
],
// \PKP\decision\Decision::PENDING_REVISIONS
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 2,
'updated_value' => 4,
],
// \PKP\decision\Decision::RESUBMIT
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 3,
'updated_value' => 5,
],
// \PKP\decision\Decision::DECLINE
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 4,
'updated_value' => 6,
],
// \PKP\decision\Decision::INITIAL_DECLINE
[
'stage_id' => [WORKFLOW_STAGE_ID_SUBMISSION],
'current_value' => 9,
'updated_value' => 8,
],
// \PKP\decision\Decision::RECOMMEND_ACCEPT
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 11,
'updated_value' => 9,
],
// \PKP\decision\Decision::RECOMMEND_PENDING_REVISIONS
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 12,
'updated_value' => 10,
],
// \PKP\decision\Decision::RECOMMEND_RESUBMIT
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 13,
'updated_value' => 11,
],
// \PKP\decision\Decision::RECOMMEND_DECLINE
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 14,
'updated_value' => 12,
],
// \PKP\decision\Decision::NEW_EXTERNAL_ROUND
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 16,
'updated_value' => 14,
],
// \PKP\decision\Decision::REVERT_DECLINE
[
'stage_id' => [WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 17,
'updated_value' => 15,
],
// \PKP\decision\Decision::REVERT_INITIAL_DECLINE
[
'stage_id' => [WORKFLOW_STAGE_ID_SUBMISSION],
'current_value' => 18,
'updated_value' => 16
],
// \PKP\decision\Decision::SKIP_EXTERNAL_REVIEW
[
'stage_id' => [WORKFLOW_STAGE_ID_EDITING],
'current_value' => 19,
'updated_value' => 17,
],
// \PKP\decision\Decision::BACK_FROM_PRODUCTION
[
'stage_id' => [WORKFLOW_STAGE_ID_EDITING],
'current_value' => 31,
'updated_value' => 29,
],
// \PKP\decision\Decision::BACK_FROM_COPYEDITING
[
'stage_id' => [WORKFLOW_STAGE_ID_SUBMISSION, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 32,
'updated_value' => 30,
],
// \PKP\decision\Decision::CANCEL_REVIEW_ROUND
[
'stage_id' => [WORKFLOW_STAGE_ID_SUBMISSION, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW],
'current_value' => 33,
'updated_value' => 31,
],
];
}
}
@@ -0,0 +1,41 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7796_UpdateCrossrefSchema.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7796_UpdateCrossrefSchema
*
* @brief Upgrade Crossref schema in filter_groups.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
use PKP\install\DowngradeNotSupportedException;
class I7796_UpdateCrossrefSchema extends \PKP\migration\Migration
{
/**
* Run the migration.
*/
public function up(): void
{
DB::table('filter_groups')
->where('output_type', 'xml::schema(https://www.crossref.org/schemas/crossref4.3.6.xsd)')
->update(['output_type' => 'xml::schema(https://www.crossref.org/schemas/crossref5.3.1.xsd)']);
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,59 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I7901_Duplicate_OAI_IDs.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I7901_Duplicate_OAI_IDs
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
class I7901_Duplicate_OAI_IDs extends \PKP\migration\Migration
{
/**
* Run the migration.
*/
public function up(): void
{
switch (DB::getDriverName()) {
case 'mysql':
DB::unprepared(
"DELETE dot
FROM data_object_tombstones dot
JOIN submissions s ON (dot.data_object_id = s.submission_id)
JOIN journals j ON (j.journal_id = s.context_id)
JOIN publications p ON (s.current_publication_id = p.publication_id)
JOIN publication_settings psissue ON (psissue.publication_id = p.publication_id AND psissue.setting_name='issueId' AND psissue.locale='')
JOIN issues i ON (CAST(i.issue_id AS CHAR(20)) = psissue.setting_value)
WHERE i.published = 1 AND j.enabled = 1 AND p.status = 3"
);
break;
case 'pgsql':
DB::unprepared(
"DELETE FROM data_object_tombstones dot
USING submissions s, journals j, publications p, publication_settings psissue, issues i
WHERE dot.data_object_id = s.submission_id
AND j.journal_id = s.context_id
AND s.current_publication_id = p.publication_id
AND psissue.publication_id = p.publication_id
AND psissue.setting_name='issueId' AND psissue.locale='' AND (CAST(i.issue_id AS CHAR(20)) = psissue.setting_value)
AND i.published = 1 AND j.enabled = 1 AND p.status = 3"
);
break;
}
}
/**
* Reverse the migration
*/
public function down(): void
{
// The migration deletes bad data, which is not recovered on downgrade.
}
}
@@ -0,0 +1,52 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I8027_DoiVersioning.php
*
* Copyright (c) 2022 Simon Fraser University
* Copyright (c) 2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I8027_DoiVersioning
*
* @brief Add new DOI versioning context setting
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Facades\DB;
class I8027_DoiVersioning extends \PKP\migration\Migration
{
/**
* @inheritDoc
*/
public function up(): void
{
$journalIds = DB::table('journals')
->distinct()
->get(['journal_id']);
$insertStatements = $journalIds->reduce(function ($carry, $item) {
$carry[] = [
'journal_id' => $item->journal_id,
'setting_name' => 'doiVersioning',
'setting_value' => 0
];
return $carry;
}, []);
DB::table('journal_settings')
->insert($insertStatements);
}
/**
* @inheritDoc
*/
public function down(): void
{
DB::table('journal_settings')
->where('setting_name', '=', 'doiVersioning')
->delete();
}
}
@@ -0,0 +1,57 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I8151_ExtendSettingValues.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I8151_ExtendSettingValues
*
* @brief Describe upgrade/downgrade operations for extending TEXT columns to MEDIUMTEXT
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class I8151_ExtendSettingValues extends \PKP\migration\Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('section_settings', function (Blueprint $table) {
$table->mediumText('setting_value')->nullable()->change();
});
Schema::table('issue_settings', function (Blueprint $table) {
$table->mediumText('setting_value')->nullable()->change();
});
Schema::table('issue_galley_settings', function (Blueprint $table) {
$table->mediumText('setting_value')->nullable()->change();
});
Schema::table('publication_galley_settings', function (Blueprint $table) {
$table->mediumText('setting_value')->nullable()->change();
});
Schema::table('subscription_type_settings', function (Blueprint $table) {
$table->mediumText('setting_value')->nullable()->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// This downgrade is intentionally not implemented. Changing MEDIUMTEXT back to TEXT
// may result in data truncation. Having MEDIUMTEXT in place of TEXT in an otherwise
// downgraded database will not have side-effects.
}
}
@@ -0,0 +1,29 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I8933_EventLogLocalized.php
*
* Copyright (c) 2023 Simon Fraser University
* Copyright (c) 2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I8933_EventLogLocalized.php
*
* @brief Extends the event log migration with the correct table names for OJS.
*/
namespace APP\migration\upgrade\v3_4_0;
class I8933_EventLogLocalized extends \PKP\migration\upgrade\v3_4_0\I8933_EventLogLocalized
{
protected function getContextTable(): string
{
return 'journals';
}
protected function getContextIdColumn(): string
{
return 'journal_id';
}
}
@@ -0,0 +1,32 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I8992_FixEmptyUrlPaths.php
*
* Copyright (c) 2014-2023 Simon Fraser University
* Copyright (c) 2000-2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I8992_FixEmptyUrlPaths.php
*
* @brief Standardize the url columns to hold NULL instead of NULL/empty string.
*
*/
namespace APP\migration\upgrade\v3_4_0;
class I8992_FixEmptyUrlPaths extends \PKP\migration\upgrade\v3_4_0\I8992_FixEmptyUrlPaths
{
/**
* @copydoc \PKP\migration\upgrade\v3_4_0\I8992_FixEmptyUrlPaths::getFieldset()
*/
protected function getFieldset(): array
{
return array_merge(parent::getFieldset(), [
['publication_galleys', 'url_path'],
['publication_galleys', 'remote_url'],
['issue_galleys', 'url_path'],
['issues', 'url_path']
]);
}
}
@@ -0,0 +1,26 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I9040_DropSettingType.php
*
* Copyright (c) 2023 Simon Fraser University
* Copyright (c) 2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I9040_DropSettingType
*
* @brief Drop not needed setting_type fields
*/
namespace APP\migration\upgrade\v3_4_0;
class I9040_DropSettingType extends \PKP\migration\upgrade\v3_4_0\I9040_DropSettingType
{
/**
* @copydoc \PKP\migration\upgrade\v3_4_0\I9040_DropSettingType::getEntities()
*/
protected function getEntities(): array
{
return array_merge(parent::getEntities(), ['journal_settings', 'issue_settings']);
}
}
@@ -0,0 +1,86 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/I9231_FixMetricsIndexes.php
*
* Copyright (c) 2023 Simon Fraser University
* Copyright (c) 2023 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class I9231_FixMetricsIndexes
*
* @brief Use smaller data type for load_id, and use city column prefix index for MySQL.
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema as Schema;
use PKP\install\DowngradeNotSupportedException;
use PKP\migration\Migration;
class I9231_FixMetricsIndexes extends Migration
{
/**
* Run the migration.
*/
public function up(): void
{
// Decrease the size of the column load_id to 50 characters
$loadIdTables = [
'metrics_context',
'metrics_submission',
'metrics_issue',
'metrics_counter_submission_daily',
'metrics_counter_submission_institution_daily',
'metrics_submission_geo_daily',
'usage_stats_total_temporary_records',
'usage_stats_unique_item_investigations_temporary_records',
'usage_stats_unique_item_requests_temporary_records',
'usage_stats_institution_temporary_records'
];
foreach ($loadIdTables as $loadIdTable) {
Schema::table($loadIdTable, function (Blueprint $table) {
$table->string('load_id', 50)->change();
});
}
// Drop the too big unique indexes
// msgd_uc_load_context_submission_c_r_c_date and
// msgm_uc_context_submission_c_r_c_month,
// and create new ones using city column prefix for MySQL
Schema::table('metrics_submission_geo_daily', function (Blueprint $table) {
$table->dropUnique('msgd_uc_load_context_submission_c_r_c_date');
switch (DB::getDriverName()) {
case 'mysql':
$table->unique([DB::raw('load_id, context_id, submission_id, country, region, city(80), date')], 'msgd_uc_load_context_submission_c_r_c_date');
break;
case 'pgsql':
$table->unique(['load_id', 'context_id', 'submission_id', 'country', 'region', 'city', 'date'], 'msgd_uc_load_context_submission_c_r_c_date');
break;
}
});
Schema::table('metrics_submission_geo_monthly', function (Blueprint $table) {
$table->dropUnique('msgm_uc_context_submission_c_r_c_month');
switch (DB::getDriverName()) {
case 'mysql':
$table->unique([DB::raw('context_id, submission_id, country, region, city(80), month')], 'msgm_uc_context_submission_c_r_c_month');
break;
case 'pgsql':
$table->unique(['context_id', 'submission_id', 'country', 'region', 'city', 'month'], 'msgm_uc_context_submission_c_r_c_month');
break;
}
});
}
/**
* Reverse the downgrades
*
* @throws DowngradeNotSupportedException
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
}
@@ -0,0 +1,54 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/InstallEmailTemplates.php
*
* Copyright (c) 2014-2022 Simon Fraser University
* Copyright (c) 2000-2022 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class InstallEmailTemplates
*
* @brief Install new email templates for 3.4
*/
namespace APP\migration\upgrade\v3_4_0;
class InstallEmailTemplates extends \PKP\migration\upgrade\v3_4_0\InstallEmailTemplates
{
protected function getEmailTemplateKeys(): array
{
return [
'EDITOR_DECISION_NOTIFY_OTHER_AUTHORS',
'EDITOR_DECISION_NOTIFY_REVIEWERS',
'EDITOR_DECISION_NEW_ROUND',
'EDITOR_DECISION_REVERT_DECLINE',
'EDITOR_DECISION_REVERT_INITIAL_DECLINE',
'EDITOR_DECISION_SKIP_REVIEW',
'EDITORIAL_REMINDER',
'EDITOR_DECISION_BACK_FROM_PRODUCTION',
'EDITOR_DECISION_BACK_FROM_COPYEDITING',
'EDITOR_DECISION_CANCEL_REVIEW_ROUND',
'REVIEW_RESEND_REQUEST',
'DISCUSSION_NOTIFICATION_SUBMISSION',
'DISCUSSION_NOTIFICATION_REVIEW',
'DISCUSSION_NOTIFICATION_COPYEDITING',
'DISCUSSION_NOTIFICATION_PRODUCTION',
'SUBMISSION_SAVED_FOR_LATER',
'SUBMISSION_NEEDS_EDITOR',
'PAYMENT_REQUEST_NOTIFICATION',
'VERSION_CREATED',
'REVIEW_COMPLETE',
'REVIEW_EDIT',
];
}
protected function getAppVariableNames(): array
{
return [
'contextName' => 'journalName',
'contextUrl' => 'journalUrl',
'contextSignature' => 'journalSignature',
];
}
}
@@ -0,0 +1,71 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/MergeLocalesMigration.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class MergeLocalesMigration
*
* @brief Change Locales from locale_countryCode localization folder notation to locale localization folder notation
*/
namespace APP\migration\upgrade\v3_4_0;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use PKP\install\DowngradeNotSupportedException;
class MergeLocalesMigration extends \PKP\migration\upgrade\v3_4_0\MergeLocalesMigration
{
protected string $CONTEXT_TABLE = 'journals';
protected string $CONTEXT_SETTINGS_TABLE = 'journal_settings';
protected string $CONTEXT_COLUMN = 'journal_id';
/**
* Run the migrations.
*/
public function up(): void
{
parent::up();
// issue_galleys
$issueGalleys = DB::table('issue_galleys')
->get();
foreach ($issueGalleys as $issueGalley) {
$this->updateSingleValueLocale($issueGalley->locale, 'issue_galleys', 'locale', 'galley_id', $issueGalley->galley_id);
}
// publication_galleys
$publicationGalleys = DB::table('publication_galleys')
->get();
foreach ($publicationGalleys as $publicationGalley) {
$this->updateSingleValueLocale($publicationGalley->locale, 'publication_galleys', 'locale', 'galley_id', $publicationGalley->galley_id);
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
throw new DowngradeNotSupportedException();
}
public static function getSettingsTables(): Collection
{
return collect([
'issue_galley_settings' => ['galley_id', 'issue_galley_setting_id'],
'issue_settings' => ['issue_id', 'issue_setting_id'],
'journal_settings' => ['journal_id', 'journal_setting_id'],
'publication_galley_settings' => ['galley_id', 'publication_galley_setting_id'],
'section_settings' => ['section_id', 'section_setting_id'],
'static_page_settings' => ['static_page_id', 'static_page_setting_id'],
'subscription_type_settings' => ['type_id', 'subscription_type_setting_id'],
])->merge(parent::getSettingsTables());
}
}
@@ -0,0 +1,343 @@
<?php
/**
* @file classes/migration/upgrade/v3_4_0/PreflightCheckMigration.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PreflightCheckMigration
*
* @brief Check for common problems early in the upgrade process.
*/
namespace APP\migration\upgrade\v3_4_0;
use Exception;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Throwable;
class PreflightCheckMigration extends \PKP\migration\upgrade\v3_4_0\PreflightCheckMigration
{
public function up(): void
{
parent::up();
try {
$this->checkDuplicateDoiRegistrationAgencies();
} catch (Throwable $e) {
if ($fallbackVersion = $this->setFallbackVersion()) {
$this->_installer->log("A pre-flight check failed. The software was successfully upgraded to {$fallbackVersion} but could not be upgraded further (to " . $this->_installer->newVersion->getVersionString() . '). Check and correct the error, then try again.');
}
throw $e;
}
}
protected function getContextTable(): string
{
return 'journals';
}
protected function getContextKeyField(): string
{
return 'journal_id';
}
protected function getContextSettingsTable(): string
{
return 'journal_settings';
}
protected function buildOrphanedEntityProcessor(): void
{
parent::buildOrphanedEntityProcessor();
$this->addTableProcessor('issues', function (): int {
$affectedRows = 0;
// Depends directly on ~2 entities: doi_id->dois.doi_id(not found in previous version) journal_id->journals.journal_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('issues', $this->getContextKeyField(), $this->getContextTable(), $this->getContextKeyField());
return $affectedRows;
});
// Shared processor (there's another handler for this table at pkp-lib)
$this->addTableProcessor('publications', function (): int {
$affectedRows = 0;
// Depends directly on ~4 entities: primary_contact_id->authors.author_id doi_id->dois.doi_id(not found in previous version) section_id->sections.section_id submission_id->submissions.submission_id
// Custom field (not found in at least one of the softwares)
// Attempts to recover the field publications.section_id before discarding the entry
$rows = DB::table('publications AS p')
->leftJoin('sections AS s', 's.section_id', '=', 'p.section_id')
->join('submissions AS sub', 'sub.submission_id', '=', 'p.submission_id')
->whereNull('s.section_id')
->select('p.submission_id', 'p.publication_id', 'p.section_id')
->selectSub(
fn (Builder $q) => $q
->from('sections AS s')
->where('s.is_inactive', '=', 0)
->whereColumn('s.journal_id', '=', 'sub.context_id')
->selectRaw('MIN(s.section_id)'),
'new_section_id'
)
->get();
foreach ($rows as $row) {
$this->_installer->log("The publication ID ({$row->publication_id}) for the submission ID {$row->submission_id} is assigned to an invalid section ID \"{$row->section_id}\", its section will be updated to {$row->new_section_id}");
$affectedRows += DB::table('publications')->where('publication_id', '=', $row->publication_id)->update(['section_id' => $row->new_section_id]);
}
$affectedRows += $this->deleteOptionalReference('publications', 'section_id', 'sections', 'section_id');
// Remaining cleanups are inherited
return $affectedRows;
});
$this->addTableProcessor('publication_galleys', function (): int {
$affectedRows = 0;
// Depends directly on ~3 entities: doi_id->dois.doi_id(not found in previous version) publication_id->publications.publication_id submission_file_id->submission_files.submission_file_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('publication_galleys', 'publication_id', 'publications', 'publication_id');
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteOptionalReference('publication_galleys', 'submission_file_id', 'submission_files', 'submission_file_id');
return $affectedRows;
});
$this->addTableProcessor('issue_galleys', function (): int {
$affectedRows = 0;
// Depends directly on ~2 entities: file_id->issue_files.file_id issue_id->issues.issue_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('issue_galleys', 'issue_id', 'issues', 'issue_id');
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('issue_galleys', 'file_id', 'issue_files', 'file_id');
return $affectedRows;
});
$this->addTableProcessor('sections', function (): int {
$affectedRows = 0;
// Depends directly on ~2 entities: journal_id->journals.journal_id review_form_id->review_forms.review_form_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('sections', $this->getContextKeyField(), $this->getContextTable(), $this->getContextKeyField());
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->cleanOptionalReference('sections', 'review_form_id', 'review_forms', 'review_form_id');
return $affectedRows;
});
$this->addTableProcessor('subscription_types', function (): int {
$affectedRows = 0;
// Depends directly on ~1 entities: journal_id->journals.journal_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('subscription_types', $this->getContextKeyField(), $this->getContextTable(), $this->getContextKeyField());
return $affectedRows;
});
$this->addTableProcessor('issue_files', function (): int {
$affectedRows = 0;
// Depends directly on ~1 entities: issue_id->issues.issue_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('issue_files', 'issue_id', 'issues', 'issue_id');
return $affectedRows;
});
$this->addTableProcessor('subscriptions', function (): int {
$affectedRows = 0;
// Depends directly on ~3 entities: journal_id->journals.journal_id type_id->subscription_types.type_id user_id->users.user_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('subscriptions', 'user_id', 'users', 'user_id');
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('subscriptions', 'type_id', 'subscription_types', 'type_id');
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('subscriptions', $this->getContextKeyField(), $this->getContextTable(), $this->getContextKeyField());
return $affectedRows;
});
$this->addTableProcessor('completed_payments', function (): int {
$affectedRows = 0;
// Depends directly on ~2 entities: context_id->journals.journal_id user_id->users.user_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('completed_payments', 'context_id', $this->getContextTable(), $this->getContextKeyField());
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteOptionalReference('completed_payments', 'user_id', 'users', 'user_id');
return $affectedRows;
});
$this->addTableProcessor('custom_issue_orders', function (): int {
$affectedRows = 0;
// Depends directly on ~2 entities: issue_id->issues.issue_id journal_id->journals.journal_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('custom_issue_orders', $this->getContextKeyField(), $this->getContextTable(), $this->getContextKeyField());
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('custom_issue_orders', 'issue_id', 'issues', 'issue_id');
return $affectedRows;
});
$this->addTableProcessor('custom_section_orders', function (): int {
$affectedRows = 0;
// Depends directly on ~2 entities: issue_id->issues.issue_id section_id->sections.section_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('custom_section_orders', 'section_id', 'sections', 'section_id');
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('custom_section_orders', 'issue_id', 'issues', 'issue_id');
return $affectedRows;
});
$this->addTableProcessor('institutional_subscriptions', function (): int {
$affectedRows = 0;
// Depends directly on ~2 entities: institution_id->institutions.institution_id(not found in previous version) subscription_id->subscriptions.subscription_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('institutional_subscriptions', 'subscription_id', 'subscriptions', 'subscription_id');
return $affectedRows;
});
$this->addTableProcessor('issue_galley_settings', function (): int {
$affectedRows = 0;
// Depends directly on ~1 entities: galley_id->issue_galleys.galley_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('issue_galley_settings', 'galley_id', 'issue_galleys', 'galley_id');
return $affectedRows;
});
$this->addTableProcessor('issue_settings', function (): int {
$affectedRows = 0;
// Depends directly on ~1 entities: issue_id->issues.issue_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('issue_settings', 'issue_id', 'issues', 'issue_id');
return $affectedRows;
});
$this->addTableProcessor('publication_galley_settings', function (): int {
$affectedRows = 0;
// Depends directly on ~1 entities: galley_id->publication_galleys.galley_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('publication_galley_settings', 'galley_id', 'publication_galleys', 'galley_id');
return $affectedRows;
});
$this->addTableProcessor('section_settings', function (): int {
$affectedRows = 0;
// Depends directly on ~1 entities: section_id->sections.section_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('section_settings', 'section_id', 'sections', 'section_id');
return $affectedRows;
});
$this->addTableProcessor('subscription_type_settings', function (): int {
$affectedRows = 0;
// Depends directly on ~1 entities: type_id->subscription_types.type_id
// Custom field (not found in at least one of the softwares)
$affectedRows += $this->deleteRequiredReference('subscription_type_settings', 'type_id', 'subscription_types', 'type_id');
return $affectedRows;
});
// Support for the issueId setting
$this->addTableProcessor('publication_settings', function (): int {
$affectedRows = 0;
$rows = DB::table('publications AS p')
->join('publication_settings AS ps', 'ps.publication_id', '=', 'p.publication_id')
->leftJoin('issues AS i', DB::raw('CAST(i.issue_id AS CHAR(20))'), '=', 'ps.setting_value')
->where('ps.setting_name', 'issueId')
->whereNull('i.issue_id')
->get(['p.submission_id', 'p.publication_id', 'ps.setting_value']);
foreach ($rows as $row) {
$this->_installer->log("The publication ID ({$row->publication_id}) for the submission ID {$row->submission_id} is assigned to an invalid issue ID \"{$row->setting_value}\", its value will be updated to NULL");
$affectedRows += DB::table('publication_settings')
->where('publication_id', '=', $row->publication_id)
->where('setting_name', 'issueId')
->where('setting_value', $row->setting_value)
->delete();
}
return $affectedRows;
});
}
protected function getEntityRelationships(): array
{
return [
$this->getContextTable() => ['submissions', 'issues', 'user_groups', 'sections', 'categories', 'subscription_types', 'navigation_menu_items', 'genres', 'filters', 'announcement_types', 'subscriptions', 'notifications', 'navigation_menus', 'library_files', 'email_templates', 'user_group_stage', 'subeditor_submission_group', 'plugin_settings', 'notification_subscription_settings', $this->getContextSettingsTable(), 'custom_issue_orders', 'completed_payments'],
'users' => ['submission_files', 'review_assignments', 'subscriptions', 'notifications', 'event_log', 'email_log', 'user_user_groups', 'user_settings', 'user_interests', 'temporary_files', 'submission_comments', 'subeditor_submission_group', 'stage_assignments', 'sessions', 'query_participants', 'notification_subscription_settings', 'notes', 'email_log_users', 'edit_decisions', 'completed_payments', 'access_keys'],
'submissions' => ['submission_files', 'publications', 'review_rounds', 'review_assignments', 'submission_search_objects', 'library_files', 'submission_settings', 'submission_comments', 'stage_assignments', 'review_round_files', 'edit_decisions'],
'submission_files' => ['submission_files', 'publication_galleys', 'submission_file_settings', 'submission_file_revisions', 'review_round_files', 'review_files'],
// publication_settings dependency added manually
'issues' => [$this->getContextTable(), 'issue_galleys', 'issue_files', 'issue_settings', 'custom_section_orders', 'custom_issue_orders', 'publication_settings'],
'user_groups' => ['authors', 'user_user_groups', 'user_group_stage', 'user_group_settings', 'subeditor_submission_group', 'stage_assignments'],
'publications' => ['submissions', 'publication_galleys', 'authors', 'citations', 'publication_settings', 'publication_categories'],
'publication_galleys' => ['publication_galley_settings'],
'review_forms' => ['sections', 'review_form_elements', 'review_assignments', 'review_form_settings'],
'categories' => ['categories', 'publication_categories', 'category_settings'],
'issue_galleys' => ['issue_galley_settings'],
'sections' => ['publications', 'section_settings', 'custom_section_orders'],
'review_rounds' => ['review_assignments', 'review_round_files', 'edit_decisions'],
'navigation_menu_item_assignments' => ['navigation_menu_item_assignments', 'navigation_menu_item_assignment_settings'],
'authors' => ['publications', 'author_settings'],
'controlled_vocab_entries' => ['user_interests', 'controlled_vocab_entry_settings'],
'data_object_tombstones' => ['data_object_tombstone_settings', 'data_object_tombstone_oai_set_objects'],
'files' => ['submission_files', 'submission_file_revisions'],
'filters' => ['filters', 'filter_settings'],
'genres' => ['submission_files', 'genre_settings'],
'announcement_types' => ['announcements', 'announcement_type_settings'],
'navigation_menu_items' => ['navigation_menu_item_assignments', 'navigation_menu_item_settings'],
'review_assignments' => ['review_form_responses', 'review_files'],
'review_form_elements' => ['review_form_responses', 'review_form_element_settings'],
'subscription_types' => ['subscriptions', 'subscription_type_settings'],
'announcements' => ['announcement_settings'],
'queries' => ['query_participants'],
'navigation_menus' => ['navigation_menu_item_assignments'],
'notifications' => ['notification_settings'],
'filter_groups' => ['filters'],
'event_log' => ['event_log_settings'],
'email_templates' => ['email_templates_settings'],
'static_pages' => ['static_page_settings'],
'email_log' => ['email_log_users'],
'submission_search_keyword_list' => ['submission_search_object_keywords'],
'submission_search_objects' => ['submission_search_object_keywords'],
'controlled_vocabs' => ['controlled_vocab_entries'],
'library_files' => ['library_file_settings'],
'subscriptions' => ['institutional_subscriptions'],
'citations' => ['citation_settings'],
'issue_files' => ['issue_galleys']
];
}
protected function dropForeignKeys(): void
{
parent::dropForeignKeys();
if (DB::getDoctrineSchemaManager()->introspectTable('publication_galleys')->hasForeignKey('publication_galleys_submission_file_id_foreign')) {
Schema::table('publication_galleys', fn (Blueprint $table) => $table->dropForeign('publication_galleys_submission_file_id_foreign'));
}
}
/**
* Checks if DOIs have been marked registered with more than one registration agency.
*
* @throws Exception
*/
protected function checkDuplicateDoiRegistrationAgencies(): void
{
$agencies = ['crossref::status', 'datacite::status', 'medra::status'];
$submissionIds = DB::table('submission_settings')
->whereIn('setting_name', $agencies)
->groupBy('submission_id')
->havingRaw('COUNT(submission_id) > 1')
->select(['submission_id'])
->get();
$galleyIds = DB::table('publication_galley_settings')
->whereIn('setting_name', $agencies)
->groupBy('galley_id')
->havingRaw('COUNT(galley_id) > 1')
->select(['galley_id'])
->get();
$issueIds = DB::table('issue_settings')
->whereIn('setting_name', $agencies)
->groupBy('issue_id')
->havingRaw('COUNT(issue_id) > 1')
->select(['issue_id'])
->get();
if ($submissionIds->count() > 0 || $galleyIds->count() > 0 || $issueIds->count() > 0) {
throw new Exception('Some DOIs have been registered with multiple registration agencies. Resolve duplicates before continuing by running `php tools/resolveAgencyDuplicates.php`.');
}
}
}