File indexing completed on 2025-01-19 04:46:31

0001 /*
0002    SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "csvimportexportplugininterface.h"
0008 #include "import/csvimportdialog.h"
0009 #include <KActionCollection>
0010 #include <KIO/Job>
0011 #include <KJobWidgets>
0012 #include <KLocalizedString>
0013 #include <KMessageBox>
0014 #include <PimCommon/RenameFileDialog>
0015 #include <QAction>
0016 #include <QFileDialog>
0017 #include <QFileInfo>
0018 #include <QPointer>
0019 #include <QTemporaryFile>
0020 #include <QTextCodec>
0021 #include <QTextStream>
0022 
0023 #include <KAddressBookImportExport/ContactSelectionDialog>
0024 #include <KAddressBookImportExport/ImportExportEngine>
0025 
0026 CSVImportExportPluginInterface::CSVImportExportPluginInterface(QObject *parent)
0027     : KAddressBookImportExport::PluginInterface(parent)
0028 {
0029 }
0030 
0031 CSVImportExportPluginInterface::~CSVImportExportPluginInterface() = default;
0032 
0033 void CSVImportExportPluginInterface::createAction(KActionCollection *ac)
0034 {
0035     QAction *action = ac->addAction(QStringLiteral("file_export_csv"));
0036     action->setText(i18n("Export CSV file..."));
0037     action->setWhatsThis(i18n("Export contacts to a file in comma separated value format."));
0038     setExportActions(QList<QAction *>() << action);
0039     connect(action, &QAction::triggered, this, &CSVImportExportPluginInterface::slotExportCVS);
0040 
0041     action = ac->addAction(QStringLiteral("file_import_csv"));
0042     action->setText(i18n("Import CSV file..."));
0043     action->setWhatsThis(i18n("Import contacts from a file in comma separated value format."));
0044     setImportActions(QList<QAction *>() << action);
0045     connect(action, &QAction::triggered, this, &CSVImportExportPluginInterface::slotImportCVS);
0046 }
0047 
0048 void CSVImportExportPluginInterface::exec()
0049 {
0050     switch (mImportExportAction) {
0051     case Import:
0052         importCSV();
0053         break;
0054     case Export:
0055         exportCSV();
0056         break;
0057     }
0058 }
0059 
0060 void CSVImportExportPluginInterface::importCSV()
0061 {
0062     KAddressBookImportExport::ContactList contactList;
0063     QPointer<CSVImportDialog> dlg = new CSVImportDialog(parentWidget());
0064     if (dlg->exec()) {
0065         contactList.setAddressList(dlg->contacts());
0066     }
0067 
0068     delete dlg;
0069     auto engine = new KAddressBookImportExport::ImportExportEngine(this);
0070     engine->setContactList(contactList);
0071     engine->setDefaultAddressBook(defaultCollection());
0072     engine->importContacts();
0073 }
0074 
0075 void CSVImportExportPluginInterface::slotImportCVS()
0076 {
0077     mImportExportAction = Import;
0078     Q_EMIT emitPluginActivated(this);
0079 }
0080 
0081 void CSVImportExportPluginInterface::slotExportCVS()
0082 {
0083     mImportExportAction = Export;
0084     Q_EMIT emitPluginActivated(this);
0085 }
0086 
0087 void CSVImportExportPluginInterface::exportToFile(QFile *file, const KContacts::Addressee::List &contacts) const
0088 {
0089     QTextStream stream(file);
0090     stream.setEncoding(QStringConverter::System);
0091 
0092     auto fields = KAddressBookImportExport::ContactFields::allFields();
0093     fields.remove(KAddressBookImportExport::ContactFields::Undefined);
0094 
0095     bool first = true;
0096 
0097     // First output the column headings
0098     const int fieldsCount(fields.count());
0099     for (int i = 0; i < fieldsCount; ++i) {
0100         if (!first) {
0101             stream << ",";
0102         }
0103 
0104         // add quoting as defined in RFC 4180
0105         QString label = KAddressBookImportExport::ContactFields::label(fields.at(i));
0106         label.replace(QLatin1Char('"'), QStringLiteral("\"\""));
0107 
0108         stream << "\"" << label << "\"";
0109         first = false;
0110     }
0111     stream << "\n";
0112 
0113     // Then all the contacts
0114     for (int i = 0; i < contacts.count(); ++i) {
0115         const KContacts::Addressee contact = contacts.at(i);
0116         first = true;
0117 
0118         for (int j = 0; j < fields.count(); ++j) {
0119             if (!first) {
0120                 stream << ",";
0121             }
0122 
0123             QString content;
0124             if (fields.at(j) == KAddressBookImportExport::ContactFields::Birthday || fields.at(j) == KAddressBookImportExport::ContactFields::Anniversary) {
0125                 const QDateTime dateTime = QDateTime::fromString(KAddressBookImportExport::ContactFields::value(fields.at(j), contact), Qt::ISODate);
0126                 if (dateTime.isValid()) {
0127                     content = dateTime.date().toString(Qt::ISODate);
0128                 }
0129             } else {
0130                 content = KAddressBookImportExport::ContactFields::value(fields.at(j), contact).replace(QLatin1Char('\n'), QStringLiteral("\\n"));
0131             }
0132 
0133             // add quoting as defined in RFC 4180
0134             content.replace(QLatin1Char('"'), QStringLiteral("\"\""));
0135 
0136             stream << '\"' << content << '\"';
0137             first = false;
0138         }
0139 
0140         stream << "\n";
0141     }
0142 }
0143 
0144 void CSVImportExportPluginInterface::exportCSV()
0145 {
0146     QPointer<KAddressBookImportExport::ContactSelectionDialog> dlg =
0147         new KAddressBookImportExport::ContactSelectionDialog(itemSelectionModel(), false, parentWidget());
0148     dlg->setMessageText(i18n("Which contact do you want to export?"));
0149     dlg->setDefaultAddressBook(defaultCollection());
0150     if (!dlg->exec()) {
0151         delete dlg;
0152         return;
0153     }
0154     const KContacts::AddresseeList contacts = dlg->selectedContacts().addressList();
0155     delete dlg;
0156 
0157     if (contacts.isEmpty()) {
0158         KMessageBox::error(nullptr, i18n("You have not selected any contacts to export."));
0159         return;
0160     }
0161 
0162     KAddressBookImportExport::ContactList contactLists;
0163     contactLists.setAddressList(contacts);
0164     QFileDialog::Options options = QFileDialog::DontConfirmOverwrite;
0165     QUrl url = QFileDialog::getSaveFileUrl(parentWidget(), QString(), QUrl::fromLocalFile(QStringLiteral("addressbook.csv")), QString(), nullptr, options);
0166     if (url.isEmpty()) {
0167         return;
0168     }
0169 
0170     if (QFileInfo::exists(url.isLocalFile() ? url.toLocalFile() : url.path())) {
0171         if (url.isLocalFile() && QFileInfo::exists(url.toLocalFile())) {
0172             auto dialog = new PimCommon::RenameFileDialog(url, false, parentWidget());
0173             auto result = static_cast<PimCommon::RenameFileDialog::RenameFileDialogResult>(dialog->exec());
0174             if (result == PimCommon::RenameFileDialog::RENAMEFILE_RENAME) {
0175                 url = dialog->newName();
0176             } else if (result == PimCommon::RenameFileDialog::RENAMEFILE_IGNORE) {
0177                 delete dialog;
0178                 return;
0179             }
0180             delete dialog;
0181         }
0182     }
0183 
0184     if (!url.isLocalFile()) {
0185         QTemporaryFile tmpFile;
0186         if (!tmpFile.open()) {
0187             const QString msg = i18n("<qt>Unable to open file <b>%1</b></qt>", url.url());
0188             KMessageBox::error(parentWidget(), msg);
0189             return;
0190         }
0191         exportToFile(&tmpFile, contactLists.addressList());
0192         tmpFile.flush();
0193         auto job = KIO::file_copy(QUrl::fromLocalFile(tmpFile.fileName()), url, -1, KIO::Overwrite);
0194         KJobWidgets::setWindow(job, parentWidget());
0195         job->exec();
0196     } else {
0197         QFile file(url.toLocalFile());
0198         if (!file.open(QIODevice::WriteOnly)) {
0199             const QString msg = i18n("<qt>Unable to open file <b>%1</b>.</qt>", url.toLocalFile());
0200             KMessageBox::error(parentWidget(), msg);
0201             return;
0202         }
0203 
0204         exportToFile(&file, contactLists.addressList());
0205         file.close();
0206     }
0207 }
0208 
0209 bool CSVImportExportPluginInterface::canImportFileType(const QUrl &url)
0210 {
0211     return url.path().endsWith(QLatin1StringView(".csv"));
0212 }
0213 
0214 void CSVImportExportPluginInterface::importFile(const QUrl &url)
0215 {
0216     // TODO
0217     Q_UNUSED(url)
0218 }
0219 
0220 #include "moc_csvimportexportplugininterface.cpp"