Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix data event parsing & add host #754

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,24 @@
"password": "Password",
"passwordInfo": "<a href=https://support.apple.com/en-us/HT204397 target=_blank>Generate an app-specific password</a> and use this app-specific password instead of your account password."
},
"google": {
"name": "Google Calendar (deprecated)",
"url": "Google Calendar URL",
"urlInfo": "Let default URL, it will be set automatically.",
"username": "Email",
"usernameInfo": "Enter your Google email adress.",
"password": "Password",
"passwordInfo": "<a href=https://support.google.com/accounts/answer/185833?hl=en target=_blank>Generate an app-specific password</a> and use this app-specific password instead of your account password."
},
"synology": {
"name": "Synology Calendar",
"url": "Synology CalDAV URL",
"urlInfo": "In calendar application, click on 'CalDAV Account' and copy 'macOS/iOS' URL.",
"username": "Synology username",
"usernameInfo": "Enter your username.",
"password": "Password",
"passwordInfo": "Enter your password."
},
"other": {
"name": "Other",
"url": "CalDAV URL",
Expand Down
18 changes: 18 additions & 0 deletions front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,24 @@
"password": "Mot de passe",
"passwordInfo": "<a href=https://support.apple.com/fr-fr/HT204397 target=_blank>Générez un mot de passe application</a> et utilisez le à la place du mot de passe de votre compte."
},
"google": {
"name": "Google Agenda (déprécié)",
"url": "URL Google Agenda",
"urlInfo": "Laissez l'URL par défaut, il sera paramétré automatiquement.",
"username": "Email",
"usernameInfo": "Entrez l'adresse email de votre compte Google.",
"password": "Mot de passe",
"passwordInfo": "<a href=https://support.google.com/accounts/answer/185833?hl=fr target=_blank>Générez un mot de passe application</a> et utilisez le à la place du mot de passe de votre compte."
},
"synology": {
"name": "Calendrier Synology",
"url": "URL CalDAV Synology",
"urlInfo": "Dans l'application Calendrier, cliquez sur 'Compte CalDAV' et copiez l'URL 'macOS/iOS'.",
"username": "Nom d'utilisateur Synology",
"usernameInfo": "Entrez votre nom d'utilisateur.",
"password": "Mot de passe",
"passwordInfo": "Entrez votre mot de passe."
},
"other": {
"name": "Autre",
"url": "URL CalDAV",
Expand Down
4 changes: 4 additions & 0 deletions front/src/routes/integration/all/caldav/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const actions = store => ({
store.setState({
caldavUrl: 'https://caldav.icloud.com'
});
} else if (e.target.value === 'google') {
store.setState({
caldavUrl: 'https://www.google.com/calendar/dav'
});
}
},
updateCaldavUrl(state, e) {
Expand Down
47 changes: 27 additions & 20 deletions server/services/caldav/lib/calendar/calendar.requests.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
const url = require('url');
const logger = require('../../../../utils/logger');

/**
* @description Search elements as getElementsByTagName with regex instead of string.
* @param {Object} container - Element where execute research.
* @param {RegExp} regex - Regex to search.
* @returns {Array} Elements that match with regex.
* @example
* getElementsByTagRegex(document, /(cal:)?calendar-data/)
*/
function getElementsByTagRegex(container, regex) {
return Array.from(container.getElementsByTagName('*')).filter((elem) => regex.test(elem.tagName));
}

/**
* @description Get calendars from caldav server.
* @param {Object} xhr - Request with dav credentials.
Expand Down Expand Up @@ -105,36 +117,31 @@ async function requestEventsData(xhr, calendarUrl, eventsToUpdate, calDavHost) {
</c:comp-filter>
</c:filter>
</c:calendar-multiget>`,
transformRequest: (request) => request.setRequestHeader('Content-Type', 'application/xml;charset=utf-8'),
});

const eventsData = await xhr.send(req, calendarUrl);

const tags = {};
switch (calDavHost) {
case 'apple':
tags.response = 'response';
tags.calendarData = 'calendar-data';
tags.href = 'href';
break;
default:
tags.response = 'd:response';
tags.calendarData = 'cal:calendar-data';
tags.href = 'd:href';
break;
}
// Extract data from XML response
const xmlEvents = new this.xmlDom.DOMParser().parseFromString(eventsData.request.responseText);
const jsonEvents = Array.from(xmlEvents.getElementsByTagName(tags.response)).map((xmlEvent) => {
const event = this.ical.parseICS(xmlEvent.getElementsByTagName(tags.calendarData)[0].childNodes[0].data);
return {
href: xmlEvent.getElementsByTagName(tags.href)[0].childNodes[0].data,
...event[Object.keys(event)[0]],
};
const jsonEvents = getElementsByTagRegex(xmlEvents, /^[a-zA-Z]*:?response$/).map((xmlEvent) => {
const event = this.ical.parseICS(
getElementsByTagRegex(xmlEvent, /^[a-zA-Z]*:?calendar-data$/)[0].childNodes[0].data,
);
for (let j = 0; j < Object.keys(event).length; j += 1) {
if (event[Object.keys(event)[j]].type === 'VEVENT') {
return {
href: getElementsByTagRegex(xmlEvent, /^[a-zA-Z]*:?href$/)[0].childNodes[0].data,
...event[Object.keys(event)[j]],
};
}
}
return null;
});

// Filter only event objects
return jsonEvents.filter((jsonEvent) => {
return jsonEvent.type === 'VEVENT';
return jsonEvent !== null;
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ async function syncUserCalendars(userId) {
return this.gladys.calendar.updateEvent(gladysEvents[0].selector, formatedEvent);
}),
);

logger.info(`CalDAV : ${savedEvents.length} events updated.`);
logger.info(`CalDAV : ${savedEvents.length} events updated for calendar ${calendarToUpdate.name}.`);
},
{ concurrency: 1 },
);
Expand Down
46 changes: 36 additions & 10 deletions server/test/services/caldav/lib/calendar/requests.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,33 @@ describe('CalDAV requests', () => {
Request: sinon.stub().returns({ requestDate: 'request3' }),
},
ical: {
parseICS: sinon.stub().returns({
data: {
type: 'VEVENT',
uid: '49193db9-f666-4947-8ce6-3357ce3b7166',
summary: 'Evenement 1',
start: new Date('2018-06-08'),
end: new Date('2018-06-09'),
},
}),
parseICS: sinon
.stub()
.onFirstCall()
.returns({
data: {
type: 'VEVENT',
uid: '49193db9-f666-4947-8ce6-3357ce3b7166',
summary: 'Evenement 1',
start: new Date('2018-06-08'),
end: new Date('2018-06-09'),
},
})
.onSecondCall()
.returns({}),
},
xmlDom: {
DOMParser: sinon.stub().returns({
parseFromString: sinon.stub().returns({
getElementsByTagName: sinon.stub().returns([
{
tagName: 'response',
getElementsByTagName: sinon
.stub()
.onFirstCall()
.returns([
{
tagName: 'calendar-data',
childNodes: [
{
data: `
Expand Down Expand Up @@ -82,7 +89,22 @@ describe('CalDAV requests', () => {
},
])
.onSecondCall()
.returns([{ childNodes: [{ data: 'https://caldav.host.com/home/personal/event-1.ics' }] }]),
.returns([
{ tagName: 'href', childNodes: [{ data: 'https://caldav.host.com/home/personal/event-1.ics' }] },
]),
},
{
tagName: 'response',
getElementsByTagName: sinon.stub().returns([
{
tagName: 'calendar-data',
childNodes: [
{
data: '',
},
],
},
]),
},
]),
}),
Expand Down Expand Up @@ -246,6 +268,7 @@ describe('CalDAV requests', () => {
const xhr = {
send: sinon.stub(),
};
const setRequestHeader = sinon.stub();
xhr.send.resolves({ request: { responseText: '<xml></xml>' } });
const eventsData = await requests.requestEventsData(
xhr,
Expand All @@ -267,6 +290,9 @@ describe('CalDAV requests', () => {
'other',
);

requests.dav.Request.args[0][0].transformRequest({ setRequestHeader });
expect(setRequestHeader.args[0]).to.eql(['Content-Type', 'application/xml;charset=utf-8']);

expect(eventsData).to.eql([
{
href: 'https://caldav.host.com/home/personal/event-1.ics',
Expand Down