Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get contact pictures from social networks #1580

Closed
wants to merge 78 commits into from
Closed
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
2fc64dd
first steps with vue, editing the menues
Mar 30, 2020
18ed4e5
added setup instructions
Mar 30, 2020
7d23da2
hide menu when no facebook id present
Mar 31, 2020
057c6ce
higher resolution pictures
Apr 7, 2020
acad8ff
moved all profile pic stuff into ContactDetailsAvatar
Apr 8, 2020
93ce55c
moved profile loader to page controller
Apr 12, 2020
23672ef
add demo image
Apr 12, 2020
9fcf232
handle profile ids also if uri
Apr 13, 2020
4e10add
improved readability
Apr 13, 2020
f86aba8
preparation for avatar settings page to update complete addressbook
Apr 13, 2020
f82fff0
creation of empty settings page
Apr 13, 2020
56e7a97
separated avatar logic
Apr 14, 2020
95f1eb6
considering recommendation from Codacy/PR Quality Review
call-me-matt Apr 14, 2020
09df2f4
moved code from frontend to backend
Apr 15, 2020
b536fea
Merge remote-tracking branch 'upstream/master'
call-me-matt Apr 15, 2020
21927f9
renaming the route into API
call-me-matt Apr 16, 2020
12db28b
implemented contact access in SocialApiController
call-me-matt Apr 16, 2020
19a59bd
ported code to php, fixed responses
call-me-matt Apr 17, 2020
62a98e3
axios token to prevent CSRF exception
call-me-matt Apr 18, 2020
00fccc4
retrieve updated contact
call-me-matt Apr 18, 2020
9cb71e7
Merge remote-tracking branch 'upstream/master'
call-me-matt Apr 18, 2020
8a6a00c
testing only - refresh view after backend updates
call-me-matt Apr 18, 2020
28177a0
fixup! testing only - refresh view after backend updates
skjnldsv Apr 18, 2020
a345eb5
removed test dummy
call-me-matt Apr 18, 2020
c6361ec
first steps towards unit testing
call-me-matt Apr 18, 2020
c260ec2
first steps with unit tests
call-me-matt Apr 19, 2020
c5e5b9c
improving code as reviewed by skjnldsv
call-me-matt Apr 19, 2020
f74139c
accept only supported social networks
call-me-matt Apr 19, 2020
a184e98
retrieve list of supported networks from socialAPI
call-me-matt Apr 19, 2020
51771f1
added support for avatar.io/twitter
call-me-matt Apr 19, 2020
4d2a96a
notify if avatar unchanged
call-me-matt Apr 20, 2020
d990d64
using initial-state to load supported networks
call-me-matt Apr 20, 2020
3eba54f
constants for network connectors, added support for tumblr, removed a…
call-me-matt Apr 21, 2020
55ccf25
added vcard support for v3 and higher
call-me-matt Apr 21, 2020
ff69634
removing number check for fb to allow for company avatars
call-me-matt Apr 21, 2020
e58ddb4
Merge remote-tracking branch 'upstream/master'
call-me-matt Apr 21, 2020
25d6af7
creating stubs for testing
call-me-matt Apr 22, 2020
037444b
allow regex cleanups, data provider for unittests
call-me-matt Apr 22, 2020
eccae0c
icon change from globe to sync
call-me-matt Apr 22, 2020
9a34d07
improved code style after review
call-me-matt Apr 23, 2020
2cc83bf
added lock file for npm lint
call-me-matt Apr 23, 2020
164b585
allowing for selection of social network
call-me-matt Apr 23, 2020
8d7de9b
adapted error return codes
call-me-matt Apr 25, 2020
d8e2937
code cleanup, removed type for less complexity
call-me-matt Apr 25, 2020
b6e1731
removed doubled test case
call-me-matt Apr 25, 2020
87f5f75
improved as recommended by codacy
call-me-matt Apr 25, 2020
63de675
separated vcard image tag creation into own function
call-me-matt Apr 25, 2020
3b3dfdd
reducing complexity
call-me-matt Apr 25, 2020
db096f0
Merge remote-tracking branch 'upstream/master'
call-me-matt Apr 28, 2020
6a813cf
force re-fetch of contacts after update
call-me-matt Apr 28, 2020
cb30db5
prevent error logs for contacts without social profiles/avatars
call-me-matt May 1, 2020
63f05e2
renaming & compatibility with nextcloud v19
call-me-matt May 4, 2020
ba3c38b
using dependency injection
call-me-matt May 4, 2020
2bc3d03
added instagram
call-me-matt May 6, 2020
07426a5
split controller into service
call-me-matt May 7, 2020
4d9e45d
added admin setting to (de)activate social media integration
call-me-matt May 7, 2020
f1aac0b
enable social sync by default
call-me-matt May 7, 2020
81e55f2
changing for vuejs (trying)
call-me-matt May 8, 2020
2f0979e
admin settings & not case sensitive network names
May 9, 2020
133e9b9
reorder menu items according to priority
May 10, 2020
51ee1da
moved all logic from controller to service
call-me-matt May 11, 2020
e5efde0
individual icons per social network (dummies)
call-me-matt May 11, 2020
637fb80
split service into providers
call-me-matt May 15, 2020
0d69e4f
allowing for nextcloud v20
call-me-matt May 15, 2020
2f4becf
code improvements according to codacy
call-me-matt May 15, 2020
c10dc11
added twitter
call-me-matt May 16, 2020
7eb403e
added Mastodon
call-me-matt May 17, 2020
7857b91
Merge remote-tracking branch 'upstream/master'
call-me-matt May 30, 2020
638495a
delete previous photos during update for vCard version 3.0
call-me-matt Jun 18, 2020
04e4a24
cleanup (variables)
call-me-matt Jun 18, 2020
b67c85d
changed icon names to lower case
call-me-matt Jul 9, 2020
ae14dbe
fixed identation
call-me-matt Jul 9, 2020
c131de8
formatting and styling issues
call-me-matt Jul 9, 2020
30eb73f
never trust your inputs
call-me-matt Jul 9, 2020
6ae6abd
using app name from config
call-me-matt Jul 10, 2020
f6eff74
changed method for avatar change to PUT
call-me-matt Jul 10, 2020
381e7b8
use Nextcloud's HTTP service
call-me-matt Jul 10, 2020
09ef3ee
add social icons
call-me-matt Jul 10, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@
<contactsmenu>
<provider>OCA\Contacts\ContactsMenu\Providers\DetailsProvider</provider>
</contactsmenu>

