diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js
index 278e8dda8..a7c5d3994 100644
--- a/www/common/common-ui-elements.js
+++ b/www/common/common-ui-elements.js
@@ -272,25 +272,30 @@ define([
var $friends = $div.find('.cp-usergrid-user.cp-selected');
$friends.each(function (i, el) {
var curve = $(el).attr('data-curve');
- // Check if the selected element is a friend or a team
- if (curve) { // Friend
- if (!curve || !friends[curve]) { return; }
- var friend = friends[curve];
- if (!friend.notifications || !friend.curvePublic) { return; }
- common.mailbox.sendTo("SHARE_PAD", {
- href: href,
- password: config.password,
- isTemplate: config.isTemplate,
- name: myName,
- title: title
- }, {
- channel: friend.notifications,
- curvePublic: friend.curvePublic
- });
- return;
- }
- // Team
var ed = $(el).attr('data-ed');
+ var friend = curve && friends[curve];
+ var team = teams[ed];
+ // If the selected element is a friend or a team without edit right,
+ // send a notification
+ var mailbox = friend || ((team && team.viewer) ? team : undefined);
+ if (mailbox) { // Friend
+ if (friends[curve] && !mailbox.notifications) { return; }
+ if (mailbox.notifications && mailbox.curvePublic) {
+ // XXX we should mark this notification as "viewed" in our own proxy
+ common.mailbox.sendTo("SHARE_PAD", {
+ href: href,
+ password: config.password,
+ isTemplate: config.isTemplate,
+ name: myName,
+ title: title
+ }, {
+ channel: mailbox.notifications,
+ curvePublic: mailbox.curvePublic
+ });
+ return;
+ }
+ }
+ // If it's a team with edit right, add the pad directly
var team = teams[ed];
if (!team) { return; }
sframeChan.query('Q_STORE_IN_TEAM', {
@@ -409,10 +414,11 @@ define([
// config.teamId only exists when we're trying to share a pad from a team drive
// In this case, we don't want to share the pad with the current team
if (config.teamId && config.teamId === id) { return; }
- if (!teamsData[id].hasSecondaryKey) { return; }
var t = teamsData[id];
teams[t.edPublic] = {
- notifications: true,
+ viewer: !teamsData[id].hasSecondaryKey,
+ notifications: t.notifications,
+ curvePublic: t.curvePublic,
displayName: t.name,
edPublic: t.edPublic,
avatar: t.avatar,
diff --git a/www/common/notifications.js b/www/common/notifications.js
index 3abc40ca8..348b0cc44 100644
--- a/www/common/notifications.js
+++ b/www/common/notifications.js
@@ -84,6 +84,10 @@ define([
// Share pad
+ Messages.notification_padSharedTeam = "{0} has shared a pad with the team {2}: {1}"; // XXX
+ Messages.notification_fileSharedTeam = "{0} has shared a file with the team {2}: {1}"; // XXX
+ Messages.notification_folderSharedTeam = "{0} has shared a pad with the team {2}: {1}"; // XXX
+
handlers['SHARE_PAD'] = function(common, data) {
var content = data.content;
var msg = content.msg;
@@ -91,10 +95,22 @@ define([
var key = type === 'drive' ? 'notification_folderShared' :
(type === 'file' ? 'notification_fileShared' :
'notification_padShared');
+
+ var teamNotification = /^team-/.test(data.type) && Number(data.type.slice(5));
+ var teamName = '';
+ if (teamNotification) {
+ var privateData = common.getMetadataMgr().getPrivateData();
+ var teamsData = Util.tryParse(JSON.stringify(privateData.teams)) || {};
+ var team = teamsData[teamNotification];
+ if (!team || !team.name) { return; }
+ key += "Team";
+ teamName = Util.fixHTML(team.name);
+ }
+
var name = Util.fixHTML(msg.content.name) || Messages.anonymous;
var title = Util.fixHTML(msg.content.title);
content.getFormatText = function() {
- return Messages._getKey(key, [name, title]);
+ return Messages._getKey(key, [name, title, teamName]);
};
content.handler = function() {
var todo = function() {
@@ -105,6 +121,9 @@ define([
if (msg.content.isTemplate) {
common.sessionStorage.put(Constants.newPadPathKey, ['template'], waitFor());
}
+ if (teamNotification) {
+ common.sessionStorage.put(Constants.newPadTeamKey, teamNotification, waitFor());
+ }
common.sessionStorage.put('newPadPassword', msg.content.password || '', waitFor());
}).nThen(function() {
todo();
diff --git a/www/common/outer/mailbox.js b/www/common/outer/mailbox.js
index 706f1c8c2..ccae95a7e 100644
--- a/www/common/outer/mailbox.js
+++ b/www/common/outer/mailbox.js
@@ -467,6 +467,15 @@ proxy.mailboxes = {
}
});
+ Object.keys(store.proxy.teams || {}).forEach(function (teamId) {
+ var team = store.proxy.teams[teamId];
+ var teamMailbox = team.keys.mailbox || {};
+ if (!teamMailbox.channel) { return; }
+ openChannel(ctx, 'team-'+teamId, teamMailbox, function () {
+ //console.log('Mailbox team', teamId);
+ });
+ });
+
mailbox.post = function (box, type, content) {
var b = ctx.boxes[box];
if (!b) { return; }
@@ -477,8 +486,8 @@ proxy.mailboxes = {
});
};
- mailbox.open = function (key, m, cb) {
- if (TYPES.indexOf(key) === -1) { return; }
+ mailbox.open = function (key, m, cb, team) {
+ if (TYPES.indexOf(key) === -1 && !team) { return; }
openChannel(ctx, key, m, cb);
};
diff --git a/www/common/outer/team.js b/www/common/outer/team.js
index c151de004..92dc1deda 100644
--- a/www/common/outer/team.js
+++ b/www/common/outer/team.js
@@ -494,6 +494,9 @@ define([
var roHash = Hash.getViewHashFromKeys(secret);
var keyPair = Nacl.sign.keyPair(); // keyPair.secretKey , keyPair.publicKey
+ var curveSeed = Nacl.randomBytes(32);
+ var curvePair = Nacl.box.keyPair.fromSecretKey(curveSeed);
+
var rosterSeed = Crypto.Team.createSeed();
var rosterKeys = Crypto.Team.deriveMemberKeys(rosterSeed, {
curvePublic: ctx.store.proxy.curvePublic,
@@ -585,6 +588,14 @@ define([
proxy.on('ready', function () {
// Store keys in our drive
var keys = {
+ mailbox: {
+ channel: Hash.createChannelId(),
+ viewed: [],
+ keys: {
+ curvePrivate: Nacl.util.encodeBase64(curvePair.secretKey),
+ curvePublic: Nacl.util.encodeBase64(curvePair.publicKey)
+ }
+ },
drive: {
edPrivate: Nacl.util.encodeBase64(keyPair.secretKey),
edPublic: Nacl.util.encodeBase64(keyPair.publicKey)
@@ -1566,6 +1577,26 @@ define([
});
};
+ var deriveMailbox = function (team) {
+ if (!team) { return; }
+ if (team.keys && team.keys.mailbox) { return team.keys.mailbox; }
+ var channel = team.channel;
+ if (!channel) { return; }
+ // XXX maybe use something else than channel?
+ var hash = Nacl.hash(Nacl.util.decodeUTF8(channel));
+ var seed = hash.slice(0,32);
+ var channel = Util.uint8ArrayToHex(hash.slice(32,48));
+ var curvePair = Nacl.box.keyPair.fromSecretKey(seed);
+ return {
+ channel: channel,
+ viewed: [],
+ keys: {
+ curvePrivate: Nacl.util.encodeBase64(curvePair.secretKey),
+ curvePublic: Nacl.util.encodeBase64(curvePair.publicKey)
+ }
+ };
+ };
+
Team.init = function (cfg, waitFor, emit) {
var team = {};
var store = cfg.store;
@@ -1595,6 +1626,9 @@ define([
Object.keys(teams).forEach(function (id) {
ctx.onReadyHandlers[id] = [];
+ if (!Util.find(teams, id, 'keys', 'mailbox')) {
+ teams[id].keys.mailbox = deriveMailbox(teams[id]);
+ }
openChannel(ctx, teams[id], id, waitFor(function (err) {
if (err) { return void console.error(err); }
console.debug('Team '+id+' ready');
@@ -1615,6 +1649,8 @@ define([
edPublic: Util.find(teams[id], ['keys', 'drive', 'edPublic']),
avatar: Util.find(teams[id], ['metadata', 'avatar']),
viewer: !Util.find(teams[id], ['keys', 'drive', 'edPrivate']),
+ notifications: Util.find(teams[id], ['keys', 'mailbox', 'channel']),
+ curvePublic: Util.find(teams[id], ['keys', 'mailbox', 'keys', 'curvePublic']),
};
if (safe && ctx.teams[id]) {
diff --git a/www/common/sframe-common-mailbox.js b/www/common/sframe-common-mailbox.js
index a5602fc3f..eac59462e 100644
--- a/www/common/sframe-common-mailbox.js
+++ b/www/common/sframe-common-mailbox.js
@@ -105,11 +105,14 @@ define([
});
// Call the onMessage handlers
+ var isNotification = function (type) {
+ return type === "notificatons" || /^team-/.test(type);
+ };
var pushMessage = function (data, handler) {
var todo = function (f) {
try {
var el;
- if (data.type === 'notifications') {
+ if (isNotification(data.type)) {
Notifications.add(Common, data);
el = createElement(data);
}
@@ -129,7 +132,7 @@ define([
onViewedHandlers.forEach(function (f) {
try {
f(data);
- if (data.type === 'notifications') {
+ if (isNotification(data.type)) {
Notifications.remove(Common, data);
}
} catch (e) {
@@ -173,20 +176,23 @@ define([
execCommand('SUBSCRIBE', null, function () {});
subscribed = true;
}
+ var teams = types.indexOf('team') !== -1;
if (typeof(cfg.onViewed) === "function") {
onViewedHandlers.push(function (data) {
- if (types.indexOf(data.type) === -1) { return; }
+ var type = data.type;
+ if (types.indexOf(type) === -1 && !(teams && /^team-/.test(type))) { return; }
cfg.onViewed(data);
});
}
if (typeof(cfg.onMessage) === "function") {
onMessageHandlers.push(function (data, el) {
- if (types.indexOf(data.type) === -1) { return; }
+ var type = data.type;
+ if (types.indexOf(type) === -1 && !(teams && /^team-/.test(type))) { return; }
cfg.onMessage(data, el);
});
}
Object.keys(history).forEach(function (type) {
- if (types.indexOf(type) === -1) { return; }
+ if (types.indexOf(type) === -1 && !(teams && /^team-/.test(type))) { return; }
history[type].forEach(function (data) {
pushMessage({
type: type,
diff --git a/www/common/toolbar.js b/www/common/toolbar.js
index a4683a884..e55cdcf82 100644
--- a/www/common/toolbar.js
+++ b/www/common/toolbar.js
@@ -1025,7 +1025,7 @@ MessengerUI, Messages) {
$button.addClass('fa-bell');
};
- Common.mailbox.subscribe(['notifications'], {
+ Common.mailbox.subscribe(['notifications', 'team'], {
onMessage: function (data, el) {
if (el) {
$(div).prepend(el);