File indexing completed on 2024-11-17 04:45:01

0001 /*
0002     SPDX-FileCopyrightText: 2013 Daniel Vrátil <dvratil@redhat.com>
0003     SPDX-FileCopyrightText: 2020 Igor Poboiko <igor.poboiko@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-3.0-or-later
0006 */
0007 
0008 #include "googlesettingswidget.h"
0009 #include "googleresource.h"
0010 #include "googleresource_debug.h"
0011 #include "googlescopes.h"
0012 
0013 #include <QDialogButtonBox>
0014 
0015 #include <KGAPI/Account>
0016 #include <KGAPI/AuthJob>
0017 #include <KGAPI/Calendar/Calendar>
0018 #include <KGAPI/Calendar/CalendarFetchJob>
0019 #include <KGAPI/Tasks/TaskList>
0020 #include <KGAPI/Tasks/TaskListFetchJob>
0021 #include <KMessageBox>
0022 #include <KWindowSystem>
0023 
0024 #include <qt6keychain/keychain.h>
0025 
0026 using namespace QKeychain;
0027 using namespace KGAPI2;
0028 
0029 GoogleSettingsWidget::GoogleSettingsWidget(GoogleSettings &settings, const QString &identifier, QWidget *parent)
0030     : QWidget(parent)
0031     , m_settings(settings)
0032     , m_identifier(identifier)
0033 {
0034     auto mainLayout = new QVBoxLayout(this);
0035 
0036     auto mainWidget = new QWidget(this);
0037     mainLayout->addWidget(mainWidget);
0038     setupUi(mainWidget);
0039 
0040     refreshSpinBox->setSuffix(ki18np(" minute", " minutes"));
0041     enableRefresh->setChecked(m_settings.enableIntervalCheck());
0042     refreshSpinBox->setEnabled(m_settings.enableIntervalCheck());
0043     refreshSpinBox->setValue(m_settings.intervalCheckTime());
0044 
0045     eventsLimitCombo->setMaximumDate(QDate::currentDate());
0046     eventsLimitCombo->setMinimumDate(QDate::fromString(QStringLiteral("2000-01-01"), Qt::ISODate));
0047     eventsLimitCombo->setOptions(KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::WarnOnInvalid);
0048     if (m_settings.eventsSince().isEmpty()) {
0049         const QString ds = QStringLiteral("%1-01-01").arg(QString::number(QDate::currentDate().year() - 3));
0050         eventsLimitCombo->setDate(QDate::fromString(ds, Qt::ISODate));
0051     } else {
0052         eventsLimitCombo->setDate(QDate::fromString(m_settings.eventsSince(), Qt::ISODate));
0053     }
0054     connect(reloadCalendarsBtn, &QPushButton::clicked, this, &GoogleSettingsWidget::slotReloadCalendars);
0055     connect(reloadTaskListsBtn, &QPushButton::clicked, this, &GoogleSettingsWidget::slotReloadTaskLists);
0056     connect(configureBtn, &QPushButton::clicked, this, &GoogleSettingsWidget::loadSettings);
0057     if (m_settings.isReady()) {
0058         m_account = m_settings.accountPtr();
0059     }
0060     connect(&m_settings, &GoogleSettings::accountReady, this, [this](bool ready) {
0061         if (ready) {
0062             m_account = m_settings.accountPtr();
0063             accountChanged();
0064         }
0065     });
0066     QMetaObject::invokeMethod(this, &GoogleSettingsWidget::accountChanged, Qt::QueuedConnection);
0067 }
0068 
0069 GoogleSettingsWidget::~GoogleSettingsWidget()
0070 {
0071 }
0072 
0073 bool GoogleSettingsWidget::handleError(KGAPI2::Job *job)
0074 {
0075     if ((job->error() == KGAPI2::NoError) || (job->error() == KGAPI2::OK)) {
0076         return true;
0077     }
0078 
0079     if (job->error() == KGAPI2::Unauthorized) {
0080         qWarning() << job << job->errorString();
0081         const QList<QUrl> resourceScopes = googleScopes();
0082         for (const QUrl &scope : resourceScopes) {
0083             if (!m_account->scopes().contains(scope)) {
0084                 m_account->addScope(scope);
0085             }
0086         }
0087 
0088         auto authJob = new AuthJob(m_account, m_settings.clientId(), m_settings.clientSecret(), this);
0089         authJob->setProperty(JOB_PROPERTY, QVariant::fromValue(job));
0090         connect(authJob, &AuthJob::finished, this, &GoogleSettingsWidget::slotAuthJobFinished);
0091 
0092         return false;
0093     }
0094 
0095     KMessageBox::error(this, job->errorString());
0096     return false;
0097 }
0098 
0099 void GoogleSettingsWidget::accountChanged()
0100 {
0101     if (!m_account) {
0102         accountLabel->setText(i18n("<b>Not configured</b>"));
0103         calendarsList->setDisabled(true);
0104         reloadCalendarsBtn->setDisabled(true);
0105         calendarsList->clear();
0106         taskListsList->setDisabled(true);
0107         reloadTaskListsBtn->setDisabled(true);
0108         taskListsList->clear();
0109         return;
0110     }
0111     accountLabel->setText(QStringLiteral("<b>%1</b>").arg(m_account->accountName()));
0112     slotReloadCalendars();
0113     slotReloadTaskLists();
0114 }
0115 
0116 void GoogleSettingsWidget::loadSettings()
0117 {
0118     const QString username = m_account && !m_account->accountName().isEmpty() ? m_account->accountName() : QString();
0119     m_account = AccountPtr(new Account());
0120     const QList<QUrl> resourceScopes = googleScopes();
0121     for (const QUrl &scope : resourceScopes) {
0122         if (!m_account->scopes().contains(scope)) {
0123             m_account->addScope(scope);
0124         }
0125     }
0126     auto authJob = new AuthJob(m_account, m_settings.clientId(), m_settings.clientSecret());
0127     authJob->setUsername(username);
0128     connect(authJob, &AuthJob::finished, this, &GoogleSettingsWidget::slotAuthJobFinished);
0129 }
0130 
0131 void GoogleSettingsWidget::slotAuthJobFinished(KGAPI2::Job *job)
0132 {
0133     auto authJob = qobject_cast<AuthJob *>(job);
0134     m_account = authJob->account();
0135     if (authJob->error() != KGAPI2::NoError) {
0136         KMessageBox::error(this, authJob->errorString());
0137         return;
0138     }
0139     accountChanged();
0140 
0141     auto otherJob = job->property(JOB_PROPERTY).value<KGAPI2::Job *>();
0142     if (otherJob) {
0143         otherJob->setAccount(m_account);
0144         otherJob->restart();
0145     }
0146 }
0147 
0148 void GoogleSettingsWidget::saveSettings()
0149 {
0150     auto reset = [this] {
0151         m_settings.setAccount({});
0152         m_settings.setEnableIntervalCheck(enableRefresh->isChecked());
0153         m_settings.setIntervalCheckTime(refreshSpinBox->value());
0154         m_settings.setCalendars({});
0155         m_settings.setTaskLists({});
0156         m_settings.setEventsSince({});
0157         m_settings.save();
0158     };
0159 
0160     if (!m_account) {
0161         reset();
0162         return;
0163     }
0164 
0165     auto writeJob = m_settings.storeAccount(m_account);
0166     connect(writeJob, &WritePasswordJob::finished, this, [this, reset, writeJob]() {
0167         if (writeJob->error()) {
0168             qCWarning(GOOGLE_LOG) << "Failed to store account's password in secret storage" << writeJob->errorString();
0169             reset();
0170             return;
0171         }
0172 
0173         m_settings.setAccount(m_account->accountName());
0174         m_settings.setEnableIntervalCheck(enableRefresh->isChecked());
0175         m_settings.setIntervalCheckTime(refreshSpinBox->value());
0176 
0177         QStringList calendars;
0178         for (int i = 0; i < calendarsList->count(); i++) {
0179             QListWidgetItem *item = calendarsList->item(i);
0180 
0181             if (item->checkState() == Qt::Checked) {
0182                 calendars.append(item->data(Qt::UserRole).toString());
0183             }
0184         }
0185         m_settings.setCalendars(calendars);
0186 
0187         if (eventsLimitCombo->isValid()) {
0188             m_settings.setEventsSince(eventsLimitCombo->date().toString(Qt::ISODate));
0189         }
0190 
0191         QStringList taskLists;
0192         for (int i = 0; i < taskListsList->count(); i++) {
0193             QListWidgetItem *item = taskListsList->item(i);
0194 
0195             if (item->checkState() == Qt::Checked) {
0196                 taskLists.append(item->data(Qt::UserRole).toString());
0197             }
0198         }
0199         m_settings.setTaskLists(taskLists);
0200         m_settings.save();
0201     });
0202 
0203     // Ideally we should have an async API
0204     // This ensure the config dialog is not destroyed before the password
0205     // is saved;
0206     QEventLoop loop;
0207     connect(writeJob, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
0208     writeJob->start();
0209     loop.exec();
0210 }
0211 
0212 void GoogleSettingsWidget::slotReloadCalendars()
0213 {
0214     calendarsList->setDisabled(true);
0215     reloadCalendarsBtn->setDisabled(true);
0216     calendarsList->clear();
0217 
0218     if (!m_account) {
0219         return;
0220     }
0221 
0222     auto fetchJob = new CalendarFetchJob(m_account, this);
0223     connect(fetchJob, &CalendarFetchJob::finished, this, [this](KGAPI2::Job *job) {
0224         if (!handleError(job) || !m_account) {
0225             calendarsList->setEnabled(false);
0226             reloadCalendarsBtn->setEnabled(false);
0227             return;
0228         }
0229 
0230         const ObjectsList objects = qobject_cast<FetchJob *>(job)->items();
0231 
0232         QStringList activeCalendars;
0233         if (m_account->accountName() == m_settings.account()) {
0234             activeCalendars = m_settings.calendars();
0235         }
0236         calendarsList->clear();
0237         for (const ObjectPtr &object : objects) {
0238             const CalendarPtr calendar = object.dynamicCast<Calendar>();
0239 
0240             auto item = new QListWidgetItem(calendar->title());
0241             item->setData(Qt::UserRole, calendar->uid());
0242             item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
0243             item->setCheckState((activeCalendars.isEmpty() || activeCalendars.contains(calendar->uid())) ? Qt::Checked : Qt::Unchecked);
0244             calendarsList->addItem(item);
0245         }
0246 
0247         calendarsList->setEnabled(true);
0248         reloadCalendarsBtn->setEnabled(true);
0249     });
0250 }
0251 
0252 void GoogleSettingsWidget::slotReloadTaskLists()
0253 {
0254     if (!m_account) {
0255         return;
0256     }
0257 
0258     taskListsList->setDisabled(true);
0259     reloadTaskListsBtn->setDisabled(true);
0260     taskListsList->clear();
0261 
0262     auto job = new TaskListFetchJob(m_account, this);
0263     connect(job, &TaskListFetchJob::finished, this, [this](KGAPI2::Job *job) {
0264         if (!handleError(job) || !m_account) {
0265             taskListsList->setDisabled(true);
0266             reloadTaskListsBtn->setDisabled(true);
0267             return;
0268         }
0269 
0270         const ObjectsList objects = qobject_cast<FetchJob *>(job)->items();
0271 
0272         QStringList activeTaskLists;
0273         if (m_account->accountName() == m_settings.account()) {
0274             activeTaskLists = m_settings.taskLists();
0275         }
0276         taskListsList->clear();
0277         for (const ObjectPtr &object : objects) {
0278             const TaskListPtr taskList = object.dynamicCast<TaskList>();
0279 
0280             auto item = new QListWidgetItem(taskList->title());
0281             item->setData(Qt::UserRole, taskList->uid());
0282             item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
0283             item->setCheckState((activeTaskLists.isEmpty() || activeTaskLists.contains(taskList->uid())) ? Qt::Checked : Qt::Unchecked);
0284             taskListsList->addItem(item);
0285         }
0286 
0287         taskListsList->setEnabled(true);
0288         reloadTaskListsBtn->setEnabled(true);
0289     });
0290 }
0291 
0292 #include "moc_googlesettingswidget.cpp"