<settings>
<admin>OCA\Contacts\Settings\AdminSettings</admin>
</settings>
</info>
4 changes: 3 additions & 1 deletion appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'page#index', 'url' => '/{group}', 'verb' => 'GET', 'postfix' => 'group'],
['name' => 'page#index', 'url' => '/{group}/{contact}', 'verb' => 'GET', 'postfix' => 'group.contact']
['name' => 'page#index', 'url' => '/{group}/{contact}', 'verb' => 'GET', 'postfix' => 'group.contact'],
['name' => 'social_api#update_contact', 'url' => '/api/v1/social/avatar/{network}/{addressbookId}/{contactId}', 'verb' => 'GET'],
call-me-matt marked this conversation as resolved.
Show resolved Hide resolved
['name' => 'social_api#set_app_config', 'url' => '/api/v1/social/config/{key}', 'verb' => 'POST'],
call-me-matt marked this conversation as resolved.
Show resolved Hide resolved
]
];
8 changes: 8 additions & 0 deletions css/icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
@include icon-black-white('no-calendar', 'contacts', 1);
@include icon-black-white('language', 'contacts', 2);
@include icon-black-white('clone', 'contacts', 2);
@include icon-black-white('sync', 'contacts', 2);

// social network icons:
@include icon-black-white('facebook', 'contacts', 2);
@include icon-black-white('instagram', 'contacts', 2);
@include icon-black-white('mastodon', 'contacts', 2);
@include icon-black-white('tumblr', 'contacts', 2);
@include icon-black-white('twitter', 'contacts', 2);

