aboutsummaryrefslogtreecommitdiffstats
path: root/apps/contactsinteraction
diff options
context:
space:
mode:
Diffstat (limited to 'apps/contactsinteraction')
-rw-r--r--apps/contactsinteraction/.noopenapi0
-rw-r--r--apps/contactsinteraction/appinfo/info.xml34
-rw-r--r--apps/contactsinteraction/composer/autoload.php25
-rw-r--r--apps/contactsinteraction/composer/composer.json13
-rw-r--r--apps/contactsinteraction/composer/composer.lock18
-rw-r--r--apps/contactsinteraction/composer/composer/ClassLoader.php579
-rw-r--r--apps/contactsinteraction/composer/composer/InstalledVersions.php359
-rw-r--r--apps/contactsinteraction/composer/composer/LICENSE21
-rw-r--r--apps/contactsinteraction/composer/composer/autoload_classmap.php20
-rw-r--r--apps/contactsinteraction/composer/composer/autoload_namespaces.php9
-rw-r--r--apps/contactsinteraction/composer/composer/autoload_psr4.php10
-rw-r--r--apps/contactsinteraction/composer/composer/autoload_real.php37
-rw-r--r--apps/contactsinteraction/composer/composer/autoload_static.php46
-rw-r--r--apps/contactsinteraction/composer/composer/installed.json5
-rw-r--r--apps/contactsinteraction/composer/composer/installed.php23
-rw-r--r--apps/contactsinteraction/l10n/.gitkeep0
-rw-r--r--apps/contactsinteraction/l10n/ar.js9
-rw-r--r--apps/contactsinteraction/l10n/ar.json7
-rw-r--r--apps/contactsinteraction/l10n/ast.js9
-rw-r--r--apps/contactsinteraction/l10n/ast.json7
-rw-r--r--apps/contactsinteraction/l10n/bg.js7
-rw-r--r--apps/contactsinteraction/l10n/bg.json5
-rw-r--r--apps/contactsinteraction/l10n/ca.js9
-rw-r--r--apps/contactsinteraction/l10n/ca.json7
-rw-r--r--apps/contactsinteraction/l10n/cs.js9
-rw-r--r--apps/contactsinteraction/l10n/cs.json7
-rw-r--r--apps/contactsinteraction/l10n/da.js9
-rw-r--r--apps/contactsinteraction/l10n/da.json7
-rw-r--r--apps/contactsinteraction/l10n/de.js9
-rw-r--r--apps/contactsinteraction/l10n/de.json7
-rw-r--r--apps/contactsinteraction/l10n/de_DE.js9
-rw-r--r--apps/contactsinteraction/l10n/de_DE.json7
-rw-r--r--apps/contactsinteraction/l10n/el.js9
-rw-r--r--apps/contactsinteraction/l10n/el.json7
-rw-r--r--apps/contactsinteraction/l10n/en_GB.js9
-rw-r--r--apps/contactsinteraction/l10n/en_GB.json7
-rw-r--r--apps/contactsinteraction/l10n/es.js9
-rw-r--r--apps/contactsinteraction/l10n/es.json7
-rw-r--r--apps/contactsinteraction/l10n/es_EC.js7
-rw-r--r--apps/contactsinteraction/l10n/es_EC.json5
-rw-r--r--apps/contactsinteraction/l10n/es_MX.js9
-rw-r--r--apps/contactsinteraction/l10n/es_MX.json7
-rw-r--r--apps/contactsinteraction/l10n/et_EE.js9
-rw-r--r--apps/contactsinteraction/l10n/et_EE.json7
-rw-r--r--apps/contactsinteraction/l10n/eu.js9
-rw-r--r--apps/contactsinteraction/l10n/eu.json7
-rw-r--r--apps/contactsinteraction/l10n/fa.js7
-rw-r--r--apps/contactsinteraction/l10n/fa.json5
-rw-r--r--apps/contactsinteraction/l10n/fi.js7
-rw-r--r--apps/contactsinteraction/l10n/fi.json5
-rw-r--r--apps/contactsinteraction/l10n/fr.js9
-rw-r--r--apps/contactsinteraction/l10n/fr.json7
-rw-r--r--apps/contactsinteraction/l10n/ga.js9
-rw-r--r--apps/contactsinteraction/l10n/ga.json7
-rw-r--r--apps/contactsinteraction/l10n/gl.js9
-rw-r--r--apps/contactsinteraction/l10n/gl.json7
-rw-r--r--apps/contactsinteraction/l10n/he.js7
-rw-r--r--apps/contactsinteraction/l10n/he.json5
-rw-r--r--apps/contactsinteraction/l10n/hr.js7
-rw-r--r--apps/contactsinteraction/l10n/hr.json5
-rw-r--r--apps/contactsinteraction/l10n/hu.js9
-rw-r--r--apps/contactsinteraction/l10n/hu.json7
-rw-r--r--apps/contactsinteraction/l10n/is.js9
-rw-r--r--apps/contactsinteraction/l10n/is.json7
-rw-r--r--apps/contactsinteraction/l10n/it.js9
-rw-r--r--apps/contactsinteraction/l10n/it.json7
-rw-r--r--apps/contactsinteraction/l10n/ja.js9
-rw-r--r--apps/contactsinteraction/l10n/ja.json7
-rw-r--r--apps/contactsinteraction/l10n/ko.js9
-rw-r--r--apps/contactsinteraction/l10n/ko.json7
-rw-r--r--apps/contactsinteraction/l10n/lt_LT.js7
-rw-r--r--apps/contactsinteraction/l10n/lt_LT.json5
-rw-r--r--apps/contactsinteraction/l10n/mk.js7
-rw-r--r--apps/contactsinteraction/l10n/mk.json5
-rw-r--r--apps/contactsinteraction/l10n/nb.js9
-rw-r--r--apps/contactsinteraction/l10n/nb.json7
-rw-r--r--apps/contactsinteraction/l10n/nl.js9
-rw-r--r--apps/contactsinteraction/l10n/nl.json7
-rw-r--r--apps/contactsinteraction/l10n/pl.js9
-rw-r--r--apps/contactsinteraction/l10n/pl.json7
-rw-r--r--apps/contactsinteraction/l10n/pt_BR.js9
-rw-r--r--apps/contactsinteraction/l10n/pt_BR.json7
-rw-r--r--apps/contactsinteraction/l10n/ro.js7
-rw-r--r--apps/contactsinteraction/l10n/ro.json5
-rw-r--r--apps/contactsinteraction/l10n/ru.js9
-rw-r--r--apps/contactsinteraction/l10n/ru.json7
-rw-r--r--apps/contactsinteraction/l10n/sc.js7
-rw-r--r--apps/contactsinteraction/l10n/sc.json5
-rw-r--r--apps/contactsinteraction/l10n/sk.js9
-rw-r--r--apps/contactsinteraction/l10n/sk.json7
-rw-r--r--apps/contactsinteraction/l10n/sl.js9
-rw-r--r--apps/contactsinteraction/l10n/sl.json7
-rw-r--r--apps/contactsinteraction/l10n/sr.js9
-rw-r--r--apps/contactsinteraction/l10n/sr.json7
-rw-r--r--apps/contactsinteraction/l10n/sv.js9
-rw-r--r--apps/contactsinteraction/l10n/sv.json7
-rw-r--r--apps/contactsinteraction/l10n/sw.js9
-rw-r--r--apps/contactsinteraction/l10n/sw.json7
-rw-r--r--apps/contactsinteraction/l10n/tr.js9
-rw-r--r--apps/contactsinteraction/l10n/tr.json7
-rw-r--r--apps/contactsinteraction/l10n/ug.js9
-rw-r--r--apps/contactsinteraction/l10n/ug.json7
-rw-r--r--apps/contactsinteraction/l10n/uk.js9
-rw-r--r--apps/contactsinteraction/l10n/uk.json7
-rw-r--r--apps/contactsinteraction/l10n/vi.js7
-rw-r--r--apps/contactsinteraction/l10n/vi.json5
-rw-r--r--apps/contactsinteraction/l10n/zh_CN.js9
-rw-r--r--apps/contactsinteraction/l10n/zh_CN.json7
-rw-r--r--apps/contactsinteraction/l10n/zh_HK.js9
-rw-r--r--apps/contactsinteraction/l10n/zh_HK.json7
-rw-r--r--apps/contactsinteraction/l10n/zh_TW.js9
-rw-r--r--apps/contactsinteraction/l10n/zh_TW.json7
-rw-r--r--apps/contactsinteraction/lib/AddressBook.php151
-rw-r--r--apps/contactsinteraction/lib/AddressBookProvider.php58
-rw-r--r--apps/contactsinteraction/lib/AppInfo/Application.php31
-rw-r--r--apps/contactsinteraction/lib/BackgroundJob/CleanupJob.php33
-rw-r--r--apps/contactsinteraction/lib/Card.php110
-rw-r--r--apps/contactsinteraction/lib/Db/CardSearchDao.php81
-rw-r--r--apps/contactsinteraction/lib/Db/RecentContact.php44
-rw-r--r--apps/contactsinteraction/lib/Db/RecentContactMapper.php115
-rw-r--r--apps/contactsinteraction/lib/Listeners/ContactInteractionListener.php132
-rw-r--r--apps/contactsinteraction/lib/Migration/Version010000Date20200304152605.php75
-rw-r--r--apps/contactsinteraction/tests/Db/RecentContactMapperTest.php115
123 files changed, 2868 insertions, 0 deletions
diff --git a/apps/contactsinteraction/.noopenapi b/apps/contactsinteraction/.noopenapi
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/apps/contactsinteraction/.noopenapi
diff --git a/apps/contactsinteraction/appinfo/info.xml b/apps/contactsinteraction/appinfo/info.xml
new file mode 100644
index 00000000000..54b9a134f58
--- /dev/null
+++ b/apps/contactsinteraction/appinfo/info.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!--
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
+ <id>contactsinteraction</id>
+ <name>Contacts Interaction</name>
+ <summary>Manages interaction between accounts and contacts</summary>
+ <description>Collect data about accounts and contacts interactions and provide an address book for the data</description>
+ <version>1.13.0</version>
+ <licence>agpl</licence>
+ <author>Christoph Wurst</author>
+ <author homepage="https://github.com/nextcloud/groupware">Nextcloud Groupware Team</author>
+ <namespace>ContactsInteraction</namespace>
+ <types>
+ <dav/>
+ </types>
+ <category>integration</category>
+ <category>social</category>
+ <bugs>https://github.com/nextcloud/server/issues</bugs>
+ <dependencies>
+ <nextcloud min-version="32" max-version="32"/>
+ </dependencies>
+ <background-jobs>
+ <job>OCA\ContactsInteraction\BackgroundJob\CleanupJob</job>
+ </background-jobs>
+ <sabre>
+ <address-book-plugins>
+ <plugin>OCA\ContactsInteraction\AddressBookProvider</plugin>
+ </address-book-plugins>
+ </sabre>
+</info>
diff --git a/apps/contactsinteraction/composer/autoload.php b/apps/contactsinteraction/composer/autoload.php
new file mode 100644
index 00000000000..ee3abcce2c2
--- /dev/null
+++ b/apps/contactsinteraction/composer/autoload.php
@@ -0,0 +1,25 @@
+<?php
+
+// autoload.php @generated by Composer
+
+if (PHP_VERSION_ID < 50600) {
+ if (!headers_sent()) {
+ header('HTTP/1.1 500 Internal Server Error');
+ }
+ $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
+ if (!ini_get('display_errors')) {
+ if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
+ fwrite(STDERR, $err);
+ } elseif (!headers_sent()) {
+ echo $err;
+ }
+ }
+ trigger_error(
+ $err,
+ E_USER_ERROR
+ );
+}
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInitContactsInteraction::getLoader();
diff --git a/apps/contactsinteraction/composer/composer.json b/apps/contactsinteraction/composer/composer.json
new file mode 100644
index 00000000000..232fef13e81
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer.json
@@ -0,0 +1,13 @@
+{
+ "config" : {
+ "vendor-dir": ".",
+ "optimize-autoloader": true,
+ "classmap-authoritative": true,
+ "autoloader-suffix": "ContactsInteraction"
+ },
+ "autoload" : {
+ "psr-4": {
+ "OCA\\ContactsInteraction\\": "../lib/"
+ }
+ }
+}
diff --git a/apps/contactsinteraction/composer/composer.lock b/apps/contactsinteraction/composer/composer.lock
new file mode 100644
index 00000000000..fd0bcbcb753
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer.lock
@@ -0,0 +1,18 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "d751713988987e9331980363e24189ce",
+ "packages": [],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": [],
+ "plugin-api-version": "2.1.0"
+}
diff --git a/apps/contactsinteraction/composer/composer/ClassLoader.php b/apps/contactsinteraction/composer/composer/ClassLoader.php
new file mode 100644
index 00000000000..7824d8f7eaf
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/ClassLoader.php
@@ -0,0 +1,579 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ * Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ * @author Jordi Boggiano <j.boggiano@seld.be>
+ * @see https://www.php-fig.org/psr/psr-0/
+ * @see https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ /** @var \Closure(string):void */
+ private static $includeFile;
+
+ /** @var string|null */
+ private $vendorDir;
+
+ // PSR-4
+ /**
+ * @var array<string, array<string, int>>
+ */
+ private $prefixLengthsPsr4 = array();
+ /**
+ * @var array<string, list<string>>
+ */
+ private $prefixDirsPsr4 = array();
+ /**
+ * @var list<string>
+ */
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ /**
+ * List of PSR-0 prefixes
+ *
+ * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
+ *
+ * @var array<string, array<string, list<string>>>
+ */
+ private $prefixesPsr0 = array();
+ /**
+ * @var list<string>
+ */
+ private $fallbackDirsPsr0 = array();
+
+ /** @var bool */
+ private $useIncludePath = false;
+
+ /**
+ * @var array<string, string>
+ */
+ private $classMap = array();
+
+ /** @var bool */
+ private $classMapAuthoritative = false;
+
+ /**
+ * @var array<string, bool>
+ */
+ private $missingClasses = array();
+
+ /** @var string|null */
+ private $apcuPrefix;
+
+ /**
+ * @var array<string, self>
+ */
+ private static $registeredLoaders = array();
+
+ /**
+ * @param string|null $vendorDir
+ */
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ self::initializeIncludeClosure();
+ }
+
+ /**
+ * @return array<string, list<string>>
+ */
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ /**
+ * @return array<string, list<string>>
+ */
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ /**
+ * @return list<string>
+ */
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ /**
+ * @return list<string>
+ */
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ /**
+ * @return array<string, string> Array of classname => path
+ */
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array<string, string> $classMap Class to filename map
+ *
+ * @return void
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list<string>|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @return void
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list<string>|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list<string>|string $paths The PSR-0 base directories
+ *
+ * @return void
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list<string>|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ *
+ * @return void
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ *
+ * @return void
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ *
+ * @return void
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ *
+ * @return void
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ *
+ * @return void
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return true|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ $includeFile = self::$includeFile;
+ $includeFile($file);
+
+ return true;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the currently registered loaders keyed by their corresponding vendor directories.
+ *
+ * @return array<string, self>
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ /**
+ * @param string $class
+ * @param string $ext
+ * @return string|false
+ */
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+
+ /**
+ * @return void
+ */
+ private static function initializeIncludeClosure()
+ {
+ if (self::$includeFile !== null) {
+ return;
+ }
+
+ /**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ *
+ * @param string $file
+ * @return void
+ */
+ self::$includeFile = \Closure::bind(static function($file) {
+ include $file;
+ }, null, null);
+ }
+}
diff --git a/apps/contactsinteraction/composer/composer/InstalledVersions.php b/apps/contactsinteraction/composer/composer/InstalledVersions.php
new file mode 100644
index 00000000000..51e734a774b
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/InstalledVersions.php
@@ -0,0 +1,359 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ * Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ *
+ * @final
+ */
+class InstalledVersions
+{
+ /**
+ * @var mixed[]|null
+ * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
+ */
+ private static $installed;
+
+ /**
+ * @var bool|null
+ */
+ private static $canGetVendors;
+
+ /**
+ * @var array[]
+ * @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
+ */
+ private static $installedByVendor = array();
+
+ /**
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
+ *
+ * @return string[]
+ * @psalm-return list<string>
+ */
+ public static function getInstalledPackages()
+ {
+ $packages = array();
+ foreach (self::getInstalled() as $installed) {
+ $packages[] = array_keys($installed['versions']);
+ }
+
+ if (1 === \count($packages)) {
+ return $packages[0];
+ }
+
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+ }
+
+ /**
+ * Returns a list of all package names with a specific type e.g. 'library'
+ *
+ * @param string $type
+ * @return string[]
+ * @psalm-return list<string>
+ */
+ public static function getInstalledPackagesByType($type)
+ {
+ $packagesByType = array();
+
+ foreach (self::getInstalled() as $installed) {
+ foreach ($installed['versions'] as $name => $package) {
+ if (isset($package['type']) && $package['type'] === $type) {
+ $packagesByType[] = $name;
+ }
+ }
+ }
+
+ return $packagesByType;
+ }
+
+ /**
+ * Checks whether the given package is installed
+ *
+ * This also returns true if the package name is provided or replaced by another package
+ *
+ * @param string $packageName
+ * @param bool $includeDevRequirements
+ * @return bool
+ */
+ public static function isInstalled($packageName, $includeDevRequirements = true)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (isset($installed['versions'][$packageName])) {
+ return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given package satisfies a version constraint
+ *
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+ *
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+ *
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
+ * @param string $packageName
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+ * @return bool
+ */
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
+ {
+ $constraint = $parser->parseConstraints((string) $constraint);
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+ return $provided->matches($constraint);
+ }
+
+ /**
+ * Returns a version constraint representing all the range(s) which are installed for a given package
+ *
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+ * whether a given version of a package is installed, and not just whether it exists
+ *
+ * @param string $packageName
+ * @return string Version constraint usable with composer/semver
+ */
+ public static function getVersionRanges($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ $ranges = array();
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+ }
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+ }
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+ }
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+ }
+
+ return implode(' || ', $ranges);
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getPrettyVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['pretty_version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+ */
+ public static function getReference($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['reference'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['reference'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+ */
+ public static function getInstallPath($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @return array
+ * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
+ */
+ public static function getRootPackage()
+ {
+ $installed = self::getInstalled();
+
+ return $installed[0]['root'];
+ }
+
+ /**
+ * Returns the raw installed.php data for custom implementations
+ *
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+ * @return array[]
+ * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
+ */
+ public static function getRawData()
+ {
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = include __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ return self::$installed;
+ }
+
+ /**
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
+ *
+ * @return array[]
+ * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
+ */
+ public static function getAllRawData()
+ {
+ return self::getInstalled();
+ }
+
+ /**
+ * Lets you reload the static array from another file
+ *
+ * This is only useful for complex integrations in which a project needs to use
+ * this class but then also needs to execute another project's autoloader in process,
+ * and wants to ensure both projects have access to their version of installed.php.
+ *
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
+ * the data it needs from this class, then call reload() with
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+ * the project in which it runs can then also use this class safely, without
+ * interference between PHPUnit's dependencies and the project's dependencies.
+ *
+ * @param array[] $data A vendor/composer/installed.php data set
+ * @return void
+ *
+ * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
+ */
+ public static function reload($data)
+ {
+ self::$installed = $data;
+ self::$installedByVendor = array();
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
+ */
+ private static function getInstalled()
+ {
+ if (null === self::$canGetVendors) {
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+ }
+
+ $installed = array();
+
+ if (self::$canGetVendors) {
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ if (isset(self::$installedByVendor[$vendorDir])) {
+ $installed[] = self::$installedByVendor[$vendorDir];
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+ $required = require $vendorDir.'/composer/installed.php';
+ $installed[] = self::$installedByVendor[$vendorDir] = $required;
+ if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
+ self::$installed = $installed[count($installed) - 1];
+ }
+ }
+ }
+ }
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
+ $required = require __DIR__ . '/installed.php';
+ self::$installed = $required;
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ if (self::$installed !== array()) {
+ $installed[] = self::$installed;
+ }
+
+ return $installed;
+ }
+}
diff --git a/apps/contactsinteraction/composer/composer/LICENSE b/apps/contactsinteraction/composer/composer/LICENSE
new file mode 100644
index 00000000000..f27399a042d
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/apps/contactsinteraction/composer/composer/autoload_classmap.php b/apps/contactsinteraction/composer/composer/autoload_classmap.php
new file mode 100644
index 00000000000..6cc1fd7d984
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/autoload_classmap.php
@@ -0,0 +1,20 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = $vendorDir;
+
+return array(
+ 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
+ 'OCA\\ContactsInteraction\\AddressBook' => $baseDir . '/../lib/AddressBook.php',
+ 'OCA\\ContactsInteraction\\AddressBookProvider' => $baseDir . '/../lib/AddressBookProvider.php',
+ 'OCA\\ContactsInteraction\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
+ 'OCA\\ContactsInteraction\\BackgroundJob\\CleanupJob' => $baseDir . '/../lib/BackgroundJob/CleanupJob.php',
+ 'OCA\\ContactsInteraction\\Card' => $baseDir . '/../lib/Card.php',
+ 'OCA\\ContactsInteraction\\Db\\CardSearchDao' => $baseDir . '/../lib/Db/CardSearchDao.php',
+ 'OCA\\ContactsInteraction\\Db\\RecentContact' => $baseDir . '/../lib/Db/RecentContact.php',
+ 'OCA\\ContactsInteraction\\Db\\RecentContactMapper' => $baseDir . '/../lib/Db/RecentContactMapper.php',
+ 'OCA\\ContactsInteraction\\Listeners\\ContactInteractionListener' => $baseDir . '/../lib/Listeners/ContactInteractionListener.php',
+ 'OCA\\ContactsInteraction\\Migration\\Version010000Date20200304152605' => $baseDir . '/../lib/Migration/Version010000Date20200304152605.php',
+);
diff --git a/apps/contactsinteraction/composer/composer/autoload_namespaces.php b/apps/contactsinteraction/composer/composer/autoload_namespaces.php
new file mode 100644
index 00000000000..3f5c9296251
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = $vendorDir;
+
+return array(
+);
diff --git a/apps/contactsinteraction/composer/composer/autoload_psr4.php b/apps/contactsinteraction/composer/composer/autoload_psr4.php
new file mode 100644
index 00000000000..4e53aac8792
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/autoload_psr4.php
@@ -0,0 +1,10 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(__DIR__);
+$baseDir = $vendorDir;
+
+return array(
+ 'OCA\\ContactsInteraction\\' => array($baseDir . '/../lib'),
+);
diff --git a/apps/contactsinteraction/composer/composer/autoload_real.php b/apps/contactsinteraction/composer/composer/autoload_real.php
new file mode 100644
index 00000000000..09f95c83645
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/autoload_real.php
@@ -0,0 +1,37 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInitContactsInteraction
+{
+ private static $loader;
+
+ public static function loadClassLoader($class)
+ {
+ if ('Composer\Autoload\ClassLoader' === $class) {
+ require __DIR__ . '/ClassLoader.php';
+ }
+ }
+
+ /**
+ * @return \Composer\Autoload\ClassLoader
+ */
+ public static function getLoader()
+ {
+ if (null !== self::$loader) {
+ return self::$loader;
+ }
+
+ spl_autoload_register(array('ComposerAutoloaderInitContactsInteraction', 'loadClassLoader'), true, true);
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
+ spl_autoload_unregister(array('ComposerAutoloaderInitContactsInteraction', 'loadClassLoader'));
+
+ require __DIR__ . '/autoload_static.php';
+ call_user_func(\Composer\Autoload\ComposerStaticInitContactsInteraction::getInitializer($loader));
+
+ $loader->setClassMapAuthoritative(true);
+ $loader->register(true);
+
+ return $loader;
+ }
+}
diff --git a/apps/contactsinteraction/composer/composer/autoload_static.php b/apps/contactsinteraction/composer/composer/autoload_static.php
new file mode 100644
index 00000000000..c7cdc26dc68
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/autoload_static.php
@@ -0,0 +1,46 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInitContactsInteraction
+{
+ public static $prefixLengthsPsr4 = array (
+ 'O' =>
+ array (
+ 'OCA\\ContactsInteraction\\' => 24,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'OCA\\ContactsInteraction\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/../lib',
+ ),
+ );
+
+ public static $classMap = array (
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'OCA\\ContactsInteraction\\AddressBook' => __DIR__ . '/..' . '/../lib/AddressBook.php',
+ 'OCA\\ContactsInteraction\\AddressBookProvider' => __DIR__ . '/..' . '/../lib/AddressBookProvider.php',
+ 'OCA\\ContactsInteraction\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
+ 'OCA\\ContactsInteraction\\BackgroundJob\\CleanupJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupJob.php',
+ 'OCA\\ContactsInteraction\\Card' => __DIR__ . '/..' . '/../lib/Card.php',
+ 'OCA\\ContactsInteraction\\Db\\CardSearchDao' => __DIR__ . '/..' . '/../lib/Db/CardSearchDao.php',
+ 'OCA\\ContactsInteraction\\Db\\RecentContact' => __DIR__ . '/..' . '/../lib/Db/RecentContact.php',
+ 'OCA\\ContactsInteraction\\Db\\RecentContactMapper' => __DIR__ . '/..' . '/../lib/Db/RecentContactMapper.php',
+ 'OCA\\ContactsInteraction\\Listeners\\ContactInteractionListener' => __DIR__ . '/..' . '/../lib/Listeners/ContactInteractionListener.php',
+ 'OCA\\ContactsInteraction\\Migration\\Version010000Date20200304152605' => __DIR__ . '/..' . '/../lib/Migration/Version010000Date20200304152605.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInitContactsInteraction::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInitContactsInteraction::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInitContactsInteraction::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/apps/contactsinteraction/composer/composer/installed.json b/apps/contactsinteraction/composer/composer/installed.json
new file mode 100644
index 00000000000..f20a6c47c6d
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/installed.json
@@ -0,0 +1,5 @@
+{
+ "packages": [],
+ "dev": false,
+ "dev-package-names": []
+}
diff --git a/apps/contactsinteraction/composer/composer/installed.php b/apps/contactsinteraction/composer/composer/installed.php
new file mode 100644
index 00000000000..1a66c7f2416
--- /dev/null
+++ b/apps/contactsinteraction/composer/composer/installed.php
@@ -0,0 +1,23 @@
+<?php return array(
+ 'root' => array(
+ 'name' => '__root__',
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'reference' => 'b1797842784b250fb01ed5e3bf130705eb94751b',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../',
+ 'aliases' => array(),
+ 'dev' => false,
+ ),
+ 'versions' => array(
+ '__root__' => array(
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
+ 'reference' => 'b1797842784b250fb01ed5e3bf130705eb94751b',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
+ ),
+);
diff --git a/apps/contactsinteraction/l10n/.gitkeep b/apps/contactsinteraction/l10n/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/apps/contactsinteraction/l10n/.gitkeep
diff --git a/apps/contactsinteraction/l10n/ar.js b/apps/contactsinteraction/l10n/ar.js
new file mode 100644
index 00000000000..2304a43b90d
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ar.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "تمّ الاتصال به مؤخّراً",
+ "Contacts Interaction" : "تفاعل جهات الاتصال",
+ "Manages interaction between accounts and contacts" : "إدارة التفاعلات بين الحسابات و جهات الاتصال",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "تجميع البيانات حول التفاعلات بين الحسابات و جهات الاتصال و توفير دفتر عنوان للبيانات"
+},
+"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;");
diff --git a/apps/contactsinteraction/l10n/ar.json b/apps/contactsinteraction/l10n/ar.json
new file mode 100644
index 00000000000..5eac6dbf10c
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ar.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "تمّ الاتصال به مؤخّراً",
+ "Contacts Interaction" : "تفاعل جهات الاتصال",
+ "Manages interaction between accounts and contacts" : "إدارة التفاعلات بين الحسابات و جهات الاتصال",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "تجميع البيانات حول التفاعلات بين الحسابات و جهات الاتصال و توفير دفتر عنوان للبيانات"
+},"pluralForm" :"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/ast.js b/apps/contactsinteraction/l10n/ast.js
new file mode 100644
index 00000000000..63887f1ecb1
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ast.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Contautos de recién",
+ "Contacts Interaction" : "Interaición con contautos",
+ "Manages interaction between accounts and contacts" : "Xestiona la interaición ente cuentes y contautos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Recueye datos de les interaiciones de cuentes y contautos y forne una llibreta de direiciones colos datos"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/ast.json b/apps/contactsinteraction/l10n/ast.json
new file mode 100644
index 00000000000..b9ccd54f5ae
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ast.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Contautos de recién",
+ "Contacts Interaction" : "Interaición con contautos",
+ "Manages interaction between accounts and contacts" : "Xestiona la interaición ente cuentes y contautos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Recueye datos de les interaiciones de cuentes y contautos y forne una llibreta de direiciones colos datos"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/bg.js b/apps/contactsinteraction/l10n/bg.js
new file mode 100644
index 00000000000..fc52a7f9b3c
--- /dev/null
+++ b/apps/contactsinteraction/l10n/bg.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Наскоро влезли в контакт",
+ "Contacts Interaction" : " Взаимодействие на Контакти"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/bg.json b/apps/contactsinteraction/l10n/bg.json
new file mode 100644
index 00000000000..0773ccf0d06
--- /dev/null
+++ b/apps/contactsinteraction/l10n/bg.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "Наскоро влезли в контакт",
+ "Contacts Interaction" : " Взаимодействие на Контакти"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/ca.js b/apps/contactsinteraction/l10n/ca.js
new file mode 100644
index 00000000000..72cde18bc0e
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ca.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Contactes recents",
+ "Contacts Interaction" : "Interacció amb contactes",
+ "Manages interaction between accounts and contacts" : "Gestiona la interacció entre comptes i contactes",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Reculliu dades sobre comptes i interaccions de contactes i proporcioneu una llibreta d'adreces per a les dades"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/ca.json b/apps/contactsinteraction/l10n/ca.json
new file mode 100644
index 00000000000..e217cde9671
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ca.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Contactes recents",
+ "Contacts Interaction" : "Interacció amb contactes",
+ "Manages interaction between accounts and contacts" : "Gestiona la interacció entre comptes i contactes",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Reculliu dades sobre comptes i interaccions de contactes i proporcioneu una llibreta d'adreces per a les dades"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/cs.js b/apps/contactsinteraction/l10n/cs.js
new file mode 100644
index 00000000000..b69e78eaac8
--- /dev/null
+++ b/apps/contactsinteraction/l10n/cs.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Nedávno kontaktovaní",
+ "Contacts Interaction" : "Interakce s kontakty",
+ "Manages interaction between accounts and contacts" : "Spravuje interakci mezi účty a kontakty",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Shromažďuje data o interakcích mezi účty a kontakty a poskytuje pro tato data adresář kontaktů"
+},
+"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;");
diff --git a/apps/contactsinteraction/l10n/cs.json b/apps/contactsinteraction/l10n/cs.json
new file mode 100644
index 00000000000..34bb5d64017
--- /dev/null
+++ b/apps/contactsinteraction/l10n/cs.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Nedávno kontaktovaní",
+ "Contacts Interaction" : "Interakce s kontakty",
+ "Manages interaction between accounts and contacts" : "Spravuje interakci mezi účty a kontakty",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Shromažďuje data o interakcích mezi účty a kontakty a poskytuje pro tato data adresář kontaktů"
+},"pluralForm" :"nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/da.js b/apps/contactsinteraction/l10n/da.js
new file mode 100644
index 00000000000..7f5240181e4
--- /dev/null
+++ b/apps/contactsinteraction/l10n/da.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Nylig kontaktet",
+ "Contacts Interaction" : "Kontakter Interaktion",
+ "Manages interaction between accounts and contacts" : "Styrer interaktion mellem konti og kontakter",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Indsaml data om konti og kontakt interaktioner og angiv en adressebog til dataene"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/da.json b/apps/contactsinteraction/l10n/da.json
new file mode 100644
index 00000000000..c5cb4d616c6
--- /dev/null
+++ b/apps/contactsinteraction/l10n/da.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Nylig kontaktet",
+ "Contacts Interaction" : "Kontakter Interaktion",
+ "Manages interaction between accounts and contacts" : "Styrer interaktion mellem konti og kontakter",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Indsaml data om konti og kontakt interaktioner og angiv en adressebog til dataene"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/de.js b/apps/contactsinteraction/l10n/de.js
new file mode 100644
index 00000000000..4c4675d4073
--- /dev/null
+++ b/apps/contactsinteraction/l10n/de.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Zuletzt kontaktiert",
+ "Contacts Interaction" : "Kontakte-Interaktion",
+ "Manages interaction between accounts and contacts" : "Verwaltet Interaktionen zwischen Konten und Kontakten.",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Sammelt Daten über Konten- und Kontaktinteraktionen und stellt ein Adressbuch für die Daten bereit"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/de.json b/apps/contactsinteraction/l10n/de.json
new file mode 100644
index 00000000000..51c308a3a21
--- /dev/null
+++ b/apps/contactsinteraction/l10n/de.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Zuletzt kontaktiert",
+ "Contacts Interaction" : "Kontakte-Interaktion",
+ "Manages interaction between accounts and contacts" : "Verwaltet Interaktionen zwischen Konten und Kontakten.",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Sammelt Daten über Konten- und Kontaktinteraktionen und stellt ein Adressbuch für die Daten bereit"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/de_DE.js b/apps/contactsinteraction/l10n/de_DE.js
new file mode 100644
index 00000000000..4c4675d4073
--- /dev/null
+++ b/apps/contactsinteraction/l10n/de_DE.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Zuletzt kontaktiert",
+ "Contacts Interaction" : "Kontakte-Interaktion",
+ "Manages interaction between accounts and contacts" : "Verwaltet Interaktionen zwischen Konten und Kontakten.",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Sammelt Daten über Konten- und Kontaktinteraktionen und stellt ein Adressbuch für die Daten bereit"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/de_DE.json b/apps/contactsinteraction/l10n/de_DE.json
new file mode 100644
index 00000000000..51c308a3a21
--- /dev/null
+++ b/apps/contactsinteraction/l10n/de_DE.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Zuletzt kontaktiert",
+ "Contacts Interaction" : "Kontakte-Interaktion",
+ "Manages interaction between accounts and contacts" : "Verwaltet Interaktionen zwischen Konten und Kontakten.",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Sammelt Daten über Konten- und Kontaktinteraktionen und stellt ein Adressbuch für die Daten bereit"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/el.js b/apps/contactsinteraction/l10n/el.js
new file mode 100644
index 00000000000..2e42ef4dce1
--- /dev/null
+++ b/apps/contactsinteraction/l10n/el.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Πρόσφατη επικοινωνία",
+ "Contacts Interaction" : "Ενσωμάτωση επαφών",
+ "Manages interaction between accounts and contacts" : "Διαχειρίζεται την αλληλεπίδραση μεταξύ λογαριασμών και επαφών",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Συλλογή δεδομένων σχετικά με λογαριασμούς και αλληλεπιδράσεις επαφών και παροχή ενός βιβλίου διευθύνσεων για τα δεδομένα"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/el.json b/apps/contactsinteraction/l10n/el.json
new file mode 100644
index 00000000000..497eac88018
--- /dev/null
+++ b/apps/contactsinteraction/l10n/el.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Πρόσφατη επικοινωνία",
+ "Contacts Interaction" : "Ενσωμάτωση επαφών",
+ "Manages interaction between accounts and contacts" : "Διαχειρίζεται την αλληλεπίδραση μεταξύ λογαριασμών και επαφών",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Συλλογή δεδομένων σχετικά με λογαριασμούς και αλληλεπιδράσεις επαφών και παροχή ενός βιβλίου διευθύνσεων για τα δεδομένα"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/en_GB.js b/apps/contactsinteraction/l10n/en_GB.js
new file mode 100644
index 00000000000..254fc00e757
--- /dev/null
+++ b/apps/contactsinteraction/l10n/en_GB.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Recently contacted",
+ "Contacts Interaction" : "Contacts Interaction",
+ "Manages interaction between accounts and contacts" : "Manages interaction between accounts and contacts",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Collect data about accounts and contacts interactions and provide an address book for the data"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/en_GB.json b/apps/contactsinteraction/l10n/en_GB.json
new file mode 100644
index 00000000000..660eac4583a
--- /dev/null
+++ b/apps/contactsinteraction/l10n/en_GB.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Recently contacted",
+ "Contacts Interaction" : "Contacts Interaction",
+ "Manages interaction between accounts and contacts" : "Manages interaction between accounts and contacts",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Collect data about accounts and contacts interactions and provide an address book for the data"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/es.js b/apps/contactsinteraction/l10n/es.js
new file mode 100644
index 00000000000..cf2a836be76
--- /dev/null
+++ b/apps/contactsinteraction/l10n/es.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Contactados recientemente",
+ "Contacts Interaction" : "Interacción de contactos",
+ "Manages interaction between accounts and contacts" : "Administra la interacción entre cuentas y contactos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Recopila datos de las interacciones entre cuentas y contactos, y provee una libreta de direcciones para estos datos"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/contactsinteraction/l10n/es.json b/apps/contactsinteraction/l10n/es.json
new file mode 100644
index 00000000000..2cc3cc7b08e
--- /dev/null
+++ b/apps/contactsinteraction/l10n/es.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Contactados recientemente",
+ "Contacts Interaction" : "Interacción de contactos",
+ "Manages interaction between accounts and contacts" : "Administra la interacción entre cuentas y contactos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Recopila datos de las interacciones entre cuentas y contactos, y provee una libreta de direcciones para estos datos"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/es_EC.js b/apps/contactsinteraction/l10n/es_EC.js
new file mode 100644
index 00000000000..7b8ccf8eb2f
--- /dev/null
+++ b/apps/contactsinteraction/l10n/es_EC.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Recientemente contactado",
+ "Contacts Interaction" : "Interacción con contactos"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/contactsinteraction/l10n/es_EC.json b/apps/contactsinteraction/l10n/es_EC.json
new file mode 100644
index 00000000000..8e6aae5c3ae
--- /dev/null
+++ b/apps/contactsinteraction/l10n/es_EC.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "Recientemente contactado",
+ "Contacts Interaction" : "Interacción con contactos"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/es_MX.js b/apps/contactsinteraction/l10n/es_MX.js
new file mode 100644
index 00000000000..1d3ac7a0b33
--- /dev/null
+++ b/apps/contactsinteraction/l10n/es_MX.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Contactado recientemente",
+ "Contacts Interaction" : "Interacción de contactos",
+ "Manages interaction between accounts and contacts" : "Administra la interacción entre usuarios y contactos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Recopila datos de las interacciones entre usuarios y contactos, y provee una libreta de direcciones para esos datos"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/contactsinteraction/l10n/es_MX.json b/apps/contactsinteraction/l10n/es_MX.json
new file mode 100644
index 00000000000..84af2f94b9b
--- /dev/null
+++ b/apps/contactsinteraction/l10n/es_MX.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Contactado recientemente",
+ "Contacts Interaction" : "Interacción de contactos",
+ "Manages interaction between accounts and contacts" : "Administra la interacción entre usuarios y contactos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Recopila datos de las interacciones entre usuarios y contactos, y provee una libreta de direcciones para esos datos"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/et_EE.js b/apps/contactsinteraction/l10n/et_EE.js
new file mode 100644
index 00000000000..b0bdc000d04
--- /dev/null
+++ b/apps/contactsinteraction/l10n/et_EE.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Hiljuti ühendust võetud",
+ "Contacts Interaction" : "Kontaktide interaktsioon",
+ "Manages interaction between accounts and contacts" : "Haldab kontode ja kontaktide vahelist suhtlust",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Koguge andmeid kontode ja kontaktidega suhtlemise kohta ning looge andmete jaoks aadressiraamat"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/et_EE.json b/apps/contactsinteraction/l10n/et_EE.json
new file mode 100644
index 00000000000..47a9de61eee
--- /dev/null
+++ b/apps/contactsinteraction/l10n/et_EE.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Hiljuti ühendust võetud",
+ "Contacts Interaction" : "Kontaktide interaktsioon",
+ "Manages interaction between accounts and contacts" : "Haldab kontode ja kontaktide vahelist suhtlust",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Koguge andmeid kontode ja kontaktidega suhtlemise kohta ning looge andmete jaoks aadressiraamat"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/eu.js b/apps/contactsinteraction/l10n/eu.js
new file mode 100644
index 00000000000..6ed0f501fc6
--- /dev/null
+++ b/apps/contactsinteraction/l10n/eu.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Berriki kontaktatuak",
+ "Contacts Interaction" : "Kontaktuen hartu-emana",
+ "Manages interaction between accounts and contacts" : "Kontu eta kontaktuen arteko interakzioa kudeatzen du",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Bildu kontu eta kontaktuen arteko interakzioen datuak eta eskaini datuentzat helbide-agenda bat"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/eu.json b/apps/contactsinteraction/l10n/eu.json
new file mode 100644
index 00000000000..9158ab6fd28
--- /dev/null
+++ b/apps/contactsinteraction/l10n/eu.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Berriki kontaktatuak",
+ "Contacts Interaction" : "Kontaktuen hartu-emana",
+ "Manages interaction between accounts and contacts" : "Kontu eta kontaktuen arteko interakzioa kudeatzen du",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Bildu kontu eta kontaktuen arteko interakzioen datuak eta eskaini datuentzat helbide-agenda bat"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/fa.js b/apps/contactsinteraction/l10n/fa.js
new file mode 100644
index 00000000000..bb31d886878
--- /dev/null
+++ b/apps/contactsinteraction/l10n/fa.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "اخیراً تماس گرفته شده است",
+ "Contacts Interaction" : "تعامل با مخاطبین"
+},
+"nplurals=2; plural=(n > 1);");
diff --git a/apps/contactsinteraction/l10n/fa.json b/apps/contactsinteraction/l10n/fa.json
new file mode 100644
index 00000000000..6ea608ce4ee
--- /dev/null
+++ b/apps/contactsinteraction/l10n/fa.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "اخیراً تماس گرفته شده است",
+ "Contacts Interaction" : "تعامل با مخاطبین"
+},"pluralForm" :"nplurals=2; plural=(n > 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/fi.js b/apps/contactsinteraction/l10n/fi.js
new file mode 100644
index 00000000000..66a9a22fc48
--- /dev/null
+++ b/apps/contactsinteraction/l10n/fi.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Äskettäin yhteydessä",
+ "Contacts Interaction" : "Yhteystietojen vuorovaikutteisuus"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/fi.json b/apps/contactsinteraction/l10n/fi.json
new file mode 100644
index 00000000000..123b2ca9692
--- /dev/null
+++ b/apps/contactsinteraction/l10n/fi.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "Äskettäin yhteydessä",
+ "Contacts Interaction" : "Yhteystietojen vuorovaikutteisuus"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/fr.js b/apps/contactsinteraction/l10n/fr.js
new file mode 100644
index 00000000000..f93ab8d71a5
--- /dev/null
+++ b/apps/contactsinteraction/l10n/fr.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Contacté récemment",
+ "Contacts Interaction" : "Interaction avec les contacts",
+ "Manages interaction between accounts and contacts" : "Gère l'interaction entre les comptes et les contacts",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Collecte des données sur les interactions entre les comptes et les contacts et fournit un répertoire pour ces données"
+},
+"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/contactsinteraction/l10n/fr.json b/apps/contactsinteraction/l10n/fr.json
new file mode 100644
index 00000000000..0f825180e8d
--- /dev/null
+++ b/apps/contactsinteraction/l10n/fr.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Contacté récemment",
+ "Contacts Interaction" : "Interaction avec les contacts",
+ "Manages interaction between accounts and contacts" : "Gère l'interaction entre les comptes et les contacts",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Collecte des données sur les interactions entre les comptes et les contacts et fournit un répertoire pour ces données"
+},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/ga.js b/apps/contactsinteraction/l10n/ga.js
new file mode 100644
index 00000000000..7e2f9f7597d
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ga.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Rinneadh teagmháil le déanaí",
+ "Contacts Interaction" : "Teagmhálaithe Idirghníomhaíocht",
+ "Manages interaction between accounts and contacts" : "Bainistíonn sé idirghníomhaíocht idir cuntais agus teagmhálaithe",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Bailigh sonraí faoi idirghníomhaíochtaí cuntais agus teagmhálaithe agus cuir leabhar seoltaí ar fáil do na sonraí"
+},
+"nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4);");
diff --git a/apps/contactsinteraction/l10n/ga.json b/apps/contactsinteraction/l10n/ga.json
new file mode 100644
index 00000000000..e10e42131d4
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ga.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Rinneadh teagmháil le déanaí",
+ "Contacts Interaction" : "Teagmhálaithe Idirghníomhaíocht",
+ "Manages interaction between accounts and contacts" : "Bainistíonn sé idirghníomhaíocht idir cuntais agus teagmhálaithe",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Bailigh sonraí faoi idirghníomhaíochtaí cuntais agus teagmhálaithe agus cuir leabhar seoltaí ar fáil do na sonraí"
+},"pluralForm" :"nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/gl.js b/apps/contactsinteraction/l10n/gl.js
new file mode 100644
index 00000000000..0c55dd9dcf6
--- /dev/null
+++ b/apps/contactsinteraction/l10n/gl.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Contactado recentemente",
+ "Contacts Interaction" : "Interacción de contactos",
+ "Manages interaction between accounts and contacts" : "Xestiona a interacción entre contas e contactos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Recolle datos sobre as interaccións entre contas e contactos e fornece un caderno de enderezos para os datos"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/gl.json b/apps/contactsinteraction/l10n/gl.json
new file mode 100644
index 00000000000..549e031cadb
--- /dev/null
+++ b/apps/contactsinteraction/l10n/gl.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Contactado recentemente",
+ "Contacts Interaction" : "Interacción de contactos",
+ "Manages interaction between accounts and contacts" : "Xestiona a interacción entre contas e contactos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Recolle datos sobre as interaccións entre contas e contactos e fornece un caderno de enderezos para os datos"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/he.js b/apps/contactsinteraction/l10n/he.js
new file mode 100644
index 00000000000..00e549dcb3c
--- /dev/null
+++ b/apps/contactsinteraction/l10n/he.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "נוצר קשר לאחרונה",
+ "Contacts Interaction" : "אינטראקציה בין אנשי קשר"
+},
+"nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;");
diff --git a/apps/contactsinteraction/l10n/he.json b/apps/contactsinteraction/l10n/he.json
new file mode 100644
index 00000000000..12c0725c8f6
--- /dev/null
+++ b/apps/contactsinteraction/l10n/he.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "נוצר קשר לאחרונה",
+ "Contacts Interaction" : "אינטראקציה בין אנשי קשר"
+},"pluralForm" :"nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/hr.js b/apps/contactsinteraction/l10n/hr.js
new file mode 100644
index 00000000000..f603cbd31a1
--- /dev/null
+++ b/apps/contactsinteraction/l10n/hr.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Nedavno kontaktiran",
+ "Contacts Interaction" : "Interakcija kontakata"
+},
+"nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;");
diff --git a/apps/contactsinteraction/l10n/hr.json b/apps/contactsinteraction/l10n/hr.json
new file mode 100644
index 00000000000..c88638bbc38
--- /dev/null
+++ b/apps/contactsinteraction/l10n/hr.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "Nedavno kontaktiran",
+ "Contacts Interaction" : "Interakcija kontakata"
+},"pluralForm" :"nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/hu.js b/apps/contactsinteraction/l10n/hu.js
new file mode 100644
index 00000000000..678fcad0300
--- /dev/null
+++ b/apps/contactsinteraction/l10n/hu.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Legutóbbi kapcsolatfelvételek",
+ "Contacts Interaction" : "Kapcsolatfelvételek",
+ "Manages interaction between accounts and contacts" : "A fiókok és a kapcsolataik közötti interakciót kezeli",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Adatokat gyűjt a fiókok és kapcsolataik közötti interakciókról, és címjegyzéket biztosít az adatokhoz"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/hu.json b/apps/contactsinteraction/l10n/hu.json
new file mode 100644
index 00000000000..e441f4ee514
--- /dev/null
+++ b/apps/contactsinteraction/l10n/hu.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Legutóbbi kapcsolatfelvételek",
+ "Contacts Interaction" : "Kapcsolatfelvételek",
+ "Manages interaction between accounts and contacts" : "A fiókok és a kapcsolataik közötti interakciót kezeli",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Adatokat gyűjt a fiókok és kapcsolataik közötti interakciókról, és címjegyzéket biztosít az adatokhoz"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/is.js b/apps/contactsinteraction/l10n/is.js
new file mode 100644
index 00000000000..8459474e2ae
--- /dev/null
+++ b/apps/contactsinteraction/l10n/is.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Nýlega haft samband við",
+ "Contacts Interaction" : "Gagnvirkni tengiliða",
+ "Manages interaction between accounts and contacts" : "Stýrir gagnvirkni milli notendaaðganga og tengiliða",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Safnaðu gögnum um samskipti milli notenda og tengiliða og gefðu upp nafnaskrá fyrir gögnin"
+},
+"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);");
diff --git a/apps/contactsinteraction/l10n/is.json b/apps/contactsinteraction/l10n/is.json
new file mode 100644
index 00000000000..cba457a68d0
--- /dev/null
+++ b/apps/contactsinteraction/l10n/is.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Nýlega haft samband við",
+ "Contacts Interaction" : "Gagnvirkni tengiliða",
+ "Manages interaction between accounts and contacts" : "Stýrir gagnvirkni milli notendaaðganga og tengiliða",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Safnaðu gögnum um samskipti milli notenda og tengiliða og gefðu upp nafnaskrá fyrir gögnin"
+},"pluralForm" :"nplurals=2; plural=(n % 10 != 1 || n % 100 == 11);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/it.js b/apps/contactsinteraction/l10n/it.js
new file mode 100644
index 00000000000..f53062b34a0
--- /dev/null
+++ b/apps/contactsinteraction/l10n/it.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Contattati di recente",
+ "Contacts Interaction" : "Interazione contatti",
+ "Manages interaction between accounts and contacts" : "Gestisce l'interazione tra account e contatti",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Raccoglie dati sulle interazioni tra gli account e i contatti e fornisce una rubrica per i dati"
+},
+"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/contactsinteraction/l10n/it.json b/apps/contactsinteraction/l10n/it.json
new file mode 100644
index 00000000000..84425c5b052
--- /dev/null
+++ b/apps/contactsinteraction/l10n/it.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Contattati di recente",
+ "Contacts Interaction" : "Interazione contatti",
+ "Manages interaction between accounts and contacts" : "Gestisce l'interazione tra account e contatti",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Raccoglie dati sulle interazioni tra gli account e i contatti e fornisce una rubrica per i dati"
+},"pluralForm" :"nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/ja.js b/apps/contactsinteraction/l10n/ja.js
new file mode 100644
index 00000000000..78b56805ae1
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ja.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "最近連絡した",
+ "Contacts Interaction" : "連絡先のやり取り",
+ "Manages interaction between accounts and contacts" : "アカウントと連絡先間のやり取りを管理します",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "アカウントと連絡先のやり取りに関するデータを収集し、データのアドレス帳を提供します"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/contactsinteraction/l10n/ja.json b/apps/contactsinteraction/l10n/ja.json
new file mode 100644
index 00000000000..9a533d7958c
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ja.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "最近連絡した",
+ "Contacts Interaction" : "連絡先のやり取り",
+ "Manages interaction between accounts and contacts" : "アカウントと連絡先間のやり取りを管理します",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "アカウントと連絡先のやり取りに関するデータを収集し、データのアドレス帳を提供します"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/ko.js b/apps/contactsinteraction/l10n/ko.js
new file mode 100644
index 00000000000..96371895d1f
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ko.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "최근 연락함",
+ "Contacts Interaction" : "Contacts Interaction",
+ "Manages interaction between accounts and contacts" : "계정 및 연락처 간 상호작용을 관리합니다",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "계정 및 연락처 간 상호작용에 관한 데이터를 수집하고, 데이터 주소록을 제공합니다"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/contactsinteraction/l10n/ko.json b/apps/contactsinteraction/l10n/ko.json
new file mode 100644
index 00000000000..e0246c3418d
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ko.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "최근 연락함",
+ "Contacts Interaction" : "Contacts Interaction",
+ "Manages interaction between accounts and contacts" : "계정 및 연락처 간 상호작용을 관리합니다",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "계정 및 연락처 간 상호작용에 관한 데이터를 수집하고, 데이터 주소록을 제공합니다"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/lt_LT.js b/apps/contactsinteraction/l10n/lt_LT.js
new file mode 100644
index 00000000000..b7ad175d8d0
--- /dev/null
+++ b/apps/contactsinteraction/l10n/lt_LT.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Paskiausiai susisiekta",
+ "Contacts Interaction" : "Sąveika su adresatais"
+},
+"nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);");
diff --git a/apps/contactsinteraction/l10n/lt_LT.json b/apps/contactsinteraction/l10n/lt_LT.json
new file mode 100644
index 00000000000..a55a9a38e7e
--- /dev/null
+++ b/apps/contactsinteraction/l10n/lt_LT.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "Paskiausiai susisiekta",
+ "Contacts Interaction" : "Sąveika su adresatais"
+},"pluralForm" :"nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/mk.js b/apps/contactsinteraction/l10n/mk.js
new file mode 100644
index 00000000000..9db757f1ea8
--- /dev/null
+++ b/apps/contactsinteraction/l10n/mk.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Неодамна контактирани",
+ "Contacts Interaction" : "Интеракција на контакти"
+},
+"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;");
diff --git a/apps/contactsinteraction/l10n/mk.json b/apps/contactsinteraction/l10n/mk.json
new file mode 100644
index 00000000000..52d1444c1b6
--- /dev/null
+++ b/apps/contactsinteraction/l10n/mk.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "Неодамна контактирани",
+ "Contacts Interaction" : "Интеракција на контакти"
+},"pluralForm" :"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/nb.js b/apps/contactsinteraction/l10n/nb.js
new file mode 100644
index 00000000000..d6386ebf3ff
--- /dev/null
+++ b/apps/contactsinteraction/l10n/nb.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Nylig kontaktet",
+ "Contacts Interaction" : "Interaksjon med kontakter",
+ "Manages interaction between accounts and contacts" : "Administrerer interaksjon mellom kontoer og kontakter",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Samle inn data om kontoer- og kontaktinteraksjoner og oppgi en adressebok for dataene"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/nb.json b/apps/contactsinteraction/l10n/nb.json
new file mode 100644
index 00000000000..e2e6d73a232
--- /dev/null
+++ b/apps/contactsinteraction/l10n/nb.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Nylig kontaktet",
+ "Contacts Interaction" : "Interaksjon med kontakter",
+ "Manages interaction between accounts and contacts" : "Administrerer interaksjon mellom kontoer og kontakter",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Samle inn data om kontoer- og kontaktinteraksjoner og oppgi en adressebok for dataene"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/nl.js b/apps/contactsinteraction/l10n/nl.js
new file mode 100644
index 00000000000..3c33fb3c464
--- /dev/null
+++ b/apps/contactsinteraction/l10n/nl.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Recentelijk gecontacteerd",
+ "Contacts Interaction" : "Contactpersoneninteractie",
+ "Manages interaction between accounts and contacts" : "Beheert de interactie tussen accounts en contacten",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Verzamel gegevens over interacties van accounts en contacten en bied een adresboek voor de gegevens"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/nl.json b/apps/contactsinteraction/l10n/nl.json
new file mode 100644
index 00000000000..648a52aa99d
--- /dev/null
+++ b/apps/contactsinteraction/l10n/nl.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Recentelijk gecontacteerd",
+ "Contacts Interaction" : "Contactpersoneninteractie",
+ "Manages interaction between accounts and contacts" : "Beheert de interactie tussen accounts en contacten",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Verzamel gegevens over interacties van accounts en contacten en bied een adresboek voor de gegevens"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/pl.js b/apps/contactsinteraction/l10n/pl.js
new file mode 100644
index 00000000000..73dc7e4b0f0
--- /dev/null
+++ b/apps/contactsinteraction/l10n/pl.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Ostatnio skontaktowano się",
+ "Contacts Interaction" : "Interakcja kontaktów",
+ "Manages interaction between accounts and contacts" : "Zarządza interakcją pomiędzy kontami i kontaktami",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Zbieraj dane o kontach i interakcjach kontaktów oraz udostępniaj książkę adresową dla danych"
+},
+"nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);");
diff --git a/apps/contactsinteraction/l10n/pl.json b/apps/contactsinteraction/l10n/pl.json
new file mode 100644
index 00000000000..9b138464e47
--- /dev/null
+++ b/apps/contactsinteraction/l10n/pl.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Ostatnio skontaktowano się",
+ "Contacts Interaction" : "Interakcja kontaktów",
+ "Manages interaction between accounts and contacts" : "Zarządza interakcją pomiędzy kontami i kontaktami",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Zbieraj dane o kontach i interakcjach kontaktów oraz udostępniaj książkę adresową dla danych"
+},"pluralForm" :"nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/pt_BR.js b/apps/contactsinteraction/l10n/pt_BR.js
new file mode 100644
index 00000000000..3f4e936105c
--- /dev/null
+++ b/apps/contactsinteraction/l10n/pt_BR.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Contactados recentemente",
+ "Contacts Interaction" : "Interação de Contatos",
+ "Manages interaction between accounts and contacts" : "Gerencia a interação entre contas e contatos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Colete dados sobre interações de contas e contatos e forneça um catálogo de endereços para os dados"
+},
+"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;");
diff --git a/apps/contactsinteraction/l10n/pt_BR.json b/apps/contactsinteraction/l10n/pt_BR.json
new file mode 100644
index 00000000000..cd174f79e6e
--- /dev/null
+++ b/apps/contactsinteraction/l10n/pt_BR.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Contactados recentemente",
+ "Contacts Interaction" : "Interação de Contatos",
+ "Manages interaction between accounts and contacts" : "Gerencia a interação entre contas e contatos",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Colete dados sobre interações de contas e contatos e forneça um catálogo de endereços para os dados"
+},"pluralForm" :"nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/ro.js b/apps/contactsinteraction/l10n/ro.js
new file mode 100644
index 00000000000..d2124348808
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ro.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Contactat recent",
+ "Contacts Interaction" : "Interacțiunea contactelor"
+},
+"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));");
diff --git a/apps/contactsinteraction/l10n/ro.json b/apps/contactsinteraction/l10n/ro.json
new file mode 100644
index 00000000000..7d93f0e82ed
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ro.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "Contactat recent",
+ "Contacts Interaction" : "Interacțiunea contactelor"
+},"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/ru.js b/apps/contactsinteraction/l10n/ru.js
new file mode 100644
index 00000000000..034c32a7d31
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ru.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Недавние контакты",
+ "Contacts Interaction" : "Взаимодействие с контактами",
+ "Manages interaction between accounts and contacts" : "Управляет взаимодействием между учетными записями и контактами",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Соберите данные об учетных записях и контактах, взаимодействиях и предоставьте адресную книгу для хранения этих данных"
+},
+"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);");
diff --git a/apps/contactsinteraction/l10n/ru.json b/apps/contactsinteraction/l10n/ru.json
new file mode 100644
index 00000000000..6866e2c7a4b
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ru.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Недавние контакты",
+ "Contacts Interaction" : "Взаимодействие с контактами",
+ "Manages interaction between accounts and contacts" : "Управляет взаимодействием между учетными записями и контактами",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Соберите данные об учетных записях и контактах, взаимодействиях и предоставьте адресную книгу для хранения этих данных"
+},"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/sc.js b/apps/contactsinteraction/l10n/sc.js
new file mode 100644
index 00000000000..df8b07cf3bc
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sc.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Cuntatos reghentes",
+ "Contacts Interaction" : "Interatzione cun is cuntatos"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/sc.json b/apps/contactsinteraction/l10n/sc.json
new file mode 100644
index 00000000000..c07afdbb45c
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sc.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "Cuntatos reghentes",
+ "Contacts Interaction" : "Interatzione cun is cuntatos"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/sk.js b/apps/contactsinteraction/l10n/sk.js
new file mode 100644
index 00000000000..b4c23fd8c83
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sk.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Nedávno kontaktovaní",
+ "Contacts Interaction" : "Napojenia na kontakty",
+ "Manages interaction between accounts and contacts" : "Spravuje interakciu medzi účtami a kontaktmi",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Zhromaždiť údaje o interakciách používateľov a kontaktov a poskytne adresár pre údaje"
+},
+"nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);");
diff --git a/apps/contactsinteraction/l10n/sk.json b/apps/contactsinteraction/l10n/sk.json
new file mode 100644
index 00000000000..6384cf93f6f
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sk.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Nedávno kontaktovaní",
+ "Contacts Interaction" : "Napojenia na kontakty",
+ "Manages interaction between accounts and contacts" : "Spravuje interakciu medzi účtami a kontaktmi",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Zhromaždiť údaje o interakciách používateľov a kontaktov a poskytne adresár pre údaje"
+},"pluralForm" :"nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/sl.js b/apps/contactsinteraction/l10n/sl.js
new file mode 100644
index 00000000000..4278e968572
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sl.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Nedavno v stiku",
+ "Contacts Interaction" : "Povezave stikov",
+ "Manages interaction between accounts and contacts" : "Upravlja povezave med uporabniki in stiki",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Zbiranje podatkov o povezavah med uporabniki in stiki ter pripravljanje imenika zbranih podatkov povezav."
+},
+"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);");
diff --git a/apps/contactsinteraction/l10n/sl.json b/apps/contactsinteraction/l10n/sl.json
new file mode 100644
index 00000000000..daeed4f651d
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sl.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Nedavno v stiku",
+ "Contacts Interaction" : "Povezave stikov",
+ "Manages interaction between accounts and contacts" : "Upravlja povezave med uporabniki in stiki",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Zbiranje podatkov o povezavah med uporabniki in stiki ter pripravljanje imenika zbranih podatkov povezav."
+},"pluralForm" :"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/sr.js b/apps/contactsinteraction/l10n/sr.js
new file mode 100644
index 00000000000..1bd2056371b
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sr.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Недавно контактирани",
+ "Contacts Interaction" : "Интеракција контаката",
+ "Manages interaction between accounts and contacts" : "Управља интеракцијом између налога и контаката",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Прикупља податке о интеракцији налога и контаката и обезбеђује адресар за податке"
+},
+"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);");
diff --git a/apps/contactsinteraction/l10n/sr.json b/apps/contactsinteraction/l10n/sr.json
new file mode 100644
index 00000000000..b3609285df8
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sr.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Недавно контактирани",
+ "Contacts Interaction" : "Интеракција контаката",
+ "Manages interaction between accounts and contacts" : "Управља интеракцијом између налога и контаката",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Прикупља податке о интеракцији налога и контаката и обезбеђује адресар за податке"
+},"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/sv.js b/apps/contactsinteraction/l10n/sv.js
new file mode 100644
index 00000000000..481441d79ec
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sv.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Nyligen kontaktade",
+ "Contacts Interaction" : "Kontaktinteraktion",
+ "Manages interaction between accounts and contacts" : "Hanterar interaktion mellan konton och kontakter",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Samla in data om konton och kontaktinteraktioner och tillhandahåll en adressbok för data"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/sv.json b/apps/contactsinteraction/l10n/sv.json
new file mode 100644
index 00000000000..ce4bf5d588f
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sv.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Nyligen kontaktade",
+ "Contacts Interaction" : "Kontaktinteraktion",
+ "Manages interaction between accounts and contacts" : "Hanterar interaktion mellan konton och kontakter",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Samla in data om konton och kontaktinteraktioner och tillhandahåll en adressbok för data"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/sw.js b/apps/contactsinteraction/l10n/sw.js
new file mode 100644
index 00000000000..254fc00e757
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sw.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Recently contacted",
+ "Contacts Interaction" : "Contacts Interaction",
+ "Manages interaction between accounts and contacts" : "Manages interaction between accounts and contacts",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Collect data about accounts and contacts interactions and provide an address book for the data"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/sw.json b/apps/contactsinteraction/l10n/sw.json
new file mode 100644
index 00000000000..660eac4583a
--- /dev/null
+++ b/apps/contactsinteraction/l10n/sw.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Recently contacted",
+ "Contacts Interaction" : "Contacts Interaction",
+ "Manages interaction between accounts and contacts" : "Manages interaction between accounts and contacts",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Collect data about accounts and contacts interactions and provide an address book for the data"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/tr.js b/apps/contactsinteraction/l10n/tr.js
new file mode 100644
index 00000000000..6e83f79a528
--- /dev/null
+++ b/apps/contactsinteraction/l10n/tr.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Son görüşülenler",
+ "Contacts Interaction" : "Kişi etkileşimi",
+ "Manages interaction between accounts and contacts" : "Hesaplar ve kişiler arasındaki etkileşimi yönetir",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Hesap ve kişi etkileşimleri ile ilgili verileri toplar ve veriler için bir adres defteri sağlar"
+},
+"nplurals=2; plural=(n > 1);");
diff --git a/apps/contactsinteraction/l10n/tr.json b/apps/contactsinteraction/l10n/tr.json
new file mode 100644
index 00000000000..5234181b5c9
--- /dev/null
+++ b/apps/contactsinteraction/l10n/tr.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Son görüşülenler",
+ "Contacts Interaction" : "Kişi etkileşimi",
+ "Manages interaction between accounts and contacts" : "Hesaplar ve kişiler arasındaki etkileşimi yönetir",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Hesap ve kişi etkileşimleri ile ilgili verileri toplar ve veriler için bir adres defteri sağlar"
+},"pluralForm" :"nplurals=2; plural=(n > 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/ug.js b/apps/contactsinteraction/l10n/ug.js
new file mode 100644
index 00000000000..fc33be1787a
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ug.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "يېقىندا ئالاقىلاشتى",
+ "Contacts Interaction" : "ئالاقىلىشىش",
+ "Manages interaction between accounts and contacts" : "ھېسابات ۋە ئالاقىلەر ئارىسىدىكى ئالاقىنى باشقۇرىدۇ",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "ھېسابات ۋە ئالاقىلىشىش ھەققىدىكى ئۇچۇرلارنى توپلاڭ ۋە سانلىق مەلۇمات ئۈچۈن ئادرېس دەپتىرى بىلەن تەمىنلەڭ"
+},
+"nplurals=2; plural=(n != 1);");
diff --git a/apps/contactsinteraction/l10n/ug.json b/apps/contactsinteraction/l10n/ug.json
new file mode 100644
index 00000000000..c3b2921e0a9
--- /dev/null
+++ b/apps/contactsinteraction/l10n/ug.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "يېقىندا ئالاقىلاشتى",
+ "Contacts Interaction" : "ئالاقىلىشىش",
+ "Manages interaction between accounts and contacts" : "ھېسابات ۋە ئالاقىلەر ئارىسىدىكى ئالاقىنى باشقۇرىدۇ",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "ھېسابات ۋە ئالاقىلىشىش ھەققىدىكى ئۇچۇرلارنى توپلاڭ ۋە سانلىق مەلۇمات ئۈچۈن ئادرېس دەپتىرى بىلەن تەمىنلەڭ"
+},"pluralForm" :"nplurals=2; plural=(n != 1);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/uk.js b/apps/contactsinteraction/l10n/uk.js
new file mode 100644
index 00000000000..422d5c77904
--- /dev/null
+++ b/apps/contactsinteraction/l10n/uk.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Нещодавно спілкувалися",
+ "Contacts Interaction" : "Взаємодія з контактами",
+ "Manages interaction between accounts and contacts" : "Керує взаємодією між обліковими записами та контактами",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Збирати дані про облікові записи та взаємодію з контактами, надавати дані адресної книги"
+},
+"nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);");
diff --git a/apps/contactsinteraction/l10n/uk.json b/apps/contactsinteraction/l10n/uk.json
new file mode 100644
index 00000000000..94d1adde72a
--- /dev/null
+++ b/apps/contactsinteraction/l10n/uk.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "Нещодавно спілкувалися",
+ "Contacts Interaction" : "Взаємодія з контактами",
+ "Manages interaction between accounts and contacts" : "Керує взаємодією між обліковими записами та контактами",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "Збирати дані про облікові записи та взаємодію з контактами, надавати дані адресної книги"
+},"pluralForm" :"nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/vi.js b/apps/contactsinteraction/l10n/vi.js
new file mode 100644
index 00000000000..2e909281ad3
--- /dev/null
+++ b/apps/contactsinteraction/l10n/vi.js
@@ -0,0 +1,7 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "Liên hệ gần đây",
+ "Contacts Interaction" : "Liên hệ tương tác"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/contactsinteraction/l10n/vi.json b/apps/contactsinteraction/l10n/vi.json
new file mode 100644
index 00000000000..10e2cf38bef
--- /dev/null
+++ b/apps/contactsinteraction/l10n/vi.json
@@ -0,0 +1,5 @@
+{ "translations": {
+ "Recently contacted" : "Liên hệ gần đây",
+ "Contacts Interaction" : "Liên hệ tương tác"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/zh_CN.js b/apps/contactsinteraction/l10n/zh_CN.js
new file mode 100644
index 00000000000..0e0b5413278
--- /dev/null
+++ b/apps/contactsinteraction/l10n/zh_CN.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "最近联系过的",
+ "Contacts Interaction" : "联系人互动",
+ "Manages interaction between accounts and contacts" : "管理用户和联系人之间的互动",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "收集关于用户和联系人互动的数据,并提供该数据的地址簿"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/contactsinteraction/l10n/zh_CN.json b/apps/contactsinteraction/l10n/zh_CN.json
new file mode 100644
index 00000000000..1b11ce9ef27
--- /dev/null
+++ b/apps/contactsinteraction/l10n/zh_CN.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "最近联系过的",
+ "Contacts Interaction" : "联系人互动",
+ "Manages interaction between accounts and contacts" : "管理用户和联系人之间的互动",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "收集关于用户和联系人互动的数据,并提供该数据的地址簿"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/zh_HK.js b/apps/contactsinteraction/l10n/zh_HK.js
new file mode 100644
index 00000000000..0276a95eb69
--- /dev/null
+++ b/apps/contactsinteraction/l10n/zh_HK.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "最近聯絡",
+ "Contacts Interaction" : "聯絡人互動",
+ "Manages interaction between accounts and contacts" : "管理帳戶與聯絡人間的互動",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "蒐集關於帳戶與聯絡人互動的資料,並提供資料的通訊錄"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/contactsinteraction/l10n/zh_HK.json b/apps/contactsinteraction/l10n/zh_HK.json
new file mode 100644
index 00000000000..71186459879
--- /dev/null
+++ b/apps/contactsinteraction/l10n/zh_HK.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "最近聯絡",
+ "Contacts Interaction" : "聯絡人互動",
+ "Manages interaction between accounts and contacts" : "管理帳戶與聯絡人間的互動",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "蒐集關於帳戶與聯絡人互動的資料,並提供資料的通訊錄"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/l10n/zh_TW.js b/apps/contactsinteraction/l10n/zh_TW.js
new file mode 100644
index 00000000000..48d931c2214
--- /dev/null
+++ b/apps/contactsinteraction/l10n/zh_TW.js
@@ -0,0 +1,9 @@
+OC.L10N.register(
+ "contactsinteraction",
+ {
+ "Recently contacted" : "最近聯絡",
+ "Contacts Interaction" : "聯絡人互動",
+ "Manages interaction between accounts and contacts" : "管理帳號與聯絡人間的互動",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "蒐集關於帳號與聯絡人互動的資料,並提供資料的通訊錄"
+},
+"nplurals=1; plural=0;");
diff --git a/apps/contactsinteraction/l10n/zh_TW.json b/apps/contactsinteraction/l10n/zh_TW.json
new file mode 100644
index 00000000000..696d2611991
--- /dev/null
+++ b/apps/contactsinteraction/l10n/zh_TW.json
@@ -0,0 +1,7 @@
+{ "translations": {
+ "Recently contacted" : "最近聯絡",
+ "Contacts Interaction" : "聯絡人互動",
+ "Manages interaction between accounts and contacts" : "管理帳號與聯絡人間的互動",
+ "Collect data about accounts and contacts interactions and provide an address book for the data" : "蒐集關於帳號與聯絡人互動的資料,並提供資料的通訊錄"
+},"pluralForm" :"nplurals=1; plural=0;"
+} \ No newline at end of file
diff --git a/apps/contactsinteraction/lib/AddressBook.php b/apps/contactsinteraction/lib/AddressBook.php
new file mode 100644
index 00000000000..60c9eccece3
--- /dev/null
+++ b/apps/contactsinteraction/lib/AddressBook.php
@@ -0,0 +1,151 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction;
+
+use Exception;
+use OCA\ContactsInteraction\AppInfo\Application;
+use OCA\ContactsInteraction\Db\RecentContact;
+use OCA\ContactsInteraction\Db\RecentContactMapper;
+use OCA\DAV\CardDAV\Integration\ExternalAddressBook;
+use OCA\DAV\DAV\Sharing\Plugin;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\IL10N;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\PropPatch;
+use Sabre\DAVACL\ACLTrait;
+use Sabre\DAVACL\IACL;
+
+class AddressBook extends ExternalAddressBook implements IACL {
+ use ACLTrait;
+
+ public const URI = 'recent';
+
+ public function __construct(
+ private RecentContactMapper $mapper,
+ private IL10N $l10n,
+ private string $principalUri,
+ ) {
+ parent::__construct(Application::APP_ID, self::URI);
+ }
+
+ /**
+ * @inheritDoc
+ * @throws Exception
+ */
+ public function delete(): void {
+ throw new Exception('This addressbook is immutable');
+ }
+
+ /**
+ * @inheritDoc
+ * @throws Exception
+ */
+ public function createFile($name, $data = null) {
+ throw new Exception('This addressbook is immutable');
+ }
+
+ /**
+ * @inheritDoc
+ * @throws NotFound
+ */
+ public function getChild($name): Card {
+ try {
+ return new Card(
+ $this->mapper->find(
+ $this->getUid(),
+ (int)$name
+ ),
+ $this->principalUri,
+ $this->getACL()
+ );
+ } catch (DoesNotExistException $ex) {
+ throw new NotFound('Contact does not exist: ' . $ex->getMessage(), 0, $ex);
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getChildren(): array {
+ return array_map(
+ function (RecentContact $contact) {
+ return new Card(
+ $contact,
+ $this->principalUri,
+ $this->getACL()
+ );
+ },
+ $this->mapper->findAll($this->getUid())
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function childExists($name): bool {
+ try {
+ $this->mapper->find(
+ $this->getUid(),
+ (int)$name
+ );
+ return true;
+ } catch (DoesNotExistException $e) {
+ return false;
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLastModified(): ?int {
+ return $this->mapper->findLastUpdatedForUserId($this->getUid());
+ }
+
+ /**
+ * @inheritDoc
+ * @throws Exception
+ */
+ public function propPatch(PropPatch $propPatch) {
+ throw new Exception('This addressbook is immutable');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getProperties($properties): array {
+ return [
+ 'principaluri' => $this->principalUri,
+ '{DAV:}displayname' => $this->l10n->t('Recently contacted'),
+ '{' . Plugin::NS_OWNCLOUD . '}read-only' => true,
+ '{' . \OCA\DAV\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($this->getLastModified() ?? 0),
+ ];
+ }
+
+ public function getOwner(): string {
+ return $this->principalUri;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getACL(): array {
+ return [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $this->getOwner(),
+ 'protected' => true,
+ ],
+ ];
+ }
+
+ private function getUid(): string {
+ [, $uid] = \Sabre\Uri\split($this->principalUri);
+ return $uid;
+ }
+}
diff --git a/apps/contactsinteraction/lib/AddressBookProvider.php b/apps/contactsinteraction/lib/AddressBookProvider.php
new file mode 100644
index 00000000000..bf85409a143
--- /dev/null
+++ b/apps/contactsinteraction/lib/AddressBookProvider.php
@@ -0,0 +1,58 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction;
+
+use OCA\ContactsInteraction\AppInfo\Application;
+use OCA\ContactsInteraction\Db\RecentContactMapper;
+use OCA\DAV\CardDAV\Integration\ExternalAddressBook;
+use OCA\DAV\CardDAV\Integration\IAddressBookProvider;
+use OCP\IL10N;
+
+class AddressBookProvider implements IAddressBookProvider {
+
+ public function __construct(
+ private RecentContactMapper $mapper,
+ private IL10N $l10n,
+ ) {
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getAppId(): string {
+ return Application::APP_ID;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function fetchAllForAddressBookHome(string $principalUri): array {
+ return [
+ new AddressBook($this->mapper, $this->l10n, $principalUri)
+ ];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function hasAddressBookInAddressBookHome(string $principalUri, string $uri): bool {
+ return $uri === AddressBook::URI;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getAddressBookInAddressBookHome(string $principalUri, string $uri): ?ExternalAddressBook {
+ if ($uri === AddressBook::URI) {
+ return new AddressBook($this->mapper, $this->l10n, $principalUri);
+ }
+
+ return null;
+ }
+}
diff --git a/apps/contactsinteraction/lib/AppInfo/Application.php b/apps/contactsinteraction/lib/AppInfo/Application.php
new file mode 100644
index 00000000000..b844ee1699c
--- /dev/null
+++ b/apps/contactsinteraction/lib/AppInfo/Application.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction\AppInfo;
+
+use OCA\ContactsInteraction\Listeners\ContactInteractionListener;
+use OCP\AppFramework\App;
+use OCP\AppFramework\Bootstrap\IBootContext;
+use OCP\AppFramework\Bootstrap\IBootstrap;
+use OCP\AppFramework\Bootstrap\IRegistrationContext;
+use OCP\Contacts\Events\ContactInteractedWithEvent;
+
+class Application extends App implements IBootstrap {
+ public const APP_ID = 'contactsinteraction';
+
+ public function __construct() {
+ parent::__construct(self::APP_ID);
+ }
+
+ public function register(IRegistrationContext $context): void {
+ $context->registerEventListener(ContactInteractedWithEvent::class, ContactInteractionListener::class);
+ }
+
+ public function boot(IBootContext $context): void {
+ }
+}
diff --git a/apps/contactsinteraction/lib/BackgroundJob/CleanupJob.php b/apps/contactsinteraction/lib/BackgroundJob/CleanupJob.php
new file mode 100644
index 00000000000..b89a2c1fbb3
--- /dev/null
+++ b/apps/contactsinteraction/lib/BackgroundJob/CleanupJob.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction\BackgroundJob;
+
+use OCA\ContactsInteraction\Db\RecentContactMapper;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
+
+class CleanupJob extends TimedJob {
+
+ public function __construct(
+ ITimeFactory $time,
+ private RecentContactMapper $mapper,
+ ) {
+ parent::__construct($time);
+
+ $this->setInterval(24 * 60 * 60);
+ $this->setTimeSensitivity(self::TIME_INSENSITIVE);
+
+ }
+
+ protected function run(mixed $argument): void {
+ $time = $this->time->getDateTime();
+ $time->modify('-7days');
+ $this->mapper->cleanUp($time->getTimestamp());
+ }
+}
diff --git a/apps/contactsinteraction/lib/Card.php b/apps/contactsinteraction/lib/Card.php
new file mode 100644
index 00000000000..bf0acca7bd5
--- /dev/null
+++ b/apps/contactsinteraction/lib/Card.php
@@ -0,0 +1,110 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction;
+
+use OCA\ContactsInteraction\Db\RecentContact;
+use Sabre\CardDAV\ICard;
+use Sabre\DAV\Exception\NotImplemented;
+use Sabre\DAVACL\ACLTrait;
+use Sabre\DAVACL\IACL;
+
+class Card implements ICard, IACL {
+ use ACLTrait;
+
+ public function __construct(
+ private RecentContact $contact,
+ private string $principal,
+ private array $acls,
+ ) {
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getOwner(): ?string {
+ return $this->principal;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getACL(): array {
+ return $this->acls;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setAcls(array $acls): void {
+ throw new NotImplemented();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function put($data): ?string {
+ throw new NotImplemented();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function get(): string {
+ return $this->contact->getCard();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getContentType(): ?string {
+ return 'text/vcard; charset=utf-8';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getETag(): ?string {
+ return '"' . md5((string)$this->getLastModified()) . '"';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getSize(): int {
+ return strlen($this->contact->getCard());
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function delete(): void {
+ throw new NotImplemented();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getName(): string {
+ return (string)$this->contact->getId();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function setName($name): void {
+ throw new NotImplemented();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getLastModified(): ?int {
+ return $this->contact->getLastContact();
+ }
+}
diff --git a/apps/contactsinteraction/lib/Db/CardSearchDao.php b/apps/contactsinteraction/lib/Db/CardSearchDao.php
new file mode 100644
index 00000000000..b1dadd6cfbc
--- /dev/null
+++ b/apps/contactsinteraction/lib/Db/CardSearchDao.php
@@ -0,0 +1,81 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction\Db;
+
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\IUser;
+use function is_resource;
+use function stream_get_contents;
+
+class CardSearchDao {
+
+ public function __construct(
+ private IDBConnection $db,
+ ) {
+ }
+
+ public function findExisting(IUser $user,
+ ?string $uid,
+ ?string $email,
+ ?string $cloudId): ?string {
+ $addressbooksQuery = $this->db->getQueryBuilder();
+ $cardQuery = $this->db->getQueryBuilder();
+ $propQuery = $this->db->getQueryBuilder();
+
+ $additionalWheres = [];
+ if ($uid !== null) {
+ $additionalWheres[] = $propQuery->expr()->andX(
+ $propQuery->expr()->eq('name', $cardQuery->createNamedParameter('UID')),
+ $propQuery->expr()->eq('value', $cardQuery->createNamedParameter($uid))
+ );
+ }
+ if ($email !== null) {
+ $additionalWheres[] = $propQuery->expr()->andX(
+ $propQuery->expr()->eq('name', $cardQuery->createNamedParameter('EMAIL')),
+ $propQuery->expr()->eq('value', $cardQuery->createNamedParameter($email))
+ );
+ }
+ if ($cloudId !== null) {
+ $additionalWheres[] = $propQuery->expr()->andX(
+ $propQuery->expr()->eq('name', $cardQuery->createNamedParameter('CLOUD')),
+ $propQuery->expr()->eq('value', $cardQuery->createNamedParameter($cloudId))
+ );
+ }
+ $addressbooksQuery->selectDistinct('id')
+ ->from('addressbooks')
+ ->where($addressbooksQuery->expr()->eq('principaluri', $cardQuery->createNamedParameter('principals/users/' . $user->getUID())));
+ $propQuery->selectDistinct('cardid')
+ ->from('cards_properties')
+ ->where($propQuery->expr()->in('addressbookid', $propQuery->createFunction($addressbooksQuery->getSQL()), IQueryBuilder::PARAM_INT_ARRAY))
+ ->groupBy('cardid');
+
+ if (!empty($additionalWheres)) {
+ $propQuery->andWhere($propQuery->expr()->orX(...$additionalWheres));
+ }
+
+ $cardQuery->select('carddata')
+ ->from('cards')
+ ->where($cardQuery->expr()->in('id', $cardQuery->createFunction($propQuery->getSQL()), IQueryBuilder::PARAM_INT_ARRAY))
+ ->andWhere($cardQuery->expr()->in('addressbookid', $cardQuery->createFunction($addressbooksQuery->getSQL()), IQueryBuilder::PARAM_INT_ARRAY))
+ ->setMaxResults(1);
+ $result = $cardQuery->executeQuery();
+ /** @var string|resource|false $card */
+ $card = $result->fetchOne();
+
+ if ($card === false) {
+ return null;
+ }
+ if (is_resource($card)) {
+ return stream_get_contents($card);
+ }
+
+ return $card;
+ }
+}
diff --git a/apps/contactsinteraction/lib/Db/RecentContact.php b/apps/contactsinteraction/lib/Db/RecentContact.php
new file mode 100644
index 00000000000..fc6ec3cc249
--- /dev/null
+++ b/apps/contactsinteraction/lib/Db/RecentContact.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction\Db;
+
+use OCP\AppFramework\Db\Entity;
+use OCP\DB\Types;
+
+/**
+ * @method void setActorUid(string $uid)
+ * @method string|null getActorUid()
+ * @method void setUid(string $uid)
+ * @method string|null getUid()
+ * @method void setEmail(string $email)
+ * @method string|null getEmail()
+ * @method void setFederatedCloudId(string $federatedCloudId)
+ * @method string|null getFederatedCloudId()
+ * @method void setCard(string $card)
+ * @method string getCard()
+ * @method void setLastContact(int $lastContact)
+ * @method int getLastContact()
+ */
+class RecentContact extends Entity {
+ protected string $actorUid = '';
+ protected ?string $uid = null;
+ protected ?string $email = null;
+ protected ?string $federatedCloudId = null;
+ protected string $card = '';
+ protected int $lastContact = -1;
+
+ public function __construct() {
+ $this->addType('actorUid', Types::STRING);
+ $this->addType('uid', Types::STRING);
+ $this->addType('email', Types::STRING);
+ $this->addType('federatedCloudId', Types::STRING);
+ $this->addType('card', Types::BLOB);
+ $this->addType('lastContact', Types::INTEGER);
+ }
+}
diff --git a/apps/contactsinteraction/lib/Db/RecentContactMapper.php b/apps/contactsinteraction/lib/Db/RecentContactMapper.php
new file mode 100644
index 00000000000..c835b5287c8
--- /dev/null
+++ b/apps/contactsinteraction/lib/Db/RecentContactMapper.php
@@ -0,0 +1,115 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction\Db;
+
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\AppFramework\Db\QBMapper;
+use OCP\IDBConnection;
+use OCP\IUser;
+
+/**
+ * @template-extends QBMapper<RecentContact>
+ */
+class RecentContactMapper extends QBMapper {
+ public const TABLE_NAME = 'recent_contact';
+
+ public function __construct(IDBConnection $db) {
+ parent::__construct($db, self::TABLE_NAME);
+ }
+
+ /**
+ * @return RecentContact[]
+ */
+ public function findAll(string $uid): array {
+ $qb = $this->db->getQueryBuilder();
+
+ $select = $qb
+ ->select('*')
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
+
+ return $this->findEntities($select);
+ }
+
+ /**
+ * @throws DoesNotExistException
+ */
+ public function find(string $uid, int $id): RecentContact {
+ $qb = $this->db->getQueryBuilder();
+
+ $select = $qb
+ ->select('*')
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, $qb::PARAM_INT)))
+ ->andWhere($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)));
+
+ return $this->findEntity($select);
+ }
+
+ /**
+ * @return RecentContact[]
+ */
+ public function findMatch(IUser $user,
+ ?string $uid,
+ ?string $email,
+ ?string $cloudId): array {
+ $qb = $this->db->getQueryBuilder();
+
+ $additionalWheres = [];
+ if ($uid !== null) {
+ $additionalWheres[] = $qb->expr()->eq('uid', $qb->createNamedParameter($uid));
+ }
+ if ($email !== null) {
+ $additionalWheres[] = $qb->expr()->eq('email', $qb->createNamedParameter($email));
+ }
+ if ($cloudId !== null) {
+ $additionalWheres[] = $qb->expr()->eq('federated_cloud_id', $qb->createNamedParameter($cloudId));
+ }
+
+ $select = $qb
+ ->select('*')
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($user->getUID())));
+
+ if (!empty($additionalWheres)) {
+ $select->andWhere($select->expr()->orX(...$additionalWheres));
+ }
+ return $this->findEntities($select);
+ }
+
+ public function findLastUpdatedForUserId(string $uid): ?int {
+ $qb = $this->db->getQueryBuilder();
+
+ $select = $qb
+ ->select('last_contact')
+ ->from($this->getTableName())
+ ->where($qb->expr()->eq('actor_uid', $qb->createNamedParameter($uid)))
+ ->orderBy('last_contact', 'DESC')
+ ->setMaxResults(1);
+
+ $cursor = $select->executeQuery();
+ $row = $cursor->fetch();
+
+ if ($row === false) {
+ return null;
+ }
+
+ return (int)$row['last_contact'];
+ }
+
+ public function cleanUp(int $olderThan): void {
+ $qb = $this->db->getQueryBuilder();
+
+ $delete = $qb
+ ->delete($this->getTableName())
+ ->where($qb->expr()->lt('last_contact', $qb->createNamedParameter($olderThan)));
+
+ $delete->executeStatement();
+ }
+}
diff --git a/apps/contactsinteraction/lib/Listeners/ContactInteractionListener.php b/apps/contactsinteraction/lib/Listeners/ContactInteractionListener.php
new file mode 100644
index 00000000000..61f529f9c46
--- /dev/null
+++ b/apps/contactsinteraction/lib/Listeners/ContactInteractionListener.php
@@ -0,0 +1,132 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction\Listeners;
+
+use OCA\ContactsInteraction\Db\CardSearchDao;
+use OCA\ContactsInteraction\Db\RecentContact;
+use OCA\ContactsInteraction\Db\RecentContactMapper;
+use OCP\AppFramework\Db\TTransactional;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Contacts\Events\ContactInteractedWithEvent;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IDBConnection;
+use OCP\IL10N;
+use OCP\IUserManager;
+use Psr\Log\LoggerInterface;
+use Sabre\VObject\Component\VCard;
+use Sabre\VObject\UUIDUtil;
+
+/** @template-implements IEventListener<ContactInteractedWithEvent> */
+class ContactInteractionListener implements IEventListener {
+
+ use TTransactional;
+
+ public function __construct(
+ private RecentContactMapper $mapper,
+ private CardSearchDao $cardSearchDao,
+ private IUserManager $userManager,
+ private IDBConnection $dbConnection,
+ private ITimeFactory $timeFactory,
+ private IL10N $l10n,
+ private LoggerInterface $logger,
+ ) {
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof ContactInteractedWithEvent)) {
+ return;
+ }
+
+ if ($event->getUid() === null && $event->getEmail() === null && $event->getFederatedCloudId() === null) {
+ $this->logger->warning('Contact interaction event has no user identifier set');
+ return;
+ }
+
+ if ($event->getUid() !== null && $event->getUid() === $event->getActor()->getUID()) {
+ $this->logger->info('Ignoring contact interaction with self');
+ return;
+ }
+
+ $this->atomic(function () use ($event): void {
+ $uid = $event->getUid();
+ $email = $event->getEmail();
+ $federatedCloudId = $event->getFederatedCloudId();
+
+ $existingContact = $this->cardSearchDao->findExisting(
+ $event->getActor(),
+ $uid,
+ $email,
+ $federatedCloudId);
+ if ($existingContact !== null) {
+ return;
+ }
+
+ $existingRecentlyContacted = $this->mapper->findMatch(
+ $event->getActor(),
+ $uid,
+ $email,
+ $federatedCloudId
+ );
+ if (!empty($existingRecentlyContacted)) {
+ $now = $this->timeFactory->getTime();
+ foreach ($existingRecentlyContacted as $c) {
+ $c->setLastContact($now);
+ $this->mapper->update($c);
+ }
+
+ return;
+ }
+
+ $contact = new RecentContact();
+ $contact->setActorUid($event->getActor()->getUID());
+ if ($uid !== null) {
+ $contact->setUid($uid);
+ }
+ if ($email !== null) {
+ $contact->setEmail($email);
+ }
+ if ($federatedCloudId !== null) {
+ $contact->setFederatedCloudId($federatedCloudId);
+ }
+ $contact->setLastContact($this->timeFactory->getTime());
+ $contact->setCard($this->generateCard($contact));
+
+ $this->mapper->insert($contact);
+ }, $this->dbConnection);
+ }
+
+ private function getDisplayName(?string $uid): ?string {
+ if ($uid === null) {
+ return null;
+ }
+ if (($user = $this->userManager->get($uid)) === null) {
+ return null;
+ }
+
+ return $user->getDisplayName();
+ }
+
+ 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'),
+ ];
+
+ if ($contact->getEmail() !== null) {
+ $props['EMAIL'] = $contact->getEmail();
+ }
+ if ($contact->getFederatedCloudId() !== null) {
+ $props['CLOUD'] = $contact->getFederatedCloudId();
+ }
+
+ return (new VCard($props))->serialize();
+ }
+}
diff --git a/apps/contactsinteraction/lib/Migration/Version010000Date20200304152605.php b/apps/contactsinteraction/lib/Migration/Version010000Date20200304152605.php
new file mode 100644
index 00000000000..d3298702519
--- /dev/null
+++ b/apps/contactsinteraction/lib/Migration/Version010000Date20200304152605.php
@@ -0,0 +1,75 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction\Migration;
+
+use Closure;
+use OCA\ContactsInteraction\Db\RecentContactMapper;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version010000Date20200304152605 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ *
+ * @return ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $table = $schema->createTable(RecentContactMapper::TABLE_NAME);
+ $table->addColumn('id', 'integer', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 4,
+ ]);
+ $table->addColumn('actor_uid', 'string', [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('uid', 'string', [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('email', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('federated_cloud_id', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('card', 'blob', [
+ 'notnull' => true,
+ ]);
+ $table->addColumn('last_contact', 'integer', [
+ 'notnull' => true,
+ 'length' => 4,
+ ]);
+ $table->setPrimaryKey(['id']);
+ // To find all recent entries
+ $table->addIndex(['actor_uid'], RecentContactMapper::TABLE_NAME . '_actor_uid');
+ // To find a specific entry
+ $table->addIndex(['id', 'actor_uid'], RecentContactMapper::TABLE_NAME . '_id_uid');
+ // To find all recent entries with a given UID
+ $table->addIndex(['uid'], RecentContactMapper::TABLE_NAME . '_uid');
+ // To find all recent entries with a given email address
+ $table->addIndex(['email'], RecentContactMapper::TABLE_NAME . '_email');
+ // To find all recent entries with a give federated cloud id
+ $table->addIndex(['federated_cloud_id'], RecentContactMapper::TABLE_NAME . '_fed_id');
+ // For the cleanup
+ $table->addIndex(['last_contact'], RecentContactMapper::TABLE_NAME . '_last_contact');
+
+ return $schema;
+ }
+}
diff --git a/apps/contactsinteraction/tests/Db/RecentContactMapperTest.php b/apps/contactsinteraction/tests/Db/RecentContactMapperTest.php
new file mode 100644
index 00000000000..f5df27820d7
--- /dev/null
+++ b/apps/contactsinteraction/tests/Db/RecentContactMapperTest.php
@@ -0,0 +1,115 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\ContactsInteraction\Tests\Db;
+
+use OCA\ContactsInteraction\Db\RecentContact;
+use OCA\ContactsInteraction\Db\RecentContactMapper;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\IUser;
+use OCP\Server;
+use Sabre\VObject\Component\VCard;
+use Sabre\VObject\UUIDUtil;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class RecentContactMapperTest extends TestCase {
+ private RecentContactMapper $recentContactMapper;
+ private ITimeFactory $time;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->recentContactMapper = Server::get(RecentContactMapper::class);
+ $this->time = Server::get(ITimeFactory::class);
+ }
+
+ protected function tearDown(): void {
+ parent::tearDown();
+
+ $this->recentContactMapper->cleanUp(time());
+ }
+
+ public function testCreateRecentContact(): void {
+ $contact = $this->createRecentContact();
+ $this->assertNotNull($contact->getId());
+ }
+
+ public function testFindAll(): void {
+ $this->createRecentContact();
+ $this->createRecentContact();
+
+ $contacts = $this->recentContactMapper->findAll('admin');
+ $this->assertCount(2, $contacts);
+ }
+
+ public function testFindMatchByEmail(): void {
+ $this->createRecentContact();
+ $contact = $this->createRecentContact('foo@bar');
+
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('admin');
+
+ $result = $this->recentContactMapper->findMatch($user, null, 'foo@bar', null);
+
+ $this->assertCount(1, $result);
+ $this->assertEquals($contact->getId(), $result[0]->getId());
+ }
+
+ public function testFindMatchByFederatedCloudId(): void {
+ $this->createRecentContact();
+ $contact = $this->createRecentContact(null, 'foo.bar');
+
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('admin');
+
+ $result = $this->recentContactMapper->findMatch($user, null, null, 'foo.bar');
+
+ $this->assertCount(1, $result);
+ $this->assertEquals($contact->getId(), $result[0]->getId());
+ }
+
+ public function testCleanUp(): void {
+ $this->createRecentContact();
+ $this->createRecentContact();
+ $this->assertCount(2, $this->recentContactMapper->findAll('admin'));
+
+ $this->recentContactMapper->cleanUp(time());
+ $this->assertCount(0, $this->recentContactMapper->findAll('admin'));
+ }
+
+ protected function createRecentContact(?string $email = null, ?string $federatedCloudId = null): RecentContact {
+ $props = [
+ 'URI' => UUIDUtil::getUUID(),
+ 'FN' => 'Foo Bar',
+ 'CATEGORIES' => 'Recently contacted',
+ ];
+
+ $time = $this->time->getDateTime();
+ $time->modify('-14days');
+
+ $contact = new RecentContact();
+ $contact->setActorUid('admin');
+ $contact->setCard((new VCard($props))->serialize());
+ $contact->setLastContact($time->getTimestamp());
+
+ if ($email !== null) {
+ $contact->setEmail($email);
+ }
+
+ if ($federatedCloudId !== null) {
+ $contact->setFederatedCloudId($federatedCloudId);
+ }
+
+ return $this->recentContactMapper->insert($contact);
+ }
+}