feat(integrations): add role-based security and 1-click Outlook connect for colleagues
This commit is contained in:
+19
-13
@@ -72,18 +72,15 @@ if (empty($code)) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Instellingen ophalen
|
// 4. Centrale instellingen ophalen
|
||||||
$stmt = $db->prepare("SELECT settings FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
$appConfig = microsoft_get_app_config();
|
||||||
$stmt->execute([$user['id']]);
|
|
||||||
$row = $stmt->fetch();
|
|
||||||
$savedSettings = json_decode($row['settings'] ?? '{}', true) ?: [];
|
|
||||||
|
|
||||||
$clientId = !empty($savedSettings['client_id']) ? $savedSettings['client_id'] : fociz_env('MICROSOFT_CLIENT_ID', '');
|
$clientId = $appConfig['client_id'];
|
||||||
$clientSecret = !empty($savedSettings['client_secret']) ? $savedSettings['client_secret'] : fociz_env('MICROSOFT_CLIENT_SECRET', '');
|
$clientSecret = $appConfig['client_secret'];
|
||||||
$tenantId = !empty($savedSettings['tenant_id']) ? $savedSettings['tenant_id'] : fociz_env('MICROSOFT_TENANT_ID', 'common');
|
$tenantId = $appConfig['tenant_id'];
|
||||||
|
|
||||||
if (empty($clientId) || empty($clientSecret)) {
|
if (empty($clientId) || empty($clientSecret)) {
|
||||||
header('Location: /integrations.php?error=' . urlencode('Client ID of Client Secret ontbreekt in Fociz. Vul deze eerst in bij de Outlook instellingen.'));
|
header('Location: /integrations.php?error=' . urlencode('Microsoft 365 configuratie (Client ID of Secret) ontbreekt in Fociz. Neem contact op met de beheerder.'));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,15 +105,24 @@ $accountEmail = $profile['mail'] ?? ($profile['userPrincipalName'] ?? $user['ema
|
|||||||
$accountName = $profile['displayName'] ?? 'Microsoft 365 Account';
|
$accountName = $profile['displayName'] ?? 'Microsoft 365 Account';
|
||||||
|
|
||||||
// 7. Opslaan in MariaDB
|
// 7. Opslaan in MariaDB
|
||||||
$newSettings = array_merge($savedSettings, [
|
$stmtExisting = $db->prepare("SELECT settings FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
||||||
'client_id' => $clientId,
|
$stmtExisting->execute([$user['id']]);
|
||||||
'client_secret' => $clientSecret,
|
$existingRow = $stmtExisting->fetch();
|
||||||
'tenant_id' => $tenantId,
|
$existingSettings = json_decode($existingRow['settings'] ?? '{}', true) ?: [];
|
||||||
|
|
||||||
|
$newSettings = array_merge($existingSettings, [
|
||||||
'expires_at' => $expiresAt,
|
'expires_at' => $expiresAt,
|
||||||
'account_email' => $accountEmail,
|
'account_email' => $accountEmail,
|
||||||
'account_name' => $accountName,
|
'account_name' => $accountName,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Als de ingelogde gebruiker Beheerder (ADMIN) is, bewaren we ook de centrale app credentials
|
||||||
|
if ($user['role'] === 'ADMIN') {
|
||||||
|
$newSettings['client_id'] = $clientId;
|
||||||
|
$newSettings['client_secret'] = $clientSecret;
|
||||||
|
$newSettings['tenant_id'] = $tenantId;
|
||||||
|
}
|
||||||
|
|
||||||
$saveStmt = $db->prepare("
|
$saveStmt = $db->prepare("
|
||||||
INSERT INTO integrations (user_id, provider, is_enabled, is_demo_mode, access_token, refresh_token, settings, last_sync_at)
|
INSERT INTO integrations (user_id, provider, is_enabled, is_demo_mode, access_token, refresh_token, settings, last_sync_at)
|
||||||
VALUES (?, 'OUTLOOK', 1, 0, ?, ?, ?, NOW())
|
VALUES (?, 'OUTLOOK', 1, 0, ?, ?, ?, NOW())
|
||||||
|
|||||||
@@ -9,22 +9,17 @@ require_once __DIR__ . '/../includes/integrations/microsoft.php';
|
|||||||
$user = require_auth();
|
$user = require_auth();
|
||||||
$db = get_db_connection();
|
$db = get_db_connection();
|
||||||
|
|
||||||
// Haal eventueel opgeslagen client_id en tenant_id op uit database
|
// Haal centrale Microsoft app configuratie op (env of admin instellingen)
|
||||||
$stmt = $db->prepare("SELECT settings FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
$appConfig = microsoft_get_app_config();
|
||||||
$stmt->execute([$user['id']]);
|
|
||||||
$row = $stmt->fetch();
|
|
||||||
$savedSettings = json_decode($row['settings'] ?? '{}', true) ?: [];
|
|
||||||
|
|
||||||
$clientId = !empty($_GET['client_id'])
|
$clientId = !empty($_GET['client_id']) ? trim($_GET['client_id']) : $appConfig['client_id'];
|
||||||
? trim($_GET['client_id'])
|
$tenantId = !empty($_GET['tenant_id']) ? trim($_GET['tenant_id']) : $appConfig['tenant_id'];
|
||||||
: (!empty($savedSettings['client_id']) ? $savedSettings['client_id'] : fociz_env('MICROSOFT_CLIENT_ID', ''));
|
|
||||||
|
|
||||||
$tenantId = !empty($_GET['tenant_id'])
|
|
||||||
? trim($_GET['tenant_id'])
|
|
||||||
: (!empty($savedSettings['tenant_id']) ? $savedSettings['tenant_id'] : fociz_env('MICROSOFT_TENANT_ID', 'common'));
|
|
||||||
|
|
||||||
if (empty($clientId)) {
|
if (empty($clientId)) {
|
||||||
header('Location: /integrations.php?error=' . urlencode('Vul eerst de Application (Client) ID en het Client Secret in bij de Outlook instellingen om te koppelen met Microsoft 365.'));
|
$err = ($user['role'] === 'ADMIN')
|
||||||
|
? 'Vul eerst de Application (Client) ID en het Client Secret in bij de Outlook instellingen om te koppelen met Microsoft 365.'
|
||||||
|
: 'Microsoft 365 is nog niet geconfigureerd door de beheerder van uw organisatie. Vraag uw beheerder om de koppeling in te stellen.';
|
||||||
|
header('Location: /integrations.php?error=' . urlencode($err));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,90 @@ const MS_OAUTH_TOKEN_URL = 'https://login.microsoftonline.com/%s/oauth2/v2.0
|
|||||||
const MS_GRAPH_BASE_URL = 'https://graph.microsoft.com/v1.0';
|
const MS_GRAPH_BASE_URL = 'https://graph.microsoft.com/v1.0';
|
||||||
const MS_DEFAULT_SCOPES = 'offline_access User.Read Calendars.ReadWrite';
|
const MS_DEFAULT_SCOPES = 'offline_access User.Read Calendars.ReadWrite';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Haalt de centrale Microsoft App Registratie configuratie op (Client ID, Secret, Tenant ID)
|
||||||
|
* Prioriteit:
|
||||||
|
* 1. Environment variabelen (.env of server env)
|
||||||
|
* 2. Opgeslagen configuratie van een Beheerder (ADMIN) in de integrations tabel
|
||||||
|
* 3. Fallback naar integrations tabel waar settings client_id bevat
|
||||||
|
*/
|
||||||
|
function microsoft_get_app_config(): array {
|
||||||
|
$clientId = fociz_env('MICROSOFT_CLIENT_ID', '');
|
||||||
|
$clientSecret = fociz_env('MICROSOFT_CLIENT_SECRET', '');
|
||||||
|
$tenantId = fociz_env('MICROSOFT_TENANT_ID', '');
|
||||||
|
|
||||||
|
if (empty($clientId) || empty($clientSecret)) {
|
||||||
|
try {
|
||||||
|
$db = get_db_connection();
|
||||||
|
// Zoek bij actieve beheerder(s) in de integrations tabel
|
||||||
|
$stmt = $db->query("
|
||||||
|
SELECT i.settings
|
||||||
|
FROM integrations i
|
||||||
|
JOIN users u ON i.user_id = u.id
|
||||||
|
WHERE u.role = 'ADMIN' AND i.provider = 'OUTLOOK' AND i.settings IS NOT NULL
|
||||||
|
ORDER BY i.updated_at DESC
|
||||||
|
LIMIT 5
|
||||||
|
");
|
||||||
|
$rows = $stmt->fetchAll();
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
if (!empty($row['settings'])) {
|
||||||
|
$saved = json_decode($row['settings'], true) ?: [];
|
||||||
|
if (empty($clientId) && !empty($saved['client_id'])) {
|
||||||
|
$clientId = trim($saved['client_id']);
|
||||||
|
}
|
||||||
|
if (empty($clientSecret) && !empty($saved['client_secret'])) {
|
||||||
|
$clientSecret = trim($saved['client_secret']);
|
||||||
|
}
|
||||||
|
if (empty($tenantId) && !empty($saved['tenant_id'])) {
|
||||||
|
$tenantId = trim($saved['tenant_id']);
|
||||||
|
}
|
||||||
|
if (!empty($clientId) && !empty($clientSecret)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: zoek een willekeurige OUTLOOK rij met ingevulde client_id
|
||||||
|
if (empty($clientId) || empty($clientSecret)) {
|
||||||
|
$stmtFallback = $db->query("
|
||||||
|
SELECT settings
|
||||||
|
FROM integrations
|
||||||
|
WHERE provider = 'OUTLOOK' AND settings LIKE '%client_id%'
|
||||||
|
ORDER BY updated_at DESC
|
||||||
|
LIMIT 3
|
||||||
|
");
|
||||||
|
$fallbackRows = $stmtFallback->fetchAll();
|
||||||
|
foreach ($fallbackRows as $fbRow) {
|
||||||
|
if (!empty($fbRow['settings'])) {
|
||||||
|
$saved = json_decode($fbRow['settings'], true) ?: [];
|
||||||
|
if (empty($clientId) && !empty($saved['client_id'])) {
|
||||||
|
$clientId = trim($saved['client_id']);
|
||||||
|
}
|
||||||
|
if (empty($clientSecret) && !empty($saved['client_secret'])) {
|
||||||
|
$clientSecret = trim($saved['client_secret']);
|
||||||
|
}
|
||||||
|
if (empty($tenantId) && !empty($saved['tenant_id'])) {
|
||||||
|
$tenantId = trim($saved['tenant_id']);
|
||||||
|
}
|
||||||
|
if (!empty($clientId) && !empty($clientSecret)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
error_log("microsoft_get_app_config error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'client_id' => $clientId ?: '',
|
||||||
|
'client_secret' => $clientSecret ?: '',
|
||||||
|
'tenant_id' => !empty($tenantId) ? $tenantId : 'common',
|
||||||
|
'is_configured' => !empty($clientId) && !empty($clientSecret),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Genereert de Microsoft OAuth2 login URL
|
* Genereert de Microsoft OAuth2 login URL
|
||||||
*/
|
*/
|
||||||
@@ -117,9 +201,10 @@ function microsoft_get_valid_token(int $userId): ?string {
|
|||||||
|
|
||||||
// Token is verlopen of verloopt bijna; refresh met refresh_token
|
// Token is verlopen of verloopt bijna; refresh met refresh_token
|
||||||
$refreshToken = $integration['refresh_token'] ?? '';
|
$refreshToken = $integration['refresh_token'] ?? '';
|
||||||
$clientId = $settings['client_id'] ?? fociz_env('MICROSOFT_CLIENT_ID', '');
|
$appConfig = microsoft_get_app_config();
|
||||||
$clientSecret = $settings['client_secret'] ?? fociz_env('MICROSOFT_CLIENT_SECRET', '');
|
$clientId = !empty($settings['client_id']) ? $settings['client_id'] : $appConfig['client_id'];
|
||||||
$tenantId = $settings['tenant_id'] ?? fociz_env('MICROSOFT_TENANT_ID', 'common');
|
$clientSecret = !empty($settings['client_secret']) ? $settings['client_secret'] : $appConfig['client_secret'];
|
||||||
|
$tenantId = !empty($settings['tenant_id']) ? $settings['tenant_id'] : $appConfig['tenant_id'];
|
||||||
|
|
||||||
if (empty($refreshToken) || empty($clientId) || empty($clientSecret)) {
|
if (empty($refreshToken) || empty($clientId) || empty($clientSecret)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -251,10 +336,33 @@ function microsoft_fetch_today_events(int $userId, ?string $date = null): array
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Ontkoppelt Microsoft integratie voor een gebruiker
|
* Ontkoppelt Microsoft integratie voor een gebruiker
|
||||||
|
* Wist uitsluitend de persoonlijke tokens; behoudt de centrale Azure App gegevens indien aanwezig.
|
||||||
*/
|
*/
|
||||||
function microsoft_disconnect(int $userId): bool {
|
function microsoft_disconnect(int $userId): bool {
|
||||||
$db = get_db_connection();
|
$db = get_db_connection();
|
||||||
$stmt = $db->prepare("DELETE FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
$stmt = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
||||||
return $stmt->execute([$userId]);
|
$stmt->execute([$userId]);
|
||||||
|
$integration = $stmt->fetch();
|
||||||
|
|
||||||
|
if (!$integration) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$settings = json_decode($integration['settings'] ?? '{}', true) ?: [];
|
||||||
|
// Verwijder gebruikers-specifieke token gegevens
|
||||||
|
unset($settings['account_email'], $settings['account_name'], $settings['expires_at']);
|
||||||
|
|
||||||
|
// Als er centrale app credentials in staan (bijv. van beheerder), behouden we deze netjes
|
||||||
|
if (!empty($settings['client_id']) || !empty($settings['client_secret'])) {
|
||||||
|
$updateStmt = $db->prepare("
|
||||||
|
UPDATE integrations
|
||||||
|
SET access_token = NULL, refresh_token = NULL, settings = ?, last_sync_at = NULL, is_enabled = 1, is_demo_mode = 0
|
||||||
|
WHERE id = ?
|
||||||
|
");
|
||||||
|
return $updateStmt->execute([json_encode($settings), $integration['id']]);
|
||||||
|
} else {
|
||||||
|
$delStmt = $db->prepare("DELETE FROM integrations WHERE id = ?");
|
||||||
|
return $delStmt->execute([$integration['id']]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+157
-18
@@ -43,19 +43,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
// 2. Outlook Opslaan of Koppelen
|
// 2. Outlook Opslaan of Koppelen
|
||||||
elseif ($provider === 'OUTLOOK') {
|
elseif ($provider === 'OUTLOOK') {
|
||||||
|
$isAdmin = ($user['role'] === 'ADMIN');
|
||||||
|
$appConfig = microsoft_get_app_config();
|
||||||
|
|
||||||
|
// A. Koppelen via OAuth (zowel voor Collega als Beheerder)
|
||||||
|
if ($action === 'connect_oauth') {
|
||||||
|
// Als de beheerder vanaf het admin-formulier koppelt en eventueel gewijzigde velden meestuurt
|
||||||
|
if ($isAdmin && isset($_POST['client_id'])) {
|
||||||
$clientId = trim($_POST['client_id'] ?? '');
|
$clientId = trim($_POST['client_id'] ?? '');
|
||||||
$clientSecret = trim($_POST['client_secret'] ?? '');
|
$clientSecret = trim($_POST['client_secret'] ?? '');
|
||||||
$tenantId = trim($_POST['tenant_id'] ?? 'common');
|
$tenantId = trim($_POST['tenant_id'] ?? 'common');
|
||||||
if (empty($tenantId)) $tenantId = 'common';
|
if (empty($tenantId)) $tenantId = 'common';
|
||||||
$isDemo = isset($_POST['is_demo_mode']) ? 1 : 0;
|
$isDemo = isset($_POST['is_demo_mode']) ? 1 : 0;
|
||||||
|
|
||||||
// Haal bestaande tokens op
|
|
||||||
$stmtExisting = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
$stmtExisting = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
||||||
$stmtExisting->execute([$userId]);
|
$stmtExisting->execute([$userId]);
|
||||||
$existing = $stmtExisting->fetch();
|
$existing = $stmtExisting->fetch();
|
||||||
$existingSettings = json_decode($existing['settings'] ?? '{}', true) ?: [];
|
$existingSettings = json_decode($existing['settings'] ?? '{}', true) ?: [];
|
||||||
|
|
||||||
// Als geheim veld leeg is gelaten of bullet tekens bevat, behoud het bestaande geheim
|
|
||||||
if (($clientSecret === '' || $clientSecret === '••••••••••••••••') && !empty($existingSettings['client_secret']) && $existingSettings['client_secret'] !== '••••••••••••••••') {
|
if (($clientSecret === '' || $clientSecret === '••••••••••••••••') && !empty($existingSettings['client_secret']) && $existingSettings['client_secret'] !== '••••••••••••••••') {
|
||||||
$clientSecret = $existingSettings['client_secret'];
|
$clientSecret = $existingSettings['client_secret'];
|
||||||
}
|
}
|
||||||
@@ -76,8 +81,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
");
|
");
|
||||||
$stmt->execute([$userId, $isDemo, json_encode($newSettings)]);
|
$stmt->execute([$userId, $isDemo, json_encode($newSettings)]);
|
||||||
|
|
||||||
// Als gebruiker direct op 'Koppel nu' klikte
|
|
||||||
if ($action === 'connect_oauth') {
|
|
||||||
if (empty($clientId)) {
|
if (empty($clientId)) {
|
||||||
$message = "Vul eerst de Application (Client) ID in van uw Azure App Registratie.";
|
$message = "Vul eerst de Application (Client) ID in van uw Azure App Registratie.";
|
||||||
$messageType = 'error';
|
$messageType = 'error';
|
||||||
@@ -85,14 +88,63 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$message = "Vul eerst het Client Secret in van uw Azure App Registratie.";
|
$message = "Vul eerst het Client Secret in van uw Azure App Registratie.";
|
||||||
$messageType = 'error';
|
$messageType = 'error';
|
||||||
} else {
|
} else {
|
||||||
header("Location: /auth/oauth_microsoft.php?client_id=" . urlencode($clientId) . "&tenant_id=" . urlencode($tenantId));
|
header("Location: /auth/oauth_microsoft.php");
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$message = "Instellingen voor Microsoft Outlook zijn succesvol opgeslagen.";
|
// Collega of Beheerder zonder inline velden
|
||||||
|
if (!$appConfig['is_configured']) {
|
||||||
|
$message = "Microsoft 365 is nog niet geconfigureerd door de beheerder. Vraag een beheerder om de Application ID en Client Secret in te stellen.";
|
||||||
|
$messageType = 'error';
|
||||||
|
} else {
|
||||||
|
header("Location: /auth/oauth_microsoft.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// B. Alleen Beheerders mogen de centrale Azure instellingen opslaan
|
||||||
|
elseif ($action === 'save') {
|
||||||
|
if (!$isAdmin) {
|
||||||
|
$message = "Alleen beheerders kunnen centrale integratie-instellingen wijzigen.";
|
||||||
|
$messageType = 'error';
|
||||||
|
} else {
|
||||||
|
$clientId = trim($_POST['client_id'] ?? '');
|
||||||
|
$clientSecret = trim($_POST['client_secret'] ?? '');
|
||||||
|
$tenantId = trim($_POST['tenant_id'] ?? 'common');
|
||||||
|
if (empty($tenantId)) $tenantId = 'common';
|
||||||
|
$isDemo = isset($_POST['is_demo_mode']) ? 1 : 0;
|
||||||
|
|
||||||
|
$stmtExisting = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK'");
|
||||||
|
$stmtExisting->execute([$userId]);
|
||||||
|
$existing = $stmtExisting->fetch();
|
||||||
|
$existingSettings = json_decode($existing['settings'] ?? '{}', true) ?: [];
|
||||||
|
|
||||||
|
if (($clientSecret === '' || $clientSecret === '••••••••••••••••') && !empty($existingSettings['client_secret']) && $existingSettings['client_secret'] !== '••••••••••••••••') {
|
||||||
|
$clientSecret = $existingSettings['client_secret'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$newSettings = array_merge($existingSettings, [
|
||||||
|
'client_id' => $clientId,
|
||||||
|
'client_secret' => $clientSecret,
|
||||||
|
'tenant_id' => $tenantId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$stmt = $db->prepare("
|
||||||
|
INSERT INTO integrations (user_id, provider, is_enabled, is_demo_mode, settings, last_sync_at)
|
||||||
|
VALUES (?, 'OUTLOOK', 1, ?, ?, NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
is_demo_mode = VALUES(is_demo_mode),
|
||||||
|
settings = VALUES(settings),
|
||||||
|
last_sync_at = NOW()
|
||||||
|
");
|
||||||
|
$stmt->execute([$userId, $isDemo, json_encode($newSettings)]);
|
||||||
|
|
||||||
|
$message = "Centrale instellingen voor Microsoft Outlook zijn succesvol opgeslagen.";
|
||||||
$messageType = 'success';
|
$messageType = 'success';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3. Teams Opslaan
|
// 3. Teams Opslaan
|
||||||
elseif ($provider === 'TEAMS') {
|
elseif ($provider === 'TEAMS') {
|
||||||
@@ -193,6 +245,22 @@ $jiraIsDemo = !empty($jiraData['is_demo']);
|
|||||||
|
|
||||||
// Callback redirect URI
|
// Callback redirect URI
|
||||||
$redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
$redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
||||||
|
|
||||||
|
$appConfig = microsoft_get_app_config();
|
||||||
|
$isAdmin = ($user['role'] === 'ADMIN');
|
||||||
|
|
||||||
|
// Voor beheerders tonen we eventueel centraal opgeslagen waarden als fallback in de inputvelden
|
||||||
|
if ($isAdmin) {
|
||||||
|
if (empty($outlookSettings['client_id']) && !empty($appConfig['client_id'])) {
|
||||||
|
$outlookSettings['client_id'] = $appConfig['client_id'];
|
||||||
|
}
|
||||||
|
if (empty($outlookSettings['client_secret']) && !empty($appConfig['client_secret'])) {
|
||||||
|
$outlookSettings['client_secret'] = $appConfig['client_secret'];
|
||||||
|
}
|
||||||
|
if (empty($outlookSettings['tenant_id']) && !empty($appConfig['tenant_id'])) {
|
||||||
|
$outlookSettings['tenant_id'] = $appConfig['tenant_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
@@ -208,12 +276,17 @@ $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
|||||||
Integraties Hub
|
Integraties Hub
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-slate-400 text-xs sm:text-sm mt-1 max-w-2xl">
|
<p class="text-slate-400 text-xs sm:text-sm mt-1 max-w-2xl">
|
||||||
Koppel Microsoft 365 Outlook (voor realtime agenda & Teams meetings) en Atlassian Jira Cloud (om direct uw toegewezen tickets in te plannen).
|
<?= $isAdmin
|
||||||
|
? 'Beheer centrale organisatie-integraties en koppel uw persoonlijke Outlook agenda en Jira tickets.'
|
||||||
|
: 'Koppel uw persoonlijke Microsoft 365 Outlook agenda en Atlassian Jira tickets om direct uw dagplanning in Fociz te vullen.' ?>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Snelle status pills -->
|
<!-- Snelle status pills -->
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
|
<span class="px-2.5 py-1 rounded-xl text-xs font-semibold <?= $isAdmin ? 'bg-emerald-500/10 border border-emerald-500/30 text-emerald-400' : 'bg-slate-800/80 border border-slate-700 text-slate-300' ?>">
|
||||||
|
<?= $isAdmin ? 'Beheerdersweergave' : 'Collega Weergave' ?>
|
||||||
|
</span>
|
||||||
<div class="flex items-center space-x-1.5 px-3 py-1.5 rounded-xl border text-xs font-semibold <?= $isOutlookConnected ? 'bg-emerald-500/10 border-emerald-500/30 text-emerald-400' : 'bg-slate-800/80 border-slate-700 text-slate-400' ?>">
|
<div class="flex items-center space-x-1.5 px-3 py-1.5 rounded-xl border text-xs font-semibold <?= $isOutlookConnected ? 'bg-emerald-500/10 border-emerald-500/30 text-emerald-400' : 'bg-slate-800/80 border-slate-700 text-slate-400' ?>">
|
||||||
<span class="w-2 h-2 rounded-full <?= $isOutlookConnected ? 'bg-emerald-400 animate-pulse' : 'bg-slate-500' ?>"></span>
|
<span class="w-2 h-2 rounded-full <?= $isOutlookConnected ? 'bg-emerald-400 animate-pulse' : 'bg-slate-500' ?>"></span>
|
||||||
<span>Outlook: <?= $isOutlookConnected ? 'Verbonden' : 'Niet verbonden' ?></span>
|
<span>Outlook: <?= $isOutlookConnected ? 'Verbonden' : 'Niet verbonden' ?></span>
|
||||||
@@ -291,6 +364,16 @@ $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($isAdmin): ?>
|
||||||
|
<!-- ADMIN WEERGAVE: Centrale melding & Instructies Accordion -->
|
||||||
|
<div class="p-2.5 bg-blue-500/10 border border-blue-500/20 rounded-xl text-[11px] text-blue-300 flex items-start space-x-2">
|
||||||
|
<i data-lucide="shield" class="w-4 h-4 flex-shrink-0 mt-0.5 text-blue-400"></i>
|
||||||
|
<div class="leading-relaxed">
|
||||||
|
<strong class="text-white block font-semibold">Centrale Beheerdersinstellingen</strong>
|
||||||
|
<span>Deze Azure App gegevens gelden voor alle collega's in de organisatie. Gewone gebruikers zien deze technische details niet en hoeven alleen op 'Koppelen' te klikken.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Azure App Instructies Accordion -->
|
<!-- Azure App Instructies Accordion -->
|
||||||
<details class="group text-xs bg-slate-950/50 rounded-xl border border-slate-800/80 p-3">
|
<details class="group text-xs bg-slate-950/50 rounded-xl border border-slate-800/80 p-3">
|
||||||
<summary class="cursor-pointer font-semibold text-blue-400 flex items-center justify-between">
|
<summary class="cursor-pointer font-semibold text-blue-400 flex items-center justify-between">
|
||||||
@@ -312,8 +395,8 @@ $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
|||||||
<p>5. Voeg API-machtigingen toe: <code>Calendars.ReadWrite</code>, <code>User.Read</code>, <code>offline_access</code>.</p>
|
<p>5. Voeg API-machtigingen toe: <code>Calendars.ReadWrite</code>, <code>User.Read</code>, <code>offline_access</code>.</p>
|
||||||
<p>6. <strong class="text-amber-300">Cruciaal voor organisaties:</strong> Klik direct boven de tabel met machtigingen op de knop <strong class="text-white">"Beheerdersgoedkeuring verlenen voor [Organisatie]"</strong> (<em>Grant admin consent</em>) en bevestig met 'Ja'. Hiermee voorkomt u de melding <em>"Beheerdersgoedkeuring vereist"</em>.</p>
|
<p>6. <strong class="text-amber-300">Cruciaal voor organisaties:</strong> Klik direct boven de tabel met machtigingen op de knop <strong class="text-white">"Beheerdersgoedkeuring verlenen voor [Organisatie]"</strong> (<em>Grant admin consent</em>) en bevestig met 'Ja'. Hiermee voorkomt u de melding <em>"Beheerdersgoedkeuring vereist"</em>.</p>
|
||||||
<?php
|
<?php
|
||||||
$curClientId = !empty($outlookSettings['client_id']) ? $outlookSettings['client_id'] : fociz_env('MICROSOFT_CLIENT_ID', '');
|
$curClientId = !empty($outlookSettings['client_id']) ? $outlookSettings['client_id'] : $appConfig['client_id'];
|
||||||
$curTenantId = !empty($outlookSettings['tenant_id']) ? $outlookSettings['tenant_id'] : 'organizations';
|
$curTenantId = !empty($outlookSettings['tenant_id']) ? $outlookSettings['tenant_id'] : (!empty($appConfig['tenant_id']) ? $appConfig['tenant_id'] : 'organizations');
|
||||||
if (!empty($curClientId)):
|
if (!empty($curClientId)):
|
||||||
$adminConsentUrl = "https://login.microsoftonline.com/" . urlencode($curTenantId) . "/v2.0/adminconsent?client_id=" . urlencode($curClientId) . "&redirect_uri=" . urlencode($redirectUri) . "&scope=" . urlencode("https://graph.microsoft.com/.default");
|
$adminConsentUrl = "https://login.microsoftonline.com/" . urlencode($curTenantId) . "/v2.0/adminconsent?client_id=" . urlencode($curClientId) . "&redirect_uri=" . urlencode($redirectUri) . "&scope=" . urlencode("https://graph.microsoft.com/.default");
|
||||||
?>
|
?>
|
||||||
@@ -326,9 +409,34 @@ $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
<?php else: ?>
|
||||||
|
<!-- COLLEGA WEERGAVE: Informatiebanner over status -->
|
||||||
|
<?php if (!$appConfig['is_configured']): ?>
|
||||||
|
<div class="p-3.5 bg-amber-500/10 border border-amber-500/20 rounded-xl text-xs text-amber-300 flex items-start space-x-3">
|
||||||
|
<i data-lucide="clock" class="w-5 h-5 flex-shrink-0 text-amber-400 mt-0.5"></i>
|
||||||
|
<div>
|
||||||
|
<strong class="block font-semibold mb-0.5">Configuratie vereist door beheerder</strong>
|
||||||
|
<span class="text-[11px] text-slate-300 leading-relaxed">
|
||||||
|
Uw beheerder heeft de Microsoft 365 integratie nog niet geconfigureerd voor uw organisatie. Vraag uw beheerder om de Azure App gegevens in te stellen.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php elseif (!$isOutlookConnected): ?>
|
||||||
|
<div class="p-3.5 bg-blue-500/10 border border-blue-500/20 rounded-xl text-xs text-slate-300 flex items-start space-x-3">
|
||||||
|
<i data-lucide="shield-check" class="w-5 h-5 flex-shrink-0 text-blue-400 mt-0.5"></i>
|
||||||
|
<div>
|
||||||
|
<strong class="text-white block font-semibold mb-0.5">Gereed voor Koppeling</strong>
|
||||||
|
<span class="text-[11px] text-slate-300 leading-relaxed">
|
||||||
|
Uw beheerder heeft Microsoft 365 klaargezet voor uw organisatie. Koppel uw persoonlijke werkagenda om al uw meetings en Teams-afspraken automatisch in uw dagplanning te laden.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Instellingen formulier -->
|
<?php if ($isAdmin): ?>
|
||||||
|
<!-- ADMIN FORMULIER -->
|
||||||
<form method="POST" action="/integrations.php" class="pt-4 border-t border-slate-800 space-y-2.5 mt-4">
|
<form method="POST" action="/integrations.php" class="pt-4 border-t border-slate-800 space-y-2.5 mt-4">
|
||||||
<input type="hidden" name="provider" value="OUTLOOK">
|
<input type="hidden" name="provider" value="OUTLOOK">
|
||||||
|
|
||||||
@@ -339,7 +447,7 @@ $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="client_id"
|
name="client_id"
|
||||||
value="<?= htmlspecialchars($outlookSettings['client_id'] ?? fociz_env('MICROSOFT_CLIENT_ID', '')) ?>"
|
value="<?= htmlspecialchars($outlookSettings['client_id'] ?? '') ?>"
|
||||||
placeholder="bijv. e8f498c1-1234-4567-89ab-cdef01234567"
|
placeholder="bijv. e8f498c1-1234-4567-89ab-cdef01234567"
|
||||||
class="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white placeholder-slate-600 focus:outline-none focus:border-blue-500 font-mono"
|
class="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white placeholder-slate-600 focus:outline-none focus:border-blue-500 font-mono"
|
||||||
>
|
>
|
||||||
@@ -355,7 +463,7 @@ $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
|||||||
type="password"
|
type="password"
|
||||||
id="msClientSecret"
|
id="msClientSecret"
|
||||||
name="client_secret"
|
name="client_secret"
|
||||||
value="<?= htmlspecialchars(!empty($outlookSettings['client_secret']) && $outlookSettings['client_secret'] !== '••••••••••••••••' ? $outlookSettings['client_secret'] : fociz_env('MICROSOFT_CLIENT_SECRET', '')) ?>"
|
value="<?= htmlspecialchars(!empty($outlookSettings['client_secret']) && $outlookSettings['client_secret'] !== '••••••••••••••••' ? $outlookSettings['client_secret'] : '') ?>"
|
||||||
placeholder="Plak hier de Waarde (Value) van uw Client Secret"
|
placeholder="Plak hier de Waarde (Value) van uw Client Secret"
|
||||||
class="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 pr-8 text-xs text-white placeholder-slate-600 focus:outline-none focus:border-blue-500 font-mono"
|
class="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 pr-8 text-xs text-white placeholder-slate-600 focus:outline-none focus:border-blue-500 font-mono"
|
||||||
>
|
>
|
||||||
@@ -387,8 +495,8 @@ $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
$curClientId = !empty($outlookSettings['client_id']) ? $outlookSettings['client_id'] : fociz_env('MICROSOFT_CLIENT_ID', '');
|
$curClientId = !empty($outlookSettings['client_id']) ? $outlookSettings['client_id'] : $appConfig['client_id'];
|
||||||
$curTenantId = !empty($outlookSettings['tenant_id']) ? $outlookSettings['tenant_id'] : 'organizations';
|
$curTenantId = !empty($outlookSettings['tenant_id']) ? $outlookSettings['tenant_id'] : (!empty($appConfig['tenant_id']) ? $appConfig['tenant_id'] : 'organizations');
|
||||||
if (!empty($curClientId)):
|
if (!empty($curClientId)):
|
||||||
$adminConsentUrl = "https://login.microsoftonline.com/" . urlencode($curTenantId) . "/v2.0/adminconsent?client_id=" . urlencode($curClientId) . "&redirect_uri=" . urlencode($redirectUri) . "&scope=" . urlencode("https://graph.microsoft.com/.default");
|
$adminConsentUrl = "https://login.microsoftonline.com/" . urlencode($curTenantId) . "/v2.0/adminconsent?client_id=" . urlencode($curClientId) . "&redirect_uri=" . urlencode($redirectUri) . "&scope=" . urlencode("https://graph.microsoft.com/.default");
|
||||||
?>
|
?>
|
||||||
@@ -421,21 +529,52 @@ $redirectUri = rtrim(APP_URL, '/') . '/auth/callback_microsoft.php';
|
|||||||
|
|
||||||
<div class="flex space-x-2 pt-2">
|
<div class="flex space-x-2 pt-2">
|
||||||
<button type="submit" name="action" value="save" class="flex-1 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-semibold transition">
|
<button type="submit" name="action" value="save" class="flex-1 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-semibold transition">
|
||||||
Opslaan
|
Instellingen Opslaan
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="submit" name="action" value="connect_oauth" class="flex-1 py-2 rounded-xl bg-blue-600 hover:bg-blue-500 text-white text-xs font-bold transition flex items-center justify-center space-x-1 shadow-md shadow-blue-500/20">
|
<button type="submit" name="action" value="connect_oauth" class="flex-1 py-2 rounded-xl bg-blue-600 hover:bg-blue-500 text-white text-xs font-bold transition flex items-center justify-center space-x-1 shadow-md shadow-blue-500/20">
|
||||||
<i data-lucide="key" class="w-3.5 h-3.5"></i>
|
<i data-lucide="key" class="w-3.5 h-3.5"></i>
|
||||||
<span><?= $isOutlookConnected ? 'Opnieuw Koppelen' : 'Koppel Outlook' ?></span>
|
<span><?= $isOutlookConnected ? 'Opnieuw Koppelen' : 'Koppel Mijn Outlook' ?></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($isOutlookConnected): ?>
|
<?php if ($isOutlookConnected): ?>
|
||||||
<button type="submit" name="action" value="disconnect" onclick="return confirm('Weet u zeker dat u Microsoft Outlook wilt ontkoppelen?')" class="w-full py-1.5 text-[11px] text-red-400 hover:text-red-300 transition text-center block">
|
<button type="submit" name="action" value="disconnect" onclick="return confirm('Weet u zeker dat u Microsoft Outlook wilt ontkoppelen voor uw account?')" class="w-full py-1.5 text-[11px] text-red-400 hover:text-red-300 transition text-center block">
|
||||||
Koppeling Verwijderen
|
Mijn Koppeling Verwijderen
|
||||||
</button>
|
</button>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</form>
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<!-- COLLEGA FORMULIER: Eenvoudig & Veilig (1-klik) -->
|
||||||
|
<div class="pt-4 border-t border-slate-800 mt-4">
|
||||||
|
<?php if (!$appConfig['is_configured']): ?>
|
||||||
|
<button type="button" disabled class="w-full py-2.5 rounded-xl bg-slate-800/60 border border-slate-700/60 text-slate-500 text-xs font-semibold cursor-not-allowed flex items-center justify-center space-x-2">
|
||||||
|
<i data-lucide="lock" class="w-3.5 h-3.5"></i>
|
||||||
|
<span>Wachten op Configuratie Beheerder</span>
|
||||||
|
</button>
|
||||||
|
<?php elseif ($isOutlookConnected): ?>
|
||||||
|
<form method="POST" action="/integrations.php" class="space-y-2">
|
||||||
|
<input type="hidden" name="provider" value="OUTLOOK">
|
||||||
|
<button type="submit" name="action" value="connect_oauth" class="w-full py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 text-xs font-semibold transition flex items-center justify-center space-x-2">
|
||||||
|
<i data-lucide="refresh-cw" class="w-3.5 h-3.5"></i>
|
||||||
|
<span>Opnieuw Aanmelden bij Microsoft</span>
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="action" value="disconnect" onclick="return confirm('Weet u zeker dat u uw persoonlijke Outlook agenda wilt ontkoppelen?')" class="w-full py-2 rounded-xl bg-red-500/10 hover:bg-red-500/20 border border-red-500/20 text-red-400 text-xs font-semibold transition flex items-center justify-center space-x-1.5">
|
||||||
|
<i data-lucide="link-2-off" class="w-3.5 h-3.5"></i>
|
||||||
|
<span>Mijn Agenda Ontkoppelen</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<form method="POST" action="/integrations.php">
|
||||||
|
<input type="hidden" name="provider" value="OUTLOOK">
|
||||||
|
<button type="submit" name="action" value="connect_oauth" class="w-full py-3 rounded-xl bg-blue-600 hover:bg-blue-500 text-white text-xs font-bold transition flex items-center justify-center space-x-2 shadow-lg shadow-blue-500/25">
|
||||||
|
<i data-lucide="calendar-plus" class="w-4 h-4"></i>
|
||||||
|
<span>Koppel Mijn Microsoft Outlook Agenda</span>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 2. MICROSOFT TEAMS -->
|
<!-- 2. MICROSOFT TEAMS -->
|
||||||
|
|||||||
Reference in New Issue
Block a user