Merge branch '2.4' of github.com:CachetHQ/Cachet into feature/2895-custom-meta-descriptions-per-incident

This commit is contained in:
Nico Stapelbroek
2018-07-02 11:20:47 +02:00
399 changed files with 10754 additions and 5607 deletions

View File

@@ -11,5 +11,5 @@ indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[{package.json,*.yml}]
[{*.json,*.yml}]
indent_size = 2

View File

@@ -3,6 +3,7 @@ APP_DEBUG=false
APP_URL=http://localhost
APP_TIMEZONE=UTC
APP_KEY=
DEBUGBAR_ENABLED=false
DB_DRIVER=mysql
DB_HOST=localhost
@@ -39,3 +40,5 @@ GITHUB_TOKEN=null
NEXMO_KEY=null
NEXMO_SECRET=null
NEXMO_SMS_FROM=Cachet
TRUSTED_PROXIES=

6
.github/config.yml vendored
View File

@@ -2,7 +2,11 @@
# Comment to be posted to on first time issues
newIssueWelcomeComment: >
:wave: Hey, it's good to see you here! Thank you for opening your first issue. Cachet support is not available here, instead we have a Slack community that can you can join at https://cachethq-slack.herokuapp.com
:wave: Thank you for opening your first issue. I'm just an automated bot that's here to help you get the information you need quicker, so please ignore this message if it doesn't apply to your issue.
If you're looking for support, you should try the Slack group by registering your email address at https://cachethq-slack.herokuapp.com. Alternatively, email support@alt-three.com for our Professional support service (please note, this a paid service.
If you're issue is with documentation, you can suggest edits by clicking the **Suggest Edits** link on any page, or open an issue at https://github.com/CachetHQ/Docs
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome

12
.github/move.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# Configuration for move-issues - https://github.com/dessant/move-issues
# Delete the command comment. Ignored when the comment also contains other content
deleteCommand: true
# Close the source issue after moving
closeSourceIssue: true
# Lock the source issue after moving
lockSourceIssue: true
# Set custom aliases for targets
# aliases:
# r: repo
# or: owner/repo

View File

@@ -1,11 +1,14 @@
language: php
dist: trusty
sudo: false
branches:
except:
- l10n_2.4
php:
- 5.6
- 7.0
- 7.1
- 7.2
before_install:
- cp .env.example .env
@@ -13,4 +16,10 @@ before_install:
install: travis_retry composer install --no-interaction --no-suggest
script: vendor/bin/phpunit
script:
- if [ "$TRAVIS_PHP_VERSION" != "7.1" ]; then vendor/bin/phpunit; fi
- if [ "$TRAVIS_PHP_VERSION" == "7.1" ]; then vendor/bin/phpunit --coverage-clover build/logs/clover.xml; fi
after_script:
- if [ "$TRAVIS_PHP_VERSION" == "7.1" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi
- if [ "$TRAVIS_PHP_VERSION" == "7.1" ]; then php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml; fi

View File

@@ -1,14 +1,20 @@
# Contribution Guidelines
## Creating issues
## Thank you
We track feature requests and bug reports on the [issue tracker](https://github.com/cachethq/Cachet/issues). Please send support requests to our support email; [support@alt-three.com](mailto:support@alt-three.com?subject=Cachet Support).
Firstly, thank you for taking an interest in Cachet and for reading this guide.
## Creating issues
We track feature requests and bug reports on the [issue tracker](https://github.com/cachethq/Cachet/issues). Please send support requests to our support email; [support@alt-three.com](mailto:support@alt-three.com?subject=Cachet%20Support).
**Always be respectful.** Organisation members reserve the right to lock topics if they feel necessary.
## Languages
Please submit non-English translations to the [Cachet CrowdIn](https://translate.cachethq.io) project. This makes syncing translations much easier.
This project accepts **English translations** only. Translations will be updated automatically through the [CrowdIn](https://translate.cachethq.io) integration.
Please submit alternative translations to the [Cachet CrowdIn](https://translate.cachethq.io) project. CrowdIn will automatically send a Pull Request with your updates in.
## Coding Standards
@@ -32,15 +38,27 @@ If you're feeling adventurous, you can become a Git & GitHub master with the [Gi
You should also make use of the [.editorconfig](/.editorconfig) file found within the root of the repository. It'll make sure that your editor is setup with the same file settings.
---
## Ways to help:
## Contributing as a non-developer/non-designer
Start by becoming familiar with Cachet. If you're already using Cachet, that's a great head start. If not, check out the latest [demo](https://dev.cachethq.io) and have a play. You can also download and install Cachet locally to familiarise yourself that way.
You don't have to be a developer to help improve Cachet, infact there are lots of ways that you can help us.
### Spread the word
There are a lot of people who don't know about Cachet or what a status page is and how important it can be. [Tweet about Cachet](https://twitter.com/CachetHQ). Write blog posts about your success (or failures, we're not perfect) with Cachet and share what you took away from it.
### Help with Documentation
Cachet is in use around the world. Cachet speaks multiple languages. Our documentation doesn't and can be hard to understand for non-English speaking people. Documentation should be easy to understand and we need your help to make this possible. Check out [CachetHQ/Docs](https://github.com/CachetHQ/Docs) to contribute to our documentation.
### As a non-developer/non-designer
We're always looking for new [translations](#translations).
Of course bug reports, feature requests and [documentation](https://docs.cachethq.io) are always appreciated.
## Contributing as a designer
### As a designer
As Cachet gains new features, the design and ideas that were once a perfect fit need updating and in some cases designing from scratch. This is where you come in! Fancy giving Cachet a lick of paint? Sweet!
@@ -56,7 +74,7 @@ If you're making a lot of changes, you'll find that running `npm run watch` will
When you're happy with your changes, please run `npm run prod` to minify the assets.
## Contributing as a developer
### As a developer
Built using [Laravel](https://laravel.com).
@@ -67,3 +85,9 @@ We use these extra dependencies to develop Cachet:
- Git
Once cloned to your local machine, you'll need some demo data! Run `php artisan cachet:seed` to get the demo installation ready for action.
### #YOLO JUST SUBMIT A PR
_A great idea taken from https://github.com/metabase/metabase/blob/master/docs/contributing.md#yolo-just-submit-a-pr._
> If you come up with something really cool, and want to share it with us, just submit a PR. If it hasn't gone through the above process, we probably won't merge it as is, but if it's compelling, we're more than willing to help you via code review, design review and generally OCD nitpicking so that it fits into the rest of our codebase.

View File

@@ -28,7 +28,7 @@ You can now [join our Slack community!](http://cachethq-slack.herokuapp.com)
## Requirements
- PHP 5.6.4+ or newer
- PHP 7.1 or later
- HTTP server with PHP support (eg: Apache, Nginx, Caddy)
- [Composer](https://getcomposer.org)
- A supported database: MySQL, PostgreSQL or SQLite
@@ -41,6 +41,7 @@ Here are some useful quick links:
- [Installing Cachet](https://docs.cachethq.io/docs/installing-cachet)
- [Getting started with Docker](https://docs.cachethq.io/docs/get-started-with-docker)
- [Installing Cachet on Windows](https://docs.cachethq.io/docs/installing-cachet-on-windows)
### Demo
@@ -71,3 +72,9 @@ We offer a professional installation service. To find out more, email us at [sup
## Security Vulnerabilities
If you discover a security vulnerability within Cachet, please send an e-mail to us at support@alt-three.com. We handle all security vulnerabilities on a case-by-case basis.
## Sponsorship
Thank you to the following for [sponsoring](https://patreon.com/jbrooksuk) Cachet.
![Exascale](/docs/images/sponsorships/exascale.jpg)

View File

@@ -25,49 +25,49 @@ final class UpdateComponentCommand
/**
* The component name.
*
* @var string
* @var string|null
*/
public $name;
/**
* The component description.
*
* @var string
* @var string|null
*/
public $description;
/**
* The component status.
*
* @var int
* @var int|null
*/
public $status;
/**
* The component link.
*
* @var string
* @var string|null
*/
public $link;
/**
* The component order.
*
* @var int
* @var int|null
*/
public $order;
/**
* The component group.
*
* @var int
* @var int|null
*/
public $group_id;
/**
* Is the component enabled?
*
* @var bool
* @var bool|null
*/
public $enabled;
@@ -106,19 +106,19 @@ final class UpdateComponentCommand
* Create a new update component command instance.
*
* @param \CachetHQ\Cachet\Models\Component $component
* @param string $name
* @param string $description
* @param int $status
* @param string $link
* @param int $order
* @param int $group_id
* @param bool $enabled
* @param string|null $name
* @param string|null $description
* @param int|null $status
* @param string|null $link
* @param int|null $order
* @param int|null $group_id
* @param bool|null $enabled
* @param array|null $meta
* @param bool $silent
*
* @return void
*/
public function __construct(Component $component, $name, $description, $status, $link, $order, $group_id, $enabled, $meta, $silent)
public function __construct(Component $component, $name = null, $description = null, $status = null, $link = null, $order = null, $group_id = null, $enabled = null, $meta = null, $silent = null)
{
$this->component = $component;
$this->name = $name;

View File

@@ -0,0 +1,51 @@
<?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\Tag;
use CachetHQ\Cachet\Models\Tag;
use Illuminate\Database\Eloquent\Model;
/**
* This is the apply tag coommand class.
*
* @author James Brooks <james@alt-three.com>
*/
final class ApplyTagCommand
{
/**
* The model to apply the tag to.
*
* @var \Illuminate\Database\Eloquent\Model
*/
public $model;
/**
* The tag to apply.
*
* @var \CachetHQ\Cachet\Models\Tag
*/
public $tag;
/**
* Create a new apply tag command instance.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param \CachetHQ\Cachet\Models\Tag $tag
*
* @return void
*/
public function __construct(Model $model, Tag $tag)
{
$this->model = $model;
$this->tag = $tag;
}
}

View File

@@ -0,0 +1,48 @@
<?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\Tag;
/**
* This is the create tag coommand class.
*
* @author James Brooks <james@alt-three.com>
*/
final class CreateTagCommand
{
/**
* The tag name.
*
* @var string
*/
public $name;
/**
* The tag slug.
*
* @var string|null
*/
public $slug;
/**
* Create a new create tag command instance.
*
* @param string $name
* @param string|null $slug
*
* @return void
*/
public function __construct($name, $slug = null)
{
$this->name = $name;
$this->slug = $slug;
}
}

View File

@@ -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\Commands\Tag;
use CachetHQ\Cachet\Models\Tag;
/**
* This is the delete tag coommand class.
*
* @author James Brooks <james@alt-three.com>
*/
final class DeleteTagCommand
{
/**
* The tag.
*
* @var \CachetHQ\Cachet\Models\Tag
*/
public $tag;
/**
* Create a new delete tag command instance.
*
* @param \CachetHQ\Cachet\Models\Tag $tag
*
* @return void
*/
public function __construct(Tag $tag)
{
$this->tag = $tag;
}
}

View File

@@ -0,0 +1,59 @@
<?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\Tag;
use CachetHQ\Cachet\Models\Tag;
/**
* This is the update tag coommand class.
*
* @author James Brooks <james@alt-three.com>
*/
final class UpdateTagCommand
{
/**
* The tag.
*
* @var \CachetHQ\Cachet\Models\Tag
*/
public $tag;
/**
* The new tag name.
*
* @var string|null
*/
public $name;
/**
* The new tag slug.
*
* @var string|null
*/
public $slug;
/**
* Create a new update tag command instance.
*
* @param \CachetHQ\Cachet\Models\Tag $tag
* @param string|null $name
* @param string|null $slug
*
* @return void
*/
public function __construct(Tag $tag, $name, $slug)
{
$this->tag = $tag;
$this->name = $name;
$this->slug = $slug;
}
}

View File

@@ -16,7 +16,7 @@ use CachetHQ\Cachet\Models\Invite;
/**
* This is the user was invited event class.
*
* @author Joseph Cohen <joealt-three.com>
* @author Joseph Cohen <joe@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
*/

View File

@@ -61,7 +61,7 @@ class CreateComponentCommandHandler
/**
* Filter the command data.
*
* @param \CachetHQ\Cachet\Bus\Commands\Incident\CreateComponentCommand $command
* @param \CachetHQ\Cachet\Bus\Commands\Component\CreateComponentCommand $command
*
* @return array
*/

View File

@@ -50,7 +50,9 @@ class UpdateComponentCommandHandler
$component = $command->component;
$originalStatus = $component->status;
event(new ComponentStatusWasChangedEvent($this->auth->user(), $component, $originalStatus, $command->status, $command->silent));
if ($command->status && (int) $originalStatus !== (int) $command->status) {
event(new ComponentStatusWasChangedEvent($this->auth->user(), $component, $originalStatus, $command->status, $command->silent));
}
$component->update($this->filter($command));
@@ -62,7 +64,7 @@ class UpdateComponentCommandHandler
/**
* Filter the command data.
*
* @param \CachetHQ\Cachet\Bus\Commands\Incident\UpdateComponentCommand $command
* @param \CachetHQ\Cachet\Bus\Commands\Component\UpdateComponentCommand $command
*
* @return array
*/

View File

@@ -73,6 +73,7 @@ class CreateIncidentCommandHandler
public function handle(CreateIncidentCommand $command)
{
$data = [
'user_id' => $this->auth->user()->id,
'name' => $command->name,
'status' => $command->status,
'visible' => $command->visible,
@@ -111,7 +112,7 @@ class CreateIncidentCommandHandler
// Update the component.
if ($component = Component::find($command->component_id)) {
dispatch(new UpdateComponentCommand(
execute(new UpdateComponentCommand(
Component::find($command->component_id),
null,
null,

View File

@@ -96,7 +96,7 @@ class UpdateIncidentCommandHandler
// Update the component.
if ($component = Component::find($command->component_id)) {
dispatch(new UpdateComponentCommand(
execute(new UpdateComponentCommand(
Component::find($command->component_id),
null,
null,

View File

@@ -63,7 +63,7 @@ class CreateIncidentUpdateCommandHandler
$update = IncidentUpdate::create($data);
// Update the original incident with the new status.
dispatch(new UpdateIncidentCommand(
execute(new UpdateIncidentCommand(
$command->incident,
null,
$command->status,

View File

@@ -20,7 +20,7 @@ class ClaimInviteCommandHandler
/**
* Handle the claim invite command.
*
* @param \CachetHQ\Cachet\Bus\Commands\User\ClaimInviteCommand $command
* @param \CachetHQ\Cachet\Bus\Commands\Invite\ClaimInviteCommand $command
*
* @return void
*/

View File

@@ -79,7 +79,7 @@ class CreateMetricPointCommandHandler
*/
protected function findOrCreatePoint(CreateMetricPointCommand $command)
{
$buffer = Carbon::now()->subMinutes($command->metric->threshold);
$buffer = Carbon::now()->subMinutes($command->metric->threshold - 1)->startOfMinute();
if ($point = MetricPoint::where('metric_id', '=', $command->metric->id)->where('value', '=', $command->value)->where('created_at', '>=', $buffer)->first()) {
return $point;

View File

@@ -58,7 +58,7 @@ class SubscribeSubscriberCommandHandler
});
if ($command->verified) {
dispatch(new VerifySubscriberCommand($subscriber));
execute(new VerifySubscriberCommand($subscriber));
} else {
$subscriber->notify(new VerifySubscriptionNotification());
}

View File

@@ -0,0 +1,39 @@
<?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\Tag;
use CachetHQ\Cachet\Bus\Commands\Tag\ApplyTagCommand;
use CachetHQ\Cachet\Models\Taggable;
/**
* This is the apply tag command handler class.
*
* @author James Brooks <james@alt-three.com>
*/
class ApplyTagCommandHandler
{
/**
* Handle the command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Tag\ApplyTagCommand $command
*
* @return void
*/
public function handle(ApplyTagCommand $command)
{
Taggable::firstOrCreate([
'tag_id' => $command->tag->id,
'taggable_id' => $command->model->id,
'taggable_type' => $command->model->getTable(),
]);
}
}

View File

@@ -0,0 +1,39 @@
<?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\Tag;
use CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand;
use CachetHQ\Cachet\Models\Tag;
use Illuminate\Support\Str;
/**
* This is the create tag command handler class.
*
* @author James Brooks <james@alt-three.com>
*/
class CreateTagCommandHandler
{
/**
* Handle the command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand $command
*
* @return \CachetHQ\Cachet\Models\Tag
*/
public function handle(CreateTagCommand $command)
{
return Tag::firstOrCreate([
'name' => $command->name,
'slug' => $command->slug ? $command->slug : Str::slug($command->name),
]);
}
}

View File

@@ -0,0 +1,34 @@
<?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\Tag;
use CachetHQ\Cachet\Bus\Commands\Tag\DeleteTagCommand;
/**
* This is the delete tag command handler class.
*
* @author James Brooks <james@alt-three.com>
*/
class DeleteTagCommandHandler
{
/**
* Handle the command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Tag\DeleteTagCommand $command
*
* @return \CachetHQ\Cachet\Models\Tag
*/
public function handle(DeleteTagCommand $command)
{
$command->tag->delete();
}
}

View File

@@ -0,0 +1,38 @@
<?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\Tag;
use CachetHQ\Cachet\Bus\Commands\Tag\UpdateTagCommand;
use Illuminate\Support\Str;
/**
* This is the create tag command handler class.
*
* @author James Brooks <james@alt-three.com>
*/
class UpdateTagCommandHandler
{
/**
* Handle the command.
*
* @param \CachetHQ\Cachet\Bus\Commands\Tag\UpdateTagCommand $command
*
* @return void
*/
public function handle(UpdateTagCommand $command)
{
return $command->tag->update([
'name' => $command->name,
'slug' => $command->slug ? $command->slug : Str::slug($command->name),
]);
}
}

View File

@@ -44,7 +44,7 @@ class SendBeaconJobHandler
/**
* Handle the send beacon job.
*
* @param \CachetHQ\Cachet\Bus\Jobs\SendBeaconJob $job
* @param \CachetHQ\Cachet\Bus\Jobs\System\SendBeaconJob $job
*
* @return void
*/

View File

@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Composers\Modules;
namespace CachetHQ\Cachet\Composers;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;

View File

@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Composers\Modules;
namespace CachetHQ\Cachet\Composers;
use CachetHQ\Cachet\Models\Metric;
use Illuminate\Contracts\Auth\Guard;

View File

@@ -1,77 +0,0 @@
<?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\Composers;
use CachetHQ\Cachet\Services\Modules\Manager;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\View\View;
/**
* This is the module composer class.
*
* @author Connor S. Parks <connor@connorvg.tv>
* @author James Brooks <james@alt-three.com>
*/
class ModuleComposer
{
/**
* The application instance.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* The modules manager instance.
*
* @var \CachetHQ\Cachet\Services\Modules\Manager
*/
protected $manager;
/**
* Create a new modules composer.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @param \CachetHQ\Cachet\Services\Modules\Manager $manager
*
* @return void
*/
public function __construct(Application $app, Manager $manager)
{
$this->app = $app;
$this->manager = $manager;
}
/**
* Bind data to the view.
*
* @param \Illuminate\Contracts\View\View $view
*
* @return void
*/
public function compose(View $view)
{
$key = $view->getName();
$view->with('view', $key);
$modules = "view.modules: {$key}";
$groups = "view.groups: {$key}";
$modules = $this->app->bound($modules) ? $this->app[$modules] : [];
$groups = $this->app->bound($groups) ? $this->app[$groups] : [];
$modules = $this->manager->groupModules($modules, $groups);
$view->withModules($modules);
}
}

View File

@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Composers\Modules;
namespace CachetHQ\Cachet\Composers;
use CachetHQ\Cachet\Models\Schedule;
use Illuminate\Contracts\View\View;

View File

@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Composers\Modules;
namespace CachetHQ\Cachet\Composers;
use CachetHQ\Cachet\Integrations\Contracts\System;
use Illuminate\Contracts\View\View;
@@ -50,6 +50,9 @@ class StatusComposer
*/
public function compose(View $view)
{
$view->with($this->system->getStatus());
$status = $this->system->getStatus();
$view->withSystemStatus(array_get($status, 'system_status'));
$view->withSystemMessage(array_get($status, 'system_message'));
}
}

View File

@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Composers\Modules;
namespace CachetHQ\Cachet\Composers;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Services\Dates\DateFactory;

View File

@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Composers\Modules;
namespace CachetHQ\Cachet\Composers;
use Illuminate\Contracts\View\View;

View File

@@ -0,0 +1,76 @@
<?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\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Events\Dispatcher;
/**
* This is the app install command.
*
* @author James Brooks <james@alt-three.com>
*/
class AppInstallCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:install';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Installs the application';
/**
* The events instance.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $events;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(Dispatcher $events)
{
$this->events = $events;
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->events->fire('command.installing', $this);
$this->events->fire('command.generatekey', $this);
$this->events->fire('command.cacheconfig', $this);
$this->events->fire('command.cacheroutes', $this);
$this->events->fire('command.publishvendors', $this);
$this->events->fire('command.runmigrations', $this);
$this->events->fire('command.runseeding', $this);
$this->events->fire('command.updatecache', $this);
$this->events->fire('command.linkstorage', $this);
$this->events->fire('command.extrastuff', $this);
$this->events->fire('command.installed', $this);
}
}

View File

@@ -0,0 +1,76 @@
<?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\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Events\Dispatcher;
/**
* This is the app reset command.
*
* @author James Brooks <james@alt-three.com>
*/
class AppResetCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:reset';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Resets and installs the application';
/**
* The events instance.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $events;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(Dispatcher $events)
{
$this->events = $events;
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->events->fire('command.resetting', $this);
$this->events->fire('command.generatekey', $this);
$this->events->fire('command.cacheconfig', $this);
$this->events->fire('command.cacheroutes', $this);
$this->events->fire('command.publishvendors', $this);
$this->events->fire('command.resetmigrations', $this);
$this->events->fire('command.runmigrations', $this);
$this->events->fire('command.runseeding', $this);
$this->events->fire('command.updatecache', $this);
$this->events->fire('command.extrastuff', $this);
$this->events->fire('command.reset', $this);
}
}

View File

@@ -0,0 +1,73 @@
<?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\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Events\Dispatcher;
/**
* This is the app update command.
*
* @author James Brooks <james@alt-three.com>
*/
class AppUpdateCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:update';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Updates the application';
/**
* The events instance.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $events;
/**
* Create a new command instance.
*
* @return void
*/
public function __construct(Dispatcher $events)
{
$this->events = $events;
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->events->fire('command.updating', $this);
$this->events->fire('command.cacheconfig', $this);
$this->events->fire('command.cacheroutes', $this);
$this->events->fire('command.publishvendors', $this);
$this->events->fire('command.runmigrations', $this);
$this->events->fire('command.updatecache', $this);
$this->events->fire('command.extrastuff', $this);
$this->events->fire('command.updated', $this);
}
}

View File

@@ -40,7 +40,7 @@ class BeaconCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
dispatch(new SendBeaconJob());
}

View File

@@ -46,7 +46,7 @@ class DemoMetricPointSeederCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
if (!$this->confirmToProceed()) {
return;

View File

@@ -79,12 +79,13 @@ class DemoSeederCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
if (!$this->confirmToProceed()) {
return;
}
$this->seedUsers();
$this->seedActions();
$this->seedComponentGroups();
$this->seedComponents();
@@ -95,7 +96,6 @@ class DemoSeederCommand extends Command
$this->seedSchedules();
$this->seedSettings();
$this->seedSubscribers();
$this->seedUsers();
$this->info('Database seeded with demo data successfully!');
}
@@ -223,6 +223,7 @@ EINCIDENT;
'component_id' => 0,
'visible' => 1,
'stickied' => false,
'user_id' => 1,
'occurred_at' => Carbon::now(),
],
[
@@ -232,6 +233,7 @@ EINCIDENT;
'component_id' => 0,
'visible' => 1,
'stickied' => false,
'user_id' => 1,
'occurred_at' => Carbon::now(),
],
];

View File

@@ -41,7 +41,7 @@ class InstallCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
if (!$this->confirm('Do you want to install Cachet?')) {
$this->line('Installation aborted. Goodbye!');

View File

@@ -39,7 +39,7 @@ class VersionCommand extends Command
*
* @return void
*/
public function fire()
public function handle()
{
$this->info('Cachet '.CACHET_VERSION.' is installed ⚡');
}

View File

@@ -11,6 +11,9 @@
namespace CachetHQ\Cachet\Console;
use CachetHQ\Cachet\Console\Commands\AppInstallCommand;
use CachetHQ\Cachet\Console\Commands\AppResetCommand;
use CachetHQ\Cachet\Console\Commands\AppUpdateCommand;
use CachetHQ\Cachet\Console\Commands\BeaconCommand;
use CachetHQ\Cachet\Console\Commands\DemoMetricPointSeederCommand;
use CachetHQ\Cachet\Console\Commands\DemoSeederCommand;
@@ -34,6 +37,9 @@ class Kernel extends ConsoleKernel
* @var array
*/
protected $commands = [
AppInstallCommand::class,
AppResetCommand::class,
AppUpdateCommand::class,
BeaconCommand::class,
DemoMetricPointSeederCommand::class,
DemoSeederCommand::class,

View File

@@ -29,7 +29,7 @@ class JsonValidationDisplayer extends JsonDisplayer implements DisplayerInterfac
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function display(Exception $exception, $id, $code, array $headers)
public function display(Exception $exception, string $id, int $code, array $headers)
{
$info = $this->info->generate($exception, $id, 400);
@@ -47,7 +47,7 @@ class JsonValidationDisplayer extends JsonDisplayer implements DisplayerInterfac
*
* @return bool
*/
public function canDisplay(Exception $original, Exception $transformed, $code)
public function canDisplay(Exception $original, Exception $transformed, int $code)
{
return $transformed instanceof ValidationException;
}

View File

@@ -53,7 +53,7 @@ class MaintenanceDisplayer implements DisplayerInterface
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function display(Exception $exception, $id, $code, array $headers)
public function display(Exception $exception, string $id, int $code, array $headers)
{
return new Response($this->render(), $code, array_merge($headers, ['Content-Type' => $this->contentType()]));
}
@@ -87,7 +87,7 @@ class MaintenanceDisplayer implements DisplayerInterface
*
* @return bool
*/
public function canDisplay(Exception $original, Exception $transformed, $code)
public function canDisplay(Exception $original, Exception $transformed, int $code)
{
return $transformed instanceof MaintenanceModeException;
}

View File

@@ -47,7 +47,7 @@ class RedirectDisplayer implements DisplayerInterface
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function display(Exception $exception, $id, $code, array $headers)
public function display(Exception $exception, string $id, int $code, array $headers)
{
return redirect()->guest('auth/login');
}
@@ -71,7 +71,7 @@ class RedirectDisplayer implements DisplayerInterface
*
* @return bool
*/
public function canDisplay(Exception $original, Exception $transformed, $code)
public function canDisplay(Exception $original, Exception $transformed, int $code)
{
$redirect = $transformed instanceof HttpExceptionInterface && $transformed->getStatusCode() === 401;

View File

@@ -47,7 +47,7 @@ class ThrottleDisplayer implements DisplayerInterface
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function display(Exception $exception, $id, $code, array $headers)
public function display(Exception $exception, string $id, int $code, array $headers)
{
return cachet_redirect('auth.login')->withError(trans('forms.login.rate-limit'));
}
@@ -71,7 +71,7 @@ class ThrottleDisplayer implements DisplayerInterface
*
* @return bool
*/
public function canDisplay(Exception $original, Exception $transformed, $code)
public function canDisplay(Exception $original, Exception $transformed, int $code)
{
return $transformed instanceof TooManyRequestsHttpException && $this->request->is('auth*');
}

View File

@@ -27,7 +27,7 @@ class ApiFilter
*
* @return \GrahamCampbell\Exceptions\Displayers\DisplayerInterface[]
*/
public function filter(array $displayers, Request $request, Exception $original, Exception $transformed, $code)
public function filter(array $displayers, Request $request, Exception $original, Exception $transformed, int $code)
{
if ($request->is('api*')) {
foreach ($displayers as $index => $displayer) {

View File

@@ -33,6 +33,8 @@ class AppServiceProvider extends ServiceProvider
* Boot the service provider.
*
* @param \AltThree\Bus\Dispatcher $dispatcher
*
* @return void
*/
public function boot(Dispatcher $dispatcher)
{
@@ -54,6 +56,7 @@ class AppServiceProvider extends ServiceProvider
'metrics' => \CachetHQ\Cachet\Models\Metric::class,
'schedules' => \CachetHQ\Cachet\Models\Schedule::class,
'subscriber' => \CachetHQ\Cachet\Models\Subscriber::class,
'tags' => \CachetHQ\Cachet\Models\Tag::class,
]);
}

View File

@@ -12,17 +12,16 @@
namespace CachetHQ\Cachet\Foundation\Providers;
use CachetHQ\Cachet\Composers\AppComposer;
use CachetHQ\Cachet\Composers\ComponentsComposer;
use CachetHQ\Cachet\Composers\CurrentUserComposer;
use CachetHQ\Cachet\Composers\DashboardComposer;
use CachetHQ\Cachet\Composers\ModuleComposer;
use CachetHQ\Cachet\Composers\Modules\ComponentsComposer as ComponentsModuleComposer;
use CachetHQ\Cachet\Composers\Modules\MetricsComposer as MetricsModuleComposer;
use CachetHQ\Cachet\Composers\Modules\ScheduledComposer as ScheduledModuleComposer;
use CachetHQ\Cachet\Composers\Modules\StatusComposer as StatusModuleComposer;
use CachetHQ\Cachet\Composers\Modules\StickiedComposer as StickiedModuleComposer;
use CachetHQ\Cachet\Composers\Modules\TimelineComposer as TimelineModuleComposer;
use CachetHQ\Cachet\Composers\MetricsComposer;
use CachetHQ\Cachet\Composers\ScheduledComposer;
use CachetHQ\Cachet\Composers\SettingsComposer;
use CachetHQ\Cachet\Composers\StatusComposer;
use CachetHQ\Cachet\Composers\StickiedComposer;
use CachetHQ\Cachet\Composers\ThemeComposer;
use CachetHQ\Cachet\Composers\TimelineComposer;
use CachetHQ\Cachet\Composers\TimezoneLocaleComposer;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\ServiceProvider;
@@ -42,13 +41,12 @@ class ComposerServiceProvider extends ServiceProvider
$factory->composer('dashboard.*', DashboardComposer::class);
$factory->composer(['setup.*', 'dashboard.settings.localization'], TimezoneLocaleComposer::class);
$factory->composer('*', ModuleComposer::class);
$factory->composer('partials.modules.components', ComponentsModuleComposer::class);
$factory->composer('partials.modules.metrics', MetricsModuleComposer::class);
$factory->composer('partials.modules.stickied', StickiedModuleComposer::class);
$factory->composer('partials.modules.scheduled', ScheduledModuleComposer::class);
$factory->composer('partials.modules.status', StatusModuleComposer::class);
$factory->composer('partials.modules.timeline', TimelineModuleComposer::class);
$factory->composer('partials.modules.components', ComponentsComposer::class);
$factory->composer('partials.modules.metrics', MetricsComposer::class);
$factory->composer('partials.modules.stickied', StickiedComposer::class);
$factory->composer('partials.modules.scheduled', ScheduledComposer::class);
$factory->composer('partials.modules.status', StatusComposer::class);
$factory->composer('partials.modules.timeline', TimelineComposer::class);
$factory->composer(['dashboard.settings.mail', 'setup.*'], SettingsComposer::class);
}

View File

@@ -1,95 +0,0 @@
<?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\Foundation\Providers;
use CachetHQ\Cachet\Services\Modules\Renderer as ModulesRenderer;
use Illuminate\Support\ServiceProvider;
use Illuminate\View\Compilers\BladeCompiler;
class ModuleServiceProvider extends ServiceProvider
{
/**
* The module definitions.
*
* @var array
*/
protected $modules = [
'index' => [
['group' => 'messages', 'partial' => 'partials.modules.messages'],
['group' => 'status', 'partial' => 'partials.modules.status'],
['group' => 'components', 'partial' => 'partials.modules.components'],
['group' => 'metrics', 'partial' => 'partials.modules.metrics'],
['group' => 'stickied', 'partial' => 'partials.modules.stickied'],
['group' => 'scheduled', 'partial' => 'partials.modules.scheduled'],
['group' => 'timeline', 'partial' => 'partials.modules.timeline'],
],
];
/**
* The group definitions.
*
* @var array
*/
protected $groups = [
'index' => [
'messages' => 10000,
'status' => 20000,
'components' => 30000,
'metrics' => 40000,
'scheduled' => 50000,
'stickied' => 60000,
'timeline' => 70000,
],
];
/**
* Boot the service provider.
*
* @param \Illuminate\View\Compilers\BladeCompiler $blade
*/
public function boot(BladeCompiler $blade)
{
$blade->directive('modules', function ($group) {
return sprintf(
'<?php echo $app->call(\'%s@%s\', [
\'factory\' => $__env,
\'data\' => array_except(get_defined_vars(), array(\'__data\', \'__path\')),
\'modules\' => $modules,
\'group\' => %s,
]); ?>',
ModulesRenderer::class,
'renderModules',
empty($group) ? 'null' : $group
);
});
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
foreach ($this->modules as $key => $modules) {
$this->app->singleton("view.modules: {$key}", function () use ($modules) {
return $modules;
});
}
foreach ($this->groups as $key => $groups) {
$this->app->singleton("view.groups: {$key}", function () use ($groups) {
return $groups;
});
}
}
}

View File

@@ -13,7 +13,13 @@ namespace CachetHQ\Cachet\Foundation\Providers;
use Barryvdh\Cors\HandleCors;
use CachetHQ\Cachet\Http\Middleware\Acceptable;
use CachetHQ\Cachet\Http\Middleware\Authenticate;
use CachetHQ\Cachet\Http\Middleware\Timezone;
use CachetHQ\Cachet\Http\Routes\ApiSystemRoutes;
use CachetHQ\Cachet\Http\Routes\AuthRoutes;
use CachetHQ\Cachet\Http\Routes\Setup\ApiRoutes as ApiSetupRoutes;
use CachetHQ\Cachet\Http\Routes\SetupRoutes;
use CachetHQ\Cachet\Http\Routes\SignupRoutes;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
@@ -41,6 +47,21 @@ class RouteServiceProvider extends ServiceProvider
*/
protected $namespace = 'CachetHQ\Cachet\Http\Controllers';
/**
* These are the route files that should always be available anonymously.
*
* When applying the always_authenticate feature, these routes will be skipped.
*
* @var string[]
*/
protected $whitelistedAuthRoutes = [
AuthRoutes::class,
SetupRoutes::class,
SignupRoutes::class,
ApiSystemRoutes::class,
ApiSetupRoutes::class,
];
/**
* Define the route model bindings, pattern filters, etc.
*
@@ -89,6 +110,7 @@ class RouteServiceProvider extends ServiceProvider
$router->group(['namespace' => $this->namespace, 'as' => 'core::'], function (Router $router) {
$path = app_path('Http/Routes');
$applyAlwaysAuthenticate = $this->app['config']->get('setting.always_authenticate', false);
$AllFileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
$PhpFileIterator = new \RegexIterator($AllFileIterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);
@@ -100,9 +122,9 @@ class RouteServiceProvider extends ServiceProvider
$routes = $this->app->make("CachetHQ\\Cachet\\Http\\Routes${class}");
if ($routes::$browser) {
$this->mapForBrowser($router, $routes);
$this->mapForBrowser($router, $routes, $applyAlwaysAuthenticate);
} else {
$this->mapOtherwise($router, $routes);
$this->mapOtherwise($router, $routes, $applyAlwaysAuthenticate);
}
}
});
@@ -113,10 +135,11 @@ class RouteServiceProvider extends ServiceProvider
*
* @param \Illuminate\Routing\Router $router
* @param object $routes
* @param bool $applyAlwaysAuthenticate
*
* @return void
*/
protected function mapForBrowser(Router $router, $routes)
protected function mapForBrowser(Router $router, $routes, $applyAlwaysAuthenticate)
{
$middleware = [
EncryptCookies::class,
@@ -127,6 +150,10 @@ class RouteServiceProvider extends ServiceProvider
SubstituteBindings::class,
];
if ($applyAlwaysAuthenticate && !$this->isWhiteListedAuthRoute($routes)) {
$middleware[] = Authenticate::class;
}
$router->group(['middleware' => $middleware], function (Router $router) use ($routes) {
$routes->map($router);
});
@@ -137,10 +164,11 @@ class RouteServiceProvider extends ServiceProvider
*
* @param \Illuminate\Routing\Router $router
* @param object $routes
* @param bool $applyAlwaysAuthenticate
*
* @return void
*/
protected function mapOtherwise(Router $router, $routes)
protected function mapOtherwise(Router $router, $routes, $applyAlwaysAuthenticate)
{
$middleware = [
HandleCors::class,
@@ -149,8 +177,31 @@ class RouteServiceProvider extends ServiceProvider
Timezone::class,
];
if ($applyAlwaysAuthenticate && !$this->isWhiteListedAuthRoute($routes)) {
$middleware[] = 'auth.api:true';
}
$router->group(['middleware' => $middleware], function (Router $router) use ($routes) {
$routes->map($router);
});
}
/**
* Validates if the route object is an instance of the whitelisted routes.
* A small workaround since we cant use multiple classes in a `instanceof` comparison.
*
* @param object $routes
*
* @return bool
*/
private function isWhiteListedAuthRoute($routes)
{
foreach ($this->whitelistedAuthRoutes as $whitelistedRoute) {
if (is_a($routes, $whitelistedRoute)) {
return true;
}
}
return false;
}
}

View File

@@ -14,11 +14,13 @@ namespace CachetHQ\Cachet\Http\Controllers\Api;
use CachetHQ\Cachet\Bus\Commands\Component\CreateComponentCommand;
use CachetHQ\Cachet\Bus\Commands\Component\RemoveComponentCommand;
use CachetHQ\Cachet\Bus\Commands\Component\UpdateComponentCommand;
use CachetHQ\Cachet\Bus\Commands\Tag\ApplyTagCommand;
use CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Tag;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Database\QueryException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
@@ -37,6 +39,10 @@ class ComponentController extends AbstractApiController
$components = Component::enabled();
}
if ($tags = Binput::get('tags')) {
$components->withAnyTags($tags);
}
$components->search(Binput::except(['sort', 'order', 'per_page']));
if ($sortBy = Binput::get('sort')) {
@@ -70,7 +76,7 @@ class ComponentController extends AbstractApiController
public function store()
{
try {
$component = dispatch(new CreateComponentCommand(
$component = execute(new CreateComponentCommand(
Binput::get('name'),
Binput::get('description'),
Binput::get('status'),
@@ -85,17 +91,16 @@ class ComponentController extends AbstractApiController
}
if (Binput::has('tags')) {
$component->tags()->delete();
// The component was added successfully, so now let's deal with the tags.
$tags = preg_split('/ ?, ?/', Binput::get('tags'));
// For every tag, do we need to create it?
$componentTags = array_map(function ($taggable) use ($component) {
return Tag::firstOrCreate([
'name' => $taggable,
])->id;
}, $tags);
$component->tags()->sync($componentTags);
Collection::make(preg_split('/ ?, ?/', $tags))->map(function ($tag) {
return trim($tag);
})->map(function ($tag) {
return execute(new CreateTagCommand($tag));
})->each(function ($tag) use ($component) {
execute(new ApplyTagCommand($component, $tag));
});
}
return $this->item($component);
@@ -111,7 +116,7 @@ class ComponentController extends AbstractApiController
public function update(Component $component)
{
try {
dispatch(new UpdateComponentCommand(
execute(new UpdateComponentCommand(
$component,
Binput::get('name'),
Binput::get('description'),
@@ -119,7 +124,7 @@ class ComponentController extends AbstractApiController
Binput::get('link'),
Binput::get('order'),
Binput::get('group_id'),
(bool) Binput::get('enabled', true),
(bool) Binput::get('enabled'),
Binput::get('meta', null),
(bool) Binput::get('silent', false)
));
@@ -128,14 +133,16 @@ class ComponentController extends AbstractApiController
}
if (Binput::has('tags')) {
$tags = preg_split('/ ?, ?/', Binput::get('tags'));
$component->tags()->delete();
// For every tag, do we need to create it?
$componentTags = array_map(function ($taggable) use ($component) {
return Tag::firstOrCreate(['name' => $taggable])->id;
}, $tags);
$component->tags()->sync($componentTags);
// The component was added successfully, so now let's deal with the tags.
Collection::make(preg_split('/ ?, ?/', $tags))->map(function ($tag) {
return trim($tag);
})->map(function ($tag) {
return execute(new CreateTagCommand($tag));
})->each(function ($tag) use ($component) {
execute(new ApplyTagCommand($component, $tag));
});
}
return $this->item($component);
@@ -150,7 +157,7 @@ class ComponentController extends AbstractApiController
*/
public function destroy(Component $component)
{
dispatch(new RemoveComponentCommand($component));
execute(new RemoveComponentCommand($component));
return $this->noContent();
}

View File

@@ -92,7 +92,7 @@ class ComponentGroupController extends AbstractApiController
public function store()
{
try {
$group = dispatch(new CreateComponentGroupCommand(
$group = execute(new CreateComponentGroupCommand(
Binput::get('name'),
Binput::get('order', 0),
Binput::get('collapsed', 0),
@@ -115,7 +115,7 @@ class ComponentGroupController extends AbstractApiController
public function update(ComponentGroup $group)
{
try {
$group = dispatch(new UpdateComponentGroupCommand(
$group = execute(new UpdateComponentGroupCommand(
$group,
Binput::get('name'),
Binput::get('order'),
@@ -138,7 +138,7 @@ class ComponentGroupController extends AbstractApiController
*/
public function destroy(ComponentGroup $group)
{
dispatch(new RemoveComponentGroupCommand($group));
execute(new RemoveComponentGroupCommand($group));
return $this->noContent();
}

View File

@@ -17,7 +17,7 @@ use CachetHQ\Cachet\Integrations\Contracts\System;
/**
* This is the general api controller.
*
* @author James Brooks <james@bluebaytravel.co.uk>
* @author James Brooks <james@alt-three.com>
*/
class GeneralController extends AbstractApiController
{

View File

@@ -67,7 +67,7 @@ class IncidentController extends AbstractApiController
public function store()
{
try {
$incident = dispatch(new CreateIncidentCommand(
$incident = execute(new CreateIncidentCommand(
Binput::get('name'),
Binput::get('status'),
Binput::get('message', null, false, false),
@@ -98,7 +98,7 @@ class IncidentController extends AbstractApiController
public function update(Incident $incident)
{
try {
$incident = dispatch(new UpdateIncidentCommand(
$incident = execute(new UpdateIncidentCommand(
$incident,
Binput::get('name'),
Binput::get('status'),
@@ -128,7 +128,7 @@ class IncidentController extends AbstractApiController
*/
public function destroy(Incident $incident)
{
dispatch(new RemoveIncidentCommand($incident));
execute(new RemoveIncidentCommand($incident));
return $this->noContent();
}

View File

@@ -74,7 +74,7 @@ class IncidentUpdateController extends AbstractApiController
public function store(Incident $incident)
{
try {
$update = dispatch(new CreateIncidentUpdateCommand(
$update = execute(new CreateIncidentUpdateCommand(
$incident,
Binput::get('status'),
Binput::get('message'),
@@ -100,7 +100,7 @@ class IncidentUpdateController extends AbstractApiController
public function update(Incident $incident, IncidentUpdate $update)
{
try {
$update = dispatch(new UpdateIncidentUpdateCommand(
$update = execute(new UpdateIncidentUpdateCommand(
$update,
Binput::get('status'),
Binput::get('message'),
@@ -124,7 +124,7 @@ class IncidentUpdateController extends AbstractApiController
public function destroy(Incident $incident, IncidentUpdate $update)
{
try {
dispatch(new RemoveIncidentUpdateCommand($update));
execute(new RemoveIncidentUpdateCommand($update));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}

View File

@@ -62,7 +62,7 @@ class MetricController extends AbstractApiController
public function store()
{
try {
$metric = dispatch(new CreateMetricCommand(
$metric = execute(new CreateMetricCommand(
Binput::get('name'),
Binput::get('suffix'),
Binput::get('description'),
@@ -92,7 +92,7 @@ class MetricController extends AbstractApiController
public function update(Metric $metric)
{
try {
$metric = dispatch(new UpdateMetricCommand(
$metric = execute(new UpdateMetricCommand(
$metric,
Binput::get('name'),
Binput::get('suffix'),
@@ -122,7 +122,7 @@ class MetricController extends AbstractApiController
*/
public function destroy(Metric $metric)
{
dispatch(new RemoveMetricCommand($metric));
execute(new RemoveMetricCommand($metric));
return $this->noContent();
}

View File

@@ -48,7 +48,7 @@ class MetricPointController extends AbstractApiController
public function store(Metric $metric)
{
try {
$metricPoint = dispatch(new CreateMetricPointCommand(
$metricPoint = execute(new CreateMetricPointCommand(
$metric,
Binput::get('value'),
Binput::get('timestamp')
@@ -64,13 +64,13 @@ class MetricPointController extends AbstractApiController
* Updates a metric point.
*
* @param \CachetHQ\Cachet\Models\Metric $metric
* @param \CachetHQ\Cachet\Models\MetircPoint $metricPoint
* @param \CachetHQ\Cachet\Models\MetricPoint $metricPoint
*
* @return \Illuminate\Http\JsonResponse
*/
public function update(Metric $metric, MetricPoint $metricPoint)
{
$metricPoint = dispatch(new UpdateMetricPointCommand(
$metricPoint = execute(new UpdateMetricPointCommand(
$metricPoint,
$metric,
Binput::get('value'),
@@ -90,7 +90,7 @@ class MetricPointController extends AbstractApiController
*/
public function destroy(Metric $metric, MetricPoint $metricPoint)
{
dispatch(new RemoveMetricPointCommand($metricPoint));
execute(new RemoveMetricPointCommand($metricPoint));
return $this->noContent();
}

View File

@@ -34,7 +34,7 @@ class ScheduleController extends AbstractApiController
*/
public function index()
{
$schedule = Schedule::whereRaw('1 = 1');
$schedule = Schedule::query();
if ($sortBy = Binput::get('sort')) {
$direction = Binput::has('order') && Binput::get('order') == 'desc';
@@ -67,7 +67,7 @@ class ScheduleController extends AbstractApiController
public function store()
{
try {
$schedule = dispatch(new CreateScheduleCommand(
$schedule = execute(new CreateScheduleCommand(
Binput::get('name'),
Binput::get('message', null, false, false),
Binput::get('status'),
@@ -92,7 +92,7 @@ class ScheduleController extends AbstractApiController
public function update(Schedule $schedule)
{
try {
$schedule = dispatch(new UpdateScheduleCommand(
$schedule = execute(new UpdateScheduleCommand(
$schedule,
Binput::get('name'),
Binput::get('message'),
@@ -118,7 +118,7 @@ class ScheduleController extends AbstractApiController
public function destroy(Schedule $schedule)
{
try {
dispatch(new DeleteScheduleCommand($schedule));
execute(new DeleteScheduleCommand($schedule));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}

View File

@@ -50,7 +50,7 @@ class SubscriberController extends AbstractApiController
$verified = Binput::get('verify', app(Repository::class)->get('setting.skip_subscriber_verification'));
try {
$subscriber = dispatch(new SubscribeSubscriberCommand(Binput::get('email'), $verified, Binput::get('components', null)));
$subscriber = execute(new SubscribeSubscriberCommand(Binput::get('email'), $verified, Binput::get('components', null)));
} catch (QueryException $e) {
throw new BadRequestHttpException();
}
@@ -67,7 +67,7 @@ class SubscriberController extends AbstractApiController
*/
public function destroy(Subscriber $subscriber)
{
dispatch(new UnsubscribeSubscriberCommand($subscriber));
execute(new UnsubscribeSubscriberCommand($subscriber));
return $this->noContent();
}

View File

@@ -30,7 +30,7 @@ class SubscriptionController extends AbstractApiController
*/
public function destroy(Subscription $subscription)
{
dispatch(new UnsubscribeSubscriptionCommand($subscription));
execute(new UnsubscribeSubscriptionCommand($subscription));
return $this->noContent();
}

View File

@@ -36,7 +36,7 @@ class ApiController extends AbstractApiController
public function postUpdateComponent(Component $component)
{
try {
dispatch(new UpdateComponentCommand(
execute(new UpdateComponentCommand(
$component,
$component->name,
$component->description,
@@ -68,7 +68,7 @@ class ApiController extends AbstractApiController
try {
$component = Component::find($componentId);
dispatch(new UpdateComponentCommand(
execute(new UpdateComponentCommand(
$component,
$component->name,
$component->description,
@@ -100,7 +100,7 @@ class ApiController extends AbstractApiController
foreach ($groupData as $order => $groupId) {
$group = ComponentGroup::find($groupId);
dispatch(new UpdateComponentGroupCommand(
execute(new UpdateComponentGroupCommand(
$group,
$group->name,
$order + 1,

View File

@@ -15,16 +15,20 @@ use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Bus\Commands\Component\CreateComponentCommand;
use CachetHQ\Cachet\Bus\Commands\Component\RemoveComponentCommand;
use CachetHQ\Cachet\Bus\Commands\Component\UpdateComponentCommand;
use CachetHQ\Cachet\Bus\Commands\ComponentGroup\CreateComponentGroupCommand;
use CachetHQ\Cachet\Bus\Commands\ComponentGroup\RemoveComponentGroupCommand;
use CachetHQ\Cachet\Bus\Commands\ComponentGroup\UpdateComponentGroupCommand;
use CachetHQ\Cachet\Bus\Commands\Tag\ApplyTagCommand;
use CachetHQ\Cachet\Bus\Commands\Tag\CreateTagCommand;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use CachetHQ\Cachet\Models\Tag;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Routing\Controller;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\View;
/**
* This is the component controller class.
*
* @author James Brooks <james@alt-three.com>
*/
class ComponentController extends Controller
{
/**
@@ -57,8 +61,8 @@ class ComponentController extends Controller
];
View::share([
'sub_menu' => $this->subMenu,
'sub_title' => trans_choice('dashboard.components.components', 2),
'subMenu' => $this->subMenu,
'subTitle' => trans_choice('dashboard.components.components', 2),
]);
}
@@ -79,21 +83,6 @@ class ComponentController extends Controller
->withSubMenu($this->subMenu);
}
/**
* Shows the component groups view.
*
* @return \Illuminate\View\View
*/
public function showComponentGroups()
{
$this->subMenu['groups']['active'] = true;
return View::make('dashboard.components.groups.index')
->withPageTitle(trans_choice('dashboard.components.groups.groups', 2).' - '.trans('dashboard.dashboard'))
->withGroups(ComponentGroup::orderBy('order')->get())
->withSubMenu($this->subMenu);
}
/**
* Shows the edit component view.
*
@@ -126,7 +115,7 @@ class ComponentController extends Controller
$tags = array_pull($componentData, 'tags');
try {
$component = dispatch(new UpdateComponentCommand(
$component = execute(new UpdateComponentCommand(
$component,
$componentData['name'],
$componentData['description'],
@@ -145,15 +134,16 @@ class ComponentController extends Controller
->withErrors($e->getMessageBag());
}
$component->tags()->delete();
// The component was added successfully, so now let's deal with the tags.
$tags = preg_split('/ ?, ?/', $tags);
// For every tag, do we need to create it?
$componentTags = array_map(function ($taggable) {
return Tag::firstOrCreate(['name' => $taggable])->id;
}, $tags);
$component->tags()->sync($componentTags);
Collection::make(preg_split('/ ?, ?/', $tags))->map(function ($tag) {
return trim($tag);
})->map(function ($tag) {
return execute(new CreateTagCommand($tag));
})->each(function ($tag) use ($component) {
execute(new ApplyTagCommand($component, $tag));
});
return cachet_redirect('dashboard.components.edit', [$component->id])
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.edit.success')));
@@ -182,7 +172,7 @@ class ComponentController extends Controller
$tags = array_pull($componentData, 'tags');
try {
$component = dispatch(new CreateComponentCommand(
$component = execute(new CreateComponentCommand(
$componentData['name'],
$componentData['description'],
$componentData['status'],
@@ -200,14 +190,13 @@ class ComponentController extends Controller
}
// The component was added successfully, so now let's deal with the tags.
$tags = preg_split('/ ?, ?/', $tags);
// For every tag, do we need to create it?
$componentTags = array_map(function ($taggable) {
return Tag::firstOrCreate(['name' => $taggable])->id;
}, $tags);
$component->tags()->sync($componentTags);
Collection::make(preg_split('/ ?, ?/', $tags))->map(function ($tag) {
return trim($tag);
})->map(function ($tag) {
return execute(new CreateTagCommand($tag));
})->each(function ($tag) use ($component) {
execute(new ApplyTagCommand($component, $tag));
});
return cachet_redirect('dashboard.components')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.add.success')));
@@ -222,102 +211,9 @@ class ComponentController extends Controller
*/
public function deleteComponentAction(Component $component)
{
dispatch(new RemoveComponentCommand($component));
execute(new RemoveComponentCommand($component));
return cachet_redirect('dashboard.components')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.delete.success')));
}
/**
* Deletes a given component group.
*
* @param \CachetHQ\Cachet\Models\ComponentGroup $group
*
* @return \Illuminate\Http\RedirectResponse
*/
public function deleteComponentGroupAction(ComponentGroup $group)
{
dispatch(new RemoveComponentGroupCommand($group));
return cachet_redirect('dashboard.components.groups')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.delete.success')));
}
/**
* Shows the add component group view.
*
* @return \Illuminate\View\View
*/
public function showAddComponentGroup()
{
return View::make('dashboard.components.groups.add')
->withPageTitle(trans('dashboard.components.groups.add.title').' - '.trans('dashboard.dashboard'));
}
/**
* Shows the edit component group view.
*
* @param \CachetHQ\Cachet\Models\ComponentGroup $group
*
* @return \Illuminate\View\View
*/
public function showEditComponentGroup(ComponentGroup $group)
{
return View::make('dashboard.components.groups.edit')
->withPageTitle(trans('dashboard.components.groups.edit.title').' - '.trans('dashboard.dashboard'))
->withGroup($group);
}
/**
* Creates a new component.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postAddComponentGroup()
{
try {
$group = dispatch(new CreateComponentGroupCommand(
Binput::get('name'),
Binput::get('order', 0),
Binput::get('collapsed'),
Binput::get('visible')
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.components.groups.create')
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.components.groups.add.failure')))
->withErrors($e->getMessageBag());
}
return cachet_redirect('dashboard.components.groups')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.groups.add.success')));
}
/**
* Updates a component group.
*
* @param \CachetHQ\Cachet\Models\ComponentGroup $group
*
* @return \Illuminate\Http\RedirectResponse
*/
public function updateComponentGroupAction(ComponentGroup $group)
{
try {
$group = dispatch(new UpdateComponentGroupCommand(
$group,
Binput::get('name'),
$group->order,
Binput::get('collapsed'),
Binput::get('visible')
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.components.groups.edit', [$group->id])
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.components.groups.edit.failure')))
->withErrors($e->getMessageBag());
}
return cachet_redirect('dashboard.components.groups.edit', [$group->id])
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.groups.edit.success')));
}
}

View File

@@ -0,0 +1,173 @@
<?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\Http\Controllers\Dashboard;
use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Bus\Commands\ComponentGroup\CreateComponentGroupCommand;
use CachetHQ\Cachet\Bus\Commands\ComponentGroup\RemoveComponentGroupCommand;
use CachetHQ\Cachet\Bus\Commands\ComponentGroup\UpdateComponentGroupCommand;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\View;
/**
* This is the component group controller class.
*
* @author James Brooks <james@bluebaytravel.co.uk>
*/
class ComponentGroupController extends Controller
{
/**
* Array of sub-menu items.
*
* @var array
*/
protected $subMenu = [];
/**
* Creates a new component controller instance.
*
* @return void
*/
public function __construct()
{
$this->subMenu = [
'components' => [
'title' => trans('dashboard.components.components'),
'url' => cachet_route('dashboard.components'),
'icon' => 'ion-ios-browsers',
'active' => false,
],
'groups' => [
'title' => trans_choice('dashboard.components.groups.groups', 2),
'url' => cachet_route('dashboard.components.groups'),
'icon' => 'ion-folder',
'active' => false,
],
];
View::share([
'sub_menu' => $this->subMenu,
'subTitle' => trans_choice('dashboard.components.components', 2),
]);
}
/**
* Shows the component groups view.
*
* @return \Illuminate\View\View
*/
public function showComponentGroups()
{
$this->subMenu['groups']['active'] = true;
return View::make('dashboard.components.groups.index')
->withPageTitle(trans_choice('dashboard.components.groups.groups', 2).' - '.trans('dashboard.dashboard'))
->withGroups(ComponentGroup::orderBy('order')->get())
->withSubMenu($this->subMenu);
}
/**
* Deletes a given component group.
*
* @param \CachetHQ\Cachet\Models\ComponentGroup $group
*
* @return \Illuminate\Http\RedirectResponse
*/
public function deleteComponentGroupAction(ComponentGroup $group)
{
execute(new RemoveComponentGroupCommand($group));
return cachet_redirect('dashboard.components.groups')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.delete.success')));
}
/**
* Shows the add component group view.
*
* @return \Illuminate\View\View
*/
public function showAddComponentGroup()
{
return View::make('dashboard.components.groups.add')
->withPageTitle(trans('dashboard.components.groups.add.title').' - '.trans('dashboard.dashboard'));
}
/**
* Shows the edit component group view.
*
* @param \CachetHQ\Cachet\Models\ComponentGroup $group
*
* @return \Illuminate\View\View
*/
public function showEditComponentGroup(ComponentGroup $group)
{
return View::make('dashboard.components.groups.edit')
->withPageTitle(trans('dashboard.components.groups.edit.title').' - '.trans('dashboard.dashboard'))
->withGroup($group);
}
/**
* Creates a new component.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postAddComponentGroup()
{
try {
$group = execute(new CreateComponentGroupCommand(
Binput::get('name'),
Binput::get('order', 0),
Binput::get('collapsed'),
Binput::get('visible')
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.components.groups.create')
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.components.groups.add.failure')))
->withErrors($e->getMessageBag());
}
return cachet_redirect('dashboard.components.groups')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.groups.add.success')));
}
/**
* Updates a component group.
*
* @param \CachetHQ\Cachet\Models\ComponentGroup $group
*
* @return \Illuminate\Http\RedirectResponse
*/
public function updateComponentGroupAction(ComponentGroup $group)
{
try {
$group = execute(new UpdateComponentGroupCommand(
$group,
Binput::get('name'),
$group->order,
Binput::get('collapsed'),
Binput::get('visible')
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.components.groups.edit', [$group->id])
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.components.groups.edit.failure')))
->withErrors($e->getMessageBag());
}
return cachet_redirect('dashboard.components.groups.edit', [$group->id])
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.components.groups.edit.success')));
}
}

View File

@@ -101,7 +101,7 @@ class DashboardController extends Controller
$welcomeUser = !Auth::user()->welcomed;
if ($welcomeUser) {
dispatch(new WelcomeUserCommand(Auth::user()));
execute(new WelcomeUserCommand(Auth::user()));
}
$entries = null;

View File

@@ -15,14 +15,11 @@ use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Bus\Commands\Incident\CreateIncidentCommand;
use CachetHQ\Cachet\Bus\Commands\Incident\RemoveIncidentCommand;
use CachetHQ\Cachet\Bus\Commands\Incident\UpdateIncidentCommand;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\CreateIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\UpdateIncidentUpdateCommand;
use CachetHQ\Cachet\Integrations\Contracts\System;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\IncidentUpdate;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Routing\Controller;
@@ -68,7 +65,7 @@ class IncidentController extends Controller
$this->auth = $auth;
$this->system = $system;
View::share('sub_title', trans('dashboard.incidents.title'));
View::share('subTitle', trans('dashboard.incidents.title'));
}
/**
@@ -120,7 +117,7 @@ class IncidentController extends Controller
public function createIncidentAction()
{
try {
$incident = dispatch(new CreateIncidentCommand(
$incident = execute(new CreateIncidentCommand(
Binput::get('name'),
Binput::get('status'),
Binput::get('message', null, false, false),
@@ -217,7 +214,7 @@ class IncidentController extends Controller
*/
public function deleteIncidentAction(Incident $incident)
{
dispatch(new RemoveIncidentCommand($incident));
execute(new RemoveIncidentCommand($incident));
return cachet_redirect('dashboard.incidents')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.incidents.delete.success')));
@@ -250,7 +247,7 @@ class IncidentController extends Controller
public function editIncidentAction(Incident $incident)
{
try {
$incident = dispatch(new UpdateIncidentCommand(
$incident = execute(new UpdateIncidentCommand(
$incident,
Binput::get('name'),
Binput::get('status'),
@@ -300,107 +297,4 @@ class IncidentController extends Controller
return cachet_redirect('dashboard.templates.edit', ['id' => $template->id])
->withUpdatedTemplate($template);
}
/**
* Shows the incident update form.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\View\View
*/
public function showIncidentUpdates(Incident $incident)
{
return View::make('dashboard.incidents.updates.index')->withIncident($incident);
}
/**
* Shows the incident update form.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\View\View
*/
public function showCreateIncidentUpdateAction(Incident $incident)
{
return View::make('dashboard.incidents.updates.add')
->withIncident($incident)
->withNotificationsEnabled($this->system->canNotifySubscribers());
}
/**
* Creates a new incident update.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\Http\RedirectResponse
*/
public function createIncidentUpdateAction(Incident $incident)
{
try {
$incidentUpdate = dispatch(new CreateIncidentUpdateCommand(
$incident,
Binput::get('status'),
Binput::get('message'),
Binput::get('component_id'),
Binput::get('component_status'),
$this->auth->user()
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.incidents.updates.create', ['id' => $incident->id])
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.incidents.updates.add.failure')))
->withErrors($e->getMessageBag());
}
if ($incident->component) {
$incident->component->update(['status' => Binput::get('component_status')]);
}
return cachet_redirect('dashboard.incidents')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.incidents.updates.success')));
}
/**
* Shows the edit incident view.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
* @param \CachetHQ\Cachet\Models\IncidentUpdate $incidentUpdate
*
* @return \Illuminate\View\View
*/
public function showEditIncidentUpdateAction(Incident $incident, IncidentUpdate $incidentUpdate)
{
return View::make('dashboard.incidents.updates.edit')
->withIncident($incident)
->withUpdate($incidentUpdate)
->withNotificationsEnabled($this->system->canNotifySubscribers());
}
/**
* Edit an incident update.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
* @param \CachetHQ\Cachet\Models\IncidentUpdate $incidentUpdate
*
* @return \Illuminate\Http\RedirectResponse
*/
public function editIncidentUpdateAction(Incident $incident, IncidentUpdate $incidentUpdate)
{
try {
$incidentUpdate = dispatch(new UpdateIncidentUpdateCommand(
$incidentUpdate,
Binput::get('status'),
Binput::get('message'),
$this->auth->user()
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.incidents.updates.edit', ['incident' => $incident->id, 'incident_update' => $incidentUpdate->id])
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.incidents.updates.edit.failure')))
->withErrors($e->getMessageBag());
}
return cachet_redirect('dashboard.incidents.updates', ['incident' => $incident->id])
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.incidents.updates.edit.success')));
}
}

View File

@@ -0,0 +1,161 @@
<?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\Http\Controllers\Dashboard;
use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Integrations\Contracts\System;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\View;
/**
* This is the incident template controller.
*
* @author James Brooks <james@alt-three.com>
*/
class IncidentTemplateController extends Controller
{
/**
* Stores the sub-sidebar tree list.
*
* @var array
*/
protected $subMenu = [];
/**
* The guard instance.
*
* @var \Illuminate\Contracts\Auth\Guard
*/
protected $auth;
/**
* The system instance.
*
* @var \CachetHQ\Cachet\Integrations\Contracts\System
*/
protected $system;
/**
* Creates a new incident controller instance.
*
* @param \Illuminate\Contracts\Auth\Guard $auth
*
* @return void
*/
public function __construct(Guard $auth, System $system)
{
$this->auth = $auth;
$this->system = $system;
View::share('sub_title', trans('dashboard.incidents.title'));
}
/**
* Shows the incident templates.
*
* @return \Illuminate\View\View
*/
public function showTemplates()
{
return View::make('dashboard.templates.index')
->withPageTitle(trans('dashboard.incidents.templates.title').' - '.trans('dashboard.dashboard'))
->withIncidentTemplates(IncidentTemplate::all());
}
/**
* Shows the add incident template view.
*
* @return \Illuminate\View\View
*/
public function showAddIncidentTemplate()
{
return View::make('dashboard.templates.add')
->withPageTitle(trans('dashboard.incidents.templates.add.title').' - '.trans('dashboard.dashboard'));
}
/**
* Shows the edit incident template view.
*
* @param \CachetHQ\Cachet\Models\IncidentTemplate $template
*
* @return \Illuminate\View\View
*/
public function showEditTemplateAction(IncidentTemplate $template)
{
return View::make('dashboard.templates.edit')
->withPageTitle(trans('dashboard.incidents.templates.edit.title').' - '.trans('dashboard.dashboard'))
->withTemplate($template);
}
/**
* Deletes an incident template.
*
* @param \CachetHQ\Cachet\Models\IncidentTemplate $template
*
* @return \Illuminate\Http\RedirectResponse
*/
public function deleteTemplateAction(IncidentTemplate $template)
{
$template->delete();
return cachet_redirect('dashboard.templates')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.incidents.templates.delete.success')));
}
/**
* Creates a new incident template.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function createIncidentTemplateAction()
{
try {
IncidentTemplate::create([
'name' => Binput::get('name'),
'template' => Binput::get('template', null, false, false),
]);
} catch (ValidationException $e) {
return cachet_redirect('dashboard.templates.create')
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.incidents.templates.add.failure')))
->withErrors($e->getMessageBag());
}
return cachet_redirect('dashboard.templates')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.incidents.templates.add.success')));
}
/**
* Edit an incident template.
*
* @param \CachetHQ\Cachet\Models\IncidentTemplate $template
*
* @return \Illuminate\Http\RedirectResponse
*/
public function editTemplateAction(IncidentTemplate $template)
{
try {
$template->update(Binput::get('template'));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.templates.edit', ['id' => $template->id])
->withUpdatedTemplate($template)
->withTemplateErrors($e->getMessageBag()->getErrors());
}
return cachet_redirect('dashboard.templates.edit', ['id' => $template->id])
->withUpdatedTemplate($template);
}
}

View File

@@ -0,0 +1,170 @@
<?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\Http\Controllers\Dashboard;
use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\CreateIncidentUpdateCommand;
use CachetHQ\Cachet\Bus\Commands\IncidentUpdate\UpdateIncidentUpdateCommand;
use CachetHQ\Cachet\Integrations\Contracts\System;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentUpdate;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\View;
/**
* This is the incident update controller.
*
* @author James Brooks <james@alt-three.com>
*/
class IncidentUpdateController extends Controller
{
/**
* Stores the sub-sidebar tree list.
*
* @var array
*/
protected $subMenu = [];
/**
* The guard instance.
*
* @var \Illuminate\Contracts\Auth\Guard
*/
protected $auth;
/**
* The system instance.
*
* @var \CachetHQ\Cachet\Integrations\Contracts\System
*/
protected $system;
/**
* Creates a new incident controller instance.
*
* @param \Illuminate\Contracts\Auth\Guard $auth
*
* @return void
*/
public function __construct(Guard $auth, System $system)
{
$this->auth = $auth;
$this->system = $system;
View::share('sub_title', trans('dashboard.incidents.title'));
}
/**
* Shows the incident update form.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\View\View
*/
public function showIncidentUpdates(Incident $incident)
{
return View::make('dashboard.incidents.updates.index')->withIncident($incident);
}
/**
* Shows the incident update form.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\View\View
*/
public function showCreateIncidentUpdateAction(Incident $incident)
{
return View::make('dashboard.incidents.updates.add')
->withIncident($incident)
->withNotificationsEnabled($this->system->canNotifySubscribers());
}
/**
* Creates a new incident update.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
*
* @return \Illuminate\Http\RedirectResponse
*/
public function createIncidentUpdateAction(Incident $incident)
{
try {
$incidentUpdate = execute(new CreateIncidentUpdateCommand(
$incident,
Binput::get('status'),
Binput::get('message'),
Binput::get('component_id'),
Binput::get('component_status'),
$this->auth->user()
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.incidents.updates.create', ['id' => $incident->id])
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.incidents.updates.add.failure')))
->withErrors($e->getMessageBag());
}
if ($incident->component) {
$incident->component->update(['status' => Binput::get('component_status')]);
}
return cachet_redirect('dashboard.incidents')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.incidents.updates.success')));
}
/**
* Shows the edit incident view.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
* @param \CachetHQ\Cachet\Models\IncidentUpdate $incidentUpdate
*
* @return \Illuminate\View\View
*/
public function showEditIncidentUpdateAction(Incident $incident, IncidentUpdate $incidentUpdate)
{
return View::make('dashboard.incidents.updates.edit')
->withIncident($incident)
->withUpdate($incidentUpdate)
->withNotificationsEnabled($this->system->canNotifySubscribers());
}
/**
* Edit an incident update.
*
* @param \CachetHQ\Cachet\Models\Incident $incident
* @param \CachetHQ\Cachet\Models\IncidentUpdate $incidentUpdate
*
* @return \Illuminate\Http\RedirectResponse
*/
public function editIncidentUpdateAction(Incident $incident, IncidentUpdate $incidentUpdate)
{
try {
$incidentUpdate = execute(new UpdateIncidentUpdateCommand(
$incidentUpdate,
Binput::get('status'),
Binput::get('message'),
$this->auth->user()
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.incidents.updates.edit', ['incident' => $incident->id, 'incident_update' => $incidentUpdate->id])
->withInput(Binput::all())
->withTitle(sprintf('%s %s', trans('dashboard.notifications.whoops'), trans('dashboard.incidents.updates.edit.failure')))
->withErrors($e->getMessageBag());
}
return cachet_redirect('dashboard.incidents.updates', ['incident' => $incident->id])
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.incidents.updates.edit.success')));
}
}

View File

@@ -71,7 +71,7 @@ class MetricController extends Controller
$metricData = Binput::get('metric');
try {
dispatch(new CreateMetricCommand(
execute(new CreateMetricCommand(
$metricData['name'],
$metricData['suffix'],
$metricData['description'],
@@ -115,7 +115,7 @@ class MetricController extends Controller
*/
public function deleteMetricAction(Metric $metric)
{
dispatch(new RemoveMetricCommand($metric));
execute(new RemoveMetricCommand($metric));
return cachet_redirect('dashboard.metrics')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.metrics.delete.success')));
@@ -146,7 +146,7 @@ class MetricController extends Controller
public function editMetricAction(Metric $metric)
{
try {
dispatch(new UpdateMetricCommand(
execute(new UpdateMetricCommand(
$metric,
Binput::get('name', null, false),
Binput::get('suffix', null, false),

View File

@@ -42,7 +42,7 @@ class ScheduleController extends Controller
*/
public function __construct()
{
View::share('sub_title', trans('dashboard.schedule.title'));
View::share('subTitle', trans('dashboard.schedule.title'));
}
/**
@@ -81,7 +81,7 @@ class ScheduleController extends Controller
public function addScheduleAction()
{
try {
dispatch(new CreateScheduleCommand(
execute(new CreateScheduleCommand(
Binput::get('name'),
Binput::get('message', null, false, false),
Binput::get('status', Schedule::UPCOMING),
@@ -127,7 +127,7 @@ class ScheduleController extends Controller
public function editScheduleAction(Schedule $schedule)
{
try {
$schedule = dispatch(new UpdateScheduleCommand(
$schedule = execute(new UpdateScheduleCommand(
$schedule,
Binput::get('name', null),
Binput::get('message', null),
@@ -156,7 +156,7 @@ class ScheduleController extends Controller
*/
public function deleteScheduleAction(Schedule $schedule)
{
dispatch(new DeleteScheduleCommand($schedule));
execute(new DeleteScheduleCommand($schedule));
return cachet_redirect('dashboard.schedule')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.schedule.delete.success')));

View File

@@ -18,11 +18,12 @@ use CachetHQ\Cachet\Notifications\System\SystemTestNotification;
use CachetHQ\Cachet\Settings\Repository;
use Exception;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Log\Writer;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\View;
@@ -115,8 +116,8 @@ class SettingsController extends Controller
];
View::share([
'sub_title' => trans('dashboard.settings.settings'),
'sub_menu' => $this->subMenu,
'subTitle' => trans('dashboard.settings.settings'),
'subMenu' => $this->subMenu,
]);
}
@@ -269,7 +270,7 @@ class SettingsController extends Controller
{
$this->subMenu['log']['active'] = true;
$log = app(Writer::class)->getMonolog();
$log = Log::getLogger();
$logContents = '';
@@ -318,7 +319,7 @@ class SettingsController extends Controller
{
$config = Binput::get('config');
dispatch(new UpdateConfigCommand($config));
execute(new UpdateConfigCommand($config));
return cachet_redirect('dashboard.settings.mail')
->withInput(Binput::all())
@@ -356,6 +357,14 @@ class SettingsController extends Controller
}
}
if (isset($parameters['stylesheet'])) {
if ($stylesheet = Binput::get('stylesheet', null, false, false)) {
$setting->set('stylesheet', $stylesheet);
} else {
$setting->delete('stylesheet');
}
}
if (Binput::hasFile('app_banner')) {
$this->handleUpdateBanner($setting);
}
@@ -366,6 +375,7 @@ class SettingsController extends Controller
'remove_banner',
'header',
'footer',
'stylesheet',
];
try {
@@ -384,6 +394,10 @@ class SettingsController extends Controller
Lang::setLocale(Binput::get('app_locale'));
}
if (Binput::has('always_authenticate')) {
Artisan::call('route:clear');
}
return Redirect::back()->withSuccess(trans('dashboard.settings.edit.success'));
}
@@ -397,6 +411,7 @@ class SettingsController extends Controller
protected function handleUpdateBanner(Repository $setting)
{
$file = Binput::file('app_banner');
$redirectUrl = $this->subMenu['theme']['url'];
// Image Validation.
// Image size in bytes.

View File

@@ -58,7 +58,7 @@ class SubscriberController extends Controller
$subscribers = preg_split("/\r\n|\n|\r/", Binput::get('email'));
foreach ($subscribers as $subscriber) {
dispatch(new SubscribeSubscriberCommand($subscriber, $verified));
execute(new SubscribeSubscriberCommand($subscriber, $verified));
}
} catch (ValidationException $e) {
return cachet_redirect('dashboard.subscribers.create')
@@ -82,7 +82,7 @@ class SubscriberController extends Controller
*/
public function deleteSubscriberAction(Subscriber $subscriber)
{
dispatch(new UnsubscribeSubscriberCommand($subscriber));
execute(new UnsubscribeSubscriberCommand($subscriber));
return cachet_redirect('dashboard.subscribers');
}

View File

@@ -80,7 +80,7 @@ class TeamController extends Controller
public function postAddUser()
{
try {
dispatch(new CreateUserCommand(
execute(new CreateUserCommand(
Binput::get('username'),
Binput::get('password'),
Binput::get('email'),
@@ -129,7 +129,7 @@ class TeamController extends Controller
public function postInviteUser()
{
try {
dispatch(new InviteUserCommand(
execute(new InviteUserCommand(
array_unique(array_filter((array) Binput::get('emails')))
));
} catch (ValidationException $e) {
@@ -152,7 +152,7 @@ class TeamController extends Controller
*/
public function deleteUser(User $user)
{
dispatch(new RemoveUserCommand($user));
execute(new RemoveUserCommand($user));
return cachet_redirect('dashboard.team')
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('dashboard.team.delete.success')));

View File

@@ -17,6 +17,7 @@ use GrahamCampbell\Markdown\Facades\Markdown;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Str;
use McCool\LaravelAutoPresenter\Facades\AutoPresenter;
/**
* This is the feed controller.
@@ -106,12 +107,17 @@ class FeedController extends Controller
*/
private function feedAddItem(Incident $incident, $isRss)
{
$incident = AutoPresenter::decorate($incident);
$this->feed->add(
$incident->name,
Config::get('setting.app_name'),
Str::canonicalize(cachet_route('incident', [$incident->id])),
$isRss ? $incident->occurred_at->toRssString() : $incident->occurred_at->toAtomString(),
$isRss ? $incident->message : Markdown::convertToHtml($incident->message)
$isRss ? $incident->getWrappedObject()->occurred_at->toRssString() : $incident->getWrappedObject()->occurred_at->toAtomString(),
$isRss ? $incident->message : Markdown::convertToHtml($incident->message),
null,
[],
$isRss ? $incident->human_status : null
);
}
}

View File

@@ -258,7 +258,7 @@ class SetupController extends Controller
$envData = array_pull($postData, 'env');
// Write the env to the .env file.
dispatch(new UpdateConfigCommand($envData));
execute(new UpdateConfigCommand($envData));
if (Request::ajax()) {
return Response::json(['status' => 1]);

View File

@@ -69,7 +69,7 @@ class SignupController extends Controller
}
try {
dispatch(new SignupUserCommand(
execute(new SignupUserCommand(
Binput::get('username'),
Binput::get('password'),
Binput::get('email'),
@@ -82,7 +82,7 @@ class SignupController extends Controller
->withErrors($e->getMessageBag());
}
dispatch(new ClaimInviteCommand($invite));
execute(new ClaimInviteCommand($invite));
return cachet_redirect('status-page')
->withSuccess(sprintf('<strong>%s</strong> %s', trans('dashboard.notifications.awesome'), trans('cachet.signup.success')));

View File

@@ -19,7 +19,6 @@ use CachetHQ\Cachet\Models\Metric;
use CachetHQ\Cachet\Models\Schedule;
use CachetHQ\Cachet\Repositories\Metric\MetricRepository;
use CachetHQ\Cachet\Services\Dates\DateFactory;
use Exception;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
@@ -45,32 +44,34 @@ class StatusPageController extends AbstractApiController
*/
public function showIndex()
{
$only_disrupted_days = Config::get('setting.only_disrupted_days');
$onlyDisruptedDays = Config::get('setting.only_disrupted_days');
$appIncidentDays = (int) Config::get('setting.app_incident_days', 1);
// Used for the database query
$startDate = Date::now();
$endDate = Date::now();
$startDate = Date::createFromFormat('Y-m-d', Binput::get('start_date', Date::now()->toDateString()));
$endDate = $startDate->copy()->subDays($appIncidentDays);
$canPageForward = false;
$canPageBackward = false;
$previousDate = null;
$nextDate = null;
if ($only_disrupted_days) {
if ($onlyDisruptedDays) {
// In this case, start_date GET parameter means the page
$page = Binput::get('start_date', 0);
if (!is_numeric($page)) {
$page = 0;
}
$page = (int) $page;
$page = (int) Binput::get('start_date', 0);
$allIncidentDays = Incident::where('visible', '>=', (int) !Auth::check())
->select('occurred_at')->distinct()->orderBy('occurred_at', 'desc')->get()->map(function (Incident $incident) {
return app(DateFactory::class)->make($incident->occurred_at)->toDateString();
})->unique()->values();
->select('occurred_at')
->whereBetween('occurred_at', [
$endDate->format('Y-m-d').' 00:00:00',
$startDate->format('Y-m-d').' 23:59:59',
])
->distinct()
->orderBy('occurred_at', 'desc')
->get()
->map(function (Incident $incident) {
return app(DateFactory::class)->make($incident->occurred_at)->toDateString();
})->unique()
->values();
$numIncidentDays = count($allIncidentDays);
$numPages = round($numIncidentDays / $appIncidentDays);
@@ -78,8 +79,8 @@ class StatusPageController extends AbstractApiController
$selectedDays = $allIncidentDays->slice($page * $appIncidentDays, $appIncidentDays)->all();
if (count($selectedDays) > 0) {
$startDate = Date::createFromFormat('Y-m-d', array_values(array_slice($selectedDays, -1))[0]);
$endDate = Date::createFromFormat('Y-m-d', array_values($selectedDays)[0]);
$startDate = Date::createFromFormat('Y-m-d', array_values($selectedDays)[0]);
$endDate = Date::createFromFormat('Y-m-d', array_values(array_slice($selectedDays, -1))[0]);
}
$canPageForward = $page > 0;
@@ -87,41 +88,22 @@ class StatusPageController extends AbstractApiController
$previousDate = $page + 1;
$nextDate = $page - 1;
} else {
$today = Date::now();
$date = Date::now();
// Check if we have another starting date
if (Binput::has('start_date')) {
try {
// If date provided is valid
$oldDate = Date::createFromFormat('Y-m-d', Binput::get('start_date'));
// If trying to get a future date fallback to today
if ($today->gt($oldDate)) {
$date = $oldDate;
}
} catch (Exception $e) {
// Fallback to today
}
}
$startDate = $date->copy()->subDays($appIncidentDays);
$endDate = $date->copy();
$canPageForward = (bool) $today->gt($date);
$canPageForward = (bool) $startDate->lt($date->sub('1 day'));
$canPageBackward = Incident::where('occurred_at', '<', $date->format('Y-m-d'))->count() > 0;
$previousDate = $date->copy()->subDays($appIncidentDays)->toDateString();
$nextDate = $date->copy()->addDays($appIncidentDays)->toDateString();
$previousDate = $startDate->copy()->subDays($appIncidentDays)->toDateString();
$nextDate = $startDate->copy()->addDays($appIncidentDays)->toDateString();
}
$allIncidents = Incident::where('visible', '>=', (int) !Auth::check())->whereBetween('occurred_at', [
$startDate->format('Y-m-d').' 00:00:00',
$endDate->format('Y-m-d').' 23:59:59',
$endDate->format('Y-m-d').' 00:00:00',
$startDate->format('Y-m-d').' 23:59:59',
])->orderBy('occurred_at', 'desc')->get()->groupBy(function (Incident $incident) {
return app(DateFactory::class)->make($incident->occurred_at)->toDateString();
});
if (!$only_disrupted_days) {
if (!$onlyDisruptedDays) {
$incidentDays = array_pad([], $appIncidentDays, null);
// Add in days that have no incidents

View File

@@ -80,7 +80,7 @@ class SubscribeController extends Controller
$verified = app(Repository::class)->get('setting.skip_subscriber_verification');
try {
$subscription = dispatch(new SubscribeSubscriberCommand($email, $verified));
$subscription = execute(new SubscribeSubscriberCommand($email, $verified));
} catch (ValidationException $e) {
return cachet_redirect('status-page')
->withInput(Binput::all())
@@ -116,7 +116,7 @@ class SubscribeController extends Controller
}
if (!$subscriber->is_verified) {
dispatch(new VerifySubscriberCommand($subscriber));
execute(new VerifySubscriberCommand($subscriber));
}
return cachet_redirect('status-page')
@@ -144,9 +144,9 @@ class SubscribeController extends Controller
}
if ($subscription) {
dispatch(new UnsubscribeSubscriptionCommand(Subscription::forSubscriber($subscriber->id)->firstOrFail()));
execute(new UnsubscribeSubscriptionCommand(Subscription::forSubscriber($subscriber->id)->firstOrFail()));
} else {
dispatch(new UnsubscribeSubscriberCommand($subscriber));
execute(new UnsubscribeSubscriberCommand($subscriber));
}
return cachet_redirect('status-page')
@@ -204,7 +204,7 @@ class SubscribeController extends Controller
}
try {
dispatch(new UpdateSubscriberSubscriptionCommand($subscriber, Binput::get('subscriptions')));
execute(new UpdateSubscriberSubscriptionCommand($subscriber, Binput::get('subscriptions')));
} catch (ValidationException $e) {
return cachet_redirect('subscribe.manage', $subscriber->verify_code)
->withInput(Binput::all())

View File

@@ -11,7 +11,19 @@
namespace CachetHQ\Cachet\Http;
use AltThree\Throttle\ThrottlingMiddleware;
use CachetHQ\Cachet\Http\Middleware\Admin;
use CachetHQ\Cachet\Http\Middleware\ApiAuthentication;
use CachetHQ\Cachet\Http\Middleware\Authenticate;
use CachetHQ\Cachet\Http\Middleware\Localize;
use CachetHQ\Cachet\Http\Middleware\ReadyForUse;
use CachetHQ\Cachet\Http\Middleware\RedirectIfAuthenticated;
use CachetHQ\Cachet\Http\Middleware\SetupAlreadyCompleted;
use CachetHQ\Cachet\Http\Middleware\SubscribersConfigured;
use CachetHQ\Cachet\Http\Middleware\TrustProxies;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
class Kernel extends HttpKernel
{
@@ -21,8 +33,8 @@ class Kernel extends HttpKernel
* @var array
*/
protected $middleware = [
'Fideloper\Proxy\TrustProxies',
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
TrustProxies::class,
CheckForMaintenanceMode::class,
];
/**
@@ -31,15 +43,15 @@ class Kernel extends HttpKernel
* @var array
*/
protected $routeMiddleware = [
'admin' => 'CachetHQ\Cachet\Http\Middleware\Admin',
'can' => 'Illuminate\Auth\Middleware\Authorize',
'auth' => 'CachetHQ\Cachet\Http\Middleware\Authenticate',
'auth.api' => 'CachetHQ\Cachet\Http\Middleware\ApiAuthentication',
'guest' => 'CachetHQ\Cachet\Http\Middleware\RedirectIfAuthenticated',
'localize' => 'CachetHQ\Cachet\Http\Middleware\Localize',
'ready' => 'CachetHQ\Cachet\Http\Middleware\ReadyForUse',
'setup' => 'CachetHQ\Cachet\Http\Middleware\SetupAlreadyCompleted',
'subscribers' => 'CachetHQ\Cachet\Http\Middleware\SubscribersConfigured',
'throttle' => 'AltThree\Throttle\ThrottlingMiddleware',
'admin' => Admin::class,
'can' => Authorize::class,
'auth' => Authenticate::class,
'auth.api' => ApiAuthentication::class,
'guest' => RedirectIfAuthenticated::class,
'localize' => Localize::class,
'ready' => ReadyForUse::class,
'setup' => SetupAlreadyCompleted::class,
'subscribers' => SubscribersConfigured::class,
'throttle' => ThrottlingMiddleware::class,
];
}

View File

@@ -0,0 +1,47 @@
<?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\Http\Middleware;
use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Request;
/**
* This is the trust proxies middleware class.
*
* @author James Brooks <james@alt-three.com>
*/
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers = Request::HEADER_X_FORWARDED_ALL;
/**
* Create new trust proxies instance.
*
* @return void
*/
public function __construct()
{
$this->proxies = empty(env('TRUSTED_PROXIES')) ? '*' : explode(',', trim(env('TRUSTED_PROXIES')));
}
}

View File

@@ -41,10 +41,6 @@ class ApiRoutes
'prefix' => 'api/v1',
], function (Registrar $router) {
$router->group(['middleware' => ['auth.api']], function (Registrar $router) {
$router->get('ping', 'GeneralController@ping');
$router->get('version', 'GeneralController@version');
$router->get('status', 'GeneralController@status');
$router->get('components', 'ComponentController@index');
$router->get('components/groups', 'ComponentGroupController@index');
$router->get('components/groups/{component_group}', 'ComponentGroupController@show');

View File

@@ -0,0 +1,50 @@
<?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\Http\Routes;
use Illuminate\Contracts\Routing\Registrar;
/**
* This is the api routes class.
*
* @author James Brooks <james@alt-three.com>
*/
class ApiSystemRoutes
{
/**
* Defines if these routes are for the browser.
*
* @var bool
*/
public static $browser = false;
/**
* Define the api routes for the system status, ping and version.
*
* @param \Illuminate\Contracts\Routing\Registrar $router
*
* @return void
*/
public function map(Registrar $router)
{
$router->group([
'namespace' => 'Api',
'prefix' => 'api/v1',
], function (Registrar $router) {
$router->group(['middleware' => ['auth.api']], function (Registrar $router) {
$router->get('ping', 'GeneralController@ping');
$router->get('version', 'GeneralController@version');
$router->get('status', 'GeneralController@status');
});
});
}
}

View File

@@ -58,29 +58,29 @@ class ComponentRoutes
$router->get('groups', [
'as' => 'get:dashboard.components.groups',
'uses' => 'ComponentController@showComponentGroups',
'uses' => 'ComponentGroupController@showComponentGroups',
]);
$router->get('groups/create', [
'as' => 'get:dashboard.components.groups.create',
'uses' => 'ComponentController@showAddComponentGroup',
'uses' => 'ComponentGroupController@showAddComponentGroup',
]);
$router->post('groups/create', [
'as' => 'post:dashboard.components.groups.create',
'uses' => 'ComponentController@postAddComponentGroup',
'uses' => 'ComponentGroupController@postAddComponentGroup',
]);
$router->get('groups/{component_group}', [
'as' => 'get:dashboard.components.groups.edit',
'uses' => 'ComponentController@showEditComponentGroup',
'uses' => 'ComponentGroupController@showEditComponentGroup',
]);
$router->post('groups/{component_group}', [
'as' => 'post:dashboard.components.groups.edit',
'uses' => 'ComponentController@updateComponentGroupAction',
'uses' => 'ComponentGroupController@updateComponentGroupAction',
]);
$router->delete('groups/{component_group}', [
'as' => 'delete:dashboard.components.groups.delete',
'uses' => 'ComponentController@deleteComponentGroupAction',
'uses' => 'ComponentGroupController@deleteComponentGroupAction',
]);
$router->get('{component}', [

View File

@@ -71,23 +71,23 @@ class IncidentRoutes
$router->get('{incident}/updates', [
'as' => 'get:dashboard.incidents.updates',
'uses' => 'IncidentController@showIncidentUpdates',
'uses' => 'IncidentUpdateController@showIncidentUpdates',
]);
$router->get('{incident}/updates/create', [
'as' => 'get:dashboard.incidents.updates.create',
'uses' => 'IncidentController@showCreateIncidentUpdateAction',
'uses' => 'IncidentUpdateController@showCreateIncidentUpdateAction',
]);
$router->post('{incident}/updates/create', [
'as' => 'post:dashboard.incidents.updates.create',
'uses' => 'IncidentController@createIncidentUpdateAction',
'uses' => 'IncidentUpdateController@createIncidentUpdateAction',
]);
$router->get('{incident}/updates/{incident_update}', [
'as' => 'get:dashboard.incidents.updates.edit',
'uses' => 'IncidentController@showEditIncidentUpdateAction',
'uses' => 'IncidentUpdateController@showEditIncidentUpdateAction',
]);
$router->post('{incident}/updates/{incident_update}', [
'as' => 'post:dashboard.incidents.updates.edit',
'uses' => 'IncidentController@editIncidentUpdateAction',
'uses' => 'IncidentUpdateController@editIncidentUpdateAction',
]);
});
}

View File

@@ -44,29 +44,29 @@ class TemplateRoutes
], function (Registrar $router) {
$router->get('/', [
'as' => 'get:dashboard.templates',
'uses' => 'IncidentController@showTemplates',
'uses' => 'IncidentTemplateController@showTemplates',
]);
$router->get('create', [
'as' => 'get:dashboard.templates.create',
'uses' => 'IncidentController@showAddIncidentTemplate',
'uses' => 'IncidentTemplateController@showAddIncidentTemplate',
]);
$router->post('create', [
'as' => 'post:dashboard.templates.create',
'uses' => 'IncidentController@createIncidentTemplateAction',
'uses' => 'IncidentTemplateController@createIncidentTemplateAction',
]);
$router->get('{incident_template}', [
'as' => 'get:dashboard.templates.edit',
'uses' => 'IncidentController@showEditTemplateAction',
'uses' => 'IncidentTemplateController@showEditTemplateAction',
]);
$router->post('{incident_template}', [
'as' => 'post:dashboard.templates.edit',
'uses' => 'IncidentController@editTemplateAction',
'uses' => 'IncidentTemplateController@editTemplateAction',
]);
$router->delete('{incident_template}', [
'as' => 'delete:dashboard.templates.delete',
'uses' => 'IncidentController@deleteTemplateAction',
'uses' => 'IncidentTemplateController@deleteTemplateAction',
]);
});
}

View File

@@ -64,7 +64,8 @@ class System implements SystemContract
$totalComponents = Component::enabled()->authenticated($includePrivate)->count();
$majorOutages = Component::enabled()->authenticated($includePrivate)->status(4)->count();
$isMajorOutage = $totalComponents ? ($majorOutages / $totalComponents) >= 0.5 : false;
$majorOutageRate = (int) $this->config->get('setting.major_outage_rate', '50');
$isMajorOutage = $totalComponents ? ($majorOutages / $totalComponents) * 100 >= $majorOutageRate : false;
// Default data
$status = [

View File

@@ -24,20 +24,6 @@ class Action extends Model
{
use ValidatingTrait;
/**
* A list of methods protected from mass assignment.
*
* @var string[]
*/
protected $guarded = ['_token', '_method'];
/**
* The relations to eager load on every query.
*
* @var string[]
*/
protected $with = ['user'];
/**
* The attributes that should be casted to native types.
*
@@ -52,6 +38,13 @@ class Action extends Model
'description' => 'string',
];
/**
* A list of methods protected from mass assignment.
*
* @var string[]
*/
protected $guarded = ['_token', '_method'];
/**
* The validation rules.
*
@@ -66,6 +59,13 @@ class Action extends Model
'description' => 'required|string',
];
/**
* The relations to eager load on every query.
*
* @var string[]
*/
protected $with = ['user'];
/**
* Get the user relation.
*

View File

@@ -12,6 +12,7 @@
namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\HasTags;
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use CachetHQ\Cachet\Presenters\ComponentPresenter;
@@ -22,7 +23,7 @@ use McCool\LaravelAutoPresenter\HasPresenter;
class Component extends Model implements HasPresenter
{
use SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
use HasTags, SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
/**
* List of attributes that have default values.
@@ -64,7 +65,6 @@ class Component extends Model implements HasPresenter
'name',
'description',
'status',
'tags',
'link',
'order',
'group_id',
@@ -135,7 +135,7 @@ class Component extends Model implements HasPresenter
}
/**
* Get all of the meta relation.
* Get the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
@@ -144,16 +144,6 @@ class Component extends Model implements HasPresenter
return $this->morphMany(Meta::class, 'meta');
}
/**
* Get the tags relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
return $this->belongsToMany(Tag::class);
}
/**
* Finds all components by status.
*
@@ -250,20 +240,6 @@ class Component extends Model implements HasPresenter
->groupBy('group_id');
}
/**
* Returns all of the tags on this component.
*
* @return string
*/
public function getTagsListAttribute()
{
$tags = $this->tags->map(function ($tag) {
return $tag->name;
});
return implode(', ', $tags->toArray());
}
/**
* Get the presenter class.
*

View File

@@ -12,6 +12,7 @@
namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\HasTags;
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use CachetHQ\Cachet\Presenters\IncidentPresenter;
@@ -29,7 +30,7 @@ use McCool\LaravelAutoPresenter\HasPresenter;
*/
class Incident extends Model implements HasPresenter
{
use SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
use HasTags, SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
/**
* Status for incident being investigated.
@@ -68,16 +69,30 @@ class Incident extends Model implements HasPresenter
'is_resolved',
];
/**
* The model's attributes.
*
* @var string[]
*/
protected $attributes = [
'stickied' => false,
'notifications' => false,
];
/**
* The attributes that should be casted to native types.
*
* @var string[]
*/
protected $casts = [
'visible' => 'int',
'stickied' => 'bool',
'occurred_at' => 'datetime',
'deleted_at' => 'date',
'component_id' => 'int',
'status' => 'int',
'user_id' => 'int',
'visible' => 'int',
'stickied' => 'bool',
'notifications' => 'bool',
'occurred_at' => 'datetime',
'deleted_at' => 'date',
];
/**
@@ -86,11 +101,13 @@ class Incident extends Model implements HasPresenter
* @var string[]
*/
protected $fillable = [
'user_id',
'component_id',
'name',
'status',
'visible',
'stickied',
'notifications',
'message',
'occurred_at',
'created_at',
@@ -103,12 +120,14 @@ class Incident extends Model implements HasPresenter
* @var string[]
*/
public $rules = [
'component_id' => 'nullable|int',
'name' => 'required|string',
'status' => 'required|int',
'visible' => 'required|bool',
'stickied' => 'required|bool',
'message' => 'required|string',
'user_id' => 'nullable|int',
'component_id' => 'nullable|int',
'name' => 'required|string',
'status' => 'required|int',
'visible' => 'required|bool',
'stickied' => 'required|bool',
'notifications' => 'nullable|bool',
'message' => 'required|string',
];
/**
@@ -118,6 +137,7 @@ class Incident extends Model implements HasPresenter
*/
protected $searchable = [
'id',
'user_id',
'component_id',
'name',
'status',
@@ -132,6 +152,7 @@ class Incident extends Model implements HasPresenter
*/
protected $sortable = [
'id',
'user_id',
'name',
'status',
'visible',
@@ -180,6 +201,16 @@ class Incident extends Model implements HasPresenter
return $this->hasMany(IncidentUpdate::class)->orderBy('created_at', 'desc');
}
/**
* Get the user relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Finds all visible incidents.
*

View File

@@ -83,6 +83,16 @@ class IncidentUpdate extends Model implements HasPresenter
return $this->belongsTo(Incident::class);
}
/**
* Get the user relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Get the presenter class.
*

View File

@@ -166,7 +166,7 @@ class Metric extends Model implements HasPresenter
}
/**
* Get all of the meta relation.
* Get the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
@@ -194,7 +194,7 @@ class Metric extends Model implements HasPresenter
*/
public function scopeDisplayable(Builder $query)
{
return $query->where('display_chart', '=', true)->where('visible', '!=', self::VISIBLE_HIDDEN);
return $query->where('display_chart', '=', true)->where('visible', '<>', self::VISIBLE_HIDDEN);
}
/**

View File

@@ -13,9 +13,18 @@ namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Presenters\MetricPointPresenter;
use Carbon\Carbon;
use DateTime;
use Illuminate\Database\Eloquent\Model;
use McCool\LaravelAutoPresenter\HasPresenter;
/**
* This is the metric point model class.
*
* @author James Brooks <james@alt-three.com>
* @author Joseph Cohen <joe@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class MetricPoint extends Model implements HasPresenter
{
use ValidatingTrait;
@@ -88,6 +97,29 @@ class MetricPoint extends Model implements HasPresenter
return round((float) $value, $this->metric->places);
}
/**
* Round the created at value into intervals of 30 seconds.
*
* @param string $createdAt
*
* @return string|void
*/
public function setCreatedAtAttribute($createdAt)
{
if (!$createdAt) {
return;
}
if (!$createdAt instanceof DateTime) {
$createdAt = Carbon::parse($createdAt);
}
$timestamp = $createdAt->format('U');
$timestamp = 30 * round($timestamp / 30);
return Carbon::createFromFormat('U', $timestamp)->toDateTimeString();
}
/**
* Get the presenter class.
*

View File

@@ -18,6 +18,7 @@ use CachetHQ\Cachet\Presenters\SchedulePresenter;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use McCool\LaravelAutoPresenter\HasPresenter;
/**
@@ -27,7 +28,7 @@ use McCool\LaravelAutoPresenter\HasPresenter;
*/
class Schedule extends Model implements HasPresenter
{
use SearchableTrait, SortableTrait, ValidatingTrait;
use SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
/**
* The upcoming status.
@@ -143,7 +144,7 @@ class Schedule extends Model implements HasPresenter
}
/**
* Get all of the meta relation.
* Get the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
@@ -161,7 +162,7 @@ class Schedule extends Model implements HasPresenter
*/
public function scopeInProgress(Builder $query)
{
return $query->where('scheduled_at', '<=', Carbon::now())->where('status', '!=', self::COMPLETE)->where(function ($query) {
return $query->where('scheduled_at', '<=', Carbon::now())->where('status', '<>', self::COMPLETE)->where(function ($query) {
$query->whereNull('completed_at')->orWhere('completed_at', '>', Carbon::now());
});
}

View File

@@ -15,6 +15,13 @@ use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
/**
* List of attributes that have default values.
*
* @var string[]
*/
protected $attributes = ['value' => ''];
/**
* The attributes that should be casted to native types.
*
@@ -31,11 +38,4 @@ class Setting extends Model
* @var string[]
*/
protected $fillable = ['name', 'value'];
/**
* List of attributes that have default values.
*
* @var string[]
*/
protected $attributes = ['value' => ''];
}

View File

@@ -91,7 +91,7 @@ class Subscriber extends Model implements HasPresenter
}
/**
* Get all of the meta relation.
* Get the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/

79
app/Models/Taggable.php Normal file
View File

@@ -0,0 +1,79 @@
<?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\Model;
/**
* This is the taggable model class.
*
* @author James Brooks <james@alt-three.com>
*/
class Taggable extends Model
{
use ValidatingTrait;
/**
* The attributes that should be casted to native types.
*
* @var string[]
*/
protected $casts = [
'id' => 'int',
'tag_id' => 'int',
'taggable_id' => 'int',
'taggable_type' => 'string',
];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'tag_id',
'taggable_id',
'taggable_type',
];
/**
* The validation rules.
*
* @var string[]
*/
public $rules = [
'tag_id' => 'required|int',
'taggable_id' => 'required|int',
'taggable_type' => 'required|string',
];
/**
* Get the tag relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function tag()
{
return $this->belongsTo(Tag::class);
}
/**
* Get the taggable relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function taggable()
{
return $this->morphTo();
}
}

View File

@@ -0,0 +1,87 @@
<?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\Traits;
use CachetHQ\Cachet\Models\Tag;
use Illuminate\Database\Eloquent\Builder;
/**
* This is the has tags trait.
*
* @author James Brooks <james@alt-three.com>
*/
trait HasTags
{
/**
* Get the tags relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable');
}
/**
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array|\ArrayAccess $tags
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeWithAllTags(Builder $query, $tags)
{
$tags = static::convertToTags($tags);
$tags->each(function ($tag) use ($query) {
$query->whereHas('tags', function (Builder $query) use ($tag) {
return $query->where('id', $tag ? $tag->id : 0);
});
});
return $query;
}
/**
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array|\ArrayAccess $tags
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeWithAnyTags(Builder $query, $tags)
{
$tags = static::convertToTags($tags);
return $query->whereHas('tags', function (Builder $query) use ($tags) {
$tagIds = $tags->pluck('id')->toArray();
$query->whereIn('taggables.tag_id', $tagIds);
});
}
/**
* Convert a list of tags into a collection of \CachetHQ\Cachet\Models\Tag.
*
* @param array|\ArrayAccess $values
*
* @return \Illuminate\Support\Collection
*/
protected static function convertToTags($values)
{
return collect($values)->map(function ($value) {
if ($value instanceof Tag) {
return $value;
}
return Tag::where('slug', '=', $value)->first();
});
}
}

View File

@@ -88,7 +88,8 @@ class ComponentStatusChangedNotification extends Notification
->greeting(trans('notifications.component.status_update.mail.subject'))
->line($content)
->action(trans('notifications.component.status_update.mail.action'), cachet_route('status-page'))
->line(trans('cachet.subscriber.unsubscribe', ['link' => cachet_route('subscribe.unsubscribe', $notifiable->verify_code)]));
->line(trans('cachet.subscriber.unsubscribe', ['link' => cachet_route('subscribe.unsubscribe', $notifiable->verify_code)]))
->line(trans('cachet.subscriber.manage.manage_at_link', ['link' => cachet_route('subscribe.manage', $notifiable->verify_code)]));
}
/**

View File

@@ -75,11 +75,16 @@ class NewIncidentNotification extends Notification
return (new MailMessage())
->subject(trans('notifications.incident.new.mail.subject'))
->greeting(trans('notifications.incident.new.mail.greeting', ['app_name' => Config::get('setting.app_name')]))
->line($content)
->action(trans('notifications.incident.new.mail.action'), cachet_route('incident', [$this->incident]))
->markdown($this->incident->message)
->line(trans('cachet.subscriber.unsubscribe', ['link' => cachet_route('subscribe.unsubscribe', $notifiable->verify_code)]));
->markdown('notifications.incident.new', [
'incident' => $this->incident,
'content' => $content,
'actionText' => trans('notifications.incident.new.mail.action'),
'actionUrl' => cachet_route('incident', [$this->incident]),
'unsubscribeText' => trans('cachet.subscriber.unsubscribe'),
'unsubscribeUrl' => cachet_route('subscribe.unsubscribe', $notifiable->verify_code),
'manageSubscriptionText' => trans('cachet.subscriber.manage_subscription'),
'manageSubscriptionUrl' => cachet_route('subscribe.manage', $notifiable->verify_code),
]);
}
/**

View File

@@ -82,8 +82,9 @@ class IncidentUpdatedNotification extends Notification
]))
->line($content)
->action(trans('notifications.incident.update.mail.action'), cachet_route('incident', [$this->update->incident]))
->markdown($this->update->message)
->line(trans('cachet.subscriber.unsubscribe', ['link' => cachet_route('subscribe.unsubscribe', $notifiable->verify_code)]));
->line($this->update->raw_message)
->line(trans('cachet.subscriber.unsubscribe', ['link' => cachet_route('subscribe.unsubscribe', $notifiable->verify_code)]))
->line(trans('cachet.subscriber.manage.manage_at_link', ['link' => cachet_route('subscribe.manage', $notifiable->verify_code)]));
}
/**

Some files were not shown because too many files have changed in this diff Show More