diff options
Diffstat (limited to 'apps/contactsinteraction')
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); + } +} |