Merge pull request #2234 from cryptpad/team-pin

fix(pinning): fix multiple identical RESET commands stored in pin log
This commit is contained in:
yflory 2026-03-12 15:27:48 +01:00 committed by GitHub
commit 37d674dc74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 71 additions and 101 deletions

View File

@ -143,10 +143,10 @@ const factory = (UserObject, Util, Hash,
var _ownedByMe = function (Env, owners) {
return Array.isArray(owners) && owners.indexOf(Env.edPublic) !== -1;
};
var _ownedByOther = function (Env, owners) {
return Array.isArray(owners) && owners.length &&
(!Env.edPublic || owners.indexOf(Env.edPublic) === -1);
};
// var _ownedByOther = function (Env, owners) {
// return Array.isArray(owners) && owners.length &&
// (!Env.edPublic || owners.indexOf(Env.edPublic) === -1);
// };
var _getUserObjects = function (Env) {
var userObjects = [Env.user.userObject];
@ -1307,37 +1307,30 @@ const factory = (UserObject, Util, Hash,
*/
var excludeInvalidIdentifiers = function (result) {
return result.filter(function (channel) {
const filter = (channel) => {
if (typeof(channel) !== 'string') { return; }
return [32, 48].indexOf(channel.length) !== -1;
});
};
let newSet = new Set();
for (var channel of result) {
if(filter(channel)) { newSet.add(channel); }
}
return newSet;
};
// Get the list of channels filtered by a type (expirable channels, owned channels, pin list)
var getChannelsList = function (Env, type) {
var result = [];
//var result = [];
let result = new Set();
var addChannel = function (userObject) {
if (type === 'expirable') {
return function (fileId) {
var data = userObject.getFileData(fileId);
if (!data) { return; }
// Don't push duplicates
if (result.indexOf(data.channel) !== -1) { return; }
// Return pads owned by someone else or expired by time
if (_ownedByOther(Env, data.owners) || (data.expire && data.expire < (+new Date()))) {
result.push(data.channel);
}
};
}
if (type === 'owned') {
return function (fileId) {
var data = userObject.getFileData(fileId);
if (!data) { return; }
// Don't push duplicates
if (result.indexOf(data.channel) !== -1) { return; }
// Return owned pads
if (_ownedByMe(Env, data.owners)) {
result.push(data.channel);
result.add(data.channel);
}
};
}
@ -1345,37 +1338,31 @@ const factory = (UserObject, Util, Hash,
return function (fileId) {
var data = userObject.getFileData(fileId);
if (!data) { return; }
// Don't pin pads owned by someone else
//if (_ownedByOther(Env, data.owners)) { return; }
// Pin onlyoffice checkpoints
if (data.lastVersion) {
var otherChan = Hash.hrefToHexChannelId(data.lastVersion);
if (result.indexOf(otherChan) === -1) {
result.push(otherChan);
}
result.add(otherChan);
}
// Pin form answers channels
if (data.answersChannel && result.indexOf(data.answersChannel) === -1) {
result.push(data.answersChannel);
if (data.answersChannel) {
result.add(data.answersChannel);
}
// Pin onlyoffice realtime patches
if (data.rtChannel && result.indexOf(data.rtChannel) === -1) {
result.push(data.rtChannel);
if (data.rtChannel) {
result.add(data.rtChannel);
}
// Pin onlyoffice images
if (data.ooImages && Array.isArray(data.ooImages)) {
Array.prototype.push.apply(result, data.ooImages);
data.ooImages.forEach(id => result.add(id));
}
// Pin the pad
if (result.indexOf(data.channel) === -1) {
result.push(data.channel);
}
result.add(data.channel);
};
}
};
if (type === 'owned' && !Env.edPublic) { return excludeInvalidIdentifiers(result); }
if (type === 'pin' && !Env.edPublic) { return excludeInvalidIdentifiers(result); }
if (type === 'owned' && !Env.edPublic) { return result; }
if (type === 'pin' && !Env.edPublic) { return result; }
// Get the list of user objects
var userObjects = _getUserObjects(Env);
@ -1393,7 +1380,7 @@ const factory = (UserObject, Util, Hash,
}).map(function (fId) {
return Env.user.proxy[UserObject.SHARED_FOLDERS][fId].channel;
});
Array.prototype.push.apply(result, sfOwned);
sfOwned.forEach(id => result.add(id));
}
if (type === "pin") {
var sfChannels = Object.keys(Env.folders).map(function (fId) {
@ -1403,7 +1390,7 @@ const factory = (UserObject, Util, Hash,
console.error(err);
}
}).filter(Boolean);
Array.prototype.push.apply(result, sfChannels);
sfChannels.forEach(id => result.add(id));
}
return excludeInvalidIdentifiers(result);

View File

@ -213,31 +213,36 @@ const factory = (Sortify, UserObject, ProxyManager,
});
};
var getUserChannelList = function () {
var userChannel = `${store.driveChannel}#drive`;
var getUserChannelList = function (compareHash) {
// If compareHash is true, we're going to compare our local
// hash with the server one. We must remove the "#drive" tag
// and the block (which isn't a channel)
var userChannel = compareHash ? store.driveChannel
: `${store.driveChannel}#drive`;
if (!userChannel) { return null; }
// Get the list of pads' channel ID in your drive
// This list is filtered so that it doesn't include pad owned by other users
// It now includes channels from shared folders
var list = store.manager.getChannelsList('pin');
var list = store.manager.getChannelsList('pin'); // "list" is a Set
// Get the avatar & profile
var profile = store.proxy.profile;
if (profile) {
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
if (profileChan) { list.push(profileChan); }
if (profileChan) { list.add(profileChan); }
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar, null) : null;
if (avatarChan) { list.push(avatarChan); }
if (avatarChan) { list.add(avatarChan); }
}
if (store.proxy.todo) {
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
list.add(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
}
if (store.proxy.friends) {
var fList = Messaging.getFriendChannelsList(store.proxy);
list = list.concat(fList);
fList.forEach(id => list.add(id));
}
if (store.proxy.mailboxes) {
@ -245,34 +250,23 @@ const factory = (Sortify, UserObject, ProxyManager,
if (m === "broadcast" && !store.isAdmin) { return; }
return store.proxy.mailboxes[m].channel;
}).filter(Boolean);
list = list.concat(mList);
mList.forEach(id => list.add(id));
}
if (store.proxy.calendars) {
var cList = Object.keys(store.proxy.calendars).map(function (c) {
return store.proxy.calendars[c].channel;
});
list = list.concat(cList);
cList.forEach(id => list.add(id));
}
list.push(userChannel);
list.add(userChannel);
if (store.data && store.data.blockId) {
list.push(`${store.data.blockId}#block`);
if (store.data && store.data.blockId && !compareHash) {
list.add(`${store.data.blockId}#block`);
}
list.sort();
return list;
};
var getExpirableChannelList = function () {
return store.manager.getChannelsList('expirable');
};
var getCanonicalChannelList = function (expirable) {
var list = expirable ? getExpirableChannelList() : getUserChannelList();
return Util.deduplicateString(list).sort();
return Array.from(list).sort();
};
//////////////////////////////////////////////////////////////////
@ -352,7 +346,7 @@ const factory = (Sortify, UserObject, ProxyManager,
var arePinsSynced = function (cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var list = getCanonicalChannelList(false);
var list = getUserChannelList(true);
var local = Hash.hashChannelList(list);
store.rpc.getServerHash(function (e, hash) {
if (e) { return void cb(e); }
@ -363,7 +357,7 @@ const factory = (Sortify, UserObject, ProxyManager,
var resetPins = function (cb) {
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
var list = getCanonicalChannelList(false);
var list = getUserChannelList();
store.rpc.reset(list, function (e) {
if (e) { return void cb(e); }
cb(null);
@ -524,7 +518,7 @@ const factory = (Sortify, UserObject, ProxyManager,
Store.getDeletedPads = function (clientId, data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var list = (data && data.list) || getCanonicalChannelList(true);
var list = data?.list;
if (!Array.isArray(list)) {
return void cb({error: 'INVALID_FILE_LIST'});
}
@ -705,8 +699,8 @@ const factory = (Sortify, UserObject, ProxyManager,
};
var getOwnedPads = function (account) {
var list = [];
if (account) {
var list = [];
if (store.proxy.todo) {
// No password for todo
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
@ -718,29 +712,17 @@ const factory = (Sortify, UserObject, ProxyManager,
if (store.proxy.mailboxes) {
Object.keys(store.proxy.mailboxes || {}).forEach(function (id) {
if (id === 'supportadmin') { return; }
if (id === 'supportteam') { return; }
var m = store.proxy.mailboxes[id];
list.push(m.channel);
});
}
} else {
list = store.manager.getChannelsList('owned');
/*
if (store.proxy.teams) {
Object.keys(store.proxy.teams || {}).forEach(function (id) {
var t = store.proxy.teams[id];
if (t.owner) {
list.push(t.channel);
list.push(t.keys.roster.channel);
list.push(t.keys.chat.channel);
}
});
}
*/
return list.filter(function (channel) {
if (typeof(channel) !== 'string') { return; }
return [32, 48].indexOf(channel.length) !== -1;
});
}
return list.filter(function (channel) {
if (typeof(channel) !== 'string') { return; }
return [32, 48].indexOf(channel.length) !== -1;
});
return Array.from(store.manager.getChannelsList('owned'));
};
var removeOwnedPads = function (account, waitFor) {
// Delete owned pads
@ -928,7 +910,7 @@ const factory = (Sortify, UserObject, ProxyManager,
// Reset the drive part of the userObject (from settings)
Store.resetDrive = function (clientId, data, cb) {
nThen(function (waitFor) {
removeOwnedPads(waitFor);
removeOwnedPads(false, waitFor);
}).nThen(function () {
store.proxy.drive = store.userObject.getStructure();
sendDriveEvent('DRIVE_CHANGE', {

View File

@ -123,41 +123,41 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
}
};
var getTeamChannelList = function (ctx, id) {
var getTeamChannelList = function (ctx, compareHash, id) {
// Get the list of pads' channel ID in your drive
// This list is filtered so that it doesn't include pad owned by other users
// It now includes channels from shared folders
var store = ctx.teams[id];
if (!store) { return null; }
var list = store.manager.getChannelsList('pin');
var list = store.manager.getChannelsList('pin'); // list is a Set
var team = ctx.store.proxy.teams[id];
list.push(`${team.channel}#drive`);
const teamChan = compareHash ? team.channel : `${team.channel}#drive`;
list.add(teamChan);
var chatChannel = Util.find(team, ['keys', 'chat', 'channel']);
var membersChannel = Util.find(team, ['keys', 'roster', 'channel']);
var mailboxChannel = Util.find(team, ['keys', 'mailbox', 'channel']);
if (chatChannel) { list.push(chatChannel); }
if (membersChannel) { list.push(membersChannel); }
if (mailboxChannel) { list.push(mailboxChannel); }
if (chatChannel) { list.add(chatChannel); }
if (membersChannel) { list.add(membersChannel); }
if (mailboxChannel) { list.add(mailboxChannel); }
if (store.proxy.calendars) {
var cList = Object.keys(store.proxy.calendars).map(function (c) {
return store.proxy.calendars[c].channel;
});
list = list.concat(cList);
cList.forEach(id => list.add(id));
}
var state = store.roster.getState();
if (state.members) {
Object.keys(state.members).forEach(function (curve) {
var m = state.members[curve];
if (m.inviteChannel && m.pending) { list.push(m.inviteChannel); }
if (m.previewChannel && m.pending) { list.push(m.previewChannel); }
if (m.inviteChannel && m.pending) { list.add(m.inviteChannel); }
if (m.previewChannel && m.pending) { list.add(m.previewChannel); }
});
}
list.sort();
return list;
return Array.from(list).sort();
};
var handleSharedFolder = function (ctx, id, sfId, rt) {
@ -365,14 +365,15 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
});
}).nThen(function () {
if (!team.rpc) { return; }
var list = getTeamChannelList(ctx, id);
var list = getTeamChannelList(ctx, true, id);
var local = Hash.hashChannelList(list);
// Check pin list
team.rpc.getServerHash(function (e, hash) {
if (e) { return void console.warn(e); }
if (hash !== local) {
// Reset pin list
team.rpc.reset(list, function (e/*, hash*/) {
var list2 = getTeamChannelList(ctx, false, id);
team.rpc.reset(list2, function (e/*, hash*/) {
if (e) { console.warn(e); }
});
}
@ -853,7 +854,7 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
// For each pad, check on the server if there are other owners.
// If yes, then remove yourself as an owner
// If no, delete the pad
var ownedPads = team.manager.getChannelsList('owned');
var ownedPads = Array.from(team.manager.getChannelsList('owned'));
var sem = Util.Saferphore.create(10);
ownedPads.forEach(function (c) {
var w = waitFor();

File diff suppressed because one or more lines are too long