csvimportexportplugininterface.cpp 7.36 KB
Newer Older
Laurent Montel's avatar
Laurent Montel committed
1
/*
2
   SPDX-FileCopyrightText: 2016-2020 Laurent Montel <montel@kde.org>
Laurent Montel's avatar
Laurent Montel committed
3

4
   SPDX-License-Identifier: GPL-2.0-or-later
Laurent Montel's avatar
Laurent Montel committed
5 6 7
*/

#include "csvimportexportplugininterface.h"
Laurent Montel's avatar
Laurent Montel committed
8
#include "import/csvimportdialog.h"
Laurent Montel's avatar
Laurent Montel committed
9 10 11
#include <KLocalizedString>
#include <KActionCollection>
#include <QAction>
Laurent Montel's avatar
Laurent Montel committed
12 13 14 15 16 17 18 19
#include <QFileInfo>
#include <QTemporaryFile>
#include <PimCommon/RenameFileDialog>
#include <KMessageBox>
#include <KJobWidgets>
#include <QTextStream>
#include <QFileDialog>
#include <QTextCodec>
Laurent Montel's avatar
Laurent Montel committed
20
#include <QPointer>
Laurent Montel's avatar
Laurent Montel committed
21
#include <KIO/Job>
Laurent Montel's avatar
Laurent Montel committed
22

23 24 25
#include <KAddressBookImportExport/ContactSelectionDialog>
#include <KAddressBookImportExport/ImportExportEngine>

Laurent Montel's avatar
Laurent Montel committed
26
CSVImportExportPluginInterface::CSVImportExportPluginInterface(QObject *parent)
27
    : KAddressBookImportExport::PluginInterface(parent)
Laurent Montel's avatar
Laurent Montel committed
28 29 30
{
}

31
CSVImportExportPluginInterface::~CSVImportExportPluginInterface() = default;
Laurent Montel's avatar
Laurent Montel committed
32 33 34

void CSVImportExportPluginInterface::createAction(KActionCollection *ac)
{
Laurent Montel's avatar
Laurent Montel committed
35 36 37 38
    QAction *action = ac->addAction(QStringLiteral("file_export_csv"));
    action->setText(i18n("Export CSV file..."));
    action->setWhatsThis(i18n("Export contacts to a file in comma separated value format."));
    setExportActions(QList<QAction *>() << action);
Laurent Montel's avatar
Laurent Montel committed
39
    connect(action, &QAction::triggered, this, &CSVImportExportPluginInterface::slotExportCVS);
Laurent Montel's avatar
Laurent Montel committed
40 41

    action = ac->addAction(QStringLiteral("file_import_csv"));
Laurent Montel's avatar
Laurent Montel committed
42 43 44
    action->setText(i18n("Import CSV file..."));
    action->setWhatsThis(i18n("Import contacts from a file in comma separated value format."));
    setImportActions(QList<QAction *>() << action);
Laurent Montel's avatar
Laurent Montel committed
45
    connect(action, &QAction::triggered, this, &CSVImportExportPluginInterface::slotImportCVS);
Laurent Montel's avatar
Laurent Montel committed
46 47 48 49
}

void CSVImportExportPluginInterface::exec()
{
Laurent Montel's avatar
Laurent Montel committed
50
    switch (mImportExportAction) {
Laurent Montel's avatar
Laurent Montel committed
51
    case Import:
Laurent Montel's avatar
Laurent Montel committed
52
        importCSV();
Laurent Montel's avatar
Laurent Montel committed
53 54
        break;
    case Export:
Laurent Montel's avatar
Laurent Montel committed
55
        exportCSV();
Laurent Montel's avatar
Laurent Montel committed
56 57 58 59
        break;
    }
}

