Split properties modal into 2 modals

pull/1/head
yflory 5 years ago
parent ff895bbc82
commit de820dc0d1

@ -100,588 +100,8 @@ define([
};
};
var createOwnerModal = function (common, data) {
var friends = common.getFriends(true);
var sframeChan = common.getSframeChannel();
var priv = common.getMetadataMgr().getPrivateData();
var user = common.getMetadataMgr().getUserData();
var edPublic = priv.edPublic;
var channel = data.channel;
var owners = data.owners || [];
var pending_owners = data.pending_owners || [];
var teams = priv.teams;
var teamOwner = data.teamId;
var redrawAll = function () {};
var div1 = h('div.cp-usergrid-user.cp-share-column.cp-ownership');
var div2 = h('div.cp-usergrid-user.cp-share-column.cp-ownership');
var $div1 = $(div1);
var $div2 = $(div2);
// Remove owner column
var drawRemove = function (pending) {
var _owners = {};
var o = (pending ? pending_owners : owners) || [];
o.forEach(function (ed) {
var f;
Object.keys(friends).some(function (c) {
if (friends[c].edPublic === ed) {
f = friends[c];
return true;
}
});
Object.keys(teams).some(function (id) {
if (teams[id].edPublic === ed) {
f = teams[id];
f.teamId = id;
}
});
if (ed === edPublic) {
f = f || user;
if (f.name) { f.edPublic = edPublic; }
}
_owners[ed] = f || {
displayName: Messages._getKey('owner_unknownUser', [ed]),
edPublic: ed,
};
});
var msg = pending ? Messages.owner_removePendingText
: Messages.owner_removeText;
var removeCol = UIElements.getUserGrid(msg, {
common: common,
large: true,
data: _owners,
noFilter: true
}, function () {
});
var $div = $(removeCol.div);
// When clicking on the remove button, we check the selected users.
// If you try to remove yourself, we'll display an additional warning message
var btnMsg = pending ? Messages.owner_removePendingButton : Messages.owner_removeButton;
var removeButton = h('button.no-margin', btnMsg);
$(removeButton).click(function () {
// Check selection
var $sel = $div.find('.cp-usergrid-user.cp-selected');
var sel = $sel.toArray();
if (!sel.length) { return; }
var me = false;
var toRemove = sel.map(function (el) {
var ed = $(el).attr('data-ed');
if (!ed) { return; }
if (teamOwner && teams[teamOwner] && teams[teamOwner].edPublic === ed) { me = true; }
if (ed === edPublic && !teamOwner) { me = true; }
return ed;
}).filter(function (x) { return x; });
NThen(function (waitFor) {
var msg = me ? Messages.owner_removeMeConfirm : Messages.owner_removeConfirm;
UI.confirm(msg, waitFor(function (yes) {
if (!yes) {
waitFor.abort();
return;
}
}));
}).nThen(function (waitFor) {
// Send the command
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: pending ? 'RM_PENDING_OWNERS' : 'RM_OWNERS',
value: toRemove,
teamId: teamOwner
}, waitFor(function (err, res) {
err = err || (res && res.error);
if (err) {
waitFor.abort();
redrawAll();
var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden
: Messages.error;
return void UI.warn(text);
}
UI.log(Messages.saved);
}));
}).nThen(function (waitFor) {
sel.forEach(function (el) {
var curve = $(el).attr('data-curve');
var friend = curve === user.curvePublic ? user : friends[curve];
if (!friend) { return; }
common.mailbox.sendTo("RM_OWNER", {
channel: channel,
title: data.title,
pending: pending
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
}, waitFor());
});
}).nThen(function () {
redrawAll();
});
});
$div.append(h('p', removeButton));
return $div;
};
// Add owners column
var drawAdd = function () {
var $div = $(h('div.cp-share-column'));
var _friends = JSON.parse(JSON.stringify(friends));
Object.keys(_friends).forEach(function (curve) {
if (owners.indexOf(_friends[curve].edPublic) !== -1 ||
pending_owners.indexOf(_friends[curve].edPublic) !== -1 ||
!_friends[curve].notifications) {
delete _friends[curve];
}
});
var addCol = UIElements.getUserGrid(Messages.owner_addText, {
common: common,
large: true,
data: _friends
}, function () {
//console.log(arguments);
});
$div.append(addCol.div);
var teamsData = Util.tryParse(JSON.stringify(priv.teams)) || {};
Object.keys(teamsData).forEach(function (id) {
var t = teamsData[id];
t.teamId = id;
if (owners.indexOf(t.edPublic) !== -1 || pending_owners.indexOf(t.edPublic) !== -1) {
delete teamsData[id];
}
});
var teamsList = UIElements.getUserGrid(Messages.owner_addTeamText, {
common: common,
large: true,
noFilter: true,
data: teamsData
}, function () {});
$div.append(teamsList.div);
// When clicking on the add button, we get the selected users.
var addButton = h('button.no-margin', Messages.owner_addButton);
$(addButton).click(function () {
// Check selection
var $sel = $div.find('.cp-usergrid-user.cp-selected');
var sel = $sel.toArray();
if (!sel.length) { return; }
var toAdd = sel.map(function (el) {
var curve = $(el).attr('data-curve');
// If the pad is woned by a team, we can transfer ownership to ourselves
if (curve === user.curvePublic && teamOwner) { return priv.edPublic; }
var friend = friends[curve];
if (!friend) { return; }
return friend.edPublic;
}).filter(function (x) { return x; });
var toAddTeams = sel.map(function (el) {
var team = teamsData[$(el).attr('data-teamid')];
if (!team || !team.edPublic) { return; }
return {
edPublic: team.edPublic,
id: $(el).attr('data-teamid')
};
}).filter(function (x) { return x; });
NThen(function (waitFor) {
var msg = Messages.owner_addConfirm;
UI.confirm(msg, waitFor(function (yes) {
if (!yes) {
waitFor.abort();
return;
}
}));
}).nThen(function (waitFor) {
// Add one of our teams as an owner
if (toAddTeams.length) {
// Send the command
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: 'ADD_OWNERS',
value: toAddTeams.map(function (obj) { return obj.edPublic; }),
teamId: teamOwner
}, waitFor(function (err, res) {
err = err || (res && res.error);
if (err) {
waitFor.abort();
redrawAll();
var text = err === "INSUFFICIENT_PERMISSIONS" ?
Messages.fm_forbidden : Messages.error;
return void UI.warn(text);
}
var isTemplate = priv.isTemplate || data.isTemplate;
toAddTeams.forEach(function (obj) {
sframeChan.query('Q_STORE_IN_TEAM', {
href: data.href || data.rohref,
password: data.password,
path: isTemplate ? ['template'] : undefined,
title: data.title || '',
teamId: obj.id
}, waitFor(function (err) {
if (err) { return void console.error(err); }
}));
});
}));
}
}).nThen(function (waitFor) {
// Offer ownership to a friend
if (toAdd.length) {
// Send the command
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: 'ADD_PENDING_OWNERS',
value: toAdd,
teamId: teamOwner
}, waitFor(function (err, res) {
err = err || (res && res.error);
if (err) {
waitFor.abort();
redrawAll();
var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden
: Messages.error;
return void UI.warn(text);
}
}));
}
}).nThen(function (waitFor) {
sel.forEach(function (el) {
var curve = $(el).attr('data-curve');
var friend = curve === user.curvePublic ? user : friends[curve];
if (!friend) { return; }
common.mailbox.sendTo("ADD_OWNER", {
channel: channel,
href: data.href,
password: data.password,
title: data.title
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
}, waitFor());
});
}).nThen(function () {
redrawAll();
UI.log(Messages.saved);
});
});
$div.append(h('p', addButton));
return $div;
};
redrawAll = function (md) {
var todo = function (obj) {
if (obj && obj.error) { return; }
owners = obj.owners || [];
pending_owners = obj.pending_owners || [];
$div1.empty();
$div2.empty();
$div1.append(drawRemove(false)).append(drawRemove(true));
$div2.append(drawAdd());
};
if (md) { return void todo(md); }
common.getPadMetadata({
channel: data.channel
}, todo);
};
$div1.append(drawRemove(false)).append(drawRemove(true));
$div2.append(drawAdd());
var handler = sframeChan.on('EV_RT_METADATA', function (md) {
if (!$div1.length) {
return void handler.stop();
}
owners = md.owners || [];
pending_owners = md.pending_owners || [];
redrawAll(md);
});
// Create modal
var link = h('div.cp-share-columns', [
div1,
div2
/*drawRemove()[0],
drawAdd()[0]*/
]);
var linkButtons = [{
className: 'cancel',
name: Messages.filePicker_close,
onClick: function () {},
keys: [27]
}];
return UI.dialog.customModal(link, {buttons: linkButtons});
};
var getRightsProperties = function (common, data, cb) {
var $div = $('<div>');
if (!data) { return void cb(void 0, $div); }
var draw = function () {
var $d = $('<div>');
var priv = common.getMetadataMgr().getPrivateData();
var user = common.getMetadataMgr().getUserData();
var edPublic = priv.edPublic;
var owned = false;
var _owners = {};
if (data.owners && data.owners.length) {
if (data.owners.indexOf(edPublic) !== -1) {
owned = true;
} else {
Object.keys(priv.teams || {}).some(function (id) {
var team = priv.teams[id] || {};
if (team.viewer) { return; }
if (data.owners.indexOf(team.edPublic) === -1) { return; }
owned = id;
return true;
});
}
var strangers = 0;
data.owners.forEach(function (ed) {
// If a friend is an owner, add their name to the list
// otherwise, increment the list of strangers
// Our edPublic? print "Yourself"
if (ed === edPublic) {
_owners[ed] = {
selected: true,
name: user.name,
avatar: user.avatar
};
return;
}
// One of our teams? print the team name
if (Object.keys(priv.teams || {}).some(function (id) {
var team = priv.teams[id] || {};
if (team.edPublic !== ed) { return; }
_owners[ed] = {
name: team.name,
avatar: team.avatar
};
return true;
})) {
return;
}
// One of our friends? print the friend name
if (Object.keys(priv.friends || {}).some(function (c) {
var friend = priv.friends[c] || {};
if (friend.edPublic !== ed || c === 'me') { return; }
_owners[friend.edPublic] = {
name: friend.displayName,
avatar: friend.avatar
};
return true;
})) {
return;
}
// Otherwise it's a stranger
strangers++;
});
if (strangers) {
_owners['stangers'] = {
name: Messages._getKey('properties_unknownUser', [strangers]),
};
}
}
var _ownersGrid = UIElements.getUserGrid(Messages.creation_owners, {
common: common,
noSelect: true,
data: _owners,
large: true
}, function () {});
if (_ownersGrid && Object.keys(_owners).length) {
$d.append(_ownersGrid.div);
} else {
$d.append([
h('label', Messages.creation_owners),
]);
$d.append(UI.dialog.selectable(Messages.creation_noOwner, {
id: 'cp-app-prop-owners',
}));
}
var parsed;
if (data.href || data.roHref) {
parsed = Hash.parsePadUrl(data.href || data.roHref);
}
if (owned && parsed.hashData.type === 'pad') {
var manageOwners = h('button.no-margin', Messages.owner_openModalButton);
$(manageOwners).click(function () {
data.teamId = typeof(owned) !== "boolean" ? owned : undefined;
var modal = createOwnerModal(common, data);
UI.openCustomModal(modal, {
wide: true,
});
});
$d.append(h('p', manageOwners));
}
if (!data.noExpiration) {
var expire = Messages.creation_expireFalse;
if (data.expire && typeof (data.expire) === "number") {
expire = new Date(data.expire).toLocaleString();
}
$('<label>', {'for': 'cp-app-prop-expire'}).text(Messages.creation_expiration)
.appendTo($d);
$d.append(UI.dialog.selectable(expire, {
id: 'cp-app-prop-expire',
}));
}
if (!data.noPassword) {
var hasPassword = data.password;
var $pwLabel = $('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
.hide().appendTo($d);
var password = UI.passwordInput({
id: 'cp-app-prop-password',
readonly: 'readonly'
});
var $password = $(password).hide();
var $pwInput = $password.find('.cp-password-input');
$pwInput.val(data.password).click(function () {
$pwInput[0].select();
});
$d.append(password);
if (hasPassword) {
$pwLabel.show();
$password.css('display', 'flex');
}
// In the properties, we should have the edit href if we know it.
// We should know it because the pad is stored, but it's better to check...
if (!data.noEditPassword && owned && data.href) { // FIXME SHEET fix password change for sheets
var sframeChan = common.getSframeChannel();
var isOO = parsed.type === 'sheet';
var isFile = parsed.hashData.type === 'file';
var isSharedFolder = parsed.type === 'drive';
var changePwTitle = Messages.properties_changePassword;
var changePwConfirm = isFile ? Messages.properties_confirmChangeFile : Messages.properties_confirmChange;
if (!hasPassword) {
changePwTitle = Messages.properties_addPassword;
changePwConfirm = isFile ? Messages.properties_confirmNewFile : Messages.properties_confirmNew;
}
$('<label>', {'for': 'cp-app-prop-change-password'})
.text(changePwTitle).appendTo($d);
var newPassword = UI.passwordInput({
id: 'cp-app-prop-change-password',
style: 'flex: 1;'
});
var passwordOk = h('button', Messages.properties_changePasswordButton);
var changePass = h('span.cp-password-change-container', [
newPassword,
passwordOk
]);
var pLocked = false;
$(passwordOk).click(function () {
var newPass = $(newPassword).find('input').val();
if (data.password === newPass ||
(!data.password && !newPass)) {
return void UI.alert(Messages.properties_passwordSame);
}
if (pLocked) { return; }
pLocked = true;
UI.confirm(changePwConfirm, function (yes) {
if (!yes) { pLocked = false; return; }
$(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'}));
var q = isFile ? 'Q_BLOB_PASSWORD_CHANGE' :
(isOO ? 'Q_OO_PASSWORD_CHANGE' : 'Q_PAD_PASSWORD_CHANGE');
// If this is a file password change, register to the upload events:
// * if there is a pending upload, ask if we shoudl interrupt
// * display upload progress
var onPending;
var onProgress;
if (isFile) {
onPending = sframeChan.on('Q_BLOB_PASSWORD_CHANGE_PENDING', function (data, cb) {
onPending.stop();
UI.confirm(Messages.upload_uploadPending, function (yes) {
cb({cancel: yes});
});
});
onProgress = sframeChan.on('EV_BLOB_PASSWORD_CHANGE_PROGRESS', function (data) {
if (typeof (data) !== "number") { return; }
var p = Math.round(data);
$(passwordOk).text(p + '%');
});
}
sframeChan.query(q, {
teamId: typeof(owned) !== "boolean" ? owned : undefined,
href: data.href,
password: newPass
}, function (err, data) {
$(passwordOk).text(Messages.properties_changePasswordButton);
pLocked = false;
if (err || data.error) {
console.error(err || data.error);
return void UI.alert(Messages.properties_passwordError);
}
UI.findOKButton().click();
$pwLabel.show();
$password.css('display', 'flex');
$pwInput.val(newPass);
// If the current document is a file or if we're changing the password from a drive,
// we don't have to reload the page at the end.
// Tell the user the password change was successful and abort
if (isFile || priv.app !== parsed.type) {
if (onProgress && onProgress.stop) { onProgress.stop(); }
$(passwordOk).text(Messages.properties_changePasswordButton);
var alertMsg = data.warning ? Messages.properties_passwordWarningFile
: Messages.properties_passwordSuccessFile;
return void UI.alert(alertMsg, undefined, {force: true});
}
// Pad password changed: update the href
// Use hidden hash if needed (we're an owner of this pad so we know it is stored)
var useUnsafe = Util.find(priv, ['settings', 'security', 'unsafeLinks']);
var href = (priv.readOnly && data.roHref) ? data.roHref : data.href;
if (useUnsafe === false) {
var newParsed = Hash.parsePadUrl(href);
var newSecret = Hash.getSecrets(newParsed.type, newParsed.hash, newPass);
var newHash = Hash.getHiddenHashFromKeys(parsed.type, newSecret, {});
href = Hash.hashToHref(newHash, parsed.type);
}
if (data.warning) {
return void UI.alert(Messages.properties_passwordWarning, function () {
common.gotoURL(href);
}, {force: true});
}
return void UI.alert(Messages.properties_passwordSuccess, function () {
if (!isSharedFolder) {
common.gotoURL(href);
}
}, {force: true});
});
});
});
$d.append(changePass);
}
}
return $d;
};
var sframeChan = common.getSframeChannel();
var handler = sframeChan.on('EV_RT_METADATA', function (md) {
if (!$div.length) {
handler.stop();
return;
}
md = JSON.parse(JSON.stringify(md));
data.owners = md.owners;
data.expire = md.expire;
data.pending_owners = md.pending_owners;
$div.empty();
$div.append(draw());
});
$div.append(draw());
cb(void 0, $div);
};
var getPadProperties = function (common, data, cb) {
var getPadProperties = function (common, data, opts, cb) {
opts = opts || {};
var $d = $('<div>');
if (!data) { return void cb(void 0, $d); }
@ -695,7 +115,7 @@ define([
}));
}
if (data.roHref) {
if (data.roHref && !opts.noReadOnly) {
$('<label>', {'for': 'cp-app-prop-rolink'}).text(Messages.viewShare).appendTo($d);
$d.append(UI.dialog.selectable(data.roHref, {
id: 'cp-app-prop-rolink',
@ -839,7 +259,8 @@ define([
};
var getPropertiesData = function (common, cb) {
var getPropertiesData = function (common, opts, cb) {
opts = opts || {};
var data = {};
NThen(function (waitFor) {
var base = common.getMetadataMgr().getPrivateData().origin;
@ -855,46 +276,33 @@ define([
Util.extend(data, val);
if (data.href) { data.href = base + data.href; }
if (data.roHref) { data.roHref = base + data.roHref; }
}));
common.getPadMetadata(null, waitFor(function (obj) {
if (obj && obj.error) { return; }
data.owners = obj.owners;
data.expire = obj.expire;
data.pending_owners = obj.pending_owners;
}));
}), opts.href);
}).nThen(function () {
cb(void 0, data);
});
};
UIElements.getProperties = function (common, data, cb) {
var c1;
var c2;
var button = [{
className: 'primary',
name: Messages.okButton,
onClick: function () {},
keys: [13]
}];
UIElements.getProperties = function (common, opts, cb) {
var data;
var content;
NThen(function (waitFor) {
getPadProperties(common, data, waitFor(function (e, c) {
c1 = UI.dialog.customModal(c[0], {
buttons: button
});
getPropertiesData(common, opts, waitFor(function (e, _data) {
if (e) {
waitFor.abort();
return void cb(e);
}
data = _data;
}));
getRightsProperties(common, data, waitFor(function (e, c) {
c2 = UI.dialog.customModal(c[0], {
buttons: button
});
}).nThen(function (waitFor) {
getPadProperties(common, data, opts, waitFor(function (e, c) {
if (e) {
waitFor.abort();
return void cb(e);
}
content = c[0];
}));
}).nThen(function () {
var tabs = UI.dialog.tabs([{
title: Messages.fc_prop,
content: c1
}, {
title: Messages.creation_propertiesTitle,
content: c2
}]);
cb (void 0, $(tabs));
var modal = UI.alert(content);
cb (void 0, modal);
});
};
@ -2382,12 +1790,8 @@ define([
if (!data) {
return void UI.alert(Messages.autostore_notAvailable);
}
getPropertiesData(common, function (e, data) {
UIElements.getProperties(common, {}, function (e) {
if (e) { return void console.error(e); }
UIElements.getProperties(common, data, function (e, $prop) {
if (e) { return void console.error(e); }
UI.openCustomModal($prop[0]);
});
});
});
});

@ -8,6 +8,9 @@ define([
'/common/common-interface.js',
'/common/common-constants.js',
'/common/common-feedback.js',
'/common/inner/access.js',
'/bower_components/nthen/index.js',
'/common/hyperscript.js',
'/common/proxy-manager.js',
@ -23,6 +26,7 @@ define([
UI,
Constants,
Feedback,
Access,
nThen,
h,
ProxyManager,
@ -79,6 +83,7 @@ define([
var faColor = 'cptools-palette';
var faTrash = 'fa-trash';
var faDelete = 'fa-eraser';
var faAccess = 'fa-unlock-alt';
var faProperties = 'fa-info-circle';
var faTags = 'fa-hashtag';
var faUploadFiles = 'cptools-file-upload';
@ -448,6 +453,10 @@ define([
'data-icon': faDelete,
}, Messages.fc_remove_sharedfolder)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-access.dropdown-item', {
'tabindex': '-1',
'data-icon': faAccess,
}, "ACCESS")), // XXX
h('li', h('a.cp-app-drive-context-properties.dropdown-item', {
'tabindex': '-1',
'data-icon': faProperties,
@ -1203,7 +1212,7 @@ define([
hide.push('savelocal');
hide.push('openro');
hide.push('openincode');
hide.push('properties');
hide.push('properties', 'access');
hide.push('hashtag');
}
// If we're in the trash, hide restore and properties for non-root elements
@ -1233,7 +1242,7 @@ define([
});
if (paths.length > 1) {
hide.push('restore');
hide.push('properties');
hide.push('properties', 'access');
hide.push('rename');
hide.push('openparent');
hide.push('hashtag');
@ -1259,10 +1268,10 @@ define([
case 'tree':
show = ['open', 'openro', 'openincode', 'expandall', 'collapseall',
'color', 'download', 'share', 'savelocal', 'rename', 'delete',
'deleteowned', 'removesf', 'properties', 'hashtag'];
'deleteowned', 'removesf', 'properties', 'access', 'hashtag'];
break;
case 'default':
show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'hashtag'];
show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'access', 'hashtag'];
break;
case 'trashtree': {
show = ['empty'];
@ -3074,9 +3083,8 @@ define([
}).appendTo($openDir);
}
$('<a>').text(Messages.fc_prop).click(function () {
APP.getProperties(r.id, function (e, $prop) {
APP.getProperties(r.id, function (e) {
if (e) { return void logError(e); }
UI.alert($prop[0], undefined, true);
});
}).appendTo($openDir);
}
@ -3824,12 +3832,11 @@ define([
}
};
var getProperties = APP.getProperties = function (el, cb) {
APP.getProperties = function (el, cb) {
if (!manager.isFile(el) && !manager.isSharedFolder(el)) {
return void cb('NOT_FILE');
}
//var ro = manager.isReadOnlyFile(el);
var base = APP.origin;
var data;
if (manager.isSharedFolder(el)) {
data = JSON.parse(JSON.stringify(manager.getSharedFolderData(el)));
@ -3838,42 +3845,42 @@ define([
}
if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
if (data.href) {
data.href = base + data.href;
var opts = {};
opts.href = Hash.getRelativeHref(data.href || data.roHref);
if (manager.isSharedFolder(el)) {
var ro = folders[el] && folders[el].version >= 2;
if (!ro) { opts.noReadOnly = true; }
}
if (data.roHref) {
data.roHref = base + data.roHref;
UIElements.getProperties(common, opts, cb);
};
APP.getAccess = function (el, cb) {
if (!manager.isFile(el) && !manager.isSharedFolder(el)) {
return void cb('NOT_FILE');
}
var data;
if (manager.isSharedFolder(el)) {
data = JSON.parse(JSON.stringify(manager.getSharedFolderData(el)));
} else {
data = JSON.parse(JSON.stringify(manager.getFileData(el)));
}
if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
var opts = {};
opts.href = Hash.getRelativeHref(data.href || data.roHref);
opts.channel = data.channel;
// Transfer ownership: templates are stored as templates for other users/teams
if (currentPath[0] === TEMPLATE) {
data.isTemplate = true;
opts.isTemplate = true;
}
// Shared folders: no expiration date
if (manager.isSharedFolder(el)) {
var ro = folders[el] && folders[el].version >= 2;
if (!ro) { delete data.roHref; }
//data.noPassword = true;
//data.noEditPassword = true;
data.noExpiration = true;
// this is here to allow users to check the channel id of a shared folder
// we should remove it at some point
data.sharedFolder = true;
}
if ((manager.isFile(el) && data.roHref) || manager.isSharedFolder(el)) { // Only for pads!
sframeChan.query('Q_GET_PAD_METADATA', {
channel: data.channel
}, function (err, val) {
if (!err && !(val && val.error)) {
data.owners = val.owners;
data.expire = val.expire;
data.pending_owners = val.pending_owners;
}
UIElements.getProperties(common, data, cb);
});
return;
opts.noExpiration = true;
}
UIElements.getProperties(common, data, cb);
Access.getAccessModal(common, opts, cb);
};
if (!APP.loggedIn) {
@ -4218,9 +4225,19 @@ define([
// ANON_SHARED_FOLDER
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
}
getProperties(el, function (e, $prop) {
APP.getProperties(el, function (e) {
if (e) { return void logError(e); }
});
}
else if ($this.hasClass("cp-app-drive-context-access")) {
if (paths.length !== 1) { return; }
el = manager.find(paths[0].path);
if (paths[0].path[0] === SHARED_FOLDER && APP.newSharedFolder) {
// ANON_SHARED_FOLDER
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
}
APP.getAccess(el, function (e) {
if (e) { return void logError(e); }
UI.openCustomModal($prop[0]);
});
}
else if ($this.hasClass("cp-app-drive-context-hashtag")) {

@ -0,0 +1,658 @@
define([
'jquery',
'/common/common-util.js',
'/common/common-hash.js',
'/common/common-interface.js',
'/common/common-ui-elements.js',
'/common/hyperscript.js',
'/customize/messages.js',
'/bower_components/nthen/index.js',
], function ($, Util, Hash, UI, UIElements, h,
Messages, nThen) {
console.log(UI, UIElements, h);
var Access = {};
var createOwnerModal = function (common, data) {
var friends = common.getFriends(true);
var sframeChan = common.getSframeChannel();
var priv = common.getMetadataMgr().getPrivateData();
var user = common.getMetadataMgr().getUserData();
var edPublic = priv.edPublic;
var channel = data.channel;
var owners = data.owners || [];
var pending_owners = data.pending_owners || [];
var teams = priv.teams;
var teamOwner = data.teamId;
var redrawAll = function () {};
var div1 = h('div.cp-usergrid-user.cp-share-column.cp-ownership');
var div2 = h('div.cp-usergrid-user.cp-share-column.cp-ownership');
var $div1 = $(div1);
var $div2 = $(div2);
// Remove owner column
var drawRemove = function (pending) {
var _owners = {};
var o = (pending ? pending_owners : owners) || [];
o.forEach(function (ed) {
var f;
Object.keys(friends).some(function (c) {
if (friends[c].edPublic === ed) {
f = friends[c];
return true;
}
});
Object.keys(teams).some(function (id) {
if (teams[id].edPublic === ed) {
f = teams[id];
f.teamId = id;
}
});
if (ed === edPublic) {
f = f || user;
if (f.name) { f.edPublic = edPublic; }
}
_owners[ed] = f || {
displayName: Messages._getKey('owner_unknownUser', [ed]),
edPublic: ed,
};
});
var msg = pending ? Messages.owner_removePendingText
: Messages.owner_removeText;
var removeCol = UIElements.getUserGrid(msg, {
common: common,
large: true,
data: _owners,
noFilter: true
}, function () {
});
var $div = $(removeCol.div);
// When clicking on the remove button, we check the selected users.
// If you try to remove yourself, we'll display an additional warning message
var btnMsg = pending ? Messages.owner_removePendingButton : Messages.owner_removeButton;
var removeButton = h('button.no-margin', btnMsg);
$(removeButton).click(function () {
// Check selection
var $sel = $div.find('.cp-usergrid-user.cp-selected');
var sel = $sel.toArray();
if (!sel.length) { return; }
var me = false;
var toRemove = sel.map(function (el) {
var ed = $(el).attr('data-ed');
if (!ed) { return; }
if (teamOwner && teams[teamOwner] && teams[teamOwner].edPublic === ed) { me = true; }
if (ed === edPublic && !teamOwner) { me = true; }
return ed;
}).filter(function (x) { return x; });
nThen(function (waitFor) {
var msg = me ? Messages.owner_removeMeConfirm : Messages.owner_removeConfirm;
UI.confirm(msg, waitFor(function (yes) {
if (!yes) {
waitFor.abort();
return;
}
}));
}).nThen(function (waitFor) {
// Send the command
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: pending ? 'RM_PENDING_OWNERS' : 'RM_OWNERS',
value: toRemove,
teamId: teamOwner
}, waitFor(function (err, res) {
err = err || (res && res.error);
if (err) {
waitFor.abort();
redrawAll();
var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden
: Messages.error;
return void UI.warn(text);
}
UI.log(Messages.saved);
}));
}).nThen(function (waitFor) {
sel.forEach(function (el) {
var curve = $(el).attr('data-curve');
var friend = curve === user.curvePublic ? user : friends[curve];
if (!friend) { return; }
common.mailbox.sendTo("RM_OWNER", {
channel: channel,
title: data.title,
pending: pending
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
}, waitFor());
});
}).nThen(function () {
redrawAll();
});
});
$div.append(h('p', removeButton));
return $div;
};
// Add owners column
var drawAdd = function () {
var $div = $(h('div.cp-share-column'));
var _friends = JSON.parse(JSON.stringify(friends));
Object.keys(_friends).forEach(function (curve) {
if (owners.indexOf(_friends[curve].edPublic) !== -1 ||
pending_owners.indexOf(_friends[curve].edPublic) !== -1 ||
!_friends[curve].notifications) {
delete _friends[curve];
}
});
var addCol = UIElements.getUserGrid(Messages.owner_addText, {
common: common,
large: true,
data: _friends
}, function () {
//console.log(arguments);
});
$div.append(addCol.div);
var teamsData = Util.tryParse(JSON.stringify(priv.teams)) || {};
Object.keys(teamsData).forEach(function (id) {
var t = teamsData[id];
t.teamId = id;
if (owners.indexOf(t.edPublic) !== -1 || pending_owners.indexOf(t.edPublic) !== -1) {
delete teamsData[id];
}
});
var teamsList = UIElements.getUserGrid(Messages.owner_addTeamText, {
common: common,
large: true,
noFilter: true,
data: teamsData
}, function () {});
$div.append(teamsList.div);
// When clicking on the add button, we get the selected users.
var addButton = h('button.no-margin', Messages.owner_addButton);
$(addButton).click(function () {
// Check selection
var $sel = $div.find('.cp-usergrid-user.cp-selected');
var sel = $sel.toArray();
if (!sel.length) { return; }
var toAdd = sel.map(function (el) {
var curve = $(el).attr('data-curve');
// If the pad is woned by a team, we can transfer ownership to ourselves
if (curve === user.curvePublic && teamOwner) { return priv.edPublic; }
var friend = friends[curve];
if (!friend) { return; }
return friend.edPublic;
}).filter(function (x) { return x; });
var toAddTeams = sel.map(function (el) {
var team = teamsData[$(el).attr('data-teamid')];
if (!team || !team.edPublic) { return; }
return {
edPublic: team.edPublic,
id: $(el).attr('data-teamid')
};
}).filter(function (x) { return x; });
nThen(function (waitFor) {
var msg = Messages.owner_addConfirm;
UI.confirm(msg, waitFor(function (yes) {
if (!yes) {
waitFor.abort();
return;
}
}));
}).nThen(function (waitFor) {
// Add one of our teams as an owner
if (toAddTeams.length) {
// Send the command
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: 'ADD_OWNERS',
value: toAddTeams.map(function (obj) { return obj.edPublic; }),
teamId: teamOwner
}, waitFor(function (err, res) {
err = err || (res && res.error);
if (err) {
waitFor.abort();
redrawAll();
var text = err === "INSUFFICIENT_PERMISSIONS" ?
Messages.fm_forbidden : Messages.error;
return void UI.warn(text);
}
var isTemplate = priv.isTemplate || data.isTemplate;
toAddTeams.forEach(function (obj) {
sframeChan.query('Q_STORE_IN_TEAM', {
href: data.href || data.rohref,
password: data.password,
path: isTemplate ? ['template'] : undefined,
title: data.title || '',
teamId: obj.id
}, waitFor(function (err) {
if (err) { return void console.error(err); }
}));
});
}));
}
}).nThen(function (waitFor) {
// Offer ownership to a friend
if (toAdd.length) {
// Send the command
sframeChan.query('Q_SET_PAD_METADATA', {
channel: channel,
command: 'ADD_PENDING_OWNERS',
value: toAdd,
teamId: teamOwner
}, waitFor(function (err, res) {
err = err || (res && res.error);
if (err) {
waitFor.abort();
redrawAll();
var text = err === "INSUFFICIENT_PERMISSIONS" ? Messages.fm_forbidden
: Messages.error;
return void UI.warn(text);
}
}));
}
}).nThen(function (waitFor) {
sel.forEach(function (el) {
var curve = $(el).attr('data-curve');
var friend = curve === user.curvePublic ? user : friends[curve];
if (!friend) { return; }
common.mailbox.sendTo("ADD_OWNER", {
channel: channel,
href: data.href,
password: data.password,
title: data.title
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
}, waitFor());
});
}).nThen(function () {
redrawAll();
UI.log(Messages.saved);
});
});
$div.append(h('p', addButton));
return $div;
};
redrawAll = function (md) {
var todo = function (obj) {
if (obj && obj.error) { return; }
owners = obj.owners || [];
pending_owners = obj.pending_owners || [];
$div1.empty();
$div2.empty();
$div1.append(drawRemove(false)).append(drawRemove(true));
$div2.append(drawAdd());
};
if (md) { return void todo(md); }
common.getPadMetadata({
channel: data.channel
}, todo);
};
$div1.append(drawRemove(false)).append(drawRemove(true));
$div2.append(drawAdd());
var handler = sframeChan.on('EV_RT_METADATA', function (md) {
if (!$div1.length) {
return void handler.stop();
}
owners = md.owners || [];
pending_owners = md.pending_owners || [];
redrawAll(md);
});
// Create modal
var link = h('div.cp-share-columns', [
div1,
div2
/*drawRemove()[0],
drawAdd()[0]*/
]);
var linkButtons = [{
className: 'cancel',
name: Messages.filePicker_close,
onClick: function () {},
keys: [27]
}];
return UI.dialog.customModal(link, {buttons: linkButtons});
};
var getRightsProperties = function (common, data, cb) {
var $div = $('<div>');
if (!data) { return void cb(void 0, $div); }
var draw = function () {
var $d = $('<div>');
var priv = common.getMetadataMgr().getPrivateData();
var user = common.getMetadataMgr().getUserData();
var edPublic = priv.edPublic;
var owned = false;
var _owners = {};
if (data.owners && data.owners.length) {
if (data.owners.indexOf(edPublic) !== -1) {
owned = true;
} else {
Object.keys(priv.teams || {}).some(function (id) {
var team = priv.teams[id] || {};
if (team.viewer) { return; }
if (data.owners.indexOf(team.edPublic) === -1) { return; }
owned = id;
return true;
});
}
var strangers = 0;
data.owners.forEach(function (ed) {
// If a friend is an owner, add their name to the list
// otherwise, increment the list of strangers
// Our edPublic? print "Yourself"
if (ed === edPublic) {
_owners[ed] = {
selected: true,
name: user.name,
avatar: user.avatar
};
return;
}
// One of our teams? print the team name
if (Object.keys(priv.teams || {}).some(function (id) {
var team = priv.teams[id] || {};
if (team.edPublic !== ed) { return; }
_owners[ed] = {
name: team.name,
avatar: team.avatar
};
return true;
})) {
return;
}
// One of our friends? print the friend name
if (Object.keys(priv.friends || {}).some(function (c) {
var friend = priv.friends[c] || {};
if (friend.edPublic !== ed || c === 'me') { return; }
_owners[friend.edPublic] = {
name: friend.displayName,
avatar: friend.avatar
};
return true;
})) {
return;
}
// Otherwise it's a stranger
strangers++;
});
if (strangers) {
_owners['stangers'] = {
name: Messages._getKey('properties_unknownUser', [strangers]),
};
}
}
var _ownersGrid = UIElements.getUserGrid(Messages.creation_owners, {
common: common,
noSelect: true,
data: _owners,
large: true
}, function () {});
if (_ownersGrid && Object.keys(_owners).length) {
$d.append(_ownersGrid.div);
} else {
$d.append([
h('label', Messages.creation_owners),
]);
$d.append(UI.dialog.selectable(Messages.creation_noOwner, {
id: 'cp-app-prop-owners',
}));
}
var parsed;
if (data.href || data.roHref) {
parsed = Hash.parsePadUrl(data.href || data.roHref);
}
if (owned && parsed.hashData.type === 'pad') {
var manageOwners = h('button.no-margin', Messages.owner_openModalButton);
$(manageOwners).click(function () {
data.teamId = typeof(owned) !== "boolean" ? owned : undefined;
var modal = createOwnerModal(common, data);
UI.openCustomModal(modal, {
wide: true,
});
});
$d.append(h('p', manageOwners));
}
if (!data.noExpiration) {
var expire = Messages.creation_expireFalse;
if (data.expire && typeof (data.expire) === "number") {
expire = new Date(data.expire).toLocaleString();
}
$('<label>', {'for': 'cp-app-prop-expire'}).text(Messages.creation_expiration)
.appendTo($d);
$d.append(UI.dialog.selectable(expire, {
id: 'cp-app-prop-expire',
}));
}
if (!data.noPassword) {
var hasPassword = data.password;
var $pwLabel = $('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
.hide().appendTo($d);
var password = UI.passwordInput({
id: 'cp-app-prop-password',
readonly: 'readonly'
});
var $password = $(password).hide();
var $pwInput = $password.find('.cp-password-input');
$pwInput.val(data.password).click(function () {
$pwInput[0].select();
});
$d.append(password);
if (hasPassword) {
$pwLabel.show();
$password.css('display', 'flex');
}
// In the properties, we should have the edit href if we know it.
// We should know it because the pad is stored, but it's better to check...
if (!data.noEditPassword && owned && data.href) { // FIXME SHEET fix password change for sheets
var sframeChan = common.getSframeChannel();
var isOO = parsed.type === 'sheet';
var isFile = parsed.hashData.type === 'file';
var isSharedFolder = parsed.type === 'drive';
var changePwTitle = Messages.properties_changePassword;
var changePwConfirm = isFile ? Messages.properties_confirmChangeFile : Messages.properties_confirmChange;
if (!hasPassword) {
changePwTitle = Messages.properties_addPassword;
changePwConfirm = isFile ? Messages.properties_confirmNewFile : Messages.properties_confirmNew;
}
$('<label>', {'for': 'cp-app-prop-change-password'})
.text(changePwTitle).appendTo($d);
var newPassword = UI.passwordInput({
id: 'cp-app-prop-change-password',
style: 'flex: 1;'
});
var passwordOk = h('button', Messages.properties_changePasswordButton);
var changePass = h('span.cp-password-change-container', [
newPassword,
passwordOk
]);
var pLocked = false;
$(passwordOk).click(function () {
var newPass = $(newPassword).find('input').val();
if (data.password === newPass ||
(!data.password && !newPass)) {
return void UI.alert(Messages.properties_passwordSame);
}
if (pLocked) { return; }
pLocked = true;
UI.confirm(changePwConfirm, function (yes) {
if (!yes) { pLocked = false; return; }
$(passwordOk).html('').append(h('span.fa.fa-spinner.fa-spin', {style: 'margin-left: 0'}));
var q = isFile ? 'Q_BLOB_PASSWORD_CHANGE' :
(isOO ? 'Q_OO_PASSWORD_CHANGE' : 'Q_PAD_PASSWORD_CHANGE');
// If this is a file password change, register to the upload events:
// * if there is a pending upload, ask if we shoudl interrupt
// * display upload progress
var onPending;
var onProgress;
if (isFile) {
onPending = sframeChan.on('Q_BLOB_PASSWORD_CHANGE_PENDING', function (data, cb) {
onPending.stop();
UI.confirm(Messages.upload_uploadPending, function (yes) {
cb({cancel: yes});
});
});
onProgress = sframeChan.on('EV_BLOB_PASSWORD_CHANGE_PROGRESS', function (data) {
if (typeof (data) !== "number") { return; }
var p = Math.round(data);
$(passwordOk).text(p + '%');
});
}
sframeChan.query(q, {
teamId: typeof(owned) !== "boolean" ? owned : undefined,
href: data.href,
password: newPass
}, function (err, data) {
$(passwordOk).text(Messages.properties_changePasswordButton);
pLocked = false;
if (err || data.error) {
console.error(err || data.error);
return void UI.alert(Messages.properties_passwordError);
}
UI.findOKButton().click();
$pwLabel.show();
$password.css('display', 'flex');
$pwInput.val(newPass);
// If the current document is a file or if we're changing the password from a drive,
// we don't have to reload the page at the end.
// Tell the user the password change was successful and abort
if (isFile || priv.app !== parsed.type) {
if (onProgress && onProgress.stop) { onProgress.stop(); }
$(passwordOk).text(Messages.properties_changePasswordButton);
var alertMsg = data.warning ? Messages.properties_passwordWarningFile
: Messages.properties_passwordSuccessFile;
return void UI.alert(alertMsg, undefined, {force: true});
}
// Pad password changed: update the href
// Use hidden hash if needed (we're an owner of this pad so we know it is stored)
var useUnsafe = Util.find(priv, ['settings', 'security', 'unsafeLinks']);
var href = (priv.readOnly && data.roHref) ? data.roHref : data.href;
if (useUnsafe === false) {
var newParsed = Hash.parsePadUrl(href);
var newSecret = Hash.getSecrets(newParsed.type, newParsed.hash, newPass);
var newHash = Hash.getHiddenHashFromKeys(parsed.type, newSecret, {});
href = Hash.hashToHref(newHash, parsed.type);
}
if (data.warning) {
return void UI.alert(Messages.properties_passwordWarning, function () {
common.gotoURL(href);
}, {force: true});
}
return void UI.alert(Messages.properties_passwordSuccess, function () {
if (!isSharedFolder) {
common.gotoURL(href);
}
}, {force: true});
});
});
});
$d.append(changePass);
}
}
return $d;
};
var sframeChan = common.getSframeChannel();
var handler = sframeChan.on('EV_RT_METADATA', function (md) {
if (!$div.length) {
handler.stop();
return;
}
md = JSON.parse(JSON.stringify(md));
data.owners = md.owners;
data.expire = md.expire;
data.pending_owners = md.pending_owners;
$div.empty();
$div.append(draw());
});
$div.append(draw());
cb(void 0, $div);
};
var getAccessData = function (common, opts, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
opts = opts || {};
var data = {};
nThen(function (waitFor) {
var base = common.getMetadataMgr().getPrivateData().origin;
common.getPadAttribute('', waitFor(function (err, val) {
if (err || !val) {
waitFor.abort();
return void cb(err || 'EEMPTY');
}
if (!val.fileType) {
delete val.owners;
delete val.expire;
}
Util.extend(data, val);
if (data.href) { data.href = base + data.href; }
if (data.roHref) { data.roHref = base + data.roHref; }
}), opts.href);
// If this is a file, don't try to look for metadata
if (opts.channel && opts.channel.length > 34) { return; }
common.getPadMetadata({
channel: opts.channel // optional, fallback to current pad
}, waitFor(function (obj) {
if (obj && obj.error) { console.error(obj.error); return; }
data.owners = obj.owners;
data.expire = obj.expire;
data.pending_owners = obj.pending_owners;
}));
}).nThen(function () {
cb(void 0, data);
});
};
Access.getAccessModal = function (common, opts, cb) {
var data;
var content;
nThen(function (waitFor) {
getAccessData(common, opts, waitFor(function (e, _data) {
if (e) {
waitFor.abort();
return void cb(e);
}
data = _data;
}));
}).nThen(function (waitFor) {
getRightsProperties(common, data, waitFor(function (e, c) {
if (e) {
waitFor.abort();
return void cb(e);
}
content = c[0];
}));
}).nThen(function () {
var modal = UI.alert(content);
cb (void 0, modal);
});
};
return Access;
});
Loading…
Cancel
Save