Commit 66018d78 authored by David Jarvie's avatar David Jarvie

Correctly interpret resource IDs in command line and DBus calls

Resource IDs were often ignored when prefixed to an event ID, in
options such as --triggerEvent.
parent 7781bb6b
......@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.5)
set(KDEPIM_VERSION_NUMBER "5.14.1")
set(PIM_VERSION ${KDEPIM_VERSION_NUMBER})
set(RELEASE_SERVICE_VERSION "20.04.0")
set(KALARM_VERSION "2.14.0")
set(KALARM_VERSION "2.14.1")
project(kalarm VERSION ${KALARM_VERSION})
set(KALARM_FULL_VERSION "${KALARM_VERSION} (KDE Apps ${RELEASE_SERVICE_VERSION})")
......
KAlarm Change Log
=== Version 2.14.1 (KDE Applications 20.04.1) --- 9 April 2020 ===
+ Correctly interpret resource IDs in command line and DBus calls.
=== Version 2.14.0 (KDE Applications 20.04) --- 27 March 2020 ===
+ Warn user if archiving but no default archived alarms calendar is set.
+ Fix some error messages not being displayed.
......
......@@ -287,9 +287,9 @@ void CommandOptions::process()
|| checkCommand(OptCANCEL_EVENT, CANCEL_EVENT)
|| checkCommand(OptEDIT, EDIT))
{
// Fetch the event ID. This can optionally include a prefix of the
// resource ID followed by a colon delimiter.
mEventId = EventId(mParser->value(*mOptions.at(mCommandOpt)));
// Fetch the resource and event IDs. The supplied ID is the event ID,
// optionally prefixed by the resource ID followed by a colon delimiter.
mResourceId = EventId::extractIDs(mParser->value(*mOptions.at(mCommandOpt)), mEventId);
}
if (checkCommand(OptEDIT_NEW_PRESET, EDIT_NEW_PRESET))
{
......
/*
* commandoptions.h - extract command line options
* Program: kalarm
* Copyright © 2001-2019 David Jarvie <djarvie@kde.org>
* Copyright © 2001-2020 David Jarvie <djarvie@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -59,7 +59,8 @@ class CommandOptions
void process();
Command command() const { return mCommand; }
QString commandName() const { return optionName(mCommandOpt); }
EventId eventId() const { return mEventId; }
QString eventId() const { return mEventId; }
QString resourceId() const { return mResourceId; }
QString templateName() const { return mTemplateName; }
EditAlarmDlg::Type editType() const { return mEditType; }
KAEvent::SubAction editAction() const { return mEditAction; }
......@@ -154,7 +155,8 @@ class CommandOptions
QString mError; // error message
Command mCommand {NONE}; // the selected command
Option mCommandOpt; // option for the selected command
EventId mEventId; // TRIGGER_EVENT, CANCEL_EVENT, EDIT: event ID
QString mEventId; // TRIGGER_EVENT, CANCEL_EVENT, EDIT: event ID
QString mResourceId; // TRIGGER_EVENT, CANCEL_EVENT, EDIT: optional resource ID
QString mTemplateName; // EDIT_NEW_PRESET: template name
EditAlarmDlg::Type mEditType; // NEW, EDIT_NEW_*: alarm edit type
KAEvent::SubAction mEditAction; // NEW: alarm edit sub-type
......
......@@ -27,6 +27,7 @@
#include "kamail.h"
#include "mainwindow.h"
#include "preferences.h"
#include "resources/resources.h"
#include <kalarmadaptor.h>
#include "kalarm_debug.h"
......@@ -58,11 +59,15 @@ DBusHandler::DBusHandler()
bool DBusHandler::cancelEvent(const QString& eventId)
{
if (!Resources::allPopulated())
return false; // can't access events before calendars are loaded
return theApp()->dbusDeleteEvent(EventId(eventId));
}
bool DBusHandler::triggerEvent(const QString& eventId)
{
if (!Resources::allPopulated())
return false; // can't access events before calendars are loaded
return theApp()->dbusTriggerEvent(EventId(eventId));
}
......@@ -257,6 +262,8 @@ bool DBusHandler::scheduleAudio(const QString& audioUrl, int volumePercent, cons
bool DBusHandler::edit(const QString& eventID)
{
if (!Resources::allPopulated())
return false; // can't access events before calendars are loaded
return KAlarm::editAlarmById(EventId(eventID));
}
......
......@@ -24,33 +24,58 @@
#include "resources/resources.h"
#include "kalarm_debug.h"
#include <QRegExp>
#include <QRegularExpression>
/** Set by event ID prefixed by optional resource ID, in the format "[rid:]eid". */
EventId::EventId(const QString& resourceEventId)
{
bool resourceOk = false;
QRegExp rx(QStringLiteral("^\\w+:"));
if (rx.indexIn(resourceEventId) == 0)
{
// A resource ID has been supplied, so use it
int n = rx.matchedLength();
Resource res = Resources::resourceForConfigName(resourceEventId.left(n - 1));
first = res.id();
second = resourceEventId.mid(n);
resourceOk = true;
}
if (!resourceOk)
const QString resourceIdString = extractIDs(resourceEventId, mEventId);
if (!resourceIdString.isEmpty())
mResourceId = getResourceId(resourceIdString); // convert the resource ID string
}
bool EventId::operator==(const EventId& other) const
{
return mEventId == other.mEventId && mResourceId == other.mResourceId;
}
ResourceId EventId::resourceDisplayId() const
{
return mResourceId;
}
QString EventId::extractIDs(const QString& resourceEventId, QString& eventId)
{
QRegularExpression rx(QStringLiteral("^(\\w+):(.*)$"));
QRegularExpressionMatch rxmatch = rx.match(resourceEventId);
if (!rxmatch.hasMatch())
{
// Only an event ID has been supplied (or the syntax was invalid)
first = -1;
second = resourceEventId;
eventId = resourceEventId; // no resource ID supplied
return QString();
}
// A resource ID has been supplied
eventId = rxmatch.captured(2);
return rxmatch.captured(1);
}
ResourceId EventId::resourceDisplayId() const
ResourceId EventId::getResourceId(const QString& resourceIdString)
{
return first;
// Check if a resource configuration name has been supplied.
Resource res = Resources::resourceForConfigName(resourceIdString);
if (res.isValid())
return res.id();
// Check if a resource ID number has been supplied.
bool ok;
const ResourceId id = resourceIdString.toLongLong(&ok);
if (ok)
{
res = Resources::resource(id);
if (res.isValid())
return id;
}
return -1;
}
// vim: et sw=4:
......@@ -25,8 +25,6 @@
#include <KAlarmCal/KAEvent>
#include <QPair>
using namespace KAlarmCal;
/**
......@@ -37,32 +35,76 @@ using namespace KAlarmCal;
* Note that the resource ID of the display calendar is -1, since it is not a
* resources calendar.
*/
struct EventId : public QPair<ResourceId, QString>
class EventId
{
EventId()
: QPair<ResourceId, QString>(-1, QString()) {}
public:
EventId() {}
EventId(ResourceId c, const QString& e)
: QPair<ResourceId, QString>(c, e) {}
: mEventId(e)
, mResourceId(c)
{}
explicit EventId(const KAEvent& event)
: QPair<ResourceId, QString>(event.resourceId(), event.id()) {}
: mEventId(event.id())
, mResourceId(event.resourceId())
{}
/** Set by event ID prefixed by optional resource ID, in the format "[rid:]eid". */
/** Set by event ID prefixed by optional resource ID, in the format "[rid:]eid".
* "rid" can be the resource configuration name, or the resource ID number in
* string format.
* @note Resources must have been created before calling this method;
* otherwise, the resource ID will be invalid (-1).
*/
explicit EventId(const QString& resourceEventId);
void clear() { first = -1; second.clear(); }
bool operator==(const EventId&) const;
bool operator!=(const EventId& other) const { return !operator==(other); }
void clear() { mResourceId = -1; mEventId.clear(); }
/** Return whether the instance contains any data. */
bool isEmpty() const { return second.isEmpty(); }
bool isEmpty() const { return mEventId.isEmpty(); }
ResourceId resourceId() const { return first; }
ResourceId resourceId() const { return mResourceId; }
ResourceId resourceDisplayId() const;
QString eventId() const { return second; }
void setResourceId(ResourceId id) { first = id; }
QString eventId() const { return mEventId; }
void setResourceId(ResourceId id) { mResourceId = id; }
/** Extract the resource and event ID strings from an ID in the format "[rid:]eid".
* "rid" can be the resource configuration name, or the resource ID number in
* string format.
* @param resourceEventId Full ID "[rid:]eid"
* @param eventId Receives the event ID "eid"
* @return The resource ID "rid".
*/
static QString extractIDs(const QString& resourceEventId, QString& eventId);
/** Get the numerical resource ID from a resource ID string.
* The string can be the resource configuration name, or the resource ID
* number in string format.
* @note Resources must have been created before calling this function;
* otherwise, the returned resource ID will be invalid (-1).
*
* @param resourceIdString Resource ID string "rid"
* @return The resource ID, or -1 if not found.
*/
static ResourceId getResourceId(const QString& resourceIdString);
private:
QString mEventId;
ResourceId mResourceId {-1};
};
// Declare as a movable type (note that QString is movable).
Q_DECLARE_TYPEINFO(EventId, Q_MOVABLE_TYPE);
inline uint qHash(const EventId& eid, uint seed)
{
uint h1 = qHash(eid.eventId(), seed);
uint h2 = qHash(eid.resourceId(), seed);
return ((h1 << 16) | (h1 >> 16)) ^ h2 ^ seed;
}
inline QDebug operator<<(QDebug s, const EventId& id)
{
s.nospace() << "\"" << id.resourceDisplayId() << "::" << id.eventId().toLatin1().constData() << "\"";
......
......@@ -418,8 +418,8 @@ int KAlarmApp::activateInstance(const QStringList& args, const QString& workingD
case CommandOptions::CANCEL_EVENT:
{
// Display or delete the event with the specified event ID
const QueuedAction action = static_cast<QueuedAction>(int((command == CommandOptions::TRIGGER_EVENT) ? QueuedAction::Trigger : QueuedAction::Cancel)
| int(QueuedAction::FindId) | int(QueuedAction::Exit));
QueuedAction action = static_cast<QueuedAction>(int((command == CommandOptions::TRIGGER_EVENT) ? QueuedAction::Trigger : QueuedAction::Cancel)
| int(QueuedAction::Exit));
// Open the calendar, don't start processing execution queue yet,
// and wait for the calendar resources to be populated.
if (!initCheck(true))
......@@ -427,7 +427,12 @@ int KAlarmApp::activateInstance(const QStringList& args, const QString& workingD
else
{
mCommandOption = options->commandName();
mActionQueue.enqueue(ActionQEntry(action, options->eventId()));
// Get the resource ID string and event UID. Note that if
// resources have not been created yet, the numeric
// resource ID can't yet be looked up.
if (options->resourceId().isEmpty())
action = static_cast<QueuedAction>((int)action | int(QueuedAction::FindId));
mActionQueue.enqueue(ActionQEntry(action, EventId(options->eventId()), options->resourceId()));
startProcessQueue(); // start processing the execution queue
dontRedisplay = true;
}
......@@ -460,7 +465,10 @@ int KAlarmApp::activateInstance(const QStringList& args, const QString& workingD
mCommandOption = options->commandName();
if (firstInstance)
mEditingCmdLineAlarm = 0x10; // want to redisplay alarms if successful
mActionQueue.enqueue(ActionQEntry(QueuedAction::Edit, options->eventId()));
// Get the resource ID string and event UID. Note that if
// resources have not been created yet, the numeric
// resource ID can't yet be looked up.
mActionQueue.enqueue(ActionQEntry(QueuedAction::Edit, EventId(options->eventId()), options->resourceId()));
startProcessQueue(); // start processing the execution queue
dontRedisplay = true;
}
......@@ -941,6 +949,25 @@ void KAlarmApp::processQueue()
{
ActionQEntry& entry = mActionQueue.head();
// If the first action's resource ID is a string, can't process it
// until its numeric resource ID can be found.
if (!entry.resourceId.isEmpty())
{
if (!Resources::allCreated())
{
// If resource population has timed out, discard all queued events.
if (mResourcesTimedOut)
{
qCCritical(KALARM_LOG) << "Error! Timeout creating calendars";
mActionQueue.clear();
}
break;
}
// Convert the resource ID string to the numeric resource ID.
entry.eventId.setResourceId(EventId::getResourceId(entry.resourceId));
entry.resourceId.clear();
}
// Can't process the first action until its resource has been populated.
const ResourceId id = entry.eventId.resourceId();
if ((id < 0 && !Resources::allPopulated())
......@@ -1288,6 +1315,7 @@ void KAlarmApp::slotResourcesCreated()
}
checkWritableCalendar();
checkArchivedCalendar();
processQueue();
}
/******************************************************************************
......
......@@ -182,11 +182,13 @@ class KAlarmApp : public QApplication
struct ActionQEntry
{
ActionQEntry(QueuedAction a, const EventId& id) : action(a), eventId(id) { }
ActionQEntry(QueuedAction a, const EventId& id, const QString& resId) : action(a), eventId(id), resourceId(resId) { }
ActionQEntry(const KAEvent& e, QueuedAction a = QueuedAction::Handle) : action(a), event(e) { }
ActionQEntry() { }
QueuedAction action;
EventId eventId;
KAEvent event;
QString resourceId; // resource ID or name, if resources not created yet
};
KAlarmApp(int& argc, char** argv);
......
......@@ -155,9 +155,9 @@ enum
};
QList<MessageWin*> MessageWin::mWindowList;
QMap<EventId, unsigned> MessageWin::mErrorMessages;
bool MessageWin::mRedisplayed = false;
QList<MessageWin*> MessageWin::mWindowList;
QHash<EventId, unsigned> MessageWin::mErrorMessages;
bool MessageWin::mRedisplayed = false;
// There can only be one audio thread at a time: trying to play multiple
// sound files simultaneously would result in a cacophony, and besides
// that, Phonon currently crashes...
......
/*
* messagewin.h - displays an alarm message
* Program: kalarm
* Copyright © 2001-2019 David Jarvie <djarvie@kde.org>
* Copyright © 2001-2020 David Jarvie <djarvie@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -31,7 +31,7 @@
#include <KAlarmCal/KAEvent>
#include <QList>
#include <QMap>
#include <QHash>
#include <QPointer>
#include <QDateTime>
......@@ -138,7 +138,7 @@ class MessageWin : public MainWindowBase
static bool isSpread(const QPoint& topLeft);
static QList<MessageWin*> mWindowList; // list of existing message windows
static QMap<EventId, unsigned> mErrorMessages; // error messages currently displayed, by event ID
static QHash<EventId, unsigned> mErrorMessages; // error messages currently displayed, by event ID
static bool mRedisplayed; // redisplayAlarms() was called
// Sound file playing
static QPointer<AudioThread> mAudioThread; // thread to play audio file
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment