Added per-component subscriptions. Closes #734
This commit is contained in:
committed by
James Brooks
parent
e5c137f82b
commit
ac3888f7c8
@@ -11,6 +11,11 @@
|
||||
|
||||
namespace CachetHQ\Cachet\Bus\Commands\Subscriber;
|
||||
|
||||
/**
|
||||
* This is the subscribe subscriber command.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
final class SubscribeSubscriberCommand
|
||||
{
|
||||
/**
|
||||
@@ -27,6 +32,13 @@ final class SubscribeSubscriberCommand
|
||||
*/
|
||||
public $verified;
|
||||
|
||||
/**
|
||||
* The subscriptions that we want to add.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
public $subscriptions;
|
||||
|
||||
/**
|
||||
* The validation rules.
|
||||
*
|
||||
@@ -39,14 +51,16 @@ final class SubscribeSubscriberCommand
|
||||
/**
|
||||
* Create a new subscribe subscriber command instance.
|
||||
*
|
||||
* @param string $email
|
||||
* @param bool $verified
|
||||
* @param string $email
|
||||
* @param bool $verified
|
||||
* @param null|array $subscriptions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($email, $verified = false)
|
||||
public function __construct($email, $verified = false, $subscriptions = null)
|
||||
{
|
||||
$this->email = $email;
|
||||
$this->verified = $verified;
|
||||
$this->subscriptions = $subscriptions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Bus\Commands\Subscriber;
|
||||
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
|
||||
final class UnsubscribeSubscriptionCommand
|
||||
{
|
||||
/**
|
||||
* The subscription to unsubscribe.
|
||||
*
|
||||
* @var \CachetHQ\Cachet\Models\Subscription
|
||||
*/
|
||||
public $subscription;
|
||||
|
||||
/**
|
||||
* Create a unsubscribe subscription command instance.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Models\Subscription $subscription
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Subscription $subscription)
|
||||
{
|
||||
$this->subscription = $subscription;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Bus\Events\Subscriber;
|
||||
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
|
||||
/**
|
||||
* This is the subscriber has updated subscriptions event.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
final class SubscriberHasUpdatedSubscriptionsEvent implements SubscriberEventInterface
|
||||
{
|
||||
/**
|
||||
* The subscriber.
|
||||
*
|
||||
* @var \CachetHQ\Cachet\Models\Subscriber
|
||||
*/
|
||||
public $subscriber;
|
||||
|
||||
/**
|
||||
* Create a new subscriber has updated subscriptions event instance.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Subscriber $subscriber)
|
||||
{
|
||||
$this->subscriber = $subscriber;
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,16 @@ namespace CachetHQ\Cachet\Bus\Handlers\Commands\Subscriber;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\VerifySubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasSubscribedEvent;
|
||||
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUpdatedSubscriptionsEvent;
|
||||
use CachetHQ\Cachet\Bus\Exceptions\Subscriber\AlreadySubscribedException;
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
|
||||
/**
|
||||
* This is the subscribe subscriber command handler.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
class SubscribeSubscriberCommandHandler
|
||||
{
|
||||
/**
|
||||
@@ -30,16 +37,29 @@ class SubscribeSubscriberCommandHandler
|
||||
*/
|
||||
public function handle(SubscribeSubscriberCommand $command)
|
||||
{
|
||||
if (Subscriber::where('email', $command->email)->first()) {
|
||||
if (Subscriber::where('email', $command->email)->first() && $command->subscriptions === null) {
|
||||
throw new AlreadySubscribedException("Cannot subscribe {$command->email} because they're already subscribed.");
|
||||
}
|
||||
|
||||
$subscriber = Subscriber::create(['email' => $command->email]);
|
||||
$subscriber = Subscriber::firstOrCreate(['email' => $command->email]);
|
||||
|
||||
if ($command->verified) {
|
||||
dispatch(new VerifySubscriberCommand($subscriber));
|
||||
if ($subscriptions = $command->subscriptions) {
|
||||
foreach ($subscriptions as $subscription => $subscriptionValue) {
|
||||
Subscription::firstOrCreate([
|
||||
'subscriber_id' => $subscriber->id,
|
||||
$subscription => $subscriptionValue,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($subscriber->is_verified === false) {
|
||||
if ($command->verified) {
|
||||
dispatch(new VerifySubscriberCommand($subscriber));
|
||||
} else {
|
||||
event(new SubscriberHasSubscribedEvent($subscriber));
|
||||
}
|
||||
} else {
|
||||
event(new SubscriberHasSubscribedEvent($subscriber));
|
||||
event(new SubscriberHasUpdatedSubscriptionsEvent($subscriber));
|
||||
}
|
||||
|
||||
return $subscriber;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Bus\Handlers\Commands\Subscriber;
|
||||
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriptionCommand;
|
||||
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUnsubscribedEvent;
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
|
||||
class UnsubscribeSubscriptionCommandHandler
|
||||
{
|
||||
/**
|
||||
* Handle the unsubscribe subscription command.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriptionCommand $command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(UnsubscribeSubscriptionCommand $command)
|
||||
{
|
||||
$subscription = $command->subscription;
|
||||
|
||||
event(new SubscriberHasUnsubscribedEvent($subscription->subscriber));
|
||||
|
||||
$subscription->delete();
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ class VerifySubscriberCommandHandler
|
||||
{
|
||||
$subscriber = $command->subscriber;
|
||||
|
||||
// Mark the subscriber as verified.
|
||||
$subscriber->verified_at = Carbon::now();
|
||||
$subscriber->save();
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Bus\Handlers\Events\Component;
|
||||
|
||||
use CachetHQ\Cachet\Bus\Events\Component\ComponentWasUpdatedEvent;
|
||||
use CachetHQ\Cachet\Models\Component;
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
use Illuminate\Contracts\Mail\MailQueue;
|
||||
use Illuminate\Mail\Message;
|
||||
use McCool\LaravelAutoPresenter\Facades\AutoPresenter;
|
||||
|
||||
class SendComponentUpdateEmailNotificationHandler
|
||||
{
|
||||
/**
|
||||
* The mailer instance.
|
||||
*
|
||||
* @var \Illuminate\Contracts\Mail\Mailer
|
||||
*/
|
||||
protected $mailer;
|
||||
|
||||
/**
|
||||
* Create a new send incident email notification handler.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Mail\Mailer $mailer
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(MailQueue $mailer)
|
||||
{
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Bus\Events\Component\ComponentWasUpdatedEvent $event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle(ComponentWasUpdatedEvent $event)
|
||||
{
|
||||
$component = AutoPresenter::decorate($event->component);
|
||||
|
||||
$mail = [
|
||||
'subject' => trans('cachet.subscriber.email.component.subject'),
|
||||
'component_name' => $component->name,
|
||||
'component_human_status' => $component->human_status,
|
||||
];
|
||||
|
||||
foreach (Subscription::isVerifiedForComponent($component->id)->with('subscriber')->get() as $subscription) {
|
||||
$subscriber = $subscription->subscriber;
|
||||
$mail['email'] = $subscriber->email;
|
||||
$mail['unsubscribe_link'] = route('subscribe.unsubscribe', ['code' => $subscriber->verify_code, 'subscription' => $subscription->id]);
|
||||
|
||||
$this->mailer->queue([
|
||||
'html' => 'emails.components.update-html',
|
||||
'text' => 'emails.components.update-text',
|
||||
], $mail, function (Message $message) use ($mail) {
|
||||
$message->to($mail['email'])->subject($mail['subject']);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ class EventServiceProvider extends ServiceProvider
|
||||
//
|
||||
],
|
||||
'CachetHQ\Cachet\Bus\Events\Component\ComponentWasUpdatedEvent' => [
|
||||
//
|
||||
'CachetHQ\Cachet\Bus\Handlers\Events\Component\SendComponentUpdateEmailNotificationHandler',
|
||||
],
|
||||
'CachetHQ\Cachet\Bus\Events\Incident\IncidentWasReportedEvent' => [
|
||||
'CachetHQ\Cachet\Bus\Handlers\Events\Incident\SendIncidentEmailNotificationHandler',
|
||||
@@ -72,6 +72,9 @@ class EventServiceProvider extends ServiceProvider
|
||||
'CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUnsubscribedEvent' => [
|
||||
//
|
||||
],
|
||||
'CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUpdatedSubscriptionsEvent' => [
|
||||
//
|
||||
],
|
||||
'CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasVerifiedEvent' => [
|
||||
//
|
||||
],
|
||||
|
||||
@@ -54,6 +54,7 @@ class RouteServiceProvider extends ServiceProvider
|
||||
$this->app->router->model('metric_point', 'CachetHQ\Cachet\Models\MetricPoint');
|
||||
$this->app->router->model('setting', 'CachetHQ\Cachet\Models\Setting');
|
||||
$this->app->router->model('subscriber', 'CachetHQ\Cachet\Models\Subscriber');
|
||||
$this->app->router->model('subscription', 'CachetHQ\Cachet\Models\Subscription');
|
||||
$this->app->router->model('user', 'CachetHQ\Cachet\Models\User');
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@ namespace CachetHQ\Cachet\Http\Controllers\Api;
|
||||
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriptionCommand;
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
use GrahamCampbell\Binput\Facades\Binput;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
@@ -41,7 +43,7 @@ class SubscriberController extends AbstractApiController
|
||||
public function postSubscribers()
|
||||
{
|
||||
try {
|
||||
$subscriber = dispatch(new SubscribeSubscriberCommand(Binput::get('email'), Binput::get('verify', false)));
|
||||
$subscriber = dispatch(new SubscribeSubscriberCommand(Binput::get('email'), Binput::get('verify', false), null));
|
||||
} catch (QueryException $e) {
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
@@ -62,4 +64,18 @@ class SubscriberController extends AbstractApiController
|
||||
|
||||
return $this->noContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a subscriber.
|
||||
*
|
||||
* @param \CachetHQ\Cachet\Models\Subscriber $subscriber
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function deleteSubscription(Subscription $subscriber)
|
||||
{
|
||||
dispatch(new UnsubscribeSubscriptionCommand($subscriber));
|
||||
|
||||
return $this->noContent();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,12 @@ namespace CachetHQ\Cachet\Http\Controllers;
|
||||
use AltThree\Validator\ValidationException;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\SubscribeSubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriptionCommand;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\VerifySubscriberCommand;
|
||||
use CachetHQ\Cachet\Bus\Exceptions\Subscriber\AlreadySubscribedException;
|
||||
use CachetHQ\Cachet\Facades\Setting;
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
use GrahamCampbell\Binput\Facades\Binput;
|
||||
use GrahamCampbell\Markdown\Facades\Markdown;
|
||||
use Illuminate\Routing\Controller;
|
||||
@@ -26,6 +28,11 @@ use Illuminate\Support\Facades\View;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* This is the subscribe controller.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
class SubscribeController extends Controller
|
||||
{
|
||||
/**
|
||||
@@ -47,9 +54,10 @@ class SubscribeController extends Controller
|
||||
public function postSubscribe()
|
||||
{
|
||||
$email = Binput::get('email');
|
||||
$subscriptions = Binput::get('subscriptions');
|
||||
|
||||
try {
|
||||
dispatch(new SubscribeSubscriberCommand($email));
|
||||
dispatch(new SubscribeSubscriberCommand($email, false, $subscriptions));
|
||||
} catch (AlreadySubscribedException $e) {
|
||||
return Redirect::route('subscribe.subscribe')
|
||||
->withTitle(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.whoops'), trans('cachet.subscriber.email.failure')))
|
||||
@@ -94,10 +102,11 @@ class SubscribeController extends Controller
|
||||
* Handle the unsubscribe.
|
||||
*
|
||||
* @param string|null $code
|
||||
* @param int|null $subscription
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function getUnsubscribe($code = null)
|
||||
public function getUnsubscribe($code = null, $subscription = null)
|
||||
{
|
||||
if ($code === null) {
|
||||
throw new NotFoundHttpException();
|
||||
@@ -109,7 +118,11 @@ class SubscribeController extends Controller
|
||||
throw new BadRequestHttpException();
|
||||
}
|
||||
|
||||
dispatch(new UnsubscribeSubscriberCommand($subscriber));
|
||||
if ($subscription) {
|
||||
dispatch(new UnsubscribeSubscriptionCommand(Subscription::forSubscriber($subscriber->id)->firstOrFail()));
|
||||
} else {
|
||||
dispatch(new UnsubscribeSubscriberCommand($subscriber, $subscription));
|
||||
}
|
||||
|
||||
return Redirect::route('status-page')
|
||||
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.unsubscribed')));
|
||||
|
||||
@@ -68,6 +68,7 @@ class ApiRoutes
|
||||
$router->delete('metrics/{metric}', 'MetricController@deleteMetric');
|
||||
$router->delete('metrics/{metric}/points/{metric_point}', 'MetricPointController@deleteMetricPoint');
|
||||
$router->delete('subscribers/{subscriber}', 'SubscriberController@deleteSubscriber');
|
||||
$router->delete('subscriptions/{subscription}', 'SubscriberController@deleteSubscription');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class SubscribeRoutes
|
||||
'uses' => 'SubscribeController@getVerify',
|
||||
]);
|
||||
|
||||
$router->get('unsubscribe/{code}', [
|
||||
$router->get('unsubscribe/{code}/{subscription?}', [
|
||||
'as' => 'unsubscribe',
|
||||
'uses' => 'SubscribeController@getUnsubscribe',
|
||||
]);
|
||||
|
||||
@@ -62,6 +62,16 @@ class Subscriber extends Model implements HasPresenter
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A subscriber has many subscriptions.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function subcriptions()
|
||||
{
|
||||
return $this->hasMany(Subscription::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope verified subscribers.
|
||||
*
|
||||
|
||||
113
app/Models/Subscription.php
Normal file
113
app/Models/Subscription.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Cachet\Models;
|
||||
|
||||
use AltThree\Validator\ValidatingTrait;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Subscription extends Model
|
||||
{
|
||||
use ValidatingTrait;
|
||||
|
||||
/**
|
||||
* The attributes that should be casted to native types.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $casts = [
|
||||
'subscriber_id' => 'int',
|
||||
'component_id' => 'int',
|
||||
];
|
||||
|
||||
/**
|
||||
* The fillable properties.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'subscriber_id',
|
||||
'component_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* The validation rules.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
public $rules = [
|
||||
'subscriber_id' => 'int|required',
|
||||
'component_id' => 'int',
|
||||
];
|
||||
|
||||
/**
|
||||
* A subscription belongs to a subscriber.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function subscriber()
|
||||
{
|
||||
return $this->belongsTo(Subscriber::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* A subscription has one component.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function component()
|
||||
{
|
||||
return $this->belongsTo(Component::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all subscriptions for a given subscriber.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param int $subscriber_id
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeForSubscriber(Builder $query, $subscriber_id)
|
||||
{
|
||||
return $query->where('subscriber_id', $subscriber_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all subscriptions for a component.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param int $component_id
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeForComponent(Builder $query, $component_id)
|
||||
{
|
||||
return $query->where('component_id', $component_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds all verified subscriptions for a component.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param int $component_id
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeIsVerifiedForComponent(Builder $query, $component_id)
|
||||
{
|
||||
return $query->select('subscriptions.*')
|
||||
->join('subscribers', 'subscriptions.subscriber_id', '=', 'subscribers.id')
|
||||
->where('component_id', $component_id)
|
||||
->whereNotNull('subscribers.verified_at');
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ use CachetHQ\Cachet\Models\IncidentTemplate;
|
||||
use CachetHQ\Cachet\Models\Metric;
|
||||
use CachetHQ\Cachet\Models\MetricPoint;
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
use CachetHQ\Cachet\Models\User;
|
||||
use Carbon\Carbon;
|
||||
|
||||
@@ -79,6 +80,16 @@ $factory->define(Subscriber::class, function ($faker) {
|
||||
];
|
||||
});
|
||||
|
||||
$factory->define(Subscription::class, function ($faker) {
|
||||
$user = factory(Subscriber::class)->create();
|
||||
$component = factory(Component::class)->create();
|
||||
|
||||
return [
|
||||
'subscriber_id' => $user->id,
|
||||
'component_id' => $component->id,
|
||||
];
|
||||
});
|
||||
|
||||
$factory->define(User::class, function ($faker) {
|
||||
return [
|
||||
'username' => $faker->userName,
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateSubscriptionsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('subscriptions', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('subscriber_id')->unsigned()->index();
|
||||
$table->integer('component_id')->unsigned()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('subscriptions');
|
||||
}
|
||||
}
|
||||
@@ -376,6 +376,23 @@ $(function() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Open a modal.
|
||||
$('#subscribe-modal')
|
||||
.on('show.bs.modal', function (event) {
|
||||
var $button = $(event.relatedTarget);
|
||||
var $modal = $(this);
|
||||
$modal.find('#subscribe-modal-id').val($button.data('component-id'));
|
||||
})
|
||||
.on('hidden.bs.modal', function (event) {
|
||||
var $modal = $(this);
|
||||
$modal.find('#subscribe-modal-id').val('');
|
||||
});
|
||||
|
||||
// Focus on any modals.
|
||||
$('.modal').on('shown.bs.modal', function () {
|
||||
$(this).find('input[type=text]').focus();
|
||||
});
|
||||
});
|
||||
|
||||
function askConfirmation(callback) {
|
||||
|
||||
@@ -86,6 +86,13 @@ return [
|
||||
'html-preheader' => 'New incident has been reported on :app_name.',
|
||||
'html' => '<p>New incident has been reported on :app_name.</p><p>Thank you, :app_name</p>',
|
||||
],
|
||||
'component' => [
|
||||
'subject' => 'Component Status Update',
|
||||
'text' => 'The component :component_name has seen a status change. The component is now at :component_human_status.\nThank you, :app_name',
|
||||
'html-preheader' => 'Component Update from :app_name',
|
||||
'html' => '<p>The component :component_name has seen a status change. The component is now at :component_human_status.</p><p>Thank you, :app_name</p>',
|
||||
'tooltip-title' => 'Subscribe to notifications for :component_name.',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -112,6 +119,16 @@ return [
|
||||
'update' => 'There is a newer version of Cachet available. You can learn how to update <a href="https://docs.cachethq.io/docs/updating-cachet">here</a>!',
|
||||
],
|
||||
|
||||
// Modal
|
||||
'modal' => [
|
||||
'close' => 'Close',
|
||||
'subscribe' => [
|
||||
'title' => 'Subscribe to component updates?',
|
||||
'body' => 'Enter your email address to subscribe to updates for this component. If you\'re already subscribed, you\'ll receive emails for this component too.',
|
||||
'button' => 'Subscribe',
|
||||
],
|
||||
],
|
||||
|
||||
// Other
|
||||
'powered_by' => ':app Status Page is powered by <a href="https://cachethq.io" class="links">Cachet</a>.',
|
||||
'about_this_site' => 'About This Site',
|
||||
|
||||
16
resources/views/emails/components/update-html.blade.php
Normal file
16
resources/views/emails/components/update-html.blade.php
Normal file
@@ -0,0 +1,16 @@
|
||||
@extends('layout.emails')
|
||||
|
||||
@section('preheader')
|
||||
{!! trans('cachet.subscriber.email.component.html-preheader', ['app_name' => $app_name]) !!}
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
{!! trans('cachet.subscriber.email.component.html', ['component_name' => $component_name, 'component_human_status' => $component_human_status, 'app_name' => $app_name]) !!}
|
||||
|
||||
@if($show_support)
|
||||
<p>{!! trans('cachet.powered_by', ['app' => $app_name]) !!}</p>
|
||||
@endif
|
||||
<p>
|
||||
<small><a href="{{ $unsubscribe_link }}">{!! trans('cachet.subscriber.email.unsubscribe') !!}</a></small>
|
||||
</p>
|
||||
@stop
|
||||
7
resources/views/emails/components/update-text.blade.php
Normal file
7
resources/views/emails/components/update-text.blade.php
Normal file
@@ -0,0 +1,7 @@
|
||||
{!! trans('cachet.subscriber.email.component.text', ['component_name' => $component_name, 'component_human_status' => $component_human_status, 'app_name' => $app_name]) !!}
|
||||
|
||||
@if($show_support)
|
||||
{!! trans('cachet.powered_by', ['app' => $app_name]) !!}
|
||||
@endif
|
||||
|
||||
{!! trans('cachet.subscriber.email.unsubscribe') !!} {{ $unsubscribe_link }}
|
||||
@@ -56,4 +56,6 @@
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
|
||||
@include('partials.modals.subscribe')
|
||||
@stop
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
<i class="ion-ios-help-outline help-icon" data-toggle="tooltip" data-title="{{ $component->description }}"></i>
|
||||
@endif
|
||||
|
||||
@if(subscribers_enabled())
|
||||
<a href="#" data-toggle="modal" data-target="#subscribe-modal" data-component-id="{{ $component->id }}"><i class="ion-ios-email-outline" data-toggle="tooltip" data-title="{{ trans('cachet.subscriber.email.component.tooltip-title', ['component_name' => $component->name]) }}"></i></a>
|
||||
@endif
|
||||
|
||||
<div class="pull-right">
|
||||
<small class="text-component-{{ $component->status }} {{ $component->status_color }}">{{ $component->human_status }}</small>
|
||||
</div>
|
||||
|
||||
25
resources/views/partials/modals/subscribe.blade.php
Normal file
25
resources/views/partials/modals/subscribe.blade.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<div class="modal fade" tabindex="-1" role="dialog" id="subscribe-modal">
|
||||
<div class="modal-dialog">
|
||||
<form action="{{ route("subscribe.subscribe", [], false) }}" method="post" class="form">
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="subscriptions[component_id]" id="subscribe-modal-id" value="">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">{{ trans("cachet.modal.subscribe.title") }}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{ trans("cachet.modal.subscribe.body") }}</p>
|
||||
<div class="form-group">
|
||||
<input class="form-control" type="email" name="email" placeholder="hello@alt-three.com">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans("cachet.modal.close") }}</button>
|
||||
<button type="submit" class="btn btn-success">{{ trans("cachet.modal.subscribe.button") }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,4 +72,13 @@ class SubscriberTest extends AbstractApiTestCase
|
||||
$this->delete("/api/v1/subscribers/{$subscriber->id}");
|
||||
$this->assertResponseStatus(204);
|
||||
}
|
||||
|
||||
public function testDeleteSubscription()
|
||||
{
|
||||
$this->beUser();
|
||||
|
||||
$subscription = factory('CachetHQ\Cachet\Models\Subscription')->create();
|
||||
$this->delete("/api/v1/subscriptions/{$subscription->id}");
|
||||
$this->assertResponseStatus(204);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ class SubscribeSubscriberCommandTest extends AbstractTestCase
|
||||
|
||||
protected function getObjectAndParams()
|
||||
{
|
||||
$params = ['email' => 'support@cachethq.io', 'verified' => true];
|
||||
$object = new SubscribeSubscriberCommand($params['email'], $params['verified']);
|
||||
$params = ['email' => 'support@cachethq.io', 'verified' => true, 'subscriptions' => null];
|
||||
$object = new SubscribeSubscriberCommand($params['email'], $params['verified'], $params['subscriptions']);
|
||||
|
||||
return compact('params', 'object');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Tests\Cachet\Bus\Commands\Subscriber;
|
||||
|
||||
use AltThree\TestBench\CommandTrait;
|
||||
use CachetHQ\Cachet\Bus\Commands\Subscriber\UnsubscribeSubscriptionCommand;
|
||||
use CachetHQ\Cachet\Bus\Handlers\Commands\Subscriber\UnsubscribeSubscriptionCommandHandler;
|
||||
use CachetHQ\Cachet\Models\Subscription;
|
||||
use CachetHQ\Tests\Cachet\AbstractTestCase;
|
||||
|
||||
/**
|
||||
* This is the unsubscribe subscriber command test class.
|
||||
*
|
||||
* @author Joseph Cohen <joe@alt-three.com>
|
||||
*/
|
||||
class UnsubscribeSubscriptionCommandTest extends AbstractTestCase
|
||||
{
|
||||
use CommandTrait;
|
||||
|
||||
protected function getObjectAndParams()
|
||||
{
|
||||
$params = ['subscription' => new Subscription()];
|
||||
$object = new UnsubscribeSubscriptionCommand($params['subscription']);
|
||||
|
||||
return compact('params', 'object');
|
||||
}
|
||||
|
||||
protected function getHandlerClass()
|
||||
{
|
||||
return UnsubscribeSubscriptionCommandHandler::class;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class ComponentWasUpdatedEventTest extends AbstractComponentEventTestCase
|
||||
{
|
||||
protected function objectHasHandlers()
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getObjectAndParams()
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Cachet.
|
||||
*
|
||||
* (c) Alt Three Services Limited
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CachetHQ\Tests\Cachet\Bus\Events\Subscriber;
|
||||
|
||||
use CachetHQ\Cachet\Bus\Events\Subscriber\SubscriberHasUpdatedSubscriptionsEvent;
|
||||
use CachetHQ\Cachet\Models\Subscriber;
|
||||
|
||||
/**
|
||||
* This is the subscriber has updated subscriptions event test.
|
||||
*
|
||||
* @author James Brooks <james@alt-three.com>
|
||||
*/
|
||||
class SubscriberHasUpdatedSubscriptionsEventTest extends AbstractSubscriberEventTestCase
|
||||
{
|
||||
protected function objectHasHandlers()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getObjectAndParams()
|
||||
{
|
||||
$params = ['subscriber' => new Subscriber()];
|
||||
$object = new SubscriberHasUpdatedSubscriptionsEvent($params['subscriber']);
|
||||
|
||||
return compact('params', 'object');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user