diff --git a/changelog.md b/changelog.md
index 82de393..f799e99 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,12 @@
# Changelog
+## v2.1.4
+
+- Added timestamp to file export.
+ - As requested in [#30](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/30)
+- Re-added "Save as JSON" which was removed in v2.1.2 as requested in [#29](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/29)
+ - Have renamed this to `Copy as JSON` to help differentiate it from the `Export Settings` option.
+
## v2.1.3
- Fixed issue where having a player other than GM in the world would prevent the importer from correctly importing anything.
diff --git a/languages/de.json b/languages/de.json
index 8006adf..79ad8b6 100644
--- a/languages/de.json
+++ b/languages/de.json
@@ -2,7 +2,7 @@
"forien-copy-environment": {
"menu": {
"copy": "Als Text kopieren",
- "save": "Als JSON abspeichern",
+ "save": "Als JSON kopieren",
"export": "Einstellungen exportieren",
"import": "Einstellungen importieren"
},
diff --git a/languages/en.json b/languages/en.json
index cba463b..de1ab0f 100644
--- a/languages/en.json
+++ b/languages/en.json
@@ -2,7 +2,7 @@
"forien-copy-environment": {
"menu": {
"copy": "Copy as text",
- "save": "Save as JSON",
+ "save": "Copy as JSON",
"export": "Export Settings",
"import": "Import Settings"
},
diff --git a/languages/ja.json b/languages/ja.json
index 152d970..9476daf 100644
--- a/languages/ja.json
+++ b/languages/ja.json
@@ -2,7 +2,7 @@
"forien-copy-environment": {
"menu": {
"copy": "クリップボードにコピー",
- "save": "JSONに保存",
+ "save": "JSONとしてコピー",
"export": "設定のエクスポート",
"import": "設定のインポート"
},
@@ -12,6 +12,7 @@
"copiedToClipboard": "環境データをクリップボードにコピーしました!",
"updatedReloading": "ワールド設定を更新しました。5秒後に再読み込みします……",
"import": {
+ "title": "ワールドの設定",
"save": "インポート設定",
"playerList": "次のプレイヤー設定をインポートします:",
"existing": "既存のエクスポートをインポートします:",
@@ -22,7 +23,8 @@
"existingValue": "次の値は変更がないためスキップされます:",
"existingPlayerValues": "次のプレイヤーは変更がないためスキップされます:",
"updatedPlayer": "次のプレイヤーの設定を更新しました:{name}",
- "noChanges": "現在のワールド設定とインポートした設定に差異がありません。"
+ "noChanges": "現在のワールド設定とインポートした設定に差異がありません。",
+ "showSettings": "{count} の設定を表示"
}
}
-}
\ No newline at end of file
+}
diff --git a/module.json b/module.json
index 3f59603..c564209 100644
--- a/module.json
+++ b/module.json
@@ -1,4 +1,5 @@
{
+ "id": "forien-copy-environment",
"name": "forien-copy-environment",
"title": "Forien's Copy Environment",
"description": "Allows for copying list of system/modules and versions, and gives ability to export/import game and player settings",
@@ -21,9 +22,13 @@
"flags": {
"allowBugReporter": true
},
- "version": "2.1.3",
+ "version": "2.1.4",
"minimumCoreVersion": "0.6.0",
"compatibleCoreVersion": "10",
+ "compatibility": {
+ "minimum": "0.6.0",
+ "verified": "10.270"
+ },
"scripts": [],
"esmodules": [
"/scripts/module.js"
diff --git a/scripts/core.js b/scripts/core.js
index f8f0d0d..62c03b1 100755
--- a/scripts/core.js
+++ b/scripts/core.js
@@ -302,7 +302,7 @@ export default class Core extends FormApplication {
return false;
}
- if (Object.keys(changes).length === 1 && isObjectEmpty(changes.flags)) {
+ if (Object.keys(changes).length === 1 && (typeof isEmpty === 'function' ? isEmpty(changes.flags) : isObjectEmpty(changes.flags))) {
log(true, 'No changes selected for', targetUser?.name);
return false;
}
@@ -331,16 +331,6 @@ export default class Core extends FormApplication {
}
static getText() {
- const modules = (isV10orNewer() ? game.modules : game.data.modules).map(m => {
- let mod;
- if (isV10orNewer()) {
- mod = m.toObject();
- } else {
- mod = m.data.toObject();
- }
- mod.active = m.active;
- return mod;
- }).filter((m) => m.active);
const system = isV10orNewer() ? game.data.system : game.data.system.data;
const core = game.version || game.data.version;
@@ -355,7 +345,7 @@ export default class Core extends FormApplication {
text += `System: ${(system.id ?? system.name)} ${system.version} (${Array.from(new Set(systemAuthors)).join(', ')}) \n\n`;
text += `Modules: \n`;
- modules.forEach((m) => {
+ Core.getModulesForExport().forEach((m) => {
const moduleAuthors = m.authors.length ? m.authors.map(a => {
if (typeof a === 'string') {
return a;
@@ -391,6 +381,56 @@ export default class Core extends FormApplication {
);
}
+ static getModulesForExport() {
+ return (isV10orNewer() ? game.modules : game.data.modules).map(m => {
+ let mod;
+ if (isV10orNewer()) {
+ mod = m.toObject();
+ } else {
+ mod = m.data.toObject();
+ }
+ mod.active = m.active;
+ return mod;
+ }).filter((m) => m.active);
+ }
+
+ static saveSummaryAsJSON() {
+ const system = isV10orNewer() ? game.data.system : game.data.system.data;
+ const systemAuthors = system.authors.length ? system.authors.map(a => {
+ if (typeof a === 'string') {
+ return a;
+ }
+ return a.name;
+ }) : [system.author];
+
+ const data = {};
+ data.core = {
+ version: game.version || game.data.version,
+ };
+ data.system = {
+ id: system.id,
+ version: system.version,
+ author: Array.from(new Set(systemAuthors)).join(', '),
+ manifest: system.manifest,
+ };
+ data.modules = Core.getModulesForExport().map((m) => {
+ const moduleAuthors = m.authors.length ? m.authors.map(a => {
+ if (typeof a === 'string') {
+ return a;
+ }
+ return a.name;
+ }) : [m.author];
+ return {
+ id: m.id || m.name,
+ version: m.version,
+ author: Array.from(new Set(moduleAuthors)).join(', '),
+ manifest: m.manifest,
+ };
+ });
+
+ this.download(data, Core.getFilename('foundry-environment'));
+ }
+
static exportGameSettings() {
const excludeModules = game.data.modules.filter((m) => m.flags?.noCopyEnvironmentSettings || m.data?.flags?.noCopyEnvironmentSettings).map((m) => m.id) || [];
@@ -429,7 +469,22 @@ export default class Core extends FormApplication {
};
}),
);
- this.download(data, 'foundry-settings-export.json');
+ this.download(data, Core.getFilename('foundry-settings-export'));
+ }
+
+ static padNumber(number) {
+ return (number < 10 ? '0' : '') + number;
+ }
+
+ static getFilename(filename) {
+ const now = new Date();
+ const yyyy = now.getFullYear();
+ const MM = Core.padNumber(now.getMonth() + 1); // getMonth() is zero-based
+ const dd = Core.padNumber(now.getDate());
+ const hh = Core.padNumber(now.getHours());
+ const mm = Core.padNumber(now.getMinutes());
+ const ss = Core.padNumber(now.getSeconds());
+ return `${filename}-${yyyy}-${MM}-${dd}-${hh}-${mm}-${ss}.json`;
}
static importGameSettingsQuick() {
diff --git a/scripts/module.js b/scripts/module.js
index 2769335..f3e420a 100644
--- a/scripts/module.js
+++ b/scripts/module.js
@@ -1,12 +1,12 @@
import {name} from './config.js';
import Core from './core.js';
-Hooks.once('init', function(){
+Hooks.once('init', function () {
game.settings.register(name, 'selected-properties', {
scope: 'client',
config: false,
type: Object,
- default:{}
+ default: {},
});
});
@@ -28,6 +28,17 @@ Hooks.on('renderSettings', function (app, html, data) {
}
},
},
+ {
+ name: game.i18n.localize('forien-copy-environment.menu.save'),
+ icon: '',
+ callback: () => {
+ try {
+ Core.saveSummaryAsJSON();
+ } catch (e) {
+ console.error('Copy Environment | Error copying game settings to JSON', e);
+ }
+ },
+ },
{
name: game.i18n.localize('forien-copy-environment.menu.export'),
icon: '',
diff --git a/scripts/setting.js b/scripts/setting.js
index 3a1b8c6..a7643b9 100755
--- a/scripts/setting.js
+++ b/scripts/setting.js
@@ -88,7 +88,7 @@ export class WorldSetting {
}
if (typeof existingSetting === 'object' && typeof newValue === 'object') {
let diff = diffObject(existingSetting, newValue);
- if (isObjectEmpty(diff)) {
+ if (typeof isEmpty === 'function' ? isEmpty(diff) : isObjectEmpty(diff)) {
// No difference in the underlying object.
return new Difference(this.key, null, null);
}
@@ -190,6 +190,10 @@ export class PlayerSetting {
* @returns boolean
*/
hasDataChanges() {
+ if (typeof isEmpty === 'function') {
+ return !isEmpty(this.playerDifferences) || !isEmpty(this.playerFlagDifferences);
+ }
+
return (
!isObjectEmpty(this.playerDifferences) ||
!isObjectEmpty(this.playerFlagDifferences)