diff --git a/.github/workflows/craft-versions.yml b/.github/workflows/craft-versions.yml index 1828b735..67e4a690 100644 --- a/.github/workflows/craft-versions.yml +++ b/.github/workflows/craft-versions.yml @@ -147,4 +147,8 @@ jobs: echo "CRAFT_VERSION=${{ matrix.craft_version }}" >> $GITHUB_ENV - name: 🧪 Run tests for CraftCMS ${{ matrix.craft_version }} - run: make test-craft-versions + uses: nick-fields/retry@v3 + with: + max_attempts: 5 + timeout_minutes: 20 + command: make test-craft-versions diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index fba495d1..39badb47 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -16,10 +16,15 @@ jobs: "cypress/e2e/jobs/copy-source-text-flow/filters.cy.js", "cypress/e2e/jobs/copy-source-text-flow/success-path-multiple.cy.js", "cypress/e2e/jobs/copy-source-text-flow/success-path-single.cy.js", + "cypress/e2e/jobs/copy-source-text-flow/success-path-multiple-async-publishing.cy.js", + "cypress/e2e/jobs/copy-source-text-flow/success-path-single-async-publishing.cy.js", "cypress/e2e/jobs/instant/success-path-multiple.cy.js", "cypress/e2e/jobs/instant/success-path-multiple-copy-slug.cy.js", "cypress/e2e/jobs/instant/success-path-multiple-copy-slug-and-enable-after-publish.cy.js", "cypress/e2e/jobs/instant/success-path-multiple-enable-after-publish.cy.js", + "cypress/e2e/jobs/instant/success-path-single.cy.js", + "cypress/e2e/jobs/instant/success-path-multiple-async-publish.cy.js", + "cypress/e2e/jobs/instant/success-path-single-async-publishing.cy.js", "cypress/e2e/jobs/verified/success-path-multiple-bulk-publishing.cy.js", "cypress/e2e/jobs/verified/success-path-multiple-bulk-publishing-copy-slug.cy.js", "cypress/e2e/jobs/verified/success-path-multiple-bulk-publishing-copy-slug-and-enable-after-publish.cy.js", @@ -29,7 +34,9 @@ jobs: "cypress/e2e/jobs/verified/success-path-multiple-single-publishing-copy-slug-and-enable-after-publish.cy.js", "cypress/e2e/jobs/verified/success-path-multiple-single-publishing-enable-after-publish.cy.js", "cypress/e2e/jobs/verified/success-path-single.cy.js", - "cypress/e2e/jobs/instant/success-path-single.cy.js", + "cypress/e2e/jobs/verified/success-path-multiple-bulk-async-publishing.cy.js", + "cypress/e2e/jobs/verified/success-path-multiple-single-async-publishing.cy.js", + "cypress/e2e/jobs/verified/success-path-single-async-publishing.cy.js", "cypress/e2e/jobs/instant/success-path-single-500-start.cy.js", ] runs-on: ubuntu-latest diff --git a/e2e/cypress.config.js b/e2e/cypress.config.js index 2650bff6..b904a65c 100644 --- a/e2e/cypress.config.js +++ b/e2e/cypress.config.js @@ -10,4 +10,10 @@ export default defineConfig({ // implement node event listeners here }, }, + retries: { + // Configure retry attempts for `cypress run` + runMode: 5, + // Configure retry attempts for `cypress open` + openMode: 0 + } }); diff --git a/e2e/cypress/e2e/jobs/copy-source-text-flow/success-path-multiple-async-publishing.cy.js b/e2e/cypress/e2e/jobs/copy-source-text-flow/success-path-multiple-async-publishing.cy.js new file mode 100644 index 00000000..f89706f6 --- /dev/null +++ b/e2e/cypress/e2e/jobs/copy-source-text-flow/success-path-multiple-async-publishing.cy.js @@ -0,0 +1,67 @@ +const {generateJobData} = require('../../../support/job/generator.js'); + +describe( + '[Copy Source Text] Success path for job with multiple target languages', + () => { + const entryLabel = 'The Future of Augmented Reality'; + + it('with copy slug disabled & enable after publish disabled', () => { + const {jobTitle, slug} = generateJobData(); + + cy.copySourceTextFlow({ + slug, + entryLabel, + jobTitle, + copySlug: false, + enableAfterPublish: false, + languages: ['de', 'es', 'uk'], + batchPublishing: true, + publishTranslationsAsync: true + }) + }); + + it('with copy slug disabled & enable after publish enabled', () => { + const {jobTitle, slug} = generateJobData(); + + cy.copySourceTextFlow({ + slug, + entryLabel, + jobTitle, + copySlug: false, + enableAfterPublish: true, + languages: ['de', 'es', 'uk'], + batchPublishing: true, + publishTranslationsAsync: true + }) + }); + + it('with copy slug enabled & enable after publish disabled', () => { + const {jobTitle, slug} = generateJobData(); + + cy.copySourceTextFlow({ + slug, + entryLabel, + jobTitle, + copySlug: true, + enableAfterPublish: false, + languages: ['de', 'es', 'uk'], + batchPublishing: true, + publishTranslationsAsync: true + }) + }); + + it('with copy slug enabled & enable after publish enabled', () => { + const {jobTitle, slug} = generateJobData(); + + cy.copySourceTextFlow({ + slug, + entryLabel, + jobTitle, + copySlug: true, + enableAfterPublish: true, + languages: ['de', 'es', 'uk'], + batchPublishing: true, + publishTranslationsAsync: true + }) + }); + }); diff --git a/e2e/cypress/e2e/jobs/copy-source-text-flow/success-path-single-async-publishing.cy.js b/e2e/cypress/e2e/jobs/copy-source-text-flow/success-path-single-async-publishing.cy.js new file mode 100644 index 00000000..3068916a --- /dev/null +++ b/e2e/cypress/e2e/jobs/copy-source-text-flow/success-path-single-async-publishing.cy.js @@ -0,0 +1,63 @@ +const {generateJobData} = require('../../../support/job/generator.js'); + +describe( + '[Copy Source Text] Success path for job with one target language', + () => { + const entryLabel = 'The Future of Augmented Reality'; + + it('with copy slug disabled & enable after publish disabled', () => { + const {jobTitle, slug} = generateJobData(); + + cy.copySourceTextFlow({ + slug, + entryLabel, + jobTitle, + copySlug: false, + enableAfterPublish: false, + languages: ["de"], + publishTranslationsAsync: true + }) + }); + + it('with copy slug disabled & enable after publish enabled', () => { + const {jobTitle, slug} = generateJobData(); + + cy.copySourceTextFlow({ + slug, + entryLabel, + jobTitle, + copySlug: false, + enableAfterPublish: true, + languages: ["de"], + publishTranslationsAsync: true + }) + }); + + it('with copy slug enabled & enable after publish disabled', () => { + const {jobTitle, slug} = generateJobData(); + + cy.copySourceTextFlow({ + slug, + entryLabel, + jobTitle, + copySlug: true, + enableAfterPublish: false, + languages: ["de"], + publishTranslationsAsync: true + }) + }); + + it('with copy slug enabled & enable after publish enabled', () => { + const {jobTitle, slug} = generateJobData(); + + cy.copySourceTextFlow({ + slug, + entryLabel, + jobTitle, + copySlug: true, + enableAfterPublish: true, + languages: ["de"], + publishTranslationsAsync: true + }) + }); + }); diff --git a/e2e/cypress/e2e/jobs/instant/success-path-multiple-async-publish.cy.js b/e2e/cypress/e2e/jobs/instant/success-path-multiple-async-publish.cy.js new file mode 100644 index 00000000..07cb044e --- /dev/null +++ b/e2e/cypress/e2e/jobs/instant/success-path-multiple-async-publish.cy.js @@ -0,0 +1,25 @@ +const {generateJobData} = require('../../../support/job/generator.js'); + +describe( + '[Instant] Success path for job with multiple target languages', + () => { + const entryLabel = 'The Future of Augmented Reality'; + const entryId = 24; + + it('async publishing', () => { + const {jobTitle, slug} = generateJobData(); + + cy.instantFlow({ + slug, + entryLabel, + jobTitle, + entryId, + copySlug: false, + enableAfterPublish: false, + languages: ['de', 'es', 'uk'], + batchPublishing: true, + publishTranslationsAsync: true + }); + }); + + }); diff --git a/e2e/cypress/e2e/jobs/instant/success-path-single-async-publishing.cy.js b/e2e/cypress/e2e/jobs/instant/success-path-single-async-publishing.cy.js new file mode 100644 index 00000000..7baa06ff --- /dev/null +++ b/e2e/cypress/e2e/jobs/instant/success-path-single-async-publishing.cy.js @@ -0,0 +1,23 @@ +const {generateJobData} = require('../../../support/job/generator.js'); + +describe( + '[Instant] Success path for job with single target language', + () => { + const entryLabel = 'The Future of Augmented Reality'; + const entryId = 24; + + it('async publishing', () => { + const {jobTitle, slug} = generateJobData(); + + cy.instantFlow({ + slug, + entryLabel, + jobTitle, + entryId, + copySlug: false, + enableAfterPublish: false, + languages: ["de"], + publishTranslationsAsync: true + }) + }); + }); diff --git a/e2e/cypress/e2e/jobs/verified/success-path-multiple-bulk-async-publishing.cy.js b/e2e/cypress/e2e/jobs/verified/success-path-multiple-bulk-async-publishing.cy.js new file mode 100644 index 00000000..352a5fba --- /dev/null +++ b/e2e/cypress/e2e/jobs/verified/success-path-multiple-bulk-async-publishing.cy.js @@ -0,0 +1,25 @@ +const {generateJobData} = require('../../../support/job/generator.js'); + +describe( + '[Verified] Success path for job with multiple target languages with bulk publishing', + () => { + const entryLabel = 'The Future of Augmented Reality'; + const entryId = 24; + + it('async publishing', () => { + const {jobTitle, slug} = generateJobData(); + + cy.verifiedFlow({ + slug, + entryLabel, + jobTitle, + entryId, + copySlug: false, + enableAfterPublish: false, + languages: ['de', 'es', 'uk'], + batchPublishing: true, + publishTranslationsAsync: true + }); + }); + + }); diff --git a/e2e/cypress/e2e/jobs/verified/success-path-multiple-single-async-publishing.cy.js b/e2e/cypress/e2e/jobs/verified/success-path-multiple-single-async-publishing.cy.js new file mode 100644 index 00000000..d8304847 --- /dev/null +++ b/e2e/cypress/e2e/jobs/verified/success-path-multiple-single-async-publishing.cy.js @@ -0,0 +1,24 @@ +const {generateJobData} = require('../../../support/job/generator.js'); + +describe( + '[Verified] Success path for job with multiple target languages with single publishing', + () => { + const entryLabel = 'The Future of Augmented Reality'; + const entryId = 24; + + it('async publishing', () => { + const {jobTitle, slug} = generateJobData(); + + cy.verifiedFlow({ + slug, + entryLabel, + jobTitle, + entryId, + copySlug: false, + enableAfterPublish: false, + languages: ['de', 'es', 'uk'], + batchPublishing: false, + publishTranslationsAsync: true + }); + }); + }); diff --git a/e2e/cypress/e2e/jobs/verified/success-path-single-async-publishing.cy.js b/e2e/cypress/e2e/jobs/verified/success-path-single-async-publishing.cy.js new file mode 100644 index 00000000..e0bc4125 --- /dev/null +++ b/e2e/cypress/e2e/jobs/verified/success-path-single-async-publishing.cy.js @@ -0,0 +1,23 @@ +const {generateJobData} = require('../../../support/job/generator.js'); + +describe( + '[Verified] Success path for job with single target language', + () => { + const entryLabel = 'The Future of Augmented Reality'; + const entryId = 24; + + it('async publishing', () => { + const {jobTitle, slug} = generateJobData(); + + cy.verifiedFlow({ + slug, + entryLabel, + jobTitle, + entryId, + copySlug: false, + enableAfterPublish: false, + languages: ["de"], + publishTranslationsAsync: true + }) + }); + }); diff --git a/e2e/cypress/support/commands.js b/e2e/cypress/support/commands.js index 3cce30f6..0f1a6e30 100644 --- a/e2e/cypress/support/commands.js +++ b/e2e/cypress/support/commands.js @@ -91,6 +91,9 @@ Cypress.Commands.add('setConfigurationOption', (option, enabled) => { splitSend: { id: 'queueEachTranslationFileSeparately', }, + publishTranslationsAsync: { + id: 'publishTranslationsAsync', + }, }; if (!options[option]) { @@ -576,18 +579,22 @@ Cypress.Commands.add('assertEntryContent', * @param {object} options * @returns undefined */ -Cypress.Commands.add('copySourceTextFlow', ({ - slug, - entryLabel, - jobTitle, - copySlug = false, - enableAfterPublish = false, - languages = ['de'], - batchPublishing = false, //publish all translations at once with publish button - entryId = 24, - }) => { +Cypress.Commands.add( + 'copySourceTextFlow', + ({ + slug, + entryLabel, + jobTitle, + copySlug = false, + enableAfterPublish = false, + languages = ['de'], + batchPublishing = false, //publish all translations at once with publish button + entryId = 24, + publishTranslationsAsync = false, + }) => { cy.setConfigurationOption('enableEntries', enableAfterPublish); cy.setConfigurationOption('copySlug', copySlug); + cy.setConfigurationOption('publishTranslationsAsync', publishTranslationsAsync); if (copySlug) { // update slug on entry and enable slug copy option diff --git a/e2e/cypress/support/flow/instant.js b/e2e/cypress/support/flow/instant.js index 81e32aac..e274b99c 100644 --- a/e2e/cypress/support/flow/instant.js +++ b/e2e/cypress/support/flow/instant.js @@ -17,6 +17,7 @@ Cypress.Commands.add('instantFlow', ({ batchPublishing = false, //publish all translations at once with publish button entryId = 24, splitSend = true, + publishTranslationsAsync = false, options = { startFailure: false, }, @@ -30,6 +31,7 @@ Cypress.Commands.add('instantFlow', ({ cy.setConfigurationOption('enableEntries', enableAfterPublish); cy.setConfigurationOption('copySlug', copySlug); cy.setConfigurationOption('splitSend', splitSend); + cy.setConfigurationOption('publishTranslationsAsync', publishTranslationsAsync); if (copySlug) { // update slug on entry and enable slug copy option diff --git a/e2e/cypress/support/flow/verified.js b/e2e/cypress/support/flow/verified.js index 233f8aff..a7a39202 100644 --- a/e2e/cypress/support/flow/verified.js +++ b/e2e/cypress/support/flow/verified.js @@ -18,6 +18,7 @@ Cypress.Commands.add('verifiedFlow', ({ batchPublishing = false, //publish all translations at once with publish button entryId = 24, splitSend = true, + publishTranslationsAsync = true, }) => { const isMockserverEnabled = Cypress.env('MOCKSERVER_ENABLED'); @@ -28,6 +29,7 @@ Cypress.Commands.add('verifiedFlow', ({ cy.setConfigurationOption('enableEntries', enableAfterPublish); cy.setConfigurationOption('copySlug', copySlug); cy.setConfigurationOption('splitSend', splitSend); + cy.setConfigurationOption('publishTranslationsAsync', publishTranslationsAsync); if (copySlug) { // update slug on entry and enable slug copy option diff --git a/src/Craftliltplugin.php b/src/Craftliltplugin.php index abe9a4ec..280608b8 100644 --- a/src/Craftliltplugin.php +++ b/src/Craftliltplugin.php @@ -34,6 +34,7 @@ use lilthq\craftliltplugin\services\handlers\CreateTranslationsHandler; use lilthq\craftliltplugin\services\handlers\EditJobHandler; use lilthq\craftliltplugin\services\handlers\LoadI18NHandler; +use lilthq\craftliltplugin\services\handlers\PublishDraftAsyncHandler; use lilthq\craftliltplugin\services\handlers\PublishDraftHandler; use lilthq\craftliltplugin\services\handlers\RefreshJobStatusHandler; use lilthq\craftliltplugin\services\handlers\SendJobToLiltConnectorHandler; @@ -90,6 +91,7 @@ * @property SendTranslationToLiltConnectorHandler $sendTranslationToLiltConnectorHandler * @property SyncJobFromLiltConnectorHandler $syncJobFromLiltConnectorHandler * @property PublishDraftHandler $publishDraftsHandler + * @property PublishDraftAsyncHandler $publishDraftsHandlerAsync * @property Configuration $connectorConfiguration * @property JobsApi $connectorJobsApi * @property TranslationsApi $connectorTranslationsApi diff --git a/src/assets/resources/entry-edit.js b/src/assets/resources/entry-edit.js index 35e41d25..d97ad58f 100644 --- a/src/assets/resources/entry-edit.js +++ b/src/assets/resources/entry-edit.js @@ -13,6 +13,7 @@ CraftliltPlugin.EntryEditWarning = Garnish.Base.extend({ 'statuses[1]': ['in-progress'], 'statuses[2]': ['ready-for-review'], 'statuses[3]': ['ready-to-publish'], + 'statuses[4]': ['publishing'], }); const container = jQuery('
'). diff --git a/src/assets/resources/job-translation-review.js b/src/assets/resources/job-translation-review.js index 2f401acf..d6ecadc1 100644 --- a/src/assets/resources/job-translation-review.js +++ b/src/assets/resources/job-translation-review.js @@ -43,12 +43,14 @@ CraftliltPlugin.TranslationReview = Garnish.Base.extend({ const translationIsReviewed = translationRow.data('is-reviewed'); const translationIsPublished = translationRow.data('is-published'); const translationTitle = translationRow.data('title'); + const translationStatus = translationRow.data('status'); return { translationId, translationTitle, translationIsPublished, translationIsReviewed, + translationStatus, }; }, loadTranslationData: function(translationId) { @@ -114,7 +116,7 @@ CraftliltPlugin.TranslationReview = Garnish.Base.extend({ }); const { - translationIsReviewed, translationIsPublished, + translationIsReviewed, translationIsPublished, translationStatus } = this.getTranslationData(translationId); if (translationIsReviewed === 1) { @@ -128,6 +130,12 @@ CraftliltPlugin.TranslationReview = Garnish.Base.extend({ } else { this.$modalFooterButtonsPublish.removeClass('disabled'); } + + if (translationStatus === "publishing") { + this.$modalFooterButtonsPublish.addClass('disabled'); + } else { + this.$modalFooterButtonsPublish.removeClass('disabled'); + } }, showMultiModal: function(translationIds) { if (translationIds.length === 1) { @@ -599,6 +607,7 @@ $(document).ready(function() { const status = $(this).find('span.translation-status').data('status'); if (status === 'published' || status === 'failed' || status === 'new' || + status === 'publishing' || status === 'in-progress') { disabledIds.push($(this).data('id')); } else { diff --git a/src/controllers/PostConfigurationController.php b/src/controllers/PostConfigurationController.php index 53b3526d..746a5222 100644 --- a/src/controllers/PostConfigurationController.php +++ b/src/controllers/PostConfigurationController.php @@ -120,6 +120,17 @@ public function actionInvoke(): Response (string)$queueDisableAutomaticSync ); + // publishTranslationsAsync + $publishTranslationsAsync = $request->getBodyParam('publishTranslationsAsync'); + if (empty($publishTranslationsAsync)) { + $publishTranslationsAsync = 0; + } + + Craftliltplugin::getInstance()->settingsRepository->save( + SettingsRepository::PUBLISH_TRANSLATIONS_ASYNC, + (string)$publishTranslationsAsync + ); + $settingsRequest = new SettingsRequest(); $settingsRequest->setProjectPrefix( $request->getBodyParam('projectPrefix') diff --git a/src/controllers/translation/PostTranslationPublishController.php b/src/controllers/translation/PostTranslationPublishController.php index 2192371e..393670f8 100644 --- a/src/controllers/translation/PostTranslationPublishController.php +++ b/src/controllers/translation/PostTranslationPublishController.php @@ -10,11 +10,13 @@ namespace lilthq\craftliltplugin\controllers\translation; use Craft; -use craft\base\ElementInterface; use lilthq\craftliltplugin\controllers\job\AbstractJobController; use lilthq\craftliltplugin\Craftliltplugin; -use lilthq\craftliltplugin\elements\Translation; use lilthq\craftliltplugin\records\TranslationRecord; +use lilthq\craftliltplugin\services\handlers\commands\PublishDraftCommand; +use lilthq\craftliltplugin\services\handlers\PublishDraftAsyncHandler; +use lilthq\craftliltplugin\services\handlers\PublishDraftHandler; +use lilthq\craftliltplugin\services\repositories\SettingsRepository; use Throwable; use yii\web\Response; @@ -42,36 +44,41 @@ public function actionInvoke(): Response return (new Response())->setStatusCode(404); } + $publishHandler = $this->getPublishHandler(); + foreach ($translations as $translation) { - Craftliltplugin::getInstance()->publishDraftsHandler->__invoke( - $translation->translatedDraftId, - $translation->targetSiteId + $publishHandler->__invoke( + new PublishDraftCommand( + $translation->translatedDraftId, + $translation->targetSiteId, + $translation->jobId, + $translation->id + ) ); } - $updated = TranslationRecord::updateAll( - ['status' => TranslationRecord::STATUS_PUBLISHED], - ['id' => $translationIds] + Craftliltplugin::getInstance()->refreshJobStatusHandler->__invoke( + $translations[0]->jobId ); - if ($updated) { - foreach ($translations as $translation) { - Craftliltplugin::getInstance()->jobLogsRepository->create( - $translation->jobId, - Craft::$app->getUser()->getId(), - sprintf('Translation (id: %d) published', $translation->id) - ); - } + return $this->asJson([ + 'success' => true + ]); + } - Craftliltplugin::getInstance()->refreshJobStatusHandler->__invoke( - $translations[0]->jobId - ); + /** + * @return PublishDraftAsyncHandler|PublishDraftHandler + */ + private function getPublishHandler() + { + if ( + Craftliltplugin::getInstance() + ->settingsRepository + ->getBool(SettingsRepository::PUBLISH_TRANSLATIONS_ASYNC) + ) { + return Craftliltplugin::getInstance()->publishDraftsHandlerAsync; } - Craft::$app->elements->invalidateCachesForElementType(Translation::class); - - return $this->asJson([ - 'success' => $updated === 1 - ]); + return Craftliltplugin::getInstance()->publishDraftsHandler; } } diff --git a/src/elements/Job.php b/src/elements/Job.php index 540a694b..a2b124c6 100644 --- a/src/elements/Job.php +++ b/src/elements/Job.php @@ -37,6 +37,7 @@ class Job extends Element public const STATUS_NEW = 'new'; public const STATUS_DRAFT = 'draft'; public const STATUS_IN_PROGRESS = 'in-progress'; + public const STATUS_PUBLISHING = 'publishing'; public const STATUS_READY_FOR_REVIEW = 'ready-for-review'; public const STATUS_READY_TO_PUBLISH = 'ready-to-publish'; public const STATUS_COMPLETE = 'complete'; @@ -228,6 +229,7 @@ public static function statuses(): array self::STATUS_NEW => ['label' => 'New', 'color' => 'orange'], self::STATUS_DRAFT => ['label' => 'Draft', 'color' => ''], self::STATUS_IN_PROGRESS => ['label' => 'In Progress', 'color' => 'blue'], + self::STATUS_PUBLISHING => ['label' => 'Publishing', 'color' => 'blue'], self::STATUS_READY_FOR_REVIEW => ['label' => 'Ready for review', 'color' => 'yellow'], self::STATUS_READY_TO_PUBLISH => ['label' => 'Ready to publish', 'color' => 'purple'], self::STATUS_COMPLETE => ['label' => 'Complete', 'color' => 'green'], @@ -296,6 +298,16 @@ protected static function defineSources(string $context = null): array ], 'defaultSort' => ['dateCreated', 'desc'] ], + [ + 'key' => 'publishing', + 'label' => 'Publishing', + 'criteria' => [ + 'status' => [ + self::STATUS_PUBLISHING + ] + ], + 'defaultSort' => ['dateCreated', 'desc'] + ], [ 'key' => 'ready-for-review', 'label' => 'Ready for review', diff --git a/src/elements/Translation.php b/src/elements/Translation.php index 4451c105..5b870ba7 100644 --- a/src/elements/Translation.php +++ b/src/elements/Translation.php @@ -104,6 +104,7 @@ public static function statuses(): array { return [ TranslationRecord::STATUS_IN_PROGRESS => ['label' => 'In Progress', 'color' => 'blue'], + TranslationRecord::STATUS_PUBLISHING => ['label' => 'Publishing', 'color' => 'blue'], TranslationRecord::STATUS_READY_FOR_REVIEW => ['label' => 'Ready for review', 'color' => 'yellow'], TranslationRecord::STATUS_READY_TO_PUBLISH => ['label' => 'Ready to publish', 'color' => 'purple'], TranslationRecord::STATUS_PUBLISHED => ['label' => 'Published', 'color' => 'green'], diff --git a/src/modules/FetchInstantJobTranslationsFromConnector.php b/src/modules/FetchInstantJobTranslationsFromConnector.php index 82331967..5dafd266 100644 --- a/src/modules/FetchInstantJobTranslationsFromConnector.php +++ b/src/modules/FetchInstantJobTranslationsFromConnector.php @@ -111,4 +111,14 @@ public function canRetry($attempt, $error): bool { return $attempt < self::RETRY_COUNT; } + + public static function getDelay(): int + { + $envDelay = getenv('CRAFT_LILT_PLUGIN_QUEUE_DELAY_IN_SECONDS'); + if (!empty($envDelay) || $envDelay === '0') { + return (int)$envDelay; + } + + return self::DELAY_IN_SECONDS; + } } diff --git a/src/modules/FetchVerifiedJobTranslationsFromConnector.php b/src/modules/FetchVerifiedJobTranslationsFromConnector.php index a96430be..335c539f 100644 --- a/src/modules/FetchVerifiedJobTranslationsFromConnector.php +++ b/src/modules/FetchVerifiedJobTranslationsFromConnector.php @@ -183,6 +183,16 @@ function (TranslationResponse $translationResponse) use ($job, $unprocessedTrans $mutex->release($mutexKey); } + public static function getDelay(): int + { + $envDelay = getenv('CRAFT_LILT_PLUGIN_QUEUE_DELAY_IN_SECONDS'); + if (!empty($envDelay) || $envDelay === '0') { + return (int)$envDelay; + } + + return self::DELAY_IN_SECONDS; + } + /** * @inheritdoc */ diff --git a/src/modules/PublishTranslation.php b/src/modules/PublishTranslation.php new file mode 100644 index 00000000..f8f20606 --- /dev/null +++ b/src/modules/PublishTranslation.php @@ -0,0 +1,148 @@ +getCommand(); + if (empty($command)) { + return; + } + + Craftliltplugin::getInstance()->publishDraftsHandler->__invoke( + $this->getPublishDraftCommand() + ); + + Craftliltplugin::getInstance()->refreshJobStatusHandler->__invoke( + $this->jobId + ); + + $this->markAsDone($queue); + $this->release(); + } + + private function getPublishDraftCommand(): PublishDraftCommand + { + return new PublishDraftCommand( + $this->draftId, + $this->targetSiteId, + $this->jobId, + $this->translationId + ); + } + + /** + * @inheritdoc + */ + protected function defaultDescription(): ?string + { + return Craft::t( + 'app', + sprintf( + 'Publish translation draft: %d', + $this->translationId + ) + ); + } + + /** + * @param $queue + * @return void + */ + private function markAsDone($queue): void + { + $this->setProgress( + $queue, + 1, + Craft::t( + 'app', + 'Sending translation for jobId: {jobId} to lilt platform done', + [ + 'jobId' => $this->jobId, + ] + ) + ); + } + + public function canRetry(): bool + { + return $this->attempt < self::RETRY_COUNT; + } + + public function getRetryJob(): BaseJob + { + return new self([ + 'jobId' => $this->jobId, + 'translationId' => $this->translationId, + 'draftId' => $this->draftId, + 'targetSiteId' => $this->targetSiteId, + 'attempt' => $this->attempt + 1 + ]); + } + + protected function getMutexKey(): string + { + return join('_', [ + __CLASS__, + __FUNCTION__, + $this->jobId, + $this->translationId, + $this->targetSiteId, + $this->draftId, + $this->attempt + ]); + } + + public static function getDelay(): int + { + $envDelay = getenv('CRAFT_LILT_PLUGIN_QUEUE_DELAY_IN_SECONDS'); + if (!empty($envDelay) || $envDelay === '0') { + return (int)$envDelay; + } + + return self::DELAY_IN_SECONDS; + } +} diff --git a/src/records/TranslationRecord.php b/src/records/TranslationRecord.php index dd0517b4..fc65986a 100644 --- a/src/records/TranslationRecord.php +++ b/src/records/TranslationRecord.php @@ -33,6 +33,7 @@ class TranslationRecord extends ActiveRecord public const STATUS_READY_FOR_REVIEW = 'ready-for-review'; public const STATUS_READY_TO_PUBLISH = 'ready-to-publish'; public const STATUS_IN_PROGRESS = 'in-progress'; + public const STATUS_PUBLISHING = 'publishing'; public const STATUS_PUBLISHED = 'published'; public const STATUS_FAILED = 'failed'; public const STATUS_NEEDS_ATTENTION = 'needs-attention'; diff --git a/src/services/ServiceInitializer.php b/src/services/ServiceInitializer.php index 8206d499..73dfb944 100644 --- a/src/services/ServiceInitializer.php +++ b/src/services/ServiceInitializer.php @@ -5,7 +5,6 @@ namespace lilthq\craftliltplugin\services; use Craft; -use fruitstudios\linkit\fields\LinkitField; use GuzzleHttp\Client; use LiltConnectorSDK\Api\JobsApi; use LiltConnectorSDK\Api\SettingsApi; @@ -34,6 +33,7 @@ use lilthq\craftliltplugin\services\handlers\field\copier\SuperTableFieldCopier; use lilthq\craftliltplugin\services\handlers\field\CopyFieldsHandler; use lilthq\craftliltplugin\services\handlers\LoadI18NHandler; +use lilthq\craftliltplugin\services\handlers\PublishDraftAsyncHandler; use lilthq\craftliltplugin\services\handlers\PublishDraftHandler; use lilthq\craftliltplugin\services\handlers\RefreshJobStatusHandler; use lilthq\craftliltplugin\services\handlers\SendJobToLiltConnectorHandler; @@ -124,6 +124,23 @@ public function run(): void ); }, ]); + $pluginInstance->setComponents([ + 'publishDraftsHandler' => function () use ($pluginInstance) { + return new PublishDraftHandler( + new CopyFieldsHandler( + [ + CraftliltpluginParameters::CRAFT_FIELDS_MATRIX => new MatrixFieldCopier(), + CraftliltpluginParameters::BENF_NEO_FIELD => new NeoFieldCopier(), + CraftliltpluginParameters::CRAFT_FIELDS_SUPER_TABLE => new SuperTableFieldCopier(), + + CopyFieldsHandler::DEFAULT_FIELD_COPIER => new DefaultFieldCopier() + ] + ), + Craft::$app->getDrafts(), + $pluginInstance->settingsRepository + ); + }, + ]); $pluginInstance->setComponents([ 'connectorTranslationsApi' => @@ -265,10 +282,10 @@ function () { 'class' => ConnectorJobRepository::class, 'apiInstance' => $pluginInstance->connectorJobsApi, ], - 'publishDraftsHandler' => + 'publishDraftsHandlerAsync' => [ - 'class' => PublishDraftHandler::class, - 'draftRepository' => Craft::$app->getDrafts(), + 'class' => PublishDraftAsyncHandler::class, + 'translationRepository' => $pluginInstance->translationRepository ], 'connectorTranslationRepository' => [ diff --git a/src/services/handlers/CreateDraftHandler.php b/src/services/handlers/CreateDraftHandler.php index 45ee93b6..7e038388 100644 --- a/src/services/handlers/CreateDraftHandler.php +++ b/src/services/handlers/CreateDraftHandler.php @@ -18,9 +18,9 @@ use lilthq\craftliltplugin\Craftliltplugin; use lilthq\craftliltplugin\datetime\DateTime; use lilthq\craftliltplugin\parameters\CraftliltpluginParameters; -use lilthq\craftliltplugin\records\SettingRecord; use lilthq\craftliltplugin\services\handlers\commands\CreateDraftCommand; use lilthq\craftliltplugin\services\handlers\field\CopyFieldsHandler; +use lilthq\craftliltplugin\services\repositories\SettingsRepository; use Throwable; use yii\base\Exception; @@ -95,10 +95,9 @@ public function create( ); } - $copyEntriesSlugFromSourceToTarget = SettingRecord::findOne( - ['name' => 'copy_entries_slug_from_source_to_target'] - ); - $isCopySlugEnabled = (bool)($copyEntriesSlugFromSourceToTarget->value ?? false); + $isCopySlugEnabled = Craftliltplugin::getInstance() + ->settingsRepository + ->getBool(SettingsRepository::COPY_ENTRIES_SLUG_FROM_SOURCE_TO_TARGET); if ($isCopySlugEnabled) { $draft->slug = $element->slug; diff --git a/src/services/handlers/PublishDraftAsyncHandler.php b/src/services/handlers/PublishDraftAsyncHandler.php new file mode 100644 index 00000000..d66ba4fa --- /dev/null +++ b/src/services/handlers/PublishDraftAsyncHandler.php @@ -0,0 +1,50 @@ + $command->getJobId(), + 'translationId' => $command->getTranslationId(), + 'targetSiteId' => $command->getTargetSiteId(), + 'draftId' => $command->getDraftId(), + ] + )), + PublishTranslation::PRIORITY, + PublishTranslation::DELAY_IN_SECONDS + ); + + + $this->translationRepository->updateTranslationStatusById( + $command->getTranslationId(), + TranslationRecord::STATUS_PUBLISHING + ); + + Craft::$app->getElements()->invalidateCachesForElementType(Translation::class); + } +} diff --git a/src/services/handlers/PublishDraftHandler.php b/src/services/handlers/PublishDraftHandler.php index 0557fd7d..d5bdb806 100644 --- a/src/services/handlers/PublishDraftHandler.php +++ b/src/services/handlers/PublishDraftHandler.php @@ -11,13 +11,21 @@ use Craft; use craft\base\ElementInterface; +use craft\errors\ElementNotFoundException; use craft\errors\InvalidElementException; +use craft\errors\InvalidFieldException; use craft\services\Drafts as DraftRepository; +use lilthq\craftliltplugin\Craftliltplugin; +use lilthq\craftliltplugin\elements\Translation; use lilthq\craftliltplugin\parameters\CraftliltpluginParameters; use lilthq\craftliltplugin\records\SettingRecord; use lilthq\craftliltplugin\records\TranslationRecord; +use lilthq\craftliltplugin\services\handlers\commands\PublishDraftCommand; +use lilthq\craftliltplugin\services\handlers\field\CopyFieldsHandler; +use lilthq\craftliltplugin\services\repositories\SettingsRepository; use Throwable; use yii\base\Exception; +use yii\base\InvalidConfigException; class PublishDraftHandler { @@ -26,112 +34,86 @@ class PublishDraftHandler */ public $draftRepository; + /** + * @var CopyFieldsHandler + */ + public $copyFieldsHandler; + + /** + * @var SettingsRepository + */ + public $settingsRepository; + + public function __construct( + CopyFieldsHandler $copyFieldsHandler, + DraftRepository $draftRepository, + SettingsRepository $settingsRepository + ) { + $this->copyFieldsHandler = $copyFieldsHandler; + $this->draftRepository = $draftRepository; + $this->settingsRepository = $settingsRepository; + } + /** * @throws Throwable */ - public function __invoke(int $draftId, int $targetSiteId): void + public function __invoke(PublishDraftCommand $command): void { + $isCopySlugEnabled = $this->settingsRepository->getBool( + SettingsRepository::COPY_ENTRIES_SLUG_FROM_SOURCE_TO_TARGET + ); + $draftElement = Craft::$app->elements->getElementById( - $draftId, + $command->getDraftId(), null, - $targetSiteId + $command->getTargetSiteId() ); if (!$draftElement) { return; } - // Merge canonical changes for supertable fields in current draft before publishing - if ( - class_exists('verbb\supertable\SuperTable') - || class_exists('benf\neo\Plugin') - ) { - $translation = TranslationRecord::findOne(['translatedDraftId' => $draftId]); - $translations = TranslationRecord::findAll( - [ - 'jobId' => $translation->jobId, - 'status' => TranslationRecord::STATUS_PUBLISHED - ] - ); - - foreach ($translations as $translation) { - $draftElementLanguageToUpdate = Craft::$app->elements->getElementById( - $draftId, - null, - $translation->targetSiteId - ); - $draftElementLanguageToUpdate->mergingCanonicalChanges = true; - - $fieldLayout = $draftElementLanguageToUpdate->getFieldLayout(); - $fields = $fieldLayout ? $fieldLayout->getFields() : []; - foreach ($fields as $field) { - // Check if the field is of Super Table type and the required classes and methods are available - if ( - get_class($field) === CraftliltpluginParameters::CRAFT_FIELDS_SUPER_TABLE - && method_exists('verbb\supertable\SuperTable', 'getService') - && method_exists('verbb\supertable\SuperTable', 'getInstance') - ) { - // Get the Super Table plugin instance - $superTablePluginInstance = call_user_func(['verbb\supertable\SuperTable', 'getInstance']); - - // Get the Super Table plugin service - /** @var \verbb\supertable\services\SuperTableService $superTablePluginService */ - $superTablePluginService = $superTablePluginInstance->getService(); - - // Duplicate the blocks for the field - $superTablePluginService->duplicateBlocks( - $field, - $draftElementLanguageToUpdate->getCanonical(), - $draftElementLanguageToUpdate - ); - - continue; - } + $this->mergeCannonical($command); + Craftliltplugin::getInstance()->createDraftHandler->markFieldsAsChanged( + $draftElement + ); - if ( - get_class($field) === CraftliltpluginParameters::BENF_NEO_FIELD - && class_exists('benf\neo\Plugin') - && method_exists('benf\neo\Plugin', 'getInstance') - ) { - // Get the Neo plugin instance - /** @var \benf\neo\Plugin $neoPluginInstance */ - $neoPluginInstance = call_user_func(['benf\neo\Plugin', 'getInstance']); - - // Get the Neo plugin Fields service - /** @var \benf\neo\services\Fields $neoPluginFieldsService */ - $neoPluginFieldsService = $neoPluginInstance->get('fields'); - - // Clear current neo field value - $neoField = $draftElementLanguageToUpdate->getFieldValue($field->handle); - foreach ($neoField as $block) { - Craft::$app->getElements()->deleteElement($block); - } - Craft::$app->getElements()->saveElement($draftElementLanguageToUpdate); - - // Duplicate the blocks for the field - $neoPluginFieldsService->duplicateBlocks( - $field, - $draftElementLanguageToUpdate->getCanonical(), - $draftElementLanguageToUpdate - ); - - continue; - } - } - } + $attributes = ['title']; + if ($isCopySlugEnabled) { + $attributes[] = 'slug'; } + Craftliltplugin::getInstance()->createDraftHandler->upsertChangedAttributes( + $draftElement, + $attributes + ); $enableEntriesForTargetSitesRecord = SettingRecord::findOne(['name' => 'enable_entries_for_target_sites']); $enableEntriesForTargetSites = (bool)($enableEntriesForTargetSitesRecord->value ?? false); $element = $this->apply($draftElement); - if ($enableEntriesForTargetSites && !$draftElement->getEnabledForSite($targetSiteId)) { - $element->setEnabledForSite([$targetSiteId => true]); + if ($enableEntriesForTargetSites && !$draftElement->getEnabledForSite($command->getTargetSiteId())) { + $element->setEnabledForSite([$command->getTargetSiteId() => true]); } Craft::$app->getElements()->saveElement($element, true, false, false); Craft::$app->getElements()->invalidateCachesForElement($element); + + // finish publishing + $updated = TranslationRecord::updateAll( + ['status' => TranslationRecord::STATUS_PUBLISHED], + ['id' => $command->getTranslationId()] + ); + + Craft::$app->getElements()->invalidateCachesForElementType(Translation::class); + + if ($updated) { + Craftliltplugin::getInstance()->jobLogsRepository->create( + $command->getJobId(), + Craft::$app->getUser()->getId(), + sprintf('Translation (id: %d) published', $command->getTranslationId()) + ); + } } // copied from \craft\controllers\EntryRevisionsController::actionPublishDraft @@ -169,4 +151,122 @@ private function apply(ElementInterface $draft): ElementInterface return $newEntry; } + + /** + * @param PublishDraftCommand $command + * @return void + * @throws Exception + * @throws Throwable + * @throws ElementNotFoundException + * @throws InvalidFieldException + * @throws InvalidConfigException + */ + private function mergeCannonical(PublishDraftCommand $command): void + { + $isAsync = $this->settingsRepository->getBool( + SettingsRepository::PUBLISH_TRANSLATIONS_ASYNC + ); + $isCopySlugEnabled = $this->settingsRepository->getBool( + SettingsRepository::COPY_ENTRIES_SLUG_FROM_SOURCE_TO_TARGET + ); + + $translation = TranslationRecord::findOne(['translatedDraftId' => $command->getDraftId()]); + $translations = TranslationRecord::findAll( + [ + 'jobId' => $translation->jobId, + 'status' => TranslationRecord::STATUS_PUBLISHED + ] + ); + + foreach ($translations as $translation) { + $draftElementLanguageToUpdate = Craft::$app->elements->getElementById( + $command->getDraftId(), + null, + $translation->targetSiteId + ); + $draftElementLanguageToUpdate->mergingCanonicalChanges = true; + + if ($isAsync) { + $this->copyFieldsHandler->copy( + $draftElementLanguageToUpdate->getCanonical(), + $draftElementLanguageToUpdate + ); + + Craftliltplugin::getInstance()->createDraftHandler->markFieldsAsChanged( + $draftElementLanguageToUpdate + ); + + $attributes = ['title']; + if ($isCopySlugEnabled) { + $attributes[] = 'slug'; + } + Craftliltplugin::getInstance()->createDraftHandler->upsertChangedAttributes( + $draftElementLanguageToUpdate, + $attributes + ); + + return; + } + + $fieldLayout = $draftElementLanguageToUpdate->getFieldLayout(); + $fields = $fieldLayout ? $fieldLayout->getFields() : []; + foreach ($fields as $field) { + // Check if the field is of Super Table type and the required classes and methods are available + if ( + get_class($field) === CraftliltpluginParameters::CRAFT_FIELDS_SUPER_TABLE + && method_exists('verbb\supertable\SuperTable', 'getService') + && method_exists('verbb\supertable\SuperTable', 'getInstance') + ) { + // Get the Super Table plugin instance + $superTablePluginInstance = call_user_func(['verbb\supertable\SuperTable', 'getInstance']); + + // Get the Super Table plugin service + /** @var \verbb\supertable\services\SuperTableService $superTablePluginService */ + $superTablePluginService = $superTablePluginInstance->getService(); + + // Duplicate the blocks for the field + $superTablePluginService->duplicateBlocks( + $field, + $draftElementLanguageToUpdate->getCanonical(), + $draftElementLanguageToUpdate + ); + + continue; + } + + if ( + get_class($field) === CraftliltpluginParameters::BENF_NEO_FIELD + && class_exists('benf\neo\Plugin') + && method_exists('benf\neo\Plugin', 'getInstance') + ) { + // Get the Neo plugin instance + /** @var \benf\neo\Plugin $neoPluginInstance */ + $neoPluginInstance = call_user_func(['benf\neo\Plugin', 'getInstance']); + + // Get the Neo plugin Fields service + /** @var \benf\neo\services\Fields $neoPluginFieldsService */ + $neoPluginFieldsService = $neoPluginInstance->get('fields'); + + // Clear current neo field value + $neoField = $draftElementLanguageToUpdate->getFieldValue($field->handle); + foreach ($neoField as $block) { + Craft::$app->getElements()->deleteElement($block); + } + Craft::$app->getElements()->saveElement($draftElementLanguageToUpdate); + + // Duplicate the blocks for the field + $neoPluginFieldsService->duplicateBlocks( + $field, + $draftElementLanguageToUpdate->getCanonical(), + $draftElementLanguageToUpdate + ); + + continue; + } + } + + Craft::$app->getElements()->saveElement($draftElementLanguageToUpdate); + Craft::$app->getElements()->invalidateCachesForElement($draftElementLanguageToUpdate); + } + } } diff --git a/src/services/handlers/PublishDraftHandlerInterface.php b/src/services/handlers/PublishDraftHandlerInterface.php new file mode 100644 index 00000000..fd57708d --- /dev/null +++ b/src/services/handlers/PublishDraftHandlerInterface.php @@ -0,0 +1,12 @@ + $jobId]); if (!$jobRecord) { - return; + return false; } $translations = Craftliltplugin::getInstance()->translationRepository->findByJobId($jobId); @@ -34,7 +34,7 @@ public function __invoke(int $jobId): void }, $translations) ); - if ($uniqueStatuses === [TranslationRecord::STATUS_PUBLISHED]) { + if ($uniqueStatuses === [TranslationRecord::STATUS_PUBLISHED] && $jobRecord->status != Job::STATUS_COMPLETE) { $jobRecord->status = Job::STATUS_COMPLETE; $jobRecord->save(); @@ -43,9 +43,32 @@ public function __invoke(int $jobId): void Craft::$app->getUser()->getId(), 'Job published' ); + + Craft::$app->elements->invalidateCachesForElementType( + Job::class + ); + + return true; + } + + if ( + $uniqueStatuses === [TranslationRecord::STATUS_PUBLISHING] + && $jobRecord->status != Job::STATUS_PUBLISHING + ) { + $jobRecord->status = Job::STATUS_PUBLISHING; + $jobRecord->save(); + + Craft::$app->elements->invalidateCachesForElementType( + Job::class + ); + + return true; } - if ($uniqueStatuses === [TranslationRecord::STATUS_READY_TO_PUBLISH]) { + if ( + $uniqueStatuses === [TranslationRecord::STATUS_READY_TO_PUBLISH] + && $jobRecord->status != Job::STATUS_READY_TO_PUBLISH + ) { $jobRecord->status = Job::STATUS_READY_TO_PUBLISH; $jobRecord->save(); @@ -54,10 +77,15 @@ public function __invoke(int $jobId): void Craft::$app->getUser()->getId(), 'Job reviewed' ); + + Craft::$app->elements->invalidateCachesForElementType( + Job::class + ); + + return true; } - Craft::$app->elements->invalidateCachesForElementType( - Job::class - ); + + return false; } } diff --git a/src/services/handlers/commands/PublishDraftCommand.php b/src/services/handlers/commands/PublishDraftCommand.php new file mode 100644 index 00000000..0264baed --- /dev/null +++ b/src/services/handlers/commands/PublishDraftCommand.php @@ -0,0 +1,65 @@ +draftId = $draftId; + $this->targetSiteId = $targetSiteId; + $this->jobId = $jobId; + $this->translationId = $translationId; + } + + public function getDraftId(): int + { + return $this->draftId; + } + + public function getTargetSiteId(): int + { + return $this->targetSiteId; + } + + public function getJobId(): int + { + return $this->jobId; + } + + public function getTranslationId(): int + { + return $this->translationId; + } +} diff --git a/src/services/listeners/AfterErrorListener.php b/src/services/listeners/AfterErrorListener.php index ed68a432..ff3c0235 100644 --- a/src/services/listeners/AfterErrorListener.php +++ b/src/services/listeners/AfterErrorListener.php @@ -19,6 +19,7 @@ use lilthq\craftliltplugin\modules\FetchJobStatusFromConnector; use lilthq\craftliltplugin\modules\FetchTranslationFromConnector; use lilthq\craftliltplugin\modules\FetchVerifiedJobTranslationsFromConnector; +use lilthq\craftliltplugin\modules\PublishTranslation; use lilthq\craftliltplugin\modules\SendJobToConnector; use lilthq\craftliltplugin\modules\SendTranslationToConnector; use lilthq\craftliltplugin\records\JobRecord; @@ -35,6 +36,7 @@ class AfterErrorListener implements ListenerInterface FetchTranslationFromConnector::class, SendJobToConnector::class, SendTranslationToConnector::class, + PublishTranslation::class, ]; public function register(): void diff --git a/src/services/repositories/JobRepository.php b/src/services/repositories/JobRepository.php index f634ec60..374c794f 100644 --- a/src/services/repositories/JobRepository.php +++ b/src/services/repositories/JobRepository.php @@ -27,6 +27,15 @@ public function findByIds(array $ids): array return Job::findAll(['id' => $ids]); } + public function updateJobStatusById(int $id, string $status): bool + { + return JobRecord::updateAll([ + "status" => $status, + ], [ + "id" => $id + ]) > 0; + } + public function saveJob(Job $job): bool { $jobRecord = new JobRecord(); diff --git a/src/services/repositories/SettingsRepository.php b/src/services/repositories/SettingsRepository.php index cb49ffa7..2ff36895 100644 --- a/src/services/repositories/SettingsRepository.php +++ b/src/services/repositories/SettingsRepository.php @@ -20,6 +20,7 @@ class SettingsRepository public const QUEUE_EACH_TRANSLATION_FILE_SEPARATELY = 'queue_each_translation_file_separately'; public const QUEUE_DISABLE_AUTOMATIC_SYNC = 'queue_disable_automatic_sync'; public const QUEUE_MANAGER_EXECUTED_AT = 'queue_manager_executed_at'; + public const PUBLISH_TRANSLATIONS_ASYNC = 'publish_translations_async'; public const IGNORE_DROPDOWNS = 'ignore_dropdowns'; @@ -56,6 +57,19 @@ public function isQueueEachTranslationFileSeparately(): bool return (bool)$settingValue->value; } + public function getBool(string $name): bool + { + $tableSchema = Craft::$app->getDb()->schema->getTableSchema(CraftliltpluginParameters::SETTINGS_TABLE_NAME); + if ($tableSchema === null) { + return false; + } + + $queueDisableAutomaticSync = SettingRecord::findOne( + ['name' => $name] + ); + return (bool) ($queueDisableAutomaticSync->value ?? false); + } + public function get(string $name): ?string { $tableSchema = Craft::$app->getDb()->schema->getTableSchema(CraftliltpluginParameters::SETTINGS_TABLE_NAME); diff --git a/src/services/repositories/TranslationRepository.php b/src/services/repositories/TranslationRepository.php index 7e5dcc61..f8347036 100644 --- a/src/services/repositories/TranslationRepository.php +++ b/src/services/repositories/TranslationRepository.php @@ -203,4 +203,13 @@ public function findOneById(int $id): ?TranslationModel $translationRecord->toArray() ); } + + public function updateTranslationStatusById(int $id, string $status): bool + { + return TranslationRecord::updateAll([ + "status" => $status, + ], [ + "id" => $id + ]) > 0; + } } diff --git a/src/templates/_components/translation/_elements.twig b/src/templates/_components/translation/_elements.twig index c2fc79c0..2560ae68 100644 --- a/src/templates/_components/translation/_elements.twig +++ b/src/templates/_components/translation/_elements.twig @@ -1,6 +1,7 @@ {% import '_includes/forms' as forms %} {% set isJobInProgress = (constant('STATUS_IN_PROGRESS', element) is same as element.getStatus()) %} +{% set isJobPublishing = (constant('STATUS_PUBLISHING', element) is same as element.getStatus()) %}