zischenstand
This commit is contained in:
72
src/modules/forien-copy-environment/README.md
Normal file
72
src/modules/forien-copy-environment/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# FoundryVTT - Forien's Copy Environment
|
||||
|
||||
     
|
||||
|
||||
|
||||
|
||||
**NOTE** This is an unofficial forked version of the module maintained by the League of Foundry Developers to provide module continuity while Forien is unavailable.
|
||||
|
||||
**[Compatibility]**: *FoundryVTT* 0.6.0 - 12.0+
|
||||
|
||||
**[Systems]**: *any*
|
||||
|
||||
This module allows for fast copy/save environment data such as core version or list of installed modules and their versions. Supports copying as TXT or saving as JSON.
|
||||
|
||||
Module also allows to export (save/backup) current game settings and then import (restore) them. Non-GM users can only import client-side settings.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Install Forien's Copy Environment using manifest URL: https://raw.githubusercontent.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/master/module.json
|
||||
2. While loaded in World, enable **_Forien's Copy Environment_** module.
|
||||
|
||||
### Usage
|
||||
|
||||
Go to Settings tab in Sidebar and **right click** on data **below** "General Information" header
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
* Copy Environment (core, system and module versions) to clipboard
|
||||
* Save Environment (including manifest links) as a JSON file
|
||||
* Export game settings (both 'world' and 'client' scopes)
|
||||
* Import game settings ('client' ones, and if you are GM also 'world' ones - you will be able to choose which ones you want to import)
|
||||
|
||||
*Please note that importing 'world' scope settings en masse as GM might cause some issues to connected players. I advise players should logout before attempting to import World Settings*
|
||||
|
||||
## Info for Module Developers
|
||||
|
||||
### How do I opt out?
|
||||
|
||||
Perhaps you have a module that you don't want the settings being copied between worlds. You can add the following to your module manifest file to opt out of having the settings copied. The `active` state of your module will still be copied, just the settings won't.
|
||||
|
||||
1. Add `noCopyEnvironmentSettings: true` to your manifest json inside of the `flags` field of the manifest.
|
||||
|
||||
module.json
|
||||
```md
|
||||
"flags": {
|
||||
"noCopyEnvironmentSettings": true
|
||||
}
|
||||
```
|
||||
|
||||
## Contact
|
||||
|
||||
[League of Foundry Developers](https://discord.gg/gzemMfHURH) ~~If you wish to contact me for any reason, reach me out on Discord using my tag: `Forien#2130`~~
|
||||
|
||||
## Translations
|
||||
|
||||
- Japanese by touge
|
||||
- German by brockhaus
|
||||
- Portuguese by vithort
|
||||
- Italian by GregoryWarn
|
||||
- French by rectulo
|
||||
|
||||
## Support
|
||||
|
||||
If you wish to support module development, please consider [becoming Patron](https://www.patreon.com/foundryworkshop) or donating [through Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=6P2RRX7HVEMV2&source=url). Thanks!
|
||||
|
||||
## License
|
||||
|
||||
Forien's Copy Environment is a module for Foundry VTT by Forien and is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/).
|
||||
|
||||
This work is licensed under Foundry Virtual Tabletop [EULA - Limited License Agreement for module development from May 29, 2020](https://foundryvtt.com/article/license/).
|
||||
157
src/modules/forien-copy-environment/changelog.md
Normal file
157
src/modules/forien-copy-environment/changelog.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Changelog
|
||||
|
||||
## v2.2.4
|
||||
|
||||
- Added Polish translation by Lioheart.
|
||||
- Enabled Italian and French translations.
|
||||
|
||||
## v2.2.3
|
||||
|
||||
- Made it clearer when modules will not be enabled due to the selected config settings.
|
||||
- This is one of the primary use cases of this module, so most of the time it is an accident to uncheck the setting.
|
||||
- See [issue #49](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/49).
|
||||
- Added Italian translation by GregoryWarn.
|
||||
- Added French translation by rectulo.
|
||||
- Added machine translations for the new setting strings:
|
||||
- German
|
||||
- Japanese
|
||||
- Portuguese
|
||||
- Italian
|
||||
- French
|
||||
|
||||
## v2.2.2
|
||||
|
||||
- Ignore "core.time" and "pf2e.worldClock.worldCreatedOn" values by default.
|
||||
- These values can still be selected in from the importer dialog but will be unselected by default.
|
||||
- You may run `game.settings.set('forien-copy-environment', 'selected-properties', undefined);` as a script macro to reset the values to their default.
|
||||
- See [issue #53](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/53).
|
||||
|
||||
## v2.2.1
|
||||
|
||||
- v12 compatibility.
|
||||
|
||||
## v2.2.0
|
||||
|
||||
- Re-added "core.compendiumConfiguration" setting to exports to export Compendium Folder structure mappings.
|
||||
- Settings will try to map the folder IDs to the new world's folder IDs based on the compendium key.
|
||||
- Added supporting folder data structure to the export.
|
||||
- Settings will utilise the supporting folder data structure to re-create the folders where it can.
|
||||
- **Important note**: any exports prior to v2.2.0 won't have this support folder data included in the exported file so might not map correctly on the new world. *Please export your world settings again.*
|
||||
- See [issue #45](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/45).
|
||||
|
||||
## v2.1.9
|
||||
|
||||
- Added a module setting to set the maximum number of characters to display when displaying differences.
|
||||
- This is to help with the issue where a "maximum call stack size exceeded" error may occur with very large export files (see [issue #43](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/43)).
|
||||
- The default is 500 characters per difference.
|
||||
- Added machine translations for the new setting strings:
|
||||
- German
|
||||
- Japanese
|
||||
- Portuguese
|
||||
|
||||
## v2.1.8
|
||||
|
||||
- Exclude "core.compendiumConfiguration" setting from exports due to the folder ID mapping not being consistent across worlds anyway.
|
||||
|
||||
## v2.1.7
|
||||
|
||||
- Mark compatible with v11.
|
||||
- Append world name to the generated settings file on export.
|
||||
- Added Portuguese by vithort
|
||||
|
||||
## v2.1.6
|
||||
|
||||
- Exclude invalid settings from export [#35](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/35)
|
||||
|
||||
## v2.1.5
|
||||
|
||||
- Allow non-GM users to use the module.
|
||||
- Fixes [#32](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/32)
|
||||
- Users will see errors for any settings they do not have permission to update.
|
||||
- Removed deprecated fields in module manifest.
|
||||
- Fixes [#33](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/33)
|
||||
|
||||
## 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.
|
||||
|
||||
## v2.1.2
|
||||
|
||||
* Better handling of config options to make it less likely to break on certain module's configuration settings.
|
||||
* Added more logging to assist with any future issues.
|
||||
* Adjust process order to make sure that the server has time to process the updates before the client reloads.
|
||||
* Removed the `Save as JSON` context menu option as it was rarely the desired action and was confusing (it was not the version that allowed the user to import the settings).
|
||||
|
||||
### v2.1.1
|
||||
|
||||
* Fixed settings stored in an object not being compared correctly.
|
||||
* Importantly, this makes the "Enabled Modules" setting to be correctly saved and imported.
|
||||
|
||||
### v2.1.0
|
||||
|
||||
* Added compatibility with Foundry VTT v10
|
||||
* Sorted the settings in the import dialog to make them easier to find.
|
||||
* Client settings are now correctly exported and imported.
|
||||
* Request: [#10](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/10)
|
||||
* Grouped the settings by their module and collapsed them by default.
|
||||
* Request: [#13](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/13)
|
||||
* Selecting (or unselecting) a setting will now remember that state across imports, saving you time from choosing just the selections you want.
|
||||
* Request: [#18](https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues/18)
|
||||
|
||||
### v2.0.7
|
||||
|
||||
* Added German translation thanks to brockhaus
|
||||
* Added Foundry VTT Core v9 compatibility
|
||||
|
||||
### v2.0.6
|
||||
|
||||
* Added Japanese translation thanks to touge
|
||||
|
||||
### v2.0.5
|
||||
|
||||
* Fix client side settings not importing in v0.8.x
|
||||
|
||||
### v2.0.4
|
||||
|
||||
* Allow module developers to opt out of settings being copied.
|
||||
* Re-added bug reporter compatibility since 0.8.2.
|
||||
|
||||
### v2.0.3
|
||||
|
||||
* Added 0.8.4 compatibility
|
||||
|
||||
### v2.0.2
|
||||
|
||||
* Tested 0.8.1 compatibility
|
||||
|
||||
### v2.0.1
|
||||
|
||||
* Merged world and player settings into a single json to simplify use.
|
||||
* Added differential style selection for world settings import.
|
||||
* Finished adding localization support.
|
||||
|
||||
### v2.0.0
|
||||
|
||||
* Migrated to [League of Foundry Developers](https://discord.gg/gzemMfHURH) stewardship
|
||||
* Added Settings configuration in addition to the right-click context menu
|
||||
* Added export/import of player settings including differential based selection
|
||||
* Tested and bumped compatible core version to 0.8.0
|
||||
|
||||
### v1.1.1
|
||||
|
||||
* Added 0.6.6 compatibility
|
||||
|
||||
## v1.1.0
|
||||
* Added manifest URL link to JSON environment structure
|
||||
* Added options to export and import game settings. Non-GM users can only import client-side settings.
|
||||
* Tested and bumped compatible core version to 0.7.1
|
||||
|
||||
### v1.0.1
|
||||
* Initial release
|
||||
36
src/modules/forien-copy-environment/languages/de.json
Normal file
36
src/modules/forien-copy-environment/languages/de.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"forien-copy-environment": {
|
||||
"menu": {
|
||||
"copy": "Als Text kopieren",
|
||||
"save": "Als JSON kopieren",
|
||||
"export": "Einstellungen exportieren",
|
||||
"import": "Einstellungen importieren"
|
||||
},
|
||||
"settings": {
|
||||
"max-diff": "Anzahl von Charakteren",
|
||||
"max-diff-hint": "Maximale Anzahl der pro Unterschied anzuzeigenden Zeichen. Auf 0 setzen, um alle anzuzeigen."
|
||||
},
|
||||
"title": "Einstellungen importieren",
|
||||
"intro": "Bitte wähle aus, welche Welt- und Spieler-Einstellungen Du importieren möchtest.",
|
||||
"message": "Die Liste wurde generiert mit Forien's Copy Environment: https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment",
|
||||
"copiedToClipboard": "System Daten wurden in die Zwischenablage kopiert.",
|
||||
"updatedReloading": "Einstellungen der Welt erneuert. Lade die Welt in 5 Sekunden neu...",
|
||||
"import": {
|
||||
"title": "Welt Einstellungen",
|
||||
"save": "Import Einstellungen",
|
||||
"playerList": "Importiere die Einstellungen der folgenden Spieler:",
|
||||
"existing": "Importiere einen existierenden Export:",
|
||||
"property": "Eigenschaft",
|
||||
"newValue": "Neuer Wert",
|
||||
"currentValue": "Aktueller Wert",
|
||||
"notFound": "Die folgenden Spieler in der Import Datei existieren nicht in dieser Welt und werden ausgelassen.",
|
||||
"existingValue": "Bereits existierende Einstellungen, die unverändert sind und deshalb ausgelassen werden:",
|
||||
"existingPlayerValues": "Bereits existierende Spieler, die unverändert sind und deshalb ausgelassen werden:",
|
||||
"updatedPlayer": "Spieler Einstellungen angepasst für: {name}",
|
||||
"noChanges": "Es gibt keinen Unterschied zwischen der aktuellen Welt und den zu importierenden Einstellungen.",
|
||||
"showSettings": "{count} Einstellungen anzeigen",
|
||||
"warning": "Warnung",
|
||||
"warningMessage": "Sie haben sich entschieden, die ausgewählten Module nicht zu importieren. Falls dies unbeabsichtigt war, aktivieren Sie den oben stehenden Wert \"core.moduleConfiguration\""
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/modules/forien-copy-environment/languages/en.json
Normal file
36
src/modules/forien-copy-environment/languages/en.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"forien-copy-environment": {
|
||||
"menu": {
|
||||
"copy": "Copy as text",
|
||||
"save": "Copy as JSON",
|
||||
"export": "Export Settings",
|
||||
"import": "Import Settings"
|
||||
},
|
||||
"settings": {
|
||||
"max-diff": "Number of characters",
|
||||
"max-diff-hint": "Maximum number of characters to show per difference. Set to 0 to show all."
|
||||
},
|
||||
"title": "Import Settings",
|
||||
"intro": "This form allows you to select which world and player settings you want to import.",
|
||||
"message": "List generated with Forien's Copy Environment: https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment",
|
||||
"copiedToClipboard": "Environment data copied to clipboard!",
|
||||
"updatedReloading": "Updated world settings. Reloading world in 5sec...",
|
||||
"import": {
|
||||
"title": "World settings",
|
||||
"save": "Import settings",
|
||||
"playerList": "Import settings for the following players:",
|
||||
"existing": "Import an existing Export:",
|
||||
"property": "Property",
|
||||
"newValue": "New Value",
|
||||
"currentValue": "Current Value",
|
||||
"notFound": "The following users in the import file do not exist in this world and will be skipped.",
|
||||
"existingValue": "Existing values that are unchanged and will be skipped:",
|
||||
"existingPlayerValues": "Existing players that are unchanged and will be skipped:",
|
||||
"updatedPlayer": "Updated player settings for: {name}",
|
||||
"noChanges": "There are no differences between the current world and the imported settings.",
|
||||
"showSettings": "Show {count} settings",
|
||||
"warning": "Warning",
|
||||
"warningMessage": "You have chosen not to import the selected modules. If this was unintentional, enable the \"core.moduleConfiguration\" value above."
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/modules/forien-copy-environment/languages/fr.json
Normal file
36
src/modules/forien-copy-environment/languages/fr.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"forien-copy-environment": {
|
||||
"menu": {
|
||||
"copy": "Copier comme texte",
|
||||
"save": "Copier comme JSON",
|
||||
"export": "Exporter les réglages",
|
||||
"import": "Importer les réglages"
|
||||
},
|
||||
"settings": {
|
||||
"max-diff": "Nombre de caractères",
|
||||
"max-diff-hint": "Nombre maximal de caractères pour montrer par différence. Régler à 0 pour tout montrer."
|
||||
},
|
||||
"title": "Importer les réglages",
|
||||
"intro": "Ce formulaire vous permet de sélectionner les réglages du monde et des joueurs que vous souhaitez importer.",
|
||||
"message": "Liste générée avec Forien's Copy Environment: https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment",
|
||||
"copiedToClipboard": "Données d'environnement copiées dans le presse-papier!",
|
||||
"updatedReloading": "Réglages du monde mises à jour. Rechargement du monde dans 5 sec...",
|
||||
"import": {
|
||||
"title": "Réglages du monde",
|
||||
"save": "Réglages d'importation",
|
||||
"playerList": "Importer les réglages pour les joueurs suivants:",
|
||||
"existing": "Importer un export existant:",
|
||||
"property": "Propriété",
|
||||
"newValue": "Nouvelle Valeur",
|
||||
"currentValue": "Valeur actuelle",
|
||||
"notFound": "Les utilisateurs suivants dans le fichier d'importation n'existent pas dans ce monde et seront ignorés.",
|
||||
"existingValue": "Les valeurs existantes qui ne sont pas modifiées seront ignorées:",
|
||||
"existingPlayerValues": "Les joueurs existants qui ne sont pas modifiés seront ignorés:",
|
||||
"updatedPlayer": "Paramètre des joueurs mis à jour pour: {name}",
|
||||
"noChanges": "Il n'y a pas de différences entre le monde actuel et les réglagess importés.",
|
||||
"showSettings": "Afficher {count} paramètres",
|
||||
"warning": "Avertissement",
|
||||
"warningMessage": "Vous avez choisi de ne pas importer les modules sélectionnés. Si cela n’était pas intentionnel, activez la valeur \"core.moduleConfiguration\" ci-dessus."
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/modules/forien-copy-environment/languages/it.json
Normal file
36
src/modules/forien-copy-environment/languages/it.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"forien-copy-environment": {
|
||||
"menu": {
|
||||
"copy": "Copia come Testo",
|
||||
"save": "Copia come JSON",
|
||||
"export": "Esporta Impostazioni",
|
||||
"import": "Importa Impostazioni"
|
||||
},
|
||||
"settings": {
|
||||
"max-diff": "Numero di caratteri",
|
||||
"max-diff-hint": "Massimo numero di caratteri da mostrare per differenza. Imposta a 0 per mostrare tutto."
|
||||
},
|
||||
"title": "Importa Impostazioni",
|
||||
"intro": "Questo modulo ti consente di selezionare quali impostazioni del mondo e del giocatore desideri importare.",
|
||||
"message": "Elenco generato con Forien's Copy Environment: https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment",
|
||||
"copiedToClipboard": "Dati ambientali copiati negli appunti!",
|
||||
"updatedReloading": "Impostazioni del mondo aggiornate. Ricaricamento mondo in 5sec...",
|
||||
"import": {
|
||||
"title": "Impostazioni Mondo",
|
||||
"save": "Importa Impostazioni",
|
||||
"playerList": "Importa Impostazioni per i seguenti giocatori:",
|
||||
"existing": "Importa Esportazione esistente:",
|
||||
"property": "Proprietà",
|
||||
"newValue": "Nuovo Valore",
|
||||
"currentValue": "Valore Attuale",
|
||||
"notFound": "I seguenti utenti nel file di importazione non esistono in questo mondo e verranno ignorati.",
|
||||
"existingValue": "Valori esistenti che non sono cambiati e verranno ignorati:",
|
||||
"existingPlayerValues": "Giocatori esistenti che non sono cambiati e verranno ignorati:",
|
||||
"updatedPlayer": "Impostazioni giocatore aggiornate per: {name}",
|
||||
"noChanges": "Non ci sono differenze tra il mondo attuale e le impostazioni importate.",
|
||||
"showSettings": "Mostra {count} impostazioni",
|
||||
"warning": "Avvertenza",
|
||||
"warningMessage": "Hai scelto di non importare i moduli selezionati. Se questo non era intenzionale, abilita il valore \"core.moduleConfiguration\" sopra."
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/modules/forien-copy-environment/languages/ja.json
Normal file
36
src/modules/forien-copy-environment/languages/ja.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"forien-copy-environment": {
|
||||
"menu": {
|
||||
"copy": "クリップボードにコピー",
|
||||
"save": "JSONとしてコピー",
|
||||
"export": "設定のエクスポート",
|
||||
"import": "設定のインポート"
|
||||
},
|
||||
"settings": {
|
||||
"max-diff": "文字数",
|
||||
"max-diff-hint": "差異ごとに表示する最大文字数。 すべてを表示するには 0 に設定します。"
|
||||
},
|
||||
"title": "インポート設定",
|
||||
"intro": "このフォームでは、ワールドとプレイヤーのどの設定をインポートするかを選択します。",
|
||||
"message": "このリストは「Forien's Copy Environment」で作成されました: https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment",
|
||||
"copiedToClipboard": "環境データをクリップボードにコピーしました!",
|
||||
"updatedReloading": "ワールド設定を更新しました。5秒後に再読み込みします……",
|
||||
"import": {
|
||||
"title": "ワールドの設定",
|
||||
"save": "インポート設定",
|
||||
"playerList": "次のプレイヤー設定をインポートします:",
|
||||
"existing": "既存のエクスポートをインポートします:",
|
||||
"property": "プロパティ",
|
||||
"newValue": "新しい値",
|
||||
"currentValue": "現在の値",
|
||||
"notFound": "インポートファイルに含まれる次のユーザーは、このワールドに存在しないため、スキップされます。",
|
||||
"existingValue": "次の値は変更がないためスキップされます:",
|
||||
"existingPlayerValues": "次のプレイヤーは変更がないためスキップされます:",
|
||||
"updatedPlayer": "次のプレイヤーの設定を更新しました:{name}",
|
||||
"noChanges": "現在のワールド設定とインポートした設定に差異がありません。",
|
||||
"showSettings": "{count} の設定を表示",
|
||||
"warning": "警告",
|
||||
"warningMessage": "選択したモジュールをインポートしないことを選択しました。これが意図しない場合は、上記の「core.moduleConfiguration」値を有効にしてください。"
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/modules/forien-copy-environment/languages/pl.json
Normal file
36
src/modules/forien-copy-environment/languages/pl.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"forien-copy-environment": {
|
||||
"menu": {
|
||||
"copy": "Kopiuj jako tekst",
|
||||
"save": "Kopiuj jako JSON",
|
||||
"export": "Eksportuj ustawienia",
|
||||
"import": "Importuj ustawienia"
|
||||
},
|
||||
"settings": {
|
||||
"max-diff": "Liczba znaków",
|
||||
"max-diff-hint": "Maksymalna liczba znaków do wyświetlenia różnicy. Ustaw na 0, aby wyświetlić wszystkie."
|
||||
},
|
||||
"title": "Importuj ustawienia",
|
||||
"intro": "Ten formularz pozwala wybrać ustawienia świata i gracza, które mają zostać zaimportowane.",
|
||||
"message": "Lista wygenerowana za pomocą Forien's Copy Environment: https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment",
|
||||
"copiedToClipboard": "Dane środowiskowe skopiowane do schowka!",
|
||||
"updatedReloading": "Zaktualizowano ustawienia świata. Przeładowanie świata za 5 sekund...",
|
||||
"import": {
|
||||
"title": "Ustawienia świata",
|
||||
"save": "Importuj ustawienia",
|
||||
"playerList": "Importuj ustawienia dla następujących graczy:",
|
||||
"existing": "Importowanie istniejącego eksportu:",
|
||||
"property": "Własność",
|
||||
"newValue": "Nowa wartość",
|
||||
"currentValue": "Obecna wartość",
|
||||
"notFound": "Następujący użytkownicy w pliku importu nie istnieją w tym świecie i zostaną pominięci.",
|
||||
"existingValue": "Istniejące wartości, które pozostaną niezmienione i zostaną pominięte:",
|
||||
"existingPlayerValues": "Istniejący gracze, którzy nie ulegli zmianie i zostaną pominięci:",
|
||||
"updatedPlayer": "Zaktualizowano ustawienia gracza dla: {name}",
|
||||
"noChanges": "Nie ma żadnych różnic między obecnym światem a zaimportowanymi ustawieniami.",
|
||||
"showSettings": "Pokaż {count} ustawień",
|
||||
"warning": "Ostrzeżenie",
|
||||
"warningMessage": "Wybrane moduły nie zostały zaimportowane. Jeśli było to niezamierzone, włącz powyższą wartość „core.moduleConfiguration”."
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/modules/forien-copy-environment/languages/pt.json
Normal file
36
src/modules/forien-copy-environment/languages/pt.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"forien-copy-environment": {
|
||||
"menu": {
|
||||
"copy": "Copiar como texto",
|
||||
"save": "Copiar como JSON",
|
||||
"export": "Exportar Configurações",
|
||||
"import": "Importar Configurações"
|
||||
},
|
||||
"settings": {
|
||||
"max-diff": "Número de caracteres",
|
||||
"max-diff-hint": "Número máximo de caracteres a serem exibidos por diferença. Defina como 0 para mostrar tudo."
|
||||
},
|
||||
"title": "Importar Configurações",
|
||||
"intro": "Este formulário permite que você selecione quais configurações do mundo e jogador você deseja importar.",
|
||||
"message": "Lista gerada com Forien's Copy Environment: https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment",
|
||||
"copiedToClipboard": "Dados do ambiente copiados para a área de transferência!",
|
||||
"updatedReloading": "Configurações do mundo atualizadas. Recarregando o mundo em 5 segundos...",
|
||||
"import": {
|
||||
"title": "Configurações do mundo",
|
||||
"save": "Configurações de importação",
|
||||
"playerList": "Importar configurações para os seguintes jogadores:",
|
||||
"existing": "Importar uma Exportação existente:",
|
||||
"property": "Propriedade",
|
||||
"newValue": "Novo Valor",
|
||||
"currentValue": "Valor Atual",
|
||||
"notFound": "Os seguintes usuários no arquivo de importação não existem neste mundo e serão ignorados",
|
||||
"existingValue": "Valores existentes que permanecem inalterados e serão ignorados:",
|
||||
"existingPlayerValues": "Jogadores existentes que permanecem inalterados e serão ignorados:",
|
||||
"updatedPlayer": "Configurações do jogador atualizadas para: {name}",
|
||||
"noChanges": "Não há diferenças entre o mundo atual e as configurações importadas.",
|
||||
"showSettings": "Mostrar {count} configurações",
|
||||
"warning": "Aviso",
|
||||
"warningMessage": "Você escolheu não importar os módulos selecionados. Se isso não foi intencional, ative o valor \"core.moduleConfiguration\" acima."
|
||||
}
|
||||
}
|
||||
}
|
||||
83
src/modules/forien-copy-environment/module.json
Normal file
83
src/modules/forien-copy-environment/module.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"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",
|
||||
"author": "Blair McMillan",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Blair McMillan",
|
||||
"url": "https://github.com/sneat",
|
||||
"discord": "blair#9056"
|
||||
},
|
||||
{
|
||||
"name": "Forien",
|
||||
"url": "https://www.patreon.com/forien",
|
||||
"discord": "Forien#2130"
|
||||
}
|
||||
],
|
||||
"url": "https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment",
|
||||
"bugs": "https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/issues",
|
||||
"changelog": "https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/blob/master/changelog.md",
|
||||
"flags": {
|
||||
"allowBugReporter": true
|
||||
},
|
||||
"minimumCoreVersion": "0.6.0",
|
||||
"compatibleCoreVersion": "12",
|
||||
"version": "v2.2.4",
|
||||
"compatibility": {
|
||||
"minimum": "0.6.0",
|
||||
"verified": "12"
|
||||
},
|
||||
"scripts": [],
|
||||
"esmodules": [
|
||||
"/scripts/module.js"
|
||||
],
|
||||
"styles": [
|
||||
"/styles/module.css"
|
||||
],
|
||||
"languages": [
|
||||
{
|
||||
"lang": "en",
|
||||
"name": "English",
|
||||
"path": "languages/en.json"
|
||||
},
|
||||
{
|
||||
"lang": "ja",
|
||||
"name": "日本語",
|
||||
"path": "languages/ja.json"
|
||||
},
|
||||
{
|
||||
"lang": "de",
|
||||
"name": "Deutsch",
|
||||
"path": "languages/de.json"
|
||||
},
|
||||
{
|
||||
"lang": "pt",
|
||||
"name": "Português",
|
||||
"path": "languages/pt.json"
|
||||
},
|
||||
{
|
||||
"lang": "pl",
|
||||
"name": "Polski",
|
||||
"path": "languages/pl.json"
|
||||
},
|
||||
{
|
||||
"lang": "it",
|
||||
"name": "Italiano",
|
||||
"path": "languages/it.json"
|
||||
},
|
||||
{
|
||||
"lang": "fr",
|
||||
"name": "Français",
|
||||
"path": "languages/fr.json"
|
||||
}
|
||||
],
|
||||
"packs": [],
|
||||
"socket": false,
|
||||
"manifest": "https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/releases/latest/download/module.json",
|
||||
"download": "https://github.com/League-of-Foundry-Developers/foundryvtt-forien-copy-environment/releases/download/v2.2.4/module.zip",
|
||||
"protected": false,
|
||||
"coreTranslation": false,
|
||||
"library": true
|
||||
}
|
||||
28
src/modules/forien-copy-environment/scripts/config.js
Normal file
28
src/modules/forien-copy-environment/scripts/config.js
Normal file
@@ -0,0 +1,28 @@
|
||||
export const name = 'forien-copy-environment';
|
||||
|
||||
export const templates = {
|
||||
settings: `modules/${name}/templates/settings.html`,
|
||||
};
|
||||
|
||||
export function isV10orNewer() {
|
||||
const gameVersion = game.version || game.data.version;
|
||||
return gameVersion === '10.0' || foundry.utils.isNewerVersion(gameVersion, '10');
|
||||
}
|
||||
|
||||
export function log(force, ...args) {
|
||||
try {
|
||||
if (typeof force !== "boolean") {
|
||||
console.warn(
|
||||
'Copy Environment | Invalid log usage. Expected "log(force, ...args)" as boolean but got',
|
||||
force
|
||||
);
|
||||
}
|
||||
|
||||
const isDebugging = window.DEV?.getPackageDebugValue(name);
|
||||
|
||||
if (force || isDebugging) {
|
||||
console.log("Copy Environment |", ...args);
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
685
src/modules/forien-copy-environment/scripts/core.js
Normal file
685
src/modules/forien-copy-environment/scripts/core.js
Normal file
@@ -0,0 +1,685 @@
|
||||
import { name, isV10orNewer, templates, log } from './config.js';
|
||||
import Setting from './setting.js';
|
||||
|
||||
export default class Core extends FormApplication {
|
||||
/**
|
||||
* @param {Array.<Object>} settings Read from previously exported settings
|
||||
*/
|
||||
constructor(settings) {
|
||||
super();
|
||||
this.settingGroups = new Map();
|
||||
this.settings = [];
|
||||
this.hasWorldSettings = false;
|
||||
this.playerSettings = [];
|
||||
this.hasPlayerSettings = false;
|
||||
this.notChangedSettings = [];
|
||||
this.notChangedPlayers = [];
|
||||
this.notFoundPlayers = [];
|
||||
this.selectedProperties = game.settings.get(name, 'selected-properties') || {
|
||||
'core.time': false,
|
||||
'pf2e.worldClock.worldCreatedOn': false,
|
||||
};
|
||||
this.supportingData = {};
|
||||
|
||||
if (settings && Array.isArray(settings)) {
|
||||
log(true, 'Parsing provided settings', settings);
|
||||
|
||||
settings.forEach((data) => {
|
||||
try {
|
||||
let setting = new Setting(data);
|
||||
if (setting) {
|
||||
switch (setting.type) {
|
||||
case Setting.WorldType:
|
||||
if (setting.hasChanges()) {
|
||||
if (!this.settingGroups.has(setting.value.group)) {
|
||||
this.settingGroups.set(setting.value.group, []);
|
||||
}
|
||||
this.settingGroups.get(setting.value.group).push(setting.value);
|
||||
if (typeof this.selectedProperties[setting.value.key] === 'undefined') {
|
||||
this.selectedProperties[setting.value.key] = true;
|
||||
}
|
||||
this.hasWorldSettings = true;
|
||||
} else {
|
||||
this.notChangedSettings.push(setting.data.key);
|
||||
}
|
||||
break;
|
||||
case Setting.PlayerType:
|
||||
if (!setting.hasChanges()) {
|
||||
this.notChangedPlayers.push(setting.data.name);
|
||||
break;
|
||||
}
|
||||
if (setting.value.playerNotFound) {
|
||||
this.notFoundPlayers.push(setting.value);
|
||||
break;
|
||||
}
|
||||
for (const [key, val] of Object.entries(setting.value.playerDifferences)) {
|
||||
const combinedKey = `${setting.value.name}--${key}`;
|
||||
if (typeof this.selectedProperties[combinedKey] === 'undefined') {
|
||||
this.selectedProperties[combinedKey] = true;
|
||||
}
|
||||
}
|
||||
for (const [key, val] of Object.entries(setting.value.playerFlagDifferences)) {
|
||||
const combinedKey = `${setting.value.name}--flag--${key}`;
|
||||
if (typeof this.selectedProperties[combinedKey] === 'undefined') {
|
||||
this.selectedProperties[combinedKey] = true;
|
||||
}
|
||||
}
|
||||
this.playerSettings.push(setting.value);
|
||||
this.hasPlayerSettings = true;
|
||||
break;
|
||||
case Setting.SupportingDataType:
|
||||
// Merge setting value with existing support data
|
||||
this.supportingData = foundry.utils.mergeObject(this.supportingData, setting.value);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown setting type: ${setting.type}`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Copy Environment |', 'Error importing setting:', data, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.settings = Object.entries(Object.fromEntries(this.settingGroups));
|
||||
this.settings.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
for (const playerSetting of this.playerSettings) {
|
||||
playerSetting.playerDifferences = Object.entries(playerSetting.playerDifferences);
|
||||
playerSetting.playerDifferences.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
playerSetting.playerFlagDifferences = Object.entries(playerSetting.playerFlagDifferences);
|
||||
playerSetting.playerFlagDifferences.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
}
|
||||
|
||||
log(true, 'Processing world settings', this.settings);
|
||||
log(true, 'Processing player settings', this.playerSettings);
|
||||
log(true, 'Selected Properties', this.selectedProperties);
|
||||
}
|
||||
|
||||
static get defaultOptions() {
|
||||
return foundry.utils.mergeObject(super.defaultOptions, {
|
||||
classes: ['copy-environment-settings'],
|
||||
height: 'auto',
|
||||
width: Math.ceil(window.innerWidth / 2),
|
||||
id: `${name}-settings`,
|
||||
title: `${name}.title`,
|
||||
template: templates.settings,
|
||||
});
|
||||
}
|
||||
|
||||
// shouldShowCoreModuleWarning returns true if the core module configuration is not selected.
|
||||
shouldShowCoreModuleWarning() {
|
||||
return !this.selectedProperties['core.moduleConfiguration'];
|
||||
}
|
||||
|
||||
getData() {
|
||||
return {
|
||||
settings: this.settings,
|
||||
playerSettings: this.playerSettings,
|
||||
hasWorldSettings: this.hasWorldSettings,
|
||||
hasPlayerSettings: this.hasPlayerSettings,
|
||||
hasChanges: this.hasWorldSettings || this.hasPlayerSettings,
|
||||
notChangedSettings: this.notChangedSettings,
|
||||
notChangedPlayers: this.notChangedPlayers,
|
||||
notFoundPlayers: this.notFoundPlayers,
|
||||
selectedProperties: this.selectedProperties,
|
||||
shouldShowCoreModuleWarning: this.shouldShowCoreModuleWarning(),
|
||||
};
|
||||
}
|
||||
|
||||
activateListeners(html) {
|
||||
super.activateListeners(html);
|
||||
|
||||
const updateCheckboxStates = (el) => {
|
||||
let overall = $(el.closest('tbody')).find('input[type=checkbox].toggle-selections')[0];
|
||||
if ($(el).data()?.type === 'core' || $(el).data()?.type === 'flag') {
|
||||
overall = $(el.closest('fieldset')).find('input[type=checkbox].toggle-selections')[0];
|
||||
}
|
||||
|
||||
if (!overall) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = $(el.closest('tbody')).find(
|
||||
'input[type=checkbox]:not(.toggle-selections)'
|
||||
);
|
||||
|
||||
var checkedCount = 0;
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
if (options[i].checked) {
|
||||
checkedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkedCount === 0) {
|
||||
overall.checked = false;
|
||||
overall.indeterminate = false;
|
||||
} else if (checkedCount === options.length) {
|
||||
overall.checked = true;
|
||||
overall.indeterminate = false;
|
||||
} else {
|
||||
overall.checked = false;
|
||||
overall.indeterminate = true;
|
||||
}
|
||||
};
|
||||
|
||||
html.on('click', '.close', () => {
|
||||
this.close();
|
||||
});
|
||||
|
||||
html.on('change', '.toggle-all-selections', (el) => {
|
||||
$(el.target.closest('fieldset'))
|
||||
.find('td input')
|
||||
.not(el.target)
|
||||
.prop('checked', el.target.checked)
|
||||
.change();
|
||||
});
|
||||
|
||||
html.on('change', '.toggle-selections', (el) => {
|
||||
$(el.target.closest('tbody'))
|
||||
.find('td input')
|
||||
.not(el.target)
|
||||
.prop('checked', el.target.checked)
|
||||
.change();
|
||||
});
|
||||
|
||||
html.on('click', '.show-settings', (el) => {
|
||||
$(el.target.closest('tbody'))
|
||||
.find('tr')
|
||||
.not(el.target.closest('tr'))
|
||||
.toggleClass('none');
|
||||
});
|
||||
|
||||
html.on('change', 'input[type=checkbox]', (el) => {
|
||||
if (!el.target.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Setting ${el.target.name} to ${el.target.checked}`);
|
||||
this.selectedProperties[el.target.name] = el.target.checked;
|
||||
game.settings.set(name, 'selected-properties', this.selectedProperties);
|
||||
|
||||
if (el.target.name === 'core.moduleConfiguration') {
|
||||
// Update the warning visibility when the core module configuration setting changes.
|
||||
$(el.target.closest('form'))
|
||||
.find('.core-module-warning')
|
||||
.toggleClass('hidden', !this.shouldShowCoreModuleWarning());
|
||||
}
|
||||
|
||||
updateCheckboxStates(el.target);
|
||||
});
|
||||
|
||||
html.on('click', '.import', async () => {
|
||||
let changed = false;
|
||||
for (let field of Array.from(this.form.getElementsByTagName('fieldset')).sort(f => f.dataset?.type === 'player' ? -1 : 1)) {
|
||||
let targetType = field.dataset?.type;
|
||||
if (!targetType) {
|
||||
log(false, 'Could not find fieldset target type');
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (targetType) {
|
||||
case 'world':
|
||||
changed = await this.importWorldSettings(field) || changed;
|
||||
break;
|
||||
case 'player':
|
||||
changed = await this.importPlayerSettings(field) || changed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
ui.notifications.info(game.i18n.localize('forien-copy-environment.updatedReloading'), {permanent: true});
|
||||
window.setTimeout(window.location.reload.bind(window.location), 6000);
|
||||
}
|
||||
|
||||
this.close();
|
||||
});
|
||||
|
||||
html.find('tbody').each((i, el) => {
|
||||
updateCheckboxStates($(el).find('input[type=checkbox]:first'));
|
||||
});
|
||||
}
|
||||
|
||||
async importWorldSettings(fieldset) {
|
||||
let changes = [];
|
||||
for (let input of fieldset.elements) {
|
||||
if (!input.checked || !input.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const target = input.dataset?.for;
|
||||
if (!target) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const [group, val] = target.split('--');
|
||||
if (!this.settings[group] || !this.settings[group][1] || !this.settings[group][1][val]) {
|
||||
log(false, 'Import world settings: could not find target for', input);
|
||||
continue;
|
||||
}
|
||||
|
||||
log(false, 'Importing world setting', this.settings[group][1][val]);
|
||||
changes.push(this.settings[group][1][val]);
|
||||
}
|
||||
if (!changes.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.processSettings(changes);
|
||||
} catch (e) {
|
||||
console.error('Import world settings: error', e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async importPlayerSettings(fieldset) {
|
||||
let targetUser = null;
|
||||
let changes = {
|
||||
flags: {},
|
||||
};
|
||||
for (let input of fieldset.elements) {
|
||||
if (!input.checked || !input.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let target = input.dataset?.for;
|
||||
if (!this.playerSettings[target]) {
|
||||
log(true, 'Import player settings: could not find target for', input);
|
||||
continue;
|
||||
}
|
||||
|
||||
let type = input.dataset?.type;
|
||||
if (!type) {
|
||||
log(true, 'Import player settings: missing type (core or flag)');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!targetUser) {
|
||||
targetUser = game.users.getName(this.playerSettings[target].name);
|
||||
}
|
||||
|
||||
const fieldName = input.name.split('--').pop();
|
||||
if (!type) {
|
||||
log(true, 'Import player settings: missing value for', input.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type === 'core') {
|
||||
changes[fieldName] =
|
||||
Object.fromEntries(this.playerSettings[target].playerDifferences)[fieldName].newVal;
|
||||
}
|
||||
|
||||
if (type === 'flag') {
|
||||
changes.flags[fieldName] =
|
||||
Object.fromEntries(this.playerSettings[target].playerFlagDifferences)[fieldName].newVal;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetUser) {
|
||||
log(true, 'No targetUser found.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Object.keys(changes).length === 1 && (typeof foundry.utils.isEmpty === 'function' ? foundry.utils.isEmpty(changes.flags) : foundry.utils.isObjectEmpty(changes.flags))) {
|
||||
log(true, 'No changes selected for', targetUser?.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
log(true, `Updating ${targetUser.name} with`, changes);
|
||||
await targetUser.update(changes);
|
||||
|
||||
ui.notifications.info(
|
||||
game.i18n.format('forien-copy-environment.import.updatedPlayer', {
|
||||
name: targetUser.name,
|
||||
})
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static download(data, filename) {
|
||||
if (!filename) {
|
||||
log(false, 'Missing filename on download request');
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonStr = JSON.stringify(data, null, 2);
|
||||
|
||||
saveDataToFile(jsonStr, 'application/json', filename);
|
||||
}
|
||||
|
||||
static getText() {
|
||||
const system = isV10orNewer() ? game.data.system : game.data.system.data;
|
||||
const core = game.version || game.data.version;
|
||||
|
||||
let text = `Core Version: ${core}\n\n`;
|
||||
|
||||
const systemAuthors = system.authors.length ? system.authors.map(a => {
|
||||
if (typeof a === 'string') {
|
||||
return a;
|
||||
}
|
||||
return a.name;
|
||||
}) : [system.author];
|
||||
text += `System: ${(system.id ?? system.name)} ${system.version} (${Array.from(new Set(systemAuthors)).join(', ')}) \n\n`;
|
||||
|
||||
text += `Modules: \n`;
|
||||
Core.getModulesForExport().forEach((m) => {
|
||||
const moduleAuthors = m.authors.length ? m.authors.map(a => {
|
||||
if (typeof a === 'string') {
|
||||
return a;
|
||||
}
|
||||
return a.name;
|
||||
}) : [m.author];
|
||||
text += `${(m.id ?? m.name)} ${m.version} (${Array.from(new Set(moduleAuthors)).join(', ')})\n`;
|
||||
});
|
||||
|
||||
text += `\n${game.i18n.localize('forien-copy-environment.message')}`;
|
||||
|
||||
log(true, text);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
static copyAsText() {
|
||||
let text = this.getText();
|
||||
|
||||
const el = document.createElement('textarea');
|
||||
el.value = text;
|
||||
el.setAttribute('readonly', '');
|
||||
el.style.position = 'absolute';
|
||||
el.style.left = '-9999px';
|
||||
document.body.appendChild(el);
|
||||
el.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(el);
|
||||
|
||||
ui.notifications.info(
|
||||
game.i18n.localize('forien-copy-environment.copiedToClipboard'),
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
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) || [];
|
||||
|
||||
// Return an array with both the world settings and player settings along with their support data.
|
||||
let data = Array.prototype.concat(
|
||||
Array.from(game.settings.settings)
|
||||
.filter(([k, v]) => {
|
||||
try {
|
||||
const value = game.settings.get(v.namespace, v.key);
|
||||
let sameValue = value === v.default;
|
||||
if (value && typeof value === 'object' && v.default && typeof v.default === 'object') {
|
||||
sameValue = !Object.keys(foundry.utils.diffObject(v.default, value)).length && !Object.keys(foundry.utils.diffObject(value, v.default)).length;
|
||||
}
|
||||
return !sameValue && !excludeModules.some((e) => v.namespace === e);
|
||||
} catch (e) {
|
||||
console.error(`Copy Environment | Could not export settings for ${v.namespace}.${v.key} due to an error. Please report this as an issue on GitHub.`, e);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.map(([k, v]) => ({
|
||||
key: k,
|
||||
value: JSON.stringify(game.settings.get(v.namespace, v.key)),
|
||||
}))
|
||||
.sort((a, b) => a.key.localeCompare(b.key)),
|
||||
game.users.map((u) => {
|
||||
const userData = isV10orNewer() ? u : u.data;
|
||||
return {
|
||||
name: userData.name,
|
||||
core: {
|
||||
avatar: userData.avatar,
|
||||
color: userData.color,
|
||||
permissions: userData.permissions,
|
||||
role: userData.role,
|
||||
},
|
||||
flags: userData.flags,
|
||||
};
|
||||
}),
|
||||
[
|
||||
{
|
||||
type: Setting.SupportingDataType,
|
||||
value: {
|
||||
compendiumFolders: game.folders.filter((f) => f.type === 'Compendium').map(f => f.toObject()),
|
||||
}
|
||||
}
|
||||
],
|
||||
);
|
||||
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}-${game.world.id}.json`;
|
||||
}
|
||||
|
||||
static importGameSettingsQuick() {
|
||||
const input = $('<input type="file">');
|
||||
input.on('change', this.importGameSettings);
|
||||
input.trigger('click');
|
||||
}
|
||||
|
||||
static importGameSettings() {
|
||||
const file = this.files[0];
|
||||
if (!file) {
|
||||
log(false, 'No file provided for game settings importer.');
|
||||
return;
|
||||
}
|
||||
|
||||
readTextFromFile(file).then(async (result) => {
|
||||
try {
|
||||
const settings = JSON.parse(result);
|
||||
let coreSettings = new Core(settings);
|
||||
coreSettings.render(true);
|
||||
} catch (e) {
|
||||
console.error('Copy Environment | Could not parse import data.', e);
|
||||
console.error('Copy Environment | If you see an error for "maximum call stack size exceeded", try reducing the "Number of Characters" setting.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async processSettings(settings) {
|
||||
if (foundry.utils.isNewerVersion((game.version || game.data.version), '0.7.9')) {
|
||||
const updates = [];
|
||||
const creates = [];
|
||||
for (const data of settings) {
|
||||
const config = game.settings.settings.get(data.key);
|
||||
if (config?.scope === 'client') {
|
||||
const storage = game.settings.storage.get(config.scope);
|
||||
if (storage) {
|
||||
storage.setItem(data.key, data.value);
|
||||
}
|
||||
} else if (game.user.isGM) {
|
||||
const existing = game.data.settings.find((s) => s.key === data.key);
|
||||
|
||||
if (data.key === 'core.compendiumConfiguration') {
|
||||
// The Compendium Configuration setting maps compendiums to folders, and the FolderIDs
|
||||
// change in a new world, so migrating this value as is breaks the mapping.
|
||||
// Attempt to update the IDs to match the new world, but if that fails, just use the
|
||||
// existing value.
|
||||
try {
|
||||
const existingCompendiumMap = JSON.parse(existing.value);
|
||||
const newCompendiumMap = JSON.parse(data.value);
|
||||
const missingEntries = new Map();
|
||||
|
||||
// Replace IDs in the new map with the existing IDs if they exist.
|
||||
for (const [key, value] of Object.entries(newCompendiumMap)) {
|
||||
if (game.folders.get(existingCompendiumMap[key]?.folder)) {
|
||||
newCompendiumMap[key].folder = existingCompendiumMap[key].folder;
|
||||
} else {
|
||||
missingEntries.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Add any missing entries to the new map based on the supporting data.
|
||||
for (const [key, value] of missingEntries) {
|
||||
const folder = await this.createFolderRecursive(value?.folder);
|
||||
if (folder?.id) {
|
||||
newCompendiumMap[key].folder = folder.id;
|
||||
}
|
||||
}
|
||||
|
||||
data.value = JSON.stringify(newCompendiumMap);
|
||||
} catch (e) {
|
||||
console.warn('Copy Environment | Could not process compendium configuration, overwriting value rather than merging.', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (existing?._id) {
|
||||
updates.push({
|
||||
_id: existing._id,
|
||||
key: data.key,
|
||||
value: data.value,
|
||||
});
|
||||
} else {
|
||||
creates.push({
|
||||
key: data.key,
|
||||
value: data.value,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (updates.length) {
|
||||
log(true, `Updating ${updates.length} world settings.`, updates);
|
||||
await SocketInterface.dispatch('modifyDocument', {
|
||||
type: 'Setting',
|
||||
action: 'update',
|
||||
updates: updates,
|
||||
operation: {
|
||||
pack: null,
|
||||
parent: null,
|
||||
updates: updates,
|
||||
}
|
||||
});
|
||||
}
|
||||
if (creates.length) {
|
||||
log(true, `Creating ${creates.length} world settings.`, creates);
|
||||
await SocketInterface.dispatch('modifyDocument', {
|
||||
type: 'Setting',
|
||||
action: 'create',
|
||||
data: creates,
|
||||
operation: {
|
||||
pack: null,
|
||||
parent: null,
|
||||
data: creates,
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
log(true, `Settings update could not be dispatched to server.`);
|
||||
console.error(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const setting of settings) {
|
||||
const config = game.settings.settings.get(setting.key);
|
||||
if (config?.scope === 'client') {
|
||||
const storage = game.settings.storage.get(config.scope);
|
||||
storage.setItem(setting.key, setting.value);
|
||||
} else if (game.user.isGM) {
|
||||
try {
|
||||
await SocketInterface.dispatch('modifyDocument', {
|
||||
type: 'Setting',
|
||||
action: 'update',
|
||||
data: setting,
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
log(true, `Setting key ${setting.key} could not be dispatched to server.`);
|
||||
console.error(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively create folders for compendiums based on the supporting data.
|
||||
async createFolderRecursive(folderID) {
|
||||
if (game.folders.get(folderID)) {
|
||||
return game.folders.get(folderID);
|
||||
}
|
||||
|
||||
const folderData = this.supportingData?.compendiumFolders?.find(f => f._id === folderID);
|
||||
if (!folderData) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Create missing folder
|
||||
console.log(`Copy Environment | Creating missing folder "${folderData.name}" with ID ${folderID}`);
|
||||
// Check that the parent folder exists
|
||||
if (folderData.folder && !game.folders.get(folderData.folder)) {
|
||||
// Create missing parent folder
|
||||
const parentFolder = await this.createFolderRecursive(folderData.folder);
|
||||
if (parentFolder?.id) {
|
||||
folderData.folder = parentFolder.id;
|
||||
}
|
||||
}
|
||||
|
||||
return Folder.create(folderData, { keepId: true });
|
||||
}
|
||||
}
|
||||
77
src/modules/forien-copy-environment/scripts/module.js
Normal file
77
src/modules/forien-copy-environment/scripts/module.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import {name} from './config.js';
|
||||
import Core from './core.js';
|
||||
|
||||
Hooks.once('init', function () {
|
||||
game.settings.register(name, 'selected-properties', {
|
||||
scope: 'client',
|
||||
config: false,
|
||||
type: Object,
|
||||
default: {
|
||||
'core.time': false,
|
||||
'pf2e.worldClock.worldCreatedOn': false,
|
||||
},
|
||||
});
|
||||
|
||||
game.settings.register(name, 'diff-length', {
|
||||
scope: 'world',
|
||||
config: true,
|
||||
type: Number,
|
||||
default: 500,
|
||||
name: "forien-copy-environment.settings.max-diff",
|
||||
hint: "forien-copy-environment.settings.max-diff-hint",
|
||||
requiresReload: false,
|
||||
});
|
||||
});
|
||||
|
||||
Hooks.once('devModeReady', ({registerPackageDebugFlag}) => {
|
||||
registerPackageDebugFlag(name);
|
||||
});
|
||||
|
||||
Hooks.on('renderSettings', function (app, html, data) {
|
||||
new ContextMenu(html, 'div.game-system, ul#game-details', [
|
||||
{
|
||||
name: game.i18n.localize('forien-copy-environment.menu.copy'),
|
||||
icon: '<i class="far fa-copy"></i>',
|
||||
callback: () => {
|
||||
try {
|
||||
Core.copyAsText();
|
||||
} catch (e) {
|
||||
console.error('Copy Environment | Error copying game settings to clipboard', e);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: game.i18n.localize('forien-copy-environment.menu.save'),
|
||||
icon: '<i class="fas fa-copy"></i>',
|
||||
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: '<i class="fas fa-file-export"></i>',
|
||||
callback: () => {
|
||||
try {
|
||||
Core.exportGameSettings();
|
||||
} catch (e) {
|
||||
console.error('Copy Environment | Error exporting game settings', e);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: game.i18n.localize('forien-copy-environment.menu.import'),
|
||||
icon: '<i class="fas fa-file-import"></i>',
|
||||
callback: () => {
|
||||
try {
|
||||
Core.importGameSettingsQuick();
|
||||
} catch (e) {
|
||||
console.error('Copy Environment | Error importing game settings', e);
|
||||
}
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import {templates} from './config.js';
|
||||
|
||||
export const preloadTemplates = async function () {
|
||||
return loadTemplates(
|
||||
Object.keys(templates).map(function (key) {
|
||||
return templates[key];
|
||||
})
|
||||
);
|
||||
};
|
||||
246
src/modules/forien-copy-environment/scripts/setting.js
Normal file
246
src/modules/forien-copy-environment/scripts/setting.js
Normal file
@@ -0,0 +1,246 @@
|
||||
import {isV10orNewer, name as moduleName} from './config.js';
|
||||
|
||||
export default class Setting {
|
||||
/**
|
||||
* @param {Object} data - either World settings or Player settings
|
||||
*/
|
||||
constructor(data) {
|
||||
this.type = Setting.UnknownType;
|
||||
this.data = data;
|
||||
this.value = undefined;
|
||||
|
||||
if (!data || typeof data !== 'object') {
|
||||
console.error('Copy Environment | Unknown setting received:', data);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (data.key && data.value) {
|
||||
this.type = Setting.WorldType;
|
||||
this.value = new WorldSetting(this.data);
|
||||
} else if (data.name) {
|
||||
this.type = Setting.PlayerType;
|
||||
this.value = new PlayerSetting(this.data);
|
||||
} else if (data.type === Setting.SupportingDataType) {
|
||||
this.type = Setting.SupportingDataType;
|
||||
this.value = data.value;
|
||||
}
|
||||
}
|
||||
|
||||
static UnknownType = '_unknownType';
|
||||
static PlayerType = '_playerType';
|
||||
static WorldType = '_worldType';
|
||||
static SupportingDataType = '_supportingDataType';
|
||||
|
||||
isWorldSetting() {
|
||||
return this.type === Setting.WorldType;
|
||||
}
|
||||
|
||||
isPlayerSetting() {
|
||||
return this.type === Setting.PlayerType;
|
||||
}
|
||||
|
||||
isSupportingDataSetting() {
|
||||
return this.type === Setting.SupportingDataType;
|
||||
}
|
||||
|
||||
hasChanges() {
|
||||
if (!this.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.value.hasChanges();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WorldSetting represents a world level setting.
|
||||
*/
|
||||
export class WorldSetting {
|
||||
/**
|
||||
* Create a world setting from Foundry data.
|
||||
* @param {Object} setting
|
||||
*/
|
||||
constructor(setting) {
|
||||
if (!setting) {
|
||||
throw 'Invalid data';
|
||||
}
|
||||
|
||||
this.key = setting.key;
|
||||
this.value = setting.value;
|
||||
this.difference = this.calculateDifference();
|
||||
this.group = this.key.split('.').shift();
|
||||
}
|
||||
|
||||
hasChanges() {
|
||||
return this.difference.hasChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the parsed JSON setting data if possible to handle object order differences.
|
||||
* @returns {Difference}
|
||||
*/
|
||||
calculateDifference() {
|
||||
const keyParts = this.key.split('.');
|
||||
const namespace = keyParts.shift();
|
||||
const key = keyParts.join('.');
|
||||
let existingSetting;
|
||||
try {
|
||||
existingSetting = game.settings.get(namespace, key);
|
||||
} catch (e) {
|
||||
// do nothing, it just means the setting isn't registered, likely because the module isn't enabled.
|
||||
}
|
||||
try {
|
||||
let newValue = this.value;
|
||||
if (newValue) {
|
||||
newValue = JSON.parse(newValue);
|
||||
}
|
||||
if (typeof existingSetting === 'object' && typeof newValue === 'object') {
|
||||
let diff = foundry.utils.diffObject(existingSetting, newValue);
|
||||
if (typeof foundry.utils.isEmpty === 'function' ? foundry.utils.isEmpty(diff) : foundry.utils.isObjectEmpty(diff)) {
|
||||
// No difference in the underlying object.
|
||||
return new Difference(this.key, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (existingSetting === newValue) {
|
||||
return new Difference(this.key, null, null);
|
||||
}
|
||||
|
||||
return new Difference(this.key, existingSetting, newValue);
|
||||
} catch (e) {
|
||||
console.error('Copy Environment | Could not parse world setting values:', this.key, e);
|
||||
}
|
||||
|
||||
// Return the difference of the original values, not the parsed values.
|
||||
let existingSettings = game.data.settings.find((s) => s.key === this.key);
|
||||
return new Difference(this.key, existingSettings?.value, this.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PlayerSetting represents a player level setting.
|
||||
*/
|
||||
export class PlayerSetting {
|
||||
/**
|
||||
* Create a player setting from Foundry data.
|
||||
* @param {Object} setting
|
||||
*/
|
||||
constructor(setting) {
|
||||
if (!setting) {
|
||||
throw 'Invalid data';
|
||||
}
|
||||
|
||||
this.name = setting.name;
|
||||
this.playerNotFound = false;
|
||||
this.playerDifferences = {};
|
||||
this.playerFlagDifferences = {};
|
||||
|
||||
const existingUser = game.users.getName(this.name);
|
||||
if (!existingUser) {
|
||||
this.playerNotFound = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
const userData = isV10orNewer() ? existingUser : existingUser.data;
|
||||
|
||||
if (setting.core.color !== userData.color) {
|
||||
this.playerDifferences.color = new Difference(
|
||||
'color',
|
||||
userData.color,
|
||||
setting.core.color
|
||||
);
|
||||
}
|
||||
|
||||
if (setting.core.role !== userData.role) {
|
||||
this.playerDifferences.role = new Difference(
|
||||
'role',
|
||||
userData.role,
|
||||
setting.core.role
|
||||
);
|
||||
}
|
||||
|
||||
if (JSON.stringify(setting.core.permissions) !== JSON.stringify(userData.permissions)) {
|
||||
this.playerDifferences.permissions = new Difference(
|
||||
'permissions',
|
||||
userData.permissions,
|
||||
this.data.core.permissions
|
||||
);
|
||||
}
|
||||
|
||||
let flagDiff = foundry.utils.diffObject(userData.flags, setting.flags);
|
||||
for (const prop in flagDiff) {
|
||||
if (!flagDiff.hasOwnProperty(prop)) {
|
||||
continue;
|
||||
}
|
||||
this.playerFlagDifferences[prop] = new Difference(
|
||||
prop,
|
||||
userData.flags[prop],
|
||||
flagDiff[prop]
|
||||
);
|
||||
}
|
||||
|
||||
this.name = setting.name;
|
||||
this.value = setting.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this player setting is identical to a player of the same name in the current world.
|
||||
* @returns boolean
|
||||
*/
|
||||
hasChanges() {
|
||||
return this.playerNotFound || this.hasDataChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this player setting has the same data values as a player of the same name in the current world.
|
||||
* Note that if there is not a matching player, there are no data changes.
|
||||
* @see hasChanges
|
||||
* @returns boolean
|
||||
*/
|
||||
hasDataChanges() {
|
||||
if (typeof foundry.utils.isEmpty === 'function') {
|
||||
return !foundry.utils.isEmpty(this.playerDifferences) || !foundry.utils.isEmpty(this.playerFlagDifferences);
|
||||
}
|
||||
|
||||
return (
|
||||
!foundry.utils.isObjectEmpty(this.playerDifferences) ||
|
||||
!foundry.utils.isObjectEmpty(this.playerFlagDifferences)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Difference represents the difference between the existing setting and the proposed setting.
|
||||
*/
|
||||
export class Difference {
|
||||
/**
|
||||
* Create a setting difference.
|
||||
* @param {string} name
|
||||
* @param {*} oldValue
|
||||
* @param {*} newValue
|
||||
*/
|
||||
constructor(name, oldValue, newValue) {
|
||||
this.name = name;
|
||||
if (oldValue !== newValue) {
|
||||
let diffSize = game.settings.get(moduleName, "diff-length");
|
||||
if (diffSize < 0) {
|
||||
diffSize = 0;
|
||||
}
|
||||
|
||||
this.oldVal = oldValue;
|
||||
this.oldString = JSON.stringify(oldValue);
|
||||
if (diffSize && this.oldString?.length > diffSize) {
|
||||
this.oldString = this.oldString.substring(0, diffSize) + '...';
|
||||
}
|
||||
this.newVal = newValue;
|
||||
this.newString = JSON.stringify(newValue);
|
||||
if (diffSize && this.newString?.length > diffSize) {
|
||||
this.newString = this.newString.substring(0, diffSize) + '...';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasChanges() {
|
||||
return this.oldVal !== this.newVal;
|
||||
}
|
||||
}
|
||||
31
src/modules/forien-copy-environment/styles/module.css
Normal file
31
src/modules/forien-copy-environment/styles/module.css
Normal file
@@ -0,0 +1,31 @@
|
||||
@charset "UTF-8";
|
||||
#forien-copy-environment-settings fieldset th.property {
|
||||
text-align: left;
|
||||
}
|
||||
#forien-copy-environment-settings fieldset td {
|
||||
word-break: break-all;
|
||||
vertical-align: top;
|
||||
}
|
||||
#forien-copy-environment-settings fieldset td:first-of-type {
|
||||
width: 21%;
|
||||
}
|
||||
#forien-copy-environment-settings form {
|
||||
max-height: 100%;
|
||||
}
|
||||
#forien-copy-environment-settings form tbody {
|
||||
border-bottom: 1px solid #777;
|
||||
}
|
||||
#forien-copy-environment-settings form tbody tr.none {
|
||||
display: none;
|
||||
}
|
||||
#forien-copy-environment-settings form tbody tr span.show-settings {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
#forien-copy-environment-settings section.import-properties {
|
||||
overflow: auto;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#forien-copy-environment-settings .noflex {
|
||||
flex: 0;
|
||||
}
|
||||
125
src/modules/forien-copy-environment/templates/settings.html
Normal file
125
src/modules/forien-copy-environment/templates/settings.html
Normal file
@@ -0,0 +1,125 @@
|
||||
<form class="flexcol {{classes}}" autocomplete="off">
|
||||
<h2 class="noflex">{{localize 'forien-copy-environment.intro'}}</h2>
|
||||
|
||||
<section class="import-properties">
|
||||
{{#if hasWorldSettings}}
|
||||
<fieldset name="input world" data-type="world">
|
||||
<legend>{{localize 'forien-copy-environment.import.title'}}</legend>
|
||||
<table>
|
||||
<thead>
|
||||
<th class="property"><label><input type="checkbox" class="toggle-all-selections">
|
||||
{{localize 'forien-copy-environment.import.property'}}</label></th>
|
||||
<th>{{localize 'forien-copy-environment.import.newValue'}}</th>
|
||||
<th>{{localize 'forien-copy-environment.import.currentValue'}}</th>
|
||||
</thead>
|
||||
{{#each settings as |setting|}}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="3"><label><input type="checkbox" class="toggle-selections"> {{setting.[0]}}</label> <span class="show-settings">({{localize 'forien-copy-environment.import.showSettings' count=setting.[1].length}})</span></td>
|
||||
</tr>
|
||||
{{#each setting.[1]}}
|
||||
<tr class="none">
|
||||
<td><label for="world-{{key}}"><input name="{{ key }}" id="world-{{key}}" type="checkbox" data-for="{{ @../index }}--{{ @index }}"
|
||||
data-type="world" {{checked (lookup @root.selectedProperties key)}}>{{ key }}</label></td>
|
||||
<td class="value">
|
||||
<label for="world-{{key}}">{{ difference.newString }}</label>
|
||||
</td>
|
||||
<td class="value">
|
||||
<label for="world-{{key}}">{{ difference.oldString }}</label>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
{{/each}}
|
||||
</table>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
{{#if notChangedSettings}}
|
||||
<p><strong>{{localize 'forien-copy-environment.import.existingValue'}}</strong></p>
|
||||
<ul>
|
||||
{{#each notChangedSettings}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#if hasPlayerSettings}}
|
||||
<p><strong>{{localize 'forien-copy-environment.import.playerList'}}</strong></p>
|
||||
{{#each playerSettings}}
|
||||
<fieldset name="input {{ name }}" data-type="player">
|
||||
<legend>{{ name }}</legend>
|
||||
<table>
|
||||
<thead>
|
||||
<th class="property"><label><input type="checkbox" class="toggle-selections" checked>
|
||||
{{localize 'forien-copy-environment.import.property'}}</label></th>
|
||||
<th>{{localize 'forien-copy-environment.import.newValue'}}</th>
|
||||
<th>{{localize 'forien-copy-environment.import.currentValue'}}</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each playerDifferences as |diff|}}
|
||||
{{#with (concat ../name "--" diff.[1].name) as |fkey|}}
|
||||
<tr>
|
||||
<td><label for="{{fkey}}"><input name="{{fkey}}" id="{{fkey}}" type="checkbox"
|
||||
data-for="{{ @../index }}" data-type="core" {{checked (lookup @root.selectedProperties fkey)}}>{{ diff.[1].name }}</label></td>
|
||||
<td class="value">
|
||||
<label for="{{fkey}}">{{ diff.[1].newString }}</label>
|
||||
</td>
|
||||
<td class="value">
|
||||
<label for="{{fkey}}">{{ diff.[1].oldString }}</label>
|
||||
</td>
|
||||
</tr>
|
||||
{{/with}}
|
||||
{{/each}}
|
||||
{{#each playerFlagDifferences as |diff|}}
|
||||
{{#with (concat ../name "--flag--" diff.[1].name) as |fdkey|}}
|
||||
<tr>
|
||||
<td><label for="{{fdkey}}"><input name="{{fdkey}}" type="checkbox" id="{{fdkey}}" data-for="{{ @../index }}" data-type="flag"
|
||||
{{checked (lookup @root.selectedProperties fdkey)}}>{{ diff.[1].name }}</label></td>
|
||||
<td class="value">
|
||||
<label for="{{fdkey}}">{{ diff.[1].newString }}</label>
|
||||
</td>
|
||||
<td class="value">
|
||||
<label for="{{fdkey}}">{{ diff.[1].oldString }}</label>
|
||||
</td>
|
||||
</tr>
|
||||
{{/with}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
{{#if notChangedPlayers}}
|
||||
<p><strong>{{localize 'forien-copy-environment.import.existingPlayerValues'}}</strong></p>
|
||||
<ul>
|
||||
{{#each notChangedPlayers}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#if notFoundPlayers}}
|
||||
<p><strong>{{localize 'forien-copy-environment.import.notFound'}}</strong></p>
|
||||
<ul>
|
||||
{{#each notFoundPlayers}}
|
||||
<li>{{ name }}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
|
||||
{{#unless hasChanges}}
|
||||
<p><strong>{{localize 'forien-copy-environment.import.noChanges'}}</strong></p>
|
||||
{{/unless}}
|
||||
</section>
|
||||
|
||||
<div class="noflex core-module-warning {{#unless shouldShowCoreModuleWarning}}hidden{{/unless}}">
|
||||
<p><strong>{{localize 'forien-copy-environment.import.warning'}}:</strong> {{localize 'forien-copy-environment.import.warningMessage'}}</p>
|
||||
</div>
|
||||
|
||||
<div class="noflex">
|
||||
{{#if hasChanges}}<button type="button" class="import"><i class="fas fa-save"></i>
|
||||
{{localize 'forien-copy-environment.import.save'}}</button>{{/if}}
|
||||
<button type="button" class="close"><i class="fas fa-close"></i> {{localize 'Close'}}</button>
|
||||
</div>
|
||||
</form>
|
||||
Reference in New Issue
Block a user