app/core/CalendarSubscriptions.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QDateTime> | ||
| 4 | #include <QList> | ||
| 5 | #include <QObject> | ||
| 6 | #include <QSet> | ||
| 7 | #include <QString> | ||
| 8 | #include <QTimer> | ||
| 9 | #include <functional> | ||
| 10 | |||
| 11 | class CalendarStore; | ||
| 12 | class QNetworkAccessManager; | ||
| 13 | |||
| 14 | // Sprint 93 S93.5.4 (CAL-SUBSCRIBE): read-only calendars from an ICS URL | ||
| 15 | // (holidays, team feeds, webcal:// links). | ||
| 16 | // | ||
| 17 | // A subscription is a local read-only calendar in the same store and | ||
| 18 | // sidebar (accountId "subscription:<id>", path = the URL). refreshDue() | ||
| 19 | // downloads every subscription whose interval has passed (GET with the | ||
| 20 | // DAV size and timeout limits, no redirect to a less safe scheme, no | ||
| 21 | // credentials), reads it with IcsFile and replaces the calendar's | ||
| 22 | // appointments; a failed download keeps the last copy and records the | ||
| 23 | // error. The list lives in QSettings "calendar/subscriptions". | ||
| 24 | class CalendarSubscriptions final : public QObject { | ||
| 25 | 10 | Q_OBJECT | |
| 26 | public: | ||
| 27 | struct Subscription { | ||
| 28 | QString id; | ||
| 29 | QString url; // https:// (webcal:// is stored as https://) | ||
| 30 | QString name; | ||
| 31 | int intervalMinutes = 360; | ||
| 32 | QString username; // CAL-SUBSCRIBE-AUTH: "" = public feed; the password is in the keyring | ||
| 33 | qint64 lastRefresh = 0; // epoch seconds of the last successful download | ||
| 34 | QString lastError; | ||
| 35 | }; | ||
| 36 | using NamFactory = std::function<QNetworkAccessManager *(QObject *parent)>; | ||
| 37 | // Reads the keyring password of a protected feed (tests inject one). | ||
| 38 | using PasswordReader = std::function<void(QObject *owner, const Subscription &, | ||
| 39 | std::function<void(QByteArray, QString)>)>; | ||
| 40 | using PasswordWriter = std::function<bool(const Subscription &, const QByteArray &)>; | ||
| 41 | |||
| 42 | CalendarSubscriptions(CalendarStore *store, QObject *parent = nullptr); | ||
| 43 | 2 | void setNetworkAccessManagerFactory(NamFactory factory) { m_namFactory = std::move(factory); } | |
| 44 | 946 | void setNetworkGate(std::function<bool()> gate) { m_networkGate = std::move(gate); } | |
| 45 | 1 | void setClock(std::function<qint64()> clock) { m_clock = std::move(clock); } | |
| 46 | 1 | void setPasswordStore(PasswordReader reader, PasswordWriter writer) { | |
| 47 | 1 | m_passwordReader = std::move(reader); | |
| 48 | 1 | m_passwordWriter = std::move(writer); | |
| 49 | 1 | } | |
| 50 | |||
| 51 | static QList<Subscription> subscriptions(); | ||
| 52 | static QString accountIdFor(const QString &subscriptionId); | ||
| 53 | static bool isSubscription(const QString &accountId); | ||
| 54 | // Returns the new id, or "" (with *error) for an invalid URL or a | ||
| 55 | // duplicate. The first download starts at once. | ||
| 56 | // A username makes it a protected feed: https only, the password goes to | ||
| 57 | // the keyring (never into QSettings or logs). | ||
| 58 | QString add(const QString &url, const QString &name, int intervalMinutes, QString *error = nullptr, | ||
| 59 | const QString &username = {}, const QByteArray &password = {}); | ||
| 60 | bool remove(const QString &id); | ||
| 61 | // Sprint 100 S100.1.14 (D-23): rename a subscription or change how often | ||
| 62 | // it is downloaded; the calendar in the store gets the new name at once. | ||
| 63 | bool update(const QString &id, const QString &name, int intervalMinutes); | ||
| 64 | // Downloads the due subscriptions (all with `force`). | ||
| 65 | void refreshDue(bool force = false); | ||
| 66 | void refresh(const QString &id); | ||
| 67 | bool busy() const { return !m_running.isEmpty(); } | ||
| 68 | // S106-256: whether this subscription's own download runs now. | ||
| 69 | ✗ | bool running(const QString &id) const { return m_running.contains(id); } | |
| 70 | |||
| 71 | signals: | ||
| 72 | void storeChanged(); | ||
| 73 | void changed(); // list, refresh state or errors | ||
| 74 | |||
| 75 | private: | ||
| 76 | static void save(const QList<Subscription> &list); | ||
| 77 | void storeCalendar(const Subscription &entry); | ||
| 78 | void finished(const QString &id, const QByteArray &body, const QString &error); | ||
| 79 | void download(const Subscription &subscription, const QByteArray &password); | ||
| 80 | qint64 now() const; | ||
| 81 | |||
| 82 | CalendarStore *m_store = nullptr; | ||
| 83 | NamFactory m_namFactory; | ||
| 84 | std::function<bool()> m_networkGate; | ||
| 85 | std::function<qint64()> m_clock; | ||
| 86 | PasswordReader m_passwordReader; | ||
| 87 | PasswordWriter m_passwordWriter; | ||
| 88 | QSet<QString> m_running; | ||
| 89 | QTimer m_timer; | ||
| 90 | }; | ||
| 91 |