kopia lustrzana https://github.com/backface/turtlestitch
Backups (#2110)
* added backups menu * project recovery dialog * only show recover button in Cloud tab * Human-readable time diffs for recovery dialog * reverted project list context menu, ca and es translationsupd4.2
rodzic
976a964200
commit
f3ffe66a2b
21
cloud.js
21
cloud.js
|
@ -494,10 +494,11 @@ Cloud.prototype.getThumbnail = function (
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Cloud.prototype.getProject = function (projectName, onSuccess, onError) {
|
Cloud.prototype.getProject = function (projectName, delta, onSuccess, onError) {
|
||||||
this.withCredentialsRequest(
|
this.withCredentialsRequest(
|
||||||
'GET',
|
'GET',
|
||||||
'/projects/%username/' + encodeURIComponent(projectName),
|
'/projects/%username/' +
|
||||||
|
encodeURIComponent(projectName) + (delta ? '?delta=' + delta : ''),
|
||||||
onSuccess,
|
onSuccess,
|
||||||
onError,
|
onError,
|
||||||
'Could not fetch project ' + projectName,
|
'Could not fetch project ' + projectName,
|
||||||
|
@ -536,6 +537,22 @@ Cloud.prototype.getProjectMetadata = function (
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Cloud.prototype.getProjectVersionMetadata = function (
|
||||||
|
projectName,
|
||||||
|
onSuccess,
|
||||||
|
onError
|
||||||
|
) {
|
||||||
|
this.withCredentialsRequest(
|
||||||
|
'GET',
|
||||||
|
'/projects/%username/' +
|
||||||
|
encodeURIComponent(projectName) +
|
||||||
|
'/versions',
|
||||||
|
onSuccess,
|
||||||
|
onError,
|
||||||
|
'Could not fetch versions for project ' + projectName
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Cloud.prototype.deleteProject = function (
|
Cloud.prototype.deleteProject = function (
|
||||||
projectName,
|
projectName,
|
||||||
username,
|
username,
|
||||||
|
|
285
gui.js
285
gui.js
|
@ -5735,6 +5735,7 @@ ProjectDialogMorph.prototype.init = function (ide, task) {
|
||||||
this.deleteButton = null;
|
this.deleteButton = null;
|
||||||
this.shareButton = null;
|
this.shareButton = null;
|
||||||
this.unshareButton = null;
|
this.unshareButton = null;
|
||||||
|
this.recoverButton = null;
|
||||||
|
|
||||||
// initialize inherited properties:
|
// initialize inherited properties:
|
||||||
ProjectDialogMorph.uber.init.call(
|
ProjectDialogMorph.uber.init.call(
|
||||||
|
@ -5871,6 +5872,8 @@ ProjectDialogMorph.prototype.buildContents = function () {
|
||||||
if (this.task === 'open') {
|
if (this.task === 'open') {
|
||||||
this.addButton('openProject', 'Open');
|
this.addButton('openProject', 'Open');
|
||||||
this.action = 'openProject';
|
this.action = 'openProject';
|
||||||
|
this.recoverButton = this.addButton('recoveryDialog', 'Recover');
|
||||||
|
this.recoverButton.hide();
|
||||||
} else { // 'save'
|
} else { // 'save'
|
||||||
this.addButton('saveProject', 'Save');
|
this.addButton('saveProject', 'Save');
|
||||||
this.action = 'saveProject';
|
this.action = 'saveProject';
|
||||||
|
@ -6161,6 +6164,7 @@ ProjectDialogMorph.prototype.setSource = function (source) {
|
||||||
this.body.add(this.listField);
|
this.body.add(this.listField);
|
||||||
this.shareButton.hide();
|
this.shareButton.hide();
|
||||||
this.unshareButton.hide();
|
this.unshareButton.hide();
|
||||||
|
this.recoverButton.hide();
|
||||||
/*
|
/*
|
||||||
this.publishButton.hide();
|
this.publishButton.hide();
|
||||||
this.unpublishButton.hide();
|
this.unpublishButton.hide();
|
||||||
|
@ -6295,6 +6299,7 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) {
|
||||||
myself.edit();
|
myself.edit();
|
||||||
};
|
};
|
||||||
this.body.add(this.listField);
|
this.body.add(this.listField);
|
||||||
|
this.recoverButton.show();
|
||||||
this.shareButton.show();
|
this.shareButton.show();
|
||||||
this.unshareButton.hide();
|
this.unshareButton.hide();
|
||||||
this.deleteButton.show();
|
this.deleteButton.show();
|
||||||
|
@ -6314,6 +6319,13 @@ ProjectDialogMorph.prototype.clearDetails = function () {
|
||||||
this.preview.drawNew();
|
this.preview.drawNew();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ProjectDialogMorph.prototype.recoveryDialog = function () {
|
||||||
|
var proj = this.listField.selected;
|
||||||
|
if (!proj) {return; }
|
||||||
|
new ProjectRecoveryDialogMorph(this.ide, proj.projectname, this).popUp();
|
||||||
|
this.hide();
|
||||||
|
};
|
||||||
|
|
||||||
ProjectDialogMorph.prototype.openProject = function () {
|
ProjectDialogMorph.prototype.openProject = function () {
|
||||||
var proj = this.listField.selected,
|
var proj = this.listField.selected,
|
||||||
src;
|
src;
|
||||||
|
@ -6332,22 +6344,23 @@ ProjectDialogMorph.prototype.openProject = function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ProjectDialogMorph.prototype.openCloudProject = function (project) {
|
ProjectDialogMorph.prototype.openCloudProject = function (project, delta) {
|
||||||
var myself = this;
|
var myself = this;
|
||||||
myself.ide.nextSteps([
|
myself.ide.nextSteps([
|
||||||
function () {
|
function () {
|
||||||
myself.ide.showMessage('Fetching project\nfrom the cloud...');
|
myself.ide.showMessage('Fetching project\nfrom the cloud...');
|
||||||
},
|
},
|
||||||
function () {
|
function () {
|
||||||
myself.rawOpenCloudProject(project);
|
myself.rawOpenCloudProject(project, delta);
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
ProjectDialogMorph.prototype.rawOpenCloudProject = function (proj) {
|
ProjectDialogMorph.prototype.rawOpenCloudProject = function (proj, delta) {
|
||||||
var myself = this;
|
var myself = this;
|
||||||
SnapCloud.getProject(
|
SnapCloud.getProject(
|
||||||
proj.projectname,
|
proj.projectname,
|
||||||
|
delta,
|
||||||
function (clouddata) {
|
function (clouddata) {
|
||||||
myself.ide.source = 'cloud';
|
myself.ide.source = 'cloud';
|
||||||
myself.ide.nextSteps([
|
myself.ide.nextSteps([
|
||||||
|
@ -6742,6 +6755,272 @@ ProjectDialogMorph.prototype.fixLayout = function () {
|
||||||
this.changed();
|
this.changed();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ProjectRecoveryDialogMorph /////////////////////////////////////////
|
||||||
|
// I show previous versions for a particular project and
|
||||||
|
// let users recover them.
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype = new DialogBoxMorph();
|
||||||
|
ProjectRecoveryDialogMorph.prototype.constructor = ProjectRecoveryDialogMorph;
|
||||||
|
ProjectRecoveryDialogMorph.uber = DialogBoxMorph.prototype;
|
||||||
|
|
||||||
|
// ProjectRecoveryDialogMorph instance creation:
|
||||||
|
|
||||||
|
function ProjectRecoveryDialogMorph(ide, project, browser) {
|
||||||
|
this.init(ide, project, browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype.init = function (ide, projectName, browser) {
|
||||||
|
// initialize inherited properties:
|
||||||
|
ProjectRecoveryDialogMorph.uber.init.call(
|
||||||
|
this,
|
||||||
|
this, // target
|
||||||
|
null, // function
|
||||||
|
null // environment
|
||||||
|
);
|
||||||
|
|
||||||
|
this.ide = ide;
|
||||||
|
this.browser = browser;
|
||||||
|
this.key = 'recoverProject';
|
||||||
|
this.projectName = projectName;
|
||||||
|
|
||||||
|
this.versions = null;
|
||||||
|
|
||||||
|
this.handle = null;
|
||||||
|
this.listField = null;
|
||||||
|
this.preview = null;
|
||||||
|
this.notesText = null;
|
||||||
|
this.notesField = null;
|
||||||
|
|
||||||
|
this.labelString = 'Recover project';
|
||||||
|
this.createLabel();
|
||||||
|
|
||||||
|
this.buildContents();
|
||||||
|
};
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype.buildContents = function () {
|
||||||
|
this.addBody(new Morph());
|
||||||
|
this.body.color = this.color;
|
||||||
|
|
||||||
|
this.buildListField();
|
||||||
|
|
||||||
|
this.preview = new Morph();
|
||||||
|
this.preview.fixLayout = nop;
|
||||||
|
this.preview.edge = InputFieldMorph.prototype.edge;
|
||||||
|
this.preview.fontSize = InputFieldMorph.prototype.fontSize;
|
||||||
|
this.preview.typeInPadding = InputFieldMorph.prototype.typeInPadding;
|
||||||
|
this.preview.contrast = InputFieldMorph.prototype.contrast;
|
||||||
|
this.preview.drawNew = function () {
|
||||||
|
InputFieldMorph.prototype.drawNew.call(this);
|
||||||
|
if (this.texture) {
|
||||||
|
this.drawTexture(this.texture);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.preview.drawCachedTexture = function () {
|
||||||
|
var context = this.image.getContext('2d');
|
||||||
|
context.drawImage(this.cachedTexture, this.edge, this.edge);
|
||||||
|
this.changed();
|
||||||
|
};
|
||||||
|
this.preview.drawRectBorder = InputFieldMorph.prototype.drawRectBorder;
|
||||||
|
this.preview.setExtent(
|
||||||
|
this.ide.serializer.thumbnailSize.add(this.preview.edge * 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.body.add(this.preview);
|
||||||
|
this.preview.drawNew();
|
||||||
|
|
||||||
|
this.notesField = new ScrollFrameMorph();
|
||||||
|
this.notesField.fixLayout = nop;
|
||||||
|
|
||||||
|
this.notesField.edge = InputFieldMorph.prototype.edge;
|
||||||
|
this.notesField.fontSize = InputFieldMorph.prototype.fontSize;
|
||||||
|
this.notesField.typeInPadding = InputFieldMorph.prototype.typeInPadding;
|
||||||
|
this.notesField.contrast = InputFieldMorph.prototype.contrast;
|
||||||
|
this.notesField.drawNew = InputFieldMorph.prototype.drawNew;
|
||||||
|
this.notesField.drawRectBorder = InputFieldMorph.prototype.drawRectBorder;
|
||||||
|
|
||||||
|
this.notesField.acceptsDrops = false;
|
||||||
|
this.notesField.contents.acceptsDrops = false;
|
||||||
|
|
||||||
|
this.notesText = new TextMorph('');
|
||||||
|
|
||||||
|
this.notesField.isTextLineWrapping = true;
|
||||||
|
this.notesField.padding = 3;
|
||||||
|
this.notesField.setContents(this.notesText);
|
||||||
|
this.notesField.setWidth(this.preview.width());
|
||||||
|
|
||||||
|
this.body.add(this.notesField);
|
||||||
|
|
||||||
|
this.addButton('recoverProject', 'Recover');
|
||||||
|
this.addButton('cancel', 'Cancel');
|
||||||
|
|
||||||
|
this.setExtent(new Point(360, 300));
|
||||||
|
this.fixLayout();
|
||||||
|
};
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype.buildListField = function () {
|
||||||
|
var myself = this;
|
||||||
|
|
||||||
|
this.listField = new ListMorph([]);
|
||||||
|
this.fixListFieldItemColors();
|
||||||
|
this.listField.fixLayout = nop;
|
||||||
|
this.listField.edge = InputFieldMorph.prototype.edge;
|
||||||
|
this.listField.fontSize = InputFieldMorph.prototype.fontSize;
|
||||||
|
this.listField.typeInPadding = InputFieldMorph.prototype.typeInPadding;
|
||||||
|
this.listField.contrast = InputFieldMorph.prototype.contrast;
|
||||||
|
this.listField.drawNew = InputFieldMorph.prototype.drawNew;
|
||||||
|
this.listField.drawRectBorder = InputFieldMorph.prototype.drawRectBorder;
|
||||||
|
|
||||||
|
this.listField.action = function (item) {
|
||||||
|
var version;
|
||||||
|
if (item === undefined) { return; }
|
||||||
|
version = detect(
|
||||||
|
myself.versions,
|
||||||
|
function (version) {
|
||||||
|
return version.lastupdated === item;
|
||||||
|
});
|
||||||
|
myself.notesText.text = version.notes || '';
|
||||||
|
myself.notesText.drawNew();
|
||||||
|
myself.notesField.contents.adjustBounds();
|
||||||
|
myself.preview.texture = version.thumbnail;
|
||||||
|
myself.preview.cachedTexture = null;
|
||||||
|
myself.preview.drawNew();
|
||||||
|
};
|
||||||
|
|
||||||
|
SnapCloud.getProjectVersionMetadata(
|
||||||
|
this.projectName,
|
||||||
|
function (versions) {
|
||||||
|
var today = new Date(),
|
||||||
|
yesterday = new Date();
|
||||||
|
yesterday.setDate(today.getDate() - 1);
|
||||||
|
myself.versions = versions;
|
||||||
|
myself.versions.forEach(function (version) {
|
||||||
|
var date = new Date(new Date().getTime() - version.lastupdated * 1000);
|
||||||
|
if (date.toDateString() === today.toDateString()) {
|
||||||
|
version.lastupdated = localize('Today, ') + date.toLocaleTimeString();
|
||||||
|
} else if (date.toDateString() === yesterday.toDateString()) {
|
||||||
|
version.lastupdated = localize('Yesterday, ') + date.toLocaleTimeString();
|
||||||
|
} else {
|
||||||
|
version.lastupdated = date.toLocaleString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
myself.listField.elements =
|
||||||
|
myself.versions.map(function (version) {
|
||||||
|
return version.lastupdated;
|
||||||
|
});
|
||||||
|
myself.clearDetails();
|
||||||
|
myself.listField.buildListContents();
|
||||||
|
myself.fixListFieldItemColors();
|
||||||
|
myself.listField.adjustScrollBars();
|
||||||
|
myself.listField.scrollY(myself.listField.top());
|
||||||
|
myself.fixLayout();
|
||||||
|
},
|
||||||
|
this.ide.cloudError()
|
||||||
|
);
|
||||||
|
|
||||||
|
this.body.add(this.listField);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype.cancel = function () {
|
||||||
|
var myself = this;
|
||||||
|
this.browser.show();
|
||||||
|
this.browser.listField.select(
|
||||||
|
detect(
|
||||||
|
this.browser.projectList,
|
||||||
|
function (item) { return item.projectname === myself.projectName }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
ProjectRecoveryDialogMorph.uber.cancel.call(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype.recoverProject = function () {
|
||||||
|
var lastupdated = this.listField.selected,
|
||||||
|
version = detect(
|
||||||
|
this.versions,
|
||||||
|
function (version) {
|
||||||
|
return version.lastupdated === lastupdated;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.browser.openCloudProject({ projectname: this.projectName }, version.delta);
|
||||||
|
this.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype.popUp = function () {
|
||||||
|
var world = this.ide.world();
|
||||||
|
if (world) {
|
||||||
|
ProjectRecoveryDialogMorph.uber.popUp.call(this, world);
|
||||||
|
this.handle = new HandleMorph(
|
||||||
|
this,
|
||||||
|
300,
|
||||||
|
300,
|
||||||
|
this.corner,
|
||||||
|
this.corner
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype.fixListFieldItemColors =
|
||||||
|
ProjectDialogMorph.prototype.fixListFieldItemColors;
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype.clearDetails =
|
||||||
|
ProjectDialogMorph.prototype.clearDetails;
|
||||||
|
|
||||||
|
ProjectRecoveryDialogMorph.prototype.fixLayout = function () {
|
||||||
|
var titleHeight = fontHeight(this.titleFontSize) + this.titlePadding * 2,
|
||||||
|
thin = this.padding / 2,
|
||||||
|
oldFlag = Morph.prototype.trackChanges;
|
||||||
|
|
||||||
|
Morph.prototype.trackChanges = false;
|
||||||
|
|
||||||
|
if (this.body) {
|
||||||
|
this.body.setPosition(this.position().add(new Point(
|
||||||
|
this.padding,
|
||||||
|
titleHeight + this.padding
|
||||||
|
)));
|
||||||
|
this.body.setExtent(new Point(
|
||||||
|
this.width() - this.padding * 2,
|
||||||
|
this.height()
|
||||||
|
- this.padding * 3 // top, bottom and button padding.
|
||||||
|
- titleHeight
|
||||||
|
- this.buttons.height()
|
||||||
|
));
|
||||||
|
|
||||||
|
this.listField.setWidth(
|
||||||
|
this.body.width()
|
||||||
|
- this.preview.width()
|
||||||
|
- this.padding
|
||||||
|
);
|
||||||
|
this.listField.contents.children[0].adjustWidths();
|
||||||
|
|
||||||
|
this.listField.setPosition(this.body.position());
|
||||||
|
this.listField.setHeight(this.body.height());
|
||||||
|
|
||||||
|
this.preview.setRight(this.body.right());
|
||||||
|
this.preview.setTop(this.listField.top());
|
||||||
|
|
||||||
|
this.notesField.setTop(this.preview.bottom() + thin);
|
||||||
|
this.notesField.setLeft(this.preview.left());
|
||||||
|
this.notesField.setHeight(
|
||||||
|
this.body.bottom() - this.preview.bottom() - thin
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.label) {
|
||||||
|
this.label.setCenter(this.center());
|
||||||
|
this.label.setTop(
|
||||||
|
this.top() + (titleHeight - this.label.height()) / 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.buttons) {
|
||||||
|
this.buttons.fixLayout();
|
||||||
|
this.buttons.setCenter(this.center());
|
||||||
|
this.buttons.setBottom(this.bottom() - this.padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
Morph.prototype.trackChanges = oldFlag;
|
||||||
|
this.changed();
|
||||||
|
};
|
||||||
|
|
||||||
// LibraryImportDialogMorph ///////////////////////////////////////////
|
// LibraryImportDialogMorph ///////////////////////////////////////////
|
||||||
// I am preview dialog shown before importing a library.
|
// I am preview dialog shown before importing a library.
|
||||||
// I inherit from a DialogMorph but look similar to
|
// I inherit from a DialogMorph but look similar to
|
||||||
|
|
|
@ -1074,6 +1074,12 @@ SnapTranslator.dict.ca = {
|
||||||
'Segur que vols esborrar',
|
'Segur que vols esborrar',
|
||||||
'rename...':
|
'rename...':
|
||||||
'canvia el nom...',
|
'canvia el nom...',
|
||||||
|
'Recover':
|
||||||
|
'Recupera',
|
||||||
|
'Today, ':
|
||||||
|
'Avui, ',
|
||||||
|
'Yesterday, ':
|
||||||
|
'Ahir, ',
|
||||||
|
|
||||||
// costume editor
|
// costume editor
|
||||||
'Costume Editor':
|
'Costume Editor':
|
||||||
|
|
|
@ -2404,6 +2404,12 @@ SnapTranslator.dict.es = {
|
||||||
'Publicado.',
|
'Publicado.',
|
||||||
'unpublished.':
|
'unpublished.':
|
||||||
'No publicado',
|
'No publicado',
|
||||||
|
'Recover':
|
||||||
|
'Recuperar',
|
||||||
|
'Today, ':
|
||||||
|
'Hoy, ',
|
||||||
|
'Yesterday, ':
|
||||||
|
'Ayer, ',
|
||||||
|
|
||||||
// costume editor @todo (wasn't this superseed by paint editor?)
|
// costume editor @todo (wasn't this superseed by paint editor?)
|
||||||
'Costume Editor':
|
'Costume Editor':
|
||||||
|
|
Ładowanie…
Reference in New Issue