Laurent Montel's avatar
Laurent Montel committed
60 61
void CSVImportExportPluginInterface::importCSV()
{
62
    KAddressBookImportExport::ContactList contactList;
Laurent Montel's avatar
Laurent Montel committed
63
    QPointer<CSVImportDialog> dlg = new CSVImportDialog(parentWidget());
Laurent Montel's avatar
Laurent Montel committed
64
    if (dlg->exec()) {
Laurent Montel's avatar
Laurent Montel committed
65 66 67 68
        contactList.setAddressList(dlg->contacts());
    }

    delete dlg;
69
    auto *engine = new KAddressBookImportExport::ImportExportEngine(this);
Laurent Montel's avatar
Laurent Montel committed
70 71 72
    engine->setContactList(contactList);
    engine->setDefaultAddressBook(defaultCollection());
    engine->importContacts();
Laurent Montel's avatar
Laurent Montel committed
73 74
}

Laurent Montel's avatar
Laurent Montel committed
75 76 77 78 79 80 81 82 83 84
void CSVImportExportPluginInterface::slotImportCVS()
{
    mImportExportAction = Import;
    Q_EMIT emitPluginActivated(this);
}

void CSVImportExportPluginInterface::slotExportCVS()
{
    mImportExportAction = Export;
    Q_EMIT emitPluginActivated(this);
Laurent Montel's avatar
Laurent Montel committed
85
}
Laurent Montel's avatar
Laurent Montel committed
86 87 88 89 90 91

void CSVImportExportPluginInterface::exportToFile(QFile *file, const KContacts::Addressee::List &contacts) const
{
    QTextStream stream(file);
    stream.setCodec(QTextCodec::codecForLocale());

92 93
    auto fields = KAddressBookImportExport::ContactFields::allFields();
    fields.remove(KAddressBookImportExport::ContactFields::Undefined);
Laurent Montel's avatar
Laurent Montel committed
94 95 96 97

    bool first = true;

    // First output the column headings
Laurent Montel's avatar
Laurent Montel committed
98 99
    const int fieldsCount(fields.count());
    for (int i = 0; i < fieldsCount; ++i) {
Laurent Montel's avatar
Laurent Montel committed
100 101 102 103 104
        if (!first) {
            stream << ",";
        }

        // add quoting as defined in RFC 4180
105
        QString label = KAddressBookImportExport::ContactFields::label(fields.at(i));
Laurent Montel's avatar
Laurent Montel committed
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
        label.replace(QLatin1Char('"'), QStringLiteral("\"\""));

        stream << "\"" << label << "\"";
        first = false;
    }
    stream << "\n";

    // Then all the contacts
    for (int i = 0; i < contacts.count(); ++i) {
        const KContacts::Addressee contact = contacts.at(i);
        first = true;

        for (int j = 0; j < fields.count(); ++j) {
            if (!first) {
                stream << ",";
            }

            QString content;
124 125
            if (fields.at(j) == KAddressBookImportExport::ContactFields::Birthday
                || fields.at(j) == KAddressBookImportExport::ContactFields::Anniversary) {
Laurent Montel's avatar
Laurent Montel committed
126
                const QDateTime dateTime
127
                    = QDateTime::fromString(KAddressBookImportExport::ContactFields::value(fields.at(j), contact), Qt::ISODate);
Laurent Montel's avatar
Laurent Montel committed
128 129 130 131
                if (dateTime.isValid()) {
                    content = dateTime.date().toString(Qt::ISODate);
                }
            } else {
132
                content = KAddressBookImportExport::ContactFields::value(fields.at(j), contact).replace(QLatin1Char('\n'), QStringLiteral("\\n"));
Laurent Montel's avatar
Laurent Montel committed
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
            }

            // add quoting as defined in RFC 4180
            content.replace(QLatin1Char('"'), QStringLiteral("\"\""));

            stream << '\"' << content << '\"';
            first = false;
        }

        stream << "\n";
    }
}

