HEX
Server: LiteSpeed
System: Linux d8 4.18.0-553.121.1.lve.el8.x86_64 #1 SMP Thu Apr 30 16:40:41 UTC 2026 x86_64
User: wbwebdes (3015)
PHP: 8.1.31
Disabled: exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Upload Files
File: /home/wbwebdes/domains/appointment.nailsbyrianne.nl/public_html/assets/js/utils/calendar_sync.js
/* ----------------------------------------------------------------------------
 * Easy!Appointments - Online Appointment Scheduler
 *
 * @package     EasyAppointments
 * @author      A.Tselegidis <[email protected]>
 * @copyright   Copyright (c) Alex Tselegidis
 * @license     https://opensource.org/licenses/GPL-3.0 - GPLv3
 * @link        https://easyappointments.org
 * @since       v1.5.0
 * ---------------------------------------------------------------------------- */

/**
 * Calendar sync utility.
 *
 * This module implements the functionality of calendar sync.
 *
 * Old Name: BackendCalendarSync
 */
App.Utils.CalendarSync = (function () {
    const $selectFilterItem = $('#select-filter-item');
    const $enableSync = $('#enable-sync');
    const $disableSync = $('#disable-sync');
    const $triggerSync = $('#trigger-sync');
    const $syncButtonGroup = $('#sync-button-group');
    const $reloadAppointments = $('#reload-appointments');

    const FILTER_TYPE_PROVIDER = 'provider';
    let isSyncing = false;

    function hasSync(type) {
        const $selectedOption = $selectFilterItem.find('option:selected');

        return Boolean(Number($selectedOption.attr(`${type}-sync`)));
    }

    function updateSyncButtons() {
        const $selectedOption = $selectFilterItem.find('option:selected');
        const type = $selectedOption.attr('type');
        const isProvider = type === FILTER_TYPE_PROVIDER;
        const hasGoogleSync = Boolean(Number($selectedOption.attr('google-sync')));
        const hasCaldavSync = Boolean(Number($selectedOption.attr('caldav-sync')));
        const hasSync = hasGoogleSync || hasCaldavSync;

        $enableSync.prop('hidden', !isProvider || hasSync);
        $syncButtonGroup.prop('hidden', !isProvider || !hasSync);
    }

    function enableGoogleSync() {
        // Enable synchronization for selected provider.

        const authUrl = App.Utils.Url.siteUrl('google/oauth/' + $('#select-filter-item').val());

        const redirectUrl = App.Utils.Url.siteUrl('google/oauth_callback');

        const windowHandle = window.open(authUrl, 'Easy!Appointments', 'width=800, height=600');

        const authInterval = window.setInterval(() => {
            // When the browser redirects to the Google user consent page the "window.document" variable
            // becomes "undefined" and when it comes back to the redirect URL it changes back. So check
            // whether the variable is undefined to avoid javascript errors.
            try {
                if (windowHandle.document) {
                    if (windowHandle.document.URL.indexOf(redirectUrl) !== -1) {
                        // The user has granted access to his data.
                        windowHandle.close();

                        window.clearInterval(authInterval);

                        const $selectedOption = $selectFilterItem.find('option:selected');

                        $selectedOption.attr('google-sync', '1');

                        updateSyncButtons();

                        selectGoogleCalendar();
                    }
                }
            } catch (Error) {
                // Accessing the document object before the window is loaded throws an error, but it will only
                // happen during the initialization of the window. Attaching "load" event handling is not
                // possible due to CORS restrictions.
            }
        }, 100);
    }

    function disableGoogleSync() {
        App.Utils.Message.show(lang('disable_sync'), lang('disable_sync_prompt'), [
            {
                text: lang('cancel'),
                click: (event, messageModal) => {
                    messageModal.hide();
                },
            },
            {
                text: lang('confirm'),
                click: (event, messageModal) => {
                    // Disable synchronization for selected provider.
                    const providerId = $selectFilterItem.val();

                    const provider = vars('available_providers').find(
                        (availableProvider) => Number(availableProvider.id) === Number(providerId),
                    );

                    if (!provider) {
                        throw new Error('Provider not found: ' + providerId);
                    }

                    provider.settings.google_sync = '0';
                    provider.settings.google_token = null;

                    App.Http.Google.disableProviderSync(provider.id);

                    const $selectedOption = $selectFilterItem.find('option:selected');

                    $selectedOption.attr('google-sync', '0');

                    updateSyncButtons();

                    messageModal.hide();
                },
            },
        ]);
    }

    function selectGoogleCalendar() {
        const providerId = $selectFilterItem.val();

        App.Http.Google.getGoogleCalendars(providerId).done((googleCalendars) => {
            const $selectGoogleCalendar = $(`
                <select class="form-control">
                    <!-- JS -->
                </select>
            `);

            googleCalendars.forEach((googleCalendar) => {
                $selectGoogleCalendar.append(new Option(googleCalendar.summary, googleCalendar.id));
            });

            const $messageModal = App.Utils.Message.show(
                lang('select_sync_calendar'),
                lang('select_sync_calendar_prompt'),
                [
                    {
                        text: lang('select'),
                        click: (event, messageModal) => {
                            const googleCalendarId = $selectGoogleCalendar.val();

                            App.Http.Google.selectGoogleCalendar(providerId, googleCalendarId).done(() => {
                                App.Layouts.Backend.displayNotification(lang('sync_calendar_selected'));
                            });

                            messageModal.hide();
                        },
                    },
                ],
            );

            $selectGoogleCalendar.appendTo($messageModal.find('.modal-body'));
        });
    }

    function triggerGoogleSync() {
        const providerId = $selectFilterItem.val();

        App.Http.Google.syncWithGoogle(providerId)
            .done(() => {
                App.Layouts.Backend.displayNotification(lang('calendar_sync_completed'));
                $reloadAppointments.trigger('click');
            })
            .fail(() => {
                App.Layouts.Backend.displayNotification(lang('calendar_sync_failed'));
            })
            .always(() => {
                isSyncing = false;
            });
    }

    function enableCaldavSync(defaultCaldavUrl = '', defaultCaldavUsername = '', defaultCaldavPassword = '') {
        const $container = $(`
            <div>
                <div class="mb-3">
                    <label for="caldav-url" class="form-label">
                        ${lang('calendar_url')}
                    </label>
                    <input type="text" class="form-control" id="caldav-url" value="${defaultCaldavUrl}"/>
                </div> 
                <div class="mb-3">
                    <label for="caldav-username" class="form-label">
                        ${lang('username')}
                    </label>
                    <input type="text" class="form-control" id="caldav-username" value="${defaultCaldavUsername}"/>
                </div> 
                <div class="mb-3">
                    <label for="caldav-password" class="form-label">
                        ${lang('password')}
                    </label>
                    <input type="password" class="form-control" id="caldav-password" value="${defaultCaldavPassword}"/>
                </div>    
                
                <div class="alert alert-danger" hidden>
                    <!-- JS -->
                </div>
            </div>
        `);

        const $messageModal = App.Utils.Message.show(lang('caldav_server'), lang('caldav_connection_info_prompt'), [
            {
                text: lang('cancel'),
                click: (event, messageModal) => {
                    messageModal.hide();
                },
            },
            {
                text: lang('connect'),
                click: (event, messageModal) => {
                    const providerId = $selectFilterItem.val();

                    $messageModal.find('.is-invalid').removeClass('is-invalid');

                    const $alert = $messageModal.find('.alert');
                    $alert.text('').prop('hidden', true);

                    const $caldavUrl = $container.find('#caldav-url');
                    const caldavUrl = $caldavUrl.val();

                    if (!caldavUrl) {
                        $caldavUrl.addClass('is-invalid');
                        return;
                    }

                    const $caldavUsername = $container.find('#caldav-username');
                    const caldavUsername = $caldavUsername.val();

                    if (!caldavUsername) {
                        $caldavUsername.addClass('is-invalid');
                        return;
                    }

                    const $caldavPassword = $container.find('#caldav-password');
                    const caldavPassword = $caldavPassword.val();

                    if (!caldavPassword) {
                        $caldavPassword.addClass('is-invalid');
                        return;
                    }

                    App.Http.Caldav.connectToServer(providerId, caldavUrl, caldavUsername, caldavPassword).done(
                        (response) => {
                            if (!response.success) {
                                $caldavUrl.addClass('is-invalid');
                                $caldavUsername.addClass('is-invalid');
                                $caldavPassword.addClass('is-invalid');

                                $alert.text(lang('login_failed') + ' ' + response.message).prop('hidden', false);

                                return;
                            }

                            const $selectedOption = $selectFilterItem.find('option:selected');

                            $selectedOption.attr('caldav-sync', '1');

                            updateSyncButtons();

                            App.Layouts.Backend.displayNotification(lang('sync_calendar_selected'));

                            messageModal.hide();
                        },
                    );
                },
            },
        ]);

        $messageModal.find('.modal-body').append($container);
    }

    function disableCaldavSync() {
        App.Utils.Message.show(lang('disable_sync'), lang('disable_sync_prompt'), [
            {
                text: lang('cancel'),
                click: (event, messageModal) => {
                    messageModal.hide();
                },
            },
            {
                text: lang('confirm'),
                click: (event, messageModal) => {
                    // Disable synchronization for selected provider.
                    const providerId = $selectFilterItem.val();

                    const provider = vars('available_providers').find(
                        (availableProvider) => Number(availableProvider.id) === Number(providerId),
                    );

                    if (!provider) {
                        throw new Error('Provider not found: ' + providerId);
                    }

                    provider.settings.caldav_sync = '0';
                    provider.settings.caldav_url = null;
                    provider.settings.caldav_username = null;
                    provider.settings.caldav_password = null;

                    App.Http.Caldav.disableProviderSync(provider.id);

                    const $selectedOption = $selectFilterItem.find('option:selected');

                    $selectedOption.attr('caldav-sync', '0');

                    updateSyncButtons();

                    messageModal.hide();
                },
            },
        ]);
    }

    function triggerCaldavSync() {
        const providerId = $selectFilterItem.val();

        App.Http.Caldav.syncWithCaldav(providerId)
            .done(() => {
                App.Layouts.Backend.displayNotification(lang('calendar_sync_completed'));
                $reloadAppointments.trigger('click');
            })
            .fail(() => {
                App.Layouts.Backend.displayNotification(lang('calendar_sync_failed'));
            })
            .always(() => {
                isSyncing = false;
            });
    }

    function onSelectFilterItemChange() {
        updateSyncButtons();
    }

    function onEnableSyncClick() {
        const isGoogleSyncFeatureEnabled = vars('google_sync_feature');

        if (!isGoogleSyncFeatureEnabled) {
            enableCaldavSync();
            return;
        }

        App.Utils.Message.show(lang('enable_sync'), lang('sync_method_prompt'), [
            {
                text: 'CalDAV Calendar',
                className: 'btn btn-outline-primary me-auto',
                click: (event, messageModal) => {
                    messageModal.hide();
                    enableCaldavSync();
                },
            },
            {
                text: 'Google Calendar',
                click: (event, messageModal) => {
                    messageModal.hide();
                    enableGoogleSync();
                },
            },
        ]);
    }

    function onDisableSyncClick() {
        const hasGoogleSync = hasSync('google');

        if (hasGoogleSync) {
            disableGoogleSync();
            return;
        }

        const hasCalSync = hasSync('caldav');

        if (hasCalSync) {
            disableCaldavSync();
        }
    }

    function onTriggerSyncClick() {
        const hasGoogleSync = hasSync('google');
        isSyncing = true;

        if (hasGoogleSync) {
            triggerGoogleSync();
            return;
        }

        const hasCalSync = hasSync('caldav');

        if (hasCalSync) {
            triggerCaldavSync();
        }
    }

    function isCurrentlySyncing() {
        return isSyncing;
    }

    /**
     * Initialize the module.
     */
    function initialize() {
        $selectFilterItem.on('change', onSelectFilterItemChange);
        $enableSync.on('click', onEnableSyncClick);
        $disableSync.on('click', onDisableSyncClick);
        $triggerSync.on('click', onTriggerSyncClick);
    }

    document.addEventListener('DOMContentLoaded', initialize);

    return {
        initialize,
        isCurrentlySyncing,
    };
})();