From e2822b9f22361303f4d619a4ca549ede23812515 Mon Sep 17 00:00:00 2001
From: Stefan Zermatten
Date: Mon, 28 Jan 2019 11:35:56 +0200
Subject: [PATCH 1/9] added naive backup restore
---
app/.gitignore | 1 +
app/lib/functions/backupRestoreCharacter.js | 33 +++++++++++++++++++++
2 files changed, 34 insertions(+)
create mode 100644 app/lib/functions/backupRestoreCharacter.js
diff --git a/app/.gitignore b/app/.gitignore
index 086ca397..2df4ae3a 100644
--- a/app/.gitignore
+++ b/app/.gitignore
@@ -8,3 +8,4 @@ private/oldClient
nohup.out
node_modules
dump
+.cache
diff --git a/app/lib/functions/backupRestoreCharacter.js b/app/lib/functions/backupRestoreCharacter.js
new file mode 100644
index 00000000..ba95f4af
--- /dev/null
+++ b/app/lib/functions/backupRestoreCharacter.js
@@ -0,0 +1,33 @@
+let characterCollections = [
+ Actions,
+ Attacks,
+ Buffs,
+ Classes,
+ CustomBuffs,
+ Effects,
+ Experiences,
+ Features,
+ Notes,
+ Proficiencies,
+ SpellLists,
+ Items,
+ Containers,
+];
+
+function backupCharacter(charId){
+ let characterDump = {};
+ characterDump.characters = [Characters.findOne(charId)];
+ characterCollections.map(
+ c => characterDump[c._name] = c.find({charId}).fetch()
+ );
+ return characterDump;
+};
+
+function restoreCharacter(characterDump){
+ for (collectionName in characterDump){
+ let collection = Meteor.Collection.get(collectionName);
+ for (doc in characterDump[collectionName]){
+ collection.insert(doc);
+ }
+ }
+};
From 0260824c2f13cf8307a5edab282483ccf02ff6f0 Mon Sep 17 00:00:00 2001
From: Stefan Zermatten
Date: Mon, 11 Feb 2019 10:09:18 +0200
Subject: [PATCH 2/9] Made gave backup and restore the ability to change ids
for all docs
---
app/lib/functions/backupRestoreCharacter.js | 47 ++++++++++++++++++---
1 file changed, 41 insertions(+), 6 deletions(-)
diff --git a/app/lib/functions/backupRestoreCharacter.js b/app/lib/functions/backupRestoreCharacter.js
index ba95f4af..d08ef919 100644
--- a/app/lib/functions/backupRestoreCharacter.js
+++ b/app/lib/functions/backupRestoreCharacter.js
@@ -3,6 +3,7 @@ let characterCollections = [
Attacks,
Buffs,
Classes,
+ Conditions,
CustomBuffs,
Effects,
Experiences,
@@ -10,21 +11,55 @@ let characterCollections = [
Notes,
Proficiencies,
SpellLists,
+ Spells,
+ TemporaryHitPoints,
Items,
Containers,
];
-function backupCharacter(charId){
+function dumpCharacter(charId){
let characterDump = {};
- characterDump.characters = [Characters.findOne(charId)];
- characterCollections.map(
- c => characterDump[c._name] = c.find({charId}).fetch()
- );
+ characterDump.character = Characters.findOne(charId);
+ characterCollections.forEach(c => {
+ characterDump.collections[c._name] = c.find({charId}).fetch();
+ });
return characterDump;
};
+function giveCharacterDumpNewIds(characterDump){
+ // Give the character a new Id
+ const newCharId = Random.id();
+ characterDump.character._id = newCharId;
+
+ // Give all documents a new Id, and store the mapping from old to new
+ let idMap = {}; // {oldId: newId}
+ for (let colName in characterDump.collections){
+ for (let doc of characterDump.collections[colName]){
+ let oldId = doc._id;
+ let newId = Random.id();
+ doc._id = newId;
+ idMap[oldId] = newId;
+ }
+ }
+
+ // Replace all references to old Ids with new ones
+ for (let colName in characterDump.collections){
+ for (let doc of characterDump.collections[colName]){
+ // Replace the character Id with the new one
+ doc.charId = newCharId;
+ // Replace the parent reference id with a new id
+ if (doc.parent && doc.parent.id){
+ let newParentId = idMap[doc.parent.id];
+ if(!newParentId) throw `Can't find the mapping for id ${doc.parent.id}`;
+ doc.parent.id = newParentId;
+ }
+ }
+ }
+}
+
function restoreCharacter(characterDump){
- for (collectionName in characterDump){
+ Characters.insert(characterDump.character);
+ for (collectionName in characterDump.collections){
let collection = Meteor.Collection.get(collectionName);
for (doc in characterDump[collectionName]){
collection.insert(doc);
From 3343f8a8135506f6d1e099b44aa2e0443e73721b Mon Sep 17 00:00:00 2001
From: Stefan Zermatten
Date: Mon, 11 Feb 2019 10:17:43 +0200
Subject: [PATCH 3/9] Allowed canViewCharacter to take in a character instead
of a charId to save a database read
---
app/lib/functions/permissions.js | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/app/lib/functions/permissions.js b/app/lib/functions/permissions.js
index 8f160297..37b4ff73 100644
--- a/app/lib/functions/permissions.js
+++ b/app/lib/functions/permissions.js
@@ -5,12 +5,14 @@ canEditCharacter = function(charId, userId){
return (userId === char.owner || _.contains(char.writers, userId));
};
-canViewCharacter = function(charId, userId){
+canViewCharacter = function(char, userId){
userId = userId || Meteor.userId();
- var char = Characters.findOne(
- charId,
- {fields: {owner: 1, writers: 1, readers: 1, "settings.viewPermission": 1}}
- );
+ if (typeof char !== 'object'){
+ char = Characters.findOne(
+ charId,
+ {fields: {owner: 1, writers: 1, readers: 1, "settings.viewPermission": 1}}
+ );
+ }
if (!char) return true;
return userId === char.owner ||
char.settings.viewPermission === "public" ||
From 9d86cb8bee1a8590bc285fdab37569689940d384 Mon Sep 17 00:00:00 2001
From: Stefan Zermatten
Date: Mon, 11 Feb 2019 10:21:11 +0200
Subject: [PATCH 4/9] Added the copy character method
---
app/lib/methods/characterCopyPaste.js | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 app/lib/methods/characterCopyPaste.js
diff --git a/app/lib/methods/characterCopyPaste.js b/app/lib/methods/characterCopyPaste.js
new file mode 100644
index 00000000..d078b45b
--- /dev/null
+++ b/app/lib/methods/characterCopyPaste.js
@@ -0,0 +1,22 @@
+// Uses '/lib/functions/backupRestoreCharacter.js' to do most the work
+
+Meteor.methods({
+ copyCharacter: function(charId) {
+ const userId = Meteor.userId();
+ let character = Characters.findOne(charId);
+
+ // Need at least view level permission to make a copy for yourself
+ if (!canViewCharacter(character, userId)) return;
+
+ let characterDump = dumpCharacter(charId);
+ giveCharacterDumpNewIds(characterDump);
+
+ // Remove all readers and writers, make this user the new owner
+ characterDump.character.readers = [];
+ characterDump.character.writers = [];
+ characterDump.character.owner = userId;
+
+ // Write the character back to the database
+ restoreCharacter(characterDump);
+ },
+});
From 1ebb0d25275b8f37fc5648480cbf54b50d1410ff Mon Sep 17 00:00:00 2001
From: Stefan Zermatten
Date: Mon, 11 Feb 2019 11:11:51 +0200
Subject: [PATCH 5/9] Got character copying working
---
.../views/character/characterSheet.html | 10 +-
app/client/views/character/characterSheet.js | 12 +++
app/lib/functions/backupRestoreCharacter.js | 61 +++++++-----
app/lib/methods/characterCopyPaste.js | 4 +
app/package-lock.json | 93 +++++++++++++++++--
app/package.json | 1 +
6 files changed, 148 insertions(+), 33 deletions(-)
diff --git a/app/client/views/character/characterSheet.html b/app/client/views/character/characterSheet.html
index 8785892b..efc54123 100644
--- a/app/client/views/character/characterSheet.html
+++ b/app/client/views/character/characterSheet.html
@@ -31,9 +31,17 @@
Settings
-
+
Export to Improved Initiative
+
+
+ Make a copy
+
+
+
+ Download a backup
+
{{else}}
diff --git a/app/client/views/character/characterSheet.js b/app/client/views/character/characterSheet.js
index a2a549f4..0c43d387 100644
--- a/app/client/views/character/characterSheet.js
+++ b/app/client/views/character/characterSheet.js
@@ -234,6 +234,18 @@ Template.characterSheet.events({
element: event.currentTarget.parentElement.parentElement,
});
},
+ "click #characterCopy": function(event, instance){
+ Meteor.call("copyCharacter", this._id, (error, char) => {
+ if (error){
+ console.error(error);
+ } else {
+ Router.go(`/character/${char._id}/${char.urlName || "-"}`);
+ }
+ });
+ },
+ "click #characterDump": function(event, instance){
+ saveCharacterDump(this._id);
+ },
"click #unshareCharacter": function(event, instance){
pushDialogStack({
data: this,
diff --git a/app/lib/functions/backupRestoreCharacter.js b/app/lib/functions/backupRestoreCharacter.js
index d08ef919..9863d12f 100644
--- a/app/lib/functions/backupRestoreCharacter.js
+++ b/app/lib/functions/backupRestoreCharacter.js
@@ -1,24 +1,29 @@
-let characterCollections = [
- Actions,
- Attacks,
- Buffs,
- Classes,
- Conditions,
- CustomBuffs,
- Effects,
- Experiences,
- Features,
- Notes,
- Proficiencies,
- SpellLists,
- Spells,
- TemporaryHitPoints,
- Items,
- Containers,
-];
+import { saveAs } from 'file-saver';
-function dumpCharacter(charId){
- let characterDump = {};
+let characterCollections = [];
+Meteor.startup(() => {
+ characterCollections = [
+ Actions,
+ Attacks,
+ Buffs,
+ Classes,
+ Conditions,
+ CustomBuffs,
+ Effects,
+ Experiences,
+ Features,
+ Notes,
+ Proficiencies,
+ SpellLists,
+ Spells,
+ TemporaryHitPoints,
+ Items,
+ Containers,
+ ];
+});
+
+dumpCharacter = function(charId){
+ let characterDump = {collections: {}};
characterDump.character = Characters.findOne(charId);
characterCollections.forEach(c => {
characterDump.collections[c._name] = c.find({charId}).fetch();
@@ -26,13 +31,23 @@ function dumpCharacter(charId){
return characterDump;
};
-function giveCharacterDumpNewIds(characterDump){
+saveCharacterDump = function(charId){
+ let dump = dumpCharacter(charId);
+ let textDump = JSON.stringify(dump, null, 2);
+ let charName = dump.character.name;
+ let blob = new Blob([textDump], {type: "application/json;charset=utf-8"});
+ saveAs(blob, `${charName}.JSON`);
+};
+
+giveCharacterDumpNewIds = function(characterDump){
// Give the character a new Id
+ const oldCharId = characterDump.character._id;
const newCharId = Random.id();
characterDump.character._id = newCharId;
+ let idMap = {[oldCharId]: newCharId}; // {oldId: newId}
+
// Give all documents a new Id, and store the mapping from old to new
- let idMap = {}; // {oldId: newId}
for (let colName in characterDump.collections){
for (let doc of characterDump.collections[colName]){
let oldId = doc._id;
@@ -57,7 +72,7 @@ function giveCharacterDumpNewIds(characterDump){
}
}
-function restoreCharacter(characterDump){
+restoreCharacter = function(characterDump){
Characters.insert(characterDump.character);
for (collectionName in characterDump.collections){
let collection = Meteor.Collection.get(collectionName);
diff --git a/app/lib/methods/characterCopyPaste.js b/app/lib/methods/characterCopyPaste.js
index d078b45b..76173202 100644
--- a/app/lib/methods/characterCopyPaste.js
+++ b/app/lib/methods/characterCopyPaste.js
@@ -16,7 +16,11 @@ Meteor.methods({
characterDump.character.writers = [];
characterDump.character.owner = userId;
+ // Rename the character so it's obviously a copy
+ characterDump.character.name += " - Copy";
+
// Write the character back to the database
restoreCharacter(characterDump);
+ return characterDump.character;
},
});
diff --git a/app/package-lock.json b/app/package-lock.json
index 12e5ed0b..3c606c15 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -45,7 +45,7 @@
},
"ansi-regex": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"aproba": {
@@ -74,6 +74,7 @@
},
"block-stream": {
"version": "0.0.9",
+ "resolved": false,
"integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
"requires": {
"inherits": "~2.0.0"
@@ -131,7 +132,7 @@
},
"code-point-at": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"core-js": {
@@ -139,6 +140,11 @@
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
},
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -209,6 +215,11 @@
"resolved": "https://registry.npmjs.org/fibers/-/fibers-2.0.2.tgz",
"integrity": "sha512-HfVRxhYG7C8Jl9FqtrlElMR2z/8YiLQVDKf67MLY25Ic+ILx3ecmklfT1v3u+7P5/4vEFjuxaAFXhr2/Afwk5g=="
},
+ "file-saver": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.0.tgz",
+ "integrity": "sha512-cYM1ic5DAkg25pHKgi5f10ziAM7RJU37gaH1XQlyNDrtUnzhC/dfoV9zf2OmF0RMKi42jG5B0JWBnPQqyj/G6g=="
+ },
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@@ -259,7 +270,7 @@
},
"graceful-fs": {
"version": "4.1.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "resolved": false,
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
"har-schema": {
@@ -361,6 +372,7 @@
},
"inherits": {
"version": "2.0.3",
+ "resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"invert-kv": {
@@ -383,7 +395,7 @@
},
"is-fullwidth-code-point": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": {
"number-is-nan": "^1.0.0"
@@ -1064,6 +1076,37 @@
"requires": {
"inherits": "~2.0.1",
"readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
}
},
"stream-http": {
@@ -1076,6 +1119,37 @@
"readable-stream": "^2.3.3",
"to-arraybuffer": "^1.0.0",
"xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
}
},
"string_decoder": {
@@ -1434,7 +1508,7 @@
},
"number-is-nan": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"os-homedir": {
@@ -1578,6 +1652,7 @@
},
"minimist": {
"version": "1.2.0",
+ "resolved": false,
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"strip-json-comments": {
@@ -1815,7 +1890,7 @@
},
"set-blocking": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "resolved": false,
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"shebang-command": {
@@ -1833,7 +1908,7 @@
},
"signal-exit": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "resolved": false,
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"source-map": {
@@ -1909,7 +1984,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
@@ -2159,7 +2234,7 @@
},
"wrap-ansi": {
"version": "2.1.0",
- "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"requires": {
"string-width": "^1.0.1",
diff --git a/app/package.json b/app/package.json
index 13ecdf36..fbbadeae 100644
--- a/app/package.json
+++ b/app/package.json
@@ -17,6 +17,7 @@
"bower": "^1.7.9",
"core-js": "^2.5.7",
"fibers": "^2.0.2",
+ "file-saver": "^2.0.0",
"meteor-node-stubs": "^0.3.3",
"qrcode": "^1.3.0",
"source-map-support": "^0.5.9",
From 23d43f7d4332420016f44fb29fb6a1efcf561af3 Mon Sep 17 00:00:00 2001
From: Stefan Zermatten
Date: Mon, 11 Feb 2019 11:58:45 +0200
Subject: [PATCH 6/9] Added character restore functionality
---
.../views/characterList/characterList.html | 7 +++
.../views/characterList/characterList.js | 16 ++++++
.../characterRestoreDialog.html | 31 ++++++++++++
.../characterRestoreDialog.js | 49 +++++++++++++++++++
4 files changed, 103 insertions(+)
create mode 100644 app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.html
create mode 100644 app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.js
diff --git a/app/client/views/characterList/characterList.html b/app/client/views/characterList/characterList.html
index 94962a5b..c6bc6c97 100644
--- a/app/client/views/characterList/characterList.html
+++ b/app/client/views/characterList/characterList.html
@@ -58,6 +58,13 @@
{{#simpleTooltip class="always"}} New Character {{/simpleTooltip}}
+
+
+
+ {{#simpleTooltip class="always"}} Restore from backup {{/simpleTooltip}}
+
{{/fabMenu}}
diff --git a/app/client/views/characterList/characterList.js b/app/client/views/characterList/characterList.js
index 5bdee54b..47ba3abe 100644
--- a/app/client/views/characterList/characterList.js
+++ b/app/client/views/characterList/characterList.js
@@ -81,4 +81,20 @@ Template.characterList.events({
returnElement: instance.find(`.party[data-id='${partyId}']`),
});
},
+ "click .restoreCharacter": function(event, instance) {
+ pushDialogStack({
+ template: "characterRestoreDialog",
+ element: event.currentTarget,
+ callback(dump){
+ if (!dump) return;
+ dump.character.name += " - Restored"
+ giveCharacterDumpNewIds(dump);
+ restoreCharacter(dump);
+ Router.go("characterSheet", {
+ _id: dump.character._id,
+ urlName: dump.character.urlName || '-',
+ });
+ },
+ })
+ },
});
diff --git a/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.html b/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.html
new file mode 100644
index 00000000..d37292cb
--- /dev/null
+++ b/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.html
@@ -0,0 +1,31 @@
+
+
+
+
+
+ Restore Character
+
+
+
+
+
+
+
diff --git a/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.js b/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.js
new file mode 100644
index 00000000..560a2429
--- /dev/null
+++ b/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.js
@@ -0,0 +1,49 @@
+Template.characterRestoreDialog.onCreated(function(){
+ this.dump = {};
+ this.valid = new ReactiveVar(false);
+ this.error = new ReactiveVar(null);
+});
+
+Template.characterRestoreDialog.helpers({
+ invalid(){
+ return !Template.instance().valid.get();
+ },
+ error(){
+ return Template.instance().error.get();
+ },
+});
+
+const fail = function(instance){
+ instance.valid.set(false);
+ instance.error.set("Failed to convert file into a valid character");
+ instance.dump = undefined;
+};
+
+Template.characterRestoreDialog.events({
+ "input .fileInput": function(event, instance){
+ let input = event.currentTarget.$.input;
+ let reader = new FileReader();
+ reader.onload = function(){
+ let dumpString = reader.result;
+ try {
+ let dump = JSON.parse(dumpString);
+ if (dump && dump.character && dump.collections){
+ instance.valid.set(true);
+ instance.error.set(null);
+ instance.dump = dump;
+ } else {
+ fail(instance);
+ }
+ } catch (e) {
+ fail(instance);
+ }
+ };
+ reader.readAsText(input.files[0]);
+ },
+ "click .cancelButton": function(event, instance){
+ popDialogStack();
+ },
+ "click .addButton": function(event, instance){
+ popDialogStack(instance.dump);
+ },
+});
From c9710bdb09aa25da4a8d15f24a860e0f63785b7f Mon Sep 17 00:00:00 2001
From: Stefan Zermatten
Date: Mon, 11 Feb 2019 12:10:04 +0200
Subject: [PATCH 7/9] Fixed restored characters not belonging to the user
restoring them
---
app/client/views/characterList/characterList.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/client/views/characterList/characterList.js b/app/client/views/characterList/characterList.js
index 47ba3abe..53f6940b 100644
--- a/app/client/views/characterList/characterList.js
+++ b/app/client/views/characterList/characterList.js
@@ -88,6 +88,9 @@ Template.characterList.events({
callback(dump){
if (!dump) return;
dump.character.name += " - Restored"
+ dump.character.owner = Meteor.userId();
+ dump.character.readers = [];
+ dump.character.writers = [];
giveCharacterDumpNewIds(dump);
restoreCharacter(dump);
Router.go("characterSheet", {
From 8489ef5ec0f1fae7a7f3947fe5b50b6eef18b76e Mon Sep 17 00:00:00 2001
From: Stefan Zermatten
Date: Mon, 11 Feb 2019 13:04:14 +0200
Subject: [PATCH 8/9] Fixed restoring characters not working correctly for sub
documents
---
.../views/characterList/characterList.js | 12 +---------
.../characterRestoreDialog.html | 3 +++
.../characterRestoreDialog.js | 21 ++++++++++++++++--
app/lib/functions/backupRestoreCharacter.js | 22 ++++++++++++++++---
4 files changed, 42 insertions(+), 16 deletions(-)
diff --git a/app/client/views/characterList/characterList.js b/app/client/views/characterList/characterList.js
index 53f6940b..f1113243 100644
--- a/app/client/views/characterList/characterList.js
+++ b/app/client/views/characterList/characterList.js
@@ -86,17 +86,7 @@ Template.characterList.events({
template: "characterRestoreDialog",
element: event.currentTarget,
callback(dump){
- if (!dump) return;
- dump.character.name += " - Restored"
- dump.character.owner = Meteor.userId();
- dump.character.readers = [];
- dump.character.writers = [];
- giveCharacterDumpNewIds(dump);
- restoreCharacter(dump);
- Router.go("characterSheet", {
- _id: dump.character._id,
- urlName: dump.character.urlName || '-',
- });
+ return;
},
})
},
diff --git a/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.html b/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.html
index d37292cb..5ff68c04 100644
--- a/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.html
+++ b/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.html
@@ -17,6 +17,9 @@
{{error}}
{{/if}}
+ {{#if loading}}
+
+ {{/if}}
diff --git a/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.js b/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.js
index 560a2429..ebc6a4dd 100644
--- a/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.js
+++ b/app/client/views/characterList/characterRestoreDialog/characterRestoreDialog.js
@@ -2,14 +2,18 @@ Template.characterRestoreDialog.onCreated(function(){
this.dump = {};
this.valid = new ReactiveVar(false);
this.error = new ReactiveVar(null);
+ this.loading = new ReactiveVar(false);
});
Template.characterRestoreDialog.helpers({
invalid(){
return !Template.instance().valid.get();
},
- error(){
+ error(){
return Template.instance().error.get();
+ },
+ loading(){
+ return Template.instance().loading.get();
},
});
@@ -44,6 +48,19 @@ Template.characterRestoreDialog.events({
popDialogStack();
},
"click .addButton": function(event, instance){
- popDialogStack(instance.dump);
+ let dump = instance.dump;
+ if (!dump) return;
+ Meteor.call('restoreCharacter', dump, (e, char) => {
+ instance.loading.set(false);
+ if (!char){
+ instance.error.set(e.message)
+ } else {
+ popDialogStack();
+ Router.go("characterSheet", {
+ _id: char._id,
+ urlName: char.urlName || '-',
+ });
+ }
+ });
},
});
diff --git a/app/lib/functions/backupRestoreCharacter.js b/app/lib/functions/backupRestoreCharacter.js
index 9863d12f..31fced03 100644
--- a/app/lib/functions/backupRestoreCharacter.js
+++ b/app/lib/functions/backupRestoreCharacter.js
@@ -73,11 +73,27 @@ giveCharacterDumpNewIds = function(characterDump){
}
restoreCharacter = function(characterDump){
- Characters.insert(characterDump.character);
+ Characters.direct.insert(characterDump.character);
for (collectionName in characterDump.collections){
let collection = Meteor.Collection.get(collectionName);
- for (doc in characterDump[collectionName]){
- collection.insert(doc);
+ for (doc of characterDump.collections[collectionName]){
+ // delete problematic keys that shouldn't ever be available on insert
+ delete doc.restoredAt;
+ delete doc.restoredBy;
+ // Insert the doc with no hooks
+ collection.direct.insert(doc);
}
}
};
+
+Meteor.methods({
+ restoreCharacter(characterDump){
+ characterDump.character.name += " - Restored"
+ characterDump.character.owner = Meteor.userId();
+ characterDump.character.readers = [];
+ characterDump.character.writers = [];
+ giveCharacterDumpNewIds(characterDump);
+ restoreCharacter(characterDump);
+ return characterDump.character
+ },
+});
From 436c5bb785c077df7e8b86d13682665a13dd0542 Mon Sep 17 00:00:00 2001
From: Stefan Zermatten
Date: Tue, 12 Feb 2019 09:53:29 +0200
Subject: [PATCH 9/9] Fixed bug in view permission causing 500 errors for Avrae
---
app/lib/functions/permissions.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/lib/functions/permissions.js b/app/lib/functions/permissions.js
index 37b4ff73..c6b72ff1 100644
--- a/app/lib/functions/permissions.js
+++ b/app/lib/functions/permissions.js
@@ -9,7 +9,7 @@ canViewCharacter = function(char, userId){
userId = userId || Meteor.userId();
if (typeof char !== 'object'){
char = Characters.findOne(
- charId,
+ char,
{fields: {owner: 1, writers: 1, readers: 1, "settings.viewPermission": 1}}
);
}