diff --git a/src/presentation/artifacteditormodel.h b/src/presentation/artifacteditormodel.h --- a/src/presentation/artifacteditormodel.h +++ b/src/presentation/artifacteditormodel.h @@ -95,6 +95,9 @@ void setDueDate(const QDateTime &due); void setRecurrence(Domain::Task::Recurrence recurrence); void delegate(const QString &name, const QString &email); + + void addAttachment(const QString &fileName); + void removeAttachment(const QModelIndex &index); void openAttachment(const QModelIndex &index); void setEditingInProgress(bool editingInProgress); diff --git a/src/presentation/artifacteditormodel.cpp b/src/presentation/artifacteditormodel.cpp --- a/src/presentation/artifacteditormodel.cpp +++ b/src/presentation/artifacteditormodel.cpp @@ -25,9 +25,11 @@ #include "artifacteditormodel.h" #include +#include #include #include #include +#include #include #include @@ -312,6 +314,52 @@ m_delegateFunction(task, delegate); } +void ArtifactEditorModel::addAttachment(const QString &fileName) +{ + auto task = m_artifact.objectCast(); + if (!task) + return; + + QMimeDatabase mimeDb; + auto mimeType = mimeDb.mimeTypeForFile(fileName); + + auto attachment = Domain::Task::Attachment(); + attachment.setLabel(QFileInfo(fileName).fileName()); + attachment.setMimeType(mimeType.name()); + attachment.setIconName(mimeType.iconName()); + + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) { + // TODO: Might be worth extending error handling + // to deal with job-less errors later on + qWarning() << "Couldn't open" << fileName; + return; + } + + attachment.setData(file.readAll()); + + file.close(); + + auto attachments = task->attachments(); + attachments.append(attachment); + task->setAttachments(attachments); + + setSaveNeeded(true); +} + +void ArtifactEditorModel::removeAttachment(const QModelIndex &index) +{ + auto task = m_artifact.objectCast(); + if (!task) + return; + + auto attachments = task->attachments(); + attachments.removeAt(index.row()); + task->setAttachments(attachments); + + setSaveNeeded(true); +} + void ArtifactEditorModel::openAttachment(const QModelIndex &index) { auto task = m_artifact.objectCast(); diff --git a/tests/units/presentation/artifacteditormodeltest.cpp b/tests/units/presentation/artifacteditormodeltest.cpp --- a/tests/units/presentation/artifacteditormodeltest.cpp +++ b/tests/units/presentation/artifacteditormodeltest.cpp @@ -26,6 +26,7 @@ #include "utils/mockobject.h" #include +#include #include "testlib/fakejob.h" @@ -555,6 +556,85 @@ QCOMPARE(spy.size(), 1); // emitted by setArtifact QVERIFY(model.property(propertyName) != artifact->property(propertyName)); } + + void shouldAddAttachments() + { + // GIVEN + QTemporaryFile temporaryFile(QDir::tempPath() + "/artifacteditormodeltest_XXXXXX.txt"); + temporaryFile.open(); + temporaryFile.write("foo bar"); + temporaryFile.close(); + auto fileName = temporaryFile.fileName().mid(QDir::tempPath().size() + 1); + + auto task = Domain::Task::Ptr::create(); + + auto savedArtifact = Domain::Artifact::Ptr(); + auto save = [this, &savedArtifact] (const Domain::Artifact::Ptr &artifact) { + savedArtifact = artifact; + return new FakeJob(this); + }; + + Presentation::ArtifactEditorModel model; + model.setSaveFunction(save); + model.setArtifact(task); + + QSignalSpy spy(model.attachmentModel(), &QAbstractItemModel::modelReset); + + // WHEN + model.addAttachment(temporaryFile.fileName()); + + // THEN + QCOMPARE(spy.size(), 1); + QCOMPARE(model.attachmentModel()->rowCount(), 1); + QVERIFY(!savedArtifact); + + // WHEN (nothing else happens after a delay) + QTest::qWait(model.autoSaveDelay() + 50); + + // THEN + QCOMPARE(savedArtifact.objectCast(), task); + QCOMPARE(task->attachments().size(), 1); + QCOMPARE(task->attachments().first().label(), fileName); + QCOMPARE(task->attachments().first().mimeType(), QStringLiteral("text/plain")); + QCOMPARE(task->attachments().first().iconName(), QStringLiteral("text-plain")); + QCOMPARE(task->attachments().first().data(), QByteArrayLiteral("foo bar")); + } + + void shouldRemoveAttachments() + { + // GIVEN + auto task = Domain::Task::Ptr::create(); + task->setAttachments(Domain::Task::Attachments() << Domain::Task::Attachment("foo") + << Domain::Task::Attachment("bar")); + + auto savedArtifact = Domain::Artifact::Ptr(); + auto save = [this, &savedArtifact] (const Domain::Artifact::Ptr &artifact) { + savedArtifact = artifact; + return new FakeJob(this); + }; + + Presentation::ArtifactEditorModel model; + model.setSaveFunction(save); + model.setArtifact(task); + + QSignalSpy spy(model.attachmentModel(), &QAbstractItemModel::modelReset); + + // WHEN + model.removeAttachment(model.attachmentModel()->index(0, 0)); + + // THEN + QCOMPARE(spy.size(), 1); + QCOMPARE(model.attachmentModel()->rowCount(), 1); + QVERIFY(!savedArtifact); + + // WHEN (nothing else happens after a delay) + QTest::qWait(model.autoSaveDelay() + 50); + + // THEN + QCOMPARE(savedArtifact.objectCast(), task); + QCOMPARE(task->attachments().size(), 1); + QCOMPARE(task->attachments().first().data(), QByteArrayLiteral("bar")); + } }; ZANSHIN_TEST_MAIN(ArtifactEditorModelTest)