File indexing completed on 2024-05-12 05:12:41
0001 /* 0002 Copyright (C) 2013 Jonathan Marten <jjm@keelhaul.me.uk> 0003 0004 This program is free software; you can redistribute it and/or modify 0005 it under the terms of the GNU General Public License as published by 0006 the Free Software Foundation; either version 2 of the License, or 0007 (at your option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 GNU General Public License for more details. 0013 0014 You should have received a copy of the GNU General Public License along 0015 with this program; if not, write to the Free Software Foundation, Inc., 0016 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 0017 */ 0018 0019 #include "groupcommand.h" 0020 0021 #include "collectionresolvejob.h" 0022 0023 #include <Akonadi/ItemFetchJob> 0024 #include <Akonadi/ItemFetchScope> 0025 #include <Akonadi/ItemModifyJob> 0026 #include <Akonadi/ContactSearchJob> 0027 0028 #include <KContacts/Addressee> 0029 0030 #include <KCodecs/KEmailAddress> 0031 0032 #include <iostream> 0033 0034 #include "commandfactory.h" 0035 #include "errorreporter.h" 0036 0037 using namespace Akonadi; 0038 0039 DEFINE_COMMAND("group", GroupCommand, I18N_NOOP("Expand or modify a contact group")); 0040 0041 GroupCommand::GroupCommand(QObject *parent) 0042 : AbstractCommand(parent), 0043 mGroupItem(nullptr), 0044 mBriefMode(false), 0045 mOperationMode(ModeExpand) 0046 { 0047 } 0048 0049 GroupCommand::~GroupCommand() 0050 { 0051 delete mGroupItem; 0052 } 0053 0054 void GroupCommand::setupCommandOptions(QCommandLineParser *parser) 0055 { 0056 addOptionsOption(parser); 0057 parser->addOption(QCommandLineOption((QStringList() << "e" << "expand"), i18n("Show the expanded contact group (the default operation)"))); 0058 parser->addOption(QCommandLineOption((QStringList() << "a" << "add"), i18n("Add a contact to the group"))); 0059 parser->addOption(QCommandLineOption((QStringList() << "d" << "delete"), i18n("Delete a contact from the group"))); 0060 parser->addOption(QCommandLineOption((QStringList() << "C" << "clean"), i18n("Remove unknown item references from the group"))); 0061 parser->addOption(QCommandLineOption((QStringList() << "c" << "comment"), i18n("Email comment (name) for an added item"), i18n("name"))); 0062 parser->addOption(QCommandLineOption((QStringList() << "b" << "brief"), i18n("Brief output (for 'expand', email addresses only)"))); 0063 addDryRunOption(parser); 0064 0065 parser->addPositionalArgument("item", i18nc("@info:shell", "The contact group item, an ID or Akonadi URL")); 0066 parser->addPositionalArgument("args", i18nc("@info:shell", "Arguments for the operation"), i18n("args...")); 0067 } 0068 0069 int GroupCommand::initCommand(QCommandLineParser *parser) 0070 { 0071 mItemArgs = parser->positionalArguments(); 0072 if (!checkArgCount(mItemArgs, 1, i18nc("@info:shell", "Missing group argument"))) return InvalidUsage; 0073 0074 if (!getCommonOptions(parser)) return InvalidUsage; 0075 0076 int modeCount = 0; 0077 if (parser->isSet("expand")) { 0078 ++modeCount; 0079 } 0080 if (parser->isSet("add")) { 0081 ++modeCount; 0082 } 0083 if (parser->isSet("delete")) { 0084 ++modeCount; 0085 } 0086 if (parser->isSet("clean")) { 0087 ++modeCount; 0088 } 0089 if (modeCount > 1) { 0090 emitErrorSeeHelp(i18nc("@info:shell", "Only one of the 'expand', 'add', 'delete' or 'clean' options may be specified")); 0091 return (InvalidUsage); 0092 } 0093 0094 if (parser->isSet("expand")) { // see if "Expand" mode 0095 // expand GROUP 0096 mOperationMode = ModeExpand; 0097 } else if (parser->isSet("add")) { // see if "Add" mode 0098 // add GROUP EMAIL|ID... 0099 // add GROUP -c NAME EMAIL|ID 0100 if (mItemArgs.count()<2) { // missing GROUP was checked above 0101 emitErrorSeeHelp(i18nc("@info:shell", "No items specified to add")); 0102 return (InvalidUsage); 0103 } 0104 0105 mNameArg = parser->value("comment"); 0106 if (!mNameArg.isEmpty()) { 0107 // if the "comment" option is specified, 0108 // then only one EMAIL|ID argument may be given. 0109 if (mItemArgs.count()>2) { 0110 emitErrorSeeHelp(i18nc("@info:shell", "Only one item may be specified to add with 'comment'")); 0111 return (InvalidUsage); 0112 } 0113 } 0114 0115 mOperationMode = ModeAdd; 0116 } else if (parser->isSet("delete")) { // see if "Delete" mode 0117 // delete GROUP EMAIL|ID... 0118 if (mItemArgs.count()<2) { // missing GROUP was checked above 0119 emitErrorSeeHelp(i18nc("@info:shell", "No items specified to delete")); 0120 return (InvalidUsage); 0121 } 0122 0123 mOperationMode = ModeDelete; 0124 } else if (parser->isSet("clean")) { // see if "Clean" mode 0125 // clean GROUP 0126 mOperationMode = ModeClean; 0127 } else { // no mode option specified 0128 mOperationMode = ModeExpand; 0129 } 0130 0131 if (mOperationMode != ModeAdd) { 0132 if (parser->isSet("comment")) { 0133 emitErrorSeeHelp(i18nc("@info:shell", "The 'comment' option is only allowed with 'add'")); 0134 return (InvalidUsage); 0135 } 0136 } 0137 0138 mBriefMode = parser->isSet("brief"); // brief/quiet output 0139 0140 mGroupArg = mItemArgs.takeFirst(); // contact group collection 0141 0142 Akonadi::Item item = CollectionResolveJob::parseItem(mGroupArg, true); 0143 if (!item.isValid()) { 0144 return (InvalidUsage); 0145 } 0146 0147 return (NoError); 0148 } 0149 0150 void GroupCommand::start() 0151 { 0152 if (mOperationMode == ModeDelete || mOperationMode == ModeClean) { 0153 if (!isDryRun()) { // allow if not doing anything 0154 if (!allowDangerousOperation()) { 0155 emit finished(RuntimeError); 0156 return; 0157 } 0158 } 0159 } 0160 0161 fetchItems(); 0162 } 0163 0164 void GroupCommand::fetchItems() 0165 { 0166 Item item = CollectionResolveJob::parseItem(mGroupArg); 0167 Q_ASSERT(item.isValid()); 0168 0169 ItemFetchJob *job = new ItemFetchJob(item, this); 0170 job->fetchScope().fetchFullPayload(true); 0171 connect(job, &KJob::result, this, &GroupCommand::onItemsFetched); 0172 } 0173 0174 void GroupCommand::onItemsFetched(KJob *job) 0175 { 0176 if (!checkJobResult(job)) return; 0177 ItemFetchJob *fetchJob = qobject_cast<ItemFetchJob *>(job); 0178 Q_ASSERT(fetchJob != nullptr); 0179 0180 Item::List items = fetchJob->items(); 0181 if (items.count() < 1) { 0182 emit error(i18nc("@info:shell", "Cannot find '%1' as an item", mGroupArg)); 0183 emit finished(RuntimeError); 0184 return; 0185 } 0186 0187 mGroupItem = new Item(items.first()); 0188 if (mGroupItem->mimeType() != KContacts::ContactGroup::mimeType()) { 0189 emit error(i18nc("@info:shell", "Item '%1' is not a contact group", mGroupArg)); 0190 emit finished(RuntimeError); 0191 return; 0192 } 0193 0194 if (!mGroupItem->hasPayload<KContacts::ContactGroup>()) { // should never happen? 0195 emit error(i18nc("@info:shell", "Item '%1' has no contact group payload", mGroupArg)); 0196 emit finished(RuntimeError); 0197 return; 0198 } 0199 0200 KContacts::ContactGroup group = mGroupItem->payload<KContacts::ContactGroup>(); 0201 0202 AbstractCommand::Errors status = RuntimeError; 0203 switch (mOperationMode) { // perform the requested operation 0204 case ModeExpand: 0205 status = showExpandedGroup(group); 0206 break; 0207 0208 case ModeAdd: 0209 status = addGroupItems(group); 0210 break; 0211 0212 case ModeDelete: 0213 status = deleteGroupItems(group); 0214 break; 0215 0216 case ModeClean: 0217 status = cleanGroupItems(group); 0218 break; 0219 } 0220 0221 if (mOperationMode != ModeExpand) { // need to write back? 0222 if (status == NoError) { // only if there were no errors 0223 if (!isDryRun()) { 0224 mGroupItem->setPayload<KContacts::ContactGroup>(group); 0225 Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob(*mGroupItem); 0226 modifyJob->exec(); 0227 if (modifyJob->error() != 0) { 0228 emit error(modifyJob->errorString()); 0229 status = RuntimeError; 0230 } 0231 } 0232 } else { 0233 emit error(i18nc("@info:shell", "Errors occurred, group not updated")); 0234 } 0235 } 0236 0237 emit finished(status); 0238 } 0239 0240 static void writeColumn(const QString &data, int width = 0) 0241 { 0242 std::cout << qPrintable(data.leftJustified(width)) << " "; 0243 } 0244 0245 static void writeColumn(quint64 data, int width = 0) 0246 { 0247 writeColumn(QString::number(data), width); 0248 } 0249 0250 void GroupCommand::displayContactData(const KContacts::ContactGroup::Data &data) 0251 { 0252 if (mBriefMode) { 0253 return; 0254 } 0255 0256 writeColumn(" D", 5); 0257 writeColumn("", 8); 0258 writeColumn(data.email(), 30); 0259 writeColumn(data.name()); 0260 std::cout << std::endl; 0261 } 0262 0263 void GroupCommand::displayContactReference(Akonadi::Item::Id id) 0264 { 0265 if (mBriefMode) { 0266 return; 0267 } 0268 0269 writeColumn(" R", 5); 0270 writeColumn(id, 8); 0271 0272 Akonadi::Item item(id); 0273 Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item); 0274 job->fetchScope().fetchFullPayload(); 0275 0276 if (!job->exec() || job->count() == 0) { 0277 writeColumn(i18nc("@info:shell", "(unknown referenced item)")); 0278 } else { 0279 const Akonadi::Item::List fetchedItems = job->items(); 0280 Q_ASSERT(fetchedItems.count() > 0); 0281 const Akonadi::Item fetchedItem = fetchedItems.first(); 0282 0283 if (!fetchedItem.hasPayload<KContacts::Addressee>()) { 0284 writeColumn(i18nc("@info:shell", "(item has no Addressee payload)")); 0285 } else { 0286 KContacts::Addressee addr = fetchedItem.payload<KContacts::Addressee>(); 0287 writeColumn(addr.preferredEmail(), 30); 0288 writeColumn(addr.formattedName()); 0289 } 0290 } 0291 0292 std::cout << std::endl; 0293 } 0294 0295 void GroupCommand::displayContactReference(const Akonadi::Item &item, const QString &email) 0296 { 0297 if (mBriefMode) { 0298 return; 0299 } 0300 0301 writeColumn(" R", 5); 0302 writeColumn(item.id(), 8); 0303 0304 if (!item.hasPayload<KContacts::Addressee>()) { 0305 writeColumn(i18nc("@info:shell", "(item has no Addressee payload)")); 0306 } else { 0307 KContacts::Addressee addr = item.payload<KContacts::Addressee>(); 0308 writeColumn(!email.isEmpty() ? email : addr.preferredEmail(), 30); 0309 writeColumn(addr.formattedName()); 0310 } 0311 0312 std::cout << std::endl; 0313 } 0314 0315 void GroupCommand::displayReferenceError(Akonadi::Item::Id id) 0316 { 0317 if (mBriefMode) { 0318 return; 0319 } 0320 0321 writeColumn(" E", 5); 0322 writeColumn(id, 8); 0323 if (id == -1) { 0324 std::cout << qPrintable(i18nc("@item:shell", "(invalid referenced item)")); 0325 } else { 0326 std::cout << qPrintable(i18nc("@item:shell", "(unknown referenced item)")); 0327 } 0328 std::cout << std::endl; 0329 } 0330 0331 bool GroupCommand::removeReferenceById(KContacts::ContactGroup &group, const QString &id, bool verbose) 0332 { 0333 bool somethingFound = false; 0334 0335 // Remove any existing reference with the same ID from the group. 0336 for (int i = 0; i < group.contactReferenceCount();) { 0337 KContacts::ContactGroup::ContactReference existingRef = group.contactReference(i); 0338 if (existingRef.uid() == id) { 0339 group.remove(existingRef); 0340 somethingFound = true; 0341 if (verbose) { 0342 displayContactReference(id.toUInt()); 0343 } 0344 } else { 0345 ++i; 0346 } 0347 } 0348 0349 return (somethingFound); 0350 } 0351 0352 bool GroupCommand::removeDataByEmail(KContacts::ContactGroup &group, const QString &email, bool verbose) 0353 { 0354 bool somethingFound = false; 0355 0356 // Remove any existing data with the same email address from the group. 0357 for (int i = 0; i < group.dataCount();) { 0358 const KContacts::ContactGroup::Data data = group.data(i); 0359 if (QString::compare(data.email(), email, Qt::CaseInsensitive) == 0) { 0360 group.remove(data); 0361 somethingFound = true; 0362 if (verbose) { 0363 displayContactData(data); 0364 } 0365 } else { 0366 ++i; 0367 } 0368 } 0369 0370 return (somethingFound); 0371 } 0372 0373 AbstractCommand::Errors GroupCommand::showExpandedGroup(const KContacts::ContactGroup &group) 0374 { 0375 if (!mBriefMode) { 0376 std::cout << qPrintable(i18nc("@info:shell section header 1=item 2=groupref count 3=ref count 4=data count 5=name", 0377 "Group %1 \"%5\" has %2 groups, %3 references and %4 data items:", 0378 QString::number(mGroupItem->id()), 0379 group.contactGroupReferenceCount(), 0380 group.contactReferenceCount(), 0381 group.dataCount(), 0382 group.name())); 0383 std::cout << std::endl; 0384 0385 std::cout << " "; 0386 writeColumn(i18nc("@info:shell column header", "Type"), 4); 0387 writeColumn(i18nc("@info:shell column header", "ID"), 8); 0388 writeColumn(i18nc("@info:shell column header", "Email"), 30); 0389 writeColumn(i18nc("@info:shell column header", "Name")); 0390 std::cout << std::endl; 0391 } 0392 0393 int c = group.contactGroupReferenceCount(); 0394 if (!mBriefMode) { 0395 for (int i = 0; i < c; ++i) { 0396 writeColumn(" G", 5); 0397 writeColumn(group.contactGroupReference(i).uid(), 8); 0398 std::cout << std::endl; 0399 } 0400 } 0401 0402 c = group.contactReferenceCount(); 0403 QList<Item::Id> fetchIds; 0404 for (int i = 0; i < c; ++i) { 0405 Item::Id id = group.contactReference(i).uid().toInt(); 0406 fetchIds.append(id); 0407 } 0408 0409 if (!fetchIds.isEmpty()) { 0410 ItemFetchJob *itemJob = new ItemFetchJob(fetchIds, this); 0411 itemJob->fetchScope().setFetchModificationTime(false); 0412 itemJob->fetchScope().fetchAllAttributes(false); 0413 itemJob->fetchScope().fetchFullPayload(true); 0414 0415 itemJob->exec(); 0416 if (itemJob->error() != 0) { 0417 std::cout << std::endl; 0418 emit error(itemJob->errorString()); 0419 } 0420 0421 Item::List fetchedItems = itemJob->items(); 0422 if (fetchedItems.isEmpty()) { 0423 emit error(i18nc("@info:shell", "No items could be fetched")); 0424 } 0425 0426 for (Item::List::const_iterator it = fetchedItems.constBegin(); 0427 it != fetchedItems.constEnd(); ++it) { 0428 const Item item = (*it); 0429 fetchIds.removeAll(item.id()); // note that we've fetched this 0430 0431 QString email; 0432 if (item.hasPayload<KContacts::Addressee>()) { 0433 KContacts::Addressee addr = item.payload<KContacts::Addressee>(); 0434 email = addr.preferredEmail(); 0435 0436 // Retrieve the original preferred email from the contact group reference. 0437 // If there is one, display that; if not, the contact's preferred email. 0438 for (int i = 0; i < c; ++i) { 0439 Item::Id id = group.contactReference(i).uid().toInt(); 0440 if (id == item.id()) { 0441 const QString prefEmail = group.contactReference(i).preferredEmail(); 0442 if (!prefEmail.isEmpty()) { 0443 email = prefEmail; 0444 } 0445 break; 0446 } 0447 } 0448 } 0449 0450 if (mBriefMode) { // only show email 0451 std::cout << qPrintable(email); 0452 std::cout << std::endl; 0453 } else { 0454 displayContactReference(item, email); 0455 } 0456 } 0457 0458 foreach (const Item::Id id, fetchIds) { // error for any that remain 0459 displayReferenceError(id); 0460 } 0461 } 0462 0463 c = group.dataCount(); 0464 for (int i = 0; i < c; ++i) { 0465 const KContacts::ContactGroup::Data data = group.data(i); 0466 if (mBriefMode) { // only show email 0467 std::cout << qPrintable(data.email()); 0468 std::cout << std::endl; 0469 } else { 0470 displayContactData(data); // show full information 0471 } 0472 } 0473 0474 return (!fetchIds.isEmpty() ? RuntimeError : NoError); 0475 } 0476 0477 AbstractCommand::Errors GroupCommand::addGroupItems(KContacts::ContactGroup &group) 0478 { 0479 Q_ASSERT(!mItemArgs.isEmpty()); 0480 0481 if (!mBriefMode) { 0482 std::cout << qPrintable(i18nc("@info:shell section header 1=item 2=groupref count 3=ref count 4=data count 5=name", 0483 "Adding to group %1 \"%2\":", 0484 QString::number(mGroupItem->id()), group.name())); 0485 std::cout << std::endl; 0486 } 0487 0488 bool hadError = false; // not yet, anyway 0489 foreach (const QString &arg, mItemArgs) { 0490 // Look to see whether the argument is an email address 0491 if (KEmailAddress::isValidSimpleAddress(arg)) { 0492 const QString email = arg.toLower(); // email addresses are case-insensitive 0493 0494 Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob(); 0495 job->setQuery(Akonadi::ContactSearchJob::Email, email); 0496 if (!job->exec()) { 0497 ErrorReporter::error(i18nc("@info:shell", "Cannot search for email '%1', %2", 0498 email, job->errorString())); 0499 hadError = true; 0500 continue; 0501 } 0502 0503 const Item::List resultItems = job->items(); 0504 if (resultItems.count() > 0) { 0505 // If the query returned a known Akonadi contact, add that 0506 // (or the first, if there are more than one) as a reference. 0507 0508 if (resultItems.count() > 1) { 0509 ErrorReporter::warning(i18nc("@info:shell", "Multiple contacts found for '%1', using the first one", email)); 0510 } 0511 0512 Akonadi::Item item = resultItems.first(); 0513 Q_ASSERT(item.hasPayload<KContacts::Addressee>()); 0514 KContacts::ContactGroup::ContactReference ref(QString::number(item.id())); 0515 0516 // Not really equivalent to "only add the new reference if it 0517 // doesn't exist already", because e.g. the old reference may have 0518 // a preferred email set whereas the new one may be different. 0519 0520 removeReferenceById(group, ref.uid()); // remove any existing 0521 group.append(ref); // then add new reference 0522 displayContactReference(item); // report what was added 0523 } else { 0524 // If no Akonadi contact matching the specified email could be found, 0525 // add the email (with the comment, if specified) as contact data. 0526 0527 removeDataByEmail(group, email); // remove existing with that email 0528 QString name = (mNameArg.isEmpty() ? arg : mNameArg); 0529 KContacts::ContactGroup::Data data(name, email); 0530 group.append(data); // add new contact data 0531 displayContactData(data); // report what was added 0532 } 0533 } else { 0534 // Not an email address, see if an Akonadi URL or numeric item ID 0535 Item item = CollectionResolveJob::parseItem(arg, true); 0536 if (!item.isValid()) { 0537 hadError = true; 0538 continue; 0539 } 0540 0541 Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item); 0542 job->fetchScope().fetchFullPayload(); 0543 if (!job->exec()) { 0544 ErrorReporter::error(i18nc("@info:shell", "Cannot fetch requested item %1, %2", 0545 QString::number(item.id()), job->errorString())); 0546 hadError = true; 0547 continue; 0548 } 0549 0550 const Akonadi::Item::List fetchedItems = job->items(); 0551 Q_ASSERT(fetchedItems.count() > 0); 0552 const Akonadi::Item fetchedItem = fetchedItems.first(); 0553 0554 if (!fetchedItem.hasPayload<KContacts::Addressee>()) { 0555 ErrorReporter::error(i18nc("@info:shell", "Item %1 is not a contact item", 0556 QString::number(item.id()))); 0557 hadError = true; 0558 continue; 0559 } 0560 0561 KContacts::ContactGroup::ContactReference ref(QString::number(fetchedItem.id())); 0562 0563 removeReferenceById(group, ref.uid()); // remove any existing 0564 group.append(ref); // then add new reference 0565 displayContactReference(fetchedItem); // report what was added 0566 } 0567 } 0568 0569 if (!mBriefMode) { 0570 std::cout << qPrintable(i18nc("@info:shell section header 1=item 2=groupref count 3=ref count 4=data count 5=name", 0571 "Group %1 \"%5\" now has %2 groups, %3 references and %4 data items", 0572 QString::number(mGroupItem->id()), 0573 group.contactGroupReferenceCount(), 0574 group.contactReferenceCount(), 0575 group.dataCount(), 0576 group.name())); 0577 std::cout << std::endl; 0578 } 0579 0580 return (hadError ? RuntimeError : NoError); 0581 } 0582 0583 AbstractCommand::Errors GroupCommand::deleteGroupItems(KContacts::ContactGroup &group) 0584 { 0585 Q_ASSERT(!mItemArgs.isEmpty()); 0586 0587 if (!mBriefMode) { 0588 std::cout << qPrintable(i18nc("@info:shell section header 1=item 2=groupref count 3=ref count 4=data count 5=name", 0589 "Removing from group %1 \"%2\":", 0590 QString::number(mGroupItem->id()), group.name())); 0591 std::cout << std::endl; 0592 } 0593 0594 bool hadError = false; // not yet, anyway 0595 foreach (const QString &arg, mItemArgs) { 0596 // Look to see whether the argument is an email address 0597 if (KEmailAddress::isValidSimpleAddress(arg)) { 0598 bool somethingFound = false; 0599 0600 // An email address is specified. First remove any existing 0601 // data items having that email address. 0602 0603 if (removeDataByEmail(group, arg, true)) { 0604 somethingFound = true; // note that did something 0605 } 0606 0607 // Then remove any references to any item containing that email address. 0608 Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob(); 0609 job->setQuery(Akonadi::ContactSearchJob::Email, arg); 0610 if (!job->exec()) { 0611 ErrorReporter::error(i18nc("@info:shell", "Cannot search for email '%1', %2", 0612 arg, job->errorString())); 0613 hadError = true; 0614 continue; 0615 } 0616 0617 const Item::List resultItems = job->items(); 0618 for (int itemIndex = 0; itemIndex < resultItems.count(); ++itemIndex) { 0619 const Akonadi::Item item = resultItems[itemIndex]; 0620 if (removeReferenceById(group, QString::number(item.id()), true)) { 0621 somethingFound = true; // note that did something 0622 } 0623 } 0624 0625 if (!somethingFound) { // nothing found to remove 0626 ErrorReporter::warning(i18nc("@info:shell", "Nothing to remove for email '%1'", arg)); 0627 hadError = true; 0628 continue; 0629 } 0630 } else { 0631 // Not an email address, see if an Akonadi URL or numeric item ID 0632 Item item = CollectionResolveJob::parseItem(arg, true); 0633 if (!item.isValid()) { 0634 hadError = true; 0635 continue; 0636 } 0637 0638 // Remove any references to that Akonadi ID 0639 const QString itemId = QString::number(item.id()); 0640 for (int i = 0; i < group.contactReferenceCount();) { 0641 KContacts::ContactGroup::ContactReference existingRef = group.contactReference(i); 0642 if (existingRef.uid() == itemId) { 0643 displayContactReference(item.id()); 0644 group.remove(existingRef); 0645 } else { 0646 ++i; 0647 } 0648 } 0649 } 0650 } 0651 0652 if (!mBriefMode) { 0653 std::cout << qPrintable(i18nc("@info:shell section header 1=item 2=groupref count 3=ref count 4=data count 5=name", 0654 "Group %1 \"%5\" now has %2 groups, %3 references and %4 data items", 0655 QString::number(mGroupItem->id()), 0656 group.contactGroupReferenceCount(), 0657 group.contactReferenceCount(), 0658 group.dataCount(), 0659 group.name())); 0660 std::cout << std::endl; 0661 } 0662 0663 return (hadError ? RuntimeError : NoError); 0664 } 0665 0666 AbstractCommand::Errors GroupCommand::cleanGroupItems(KContacts::ContactGroup &group) 0667 { 0668 if (!mBriefMode) { 0669 std::cout << qPrintable(i18nc("@info:shell section header 1=item 2=groupref count 3=ref count 4=data count 5=name", 0670 "Cleaning references from group %1 \"%2\":", 0671 QString::number(mGroupItem->id()), group.name())); 0672 std::cout << std::endl; 0673 } 0674 0675 // Remove any reference items with an unknown or invalid ID from the group. 0676 for (int i = 0; i < group.contactReferenceCount();) { 0677 KContacts::ContactGroup::ContactReference ref = group.contactReference(i); 0678 0679 bool doDelete = false; 0680 qint64 id = ref.uid().toLong(); 0681 if (id == -1) { 0682 doDelete = true; // invalid, always remove this 0683 } else { 0684 Akonadi::Item item(id); 0685 Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item); 0686 // no need for payload 0687 if (!job->exec() || job->count() == 0) { // if fetch failed or nothing returned 0688 doDelete = true; 0689 } 0690 } 0691 0692 if (doDelete) { // reference is to be deleted 0693 group.remove(ref); // remove it from group 0694 displayReferenceError(id); 0695 } else { 0696 ++i; 0697 } 0698 } 0699 0700 if (!mBriefMode) { 0701 std::cout << qPrintable(i18nc("@info:shell section header 1=item 2=groupref count 3=ref count 4=data count 5=name", 0702 "Group %1 \"%5\" now has %2 groups, %3 references and %4 data items", 0703 QString::number(mGroupItem->id()), 0704 group.contactGroupReferenceCount(), 0705 group.contactReferenceCount(), 0706 group.dataCount(), 0707 group.name())); 0708 std::cout << std::endl; 0709 } 0710 0711 return (NoError); 0712 }