├── CHANGELOG.md ├── LICENSE ├── LinkedInOptions.php ├── LinkedInTransport.php ├── LinkedInTransportFactory.php ├── README.md ├── Share ├── AbstractLinkedInShare.php ├── AuthorShare.php ├── LifecycleStateShare.php ├── ShareContentShare.php ├── ShareMediaShare.php └── VisibilityShare.php └── composer.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | 4 | 5.3 5 | --- 6 | 7 | * The bridge is not marked as `@experimental` anymore 8 | * [BC BREAK] `LinkedInTransportFactory` is now final 9 | 10 | 5.2.0 11 | ----- 12 | 13 | * Added the bridge 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-present Fabien Potencier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /LinkedInOptions.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\LinkedIn; 13 | 14 | use Symfony\Component\Notifier\Bridge\LinkedIn\Share\AuthorShare; 15 | use Symfony\Component\Notifier\Bridge\LinkedIn\Share\LifecycleStateShare; 16 | use Symfony\Component\Notifier\Bridge\LinkedIn\Share\ShareContentShare; 17 | use Symfony\Component\Notifier\Bridge\LinkedIn\Share\VisibilityShare; 18 | use Symfony\Component\Notifier\Message\MessageOptionsInterface; 19 | use Symfony\Component\Notifier\Notification\Notification; 20 | 21 | /** 22 | * @author Smaïne Milianni 23 | */ 24 | final class LinkedInOptions implements MessageOptionsInterface 25 | { 26 | public function __construct( 27 | private array $options = [], 28 | ) { 29 | } 30 | 31 | public function toArray(): array 32 | { 33 | return $this->options; 34 | } 35 | 36 | public function getRecipientId(): ?string 37 | { 38 | return null; 39 | } 40 | 41 | public static function fromNotification(Notification $notification): self 42 | { 43 | $options = new self(); 44 | $options->specificContent(new ShareContentShare($notification->getSubject())); 45 | 46 | if ($notification->getContent()) { 47 | $options->specificContent(new ShareContentShare($notification->getContent())); 48 | } 49 | 50 | $options->visibility(new VisibilityShare()); 51 | $options->lifecycleState(new LifecycleStateShare()); 52 | 53 | return $options; 54 | } 55 | 56 | /** 57 | * @return $this 58 | */ 59 | public function contentCertificationRecord(string $contentCertificationRecord): static 60 | { 61 | $this->options['contentCertificationRecord'] = $contentCertificationRecord; 62 | 63 | return $this; 64 | } 65 | 66 | /** 67 | * @return $this 68 | */ 69 | public function firstPublishedAt(int $firstPublishedAt): static 70 | { 71 | $this->options['firstPublishedAt'] = $firstPublishedAt; 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * @return $this 78 | */ 79 | public function lifecycleState(LifecycleStateShare $lifecycleStateOption): static 80 | { 81 | $this->options['lifecycleState'] = $lifecycleStateOption->lifecycleState(); 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * @return $this 88 | */ 89 | public function origin(string $origin): static 90 | { 91 | $this->options['origin'] = $origin; 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * @return $this 98 | */ 99 | public function ugcOrigin(string $ugcOrigin): static 100 | { 101 | $this->options['ugcOrigin'] = $ugcOrigin; 102 | 103 | return $this; 104 | } 105 | 106 | /** 107 | * @return $this 108 | */ 109 | public function versionTag(string $versionTag): static 110 | { 111 | $this->options['versionTag'] = $versionTag; 112 | 113 | return $this; 114 | } 115 | 116 | /** 117 | * @return $this 118 | */ 119 | public function specificContent(ShareContentShare $specificContent): static 120 | { 121 | $this->options['specificContent']['com.linkedin.ugc.ShareContent'] = $specificContent->toArray(); 122 | 123 | return $this; 124 | } 125 | 126 | /** 127 | * @return $this 128 | */ 129 | public function author(AuthorShare $authorOption): static 130 | { 131 | $this->options['author'] = $authorOption->author(); 132 | 133 | return $this; 134 | } 135 | 136 | /** 137 | * @return $this 138 | */ 139 | public function visibility(VisibilityShare $visibilityOption): static 140 | { 141 | $this->options['visibility'] = $visibilityOption->toArray(); 142 | 143 | return $this; 144 | } 145 | 146 | public function getAuthor(): ?string 147 | { 148 | return $this->options['author'] ?? null; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /LinkedInTransport.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\LinkedIn; 13 | 14 | use Symfony\Component\Notifier\Bridge\LinkedIn\Share\AuthorShare; 15 | use Symfony\Component\Notifier\Exception\TransportException; 16 | use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException; 17 | use Symfony\Component\Notifier\Exception\UnsupportedOptionsException; 18 | use Symfony\Component\Notifier\Message\ChatMessage; 19 | use Symfony\Component\Notifier\Message\MessageInterface; 20 | use Symfony\Component\Notifier\Message\SentMessage; 21 | use Symfony\Component\Notifier\Transport\AbstractTransport; 22 | use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; 23 | use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; 24 | use Symfony\Contracts\HttpClient\HttpClientInterface; 25 | 26 | /** 27 | * @author Smaïne Milianni 28 | * 29 | * @see https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/ugc-post-api#sharecontent 30 | */ 31 | final class LinkedInTransport extends AbstractTransport 32 | { 33 | protected const HOST = 'api.linkedin.com'; 34 | 35 | public function __construct( 36 | #[\SensitiveParameter] private string $authToken, 37 | private string $accountId, 38 | ?HttpClientInterface $client = null, 39 | ?EventDispatcherInterface $dispatcher = null, 40 | ) { 41 | parent::__construct($client, $dispatcher); 42 | } 43 | 44 | public function __toString(): string 45 | { 46 | return \sprintf('linkedin://%s', $this->getEndpoint()); 47 | } 48 | 49 | public function supports(MessageInterface $message): bool 50 | { 51 | return $message instanceof ChatMessage && (null === $message->getOptions() || $message->getOptions() instanceof LinkedInOptions); 52 | } 53 | 54 | /** 55 | * @see https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/ugc-post-api 56 | */ 57 | protected function doSend(MessageInterface $message): SentMessage 58 | { 59 | if (!$message instanceof ChatMessage) { 60 | throw new UnsupportedMessageTypeException(__CLASS__, ChatMessage::class, $message); 61 | } 62 | 63 | if (($options = $message->getOptions()) && !$options instanceof LinkedInOptions) { 64 | throw new UnsupportedOptionsException(__CLASS__, LinkedInOptions::class, $options); 65 | } 66 | 67 | if (!$options && $notification = $message->getNotification()) { 68 | $options = LinkedInOptions::fromNotification($notification); 69 | $options->author(new AuthorShare($this->accountId)); 70 | } 71 | 72 | $endpoint = \sprintf('https://%s/v2/ugcPosts', $this->getEndpoint()); 73 | 74 | $response = $this->client->request('POST', $endpoint, [ 75 | 'auth_bearer' => $this->authToken, 76 | 'headers' => ['X-Restli-Protocol-Version' => '2.0.0'], 77 | 'json' => array_filter($options?->toArray() ?? $this->bodyFromMessageWithNoOption($message)), 78 | ]); 79 | 80 | try { 81 | $statusCode = $response->getStatusCode(); 82 | } catch (TransportExceptionInterface $e) { 83 | throw new TransportException('Could not reach the remote LinkedIn server.', $response, 0, $e); 84 | } 85 | 86 | if (201 !== $statusCode) { 87 | throw new TransportException(\sprintf('Unable to post the Linkedin message: "%s".', $response->getContent(false)), $response); 88 | } 89 | 90 | $result = $response->toArray(false); 91 | 92 | if (!$result['id']) { 93 | throw new TransportException(\sprintf('Unable to post the Linkedin message: "%s".', $result['error']), $response); 94 | } 95 | 96 | $sentMessage = new SentMessage($message, (string) $this); 97 | $sentMessage->setMessageId($result['id']); 98 | 99 | return $sentMessage; 100 | } 101 | 102 | private function bodyFromMessageWithNoOption(MessageInterface $message): array 103 | { 104 | return [ 105 | 'specificContent' => [ 106 | 'com.linkedin.ugc.ShareContent' => [ 107 | 'shareCommentary' => [ 108 | 'attributes' => [], 109 | 'text' => $message->getSubject(), 110 | ], 111 | 'shareMediaCategory' => 'NONE', 112 | ], 113 | ], 114 | 'visibility' => [ 115 | 'com.linkedin.ugc.MemberNetworkVisibility' => 'PUBLIC', 116 | ], 117 | 'lifecycleState' => 'PUBLISHED', 118 | 'author' => \sprintf('urn:li:person:%s', $this->accountId), 119 | ]; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /LinkedInTransportFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\LinkedIn; 13 | 14 | use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; 15 | use Symfony\Component\Notifier\Transport\AbstractTransportFactory; 16 | use Symfony\Component\Notifier\Transport\Dsn; 17 | 18 | /** 19 | * @author Smaïne Milianni 20 | */ 21 | final class LinkedInTransportFactory extends AbstractTransportFactory 22 | { 23 | public function create(Dsn $dsn): LinkedInTransport 24 | { 25 | $scheme = $dsn->getScheme(); 26 | 27 | if ('linkedin' !== $scheme) { 28 | throw new UnsupportedSchemeException($dsn, 'linkedin', $this->getSupportedSchemes()); 29 | } 30 | 31 | $authToken = $this->getUser($dsn); 32 | $accountId = $this->getPassword($dsn); 33 | $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); 34 | $port = $dsn->getPort(); 35 | 36 | return (new LinkedInTransport($authToken, $accountId, $this->client, $this->dispatcher))->setHost($host)->setPort($port); 37 | } 38 | 39 | protected function getSupportedSchemes(): array 40 | { 41 | return ['linkedin']; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LinkedIn Notifier 2 | ================= 3 | 4 | Provides LinkedIn integration for Symfony Notifier. 5 | 6 | DSN example 7 | ----------- 8 | 9 | ``` 10 | LINKEDIN_DSN=linkedin://ACCESS_TOKEN:USER_ID@default 11 | ``` 12 | 13 | where: 14 | - `ACCESS_TOKEN` is your LinkedIn access token 15 | - `USER_ID` is your LinkedIn user id 16 | 17 | Resources 18 | --------- 19 | 20 | * [Contributing](https://symfony.com/doc/current/contributing/index.html) 21 | * [Report issues](https://github.com/symfony/symfony/issues) and 22 | [send Pull Requests](https://github.com/symfony/symfony/pulls) 23 | in the [main Symfony repository](https://github.com/symfony/symfony) 24 | -------------------------------------------------------------------------------- /Share/AbstractLinkedInShare.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\LinkedIn\Share; 13 | 14 | /** 15 | * @author Smaïne Milianni 16 | */ 17 | abstract class AbstractLinkedInShare 18 | { 19 | protected array $options = []; 20 | 21 | public function toArray(): array 22 | { 23 | return $this->options; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Share/AuthorShare.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\LinkedIn\Share; 13 | 14 | /** 15 | * @author Smaïne Milianni 16 | */ 17 | final class AuthorShare extends AbstractLinkedInShare 18 | { 19 | public const PERSON = 'person'; 20 | 21 | private string $author; 22 | 23 | public function __construct(string $value, string $organisation = self::PERSON) 24 | { 25 | $this->author = "urn:li:$organisation:$value"; 26 | } 27 | 28 | public function author(): string 29 | { 30 | return $this->author; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Share/LifecycleStateShare.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\LinkedIn\Share; 13 | 14 | use Symfony\Component\Notifier\Exception\LogicException; 15 | 16 | /** 17 | * @author Smaïne Milianni 18 | * 19 | * @see https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/ugc-post-api#schema lifecycleState section 20 | */ 21 | final class LifecycleStateShare extends AbstractLinkedInShare 22 | { 23 | public const DRAFT = 'DRAFT'; 24 | public const PUBLISHED = 'PUBLISHED'; 25 | public const PROCESSING = 'PROCESSING'; 26 | public const PROCESSING_FAILED = 'PROCESSING_FAILED'; 27 | public const DELETED = 'DELETED'; 28 | public const PUBLISHED_EDITED = 'PUBLISHED_EDITED'; 29 | 30 | private const AVAILABLE_LIFECYCLE = [ 31 | self::DRAFT, 32 | self::PUBLISHED, 33 | self::PROCESSING_FAILED, 34 | self::DELETED, 35 | self::PROCESSING_FAILED, 36 | self::PUBLISHED_EDITED, 37 | ]; 38 | 39 | private string $lifecycleState; 40 | 41 | public function __construct(string $lifecycleState = self::PUBLISHED) 42 | { 43 | if (!\in_array($lifecycleState, self::AVAILABLE_LIFECYCLE, true)) { 44 | throw new LogicException(\sprintf('"%s" is not a valid value, available lifecycle are "%s".', $lifecycleState, implode(', ', self::AVAILABLE_LIFECYCLE))); 45 | } 46 | 47 | $this->lifecycleState = $lifecycleState; 48 | } 49 | 50 | public function lifecycleState(): string 51 | { 52 | return $this->lifecycleState; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Share/ShareContentShare.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\LinkedIn\Share; 13 | 14 | use Symfony\Component\Notifier\Exception\LogicException; 15 | 16 | /** 17 | * @author Smaïne Milianni 18 | * 19 | * @see https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/ugc-post-api#sharecontent 20 | */ 21 | final class ShareContentShare extends AbstractLinkedInShare 22 | { 23 | public const ARTICLE = 'ARTICLE'; 24 | public const IMAGE = 'IMAGE'; 25 | public const NONE = 'NONE'; 26 | public const RICH = 'RICH'; 27 | public const VIDEO = 'VIDEO'; 28 | public const LEARNING_COURSE = 'LEARNING_COURSE'; 29 | public const JOB = 'JOB'; 30 | public const QUESTION = 'QUESTION'; 31 | public const ANSWER = 'ANSWER'; 32 | public const CAROUSEL = 'CAROUSEL'; 33 | public const TOPIC = 'TOPIC'; 34 | public const NATIVE_DOCUMENT = 'NATIVE_DOCUMENT'; 35 | public const URN_REFERENCE = 'URN_REFERENCE'; 36 | public const LIVE_VIDEO = 'LIVE_VIDEO'; 37 | 38 | public const ALL = [ 39 | self::ARTICLE, 40 | self::IMAGE, 41 | self::NONE, 42 | self::RICH, 43 | self::VIDEO, 44 | self::LEARNING_COURSE, 45 | self::JOB, 46 | self::QUESTION, 47 | self::ANSWER, 48 | self::CAROUSEL, 49 | self::TOPIC, 50 | self::NATIVE_DOCUMENT, 51 | self::URN_REFERENCE, 52 | self::LIVE_VIDEO, 53 | ]; 54 | 55 | public function __construct(string $text, array $attributes = [], ?string $inferredLocale = null, ?ShareMediaShare $media = null, ?string $primaryLandingPageUrl = null, string $shareMediaCategory = self::NONE) 56 | { 57 | $this->options['shareCommentary'] = [ 58 | 'attributes' => $attributes, 59 | 'text' => $text, 60 | ]; 61 | 62 | if (null !== $inferredLocale) { 63 | $this->options['shareCommentary']['inferredLocale'] = $inferredLocale; 64 | } 65 | 66 | if (null !== $media) { 67 | $this->options['media'] = $media->toArray(); 68 | } 69 | 70 | if (null !== $primaryLandingPageUrl) { 71 | $this->options['primaryLandingPageUrl'] = $primaryLandingPageUrl; 72 | } 73 | 74 | if ($shareMediaCategory) { 75 | if (!\in_array($shareMediaCategory, self::ALL, true)) { 76 | throw new LogicException(\sprintf('"%s" is not valid option, available options are "%s".', $shareMediaCategory, implode(', ', self::ALL))); 77 | } 78 | 79 | $this->options['shareMediaCategory'] = $shareMediaCategory; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Share/ShareMediaShare.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\LinkedIn\Share; 13 | 14 | use Symfony\Component\Notifier\Exception\LogicException; 15 | 16 | /** 17 | * @author Smaïne Milianni 18 | * 19 | * @see https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares/ugc-post-api#sharemedia 20 | */ 21 | class ShareMediaShare extends AbstractLinkedInShare 22 | { 23 | public const LEARN_MORE = 'LEARN_MORE'; 24 | public const APPLY_NOW = 'APPLY_NOW '; 25 | public const DOWNLOAD = 'DOWNLOAD'; 26 | public const GET_QUOTE = 'GET_QUOTE'; 27 | public const SIGN_UP = 'SIGN_UP'; 28 | public const SUBSCRIBE = 'SUBSCRIBE '; 29 | public const REGISTER = 'REGISTER'; 30 | 31 | public const ALL = [ 32 | self::LEARN_MORE, 33 | self::APPLY_NOW, 34 | self::DOWNLOAD, 35 | self::GET_QUOTE, 36 | self::SIGN_UP, 37 | self::SUBSCRIBE, 38 | self::REGISTER, 39 | ]; 40 | 41 | public function __construct(string $text, array $attributes = [], ?string $inferredLocale = null, bool $landingPage = false, ?string $landingPageTitle = null, ?string $landingPageUrl = null) 42 | { 43 | $this->options['description'] = [ 44 | 'text' => $text, 45 | 'attributes' => $attributes, 46 | ]; 47 | 48 | if ($inferredLocale) { 49 | $this->options['description']['inferredLocale'] = $inferredLocale; 50 | } 51 | 52 | if ($landingPage || $landingPageUrl) { 53 | $this->options['landingPage']['landingPageUrl'] = $landingPageUrl; 54 | } 55 | 56 | if (null !== $landingPageTitle) { 57 | if (!\in_array($landingPageTitle, self::ALL, true)) { 58 | throw new LogicException(\sprintf('"%s" is not valid option, available options are "%s".', $landingPageTitle, implode(', ', self::ALL))); 59 | } 60 | 61 | $this->options['landingPage']['landingPageTitle'] = $landingPageTitle; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Share/VisibilityShare.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Symfony\Component\Notifier\Bridge\LinkedIn\Share; 13 | 14 | use Symfony\Component\Notifier\Exception\LogicException; 15 | 16 | /** 17 | * @author Smaïne Milianni 18 | */ 19 | final class VisibilityShare extends AbstractLinkedInShare 20 | { 21 | public const MEMBER_NETWORK_VISIBILITY = 'MemberNetworkVisibility'; 22 | public const SPONSORED_CONTENT_VISIBILITY = 'SponsoredContentVisibility'; 23 | 24 | public const CONNECTIONS = 'CONNECTIONS'; 25 | public const PUBLIC = 'PUBLIC'; 26 | public const LOGGED_IN = 'LOGGED_IN'; 27 | public const DARK = 'DARK'; 28 | 29 | private const MEMBER_NETWORK = [ 30 | self::CONNECTIONS, 31 | self::PUBLIC, 32 | self::LOGGED_IN, 33 | ]; 34 | 35 | private const AVAILABLE_VISIBILITY = [ 36 | self::MEMBER_NETWORK_VISIBILITY, 37 | self::SPONSORED_CONTENT_VISIBILITY, 38 | ]; 39 | 40 | public function __construct(string $visibility = self::MEMBER_NETWORK_VISIBILITY, string $value = 'PUBLIC') 41 | { 42 | if (!\in_array($visibility, self::AVAILABLE_VISIBILITY, true)) { 43 | throw new LogicException(\sprintf('"%s" is not a valid visibility, available visibility are "%s".', $visibility, implode(', ', self::AVAILABLE_VISIBILITY))); 44 | } 45 | 46 | if (self::MEMBER_NETWORK_VISIBILITY === $visibility && !\in_array($value, self::MEMBER_NETWORK, true)) { 47 | throw new LogicException(\sprintf('"%s" is not a valid value, available value for visibility "%s" are "%s".', $value, $visibility, implode(', ', self::MEMBER_NETWORK))); 48 | } 49 | 50 | if (self::SPONSORED_CONTENT_VISIBILITY === $visibility && self::DARK !== $value) { 51 | throw new LogicException(\sprintf('"%s" is not a valid value, available value for visibility "%s" is "%s".', $value, $visibility, self::DARK)); 52 | } 53 | 54 | $this->options['com.linkedin.ugc.'.$visibility] = $value; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "symfony/linked-in-notifier", 3 | "type": "symfony-notifier-bridge", 4 | "description": "Symfony LinkedIn Notifier Bridge", 5 | "keywords": ["linkedin", "notifier"], 6 | "homepage": "https://symfony.com", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Smaïne Milianni", 11 | "email": "smaine.milianni@gmail.com" 12 | }, 13 | { 14 | "name": "Symfony Community", 15 | "homepage": "https://symfony.com/contributors" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=8.2", 20 | "symfony/http-client": "^6.4|^7.0", 21 | "symfony/notifier": "^7.3" 22 | }, 23 | "autoload": { 24 | "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\LinkedIn\\": "" }, 25 | "exclude-from-classmap": [ 26 | "/Tests/" 27 | ] 28 | }, 29 | "minimum-stability": "dev" 30 | } 31 | --------------------------------------------------------------------------------