Skip to content

Commit

Permalink
Merge branch '0.4.x-dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
bpolaszek committed Nov 14, 2018
2 parents ed8c1c3 + 328d7f3 commit 47acb8c
Show file tree
Hide file tree
Showing 42 changed files with 1,681 additions and 720 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.idea
/vendor
composer.lock
28 changes: 28 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
language: php

cache:
directories:
- $HOME/.composer/cache

php:
- '7.1'
- '7.2'
- '7.3'
- nightly

env:
- COMPOSER_FLAGS="--prefer-lowest --prefer-stable"
- COMPOSER_FLAGS="--prefer-stable"

matrix:
fast_finish: true
allow_failures:
- php: nightly


install:
- composer update $COMPOSER_FLAGS --no-interaction --prefer-dist --no-progress --no-suggest --ansi

script:
- vendor/bin/phpunit

21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2018 Beno!t POLASZEK

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
66 changes: 26 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
[![Latest Stable Version](https://poser.pugx.org/bentools/webpush-bundle/v/stable)](https://packagist.org/packages/bentools/webpush-bundle)
[![License](https://poser.pugx.org/bentools/webpush-bundle/license)](https://packagist.org/packages/bentools/webpush-bundle)
[![Build Status](https://img.shields.io/travis/bpolaszek/webpush-bundle/master.svg?style=flat-square)](https://travis-ci.org/bpolaszek/webpush-bundle)
[![Quality Score](https://img.shields.io/scrutinizer/g/bpolaszek/webpush-bundle.svg?style=flat-square)](https://scrutinizer-ci.com/g/bpolaszek/webpush-bundle)
[![Total Downloads](https://poser.pugx.org/bentools/webpush-bundle/downloads)](https://packagist.org/packages/bentools/webpush-bundle)

# Webpush Bundle

This bundle leverages [minishlink/web-push](https://github.com/web-push-libs/web-push-php) library to associate your Symfony users with Webpush subscriptions.
This bundle allows your app to leverage [the Web Push protocol](https://developers.google.com/web/fundamentals/push-notifications/web-push-protocol) to send notifications to your users' devices, whether they're online or not.

With a small amount of code, you'll be able to associate your [Symfony users](https://symfony.com/doc/current/security.html#a-create-your-user-class) to WebPush Subscriptions:

This way you can integrate push messages into your app to send notifications.
* A single user can subscribe from multiple browsers/devices
* Multiple users can subscribe from a single browser/device

This bundle uses your own persistence system (Doctrine or anything else) to manage these associations.

We assume you have a minimum knowledge of how Push Notifications work, otherwise we highly recommend you to read [Matt Gaunt's Web Push Book](https://web-push-book.gauntface.com/).

## Use cases
**Example Use cases**

* You have a todolist app - notify users they're assigned a task
* You have an eCommerce app:
Expand All @@ -25,51 +36,20 @@ We assume you have a minimum knowledge of how Push Notifications work, otherwise

## Getting started

Because there can be different User implementations, and that some front-end is implied, there are several steps to follow to get started:
1. Install the bundle and its assets
2. Create your own `UserSubscription` class and its associated manager
3. Update your `config.yml` and `routing.yml`
4. Insert a JS snippet in your twig views.

Let's go!

-------------
This bundle is just the back-end part of the subscription process. For the front-end part, have a look at the [webpush-client](https://www.npmjs.com/package/webpush-client) package.

### Composer is your friend:

PHP7.1+ is required.

```bash
composer require bentools/webpush-bundle 0.3.*
composer require bentools/webpush-bundle 0.4.*
```

_We aren't on stable version yet - expect some changes._

If you're using Symfony 3, add the bundle to your kernel. With Symfony Flex, this should be done automatically.

### Add the bundle to your kernel:
```php
# app/AppKernel.php
⚠️ _We aren't on stable version yet - expect some changes._

class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = [
// ...
new BenTools\WebPushBundle\WebPushBundle(),
];

return $bundles;
}
}
```

### Install assets:

```bash
php bin/console assets:install --symlink
```
_We provide a service worker and a JS client._


### Generate your VAPID keys:
Expand All @@ -78,13 +58,19 @@ _We provide a service worker and a JS client._
php bin/console webpush:generate:keys
```

You'll have to update your config with the given keys. We encourage you to store them in environment variables or in `parameters.yml`.


Next: [Create your UserSubscription class](doc/01%20-%20The%20UserSubscription%20Class.md)

## Tests

We mostly need functionnal tests. Contributions are very welcome!
> ./vendor/bin/phpunit
## License

MIT
MIT

## Credits

This bundle leverages the [minishlink/web-push](https://github.com/web-push-libs/web-push-php) library.
26 changes: 22 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,38 @@
],
"require": {
"php": ">=7.1",
"minishlink/web-push": "^2.0"
"ext-json": "*",
"ext-curl": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"minishlink/web-push": "^4.0",
"symfony/http-kernel": "^3.0|^4.0"
},
"require-dev": {
"symfony/var-dumper": "^3.3",
"symfony/symfony": "^2.7|^3.0|^4.0"
"bentools/doctrine-static": "1.0.x-dev",
"nyholm/symfony-bundle-test": "^1.4",
"phpunit/phpunit": "^5.0|^6.0|^7.0",
"symfony/config": "^4.1",
"symfony/dependency-injection": "^3.0|^4.0",
"symfony/framework-bundle": "^3.0|^4.0",
"symfony/http-foundation": "^3.0|^4.0",
"symfony/routing": "^3.0|^4.0",
"symfony/security": "^3.0|^4.0",
"symfony/var-dumper": "^3.0|^4.0",
"symfony/yaml": "^3.0|^4.0",
"twig/twig": "~1.0|~2.0"
},
"autoload": {
"psr-4": {
"BenTools\\WebPushBundle\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"BenTools\\WebPushBundle\\Tests\\": "tests"
},
"files": [
"vendor/symfony/symfony/src/Symfony/Component/VarDumper/Resources/functions/dump.php"
"vendor/symfony/var-dumper/Resources/functions/dump.php"
]
},
"config": {
Expand Down
32 changes: 18 additions & 14 deletions doc/01 - The UserSubscription Class.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
Basically:

* A user can have several subscriptions (you can log in from several browsers)
* A single subscription can be shared among multiple users (you can log in with several accounts on the same browser).

We need to store these associations.

## Create your UserSubscription class

First, you have to implement `BenTools\WebPushBundle\Model\Subscription\UserSubscriptionInterface`.

It's a simple entity which associates:
1. Your user entity
2. The subscription details - it will store the JSON representation of the `Subscription` javascript object.
2. The subscription details - it will store the JSON representation of the `PushSubscription` javascript object.
3. A hash of the endpoint (or any string that could help in retrieving it).

You're free to use Doctrine or anything else.

Example class:
```php
# src/AppBundle/Entity/UserSubscription.php
# src/Entity/UserSubscription.php

namespace AppBundle\Entity;
namespace App\Entity;

use BenTools\WebPushBundle\Model\Subscription\UserSubscriptionInterface;
use Doctrine\ORM\Mapping as ORM;
Expand All @@ -43,7 +36,7 @@ class UserSubscription implements UserSubscriptionInterface

/**
* @var User
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\User")
* @ORM\ManyToOne(targetEntity="App\Entity\User")
* @ORM\JoinColumn(nullable=false)
*/
private $user;
Expand Down Expand Up @@ -104,23 +97,34 @@ class UserSubscription implements UserSubscriptionInterface
*/
public function getEndpoint(): string
{
return $this->subscription['endpoint'] ?? null;
return $this->subscription['endpoint'];
}

/**
* @inheritDoc
*/
public function getPublicKey(): string
{
return $this->subscription['keys']['p256dh'] ?? null;
return $this->subscription['keys']['p256dh'];
}

/**
* @inheritDoc
*/
public function getAuthToken(): string
{
return $this->subscription['keys']['auth'] ?? null;
return $this->subscription['keys']['auth'];
}


/**
* Content-encoding (default: aesgcm).
*
* @return string
*/
public function getContentEncoding(): string
{
return $this->subscription['content-encoding'] ?? 'aesgcm';
}

}
Expand Down
27 changes: 22 additions & 5 deletions doc/02 - The UserSubscription Manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ Then, create a class that implements `BenTools\WebPushBundle\Model\Subscription\

Example with Doctrine:
```php
# src/AppBundle/Services/UserSubscriptionManager.php
# src/Services/UserSubscriptionManager.php

namespace AppBundle\Services;
namespace App\Services;

use AppBundle\Entity\UserSubscription;
use App\Entity\UserSubscription;
use BenTools\WebPushBundle\Model\Subscription\UserSubscriptionInterface;
use BenTools\WebPushBundle\Model\Subscription\UserSubscriptionManagerInterface;
use Doctrine\Common\Persistence\ManagerRegistry;
Expand All @@ -37,15 +37,17 @@ class UserSubscriptionManager implements UserSubscriptionManagerInterface
/**
* @inheritDoc
*/
public function factory(UserInterface $user, string $subscriptionHash, array $subscription): UserSubscriptionInterface
public function factory(UserInterface $user, string $subscriptionHash, array $subscription, array $options): UserSubscriptionInterface
{
// $options is an arbitrary array that can be provided through the front-end code.
// You can use it to store meta-data about the subscription: the user agent, the referring domain, ...
return new UserSubscription($user, $subscriptionHash, $subscription);
}

/**
* @inheritDoc
*/
public function hash(string $endpoint): string {
public function hash(string $endpoint, UserInterface $user): string {
return md5($endpoint); // Encode it as you like
}

Expand Down Expand Up @@ -101,6 +103,21 @@ class UserSubscriptionManager implements UserSubscriptionManagerInterface
}
```

Now, register your `UserSubscriptionManager` in your `services.yaml`:

```yaml
# app/config/services.yml (SF3)
# config/services.yaml (SF4)

services:
App\Services\UserSubscriptionManager:
class: App\Services\UserSubscriptionManager
arguments:
- '@doctrine'
tags:
- { name: bentools_webpush.subscription_manager, user_class: 'App\Entity\User' }
```
Previous: [The UserSubscription Class](01%20-%20The%20UserSubscription%20Class.md)
Next: [Configuration](03%20-%20Configuration.md)
38 changes: 9 additions & 29 deletions doc/03 - Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,28 @@
#### Configure the bundle:

```yaml
# app/config/config.yml
# app/config/config.yml (SF3)
# config/packages/bentools_webpush.yaml (SF4)
bentools_webpush:
settings:
public_key: '%bentools_webpush.public_key%'
private_key: '%bentools_webpush.private_key%'
associations:
my_users:
user_class: AppBundle\Entity\User
user_subscription_class: AppBundle\Entity\UserSubscription
manager: '@AppBundle\Services\UserSubscriptionManager' # Manager service id
public_key: 'your_public_key'
private_key: 'your_private_key'
```
#### Update your router:
```yaml
# app/config/routing.yml
# app/config/routing.yml (SF3)
# config/routing.yaml (SF4)
bentools_webpush:
resource: '@WebPushBundle/Resources/config/routing.xml'
prefix: /webpush
```
#### Update your services file:
```yaml
# app/config/services.yml
services:
AppBundle\Services\UserSubscriptionManager:
arguments: ["@doctrine"]
```
#### Update your templates:
You will have a new route called `bentools_webpush` which will be the Ajax endpoint for handling subscriptions (POST requests) / unsubscriptions (DELETE requests).

Insert this snippet in the templates where your user is logged in:
```twig
<script src="{{ asset('bundles/webpush/js/webpush_client.js') }}" data-webpushclient></script>
<script>
var webpush = new BenToolsWebPushClient({
serverKey: '{{ bentools_pusher.server_key | e('js') }}',
url: '{{ url('bentools_webpush.subscription') }}'
});
</script>
```
The global variable `bentools_webpush.public_key` is now exposed in Twig.

_This will install the service worker and prompt your users to accept notifications._
To handle subscriptions/unsubscriptions on the front-end side, have a look at [webpush-client](https://www.npmjs.com/package/webpush-client).

Previous: [The UserSubscription Manager](02%20-%20The%20UserSubscription%20Manager.md)

Expand Down
Loading

0 comments on commit 47acb8c

Please sign in to comment.