.icon-up-force-white {
// using #fffffe to trick the accessibility dark theme icon invert
Expand Down
3 changes: 3 additions & 0 deletions img/facebook.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions img/instagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions img/mastodon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions img/sync.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions img/tumblr.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions img/twitter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class Application extends App {
public function __construct() {
parent::__construct(self::APP_ID);
}

public const AVAIL_SETTINGS = [
'allowSocialSync' => 'yes',
];

public function register() {
$server = $this->getContainer()->getServer();
Expand Down
18 changes: 13 additions & 5 deletions lib/Controller/PageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use OCP\IRequest;
use OCP\L10N\IFactory;
use OCP\Util;
use OCA\Contacts\Service\SocialApiService;

class PageController extends Controller {

Expand All @@ -44,17 +45,22 @@ class PageController extends Controller {
/** @var IFactory */
private $languageFactory;

/** @var SocialApiService */
private $socialApiService;

public function __construct(string $appName,
IRequest $request,
IConfig $config,
IInitialStateService $initialStateService,
IFactory $languageFactory) {
IRequest $request,
IConfig $config,
IInitialStateService $initialStateService,
IFactory $languageFactory,
SocialApiService $socialApiService) {
parent::__construct($appName, $request);

$this->appName = $appName;
$this->config = $config;
$this->initialStateService = $initialStateService;
$this->languageFactory = $languageFactory;
$this->socialApiService = $socialApiService;
}

/**
Expand All @@ -66,10 +72,12 @@ public function __construct(string $appName,
public function index(): TemplateResponse {
$locales = $this->languageFactory->findAvailableLocales();
$defaultProfile = $this->config->getAppValue($this->appName, 'defaultProfile', 'HOME');
$supportedNetworks = $this->socialApiService->getSupportedNetworks();

$this->initialStateService->provideInitialState($this->appName, 'locales', $locales);
$this->initialStateService->provideInitialState($this->appName, 'defaultProfile', $defaultProfile);

$this->initialStateService->provideInitialState($this->appName, 'supportedNetworks', $supportedNetworks);

Util::addScript($this->appName, 'contacts');
Util::addStyle($this->appName, 'contacts');

Expand Down
97 changes: 97 additions & 0 deletions lib/Controller/SocialApiController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* @copyright Copyright (c) 2020 Matthias Heinisch <nextcloud@matthiasheinisch.de>
*
* @author Matthias Heinisch <nextcloud@matthiasheinisch.de>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Contacts\Controller;

use OCP\IConfig;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;

use OCA\Contacts\Service\SocialApiService;
use OCA\Contacts\AppInfo\Application;


class SocialApiController extends ApiController {

/** @var IConfig */
private $config;
/** @var SocialApiService */
private $socialApiService;

public function __construct(
IRequest $request,
IConfig $config,
SocialApiService $socialApiService) {
parent::__construct(Application::APP_ID, $request);

$this->config = $config;
$this->socialApiService = $socialApiService;
}


/**
* update appconfig (admin setting)
*
* @param {String} key the identifier to change
* @param {String} allow the value to set
*
* @returns {JSONResponse} an empty JSONResponse with respective http status code
*/
public function setAppConfig($key, $allow) {
$permittedKeys = ['allowSocialSync'];
if (!in_array($key, $permittedKeys)) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
$this->config->setAppValue(Application::APP_ID, $key, $allow);
return new JSONResponse([], Http::STATUS_OK);
}

/**
* @NoAdminRequired
*
* returns an array of supported social networks
*
* @returns {array} array of the supported social networks
*/
public function getSupportedNetworks() : array {
return $this->socialApiService->getSupportedNetworks();
}


/**
* @NoAdminRequired
*
* Retrieves social profile data for a contact and updates the entry
*
* @param {String} addressbookId the addressbook identifier
* @param {String} contactId the contact identifier
* @param {String} network the social network to use (if unkown: take first match)
*
* @returns {JSONResponse} an empty JSONResponse with respective http status code
*/
public function updateContact(string $addressbookId, string $contactId, string $network) : JSONResponse {
return $this->socialApiService->updateContact($addressbookId, $contactId, $network);
}
}
97 changes: 97 additions & 0 deletions lib/Service/Social/CompositeSocialProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* @copyright 2020 Matthias Heinisch <nextcloud@matthiasheinisch.de>
*
* @author Matthias Heinisch <nextcloud@matthiasheinisch.de>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Contacts\Service\Social;

/**
* Composition of all social providers for easier usage
*/
class CompositeSocialProvider {

/** @var ISocialProvider[] */
private $providers;

public function __construct(InstagramProvider $instagramProvider,
MastodonProvider $mastodonProvider,
FacebookProvider $facebookProvider,
TwitterProvider $twitterProvider,
TumblrProvider $tumblrProvider) {

// This determines the priority of known providers
$this->providers = [
'instagram' => $instagramProvider,
'mastodon' => $mastodonProvider,
'twitter' => $twitterProvider,
'facebook' => $facebookProvider,
'tumblr' => $tumblrProvider,
];
}

/**
* returns an array of supported social providers
*
* @returns String[] array of the supported social networks
*/
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)
*
* @returns String the url to the requested information or null in case of errors
*/
public function getSocialConnector(array $socialEntries, string $network) : ?string {

$connector = null;
$selection = $this->providers;
// check if dedicated network selected
if (isset($this->providers[$network])) {
$selection = [$network => $this->providers[$network]];
}

// 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);
call-me-matt marked this conversation as resolved.
Show resolved Hide resolved
}
break;
}
}
if ($connector) {
break;
}
}
return ($connector);
}
}
55 changes: 55 additions & 0 deletions lib/Service/Social/FacebookProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/**
* @copyright Copyright (c) 2020 Matthias Heinisch <nextcloud@matthiasheinisch.de>
*
* @author Matthias Heinisch <nextcloud@matthiasheinisch.de>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/

namespace OCA\Contacts\Service\Social;

class FacebookProvider implements ISocialProvider {

public function __construct() {
}

/**
* Returns the profile-id
*
* @param {string} the value from the contact's x-socialprofile
*
* @return string
*/
public function cleanupId(string $candidate):?string {
return basename($candidate);
}

/**
* Returns the profile-picture url
*
* @param {string} profileId the profile-id
*
* @return string|null
*/
public function getImageUrl(string $profileId):?string {
$recipe = 'https://graph.facebook.com/{socialId}/picture?width=720';
$connector = str_replace("{socialId}", $profileId, $recipe);
return $connector;
}

}
Loading