diff --git a/css/icons.scss b/css/icons.scss
index 084df4e7b..853289d5a 100644
--- a/css/icons.scss
+++ b/css/icons.scss
@@ -41,6 +41,7 @@
@include icon-black-white('twitter', 'contacts', 2); // “twitter (fab)” by fontawesome.com is licensed under CC BY 4.0. (https://fontawesome.com/icons/twitter?style=brands)
@include icon-black-white('diaspora', 'contacts', 2); // “diaspora (fab)” by fontawesome.com is licensed under CC BY 4.0. (https://fontawesome.com/icons/diaspora?style=brands)
@include icon-black-white('xing', 'contacts', 2); // “xing (fab)” by fontawesome.com is licensed under CC BY 4.0. (https://fontawesome.com/icons/xing?style=brands)
+@include icon-black-white('gravatar', 'contacts', 2); // “gravatar (fab)” by svgrepo.com is licensed under public domain CCO 1.0. (https://www.svgrepo.com/page/licensing)
.icon-up-force-white {
// using #fffffe to trick the accessibility dark theme icon invert
diff --git a/img/gravatar.svg b/img/gravatar.svg
new file mode 100644
index 000000000..9b20de269
--- /dev/null
+++ b/img/gravatar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/img/license.txt b/img/license.txt
index 1022843e4..a1c9cbaa4 100644
--- a/img/license.txt
+++ b/img/license.txt
@@ -6,3 +6,5 @@
* “twitter (fab)” by fontawesome.com is licensed under CC BY 4.0. (https://fontawesome.com/icons/twitter?style=brands)
* “diaspora (fab)” by fontawesome.com is licensed under CC BY 4.0. (https://fontawesome.com/icons/diaspora?style=brands)
* “xing (fab)” by fontawesome.com is licensed under CC BY 4.0. (https://fontawesome.com/icons/xing?style=brands)
+* “gravatar (fab)” by svgrepo.com is licensed under public domain CCO 1.0. (https://www.svgrepo.com/page/licensing)
+
diff --git a/lib/Service/Social/CompositeSocialProvider.php b/lib/Service/Social/CompositeSocialProvider.php
index 40d937e6e..751f5cc68 100644
--- a/lib/Service/Social/CompositeSocialProvider.php
+++ b/lib/Service/Social/CompositeSocialProvider.php
@@ -37,17 +37,19 @@ public function __construct(InstagramProvider $instagramProvider,
TwitterProvider $twitterProvider,
TumblrProvider $tumblrProvider,
DiasporaProvider $diasporaProvider,
- XingProvider $xingProvider) {
+ XingProvider $xingProvider,
+ GravatarProvider $gravatarProvider) {
// This determines the priority of known providers
$this->providers = [
- 'instagram' => $instagramProvider,
- 'mastodon' => $mastodonProvider,
- 'twitter' => $twitterProvider,
- 'facebook' => $facebookProvider,
- 'tumblr' => $tumblrProvider,
- 'diaspora' => $diasporaProvider,
- 'xing' => $xingProvider,
+ $instagramProvider->name => $instagramProvider,
+ $mastodonProvider->name => $mastodonProvider,
+ $twitterProvider->name => $twitterProvider,
+ $facebookProvider->name => $facebookProvider,
+ $tumblrProvider->name => $tumblrProvider,
+ $diasporaProvider->name => $diasporaProvider,
+ $xingProvider->name => $xingProvider,
+ $gravatarProvider->name => $gravatarProvider
];
}
@@ -60,40 +62,28 @@ public function getSupportedNetworks() : array {
return array_keys($this->providers);
}
-
/**
* generate download url for a social entry
*
- * @param array socialEntries all social data from the contact
- * @param String network the choice which network to use (fallback: take first available)
+ * @param String network the choice which network to use
*
- * @returns String the url to the requested information or null in case of errors
+ * @return ISocialProvider if provider of 'network' is found, otherwise null
*/
- public function getSocialConnector(array $socialEntries, string $network) : ?string {
+ public function getSocialConnector(string $network) : ?ISocialProvider {
$connector = null;
- $selection = $this->providers;
// check if dedicated network selected
if (isset($this->providers[$network])) {
- $selection = [$network => $this->providers[$network]];
+ $connector = $this->providers[$network];
}
+ return $connector;
+ }
- // check selected providers in order
- foreach ($selection as $type => $socialProvider) {
-
- // search for this network in user's profile
- foreach ($socialEntries as $socialEntry) {
- if (strtolower($type) === strtolower($socialEntry['type'])) {
- $profileId = $socialProvider->cleanupId($socialEntry['value']);
- if (!is_null($profileId)) {
- $connector = $socialProvider->getImageUrl($profileId);
- }
- break;
- }
- }
- if ($connector) {
- break;
- }
- }
- return ($connector);
+ /**
+ * generate download url for a social entry
+ *
+ * @return ISocialProvider[] all social providers
+ */
+ public function getSocialConnectors() : array {
+ return array_values($this->providers);
}
}
diff --git a/lib/Service/Social/DiasporaProvider.php b/lib/Service/Social/DiasporaProvider.php
index 73f85792c..c0af278f6 100644
--- a/lib/Service/Social/DiasporaProvider.php
+++ b/lib/Service/Social/DiasporaProvider.php
@@ -30,31 +30,48 @@ class DiasporaProvider implements ISocialProvider {
/** @var IClientService */
private $httpClient;
- /** @var boolean */
+ /** @var bool */
private $looping;
+ /** @var string */
+ public $name = "diaspora";
+
public function __construct(IClientService $httpClient) {
$this->httpClient = $httpClient->NewClient();
$this->looping = false;
}
-
+
/**
- * Returns the profile-id
+ * Returns if this provider supports this contact
*
- * @param {string} the value from the contact's x-socialprofile
+ * @param {array} contact info
*
- * @return string
+ * @return bool
*/
- public function cleanupId(string $candidate):string {
- try {
- if (strpos($candidate, 'http') !== 0) {
- $user_server = explode('@', $candidate);
- $candidate = 'https://' . array_pop($user_server) . '/public/' . array_pop($user_server) . '.atom';
+ public function supportsContact(array $contact):bool {
+ $socialprofiles = $this->getProfileIds($contact);
+ return isset($socialprofiles) && count($socialprofiles) > 0;
+ }
+
+ /**
+ * Returns all possible profile-picture urls
+ *
+ * @param {array} contact information
+ *
+ * @return array
+ */
+ public function getImageUrls(array $contact):array {
+ $profileIds = $this->getProfileIds($contact);
+ $urls = [];
+
+ foreach ($profileIds as $profileId) {
+ $url = $this->getImageUrl($profileId);
+ if (isset($url)) {
+ $urls[] = $url;
}
- } catch (Exception $e) {
- $candidate = null;
}
- return $candidate;
+
+ return $urls;
}
/**
@@ -64,7 +81,7 @@ public function cleanupId(string $candidate):string {
*
* @return string|null
*/
- public function getImageUrl(string $profileUrl):?string {
+ protected function getImageUrl(string $profileUrl):?string {
try {
$result = $this->httpClient->get($profileUrl);
$htmlResult = $result->getBody();
@@ -82,8 +99,51 @@ public function getImageUrl(string $profileUrl):?string {
}
}
return null;
- } catch (Exception $e) {
+ } catch (\Exception $e) {
return null;
}
}
+
+ /**
+ * Returns all possible profile ids for contact
+ *
+ * @param {array} contact information
+ *
+ * @return array
+ */
+ protected function getProfileIds($contact):array {
+ $socialprofiles = $contact['X-SOCIALPROFILE'];
+ $profileIds = [];
+
+ if (isset($socialprofiles)) {
+ foreach ($socialprofiles as $profile) {
+ if (strtolower($profile['type']) == $this->name) {
+ $profileId = $this->cleanupId($profile['value']);
+ if (isset($profileId)) {
+ $profileIds[] = $profileId;
+ }
+ }
+ }
+ }
+ return $profileIds;
+ }
+
+ /**
+ * Returns the profile-id
+ *
+ * @param {string} the value from the contact's x-socialprofile
+ *
+ * @return string
+ */
+ protected function cleanupId(string $candidate):?string {
+ try {
+ if (strpos($candidate, 'http') !== 0) {
+ $user_server = explode('@', $candidate);
+ $candidate = 'https://' . array_pop($user_server) . '/public/' . array_pop($user_server) . '.atom';
+ }
+ } catch (\Exception $e) {
+ $candidate = null;
+ }
+ return $candidate;
+ }
}
diff --git a/lib/Service/Social/FacebookProvider.php b/lib/Service/Social/FacebookProvider.php
index 770ac45ae..e5d34397a 100644
--- a/lib/Service/Social/FacebookProvider.php
+++ b/lib/Service/Social/FacebookProvider.php
@@ -30,10 +30,43 @@ class FacebookProvider implements ISocialProvider {
/** @var IClientService */
private $httpClient;
+ /** @var string */
+ public $name = "facebook";
+
public function __construct(IClientService $httpClient) {
$this->httpClient = $httpClient->NewClient();
}
-
+
+ /**
+ * Returns if this provider supports this contact
+ *
+ * @param {array} contact info
+ *
+ * @return bool
+ */
+ public function supportsContact(array $contact):bool {
+ $socialprofiles = $this->getProfiles($contact);
+ return isset($socialprofiles) && count($socialprofiles) > 0;
+ }
+
+ /**
+ * Returns the profile-picture url
+ *
+ * @param {array} contact information
+ *
+ * @return array
+ */
+ public function getImageUrls(array $contact):array {
+ $profileIds = $this->getProfileIds($contact);
+ $urls = [];
+ foreach ($profileIds as $profileId) {
+ $recipe = 'https://graph.facebook.com/{socialId}/picture?width=720';
+ $connector = str_replace("{socialId}", $profileId, $recipe);
+ $urls[] = $connector;
+ }
+ return $urls;
+ }
+
/**
* Returns the profile-id
*
@@ -41,7 +74,7 @@ public function __construct(IClientService $httpClient) {
*
* @return string
*/
- public function cleanupId(string $candidate):string {
+ protected function cleanupId(string $candidate):string {
$candidate = basename($candidate);
if (!is_numeric($candidate)) {
$candidate = $this->findFacebookId($candidate);
@@ -50,16 +83,39 @@ public function cleanupId(string $candidate):string {
}
/**
- * Returns the profile-picture url
+ * Returns all possible profile ids for contact
*
- * @param {string} profileId the profile-id
+ * @param {array} contact information
*
- * @return string
+ * @return array of string profile ids
*/
- public function getImageUrl(string $profileId):string {
- $recipe = 'https://graph.facebook.com/{socialId}/picture?width=720';
- $connector = str_replace("{socialId}", $profileId, $recipe);
- return $connector;
+ protected function getProfiles(array $contact):array {
+ $socialprofiles = $contact['X-SOCIALPROFILE'];
+ $profiles = [];
+ if (isset($socialprofiles)) {
+ foreach ($socialprofiles as $profile) {
+ if (strtolower($profile['type']) == $this->name) {
+ $profiles[] = $profile['value'];
+ }
+ }
+ }
+ return $profiles;
+ }
+
+ /**
+ * Returns all possible profile ids for contact
+ *
+ * @param {array} contact information
+ *
+ * @return array of string profile ids
+ */
+ protected function getProfileIds(array $contact):array {
+ $profiles = $this->getProfiles($contact);
+ $profileIds = [];
+ foreach ($profiles as $profile) {
+ $profileIds[] = $this->cleanupId($profile);
+ }
+ return $profileIds;
}
/**
diff --git a/lib/Service/Social/GravatarProvider.php b/lib/Service/Social/GravatarProvider.php
new file mode 100644
index 000000000..4b9d9a842
--- /dev/null
+++ b/lib/Service/Social/GravatarProvider.php
@@ -0,0 +1,65 @@
+
+ *
+ * @author leith
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+namespace OCA\Contacts\Service\Social;
+
+class GravatarProvider implements ISocialProvider {
+ /** @var string */
+ public $name = "gravatar";
+
+ public function __construct() {
+ }
+
+ /**
+ * Returns if this provider supports this contact
+ *
+ * @param {array} contact info
+ *
+ * @return bool
+ */
+ public function supportsContact(array $contact):bool {
+ $emails = $contact['EMAIL'];
+ return isset($emails) && count($emails);
+ }
+
+ /**
+ * Returns the profile-picture url
+ *
+ * @param {array} contact information
+ *
+ * @return array
+ */
+ public function getImageUrls(array $contact):array {
+ $urls = [];
+ $emails = $contact['EMAIL'];
+ if (isset($emails)) {
+ foreach ($emails as $email) {
+ $hash = md5(strtolower(trim($email['value'])));
+ $recipe = 'https://www.gravatar.com/avatar/{hash}?s=720&d=404';
+ $connector = str_replace("{hash}", $hash, $recipe);
+ $urls[] = $connector;
+ }
+ }
+ return $urls;
+ }
+}
diff --git a/lib/Service/Social/ISocialProvider.php b/lib/Service/Social/ISocialProvider.php
index 1415f4d09..a21fe32bb 100644
--- a/lib/Service/Social/ISocialProvider.php
+++ b/lib/Service/Social/ISocialProvider.php
@@ -24,22 +24,21 @@
namespace OCA\Contacts\Service\Social;
interface ISocialProvider {
-
/**
- * Returns the profile-id
+ * Returns true if provider supports the contact
*
- * @param {string} the value from the contact's x-socialprofile
+ * @param {array} contact details
*
- * @return string
+ * @return boolean
*/
- public function cleanupId(string $candidate):?string ;
+ public function supportsContact(array $contact):bool ;
/**
- * Returns the profile-picture url
+ * Returns all possible profile-picture urls
*
- * @param {string} profileId the profile-id
+ * @param {array} contact information
*
- * @return string|null
+ * @return array
*/
- public function getImageUrl(string $profileId):?string ;
+ public function getImageUrls(array $contact):array ;
}
diff --git a/lib/Service/Social/InstagramProvider.php b/lib/Service/Social/InstagramProvider.php
index b7687e715..a43d12c3d 100644
--- a/lib/Service/Social/InstagramProvider.php
+++ b/lib/Service/Social/InstagramProvider.php
@@ -30,10 +30,44 @@ class InstagramProvider implements ISocialProvider {
/** @var IClientService */
private $httpClient;
+ /** @var string */
+ public $name = "instagram";
+
public function __construct(IClientService $httpClient) {
$this->httpClient = $httpClient->NewClient();
}
-
+
+ /**
+ * Returns if this provider supports this contact
+ *
+ * @param {array} contact info
+ *
+ * @return bool
+ */
+ public function supportsContact(array $contact):bool {
+ $socialprofiles = $this->getProfiles($contact);
+ return isset($socialprofiles) && count($socialprofiles) > 0;
+ }
+
+ /**
+ * Returns the profile-picture url
+ *
+ * @param {array} contact information
+ *
+ * @return array
+ */
+ public function getImageUrls(array $contact):array {
+ $profileIds = $this->getProfileIds($contact);
+ $urls = [];
+ foreach ($profileIds as $profileId) {
+ $recipe = 'https://www.instagram.com/{socialId}/?__a=1';
+ $connector = str_replace("{socialId}", $profileId, $recipe);
+ $connector = $this->getFromJson($connector, 'graphql->user->profile_pic_url_hd');
+ $urls[] = $connector;
+ }
+ return $urls;
+ }
+
/**
* Returns the profile-id
*
@@ -41,25 +75,47 @@ public function __construct(IClientService $httpClient) {
*
* @return string
*/
- public function cleanupId(string $candidate):string {
+ protected function cleanupId(string $candidate):string {
$candidate = preg_replace('/^' . preg_quote('x-apple:', '/') . '/', '', $candidate);
return basename($candidate);
}
+
+ /**
+ * Returns all possible profile urls for contact
+ *
+ * @param {array} contact information
+ *
+ * @return array of string profile urls
+ */
+ protected function getProfiles($contact):array {
+ $socialprofiles = $contact['X-SOCIALPROFILE'];
+ $profiles = [];
+ if (isset($socialprofiles)) {
+ foreach ($socialprofiles as $profile) {
+ if (strtolower($profile['type']) == $this->name) {
+ $profiles[] = $profile['value'];
+ }
+ }
+ }
+ return $profiles;
+ }
/**
- * Returns the profile-picture url
+ * Returns all possible profile ids for contact
*
- * @param {string} profileId the profile-id
+ * @param {array} contact information
*
- * @return string|null
+ * @return array of string profile ids
*/
- public function getImageUrl(string $profileId):?string {
- $recipe = 'https://www.instagram.com/{socialId}/?__a=1';
- $connector = str_replace("{socialId}", $profileId, $recipe);
- $connector = $this->getFromJson($connector, 'graphql->user->profile_pic_url_hd');
- return $connector;
+ protected function getProfileIds($contact):array {
+ $socialprofiles = $this->getProfiles($contact);
+ $profileIds = [];
+ foreach ($socialprofiles as $profile) {
+ $profileIds[] = $this->cleanupId($profile);
+ }
+ return $profileIds;
}
-
+
/**
* extracts desired value from a json
*
@@ -81,7 +137,7 @@ protected function getFromJson(string $url, string $desired) : ?string {
$jsonResult = $jsonResult[$loc];
}
return $jsonResult;
- } catch (Exception $e) {
+ } catch (\Exception $e) {
return null;
}
}
diff --git a/lib/Service/Social/MastodonProvider.php b/lib/Service/Social/MastodonProvider.php
index 74b68e96d..a212433c7 100644
--- a/lib/Service/Social/MastodonProvider.php
+++ b/lib/Service/Social/MastodonProvider.php
@@ -30,34 +30,49 @@ class MastodonProvider implements ISocialProvider {
/** @var IClientService */
private $httpClient;
+ /** @var string */
+ public $name = "mastodon";
+
public function __construct(IClientService $httpClient) {
$this->httpClient = $httpClient->NewClient();
}
-
+
/**
- * Returns the profile-id
+ * Returns if this provider supports this contact
*
- * @param {string} the value from the contact's x-socialprofile
+ * @param {array} contact info
*
- * @return string
+ * @return bool
*/
- public function cleanupId(string $candidate):?string {
- $candidate = preg_replace('/^' . preg_quote('x-apple:', '/') . '/', '', $candidate);
- try {
- if (strpos($candidate, 'http') !== 0) {
- $user_server = explode('@', $candidate);
- $candidate = 'https://' . array_pop($user_server) . '/@' . array_pop($user_server);
+ public function supportsContact(array $contact):bool {
+ $profiles = $this->getProfileIds($contact);
+ return isset($profiles) && count($profiles) > 0;
+ }
+
+ /**
+ * Returns all possible profile-picture urls
+ *
+ * @param {array} contact information
+ *
+ * @return array
+ */
+ public function getImageUrls(array $contact):array {
+ $profileIds = $this->getProfileIds($contact);
+ $urls = [];
+
+ foreach ($profileIds as $profileId) {
+ $url = $this->getImageUrl($profileId);
+ if (isset($url)) {
+ $urls[] = $url;
}
- } catch (Exception $e) {
- $candidate = null;
}
- return $candidate;
+ return $urls;
}
/**
* Returns the profile-picture url
*
- * @param {string} profileUrl link to the profile
+ * @param {array} contact information
*
* @return string|null
*/
@@ -72,8 +87,51 @@ public function getImageUrl(string $profileUrl):?string {
return $img->getAttribute("data-original");
}
return null;
- } catch (Exception $e) {
+ } catch (\Exception $e) {
return null;
}
}
+
+ /**
+ * Returns all possible profile ids for contact
+ *
+ * @param {array} contact information
+ *
+ * @return array of possible profileIds
+ */
+ protected function getProfileIds($contact):array {
+ $socialprofiles = $contact['X-SOCIALPROFILE'];
+ $profileIds = [];
+ if (isset($socialprofiles)) {
+ foreach ($socialprofiles as $profile) {
+ if (strtolower($profile['type']) == $this->name) {
+ $profileId = $this->cleanupId($profile['value']);
+ if (isset($profileId)) {
+ $profileIds[] = $profileId;
+ }
+ }
+ }
+ }
+ return $profileIds;
+ }
+
+ /**
+ * Returns the profile-id
+ *
+ * @param {string} the value from the contact's x-socialprofile
+ *
+ * @return string
+ */
+ protected function cleanupId(string $candidate):?string {
+ $candidate = preg_replace('/^' . preg_quote('x-apple:', '/') . '/', '', $candidate);
+ try {
+ if (strpos($candidate, 'http') !== 0) {
+ $user_server = explode('@', $candidate);
+ $candidate = 'https://' . array_pop($user_server) . '/@' . array_pop($user_server);
+ }
+ } catch (\Exception $e) {
+ $candidate = null;
+ }
+ return $candidate;
+ }
}
diff --git a/lib/Service/Social/TumblrProvider.php b/lib/Service/Social/TumblrProvider.php
index a830b945b..cd3d577f7 100644
--- a/lib/Service/Social/TumblrProvider.php
+++ b/lib/Service/Social/TumblrProvider.php
@@ -24,9 +24,42 @@
namespace OCA\Contacts\Service\Social;
class TumblrProvider implements ISocialProvider {
+ /** @var string */
+ public $name = "tumblr";
+
public function __construct() {
}
-
+
+ /**
+ * Returns if this provider supports this contact
+ *
+ * @param {array} contact info
+ *
+ * @return bool
+ */
+ public function supportsContact(array $contact):bool {
+ $socialprofiles = $this->getProfileIds($contact);
+ return isset($socialprofiles) && count($socialprofiles) > 0;
+ }
+
+ /**
+ * Returns the profile-picture url
+ *
+ * @param {string} profileId the profile-id
+ *
+ * @return array
+ */
+ public function getImageUrls(array $contact):array {
+ $profileIds = $this->getProfileIds($contact);
+ $urls = [];
+ foreach ($profileIds as $profileId) {
+ $recipe = 'https://api.tumblr.com/v2/blog/{socialId}/avatar/512';
+ $connector = str_replace("{socialId}", $profileId, $recipe);
+ $urls[] = $connector;
+ }
+ return $urls;
+ }
+
/**
* Returns the profile-id
*
@@ -34,7 +67,7 @@ public function __construct() {
*
* @return string
*/
- public function cleanupId(string $candidate):?string {
+ protected function cleanupId(string $candidate):?string {
$candidate = preg_replace('/^' . preg_quote('x-apple:', '/') . '/', '', $candidate);
$subdomain = '/(?:http[s]*\:\/\/)*(.*?)\.(?=[^\/]*\..{2,5})/i'; // subdomain
if (preg_match($subdomain, $candidate, $matches)) {
@@ -44,15 +77,22 @@ public function cleanupId(string $candidate):?string {
}
/**
- * Returns the profile-picture url
+ * Returns all possible profile ids for contact
*
- * @param {string} profileId the profile-id
+ * @param {array} contact information
*
- * @return string|null
+ * @return array of string profile ids
*/
- public function getImageUrl(string $profileId):?string {
- $recipe = 'https://api.tumblr.com/v2/blog/{socialId}/avatar/512';
- $connector = str_replace("{socialId}", $profileId, $recipe);
- return $connector;
+ protected function getProfileIds($contact):array {
+ $socialprofiles = $contact['X-SOCIALPROFILE'];
+ $profileIds = [];
+ if (isset($socialprofiles)) {
+ foreach ($socialprofiles as $profile) {
+ if (strtolower($profile['type']) == $this->name) {
+ $profileIds[] = $this->cleanupId($profile['value']);
+ }
+ }
+ }
+ return $profileIds;
}
}
diff --git a/lib/Service/Social/TwitterProvider.php b/lib/Service/Social/TwitterProvider.php
index 953eb1a09..18cd0d467 100644
--- a/lib/Service/Social/TwitterProvider.php
+++ b/lib/Service/Social/TwitterProvider.php
@@ -26,14 +26,47 @@
use OCP\Http\Client\IClientService;
class TwitterProvider implements ISocialProvider {
-
/** @var IClientService */
private $httpClient;
+ /** @var string */
+ public $name = "twitter";
+
public function __construct(IClientService $httpClient) {
$this->httpClient = $httpClient->NewClient();
}
-
+
+ /**
+ * Returns if this provider supports this contact
+ *
+ * @param {array} contact info
+ *
+ * @return bool
+ */
+ public function supportsContact(array $contact):bool {
+ $socialprofiles = $this->getProfileIds($contact);
+ return isset($socialprofiles) && count($socialprofiles) > 0;
+ }
+
+ /**
+ * Returns the profile-picture url
+ *
+ * @param {array} contact information
+ *
+ * @return array
+ */
+ public function getImageUrls(array $contact):array {
+ $profileIds = $this->getProfileIds($contact);
+ $urls = [];
+ foreach ($profileIds as $profileId) {
+ $recipe = 'https://mobile.twitter.com/{socialId}';
+ $connector = str_replace("{socialId}", $profileId, $recipe);
+ $connector = $this->getFromHtml($connector, '_normal');
+ $urls[] = $connector;
+ }
+ return $urls;
+ }
+
/**
* Returns the profile-id
*
@@ -41,7 +74,7 @@ public function __construct(IClientService $httpClient) {
*
* @return string
*/
- public function cleanupId(string $candidate):string {
+ protected function cleanupId(string $candidate):string {
$candidate = basename($candidate);
if ($candidate[0] === '@') {
$candidate = substr($candidate, 1);
@@ -50,19 +83,25 @@ public function cleanupId(string $candidate):string {
}
/**
- * Returns the profile-picture url
+ * Returns all possible profile ids for contact
*
- * @param {string} profileId the profile-id
+ * @param {array} contact information
*
- * @return string|null
+ * @return array of string profile ids
*/
- public function getImageUrl(string $profileId):?string {
- $recipe = 'https://mobile.twitter.com/{socialId}';
- $connector = str_replace("{socialId}", $profileId, $recipe);
- $connector = $this->getFromHtml($connector, '_normal');
- return $connector;
+ protected function getProfileIds($contact):array {
+ $socialprofiles = $contact['X-SOCIALPROFILE'];
+ $profileIds = [];
+ if (isset($socialprofiles)) {
+ foreach ($socialprofiles as $profile) {
+ if (strtolower($profile['type']) == $this->name) {
+ $profileIds[] = $this->cleanupId($profile['value']);
+ }
+ }
+ }
+ return $profileIds;
}
-
+
/**
* extracts desired value from an html page
*
@@ -88,7 +127,7 @@ protected function getFromHtml(string $url, string $desired) : ?string {
}
}
return null;
- } catch (Exception $e) {
+ } catch (\Exception $e) {
return null;
}
}
diff --git a/lib/Service/Social/XingProvider.php b/lib/Service/Social/XingProvider.php
index 739d1fd2f..a752d3c60 100644
--- a/lib/Service/Social/XingProvider.php
+++ b/lib/Service/Social/XingProvider.php
@@ -30,41 +30,54 @@ class XingProvider implements ISocialProvider {
/** @var IClientService */
private $httpClient;
- /** @var boolean */
- private $looping;
+ /** @var string */
+ public $name = "xing";
public function __construct(IClientService $httpClient) {
$this->httpClient = $httpClient->NewClient();
- $this->looping = false;
}
-
+
/**
- * Returns the profile-id
+ * Returns if this provider supports this contact
*
- * @param {string} the value from the contact's x-socialprofile
+ * @param {array} contact info
*
- * @return string
+ * @return bool
*/
- public function cleanupId(string $candidate):string {
- $candidate = preg_replace('/^' . preg_quote('x-apple:', '/') . '/', '', $candidate);
- try {
- if (strpos($candidate, 'http') !== 0) {
- $candidate = 'https://www.xing.com/profile/' . $candidate;
+ public function supportsContact(array $contact):bool {
+ $socialprofiles = $this->getProfileIds($contact);
+ return isset($socialprofiles) && count($socialprofiles) > 0;
+ }
+
+ /**
+ * Returns all possible profile-picture urls
+ *
+ * @param {array} contact information
+ *
+ * @return array
+ */
+ public function getImageUrls(array $contact):array {
+ $profileIds = $this->getProfileIds($contact);
+ $urls = [];
+
+ foreach ($profileIds as $profileId) {
+ $url = $this->getImageUrl($profileId);
+ if (isset($url)) {
+ $urls[] = $url;
}
- } catch (Exception $e) {
- $candidate = null;
}
- return $candidate;
+
+ return $urls;
}
/**
* Returns the profile-picture url
*
- * @param {string} profileId the profile-id
+ * @param {string} profile url
*
* @return string|null
*/
- public function getImageUrl(string $profileUrl):?string {
+ protected function getImageUrl(string $profileUrl):?string {
try {
$result = $this->httpClient->get($profileUrl);
$htmlResult = $result->getBody();
@@ -75,8 +88,50 @@ public function getImageUrl(string $profileUrl):?string {
}
// keyword not found, maybe page changed?
return null;
- } catch (Exception $e) {
+ } catch (\Exception $e) {
return null;
}
}
+
+ /**
+ * Returns the profile-id
+ *
+ * @param {string} the value from the contact's x-socialprofile
+ *
+ * @return string
+ */
+ protected function cleanupId(string $candidate):?string {
+ $candidate = preg_replace('/^' . preg_quote('x-apple:', '/') . '/', '', $candidate);
+ try {
+ if (strpos($candidate, 'http') !== 0) {
+ $candidate = 'https://www.xing.com/profile/' . $candidate;
+ }
+ } catch (\Exception $e) {
+ $candidate = null;
+ }
+ return $candidate;
+ }
+
+ /**
+ * Returns all possible profile ids for contact
+ *
+ * @param {array} contact information
+ *
+ * @return string of first profile url else null
+ */
+ protected function getProfileIds($contact):array {
+ $socialprofiles = $contact['X-SOCIALPROFILE'];
+ $profileIds = [];
+ if (isset($socialprofiles)) {
+ foreach ($socialprofiles as $profile) {
+ if (strtolower($profile['type']) == $this->name) {
+ $profileId = $this->cleanupId($profile['value']);
+ if (isset($profileId)) {
+ $profileIds[] = $profileId;
+ }
+ }
+ }
+ }
+ return $profileIds;
+ }
}
diff --git a/lib/Service/SocialApiService.php b/lib/Service/SocialApiService.php
index 03484c99e..446a9ef69 100644
--- a/lib/Service/SocialApiService.php
+++ b/lib/Service/SocialApiService.php
@@ -50,9 +50,9 @@ class SocialApiService {
private $config;
/** @var IClientService */
private $clientService;
- /** @var IL10N */
+ /** @var IL10N */
private $l10n;
- /** @var IURLGenerator */
+ /** @var IURLGenerator */
private $urlGen;
/** @var CardDavBackend */
private $davBackend;
@@ -84,7 +84,7 @@ public function __construct(
/**
* returns an array of supported social networks
*
- * @returns {array} array of the supported social networks
+ * @return {array} array of the supported social networks
*/
public function getSupportedNetworks() : array {
$syncAllowedByAdmin = $this->config->getAppValue($this->appName, 'allowSocialSync', 'yes');
@@ -167,8 +167,11 @@ protected function registerAddressbooks($userId, IManager $manager) {
*
* @returns {JSONResponse} an empty JSONResponse with respective http status code
*/
- public function updateContact(string $addressbookId, string $contactId, string $network) : JSONResponse {
- $url = null;
+ public function updateContact(string $addressbookId, string $contactId, ?string $network) : JSONResponse {
+ $socialdata = null;
+ $imageType = null;
+ $urls = [];
+ $allConnectors = $this->socialProvider->getSocialConnectors();
try {
// get corresponding addressbook
@@ -179,20 +182,42 @@ public function updateContact(string $addressbookId, string $contactId, string $
// search contact in that addressbook, get social data
$contact = $addressBook->search($contactId, ['UID'], ['types' => true])[0];
- if (!isset($contact['X-SOCIALPROFILE'])) {
+
+ if (!isset($contact)) {
+ return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
+ }
+
+ if ($network) {
+ $allConnectors = [$this->socialProvider->getSocialConnector($network)];
+ }
+
+ $connectors = array_filter($allConnectors, function ($connector) use ($contact) {
+ return $connector->supportsContact($contact);
+ });
+
+ if (count($connectors) == 0) {
return new JSONResponse([], Http::STATUS_PRECONDITION_FAILED);
}
- $socialprofiles = $contact['X-SOCIALPROFILE'];
- // retrieve data
- $url = $this->socialProvider->getSocialConnector($socialprofiles, $network);
- if (empty($url)) {
+ foreach ($connectors as $connector) {
+ $urls = array_merge($connector->getImageUrls($contact), $urls);
+ }
+
+ if (count($urls) == 0) {
return new JSONResponse([], Http::STATUS_BAD_REQUEST);
}
- $httpResult = $this->clientService->NewClient()->get($url);
- $socialdata = $httpResult->getBody();
- $imageType = $httpResult->getHeader('content-type');
+ foreach ($urls as $url) {
+ try {
+ $httpResult = $this->clientService->NewClient()->get($url);
+ $socialdata = $httpResult->getBody();
+ $imageType = $httpResult->getHeader('content-type');
+ if (isset($socialdata) && isset($imageType)) {
+ break;
+ }
+ } catch (\Exception $e) {
+ }
+ }
if (!$socialdata || $imageType === null) {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
diff --git a/src/components/ContactDetails/ContactDetailsAvatar.vue b/src/components/ContactDetails/ContactDetailsAvatar.vue
index b13e94111..92b1befb7 100644
--- a/src/components/ContactDetails/ContactDetailsAvatar.vue
+++ b/src/components/ContactDetails/ContactDetailsAvatar.vue
@@ -196,11 +196,15 @@ export default {
return false
},
supportedSocial() {
+ const emails = this.contact.vCard.getAllProperties('email')
// get social networks set for the current contact
const available = this.contact.vCard.getAllProperties('x-socialprofile')
.map(a => a.jCal[1].type.toString().toLowerCase())
// get list of social networks that allow for avatar download
const supported = supportedNetworks.map(v => v.toLowerCase())
+ if (emails.length) {
+ available.push('gravatar')
+ }
// return supported social networks which are set
return supported.filter(i => available.includes(i))
.map(j => this.capitalize(j))
diff --git a/tests/unit/Service/Social/DiasporaProviderTest.php b/tests/unit/Service/Social/DiasporaProviderTest.php
new file mode 100644
index 000000000..e4b066c1c
--- /dev/null
+++ b/tests/unit/Service/Social/DiasporaProviderTest.php
@@ -0,0 +1,182 @@
+
+ *
+ * @author Matthias Heinisch
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+
+namespace OCA\Contacts\Service\Social;
+
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IResponse;
+use OCP\Http\Client\IClientService;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class DiasporaProviderTest extends TestCase {
+ private $provider;
+
+ /** @var IClientService|MockObject */
+ private $clientService;
+
+ /** @var IClient|MockObject */
+ private $client;
+
+ /** @var IResponse|MockObject */
+ private $response;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->clientService = $this->createMock(IClientService::class);
+ $this->response = $this->createMock(IResponse::class);
+ $this->client = $this->createMock(IClient::class);
+
+ $this->clientService
+ ->method('NewClient')
+ ->willReturn($this->client);
+
+ $this->provider = new DiasporaProvider(
+ $this->clientService
+ );
+ }
+
+ public function dataProviderSupportsContact() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "diaspora"],
+ ["value" => "two", "type" => "diaspora"]
+ ]
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+
+ return [
+ 'contact with diaspora fields' => [$contactWithSocial, true],
+ 'contact without diaspora fields' => [$contactWithoutSocial, false]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderSupportsContact
+ */
+ public function testSupportsContact($contact, $expected) {
+ $result = $this->provider->supportsContact($contact);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function dataProviderGetImageUrls() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one@two", "type" => "diaspora"],
+ ["value" => "two@three", "type" => "diaspora"]
+ ]
+ ];
+ $contactWithSocialUrls = [
+ "https://two/public/one.atom",
+ "https://three/public/two.atom"
+ ];
+ $contactWithSocialHtml = array_map(function ($url) {
+ return "".$url."-small-avatar.jpg";
+ }, $contactWithSocialUrls);
+ $contactWithSocialImg = array_map(function ($url) {
+ return $url."-large-avatar.jpg";
+ }, $contactWithSocialUrls);
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+ $contactWithoutSocialUrls = [];
+ $contactWithoutSocialHtml = [];
+ $contactWithoutSocialImg = [];
+
+ return [
+ 'contact with diaspora fields' => [
+ $contactWithSocial,
+ $contactWithSocialUrls,
+ $contactWithSocialHtml,
+ $contactWithSocialImg
+ ],
+ 'contact without diaspora fields' => [
+ $contactWithoutSocial,
+ $contactWithoutSocialUrls,
+ $contactWithoutSocialHtml,
+ $contactWithoutSocialImg
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderGetImageUrls
+ */
+ public function testGetImageUrls($contact, $urls, $htmls, $imgs) {
+ if (count($urls)) {
+ $this->response
+ ->method('getBody')
+ ->willReturnOnConsecutiveCalls(...$htmls);
+
+ $urlArgs = array_map(function ($url) {
+ return [$url];
+ }, $urls);
+
+ $this->client
+ ->expects($this->exactly(count($urls)))
+ ->method('get')
+ ->withConsecutive(...$urlArgs)
+ ->willReturn($this->response);
+ }
+
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals($imgs, $result);
+ }
+
+ public function testGetImageUrlLoop() {
+ $contact = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one@two", "type" => "diaspora"],
+ ]
+ ];
+ $url1 = "https://two/public/one.atom";
+ $url2 = "https://four/public/three.atom";
+ $html1 = '';
+ $html2 = "".$url2."-small-avatar.jpg";
+ $img = $url2."-large-avatar.jpg";
+
+ $this->response
+ ->method('getBody')
+ ->willReturnOnConsecutiveCalls($html1, $html2);
+
+ $this->client
+ ->expects($this->exactly(2))
+ ->method('get')
+ ->withConsecutive([$url1], [$url2])
+ ->willReturn($this->response);
+
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals([$img], $result);
+ }
+}
diff --git a/tests/unit/Service/Social/FacebookProviderTest.php b/tests/unit/Service/Social/FacebookProviderTest.php
new file mode 100644
index 000000000..b2e3d8e0b
--- /dev/null
+++ b/tests/unit/Service/Social/FacebookProviderTest.php
@@ -0,0 +1,156 @@
+
+ *
+ * @author Matthias Heinisch
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+
+namespace OCA\Contacts\Service\Social;
+
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IResponse;
+use OCP\Http\Client\IClientService;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class FacebookProviderTest extends TestCase {
+ private $provider;
+
+ /** @var IClientService|MockObject */
+ private $clientService;
+
+ /** @var IClient|MockObject */
+ private $client;
+
+ /** @var IResponse|MockObject */
+ private $response;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->clientService = $this->createMock(IClientService::class);
+ $this->response = $this->createMock(IResponse::class);
+ $this->client = $this->createMock(IClient::class);
+
+ $this->clientService
+ ->method('NewClient')
+ ->willReturn($this->client);
+
+ $this->provider = new FacebookProvider(
+ $this->clientService
+ );
+ }
+
+ public function dataProviderSupportsContact() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "123124123", "type" => "facebook"],
+ ["value" => "23426523423", "type" => "facebook"]
+ ]
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+
+ return [
+ 'contact with facebook fields' => [$contactWithSocial, true],
+ 'contact without facebook fields' => [$contactWithoutSocial, false]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderSupportsContact
+ */
+ public function testSupportsContact($contact, $expected) {
+ $result = $this->provider->supportsContact($contact);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function dataProviderGetImageUrls() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "123456", "type" => "facebook"],
+ ["value" => "7891011", "type" => "facebook"]
+ ]
+ ];
+ $contactWithSocialUrls = [
+ "https://graph.facebook.com/123456/picture?width=720",
+ "https://graph.facebook.com/7891011/picture?width=720",
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+ $contactWithoutSocialUrls = [];
+
+ return [
+ 'contact with facebook fields' => [
+ $contactWithSocial,
+ $contactWithSocialUrls
+ ],
+ 'contact without facebook fields' => [
+ $contactWithoutSocial,
+ $contactWithoutSocialUrls
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderGetImageUrls
+ */
+ public function testGetImageUrls($contact, $urls) {
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals($urls, $result);
+ }
+
+ public function testGetImageUrlLookup() {
+ $contact = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "username1", "type" => "facebook"],
+ ]
+ ];
+ $url1 = "https://facebook.com/username1";
+ $url2 = "https://graph.facebook.com/1234567/picture?width=720";
+ $html1 = '"entity_id":"1234567"';
+
+ $this->response
+ ->method('getBody')
+ ->willReturn($html1);
+
+ $this->response
+ ->method('getStatusCode')
+ ->willReturn(200);
+
+ $this->client
+ ->expects($this->once())
+ ->method('get')
+ ->with($url1)
+ ->willReturn($this->response);
+
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals([$url2], $result);
+ }
+}
diff --git a/tests/unit/Service/Social/GravatarProviderTest.php b/tests/unit/Service/Social/GravatarProviderTest.php
new file mode 100644
index 000000000..8de4f5656
--- /dev/null
+++ b/tests/unit/Service/Social/GravatarProviderTest.php
@@ -0,0 +1,92 @@
+
+ *
+ * @author Matthias Heinisch
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+
+namespace OCA\Contacts\Service\Social;
+
+use ChristophWurst\Nextcloud\Testing\TestCase;
+
+class GravatarProviderTest extends TestCase {
+ private $provider;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->provider = new GravatarProvider(
+ );
+ }
+
+ public function dataProviderSupportsContact() {
+ $contactWithEmail = [
+ 'EMAIL' => [["value" => "one"], ["value" => "two"]]
+ ];
+
+ $contactWithoutEmail = [
+ 'PHONE' => [["value" => "one"], ["value" => "two"]]
+ ];
+
+ return [
+ 'contact with email' => [$contactWithEmail, true],
+ 'contact without email' => [$contactWithoutEmail, false]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderSupportsContact
+ */
+ public function testSupportsContact($contact, $expected) {
+ $result = $this->provider->supportsContact($contact);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function dataProviderGetImageUrls() {
+ $contactWithEmail = [
+ 'EMAIL' => [["value" => "one"], ["value" => "two"]]
+ ];
+
+ $contactWithoutEmail = [
+ 'PHONE' => [["value" => "one"], ["value" => "two"]]
+ ];
+
+ $urls = [];
+
+ foreach ($contactWithEmail['EMAIL'] as $email) {
+ $hash = md5(strtolower(trim($email['value'])));
+ $recipe = 'https://www.gravatar.com/avatar/{hash}?s=720&d=404';
+ $urls[] = str_replace("{hash}", $hash, $recipe);
+ }
+
+ return [
+ 'contact with email' => [$contactWithEmail, $urls],
+ 'contact without email' => [$contactWithoutEmail, []]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderGetImageUrls
+ */
+ public function testGetImageUrls($contact, $expected) {
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals($expected, $result);
+ }
+}
diff --git a/tests/unit/Service/Social/InstagramProviderTest.php b/tests/unit/Service/Social/InstagramProviderTest.php
new file mode 100644
index 000000000..6fa0e8668
--- /dev/null
+++ b/tests/unit/Service/Social/InstagramProviderTest.php
@@ -0,0 +1,158 @@
+
+ *
+ * @author Matthias Heinisch
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+
+namespace OCA\Contacts\Service\Social;
+
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IResponse;
+use OCP\Http\Client\IClientService;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class InstagramProviderTest extends TestCase {
+ private $provider;
+
+ /** @var IClientService|MockObject */
+ private $clientService;
+
+ /** @var IClient|MockObject */
+ private $client;
+
+ /** @var IResponse|MockObject */
+ private $response;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->clientService = $this->createMock(IClientService::class);
+ $this->response = $this->createMock(IResponse::class);
+ $this->client = $this->createMock(IClient::class);
+
+ $this->clientService
+ ->method('NewClient')
+ ->willReturn($this->client);
+
+ $this->provider = new InstagramProvider(
+ $this->clientService
+ );
+ }
+
+ public function dataProviderSupportsContact() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "username1", "type" => "instagram"],
+ ["value" => "username2", "type" => "instagram"]
+ ]
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+
+ return [
+ 'contact with instagram fields' => [$contactWithSocial, true],
+ 'contact without instagram fields' => [$contactWithoutSocial, false]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderSupportsContact
+ */
+ public function testSupportsContact($contact, $expected) {
+ $result = $this->provider->supportsContact($contact);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function dataProviderGetImageUrls() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "username1", "type" => "instagram"],
+ ["value" => "username2", "type" => "instagram"]
+ ]
+ ];
+ $contactWithSocialUrls = [
+ "https://www.instagram.com/username1/?__a=1",
+ "https://www.instagram.com/username2/?__a=1",
+ ];
+ $contactWithSocialJson = [
+ json_encode(
+ ["graphql" => ["user" => ["profile_pic_url_hd" => "username1.jpg"]]]
+ ),
+ json_encode(
+ ["graphql" => ["user" => ["profile_pic_url_hd" => "username2.jpg"]]]
+ )
+ ];
+ $contactWithSocialImgs = [
+ "username1.jpg",
+ "username2.jpg"
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+ $contactWithoutSocialUrls = [];
+ $contactWithoutSocialJson = [];
+ $contactWithoutSocialImgs = [];
+
+ return [
+ 'contact with instagram fields' => [
+ $contactWithSocial,
+ $contactWithSocialJson,
+ $contactWithSocialUrls,
+ $contactWithSocialImgs
+ ],
+ 'contact without instagram fields' => [
+ $contactWithoutSocial,
+ $contactWithoutSocialJson,
+ $contactWithoutSocialUrls,
+ $contactWithoutSocialImgs
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderGetImageUrls
+ */
+ public function testGetImageUrls($contact, $json, $urls, $imgs) {
+ if (count($urls)) {
+ $this->response->method("getBody")->willReturnOnConsecutiveCalls(...$json);
+ $this->client
+ ->expects($this->exactly(count($urls)))
+ ->method("get")
+ ->withConsecutive(...array_map(function ($a) {
+ return [$a];
+ }, $urls))
+ ->willReturn($this->response);
+ }
+
+
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals($imgs, $result);
+ }
+}
diff --git a/tests/unit/Service/Social/MastodonProviderTest.php b/tests/unit/Service/Social/MastodonProviderTest.php
new file mode 100644
index 000000000..9611bf6b3
--- /dev/null
+++ b/tests/unit/Service/Social/MastodonProviderTest.php
@@ -0,0 +1,154 @@
+
+ *
+ * @author Matthias Heinisch
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+
+namespace OCA\Contacts\Service\Social;
+
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IResponse;
+use OCP\Http\Client\IClientService;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class MastodonProviderTest extends TestCase {
+ private $provider;
+
+ /** @var IClientService|MockObject */
+ private $clientService;
+
+ /** @var IClient|MockObject */
+ private $client;
+
+ /** @var IResponse|MockObject */
+ private $response;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->clientService = $this->createMock(IClientService::class);
+ $this->response = $this->createMock(IResponse::class);
+ $this->client = $this->createMock(IClient::class);
+
+ $this->clientService
+ ->method('NewClient')
+ ->willReturn($this->client);
+
+ $this->provider = new MastodonProvider(
+ $this->clientService
+ );
+ }
+
+ public function dataProviderSupportsContact() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "user1@cloud1", "type" => "mastodon"],
+ ["value" => "user2@cloud2", "type" => "mastodon"]
+ ]
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+
+ return [
+ 'contact with mastodon fields' => [$contactWithSocial, true],
+ 'contact without mastodon fields' => [$contactWithoutSocial, false]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderSupportsContact
+ */
+ public function testSupportsContact($contact, $expected) {
+ $result = $this->provider->supportsContact($contact);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function dataProviderGetImageUrls() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "user1@cloud1", "type" => "mastodon"],
+ ["value" => "user2@cloud2", "type" => "mastodon"]
+ ]
+ ];
+ $contactWithSocialUrls = [
+ "https://cloud1/@user1",
+ "https://cloud2/@user2",
+ ];
+ $contactWithSocialHtml = [
+ '',
+ ''
+ ];
+ $contactWithSocialImgs = [
+ "user1.jpg",
+ "user2.jpg"
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+ $contactWithoutSocialUrls = [];
+ $contactWithoutSocialHtml = [];
+ $contactWithoutSocialImgs = [];
+
+ return [
+ 'contact with mastodon fields' => [
+ $contactWithSocial,
+ $contactWithSocialHtml,
+ $contactWithSocialUrls,
+ $contactWithSocialImgs
+ ],
+ 'contact without mastodon fields' => [
+ $contactWithoutSocial,
+ $contactWithoutSocialHtml,
+ $contactWithoutSocialUrls,
+ $contactWithoutSocialImgs
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderGetImageUrls
+ */
+ public function testGetImageUrls($contact, $htmls, $urls, $imgs) {
+ if (count($urls)) {
+ $this->response->method("getBody")->willReturnOnConsecutiveCalls(...$htmls);
+ $this->client
+ ->expects($this->exactly(count($urls)))
+ ->method("get")
+ ->withConsecutive(...array_map(function ($a) {
+ return [$a];
+ }, $urls))
+ ->willReturn($this->response);
+ }
+
+
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals($imgs, $result);
+ }
+}
diff --git a/tests/unit/Service/Social/TumblrProviderTest.php b/tests/unit/Service/Social/TumblrProviderTest.php
new file mode 100644
index 000000000..79e1c931f
--- /dev/null
+++ b/tests/unit/Service/Social/TumblrProviderTest.php
@@ -0,0 +1,100 @@
+
+ *
+ * @author Matthias Heinisch
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+
+namespace OCA\Contacts\Service\Social;
+
+use ChristophWurst\Nextcloud\Testing\TestCase;
+
+class TumblrProviderTest extends TestCase {
+ private $provider;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->provider = new TumblrProvider(
+ );
+ }
+
+ public function dataProviderSupportsContact() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "username1", "type" => "tumblr"],
+ ["value" => "username2", "type" => "tumblr"]
+ ]
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+
+ return [
+ 'contact with email' => [$contactWithSocial, true],
+ 'contact without email' => [$contactWithoutSocial, false]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderSupportsContact
+ */
+ public function testSupportsContact($contact, $expected) {
+ $result = $this->provider->supportsContact($contact);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function dataProviderGetImageUrls() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "username1", "type" => "tumblr"],
+ ["value" => "username2", "type" => "tumblr"]
+ ]
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+
+ foreach ($contactWithSocial['X-SOCIALPROFILE'] as $profile) {
+ $urls[] = "https://api.tumblr.com/v2/blog/".$profile['value']."/avatar/512";
+ }
+
+ return [
+ 'contact with email' => [$contactWithSocial, $urls],
+ 'contact without email' => [$contactWithoutSocial, []]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderGetImageUrls
+ */
+ public function testGetImageUrls($contact, $expected) {
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals($expected, $result);
+ }
+}
diff --git a/tests/unit/Service/Social/TwitterProviderTest.php b/tests/unit/Service/Social/TwitterProviderTest.php
new file mode 100644
index 000000000..55515b0ff
--- /dev/null
+++ b/tests/unit/Service/Social/TwitterProviderTest.php
@@ -0,0 +1,154 @@
+
+ *
+ * @author Matthias Heinisch
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+
+namespace OCA\Contacts\Service\Social;
+
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IResponse;
+use OCP\Http\Client\IClientService;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class TwitterProviderTest extends TestCase {
+ private $provider;
+
+ /** @var IClientService|MockObject */
+ private $clientService;
+
+ /** @var IClient|MockObject */
+ private $client;
+
+ /** @var IResponse|MockObject */
+ private $response;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->clientService = $this->createMock(IClientService::class);
+ $this->response = $this->createMock(IResponse::class);
+ $this->client = $this->createMock(IClient::class);
+
+ $this->clientService
+ ->method('NewClient')
+ ->willReturn($this->client);
+
+ $this->provider = new TwitterProvider(
+ $this->clientService
+ );
+ }
+
+ public function dataProviderSupportsContact() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "username1", "type" => "twitter"],
+ ["value" => "username2", "type" => "twitter"]
+ ]
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+
+ return [
+ 'contact with twitter fields' => [$contactWithSocial, true],
+ 'contact without twitter fields' => [$contactWithoutSocial, false]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderSupportsContact
+ */
+ public function testSupportsContact($contact, $expected) {
+ $result = $this->provider->supportsContact($contact);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function dataProviderGetImageUrls() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "https://twitter.com/username1", "type" => "twitter"],
+ ["value" => "https://twitter.com/@username2", "type" => "twitter"]
+ ]
+ ];
+ $contactWithSocialUrls = [
+ "https://mobile.twitter.com/username1",
+ "https://mobile.twitter.com/username2",
+ ];
+ $contactWithSocialHtml = [
+ '',
+ '',
+ ];
+ $contactWithSocialImgs = [
+ "username1_400x400.jpg",
+ "username2_400x400.jpg"
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+ $contactWithoutSocialUrls = [];
+ $contactWithoutSocialHtml = [];
+ $contactWithoutSocialImgs = [];
+
+ return [
+ 'contact with twitter fields' => [
+ $contactWithSocial,
+ $contactWithSocialHtml,
+ $contactWithSocialUrls,
+ $contactWithSocialImgs
+ ],
+ 'contact without twitter fields' => [
+ $contactWithoutSocial,
+ $contactWithoutSocialHtml,
+ $contactWithoutSocialUrls,
+ $contactWithoutSocialImgs
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderGetImageUrls
+ */
+ public function testGetImageUrls($contact, $htmls, $urls, $imgs) {
+ if (count($urls)) {
+ $this->response->method("getBody")->willReturnOnConsecutiveCalls(...$htmls);
+ $this->client
+ ->expects($this->exactly(count($urls)))
+ ->method("get")
+ ->withConsecutive(...array_map(function ($a) {
+ return [$a];
+ }, $urls))
+ ->willReturn($this->response);
+ }
+
+
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals($imgs, $result);
+ }
+}
diff --git a/tests/unit/Service/Social/XingProviderTest.php b/tests/unit/Service/Social/XingProviderTest.php
new file mode 100644
index 000000000..cb417346d
--- /dev/null
+++ b/tests/unit/Service/Social/XingProviderTest.php
@@ -0,0 +1,156 @@
+
+ *
+ * @author Matthias Heinisch
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+
+namespace OCA\Contacts\Service\Social;
+
+use OCP\Http\Client\IClient;
+use OCP\Http\Client\IResponse;
+use OCP\Http\Client\IClientService;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
+
+class XingProviderTest extends TestCase {
+ private $provider;
+
+ /** @var IClientService|MockObject */
+ private $clientService;
+
+ /** @var IClient|MockObject */
+ private $client;
+
+ /** @var IResponse|MockObject */
+ private $response;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->clientService = $this->createMock(IClientService::class);
+ $this->response = $this->createMock(IResponse::class);
+ $this->client = $this->createMock(IClient::class);
+
+ $this->clientService
+ ->method('NewClient')
+ ->willReturn($this->client);
+
+ $this->provider = new XingProvider(
+ $this->clientService
+ );
+ }
+
+ public function dataProviderSupportsContact() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "username1", "type" => "xing"],
+ ["value" => "username2", "type" => "xing"]
+ ]
+ ];
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+
+ return [
+ 'contact with xing fields' => [$contactWithSocial, true],
+ 'contact without xing fields' => [$contactWithoutSocial, false]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderSupportsContact
+ */
+ public function testSupportsContact($contact, $expected) {
+ $result = $this->provider->supportsContact($contact);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function dataProviderGetImageUrls() {
+ $contactWithSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "username1", "type" => "xing"],
+ ["value" => "username2", "type" => "xing"]
+ ]
+ ];
+ $contactWithSocialUrls = [
+ "https://www.xing.com/profile/username1",
+ "https://www.xing.com/profile/username2"
+ ];
+ $contactWithSocialHtml = array_map(function ($profile) {
+ return '';
+ }, $contactWithSocial['X-SOCIALPROFILE']);
+ $contactWithSocialImg = array_map(function ($profile) {
+ return 'https://profile-images-abc'.$profile['value'].".jpg";
+ }, $contactWithSocial['X-SOCIALPROFILE']);
+
+ $contactWithoutSocial = [
+ 'X-SOCIALPROFILE' => [
+ ["value" => "one", "type" => "social2"],
+ ["value" => "two", "type" => "social1"]
+ ]
+ ];
+ $contactWithoutSocialUrls = [];
+ $contactWithoutSocialHtml = [];
+ $contactWithoutSocialImg = [];
+
+ return [
+ 'contact with xing fields' => [
+ $contactWithSocial,
+ $contactWithSocialUrls,
+ $contactWithSocialHtml,
+ $contactWithSocialImg
+ ],
+ 'contact without xing fields' => [
+ $contactWithoutSocial,
+ $contactWithoutSocialUrls,
+ $contactWithoutSocialHtml,
+ $contactWithoutSocialImg
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataProviderGetImageUrls
+ */
+ public function testGetImageUrls($contact, $urls, $htmls, $imgs) {
+ if (count($urls)) {
+ $this->response
+ ->method('getBody')
+ ->willReturnOnConsecutiveCalls(...$htmls);
+
+ $urlArgs = array_map(function ($url) {
+ return [$url];
+ }, $urls);
+
+ $this->client
+ ->expects($this->exactly(count($urls)))
+ ->method('get')
+ ->withConsecutive(...$urlArgs)
+ ->willReturn($this->response);
+ }
+
+ $result = $this->provider->getImageUrls($contact);
+ $this->assertEquals($imgs, $result);
+ }
+}
diff --git a/tests/unit/Service/SocialApiServiceTest.php b/tests/unit/Service/SocialApiServiceTest.php
index 327bf1610..e7f4c8e3f 100644
--- a/tests/unit/Service/SocialApiServiceTest.php
+++ b/tests/unit/Service/SocialApiServiceTest.php
@@ -25,6 +25,7 @@
namespace OCA\Contacts\Service;
use OCA\Contacts\Service\Social\CompositeSocialProvider;
+use OCA\Contacts\Service\Social\ISocialProvider;
use OCP\AppFramework\Http;
use OCP\Http\Client\IClient;
@@ -45,7 +46,7 @@
class SocialApiServiceTest extends TestCase {
private $service;
- /** @var CompositeSocialProvider */
+ /** @var CompositeSocialProvider|MockObject */
private $socialProvider;
/** @var IManager|MockObject */
private $manager;
@@ -53,22 +54,56 @@ class SocialApiServiceTest extends TestCase {
private $config;
/** @var IClientService|MockObject */
private $clientService;
- /** @var IL10N|MockObject */
+ /** @var IL10N|MockObject */
private $l10n;
- /** @var IURLGenerator|MockObject */
+ /** @var IURLGenerator|MockObject */
private $urlGen;
/** @var CardDavBackend|MockObject */
private $davBackend;
/** @var ITimeFactory|MockObject */
private $timeFactory;
- public function socialProfileProvider() {
+ public function allSocialProfileProviders() {
+ $body = "the body";
+ $imageType = "jpg";
+ $contact = [
+ 'URI' => '3225c0d5-1bd2-43e5-a08c-4e65eaa406b0',
+ 'VERSION' => '4.0'
+ ];
+ $connector = $this->createMock(ISocialProvider::class);
+ $connector->method('supportsContact')->willReturn(true);
+ $connector->method('getImageUrls')->willReturn(["url1"]);
+
+ $connectorNoSupport = $this->createMock(ISocialProvider::class);
+ $connectorNoSupport->method('supportsContact')->willReturn(false);
+
+ $connectorNoUrl = $this->createMock(ISocialProvider::class);
+ $connectorNoUrl->method('supportsContact')->willReturn(true);
+ $connectorNoUrl->method('getImageUrls')->willReturn([]);
+
+ $addressbookEmpty = $this->createMock(IAddressBook::class);
+ $addressbookEmpty
+ ->method('getUri')
+ ->willReturn('contacts');
+ $addressbookEmpty
+ ->method('search')
+ ->willReturn(null);
+
+ $addressbook = $this->createMock(IAddressBook::class);
+ $addressbook
+ ->method('getUri')
+ ->willReturn('contacts');
+ $addressbook
+ ->method('search')
+ ->willReturn([$contact]);
+
return [
- 'no social profiles set' => [null, 'someConnector', 'someResult', Http::STATUS_PRECONDITION_FAILED],
- 'valid social profile' => [[['type' => 'someNetwork', 'value' => 'someId']], 'someConnector', 'someResult', Http::STATUS_OK],
- 'bad formatted profile id' => [[['type' => 'someNetwork', 'value' => 'someId']], null, 'someResult', Http::STATUS_BAD_REQUEST],
- 'not existing profile id' => [[['type' => 'someNetwork', 'value' => 'someId']], 'someConnector', '', Http::STATUS_NOT_FOUND],
- 'unchanged data' => [[['type' => 'someNetwork', 'value' => 'someId']], 'someConnector', 'thePhoto', Http::STATUS_NOT_MODIFIED],
+ 'no address book found' => [null, [], "", "", Http::STATUS_BAD_REQUEST],
+ 'no contact found' => [[$addressbookEmpty], [], "", "", Http::STATUS_PRECONDITION_FAILED],
+ 'no supporting contacts found' => [[$addressbook], [$connectorNoSupport], "", "", Http::STATUS_PRECONDITION_FAILED],
+ 'no url found' => [[$addressbook], [$connectorNoUrl], "", "", Http::STATUS_BAD_REQUEST],
+ 'no image found' => [[$addressbook], [$connector], "", "", Http::STATUS_NOT_FOUND],
+ 'image found' => [[$addressbook], [$connector], $body, $imageType, Http::STATUS_OK]
];
}
@@ -114,15 +149,54 @@ public function testDeactivatedSocial() {
}
/**
- * @dataProvider socialProfileProvider
+ * @dataProvider allSocialProfileProviders
*/
- public function testUpdateContact($social, $connector, $httpResult, $expected) {
+ public function testUpdateContactWithoutNetwork($addressbooks, $providers, $body, $imageType, $status) {
+ $this->manager
+ ->method('getUserAddressBooks')
+ ->willReturn($addressbooks);
+
+ $this->socialProvider
+ ->method('getSocialConnectors')
+ ->willReturn($providers);
+
+ $response = $this->createMock(IResponse::class);
+ $response
+ ->method('getBody')
+ ->willReturn($body);
+ $response
+ ->method('getHeader')
+ ->willReturn($imageType);
+ $client = $this->createMock(IClient::class);
+ $client
+ ->method('get')
+ ->willReturn($response);
+ $this->clientService
+ ->method('NewClient')
+ ->willReturn($client);
+
+ $result = $this->service
+ ->updateContact(
+ 'contacts',
+ '3225c0d5-1bd2-43e5-a08c-4e65eaa406b0',
+ null);
+ $this->assertEquals($status, $result->getStatus());
+ }
+
+ public function testUpdateContactWithNetwork() {
+ $network = "mastodon";
+ $body = "the body";
+ $imageType = "jpg";
+ $addressBookId = "contacts";
+ $contactId = "3225c0d5-1bd2-43e5-a08c-4e65eaa406b0";
$contact = [
- 'URI' => '3225c0d5-1bd2-43e5-a08c-4e65eaa406b0',
- 'VERSION' => '4.0',
- 'PHOTO' => "data:" . $httpResult . ";base64," . base64_encode('thePhoto'),
- 'X-SOCIALPROFILE' => $social,
+ 'URI' => $contactId,
+ 'VERSION' => '4.0'
];
+ $provider = $this->createMock(ISocialProvider::class);
+ $provider->method('supportsContact')->willReturn(true);
+ $provider->method('getImageUrls')->willReturn(["url1"]);
+
$addressbook = $this->createMock(IAddressBook::class);
$addressbook
->method('getUri')
@@ -135,17 +209,21 @@ public function testUpdateContact($social, $connector, $httpResult, $expected) {
->method('getUserAddressBooks')
->willReturn([$addressbook]);
+ $this->socialProvider
+ ->method('getSocialConnectors')
+ ->willReturn([$provider]);
+
$this->socialProvider
->method('getSocialConnector')
- ->willReturn($connector);
+ ->willReturn($provider);
$response = $this->createMock(IResponse::class);
$response
->method('getBody')
- ->willReturn($httpResult);
+ ->willReturn($body);
$response
->method('getHeader')
- ->willReturn($httpResult);
+ ->willReturn($imageType);
$client = $this->createMock(IClient::class);
$client
->method('get')
@@ -154,9 +232,24 @@ public function testUpdateContact($social, $connector, $httpResult, $expected) {
->method('NewClient')
->willReturn($client);
- $result = $this->service->updateContact('contacts', '3225c0d5-1bd2-43e5-a08c-4e65eaa406b0', 'theSocialNetwork');
+ $changes = [
+ 'URI' => $contact['URI'],
+ 'VERSION' => $contact['VERSION'],
+ 'PHOTO' => "data:".$imageType.";base64," . base64_encode($body)
+ ];
+
+ $this->socialProvider
+ ->expects($this->once())->method("getSocialConnector")->with($network);
+ $provider->expects($this->once())->method("supportsContact")->with($contact);
+ $addressbook->expects($this->once())->method("createOrUpdate")->with($changes, $addressBookId);
- $this->assertEquals($expected, $result->getStatus());
+ $result = $this->service
+ ->updateContact(
+ $addressBookId,
+ $contactId,
+ $network);
+
+ $this->assertEquals(Http::STATUS_OK, $result->getStatus());
}
protected function setupAddressbooks() {
@@ -219,15 +312,33 @@ protected function setupAddressbooks() {
->method('getUserAddressBooks')
->willReturn([$addressbook1, $addressbook2]);
- $socialConnectorMap = [
- [$validContact1['X-SOCIALPROFILE'], 'any', 'validConnector'],
- [$validContact2['X-SOCIALPROFILE'], 'any', 'validConnector'],
- [$invalidContact['X-SOCIALPROFILE'], 'any', 'invalidConnector'],
- [$emptyContact['X-SOCIALPROFILE'], 'any', 'emptyConnector'],
+ $providerSupportsMap = [
+ [$validContact1, true],
+ [$emptyContact, false],
+ [$invalidContact, false],
+ [$validContact2, true]
];
+
+ $providerUrlMap = [
+ [$validContact1, ["url1"]],
+ [$emptyContact, []],
+ [$invalidContact, []],
+ [$validContact2, ["url1"]]
+ ];
+
+ $provider = $this->createMock(ISocialProvider::class);
+ $provider->method('getImageUrls')
+ ->will($this->returnValueMap($providerUrlMap));
+ $provider->method('supportsContact')
+ ->will($this->returnValueMap($providerSupportsMap));
+
+ $this->socialProvider
+ ->method('getSocialConnectors')
+ ->willReturn([$provider]);
+
$this->socialProvider
->method('getSocialConnector')
- ->will($this->returnValueMap($socialConnectorMap));
+ ->willReturn($provider);
$validResponse = $this->createMock(IResponse::class);
$validResponse
@@ -236,29 +347,16 @@ protected function setupAddressbooks() {
$validResponse
->method('getHeader')
->willReturn('someHeader');
- $invalidResponse = $this->createMock(IResponse::class);
- $invalidResponse
- ->method('getBody')
- ->willReturn('');
- $invalidResponse
- ->method('getHeader')
- ->willReturn('');
- $clientResponseMap = [
- ['validConnector', [], $validResponse],
- ['invalidConnector', [], $invalidResponse],
- ['emptyConnector', [], $invalidResponse],
- ];
$client = $this->createMock(IClient::class);
$client
->method('get')
- ->will($this->returnValueMap($clientResponseMap));
+ ->willReturn($validResponse);
$this->clientService
->method('NewClient')
->willReturn($client);
}
-
/**
* @dataProvider updateAddressbookProvider
*/
@@ -287,9 +385,9 @@ public function testUpdateAddressbooks($syncAllowedByAdmin, $bgSyncEnabledByUser
$this->assertArrayHasKey('checked', $report[0]);
$this->assertContains('Valid Contact Two', $report[0]['checked']);
$this->assertArrayHasKey('failed', $report[0]);
- $this->assertArrayHasKey('404', $report[0]['failed']);
- $this->assertContains('Invalid Contact', $report[0]['failed']['404']);
- $this->assertNotContains('Empty Contact', $report[0]['failed']['404']);
+ $this->assertArrayHasKey('412', $report[0]['failed']);
+ $this->assertContains('Invalid Contact', $report[0]['failed']['412']);
+ $this->assertContains('Empty Contact', $report[0]['failed']['412']);
}
}