void CSVImportExportPluginInterface::exportCSV()
{
148 149
    QPointer<KAddressBookImportExport::ContactSelectionDialog> dlg
        = new KAddressBookImportExport::ContactSelectionDialog(itemSelectionModel(), false, parentWidget());
Laurent Montel's avatar
Laurent Montel committed
150 151
    dlg->setMessageText(i18n("Which contact do you want to export?"));
    dlg->setDefaultAddressBook(defaultCollection());
Laurent Montel's avatar
Laurent Montel committed
152
    if (!dlg->exec()) {
Laurent Montel's avatar
Laurent Montel committed
153 154 155 156 157 158 159
        delete dlg;
        return;
    }
    const KContacts::AddresseeList contacts = dlg->selectedContacts().addressList();
    delete dlg;

    if (contacts.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
160
        KMessageBox::sorry(nullptr, i18n("You have not selected any contacts to export."));
Laurent Montel's avatar
Laurent Montel committed
161 162 163
        return;
    }

164
    KAddressBookImportExport::ContactList contactLists;
Laurent Montel's avatar
Laurent Montel committed
165
    contactLists.setAddressList(contacts);
166
    QFileDialog::Options options = QFileDialog::DontConfirmOverwrite;
Laurent Montel's avatar
Laurent Montel committed
167
    QUrl url = QFileDialog::getSaveFileUrl(parentWidget(), QString(), QUrl::fromLocalFile(QStringLiteral("addressbook.csv")), QString(), nullptr, options);
Laurent Montel's avatar
Laurent Montel committed
168 169 170 171
    if (url.isEmpty()) {
        return;
    }

Laurent Montel's avatar
Laurent Montel committed
172 173
    if (QFileInfo::exists(url.isLocalFile() ? url.toLocalFile() : url.path())) {
        if (url.isLocalFile() && QFileInfo::exists(url.toLocalFile())) {
Laurent Montel's avatar
Laurent Montel committed
174
            PimCommon::RenameFileDialog *dialog = new PimCommon::RenameFileDialog(url, false, parentWidget());
Laurent Montel's avatar
Laurent Montel committed
175
            PimCommon::RenameFileDialog::RenameFileDialogResult result = static_cast<PimCommon::RenameFileDialog::RenameFileDialogResult>(dialog->exec());
Laurent Montel's avatar
Laurent Montel committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
            if (result == PimCommon::RenameFileDialog::RENAMEFILE_RENAME) {
                url = dialog->newName();
            } else if (result == PimCommon::RenameFileDialog::RENAMEFILE_IGNORE) {
                delete dialog;
                return;
            }
            delete dialog;
        }
    }

    if (!url.isLocalFile()) {
        QTemporaryFile tmpFile;
        if (!tmpFile.open()) {
            const QString msg = i18n("<qt>Unable to open file <b>%1</b></qt>", url.url());
            KMessageBox::error(parentWidget(), msg);
            return;
        }
Laurent Montel's avatar
Laurent Montel committed
193
        exportToFile(&tmpFile, contactLists.addressList());
Laurent Montel's avatar
Laurent Montel committed
194 195 196 197 198 199 200 201 202 203 204 205
        tmpFile.flush();
        auto job = KIO::file_copy(QUrl::fromLocalFile(tmpFile.fileName()), url, -1, KIO::Overwrite);
        KJobWidgets::setWindow(job, parentWidget());
        job->exec();
    } else {
        QFile file(url.toLocalFile());
        if (!file.open(QIODevice::WriteOnly)) {
            const QString msg = i18n("<qt>Unable to open file <b>%1</b>.</qt>", url.toLocalFile());
            KMessageBox::error(parentWidget(), msg);
            return;
        }

Laurent Montel's avatar
Laurent Montel committed
206
        exportToFile(&file, contactLists.addressList());
Laurent Montel's avatar
Laurent Montel committed
207 208 209
        file.close();
    }
}
Laurent Montel's avatar
Laurent Montel committed
210 211 212

bool CSVImportExportPluginInterface::canImportFileType(const QUrl &url)
{
Laurent Montel's avatar
Laurent Montel committed
213
    return url.path().endsWith(QLatin1String(".csv"));
Laurent Montel's avatar
Laurent Montel committed
214
}
Laurent Montel's avatar
Laurent Montel committed
215 216 217 218

void CSVImportExportPluginInterface::importFile(const QUrl &url)
{
    //TODO
Allen Winter's avatar
Allen Winter committed
219
    Q_UNUSED(url);
Laurent Montel's avatar
Laurent Montel committed
220
}