Merge branch '2.4' into feature/remote-user-authenticate

This commit is contained in:
James Brooks
2019-06-23 09:04:25 +01:00
committed by GitHub
136 changed files with 5378 additions and 2760 deletions

View File

@@ -140,7 +140,7 @@ final class CreateIncidentCommand
*
* @return void
*/
public function __construct($name, $status, $message, $visible, $component_id, $component_status, $notify, $stickied, $occurred_at, $template, array $template_vars = [], $meta = [])
public function __construct($name, $status, $message, $visible, $component_id, $component_status, $notify, $stickied, $occurred_at, $template, array $template_vars = [], array $meta = [])
{
$this->name = $name;
$this->status = $status;

View File

@@ -106,6 +106,13 @@ final class UpdateIncidentCommand
*/
public $template_vars;
/**
* Meta key/value pairs.
*
* @var array
*/
public $meta = [];
/**
* The validation rules.
*
@@ -122,6 +129,7 @@ final class UpdateIncidentCommand
'stickied' => 'nullable|bool',
'occurred_at' => 'nullable|string',
'template' => 'nullable|string',
'meta' => 'nullable|array',
];
/**
@@ -139,10 +147,11 @@ final class UpdateIncidentCommand
* @param string|null $occurred_at
* @param string|null $template
* @param array $template_vars
* @param array $meta
*
* @return void
*/
public function __construct(Incident $incident, $name, $status, $message, $visible, $component_id, $component_status, $notify, $stickied, $occurred_at, $template, array $template_vars = [])
public function __construct(Incident $incident, $name, $status, $message, $visible, $component_id, $component_status, $notify, $stickied, $occurred_at, $template, array $template_vars = [], array $meta = [])
{
$this->incident = $incident;
$this->name = $name;
@@ -156,5 +165,6 @@ final class UpdateIncidentCommand
$this->occurred_at = $occurred_at;
$this->template = $template;
$this->template_vars = $template_vars;
$this->meta = $meta;
}
}

View File

@@ -60,6 +60,13 @@ final class CreateScheduleCommand
*/
public $components;
/**
* Whether to notify that the incident was reported.
*
* @var bool
*/
public $notify;
/**
* The validation rules.
*
@@ -72,6 +79,7 @@ final class CreateScheduleCommand
'scheduled_at' => 'required|string',
'completed_at' => 'nullable|string',
'components' => 'nullable|array',
'notify' => 'nullable|bool',
];
/**
@@ -83,10 +91,11 @@ final class CreateScheduleCommand
* @param string $scheduled_at
* @param string $completed_at
* @param array $components
* @param bool $notify
*
* @return void
*/
public function __construct($name, $message, $status, $scheduled_at, $completed_at, array $components = [])
public function __construct($name, $message, $status, $scheduled_at, $completed_at, $components, $notify)
{
$this->name = $name;
$this->message = $message;
@@ -94,5 +103,6 @@ final class CreateScheduleCommand
$this->scheduled_at = $scheduled_at;
$this->completed_at = $completed_at;
$this->components = $components;
$this->notify = $notify;
}
}

View File

