File indexing completed on 2024-11-24 04:39:30
0001 /* 0002 This file is part of Contact Editor. 0003 0004 SPDX-FileCopyrightText: 2016 eyeOS S.L.U., a Telefonica company, sales@eyeos.com 0005 SPDX-FileCopyrightText: 2016-2020 Laurent Montel <montel.org> 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "customfieldslistwidget.h" 0011 #include "../utils/utils.h" 0012 0013 #include "customfieldmanager_p.h" 0014 #include "customfieldslistdelegate.h" 0015 #include <KContacts/Addressee> 0016 #include <QSortFilterProxyModel> 0017 #include <QTreeView> 0018 #include <QVBoxLayout> 0019 0020 using namespace Akonadi; 0021 0022 CustomFieldsListWidget::CustomFieldsListWidget(QWidget *parent) 0023 : QWidget(parent) 0024 , mCustomFieldList(new QTreeView(this)) 0025 , mModel(new CustomFieldsModel(this)) 0026 { 0027 auto topLayout = new QVBoxLayout(this); 0028 mCustomFieldList->setObjectName(QLatin1StringView("customfieldlist")); 0029 mCustomFieldList->setSortingEnabled(true); 0030 mCustomFieldList->setRootIsDecorated(false); 0031 auto customFieldDelegate = new Akonadi::CustomFieldsListDelegate(mCustomFieldList, this); 0032 mCustomFieldList->setItemDelegate(customFieldDelegate); 0033 topLayout->addWidget(mCustomFieldList); 0034 0035 auto proxyModel = new QSortFilterProxyModel(this); 0036 proxyModel->setDynamicSortFilter(true); 0037 proxyModel->setSourceModel(mModel); 0038 mCustomFieldList->setModel(proxyModel); 0039 mCustomFieldList->setColumnHidden(2, true); // hide the 'key' column 0040 } 0041 0042 CustomFieldsListWidget::~CustomFieldsListWidget() = default; 0043 0044 void CustomFieldsListWidget::loadContact(const KContacts::Addressee &contact) 0045 { 0046 CustomField::List externalCustomFields; 0047 0048 CustomField::List globalCustomFields = CustomFieldManager::globalCustomFieldDescriptions(); 0049 0050 const QStringList customs = contact.customs(); 0051 for (const QString &custom : customs) { 0052 QString app; 0053 QString name; 0054 QString value; 0055 Akonadi::Utils::splitCustomField(custom, app, name, value); 0056 0057 // skip all well-known fields that have separated editor widgets 0058 if (custom.startsWith(QLatin1StringView("messaging/"))) { // IM addresses 0059 continue; 0060 } 0061 0062 if (app == QLatin1StringView("KADDRESSBOOK")) { 0063 static QSet<QString> blacklist; 0064 if (blacklist.isEmpty()) { 0065 blacklist << QStringLiteral("BlogFeed") << QStringLiteral("X-IMAddress") << QStringLiteral("X-Profession") << QStringLiteral("X-Office") 0066 << QStringLiteral("X-ManagersName") << QStringLiteral("X-AssistantsName") << QStringLiteral("X-Anniversary") 0067 << QStringLiteral("X-SpousesName") << QStringLiteral("MailPreferedFormatting") << QStringLiteral("MailAllowToRemoteContent") 0068 << QStringLiteral("CRYPTOPROTOPREF") << QStringLiteral("OPENPGPFP") << QStringLiteral("SMIMEFP") << QStringLiteral("CRYPTOSIGNPREF") 0069 << QStringLiteral("CRYPTOENCRYPTPREF"); 0070 } 0071 QSet<QString> upperCaseBlacklist; 0072 for (const QString &blacklistEntry : std::as_const(blacklist)) { 0073 upperCaseBlacklist << blacklistEntry.toUpper(); 0074 } 0075 blacklist.unite(upperCaseBlacklist); 0076 if (blacklist.contains(name)) { // several KAddressBook specific fields 0077 continue; 0078 } 0079 } 0080 0081 // check whether it correspond to a local custom field 0082 bool isLocalCustomField = false; 0083 const int localCustomFieldsCount(mLocalCustomFields.count()); 0084 for (int i = 0; i < localCustomFieldsCount; ++i) { 0085 if (mLocalCustomFields[i].key() == name) { 0086 mLocalCustomFields[i].setValue(value); 0087 isLocalCustomField = true; 0088 break; 0089 } 0090 } 0091 0092 // check whether it correspond to a global custom field 0093 bool isGlobalCustomField = false; 0094 const int globalCustomFieldsCount(globalCustomFields.count()); 0095 for (int i = 0; i < globalCustomFieldsCount; ++i) { 0096 if (globalCustomFields[i].key() == name) { 0097 globalCustomFields[i].setValue(value); 0098 isGlobalCustomField = true; 0099 break; 0100 } 0101 } 0102 0103 // if not local and not global it must be external 0104 if (!isLocalCustomField && !isGlobalCustomField) { 0105 if (app == QLatin1StringView("KADDRESSBOOK")) { 0106 // however if it starts with our prefix it might be that this is an outdated 0107 // global custom field, in this case treat it as local field of type text 0108 CustomField customField(name, name, CustomField::TextType, CustomField::LocalScope); 0109 customField.setValue(value); 0110 0111 mLocalCustomFields << customField; 0112 } else { 0113 // it is really an external custom field 0114 const QString key = app + QLatin1Char('-') + name; 0115 CustomField customField(key, key, CustomField::TextType, CustomField::ExternalScope); 0116 customField.setValue(value); 0117 0118 externalCustomFields << customField; 0119 } 0120 } 0121 } 0122 0123 mModel->setCustomFields(CustomField::List() << mLocalCustomFields << globalCustomFields << externalCustomFields); 0124 } 0125 0126 void CustomFieldsListWidget::storeContact(KContacts::Addressee &contact) const 0127 { 0128 const CustomField::List customFields = mModel->customFields(); 0129 for (const CustomField &customField : customFields) { 0130 // write back values for local and global scope, leave external untouched 0131 if (customField.scope() != CustomField::ExternalScope) { 0132 if (!customField.value().isEmpty()) { 0133 contact.insertCustom(QStringLiteral("KADDRESSBOOK"), customField.key(), customField.value()); 0134 } else { 0135 contact.removeCustom(QStringLiteral("KADDRESSBOOK"), customField.key()); 0136 } 0137 } 0138 } 0139 0140 // Now remove all fields that were available in loadContact (these are stored in mLocalCustomFields) 0141 // but are not part of customFields now, which means they have been removed or renamed by the user 0142 // in the editor dialog. 0143 for (const CustomField &oldCustomField : std::as_const(mLocalCustomFields)) { 0144 if (oldCustomField.scope() != CustomField::ExternalScope) { 0145 bool fieldStillExists = false; 0146 for (const CustomField &newCustomField : std::as_const(customFields)) { 0147 if (newCustomField.scope() != CustomField::ExternalScope) { 0148 if (newCustomField.key() == oldCustomField.key()) { 0149 fieldStillExists = true; 0150 break; 0151 } 0152 } 0153 } 0154 0155 if (!fieldStillExists) { 0156 contact.removeCustom(QStringLiteral("KADDRESSBOOK"), oldCustomField.key()); 0157 } 0158 } 0159 } 0160 0161 // And store the global custom fields descriptions as well 0162 CustomField::List globalCustomFields; 0163 for (const CustomField &customField : std::as_const(customFields)) { 0164 if (customField.scope() == CustomField::GlobalScope) { 0165 globalCustomFields << customField; 0166 } 0167 } 0168 0169 CustomFieldManager::setGlobalCustomFieldDescriptions(globalCustomFields); 0170 } 0171 0172 void CustomFieldsListWidget::setReadOnly(bool readOnly) 0173 { 0174 mCustomFieldList->setEnabled(!readOnly); 0175 } 0176 0177 void CustomFieldsListWidget::slotAddNewField(const CustomField &field) 0178 { 0179 const int lastRow = mModel->rowCount(); 0180 mModel->insertRow(lastRow); 0181 mModel->setData(mModel->index(lastRow, 2), field.key(), Qt::EditRole); 0182 mModel->setData(mModel->index(lastRow, 0), field.title(), Qt::EditRole); 0183 mModel->setData(mModel->index(lastRow, 0), field.type(), CustomFieldsModel::TypeRole); 0184 mModel->setData(mModel->index(lastRow, 0), field.scope(), CustomFieldsModel::ScopeRole); 0185 } 0186 0187 void CustomFieldsListWidget::setLocalCustomFieldDescriptions(const QVariantList &descriptions) 0188 { 0189 mLocalCustomFields.clear(); 0190 mLocalCustomFields.reserve(descriptions.count()); 0191 for (const QVariant &description : descriptions) { 0192 mLocalCustomFields.append(CustomField::fromVariantMap(description.toMap(), CustomField::LocalScope)); 0193 } 0194 } 0195 0196 QVariantList CustomFieldsListWidget::localCustomFieldDescriptions() const 0197 { 0198 const CustomField::List customFields = mModel->customFields(); 0199 0200 QVariantList descriptions; 0201 for (const CustomField &field : customFields) { 0202 if (field.scope() == CustomField::LocalScope) { 0203 descriptions.append(field.toVariantMap()); 0204 } 0205 } 0206 0207 return descriptions; 0208 } 0209 0210 #include "moc_customfieldslistwidget.cpp"