mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-12-24 09:00:27 +00:00
First pass at upgrade mechanism
This commit is contained in:
parent
f08f57c5d2
commit
1f6e16318a
11
core/language/en-GB/Import.multids
Normal file
11
core/language/en-GB/Import.multids
Normal file
@ -0,0 +1,11 @@
|
||||
title: $:/language/Import/
|
||||
|
||||
Listing/Cancel/Caption: Cancel
|
||||
Listing/Hint: These tiddlers are ready to import:
|
||||
Listing/Import/Caption: Import
|
||||
Listing/Select/Caption: Select
|
||||
Listing/Status/Caption: Status
|
||||
Listing/Title/Caption: Title
|
||||
Upgrader/Plugins/Suppressed: Suppressed plugin (due to incoming <<incoming>> being older than existing <<existing>>)
|
||||
Upgrader/Plugins/Upgraded: Upgraded plugin from <<incoming>> to <<upgraded>>
|
||||
Upgrader/System/Suppressed: Suppressed system tiddler
|
70
core/modules/commands/makelibrary.js
Normal file
70
core/modules/commands/makelibrary.js
Normal file
@ -0,0 +1,70 @@
|
||||
/*\
|
||||
title: $:/core/modules/commands/makelibrary.js
|
||||
type: application/javascript
|
||||
module-type: command
|
||||
|
||||
Command to pack all of the plugins in the library into a plugin tiddler of type "library"
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.info = {
|
||||
name: "makelibrary",
|
||||
synchronous: true
|
||||
};
|
||||
|
||||
var UPGRADE_LIBRARY_TITLE = "$:/UpgradeLibrary";
|
||||
|
||||
var Command = function(params,commander,callback) {
|
||||
this.params = params;
|
||||
this.commander = commander;
|
||||
this.callback = callback;
|
||||
};
|
||||
|
||||
Command.prototype.execute = function() {
|
||||
var wiki = this.commander.wiki,
|
||||
fs = require("fs"),
|
||||
path = require("path"),
|
||||
upgradeLibraryTitle = this.params[0] || UPGRADE_LIBRARY_TITLE,
|
||||
tiddlers = {};
|
||||
// Collect up the library plugins
|
||||
var collectPlugins = function(folder) {
|
||||
var pluginFolders = fs.readdirSync(folder);
|
||||
for(var p=0; p<pluginFolders.length; p++) {
|
||||
if(!$tw.boot.excludeRegExp.test(pluginFolders[p])) {
|
||||
pluginFields = $tw.loadPluginFolder(path.resolve(folder,"./" + pluginFolders[p]));
|
||||
if(pluginFields && pluginFields.title) {
|
||||
tiddlers[pluginFields.title] = pluginFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
collectPublisherPlugins = function(folder) {
|
||||
var publisherFolders = fs.readdirSync(folder);
|
||||
for(var t=0; t<publisherFolders.length; t++) {
|
||||
if(!$tw.boot.excludeRegExp.test(publisherFolders[t])) {
|
||||
collectPlugins(path.resolve(folder,"./" + publisherFolders[t]));
|
||||
}
|
||||
}
|
||||
};
|
||||
collectPublisherPlugins(path.resolve($tw.boot.corePath,$tw.config.pluginsPath));
|
||||
collectPublisherPlugins(path.resolve($tw.boot.corePath,$tw.config.themesPath));
|
||||
collectPlugins(path.resolve($tw.boot.corePath,$tw.config.languagesPath));
|
||||
// Save the upgrade library tiddler
|
||||
var pluginFields = {
|
||||
title: upgradeLibraryTitle,
|
||||
type: "application/json",
|
||||
"plugin-type": "library",
|
||||
"text": JSON.stringify({tiddlers: tiddlers},null,$tw.config.preferences.jsonSpaces)
|
||||
};
|
||||
wiki.addTiddler(new $tw.Tiddler(pluginFields));
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.Command = Command;
|
||||
|
||||
})();
|
@ -18,7 +18,7 @@ Export our filter function
|
||||
exports.plugintiddlers = function(source,operator,options) {
|
||||
var results = [];
|
||||
source(function(tiddler,title) {
|
||||
var pluginInfo = options.wiki.getPluginInfo(title);
|
||||
var pluginInfo = options.wiki.getPluginInfo(title) || options.wiki.getTiddlerData(title,{tiddlers:[]});
|
||||
if(pluginInfo) {
|
||||
$tw.utils.each(pluginInfo.tiddlers,function(fields,title) {
|
||||
results.push(title);
|
||||
|
58
core/modules/upgraders/plugins.js
Normal file
58
core/modules/upgraders/plugins.js
Normal file
@ -0,0 +1,58 @@
|
||||
/*\
|
||||
title: $:/core/modules/upgraders/plugins.js
|
||||
type: application/javascript
|
||||
module-type: upgrader
|
||||
|
||||
Upgrader module that checks that plugins are newer than any already installed version
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var UPGRADE_LIBRARY_TITLE = "$:/UpgradeLibrary";
|
||||
|
||||
exports.upgrade = function(wiki,titles,tiddlers) {
|
||||
var self = this,
|
||||
messages = {},
|
||||
upgradeLibrary,
|
||||
getLibraryTiddler = function(title) {
|
||||
if(!upgradeLibrary) {
|
||||
upgradeLibrary = wiki.getTiddlerData(UPGRADE_LIBRARY_TITLE,{});
|
||||
upgradeLibrary.tiddlers = upgradeLibrary.tiddlers || {};
|
||||
}
|
||||
return upgradeLibrary.tiddlers[title]
|
||||
};
|
||||
|
||||
// Go through all the incoming tiddlers
|
||||
$tw.utils.each(titles,function(title) {
|
||||
var incomingTiddler = tiddlers[title];
|
||||
// Check if we're dealing with a plugin
|
||||
if(incomingTiddler && incomingTiddler["plugin-type"] && incomingTiddler["version"]) {
|
||||
// Upgrade the incoming plugin if we've got a newer version in the upgrade library
|
||||
var libraryTiddler = getLibraryTiddler(title);
|
||||
if(libraryTiddler && libraryTiddler["plugin-type"] && libraryTiddler["version"]) {
|
||||
if($tw.utils.checkVersions(libraryTiddler.version,incomingTiddler.version)) {
|
||||
tiddlers[title] = libraryTiddler;
|
||||
messages[title] = $tw.language.getString("Import/Upgrader/Plugins/Upgraded",{variables: {incoming: incomingTiddler.version, upgraded: libraryTiddler.version}});
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Suppress the incoming plugin if it is older than the currently installed one
|
||||
var existingTiddler = wiki.getTiddler(title);
|
||||
if(existingTiddler && existingTiddler.hasField("plugin-type") && existingTiddler.hasField("version")) {
|
||||
// Reject the incoming plugin by blanking all its fields
|
||||
if($tw.utils.checkVersions(existingTiddler.fields.version,incomingTiddler.version)) {
|
||||
tiddlers[title] = Object.create(null);
|
||||
messages[title] = $tw.language.getString("Import/Upgrader/Plugins/Suppressed",{variables: {incoming: incomingTiddler.version, existing: existingTiddler.fields.version}});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return messages;
|
||||
};
|
||||
|
||||
})();
|
30
core/modules/upgraders/system.js
Normal file
30
core/modules/upgraders/system.js
Normal file
@ -0,0 +1,30 @@
|
||||
/*\
|
||||
title: $:/core/modules/upgraders/system.js
|
||||
type: application/javascript
|
||||
module-type: upgrader
|
||||
|
||||
Upgrader module that suppresses certain system tiddlers that shouldn't be imported
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var DONT_IMPORT_LIST = ["$:/StoryList","$:/HistoryList"];
|
||||
|
||||
exports.upgrade = function(wiki,titles,tiddlers) {
|
||||
var self = this,
|
||||
messages = {};
|
||||
// Check for tiddlers on our list
|
||||
$tw.utils.each(titles,function(title) {
|
||||
if(DONT_IMPORT_LIST.indexOf(title) !== -1) {
|
||||
tiddlers[title] = Object.create(null);
|
||||
messages[title] = $tw.language.getString("Import/Upgrader/System/Suppressed");
|
||||
}
|
||||
});
|
||||
return messages;
|
||||
};
|
||||
|
||||
})();
|
@ -12,6 +12,8 @@ Navigator widget
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
var IMPORT_TITLE = "$:/Import";
|
||||
|
||||
var Widget = require("$:/core/modules/widgets/widget.js").widget;
|
||||
|
||||
var NavigatorWidget = function(parseTreeNode,options) {
|
||||
@ -26,7 +28,8 @@ var NavigatorWidget = function(parseTreeNode,options) {
|
||||
{type: "tw-close-all-tiddlers", handler: "handleCloseAllTiddlersEvent"},
|
||||
{type: "tw-close-other-tiddlers", handler: "handleCloseOtherTiddlersEvent"},
|
||||
{type: "tw-new-tiddler", handler: "handleNewTiddlerEvent"},
|
||||
{type: "tw-import-tiddlers", handler: "handleImportTiddlersEvent"}
|
||||
{type: "tw-import-tiddlers", handler: "handleImportTiddlersEvent"},
|
||||
{type: "tw-perform-import", handler: "handlePerformImportEvent"}
|
||||
]);
|
||||
};
|
||||
|
||||
@ -402,7 +405,7 @@ NavigatorWidget.prototype.handleNewTiddlerEvent = function(event) {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Import JSON tiddlers
|
||||
// Import JSON tiddlers into a pending import tiddler
|
||||
NavigatorWidget.prototype.handleImportTiddlersEvent = function(event) {
|
||||
var self = this;
|
||||
// Get the tiddlers
|
||||
@ -411,54 +414,74 @@ NavigatorWidget.prototype.handleImportTiddlersEvent = function(event) {
|
||||
tiddlers = JSON.parse(event.param);
|
||||
} catch(e) {
|
||||
}
|
||||
// Get the current $:/Import tiddler
|
||||
var importTiddler = this.wiki.getTiddler(IMPORT_TITLE) || {fields: {}},
|
||||
importData = this.wiki.getTiddlerData(IMPORT_TITLE,{}),
|
||||
newFields = new Object({
|
||||
title: IMPORT_TITLE,
|
||||
type: "application/json",
|
||||
"plugin-type": "import"
|
||||
}),
|
||||
incomingTiddlers = [];
|
||||
// Process each tiddler
|
||||
var importedTiddlers = [];
|
||||
importData.tiddlers = importData.tiddlers || {};
|
||||
$tw.utils.each(tiddlers,function(tiddlerFields) {
|
||||
var title = tiddlerFields.title;
|
||||
// Add it to the store
|
||||
var imported = self.wiki.importTiddler(new $tw.Tiddler(
|
||||
self.wiki.getCreationFields(),
|
||||
self.wiki.getModificationFields(),
|
||||
tiddlerFields
|
||||
));
|
||||
if(imported) {
|
||||
importedTiddlers.push(title);
|
||||
if(title) {
|
||||
incomingTiddlers.push(title);
|
||||
importData.tiddlers[title] = tiddlerFields;
|
||||
}
|
||||
});
|
||||
// Give the active upgrader modules a chance to process the incoming tiddlers
|
||||
var messages = this.wiki.invokeUpgraders(incomingTiddlers,importData.tiddlers);
|
||||
$tw.utils.each(messages,function(message,title) {
|
||||
newFields["message-" + title] = message;
|
||||
});
|
||||
// Deselect any suppressed tiddlers
|
||||
$tw.utils.each(importData.tiddlers,function(tiddler,title) {
|
||||
if($tw.utils.count(tiddler) === 0) {
|
||||
newFields["selection-" + title] = "unchecked";
|
||||
}
|
||||
});
|
||||
// Save the $:/Import tiddler
|
||||
newFields.text = JSON.stringify(importData,null,$tw.config.preferences.jsonSpaces);
|
||||
this.wiki.addTiddler(new $tw.Tiddler(importTiddler,newFields));
|
||||
// Get the story and history details
|
||||
var storyList = this.getStoryList(),
|
||||
history = [];
|
||||
// Create the import report tiddler
|
||||
if(importedTiddlers.length === 0) {
|
||||
return false;
|
||||
}
|
||||
var title;
|
||||
if(importedTiddlers.length > 1) {
|
||||
title = this.wiki.generateNewTitle("$:/temp/ImportReport");
|
||||
var tiddlerFields = {
|
||||
title: title,
|
||||
text: "# [[" + importedTiddlers.join("]]\n# [[") + "]]\n"
|
||||
};
|
||||
this.wiki.addTiddler(new $tw.Tiddler(
|
||||
self.wiki.getCreationFields(),
|
||||
tiddlerFields,
|
||||
self.wiki.getModificationFields()
|
||||
));
|
||||
} else {
|
||||
title = importedTiddlers[0];
|
||||
}
|
||||
// Add it to the story
|
||||
if(storyList.indexOf(title) === -1) {
|
||||
storyList.unshift(title);
|
||||
if(storyList.indexOf(IMPORT_TITLE) === -1) {
|
||||
storyList.unshift(IMPORT_TITLE);
|
||||
}
|
||||
// And to history
|
||||
history.push(title);
|
||||
history.push(IMPORT_TITLE);
|
||||
// Save the updated story and history
|
||||
this.saveStoryList(storyList);
|
||||
this.addToHistory(history);
|
||||
return false;
|
||||
};
|
||||
|
||||
//
|
||||
NavigatorWidget.prototype.handlePerformImportEvent = function(event) {
|
||||
var self = this,
|
||||
importTiddler = this.wiki.getTiddler(event.param) || {fields: {}},
|
||||
importData = this.wiki.getTiddlerData(event.param,{tiddlers: {}}),
|
||||
importReport = [];
|
||||
// Add the tiddlers to the store
|
||||
importReport.push("The following tiddlers were imported:\n");
|
||||
$tw.utils.each(importData.tiddlers,function(tiddlerFields) {
|
||||
var title = tiddlerFields.title;
|
||||
if(title && importTiddler.fields["selection-" + title] !== "unchecked") {
|
||||
self.wiki.addTiddler(new $tw.Tiddler(tiddlerFields));
|
||||
importReport.push("# [[" + tiddlerFields.title + "]]");
|
||||
}
|
||||
});
|
||||
// Replace the $:/Import tiddler with an import report
|
||||
this.wiki.addTiddler(new $tw.Tiddler({title: IMPORT_TITLE, text: importReport.join("\n")}));
|
||||
// Navigate to the $:/Import tiddler
|
||||
this.addToHistory([IMPORT_TITLE]);
|
||||
};
|
||||
|
||||
exports.navigator = NavigatorWidget;
|
||||
|
||||
})();
|
||||
|
@ -1125,4 +1125,31 @@ exports.addToHistory = function(title,fromPageRect,historyTitle) {
|
||||
this.setTiddlerData(historyTitle,historyList,{"current-tiddler": titles[titles.length-1]});
|
||||
};
|
||||
|
||||
/*
|
||||
Invoke the available upgrader modules
|
||||
titles: array of tiddler titles to be processed
|
||||
tiddlers: hashmap by title of tiddler fields of pending import tiddlers. These can be modified by the upgraders. An entry with no fields indicates a tiddler that was pending import has been suppressed. When entries are added to the pending import the tiddlers hashmap may have entries that are not present in the titles array
|
||||
Returns a hashmap of messages keyed by tiddler title.
|
||||
*/
|
||||
exports.invokeUpgraders = function(titles,tiddlers) {
|
||||
// Collect up the available upgrader modules
|
||||
var self = this;
|
||||
if(!this.upgraderModules) {
|
||||
this.upgraderModules = [];
|
||||
$tw.modules.forEachModuleOfType("upgrader",function(title,module) {
|
||||
if(module.upgrade) {
|
||||
self.upgraderModules.push(module);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Invoke each upgrader in turn
|
||||
var messages = {};
|
||||
for(var t=0; t<this.upgraderModules.length; t++) {
|
||||
var upgrader = this.upgraderModules[t],
|
||||
upgraderMessages = upgrader.upgrade(this,titles,tiddlers);
|
||||
$tw.utils.extend(messages,upgraderMessages);
|
||||
}
|
||||
return messages;
|
||||
};
|
||||
|
||||
})();
|
||||
|
@ -3,10 +3,20 @@ tags: $:/tags/ViewTemplate
|
||||
|
||||
<div class="body">
|
||||
|
||||
<$list filter="[all[current]has[plugin-type]] -[all[current]field:plugin-type[import]]">
|
||||
|
||||
This tiddler is a plugin
|
||||
|
||||
</$list>
|
||||
|
||||
<$list filter="[all[current]!has[plugin-type]]">
|
||||
|
||||
<$transclude>
|
||||
|
||||
<$transclude tiddler="$:/language/MissingTiddler/Hint"/>
|
||||
|
||||
</$transclude>
|
||||
|
||||
</$list>
|
||||
|
||||
</div>
|
||||
|
53
core/ui/ViewTemplate/import.tid
Normal file
53
core/ui/ViewTemplate/import.tid
Normal file
@ -0,0 +1,53 @@
|
||||
title: $:/core/ui/ViewTemplate/import
|
||||
tags: $:/tags/ViewTemplate
|
||||
|
||||
\define lingo-base() $:/language/Import/
|
||||
\define messageField()
|
||||
message-$(currentTiddler)$
|
||||
\end
|
||||
\define selectionField()
|
||||
selection-$(currentTiddler)$
|
||||
\end
|
||||
<$list filter="[all[current]field:plugin-type[import]]">
|
||||
|
||||
<div class="tw-import">
|
||||
|
||||
<<lingo Listing/Hint>>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<<lingo Listing/Select/Caption>>
|
||||
</th>
|
||||
<th>
|
||||
<<lingo Listing/Title/Caption>>
|
||||
</th>
|
||||
<th>
|
||||
<<lingo Listing/Status/Caption>>
|
||||
</th>
|
||||
</tr>
|
||||
<$list filter="[all[current]plugintiddlers[]sort[title]]">
|
||||
<tr>
|
||||
<td>
|
||||
<$checkbox tiddler=<<storyTiddler>> field=<<selectionField>> checked="checked" unchecked="unchecked" default="checked"/>
|
||||
</td>
|
||||
<td>
|
||||
<$link to={{!!title}}>
|
||||
<$view field="title"/>
|
||||
</$link>
|
||||
</td>
|
||||
<td>
|
||||
<$view tiddler=<<storyTiddler>> field=<<messageField>>/>
|
||||
</td>
|
||||
</tr>
|
||||
</$list>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<$button message="tw-delete-tiddler" param=<<currentTiddler>>><<lingo Listing/Cancel/Caption>></$button>
|
||||
<$button message="tw-perform-import" param=<<currentTiddler>>><<lingo Listing/Import/Caption>></$button>
|
||||
|
||||
</div>
|
||||
|
||||
</$list>
|
@ -9,6 +9,9 @@
|
||||
"build": {
|
||||
"index": [
|
||||
"--rendertiddler","$:/core/save/all","index.html","text/plain"],
|
||||
"upgrade": [
|
||||
"--makelibrary",
|
||||
"--rendertiddler","$:/core/save/all","upgrade.html","text/plain"],
|
||||
"externalimages": [
|
||||
"--savetiddlers","[is[image]]","images",
|
||||
"--setfield","[is[image]]","_canonical_uri","$:/core/templates/canonical-uri-external-image","text/plain",
|
||||
|
37
editions/tw5.com/tiddlers/mechanisms/UpgradeMechanism.tid
Normal file
37
editions/tw5.com/tiddlers/mechanisms/UpgradeMechanism.tid
Normal file
@ -0,0 +1,37 @@
|
||||
title: UpgradeMechanism
|
||||
modified: 20140711090154150
|
||||
created: 20140711090154150
|
||||
tags: mechanism
|
||||
|
||||
# Open upgrade.html
|
||||
# Includes a data tiddler called `$:/UpgradeLibrary` that contains the latest compatible versions of all plugins in the library
|
||||
# Drag in old wiki file
|
||||
# Place tiddlers into a data tiddler `$:/Import` that is typed as a "pending import"
|
||||
# Kick off import processing for each tiddler
|
||||
## Give each "upgrader" module a chance to inspect the incoming tiddlers
|
||||
## Upgrader modules can trigger actions for each tiddler:
|
||||
##* Display a warning message
|
||||
##* Don't import
|
||||
##* Replace with another tiddler from the upgrade library
|
||||
##* Disable incompatible plugins
|
||||
# Display the newly created pending import tiddler through a new view template segment
|
||||
## Displays the payload tiddlers as a list of titles and checkboxes, with a dropdown showing the full details of the tiddler
|
||||
## Perhaps we also suppress the usual JSON display for data tiddlers behind a reveal widget
|
||||
# The user can adjust the selection checkboxes
|
||||
# Clicking "done" performs the following actions:
|
||||
## Unpack the selected tiddlers from the pending import tiddler
|
||||
## Delete the pending import tiddler
|
||||
## Delete the upgrade library tiddler
|
||||
##> Or we could just exclude those tiddlers from the subsequent save operation
|
||||
|
||||
! ToDo
|
||||
|
||||
* Add upgrade plugin containing instructions and one-shot UI
|
||||
* Grouping plugins
|
||||
* Incoming tiddler preview
|
||||
* Better display text for plugins
|
||||
* Suppressing $:/UpgradeLibrary and upgrade plugin from save
|
||||
* Finish UpgradeMechanism docs
|
||||
* ~~Checkboxes~~
|
||||
* ~~Visual difference for suppressed tiddlers~~
|
||||
* ~~Translation~~
|
Loading…
Reference in New Issue
Block a user