diff --git a/apps/contactsinteraction/appinfo/info.xml b/apps/contactsinteraction/appinfo/info.xml
index e675e66d389b4..44442fe23df2a 100644
--- a/apps/contactsinteraction/appinfo/info.xml
+++ b/apps/contactsinteraction/appinfo/info.xml
@@ -9,7 +9,7 @@
Contacts Interaction
Manages interaction between accounts and contacts
Collect data about accounts and contacts interactions and provide an address book for the data
- 1.14.0
+ 1.14.1
agpl
Christoph Wurst
Nextcloud Groupware Team
@@ -26,6 +26,11 @@
OCA\ContactsInteraction\BackgroundJob\CleanupJob
+
+
+ OCA\ContactsInteraction\Migration\FixVcardCategory
+
+
OCA\ContactsInteraction\AddressBookProvider
diff --git a/apps/contactsinteraction/composer/composer/autoload_classmap.php b/apps/contactsinteraction/composer/composer/autoload_classmap.php
index aff453c997df7..8a61d3762bb98 100644
--- a/apps/contactsinteraction/composer/composer/autoload_classmap.php
+++ b/apps/contactsinteraction/composer/composer/autoload_classmap.php
@@ -17,5 +17,6 @@
'OCA\\ContactsInteraction\\Db\\RecentContactMapper' => $baseDir . '/../lib/Db/RecentContactMapper.php',
'OCA\\ContactsInteraction\\Listeners\\ContactInteractionListener' => $baseDir . '/../lib/Listeners/ContactInteractionListener.php',
'OCA\\ContactsInteraction\\Listeners\\UserDeletedListener' => $baseDir . '/../lib/Listeners/UserDeletedListener.php',
+ 'OCA\\ContactsInteraction\\Migration\\FixVcardCategory' => $baseDir . '/../lib/Migration/FixVcardCategory.php',
'OCA\\ContactsInteraction\\Migration\\Version010000Date20200304152605' => $baseDir . '/../lib/Migration/Version010000Date20200304152605.php',
);
diff --git a/apps/contactsinteraction/composer/composer/autoload_static.php b/apps/contactsinteraction/composer/composer/autoload_static.php
index 9cee53ae04565..fb5b603ed8100 100644
--- a/apps/contactsinteraction/composer/composer/autoload_static.php
+++ b/apps/contactsinteraction/composer/composer/autoload_static.php
@@ -32,6 +32,7 @@ class ComposerStaticInitContactsInteraction
'OCA\\ContactsInteraction\\Db\\RecentContactMapper' => __DIR__ . '/..' . '/../lib/Db/RecentContactMapper.php',
'OCA\\ContactsInteraction\\Listeners\\ContactInteractionListener' => __DIR__ . '/..' . '/../lib/Listeners/ContactInteractionListener.php',
'OCA\\ContactsInteraction\\Listeners\\UserDeletedListener' => __DIR__ . '/..' . '/../lib/Listeners/UserDeletedListener.php',
+ 'OCA\\ContactsInteraction\\Migration\\FixVcardCategory' => __DIR__ . '/..' . '/../lib/Migration/FixVcardCategory.php',
'OCA\\ContactsInteraction\\Migration\\Version010000Date20200304152605' => __DIR__ . '/..' . '/../lib/Migration/Version010000Date20200304152605.php',
);
diff --git a/apps/contactsinteraction/lib/Listeners/ContactInteractionListener.php b/apps/contactsinteraction/lib/Listeners/ContactInteractionListener.php
index 61f529f9c46e8..dc5407a25ec10 100644
--- a/apps/contactsinteraction/lib/Listeners/ContactInteractionListener.php
+++ b/apps/contactsinteraction/lib/Listeners/ContactInteractionListener.php
@@ -117,7 +117,8 @@ private function generateCard(RecentContact $contact): string {
$props = [
'URI' => UUIDUtil::getUUID(),
'FN' => $this->getDisplayName($contact->getUid()) ?? $contact->getEmail() ?? $contact->getFederatedCloudId(),
- 'CATEGORIES' => $this->l10n->t('Recently contacted'),
+ // Recently contacted not translated on purpose: https://github.com/nextcloud/contacts/issues/4663
+ 'CATEGORIES' => 'Recently contacted',
];
if ($contact->getEmail() !== null) {
diff --git a/apps/contactsinteraction/lib/Migration/FixVcardCategory.php b/apps/contactsinteraction/lib/Migration/FixVcardCategory.php
new file mode 100644
index 0000000000000..b2b7ca9fbf51c
--- /dev/null
+++ b/apps/contactsinteraction/lib/Migration/FixVcardCategory.php
@@ -0,0 +1,88 @@
+connection->getQueryBuilder();
+
+ $cardsWithTranslatedCategory = $query->select(['id', 'card'])
+ ->from('recent_contact')
+ ->where($query->expr()->notLike(
+ 'card',
+ $query->createNamedParameter('%CATEGORIES:Recently contacted%')
+ ))
+ ->setMaxResults(self::CARDS_PER_BATCH)
+ ->executeQuery();
+ $rowCount = $cardsWithTranslatedCategory->rowCount();
+
+ $output->startProgress($rowCount);
+
+ $this->connection->beginTransaction();
+
+ $updateQuery = $query->update('recent_contact')
+ ->set('card', $query->createParameter('card'))
+ ->where($query->expr()->eq('id', $query->createParameter('id')));
+
+ while ($card = $cardsWithTranslatedCategory->fetch()) {
+ $output->advance(1);
+
+ try {
+ $vcard = Reader::read($card['card']);
+ } catch (ParseException $e) {
+ $output->info('Could not parse vcard with id ' . $card['id']);
+ continue;
+ }
+
+ $vcard->remove('CATEGORIES');
+ $vcard->add('CATEGORIES', 'Recently contacted');
+
+ $updateQuery->setParameter('id', $card['id']);
+ $updateQuery->setParameter('card', $vcard->serialize());
+ $updateQuery->executeStatement();
+ }
+
+ $this->connection->commit();
+
+ $cardsWithTranslatedCategory->closeCursor();
+
+ $output->finishProgress();
+
+ if ($rowCount === self::CARDS_PER_BATCH) {
+ $this->jobList->add(BackgroundRepair::class, [
+ 'app' => Application::APP_ID,
+ 'step' => FixVcardCategory::class,
+ 'reschedule' => time(), // Use a different argument to reschedule the job
+ ]);
+ }
+ }
+}