我正在开发一个应用程序来使用fullcalendar查看我的Outlook日历上的事件,问题是我登录到Microsoft,原则上一切都是正确的,我返回我的网站但日历没有生成,更重要的是,如果我再次单击登录按钮会显示一条消息,表明登录仍在进行中
(errorCode === "interaction_in_progress")
并且我得到控制台日志'im在else(accounts.length)'中。
这是我的代码,我找不到导致日历和事件无法加载的原因。
<div id="calendar"></div>
<script>
let msalInstance;
const loginRequest = {
scopes: ["User.Read", "Calendars.Read", "Calendars.Read.Shared"]
};
async function mostrarCalendarioOutlook() {
console.log('I am inside of mostrarCalendarioOutlook');
try {
if (!msalInstance) {
throw new Error("msalInstance is not initialized");
}
const accounts = msalInstance.getAllAccounts();
if (accounts.length > 0) {
console.log('Dentro del if (accounts.length > 0)');
const account = accounts[0];
const tokenResponse = await msalInstance.acquireTokenSilent({
account,
scopes: ["User.Read", "Calendars.Read", "Calendars.Read.Shared"]
});
const events = await getCalendarEvents(tokenResponse.accessToken);
console.log('initializing render calendar function');
renderCalendar(events);
} else {
console.log('im inside the else (accounts.length)');
await msalInstance.loginRedirect(loginRequest);
}
} catch (error) {
handleError(error);
}
}
function handleError(error) {
if (error.errorCode === "interaction_in_progress") {
Swal.fire({
icon: 'info',
title: 'Process in progress',
text: 'The login process is in progress. Please wait for it to complete.',
allowOutsideClick: false
});
} else if (error.errorCode === "consent_required" || error.errorCode === "interaction_required") {
Swal.fire({
icon: 'error',
title: 'Necessary permissions',
text: 'The administrator must grant the necessary permissions to access the calendars. Contact the administrator.'
});
} else if (error.errorCode === "access_denied") {
Swal.fire({
icon: 'error',
title: 'Access denied',
text: 'The requested data could not be accessed. Check permissions and try again.'
});
} else {
Swal.fire({
icon: 'error',
title: 'Authentication error',
text: 'An error occurred during authentication. Please try again later.'
});
console.error('Error during authentication:', error);
}
}
document.addEventListener('DOMContentLoaded', async function() {
try {
const msalConfig = {
auth: {
clientId: "Client_ID",
authority: "https://login.microsoftonline.com/common",
redirectUri: "redirect_Uri",
popUp: true
}
};
msalInstance = new msal.PublicClientApplication(msalConfig);
//EVENTS
async function getCalendarEvents(accessToken) {
console.log('im inside of getCalendarEvents');
const response = await fetch('https://graph.microsoft.com/v1.0/me/events', {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
if (!response.ok) {
const errorText = await response.text();
console.error(`Error getting calendar events: ${response.status} ${response.statusText}`, errorText);
throw new Error('Error getting calendar events');
}
const data = await response.json();
const events = [];
function adjustToLocalTime(dateTime) {
const date = new Date(dateTime);
const timeZoneOffset = date.getTimezoneOffset() * 60000;
return new Date(date.getTime() - timeZoneOffset).toISOString();
}
const recurringEvents = data.value.filter(event => event.recurrence && event.recurrence.pattern);
recurringEvents.forEach(event => {
const startDate = new Date(event.start.dateTime);
const endDate = new Date(event.end.dateTime);
const recurrence = event.recurrence.pattern;
let instanceDate = new Date(startDate);
const endRecurrenceDate = event.recurrence.range && event.recurrence.range.endDate ? new Date(event.recurrence.range.endDate) : null;
while (!endRecurrenceDate || instanceDate <= endRecurrenceDate) {
if (!recurrence.daysOfWeek) {
const adjustedStartDate = new Date(instanceDate);
adjustedStartDate.setHours(startDate.getHours());
adjustedStartDate.setMinutes(startDate.getMinutes());
const adjustedEndDate = new Date(instanceDate);
adjustedEndDate.setHours(endDate.getHours());
adjustedEndDate.setMinutes(endDate.getMinutes());
if (adjustedStartDate <= endRecurrenceDate || !endRecurrenceDate) {
events.push({
title: event.subject,
start: adjustToLocalTime(adjustedStartDate.toISOString()),
end: adjustToLocalTime(adjustedEndDate.toISOString()),
allDay: event.isAllDay
});
}
switch (recurrence.type) {
case "daily":
instanceDate.setDate(instanceDate.getDate() + recurrence.interval);
break;
case "absoluteMonthly":
instanceDate.setMonth(instanceDate.getMonth() + recurrence.interval);
break;
case "absoluteYearly":
instanceDate.setFullYear(instanceDate.getFullYear() + recurrence.interval);
break;
}
continue;
}
const daysOfWeekIndices = (recurrence.daysOfWeek || []).map(day => {
switch (day.toLowerCase()) {
case "monday":
return 1;
case "tuesday":
return 2;
case "wednesday":
return 3;
case "thursday":
return 4;
case "friday":
return 5;
case "saturday":
return 6;
case "sunday":
return 0;
}
});
daysOfWeekIndices.forEach(dayIndex => {
let tempDate = new Date(instanceDate);
while (tempDate.getDay() !== dayIndex) {
tempDate.setDate(tempDate.getDate() + 1);
}
if (tempDate >= startDate && (!endRecurrenceDate || tempDate <= endRecurrenceDate)) {
const adjustedStartDate = new Date(tempDate);
adjustedStartDate.setHours(startDate.getHours());
adjustedStartDate.setMinutes(startDate.getMinutes());
const adjustedEndDate = new Date(tempDate);
adjustedEndDate.setHours(endDate.getHours());
adjustedEndDate.setMinutes(endDate.getMinutes());
if (adjustedStartDate <= endRecurrenceDate || !endRecurrenceDate) {
events.push({
title: event.subject,
start: adjustToLocalTime(adjustedStartDate.toISOString()),
end: adjustToLocalTime(adjustedEndDate.toISOString()),
allDay: event.isAllDay
});
}
}
});
instanceDate.setDate(instanceDate.getDate() + 7 * recurrence.interval);
}
if (endRecurrenceDate && recurrence.daysOfWeek) {
const tempDate = new Date(endRecurrenceDate);
const endRecurrenceDay = tempDate.toLocaleString('en-US', {
weekday: 'long'
}).toLowerCase();
if (recurrence.daysOfWeek.includes(endRecurrenceDay)) {
const adjustedStartDate = new Date(tempDate);
adjustedStartDate.setHours(startDate.getHours());
adjustedStartDate.setMinutes(startDate.getMinutes());
const adjustedEndDate = new Date(tempDate);
adjustedEndDate.setHours(endDate.getHours());
adjustedEndDate.setMinutes(endDate.getMinutes());
if (adjustedStartDate.getTime() !== endRecurrenceDate.getTime()) {
events.push({
title: event.subject,
start: adjustToLocalTime(adjustedStartDate.toISOString()),
end: adjustToLocalTime(adjustedEndDate.toISOString()),
allDay: event.isAllDay
});
}
}
}
});
const singleEvents = data.value.filter(event => !event.recurrence);
singleEvents.forEach(event => {
events.push({
title: event.subject,
start: adjustToLocalTime(event.start.dateTime),
end: adjustToLocalTime(event.end.dateTime),
allDay: event.isAllDay
});
});
return events;
}
async function handleLogoutClick() {
console.log('Signing out Microsoft...');
Swal.fire({
title: 'Signing out...',
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
},
});
try {
const accounts = await msalInstance.getAllAccounts();
if (accounts.length === 0) {
Swal.close();
return;
}
const respuesta = await msalInstance.logoutPopup({
account: accounts[0],
});
if (respuesta) {
localStorage.setItem('logoutCompleted', 'true');
console.log('Microsoft session closed successfully');
} else {
localStorage.setItem('logoutCompleted', 'false');
console.log('The session was not logged out');
}
Swal.close();
} catch (error) {
Swal.fire({
icon: 'error',
title: 'Error logout',
text: 'An error occurred while trying to log out. Please try again later.'
});
console.log('Error signing out Microsoft', error);
} finally {
Swal.close();
}
}
function renderCalendar(events) {
console.log('I enter the renderCalendar function');
const calendarEl = document.getElementById('calendar');
const calendar = new FullCalendar.Calendar(calendarEl, {
customButtons: {
myCustomButton: {
text: 'Refrescar',
click: async function() {
console.log('Updating Calendar...');
Swal.fire({
title: 'Updating Calendar...',
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
}
});
try {
const accounts = await msalInstance.getAllAccounts();
if (accounts.length === 0) {
await msalInstance.loginRedirect(loginRequest);
return;
}
const response = await msalInstance.acquireTokenSilent({
account: accounts[0],
scopes: ["User.Read", "Calendars.Read", "Calendars.Read.Shared"]
});
if (response !== null) {
const accessToken = response.accessToken;
const events = await getCalendarEvents(accessToken);
renderCalendar(events);
Swal.close();
console.log('Calendar refreshed successfully');
} else {
console.error('Could not get access token.');
console.log('Calendar NOT refreshed');
Swal.close();
}
} catch (error) {
console.log('Error when refreshing calendar');
handleError(error);
}
}
},
logout: {
text: 'Cerrar Sesión',
click: function() {
handleLogoutClick();
}
}
},
headerToolbar: {
left: 'prev,next today myCustomButton logout',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
buttonText: {
today: 'Hoy',
month: 'Mes',
week: 'Semana',
day: 'Día',
list: 'Lista'
},
initialView: 'dayGridMonth',
locale: <?php echo '"' . session('language') . '"'; ?>,
firstDay: 1,
events: events,
dayMaxEvents: true,
eventClick: function(info) {
const event = info.event;
const title = event.title;
const start = event.start;
const end = event.end;
const allDay = event.allDay;
const location = event.extendedProps.location;
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const startFormatted = start.toLocaleString('es-ES', {
timeZone: timeZone
});
const endFormatted = end ? end.toLocaleString('es-ES', {
timeZone: timeZone
}) : 'Evento de todo el día';
let content = `<h2>${title}</h2>`;
if (!allDay) {
content += `<p><strong>Inicio:</strong> ${startFormatted}</p>`;
content += `<p><strong>Fin:</strong> ${endFormatted}</p>`;
} else {
content += `<p><strong>Evento de todo el día</strong></p>`;
}
if (location) {
content += `<p><strong>Ubicación:</strong> ${location}</p>`;
}
Swal.fire({
title: 'Información del Evento',
html: content,
icon: 'info'
});
console.log('Infomacion:' + info.event);
console.log('Titulo:' + title);
console.log('Inicio:' + start);
console.log('Fin:' + end);
console.log('Evento Diario:' + allDay);
}
});
calendar.render();
}
} catch (error) {
console.error('Error during initialization:', error);
Swal.fire({
icon: 'error',
title: 'Initialization error',
text: 'An error occurred while loading the configuration. Please try again later.'
});
}
});
</script>
非常感谢!!
由于多次登录尝试或页面导航,可能会出现错误
(errorCode === "interaction_in_progress")
。
发布答案以帮助社区:
在您的场景中,问题是因为登录按钮和日历位于同一 URI 上。
要解决该问题,请检查以下内容:
https://graph.microsoft.com/v1.0/me/events
因此,请确保登录过程不会干扰应用程序的状态,这样做身份验证流程将完好无损,并且您将能够使用获取的事件成功呈现日历。