Share calendar with a team
This commit is contained in:
parent
1a3ab6fed8
commit
9ae482b744
6 changed files with 184 additions and 22 deletions
|
@ -1,6 +1,7 @@
|
||||||
@import (reference) '../../customize/src/less2/include/framework.less';
|
@import (reference) '../../customize/src/less2/include/framework.less';
|
||||||
@import (reference) '../../customize/src/less2/include/sidebar-layout.less';
|
@import (reference) '../../customize/src/less2/include/sidebar-layout.less';
|
||||||
@import (reference) '../../customize/src/less2/include/tools.less';
|
@import (reference) '../../customize/src/less2/include/tools.less';
|
||||||
|
@import (reference) '../../customize/src/less2/include/avatar.less';
|
||||||
|
|
||||||
&.cp-app-calendar {
|
&.cp-app-calendar {
|
||||||
|
|
||||||
|
@ -104,6 +105,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.cp-calendar-list {
|
.cp-calendar-list {
|
||||||
|
.cp-calendar-team {
|
||||||
|
height: 30px;
|
||||||
|
.avatar_main(30px);
|
||||||
|
.cp-avatar {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.cp-name {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
.cp-calendar-entry {
|
.cp-calendar-entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -16,6 +16,10 @@ define([
|
||||||
'/customize/application_config.js',
|
'/customize/application_config.js',
|
||||||
'/lib/calendar/tui-calendar.min.js',
|
'/lib/calendar/tui-calendar.min.js',
|
||||||
|
|
||||||
|
'/common/inner/share.js',
|
||||||
|
'/common/inner/access.js',
|
||||||
|
'/common/inner/properties.js',
|
||||||
|
|
||||||
'/common/jscolor.js',
|
'/common/jscolor.js',
|
||||||
'css!/lib/calendar/tui-calendar.min.css',
|
'css!/lib/calendar/tui-calendar.min.css',
|
||||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||||
|
@ -36,7 +40,8 @@ define([
|
||||||
h,
|
h,
|
||||||
Messages,
|
Messages,
|
||||||
AppConfig,
|
AppConfig,
|
||||||
Calendar
|
Calendar,
|
||||||
|
Share, Access, Properties
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var APP = window.APP = {
|
var APP = window.APP = {
|
||||||
|
@ -57,6 +62,7 @@ Messages.calendar_deleteConfirm = "Are you sure you want to delete this calendar
|
||||||
Messages.calendar_deleteTeamConfirm = "Are you sure you want to delete this calendar from this team?";
|
Messages.calendar_deleteTeamConfirm = "Are you sure you want to delete this calendar from this team?";
|
||||||
Messages.calendar_deleteOwned = " It will still be visible for the users it has been shared with.";
|
Messages.calendar_deleteOwned = " It will still be visible for the users it has been shared with.";
|
||||||
Messages.calendar_errorNoCalendar = "No editable calendar selected!";
|
Messages.calendar_errorNoCalendar = "No editable calendar selected!";
|
||||||
|
Messages.calendar_myCalendars = "My calendars";
|
||||||
|
|
||||||
var onCalendarsUpdate = Util.mkEvent();
|
var onCalendarsUpdate = Util.mkEvent();
|
||||||
|
|
||||||
|
@ -310,6 +316,36 @@ Messages.calendar_errorNoCalendar = "No editable calendar selected!";
|
||||||
action: function (e) {
|
action: function (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
editCalendar(id);
|
editCalendar(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tag: 'a',
|
||||||
|
attributes: {
|
||||||
|
'class': 'fa fa-shhare-alt',
|
||||||
|
},
|
||||||
|
content: h('span', Messages.shareButton),
|
||||||
|
action: function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var friends = common.getFriends();
|
||||||
|
var cal = APP.calendars[id];
|
||||||
|
var title = Util.find(cal, ['content', 'metadata', 'title']);
|
||||||
|
var color = Util.find(cal, ['content', 'metadata', 'color']);
|
||||||
|
Share.getShareModal(common, {
|
||||||
|
teamId: teamId === 1 ? undefined : teamId,
|
||||||
|
origin: APP.origin,
|
||||||
|
pathname: "/calendar/",
|
||||||
|
friends: friends,
|
||||||
|
title: title,
|
||||||
|
password: cal.password, // XXX support passwords
|
||||||
|
calendar: {
|
||||||
|
title: title,
|
||||||
|
color: color,
|
||||||
|
channel: id,
|
||||||
|
},
|
||||||
|
common: common,
|
||||||
|
hashes: cal.hashes
|
||||||
|
});
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
tag: 'a',
|
tag: 'a',
|
||||||
|
@ -361,7 +397,9 @@ Messages.calendar_errorNoCalendar = "No editable calendar selected!";
|
||||||
var md = Util.find(data, ['content', 'metadata']);
|
var md = Util.find(data, ['content', 'metadata']);
|
||||||
if (!md) { return; }
|
if (!md) { return; }
|
||||||
var active = data.hidden ? '' : '.cp-active';
|
var active = data.hidden ? '' : '.cp-active';
|
||||||
var calendar = h('div.cp-calendar-entry'+active, [
|
var calendar = h('div.cp-calendar-entry'+active, {
|
||||||
|
'data-uid': id
|
||||||
|
}, [
|
||||||
h('span.cp-calendar-color', {
|
h('span.cp-calendar-color', {
|
||||||
style: 'background-color: '+md.color+';'
|
style: 'background-color: '+md.color+';'
|
||||||
}),
|
}),
|
||||||
|
@ -370,7 +408,12 @@ Messages.calendar_errorNoCalendar = "No editable calendar selected!";
|
||||||
]);
|
]);
|
||||||
$(calendar).click(function () {
|
$(calendar).click(function () {
|
||||||
data.hidden = !data.hidden;
|
data.hidden = !data.hidden;
|
||||||
$(calendar).toggleClass('cp-active', !data.hidden);
|
if (APP.$calendars) {
|
||||||
|
APP.$calendars.find('[data-uid="'+id+'"]').toggleClass('cp-active', !data.hidden);
|
||||||
|
} else {
|
||||||
|
$(calendar).toggleClass('cp-active', !data.hidden);
|
||||||
|
}
|
||||||
|
|
||||||
renderCalendar();
|
renderCalendar();
|
||||||
});
|
});
|
||||||
if (APP.$calendars) { APP.$calendars.append(calendar); }
|
if (APP.$calendars) { APP.$calendars.append(calendar); }
|
||||||
|
@ -436,10 +479,35 @@ Messages.calendar_errorNoCalendar = "No editable calendar selected!";
|
||||||
var $calendars = APP.$calendars = $(calendars).appendTo($container);
|
var $calendars = APP.$calendars = $(calendars).appendTo($container);
|
||||||
onCalendarsUpdate.reg(function () {
|
onCalendarsUpdate.reg(function () {
|
||||||
$calendars.empty();
|
$calendars.empty();
|
||||||
Object.keys(APP.calendars || {}).forEach(function (id) {
|
var metadataMgr = common.getMetadataMgr();
|
||||||
var cal = APP.calendars[id];
|
var privateData = metadataMgr.getPrivateData();
|
||||||
if (!cal) { return; }
|
var filter = function (teamId) {
|
||||||
(cal.teams || []).forEach(function (teamId) {
|
return Object.keys(APP.calendars || {}).filter(function (id) {
|
||||||
|
var cal = APP.calendars[id] || {};
|
||||||
|
var teams = cal.teams || [];
|
||||||
|
return teams.indexOf(teamId || 1) !== -1;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var myCalendars = filter(1);
|
||||||
|
if (myCalendars.length) {
|
||||||
|
APP.$calendars.append(h('div.cp-calendar-team', [
|
||||||
|
h('span', Messages.calendar_myCalendars)
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
myCalendars.forEach(function (id) {
|
||||||
|
makeCalendarEntry(id, 1);
|
||||||
|
});
|
||||||
|
Object.keys(privateData.teams).forEach(function (teamId) {
|
||||||
|
var calendars = filter(teamId);
|
||||||
|
if (!calendars.length) { return; }
|
||||||
|
var team = privateData.teams[teamId];
|
||||||
|
var avatar = h('span.cp-avatar');
|
||||||
|
common.displayAvatar($(avatar), team.avatar, team.displayName);
|
||||||
|
APP.$calendars.append(h('div.cp-calendar-team', [
|
||||||
|
avatar,
|
||||||
|
h('span.cp-name', {title: team.name}, team.name)
|
||||||
|
]));
|
||||||
|
calendars.forEach(function (id) {
|
||||||
makeCalendarEntry(id, teamId);
|
makeCalendarEntry(id, teamId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1350,13 +1350,20 @@ define([
|
||||||
var $innerblock = $('<div>', {'class': 'cp-dropdown-content'});
|
var $innerblock = $('<div>', {'class': 'cp-dropdown-content'});
|
||||||
if (config.left) { $innerblock.addClass('cp-dropdown-left'); }
|
if (config.left) { $innerblock.addClass('cp-dropdown-left'); }
|
||||||
|
|
||||||
|
var hide = function () {
|
||||||
|
window.setTimeout(function () { $innerblock.hide(); }, 0);
|
||||||
|
};
|
||||||
|
|
||||||
config.options.forEach(function (o) {
|
config.options.forEach(function (o) {
|
||||||
if (!isValidOption(o)) { return; }
|
if (!isValidOption(o)) { return; }
|
||||||
if (isElement(o)) { return $innerblock.append($(o)); }
|
if (isElement(o)) { return $innerblock.append($(o)); }
|
||||||
var $el = $('<' + o.tag + '>', o.attributes || {}).html(o.content || '');
|
var $el = $('<' + o.tag + '>', o.attributes || {}).html(o.content || '');
|
||||||
$el.appendTo($innerblock);
|
$el.appendTo($innerblock);
|
||||||
if (typeof(o.action) === 'function') {
|
if (typeof(o.action) === 'function') {
|
||||||
$el.click(o.action);
|
$el.click(function (e) {
|
||||||
|
var close = o.action(e);
|
||||||
|
if (close) { hide(); }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1376,10 +1383,6 @@ define([
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var hide = function () {
|
|
||||||
window.setTimeout(function () { $innerblock.hide(); }, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
var show = function () {
|
var show = function () {
|
||||||
var wh = $(window).height();
|
var wh = $(window).height();
|
||||||
var button = $button[0].getBoundingClientRect();
|
var button = $button[0].getBoundingClientRect();
|
||||||
|
|
|
@ -111,6 +111,7 @@ define([
|
||||||
password: config.password,
|
password: config.password,
|
||||||
isTemplate: config.isTemplate,
|
isTemplate: config.isTemplate,
|
||||||
name: myName,
|
name: myName,
|
||||||
|
isCalendar: Boolean(config.calendar),
|
||||||
title: title
|
title: title
|
||||||
}, {
|
}, {
|
||||||
viewed: team && team.id,
|
viewed: team && team.id,
|
||||||
|
@ -122,6 +123,19 @@ define([
|
||||||
}
|
}
|
||||||
// If it's a team with edit right, add the pad directly
|
// If it's a team with edit right, add the pad directly
|
||||||
if (!team) { return; }
|
if (!team) { return; }
|
||||||
|
if (config.calendar) {
|
||||||
|
var calendarModule = common.makeUniversal('calendar');
|
||||||
|
var calendarData = config.calendar;
|
||||||
|
calendarData.href = href;
|
||||||
|
calendarData.teamId = team.id;
|
||||||
|
calendarModule.execCommand('ADD', calendarData, function (obj) {
|
||||||
|
if (obj && obj.error) {
|
||||||
|
console.error(obj.error);
|
||||||
|
return void UI.warn(Messages.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
sframeChan.query('Q_STORE_IN_TEAM', {
|
sframeChan.query('Q_STORE_IN_TEAM', {
|
||||||
href: href,
|
href: href,
|
||||||
password: config.password,
|
password: config.password,
|
||||||
|
|
|
@ -2670,6 +2670,7 @@ define([
|
||||||
}, true);
|
}, true);
|
||||||
}).nThen(function (waitFor) {
|
}).nThen(function (waitFor) {
|
||||||
loadUniversal(Team, 'team', waitFor, clientId);
|
loadUniversal(Team, 'team', waitFor, clientId);
|
||||||
|
}).nThen(function (waitFor) {
|
||||||
loadUniversal(Calendar, 'calendar', waitFor);
|
loadUniversal(Calendar, 'calendar', waitFor);
|
||||||
}).nThen(function () {
|
}).nThen(function () {
|
||||||
cb();
|
cb();
|
||||||
|
|
|
@ -102,7 +102,8 @@ ctx.calendars[channel] = {
|
||||||
deleted: !c.stores.length,
|
deleted: !c.stores.length,
|
||||||
restricted: c.restricted,
|
restricted: c.restricted,
|
||||||
owned: ctx.Store.isOwned(c.owners),
|
owned: ctx.Store.isOwned(c.owners),
|
||||||
content: Util.clone(c.proxy)
|
content: Util.clone(c.proxy),
|
||||||
|
hashes: c.hashes
|
||||||
}, ctx.clients);
|
}, ctx.clients);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,14 +128,21 @@ ctx.calendars[channel] = {
|
||||||
if (!channel) { return; }
|
if (!channel) { return; }
|
||||||
|
|
||||||
var c = ctx.calendars[channel];
|
var c = ctx.calendars[channel];
|
||||||
|
|
||||||
|
var update = function () {
|
||||||
|
sendUpdate(ctx, c);
|
||||||
|
};
|
||||||
|
|
||||||
if (c) {
|
if (c) {
|
||||||
if (c.stores && c.stores.indexOf(teamId) !== -1) { return; }
|
|
||||||
if (c.readOnly && data.href) {
|
if (c.readOnly && data.href) {
|
||||||
// XXX UPGRADE
|
// XXX UPGRADE
|
||||||
|
// c.hashes.editHash =
|
||||||
// XXX different cases if already ready or not?
|
// XXX different cases if already ready or not?
|
||||||
}
|
}
|
||||||
|
if (c.stores && c.stores.indexOf(teamId) !== -1) { return void cb(); }
|
||||||
c.stores.push(teamId);
|
c.stores.push(teamId);
|
||||||
return;
|
update();
|
||||||
|
return void cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiple teams can have the same calendar. Make sure we remember the list of stores
|
// Multiple teams can have the same calendar. Make sure we remember the list of stores
|
||||||
|
@ -143,12 +151,8 @@ ctx.calendars[channel] = {
|
||||||
ready: false,
|
ready: false,
|
||||||
channel: channel,
|
channel: channel,
|
||||||
readOnly: !data.href,
|
readOnly: !data.href,
|
||||||
stores: [teamId]
|
stores: [teamId],
|
||||||
};
|
hashes: {}
|
||||||
|
|
||||||
var update = function () {
|
|
||||||
console.log(ctx.clients);
|
|
||||||
sendUpdate(ctx, c);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -156,6 +160,11 @@ ctx.calendars[channel] = {
|
||||||
var secret = Hash.getSecrets('calendar', parsed.hash);
|
var secret = Hash.getSecrets('calendar', parsed.hash);
|
||||||
var crypto = Crypto.createEncryptor(secret.keys);
|
var crypto = Crypto.createEncryptor(secret.keys);
|
||||||
|
|
||||||
|
c.hashes.viewHash = Hash.getViewHashFromKeys(secret);
|
||||||
|
if (data.href) {
|
||||||
|
c.hashes.editHash = Hash.getEditHashFromKeys(secret);
|
||||||
|
}
|
||||||
|
|
||||||
c.proxy = {
|
c.proxy = {
|
||||||
metadata: {
|
metadata: {
|
||||||
color: data.color,
|
color: data.color,
|
||||||
|
@ -290,6 +299,14 @@ ctx.calendars[channel] = {
|
||||||
|
|
||||||
// Personal drive
|
// Personal drive
|
||||||
findFromStore(ctx.store);
|
findFromStore(ctx.store);
|
||||||
|
|
||||||
|
var teams = ctx.store.modules.team && ctx.store.modules.team.getTeamsData();
|
||||||
|
if (!teams) { return; }
|
||||||
|
Object.keys(teams).forEach(function (id) {
|
||||||
|
var store = getStore(ctx, id);
|
||||||
|
console.log(store);
|
||||||
|
findFromStore(store);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -308,6 +325,44 @@ ctx.calendars[channel] = {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var addCalendar = function (ctx, data, cId, cb) {
|
||||||
|
var store = getStore(ctx, data.teamId);
|
||||||
|
if (!store) { return void cb({error: "NO_STORE"}); }
|
||||||
|
// Check team edit rights: viewers in teams don't have rpc
|
||||||
|
if (!store.rpc) { return void cb({error: "EFORBIDDEN"}); }
|
||||||
|
|
||||||
|
var c = store.proxy.calendars = store.proxy.calendars || {};
|
||||||
|
var parsed = Hash.parsePadUrl(data.href);
|
||||||
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
||||||
|
|
||||||
|
var cal = {
|
||||||
|
href: Hash.getEditHashFromKeys(secret),
|
||||||
|
roHref: Hash.getViewHashFromKeys(secret),
|
||||||
|
color: data.color,
|
||||||
|
title: data.title,
|
||||||
|
channel: data.channel
|
||||||
|
};
|
||||||
|
cal.color = data.color;
|
||||||
|
cal.title = data.title;
|
||||||
|
openChannel(ctx, {
|
||||||
|
storeId: store.id || 1,
|
||||||
|
data: cal,
|
||||||
|
isNew: true
|
||||||
|
}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
// Can't open this channel, don't store it
|
||||||
|
console.error(err);
|
||||||
|
return void cb({error: err.error})
|
||||||
|
}
|
||||||
|
// Add the calendar and call back
|
||||||
|
c[cal.channel] = cal;
|
||||||
|
var pin = store.pin || ctx.pinPads;
|
||||||
|
pin([cal.channel], function (res) {
|
||||||
|
if (res && res.error) { console.error(res.error); }
|
||||||
|
});
|
||||||
|
ctx.Store.onSync(store.id, cb);
|
||||||
|
});
|
||||||
|
};
|
||||||
var createCalendar = function (ctx, data, cId, cb) {
|
var createCalendar = function (ctx, data, cId, cb) {
|
||||||
var store = getStore(ctx, data.teamId);
|
var store = getStore(ctx, data.teamId);
|
||||||
if (!store) { return void cb({error: "NO_STORE"}); }
|
if (!store) { return void cb({error: "NO_STORE"}); }
|
||||||
|
@ -322,7 +377,7 @@ ctx.calendars[channel] = {
|
||||||
storeId: store.id || 1,
|
storeId: store.id || 1,
|
||||||
data: cal,
|
data: cal,
|
||||||
isNew: true
|
isNew: true
|
||||||
}, function (err, proxy) {
|
}, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
// Can't open this channel, don't store it
|
// Can't open this channel, don't store it
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -460,6 +515,10 @@ ctx.calendars[channel] = {
|
||||||
if (cmd === 'SUBSCRIBE') {
|
if (cmd === 'SUBSCRIBE') {
|
||||||
return void subscribe(ctx, data, clientId, cb);
|
return void subscribe(ctx, data, clientId, cb);
|
||||||
}
|
}
|
||||||
|
if (cmd === 'ADD') {
|
||||||
|
if (ctx.store.offline) { return void cb({error: 'OFFLINE'}); }
|
||||||
|
return void addCalendar(ctx, data, clientId, cb);
|
||||||
|
}
|
||||||
if (cmd === 'CREATE') {
|
if (cmd === 'CREATE') {
|
||||||
if (ctx.store.offline) { return void cb({error: 'OFFLINE'}); }
|
if (ctx.store.offline) { return void cb({error: 'OFFLINE'}); }
|
||||||
return void createCalendar(ctx, data, clientId, cb);
|
return void createCalendar(ctx, data, clientId, cb);
|
||||||
|
|
Loading…
Reference in a new issue