@@ -36,18 +36,27 @@ final class ScheduleWasCreatedEvent implements ActionInterface, ScheduleEventInt
*/
public $schedule;
/**
* Whether to notify that the incident was reported.
*
* @var bool
*/
public $notify;
/**
* Create a new schedule was created event instance.
*
* @param \CachetHQ\Cachet\Models\User $user
* @param \CachetHQ\Cachet\Models\Schedule $schedule
* @param bool notify
*
* @return void
*/
public function __construct(User $user, Schedule $schedule)
public function __construct(User $user, Schedule $schedule, $notify = false)
{
$this->user = $user;
$this->schedule = $schedule;
$this->notify = $notify;
}
/**

View File

@@ -15,6 +15,7 @@ use CachetHQ\Cachet\Bus\Commands\Component\UpdateComponentCommand;
use CachetHQ\Cachet\Bus\Commands\Incident\CreateIncidentCommand;
use CachetHQ\Cachet\Bus\Events\Incident\IncidentWasCreatedEvent;
use CachetHQ\Cachet\Bus\Exceptions\Incident\InvalidIncidentTimestampException;
use CachetHQ\Cachet\Bus\Handlers\Traits\StoresMeta;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate;
@@ -32,6 +33,8 @@ use Twig\Loader\ArrayLoader as Twig_Loader_Array;
*/
class CreateIncidentCommandHandler
{
use StoresMeta;
/**
* The authentication guard instance.
*
@@ -104,14 +107,7 @@ class CreateIncidentCommandHandler
// Store any meta?
if ($meta = $command->meta) {
foreach ($meta as $key => $value) {
Meta::create([
'key' => $key,
'value' => $value,
'meta_type' => 'incidents',
'meta_id' => $incident->id,
]);
}
$this->storeMeta($command->meta, 'incidents', $incident->id);
}
// Update the component.

View File

@@ -15,6 +15,7 @@ use CachetHQ\Cachet\Bus\Commands\Component\UpdateComponentCommand;
use CachetHQ\Cachet\Bus\Commands\Incident\UpdateIncidentCommand;
use CachetHQ\Cachet\Bus\Events\Incident\IncidentWasUpdatedEvent;
use CachetHQ\Cachet\Bus\Exceptions\Incident\InvalidIncidentTimestampException;
use CachetHQ\Cachet\Bus\Handlers\Traits\StoresMeta;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\Incident;
use CachetHQ\Cachet\Models\IncidentTemplate;
@@ -30,6 +31,8 @@ use Twig\Loader\ArrayLoader as Twig_Loader_Array;
*/
class UpdateIncidentCommandHandler
{
use StoresMeta;
/**
* The authentication guard instance.
*
@@ -86,6 +89,11 @@ class UpdateIncidentCommandHandler
// Rather than making lots of updates, just fill and save.
$incident->save();
// Store any meta?
if ($meta = $command->meta) {
$this->storeMeta($command->meta, 'incidents', $incident->id);
}
// Update the component.
if ($component = Component::find($command->component_id)) {
execute(new UpdateComponentCommand(

View File

@@ -66,8 +66,7 @@ class CreateScheduleCommandHandler
{
try {
$schedule = Schedule::create($this->filter($command));
event(new ScheduleWasCreatedEvent($this->auth->user(), $schedule));
event(new ScheduleWasCreatedEvent($this->auth->user(), $schedule, (bool) $command->notify));
} catch (InvalidArgumentException $e) {
throw new ValidationException(new MessageBag([$e->getMessage()]));
}
@@ -96,6 +95,7 @@ class CreateScheduleCommandHandler
'status' => $command->status,
'scheduled_at' => $scheduledAt,
'completed_at' => $completedAt,
'notify' => $command->notify,
];
$availableParams = array_filter($params, function ($val) {

View File

@@ -51,6 +51,9 @@ class SendScheduleEmailNotificationHandler
public function handle(ScheduleEventInterface $event)
{
$schedule = $event->schedule;
if (!$event->notify) {
return false;
}
// First notify all global subscribers.
$globalSubscribers = $this->subscriber->isVerified()->isGlobal()->get()->each(function ($subscriber) use ($schedule) {

View File

@@ -0,0 +1,81 @@
<?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\Traits;
use CachetHQ\Cachet\Models\Meta;
trait StoresMeta
{
/**
* Stores all Meta values of a model.
*
* @param array $metaData
* @param string $metaType
* @param string|int $metaId
* @param string $metaModel
*
* @return void
*/
public function storeMeta($metaData, $metaType, $metaId, $metaModel = Meta::class)
{
// Validation required instead of type hinting because it could be passed as false or NULL
if (!is_array($metaData)) {
return;
}
foreach ($metaData as $key => $value) {
$modelInstance = call_user_func(
[$metaModel, 'firstOrNew'],
[
'key' => $key,
'meta_type' => $metaType,
'meta_id' => $metaId,
]
);
$value = $this->removeEmptyValues($value);
if (!empty($value)) {
$modelInstance->setAttribute('value', $value);
$modelInstance->save();
continue;
}
// The value is empty, remove the row
if ($modelInstance->exists) {
$modelInstance->delete();
}
}
}
/**
* Determine if a Value is empty.
*
* @param mixed $values
*
* @return array|mixed
*/
protected function removeEmptyValues($values)
{
if (!is_array($values)) {
return empty($values) ? null : $values;
}
foreach ($values as $key => $value) {
if (!empty($value)) {
continue;
}
unset($values[$key]);
}
return $values;
}
}

View File

@@ -53,7 +53,7 @@ class ComponentsComposer
public function compose(View $view)
{
$componentGroups = $this->getVisibleGroupedComponents();
$ungroupedComponents = Component::ungrouped()->get();
$ungroupedComponents = Component::ungrouped()->orderBy('status', 'desc')->get();
$view->withComponentGroups($componentGroups)
->withUngroupedComponents($ungroupedComponents);

View File

@@ -26,6 +26,69 @@ use Illuminate\Contracts\View\View;
*/
class DashboardComposer
{
/**
* The component count.
*
* @var int
*/
protected $componentCount;
/**
* The incident count.
*
* @var int
*/
protected $incidentCount;
/**
* The incident template count.
*
* @var int
*/
protected $incidentTemplateCount;
/**
* The schedule count.
*
* @var int
*/
protected $scheduleCount;
/**
* The subscriber count.
*
* @var int
*/
protected $subscriberCount;
/**
* Create a new dashboard composer instance.
*
* @return void
*/
public function __construct()
{
if (is_null($this->componentCount)) {
$this->componentCount = Component::count();
}
if (is_null($this->incidentCount)) {
$this->incidentCount = Incident::count();
}
if (is_null($this->incidentTemplateCount)) {
$this->incidentTemplateCount = IncidentTemplate::count();
}
if (is_null($this->scheduleCount)) {
$this->scheduleCount = Schedule::count();
}
if (is_null($this->subscriberCount)) {
$this->subscriberCount = Subscriber::isVerified()->count();
}
}
/**
* Bind data to the view.
*
@@ -35,11 +98,11 @@ class DashboardComposer
*/
public function compose(View $view)
{
$view->withComponentCount(Component::count());
$view->withIncidentCount(Incident::count());
$view->withIncidentTemplateCount(IncidentTemplate::count());
$view->withScheduleCount(Schedule::count());
$view->withSubscriberCount(Subscriber::isVerified()->count());
$view->withComponentCount($this->componentCount);
$view->withIncidentCount($this->incidentCount);
$view->withIncidentTemplateCount($this->incidentTemplateCount);
$view->withScheduleCount($this->scheduleCount);
$view->withSubscriberCount($this->subscriberCount);
$view->withIsWriteable(is_writable(app()->bootstrapPath().'/cachet'));
}
}

View File

@@ -31,7 +31,7 @@ class ScheduledComposer
*/
public function compose(View $view)
{
$scheduledMaintenance = Schedule::futureSchedules()->orderBy('scheduled_at')->get();
$scheduledMaintenance = Schedule::uncompleted()->orderBy('scheduled_at')->get();
$view->withScheduledMaintenance($scheduledMaintenance);
}

View File

@@ -13,6 +13,7 @@ namespace CachetHQ\Cachet\Composers;
use CachetHQ\Cachet\Integrations\Contracts\System;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Arr;
/**
* This is the status composer.
@@ -52,7 +53,7 @@ class StatusComposer
{
$status = $this->system->getStatus();
$view->withSystemStatus(array_get($status, 'system_status'));
$view->withSystemMessage(array_get($status, 'system_message'));
$view->withSystemStatus(Arr::get($status, 'system_status'));
$view->withSystemMessage(Arr::get($status, 'system_message'));
}
}

View File

@@ -15,6 +15,7 @@ use DateTime;
use DateTimeZone;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Arr;
/**
* This is the timezone locale composer class.
@@ -58,7 +59,7 @@ class TimezoneLocaleComposer
$langs = array_map(function ($lang) use ($enabledLangs) {
$locale = basename($lang);
return [$locale => array_get($enabledLangs, $locale, [
return [$locale => Arr::get($enabledLangs, $locale, [
'name' => $locale,
'subset' => null,
])];

View File

@@ -11,9 +11,11 @@
namespace CachetHQ\Cachet\Console\Commands;
use CachetHQ\Cachet\Models\User;
use Dotenv\Dotenv;
use Dotenv\Exception\InvalidPathException;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Contracts\Events\Dispatcher;
/**
@@ -70,6 +72,7 @@ class InstallCommand extends Command
$this->configureDrivers();
$this->configureMail();
$this->configureCachet();
$this->configureUser();
}
$this->line('Installing Cachet...');
@@ -313,9 +316,11 @@ class InstallCommand extends Command
/**
* Configure Cachet.
*
* @param array $config
*
* @return void
*/
protected function configureCachet()
protected function configureCachet(array $config = [])
{
$config = [];
if ($this->confirm('Do you wish to use Cachet Beacon?')) {
@@ -332,6 +337,33 @@ class InstallCommand extends Command
}
}
/**
* Configure the first user.
*
* @return void
*/
protected function configureUser()
{
if (!$this->confirm('Do you want to create an admin user?')) {
return;
}
// We need to refresh the config to get access to the newly connected database.
$this->getFreshConfiguration();
// Now we need to install the application.
// $this->call('cachet:install');
$user = [
'username' => $this->ask('Please enter your username'),
'email' => $this->ask('Please enter your email'),
'password' => $this->secret('Please enter your password'),
'level' => User::LEVEL_ADMIN,
];
User::create($user);
}
/**
* Configure the redis connection.
*
@@ -372,6 +404,17 @@ class InstallCommand extends Command
$this->table(['Setting', 'Value'], $configRows);
}
/**
* Boot a fresh copy of the application configuration.
*
* @return void
*/
protected function getFreshConfiguration()
{
$app = require $this->laravel->bootstrapPath().'/app.php';
$app->make(Kernel::class)->bootstrap();
}
/**
* Writes to the .env file with given parameters.
*

View File

@@ -13,6 +13,7 @@ namespace CachetHQ\Cachet\Foundation\Exceptions\Filters;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class ApiFilter
{
@@ -31,7 +32,7 @@ class ApiFilter
{
if ($request->is('api*')) {
foreach ($displayers as $index => $displayer) {
if (!str_contains($displayer->contentType(), 'application/')) {
if (!Str::contains($displayer->contentType(), 'application/')) {
unset($displayers[$index]);
}
}

View File

@@ -57,6 +57,6 @@ class ComposerServiceProvider extends ServiceProvider
*/
public function register()
{
//
$this->app->singleton(DashboardComposer::class);
}
}

View File

@@ -11,11 +11,11 @@
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\RemoteUserAuthenticate;
use CachetHQ\Cachet\Http\Middleware\Timezone;
use CachetHQ\Cachet\Http\Middleware\VerifyCsrfToken;
use CachetHQ\Cachet\Http\Routes\ApiSystemRoutes;
use CachetHQ\Cachet\Http\Routes\AuthRoutes;
use CachetHQ\Cachet\Http\Routes\Setup\ApiRoutes as ApiSetupRoutes;
@@ -23,7 +23,6 @@ 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;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Routing\Router;
@@ -173,7 +172,6 @@ class RouteServiceProvider extends ServiceProvider
protected function mapOtherwise(Router $router, $routes, $applyAlwaysAuthenticate)
{
$middleware = [
HandleCors::class,
SubstituteBindings::class,
Acceptable::class,
Timezone::class,

View File

@@ -73,7 +73,8 @@ class ScheduleController extends AbstractApiController
Binput::get('status'),
Binput::get('scheduled_at'),
Binput::get('completed_at'),
Binput::get('components', [])
Binput::get('components', []),
Binput::get('notify', false)
));
} catch (QueryException $e) {
throw new BadRequestHttpException();

View File

@@ -17,6 +17,7 @@ use CachetHQ\Cachet\Bus\Events\User\UserLoggedOutEvent;
use CachetHQ\Cachet\Bus\Events\User\UserPassedTwoAuthEvent;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Routing\Controller;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Session;
@@ -47,9 +48,9 @@ class AuthController extends Controller
// Login with username or email.
$loginKey = filter_var($loginData['username'], FILTER_VALIDATE_EMAIL) ? 'email' : 'username';
$loginData[$loginKey] = array_pull($loginData, 'username');
$loginData[$loginKey] = Arr::pull($loginData, 'username');
$rememberUser = array_pull($loginData, 'remember_me') === '1';
$rememberUser = Arr::pull($loginData, 'remember_me') === '1';
// Validate login credentials.
if (Auth::validate($loginData)) {

View File

@@ -21,6 +21,7 @@ use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Routing\Controller;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\View;
@@ -73,7 +74,7 @@ class ComponentController extends Controller
*/
public function showComponents()
{
$components = Component::orderBy('order')->orderBy('created_at')->get();
$components = Component::with('group')->orderBy('order')->orderBy('created_at')->get();
$this->subMenu['components']['active'] = true;
@@ -112,7 +113,7 @@ class ComponentController extends Controller
public function updateComponentAction(Component $component)
{
$componentData = Binput::get('component');
$tags = array_pull($componentData, 'tags');
$tags = Arr::pull($componentData, 'tags');
try {
$component = execute(new UpdateComponentCommand(
@@ -169,7 +170,7 @@ class ComponentController extends Controller
public function createComponentAction()
{
$componentData = Binput::get('component');
$tags = array_pull($componentData, 'tags');
$tags = Arr::pull($componentData, 'tags');
try {
$component = execute(new CreateComponentCommand(

View File

@@ -106,7 +106,9 @@ class DashboardController extends Controller
$entries = null;
if ($feed = $this->feed->latest()) {
$entries = array_slice($feed->channel->item, 0, 5);
if (is_object($feed)) {
$entries = array_slice($feed->channel->item, 0, 5);
}
}
return View::make('dashboard.index')

View File

@@ -75,7 +75,7 @@ class IncidentController extends Controller
*/
public function showIncidents()
{
$incidents = Incident::orderBy('created_at', 'desc')->get();
$incidents = Incident::with('user')->orderBy('created_at', 'desc')->get();
return View::make('dashboard.incidents.index')
->withPageTitle(trans('dashboard.incidents.incidents').' - '.trans('dashboard.dashboard'))
@@ -128,7 +128,8 @@ class IncidentController extends Controller
Binput::get('stickied', false),
Binput::get('occurred_at'),
null,
[]
[],
['seo' => Binput::get('seo', [])]
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.incidents.create')
@@ -258,7 +259,8 @@ class IncidentController extends Controller
Binput::get('stickied', false),
Binput::get('occurred_at'),
null,
[]
[],
['seo' => Binput::get('seo', [])]
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.incidents.edit', ['id' => $incident->id])

View File

@@ -15,6 +15,7 @@ use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Bus\Commands\Schedule\CreateScheduleCommand;
use CachetHQ\Cachet\Bus\Commands\Schedule\DeleteScheduleCommand;
use CachetHQ\Cachet\Bus\Commands\Schedule\UpdateScheduleCommand;
use CachetHQ\Cachet\Integrations\Contracts\System;
use CachetHQ\Cachet\Models\IncidentTemplate;
use CachetHQ\Cachet\Models\Schedule;
use GrahamCampbell\Binput\Facades\Binput;
@@ -35,13 +36,21 @@ class ScheduleController extends Controller
*/
protected $subMenu = [];
/**
* The system instance.
*
* @var \CachetHQ\Cachet\Integrations\Contracts\System
*/
protected $system;
/**
* Creates a new schedule controller instance.
*
* @return void
*/
public function __construct()
public function __construct(System $system)
{
$this->system = $system;
View::share('subTitle', trans('dashboard.schedule.title'));
}
@@ -70,7 +79,8 @@ class ScheduleController extends Controller
return View::make('dashboard.maintenance.add')
->withPageTitle(trans('dashboard.schedule.add.title').' - '.trans('dashboard.dashboard'))
->withIncidentTemplates($incidentTemplates);
->withIncidentTemplates($incidentTemplates)
->withNotificationsEnabled($this->system->canNotifySubscribers());
}
/**
@@ -87,7 +97,8 @@ class ScheduleController extends Controller
Binput::get('status', Schedule::UPCOMING),
Binput::get('scheduled_at'),
Binput::get('completed_at'),
Binput::get('components', [])
Binput::get('components', []),
Binput::get('notify', false)
));
} catch (ValidationException $e) {
return cachet_redirect('dashboard.schedule.create')

View File

@@ -18,6 +18,7 @@ use CachetHQ\Cachet\Bus\Events\User\UserRegeneratedApiTokenEvent;
use CachetHQ\Cachet\Models\User;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Routing\Controller;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\View;
use PragmaRX\Google2FA\Vendor\Laravel\Facade as Google2FA;
@@ -44,7 +45,7 @@ class UserController extends Controller
{
$userData = array_filter(Binput::only(['username', 'email', 'password', 'google2fa']));
$enable2FA = (bool) array_pull($userData, 'google2fa');
$enable2FA = (bool) Arr::pull($userData, 'google2fa');
// Let's enable/disable auth
if ($enable2FA && !Auth::user()->hasTwoFactor) {

View File

@@ -16,6 +16,7 @@ use CachetHQ\Cachet\Models\User;
use CachetHQ\Cachet\Settings\Repository;
use GrahamCampbell\Binput\Facades\Binput;
use Illuminate\Routing\Controller;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Request;
@@ -199,7 +200,7 @@ class SetupController extends Controller
});
$v->sometimes(['env.mail_username'], 'required', function ($input) {
return !in_array($input->env['mail_username'], ['sendmail']);
return !in_array($input->env['mail_driver'], ['sendmail', 'log']);
});
if ($v->passes()) {
@@ -240,7 +241,7 @@ class SetupController extends Controller
if ($v->passes()) {
// Pull the user details out.
$userDetails = array_pull($postData, 'user');
$userDetails = Arr::pull($postData, 'user');
$user = User::create([
'username' => $userDetails['username'],
@@ -253,13 +254,13 @@ class SetupController extends Controller
$setting = app(Repository::class);
$settings = array_pull($postData, 'settings');
$settings = Arr::pull($postData, 'settings');
foreach ($settings as $settingName => $settingValue) {
$setting->set($settingName, $settingValue);
}
$envData = array_pull($postData, 'env');
$envData = Arr::pull($postData, 'env');
// Write the env to the .env file.
execute(new UpdateConfigCommand($envData));

View File

@@ -96,7 +96,8 @@ class StatusPageController extends AbstractApiController
$nextDate = $startDate->copy()->addDays($appIncidentDays)->toDateString();
}
$allIncidents = Incident::where('visible', '>=', (int) !Auth::check())->whereBetween('occurred_at', [
$allIncidents = Incident::with('component')->with('updates.incident')
->where('visible', '>=', (int) !Auth::check())->whereBetween('occurred_at', [
$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) {

View File

@@ -213,6 +213,6 @@ class SubscribeController extends Controller
}
return cachet_redirect('subscribe.manage', $subscriber->verify_code)
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.subscribed')));
->withSuccess(sprintf('%s %s', trans('dashboard.notifications.awesome'), trans('cachet.subscriber.email.updated-subscribe')));
}
}

View File

@@ -11,16 +11,18 @@
namespace CachetHQ\Cachet\Http;
use AltThree\Throttle\ThrottlingMiddleware;
use Barryvdh\Cors\HandleCors;
use CachetHQ\Cachet\Http\Middleware\Admin;
use CachetHQ\Cachet\Http\Middleware\ApiAuthentication;
use CachetHQ\Cachet\Http\Middleware\Authenticate;
use CachetHQ\Cachet\Http\Middleware\CacheControl;
use CachetHQ\Cachet\Http\Middleware\Localize;
use CachetHQ\Cachet\Http\Middleware\ReadyForUse;
use CachetHQ\Cachet\Http\Middleware\RedirectIfAuthenticated;
use CachetHQ\Cachet\Http\Middleware\RemoteUserAuthenticate;
use CachetHQ\Cachet\Http\Middleware\SetupAlreadyCompleted;
use CachetHQ\Cachet\Http\Middleware\SubscribersConfigured;
use CachetHQ\Cachet\Http\Middleware\Throttler;
use CachetHQ\Cachet\Http\Middleware\TrustProxies;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
@@ -44,16 +46,18 @@ class Kernel extends HttpKernel
* @var array
*/
protected $routeMiddleware = [
'admin' => Admin::class,
'can' => Authorize::class,
'auth' => Authenticate::class,
'auth.api' => ApiAuthentication::class,
'admin' => Admin::class,
'auth.api' => ApiAuthentication::class,
'auth.remoteuser' => RemoteUserAuthenticate::class,
'guest' => RedirectIfAuthenticated::class,
'localize' => Localize::class,
'ready' => ReadyForUse::class,
'setup' => SetupAlreadyCompleted::class,
'subscribers' => SubscribersConfigured::class,
'throttle' => ThrottlingMiddleware::class,
'auth' => Authenticate::class,
'cache' => CacheControl::class,
'can' => Authorize::class,
'cors' => HandleCors::class,
'guest' => RedirectIfAuthenticated::class,
'localize' => Localize::class,
'ready' => ReadyForUse::class,
'setup' => SetupAlreadyCompleted::class,
'subscribers' => SubscribersConfigured::class,
'throttle' => Throttler::class,
];
}

View File

@@ -18,7 +18,7 @@ use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
/**
* This is the acceptable middleware class.
*
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
*/
class Acceptable

View File

@@ -20,7 +20,7 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
* This is the admin middleware class.
*
* @author Joseph Cohen <joe@alt-three.com>
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
*/
class Admin

View File

@@ -22,7 +22,7 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
* This is the api authentication middleware class.
*
* @author Joseph Cohen <joe@alt-three.com>
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
*/
class ApiAuthentication

View File

@@ -20,7 +20,7 @@ use Symfony\Component\HttpKernel\Exception\HttpException;
* This is the authenticate middleware class.
*
* @author Joseph Cohen <joe@alt-three.com>
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
*/
class Authenticate

View File

@@ -0,0 +1,37 @@
<?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 Closure;
use Illuminate\Http\Request;
class CacheControl
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
$response = $next($request);
$maxAge = time() + 30;
$response->header('Cache-Control', 'public,max-age='.$maxAge);
return $response;
}
}

View File

@@ -22,7 +22,7 @@ use Jenssegers\Date\Date;
*
* @author James Brooks <james@alt-three.com>
* @author Joseph Cohen <joe@alt-three.com>
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class Localize
{

View File

@@ -18,7 +18,7 @@ use Illuminate\Http\Request;
/**
* This is the ready for use middleware class.
*
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
* @author Joseph Cohen <joe@alt-three.com>
*/

View File

@@ -18,7 +18,7 @@ use Illuminate\Http\Request;
/**
* This is the redirect if authenticated middleware class.
*
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author Joseph Cohen <joe@alt-three.com>
* @author James Brooks <james@alt-three.com>
*/

View File

@@ -19,7 +19,7 @@ use Illuminate\Http\Request;
/**
* This is the setup already completed middelware class.
*
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
* @author James Brooks <james@alt-three.com>
* @author Joseph Cohen <joe@alt-three.com>
*/

View File

@@ -19,7 +19,7 @@ use Illuminate\Http\Request;
* This is the subscribers configured middleware class.
*
* @author James Brooks <james@alt-three.com>
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class SubscribersConfigured
{

View File

@@ -0,0 +1,125 @@
<?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 Closure;
use Illuminate\Cache\RateLimiter;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
/**
* This is the throttler middleware class.
*
* @author Graham Campbell <graham@alt-three.com>
*/
class Throttler
{
/**
* The rate limiter instance.
*
* @var \Illuminate\Cache\RateLimiter
*/
protected $limiter;
/**
* Create a new throttler middleware instance.
*
* @param \Illuminate\Cache\RateLimiter $limiter
*
* @return void
*/
public function __construct(RateLimiter $limiter)
{
$this->limiter = $limiter;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param int|string $limit
* @param int|string $decay
*
* @throws \Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException
*
* @return mixed
*/
public function handle(Request $request, Closure $next, $limit = 60, $decay = 1)
{
return $this->safeHandle($request, $next, (int) $limit, (int) $decay);
}
/**
* Handle an incoming request, with correct types.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param int $limit
* @param int $decay
*
* @throws \Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException
*
* @return mixed
*/
protected function safeHandle(Request $request, Closure $next, int $limit, int $decay)
{
$key = $request->fingerprint();
if ($this->limiter->tooManyAttempts($key, $limit, $decay)) {
throw $this->buildException($key, $limit);
}
$this->limiter->hit($key, $decay);
$response = $next($request);
$response->headers->add($this->getHeaders($key, $limit));
return $response;
}
/**
* Create a too many requests http exception.
*
* @param string $key
* @param int $limit
*
* @return \Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException
*/
protected function buildException(string $key, int $limit)
{
$after = $this->limiter->availableIn($key);
$exception = new TooManyRequestsHttpException($after, 'Rate limit exceeded.');
$exception->setHeaders($this->getHeaders($key, $limit, $after, $exception->getHeaders()));
return $exception;
}
/**
* Get the limit header information.
*
* @param string $key
* @param int $limit
* @param int|null $after
* @param array $merge
*
* @return array
*/
protected function getHeaders(string $key, int $limit, int $after = null, array $merge = [])
{
$remaining = $after === null ? $this->limiter->retriesLeft($key, $limit) : 0;
$headers = ['X-RateLimit-Limit' => $limit, 'X-RateLimit-Remaining' => $remaining];
return array_merge($headers, $merge);
}
}

View File

@@ -19,7 +19,7 @@ use Illuminate\Http\Request;
* This is the timezone middleware class.
*
* @author James Brooks <james@alt-three.com>
* @author Graham Campbell <james@alt-three.com>
* @author Graham Campbell <graham@alt-three.com>
*/
class Timezone
{

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\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
/**
* This is the verify csrf token middleware class.
*
* @author James Brooks <james@alt-three.com>
*/
class VerifyCsrfToken extends Middleware
{
/**
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
*
* @var bool
*/
protected $addHttpCookie = true;
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'/api/*',
];
}

View File

@@ -40,7 +40,7 @@ class ApiRoutes
'namespace' => 'Api',
'prefix' => 'api/v1',
], function (Registrar $router) {
$router->group(['middleware' => ['auth.api']], function (Registrar $router) {
$router->group(['middleware' => ['auth.api', 'cors']], function (Registrar $router) {
$router->get('components', 'ComponentController@index');
$router->get('components/groups', 'ComponentGroupController@index');
$router->get('components/groups/{component_group}', 'ComponentGroupController@show');

View File

@@ -43,7 +43,7 @@ class ApiSystemRoutes
$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('status', ['uses' => 'GeneralController@status', 'middleware' => ['cache']]);
});
});
}

View File

@@ -25,6 +25,7 @@ use CachetHQ\Cachet\Settings\Repository as Setting;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Support\Str;
/**
* This is the beacon class.
@@ -84,7 +85,7 @@ class Beacon implements BeaconContract
$setting = app(Setting::class);
if (!$installId = $setting->get('install_id', null)) {
$installId = sha1(str_random(20));
$installId = sha1(Str::random(20));
$setting->set('install_id', $installId);
}

View File

@@ -28,7 +28,7 @@ class Feed implements FeedContract
*
* @var string
*/
const URL = 'https://blog.alt-three.com/tag/cachet/rss';
const URL = 'https://alt-three.com/tag/cachet/rss';
/**
* The failed status indicator.

View File

@@ -12,6 +12,7 @@
namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\HasMeta;
use CachetHQ\Cachet\Models\Traits\HasTags;
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
@@ -23,7 +24,12 @@ use McCool\LaravelAutoPresenter\HasPresenter;
class Component extends Model implements HasPresenter
{
use HasTags, SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
use HasTags,
HasMeta,
SearchableTrait,
SoftDeletes,
SortableTrait,
ValidatingTrait;
/**
* List of attributes that have default values.
@@ -134,16 +140,6 @@ class Component extends Model implements HasPresenter
return $this->hasMany(Incident::class, 'component_id', 'id');
}
/**
* Get the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
/**
* Finds all components by status.
*

View File

@@ -12,6 +12,7 @@
namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\HasMeta;
use CachetHQ\Cachet\Models\Traits\HasTags;
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
@@ -30,7 +31,12 @@ use McCool\LaravelAutoPresenter\HasPresenter;
*/
class Incident extends Model implements HasPresenter
{
use HasTags, SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
use HasMeta,
HasTags,
SearchableTrait,
SoftDeletes,
SortableTrait,
ValidatingTrait;
/**
* Status for incident being investigated.
@@ -181,16 +187,6 @@ class Incident extends Model implements HasPresenter
return $this->belongsTo(Component::class, 'component_id', 'id');
}
/**
* Get all of the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
/**
* Get the updates relation.
*

View File

@@ -13,6 +13,7 @@ namespace CachetHQ\Cachet\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
/**
* This is the invite class.
@@ -51,7 +52,7 @@ class Invite extends Model
self::creating(function ($invite) {
if (!$invite->code) {
$invite->code = str_random(20);
$invite->code = Str::random(20);
}
});
}

View File

@@ -13,6 +13,7 @@ namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use AltThree\Validator\ValidationException;
use CachetHQ\Cachet\Models\Traits\HasMeta;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use CachetHQ\Cachet\Presenters\MetricPresenter;
use Illuminate\Database\Eloquent\Builder;
@@ -22,7 +23,9 @@ use McCool\LaravelAutoPresenter\HasPresenter;
class Metric extends Model implements HasPresenter
{
use SortableTrait, ValidatingTrait;
use HasMeta,
SortableTrait,
ValidatingTrait;
/**
* The calculation type of sum.
@@ -165,16 +168,6 @@ class Metric extends Model implements HasPresenter
});
}
/**
* Get the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
/**
* Get the points relation.
*

View File

@@ -12,6 +12,7 @@
namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\HasMeta;
use CachetHQ\Cachet\Models\Traits\SearchableTrait;
use CachetHQ\Cachet\Models\Traits\SortableTrait;
use CachetHQ\Cachet\Presenters\SchedulePresenter;
@@ -21,14 +22,13 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use McCool\LaravelAutoPresenter\HasPresenter;
/**
* This is the schedule class.
*
* @author James Brooks <james@alt-three.com>
*/
class Schedule extends Model implements HasPresenter
{
use SearchableTrait, SoftDeletes, SortableTrait, ValidatingTrait;
use HasMeta,
SearchableTrait,
SoftDeletes,
SortableTrait,
ValidatingTrait;
/**
* The upcoming status.
@@ -144,13 +144,17 @@ class Schedule extends Model implements HasPresenter
}
/**
* Get the meta relation.
* Scope schedules that are uncompleted.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function meta()
public function scopeUncompleted(Builder $query)
{
return $this->morphMany(Meta::class, 'meta');
return $query->whereIn('status', [self::UPCOMING, self::IN_PROGRESS])->where(function (Builder $query) {
return $query->whereNull('completed_at');
});
}
/**
@@ -163,7 +167,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) {
$query->whereNull('completed_at')->orWhere('completed_at', '>', Carbon::now());
$query->whereNull('completed_at');
});
}
@@ -174,21 +178,33 @@ class Schedule extends Model implements HasPresenter
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeFutureSchedules($query)
public function scopeScheduledInFuture($query)
{
return $query->whereIn('status', [self::UPCOMING, self::IN_PROGRESS])->where('scheduled_at', '>=', Carbon::now());
}
/**
* Scopes schedules to those in the past.
* Scopes schedules to those scheduled in the past.
*
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePastSchedules($query)
public function scopeScheduledInPast($query)
{
return $query->where('status', '<', self::COMPLETE)->where('scheduled_at', '<=', Carbon::now());
return $query->whereIn('status', [self::UPCOMING, self::IN_PROGRESS])->where('scheduled_at', '<=', Carbon::now());
}
/**
* Scopes schedules to those completed in the past.
*
* @param \Illuminate\Database\Eloquent\Builder $query
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeCompletedInPast($query)
{
return $query->where('status', '=', self::COMPLETE)->where('completed_at', '<=', Carbon::now());
}
/**

View File

@@ -12,10 +12,12 @@
namespace CachetHQ\Cachet\Models;
use AltThree\Validator\ValidatingTrait;
use CachetHQ\Cachet\Models\Traits\HasMeta;
use CachetHQ\Cachet\Presenters\SubscriberPresenter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
use McCool\LaravelAutoPresenter\HasPresenter;
/**
@@ -27,7 +29,9 @@ use McCool\LaravelAutoPresenter\HasPresenter;
*/
class Subscriber extends Model implements HasPresenter
{
use Notifiable, ValidatingTrait;
use HasMeta,
Notifiable,
ValidatingTrait;
/**
* The attributes that should be casted to native types.
@@ -90,16 +94,6 @@ class Subscriber extends Model implements HasPresenter
});
}
/**
* Get the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
/**
* Get the subscriptions relation.
*
@@ -166,7 +160,7 @@ class Subscriber extends Model implements HasPresenter
*/
public static function generateVerifyCode()
{
return str_random(42);
return Str::random(42);
}
/**

View File

@@ -0,0 +1,32 @@
<?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\Meta;
/**
* This is the has meta trait.
*
* @author James Brooks <james@alt-three.com>
*/
trait HasMeta
{
/**
* Get the meta relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function meta()
{
return $this->morphMany(Meta::class, 'meta');
}
}

View File

@@ -18,6 +18,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use McCool\LaravelAutoPresenter\HasPresenter;
/**
@@ -188,7 +189,7 @@ class User extends Authenticatable implements HasPresenter
*/
public static function generateApiKey()
{
return str_random(20);
return Str::random(20);
}
/**

View File

@@ -128,7 +128,7 @@ class NewIncidentNotification extends Notification
->$status()
->content($content)
->attachment(function ($attachment) use ($notifiable) {
$attachment->title(trans('notifications.incident.new.slack.title', [$this->incident->name]))
$attachment->title(trans('notifications.incident.new.slack.title', ['name' => $this->incident->name]))
->timestamp($this->incident->getWrappedObject()->occurred_at)
->fields(array_filter([
'ID' => "#{$this->incident->id}",

View File

@@ -78,6 +78,7 @@ class IncidentUpdatedNotification extends Notification
->subject(trans('notifications.incident.update.mail.subject'))
->markdown('notifications.incident.update', [
'incident' => $this->update->incident,
'update' => $this->update,
'content' => $content,
'actionText' => trans('notifications.incident.new.mail.action'),
'actionUrl' => cachet_route('incident', [$this->update->incident]),

View File

@@ -20,6 +20,13 @@ class ComponentGroupPresenter extends BasePresenter implements Arrayable
{
use TimestampsTrait;
/**
* Flag for the enabled_components_lowest function.
*
* @var bool
*/
protected $enabledComponentsLowest = false;
/**
* Returns the lowest component status.
*
@@ -27,7 +34,7 @@ class ComponentGroupPresenter extends BasePresenter implements Arrayable
*/
public function lowest_status()
{
if ($component = $this->wrappedObject->enabled_components_lowest()->first()) {
if ($component = $this->enabled_components_lowest()) {
return AutoPresenter::decorate($component)->status;
}
}
@@ -39,7 +46,7 @@ class ComponentGroupPresenter extends BasePresenter implements Arrayable
*/
public function lowest_human_status()
{
if ($component = $this->wrappedObject->enabled_components_lowest()->first()) {
if ($component = $this->enabled_components_lowest()) {
return AutoPresenter::decorate($component)->human_status;
}
}
@@ -51,11 +58,25 @@ class ComponentGroupPresenter extends BasePresenter implements Arrayable
*/
public function lowest_status_color()
{
if ($component = $this->wrappedObject->enabled_components_lowest()->first()) {
if ($component = $this->enabled_components_lowest()) {
return AutoPresenter::decorate($component)->status_color;
}
}
/**
* Return the enabled components from the wrapped object, and cache it if need be.
*
* @return bool
*/
public function enabled_components_lowest()
{
if (is_bool($this->enabledComponentsLowest)) {
$this->enabledComponentsLowest = $this->wrappedObject->enabled_components_lowest()->first();
}
return $this->enabledComponentsLowest;
}
/**
* Determine the class for collapsed/uncollapsed groups.
*

View File

@@ -29,6 +29,13 @@ class IncidentPresenter extends BasePresenter implements Arrayable
*/
protected $dates;
/**
* Flag for the latest function.
*
* @var bool
*/
protected $latest = false;
/**
* Incident icon lookup.
*
@@ -248,9 +255,11 @@ class IncidentPresenter extends BasePresenter implements Arrayable
*/
public function latest()
{
if ($update = $this->wrappedObject->updates()->orderBy('created_at', 'desc')->first()) {
return $update;
if (is_bool($this->latest)) {
$this->latest = $this->wrappedObject->updates()->first();
}
return $this->latest;
}
/**

View File

@@ -12,6 +12,7 @@
namespace CachetHQ\Cachet\Presenters;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Facades\Config;
use Laravolt\Avatar\Facade as Avatar;
use McCool\LaravelAutoPresenter\BasePresenter;
@@ -29,7 +30,7 @@ class UserPresenter extends BasePresenter implements Arrayable
*/
public function avatar()
{
if (setting('enable_external_dependencies')) {
if (Config::get('setting.enable_external_dependencies')) {
return sprintf('https://www.gravatar.com/avatar/%s?size=%d', md5(strtolower($this->email)), 200);
}

View File

@@ -108,7 +108,7 @@ abstract class AbstractMetricRepository
$point->value = $metric->default_value;
}
if ($point->value === 0 && $metric->default_value != $value) {
if ($point->value === 0 && $metric->default_value != $point->value) {
$point->value = $metric->default_value;
}

View File

@@ -38,7 +38,7 @@ class PgSqlRepository extends AbstractMetricRepository implements MetricInterfac
"AND {$this->getMetricPointsTable()}.created_at >= (NOW() - INTERVAL '{$minutes}' MINUTE) ".
"AND {$this->getMetricPointsTable()}.created_at <= NOW() ".
"GROUP BY to_char({$this->getMetricPointsTable()}.created_at, 'HH24:MI') ".
"ORDER BY to_char({$this->getMetricPointsTable()}.created_at, 'HH24:MI')", [
"ORDER BY {$this->getMetricPointsTable()}.created_at", [
'metricId' => $metric->id,
]);
@@ -62,7 +62,7 @@ class PgSqlRepository extends AbstractMetricRepository implements MetricInterfac
"AND {$this->getMetricPointsTable()}.created_at >= (NOW() - INTERVAL '{$hour}' HOUR) ".
"AND {$this->getMetricPointsTable()}.created_at <= NOW() ".
"GROUP BY to_char({$this->getMetricPointsTable()}.created_at, 'HH24:00') ".
"ORDER BY to_char({$this->getMetricPointsTable()}.created_at, 'HH24:00')", [
"ORDER BY {$this->getMetricPointsTable()}.created_at", [
'metricId' => $metric->id,
]);

View File

@@ -177,7 +177,7 @@ class CommandSubscriber
*/
public function onPublishVendors(Command $command)
{
$command->call('vendor:publish');
$command->call('vendor:publish', ['--all' => true]);
}
/**