Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle different locale #48

Merged
merged 19 commits into from
Nov 28, 2023
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## UNRELEASED

### Adds

* TODO:

### Changes

* Hide "Duplicates Detected." notification.
Expand Down
2 changes: 2 additions & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"exporting": "Exporting {{ type }}...",
"import": "Import {{ type }}",
"importCleanFailed": "The cleaning of the imported file on server failed: {{ exportPath }}",
"importWithCurrentLocaleHeading": "Different locale detected",
"importWithCurrentLocaleDescription": "This file was exported from the \"{{ foundLocale }}\" locale. Are you sure you want to import it into the \"{{ currentLocale }}\" locale?",
"importDuplicateContinue": "Continue Import",
"importDuplicateDetected": "Duplicates Detected.",
"importDuplicateMessage": "Check the items you'd like to overwrite during import.",
Expand Down
103 changes: 92 additions & 11 deletions lib/methods/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,63 @@ const { EJSON } = require('bson');
module.exports = self => {
return {
async import(req, moduleName) {
const {
docs, attachmentsInfo, exportPath, filePath
} = await self.readExportFile(req);
if (!req.user) {
throw self.apos.error('forbidden');
}

const overrideLocale = self.apos.launder.boolean(req.body.overrideLocale);
let exportPath = self.apos.launder.string(req.body.exportPath);
let filePath = self.apos.launder.string(req.body.filePath);

let docs;
let attachmentsInfo;

if (exportPath) {
({
docs, attachmentsInfo
} = await self.getFilesData(exportPath));
} else {
({
docs, attachmentsInfo, exportPath, filePath
} = await self.readExportFile(req));
}

const siteHasMultipleLocales = Object.keys(self.apos.i18n.locales).length > 1;
const differentLocaleFoundInDocs = self.getFirstDifferentLocale(req, docs);

if (siteHasMultipleLocales && differentLocaleFoundInDocs) {
if (!overrideLocale) {
// Display confirmation modal to ask the user if he wants to
// continue the import with the current locale overriding the docs one:
await self.apos.notify(req, ' ', {
type: 'warning',
event: {
name: 'import-locale-differs',
data: {
moduleName,
exportPath,
filePath,
content: {
heading: req.t('aposImportExport:importWithCurrentLocaleHeading'),
description: req.t('aposImportExport:importWithCurrentLocaleDescription', {
foundLocale: differentLocaleFoundInDocs,
currentLocale: req.locale
}),
negativeLabel: 'apostrophe:no',
affirmativeLabel: 'apostrophe:yes'
}
}
},
classes: [ 'apos-notification--hidden' ]
});

return;
}

// The user decided to continue the import (`overrideLocale` param sent)
self.rewriteDocsWithCurrentLocale(req, docs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this method is called twice, another time with the confirmation to change locale?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is called only during the 2nd call (when the user chooses Yes), see the early return above

}

const total = docs.length + attachmentsInfo.length;
const {
reporting, jobId, notificationId
Expand All @@ -21,6 +71,7 @@ module.exports = self => {
const {
duplicatedDocs, duplicatedIds, failedIds
} = await self.insertDocs(req, docs, reporting);

const importedAttachments = await self.insertAttachments(req, {
attachmentsInfo,
reporting,
Expand Down Expand Up @@ -180,6 +231,36 @@ module.exports = self => {
};
},

getFirstDifferentLocale(req, docs) {
const doc = docs
.find(doc => self.isLocaleDifferent(req, doc));

return doc && self.stripOutMode(doc.aposLocale);
},

rewriteDocsWithCurrentLocale(req, docs) {
for (const doc of docs) {
if (self.isLocaleDifferent(req, doc)) {
self.replaceLocale(req, doc);
}
}
},

isLocaleDifferent(req, doc) {
return doc.aposLocale && self.stripOutMode(doc.aposLocale) !== req.locale;
},

replaceLocale(req, doc) {
const [ id, , mode ] = doc._id.split(':');

doc._id = [ id, req.locale, mode ].join(':');
doc.aposLocale = [ req.locale, mode ].join(':');
},

stripOutMode(aposLocale) {
return aposLocale.split(':').at(0);
},

async insertDocs(req, docs, reporting) {
const duplicatedDocs = [];
const duplicatedIds = [];
Expand Down Expand Up @@ -428,15 +509,15 @@ module.exports = self => {
const jobId = self.apos.launder.string(req.body.jobId);
const notificationId = self.apos.launder.string(req.body.notificationId);

const jobManager = self.apos.modules['@apostrophecms/job'];
const job = await jobManager.db.findOne({ _id: jobId });

jobManager.end(job, true);
if (jobId) {
const jobManager = self.apos.modules['@apostrophecms/job'];
const job = await jobManager.db.findOne({ _id: jobId });
jobManager.end(job, true);
}
if (notificationId) {
self.apos.notification.dismiss(req, notificationId, 2000).catch(self.apos.util.error);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clean job and notification only if existing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see /api/v1/@apostrophecms/import-export/clean-export call in app.js below, no job nor notification is instantiated at this step, but we still want to remove the export path


self.apos.notification.dismiss(req, notificationId, 2000)
.catch((error) => {
self.apos.util.error(error);
});
await self.cleanFile(exportPath);
}
};
Expand Down
42 changes: 42 additions & 0 deletions ui/apos/apps/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default () => {
apos.bus.$on('export-download', openUrl);
apos.bus.$on('import-started', addBeforeUnloadListener);
apos.bus.$on('import-ended', removeBeforeUnloadListener);
apos.bus.$on('import-locale-differs', handleDifferentLocale);
apos.bus.$on('import-duplicates', handleDuplicates);
}
});
Expand All @@ -25,6 +26,47 @@ export default () => {
window.removeEventListener('beforeunload', warningImport);
}

async function handleDifferentLocale(event) {
const continueImport = await apos.modal.execute('AposModalConfirm', event);

if (continueImport) {
try {
const moduleAction = apos.modules[event.moduleName].action;

await apos.http.post(`${moduleAction}/import`, {
body: {
exportPath: event.exportPath,
filePath: event.filePath,
overrideLocale: true
}
});
} catch (error) {
apos.notify(this.$t('aposImportExport:importFailed'), {
type: 'danger',
dismiss: true
});
}

return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we agree that in this case the export file is cleaned from the import route directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it should be done at line 107, as in a "normal" case where no locale overriding is involved

}

// If not, we still need to clean the uploaded archive
try {
await apos.http.post('/api/v1/@apostrophecms/import-export/clean-export', {
body: {
exportPath: event.exportPath
}
});
} catch (error) {
apos.notify(this.$t('aposImportExport:importCleanFailed'), {
type: 'warning',
interpolate: {
exportPath: event.exportPath
}
});
}
}

async function handleDuplicates(event) {
if (event.duplicatedDocs.length) {
await apos.modal.execute('AposDuplicateImportModal', event);
Expand Down