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 @@ + 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}} ); }