@@ -15,6 +15,7 @@ use AltThree\Bus\Dispatcher;
|
|||||||
use CachetHQ\Cachet\Bus\Middleware\UseDatabaseTransactions;
|
use CachetHQ\Cachet\Bus\Middleware\UseDatabaseTransactions;
|
||||||
use CachetHQ\Cachet\Dates\DateFactory;
|
use CachetHQ\Cachet\Dates\DateFactory;
|
||||||
use CachetHQ\Cachet\Integrations\Credits;
|
use CachetHQ\Cachet\Integrations\Credits;
|
||||||
|
use CachetHQ\Cachet\Integrations\Feed;
|
||||||
use CachetHQ\Cachet\Integrations\Releases;
|
use CachetHQ\Cachet\Integrations\Releases;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -55,6 +56,7 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
{
|
{
|
||||||
$this->registerDateFactory();
|
$this->registerDateFactory();
|
||||||
$this->registerCredits();
|
$this->registerCredits();
|
||||||
|
$this->registerFeed();
|
||||||
$this->registerReleases();
|
$this->registerReleases();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +89,20 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register the feed class.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function registerFeed()
|
||||||
|
{
|
||||||
|
$this->app->singleton(Feed::class, function ($app) {
|
||||||
|
$cache = $app['cache.store'];
|
||||||
|
|
||||||
|
return new Feed($cache);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the releases class.
|
* Register the releases class.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace CachetHQ\Cachet\Http\Controllers\Dashboard;
|
namespace CachetHQ\Cachet\Http\Controllers\Dashboard;
|
||||||
|
|
||||||
|
use CachetHQ\Cachet\Integrations\Feed;
|
||||||
use CachetHQ\Cachet\Models\Component;
|
use CachetHQ\Cachet\Models\Component;
|
||||||
use CachetHQ\Cachet\Models\Incident;
|
use CachetHQ\Cachet\Models\Incident;
|
||||||
use CachetHQ\Cachet\Models\Subscriber;
|
use CachetHQ\Cachet\Models\Subscriber;
|
||||||
@@ -36,12 +37,22 @@ class DashboardController extends Controller
|
|||||||
protected $timeZone;
|
protected $timeZone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new dashboard controller.
|
* The feed integration.
|
||||||
|
*
|
||||||
|
* @var \CachetHQ\Cachet\Integrations\Feed
|
||||||
|
*/
|
||||||
|
protected $feed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new dashboard controller instance.
|
||||||
|
*
|
||||||
|
* @param \CachetHQ\Cachet\Integrations\Feed $feed
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct(Feed $feed)
|
||||||
{
|
{
|
||||||
|
$this->feed = $feed;
|
||||||
$this->startDate = new Date();
|
$this->startDate = new Date();
|
||||||
$this->dateTimeZone = Config::get('cachet.timezone');
|
$this->dateTimeZone = Config::get('cachet.timezone');
|
||||||
}
|
}
|
||||||
@@ -56,12 +67,16 @@ class DashboardController extends Controller
|
|||||||
$components = Component::orderBy('order')->get();
|
$components = Component::orderBy('order')->get();
|
||||||
$incidents = $this->getIncidents();
|
$incidents = $this->getIncidents();
|
||||||
$subscribers = $this->getSubscribers();
|
$subscribers = $this->getSubscribers();
|
||||||
|
$feed = $this->feed->entries();
|
||||||
|
|
||||||
|
$entries = array_slice($feed->channel->item, 0, 5);
|
||||||
|
|
||||||
return View::make('dashboard.index')
|
return View::make('dashboard.index')
|
||||||
->withPageTitle(trans('dashboard.dashboard'))
|
->withPageTitle(trans('dashboard.dashboard'))
|
||||||
->withComponents($components)
|
->withComponents($components)
|
||||||
->withIncidents($incidents)
|
->withIncidents($incidents)
|
||||||
->withSubscribers($subscribers);
|
->withSubscribers($subscribers)
|
||||||
|
->withEntries($entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<?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\Integrations;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use Illuminate\Contracts\Cache\Repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the feed class.
|
||||||
|
*
|
||||||
|
* @author James Brooks <james@alt-three.com>
|
||||||
|
*/
|
||||||
|
class Feed
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The default url.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
const URL = 'https://blog.alt-three.com/tag/cachet/rss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cache repository instance.
|
||||||
|
*
|
||||||
|
* @var \Illuminate\Contracts\Cache\Repository
|
||||||
|
*/
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The url to use.
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new feed instance.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||||
|
* @param string|null $url
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Repository $cache, $url = null)
|
||||||
|
{
|
||||||
|
$this->cache = $cache;
|
||||||
|
$this->url = $url ?: static::URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the entries.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function entries()
|
||||||
|
{
|
||||||
|
return $this->cache->remember('feeds', 2880, function () {
|
||||||
|
$xml = simplexml_load_string((new Client())->get($this->url, [
|
||||||
|
'headers' => ['Accept' => 'application/rss+xml', 'User-Agent' => defined('CACHET_VERSION') ? 'cachet/'.constant('CACHET_VERSION') : 'cachet'],
|
||||||
|
])->getBody()->getContents(), null, LIBXML_NOCDATA);
|
||||||
|
|
||||||
|
return json_decode(json_encode($xml));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -40,6 +40,28 @@
|
|||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stats-body {
|
||||||
|
margin-top: -20px;
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
|
.list-group {
|
||||||
|
border: none;
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
.list-group-item {
|
||||||
|
border-right: none;
|
||||||
|
border-left: none;
|
||||||
|
|
||||||
|
border-color: #eee;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.stats-bottom {
|
.stats-bottom {
|
||||||
border-top: #eee 1px solid;
|
border-top: #eee 1px solid;
|
||||||
color: #777;
|
color: #777;
|
||||||
|
|||||||
@@ -249,6 +249,14 @@ return [
|
|||||||
'whoops' => 'Whoops.',
|
'whoops' => 'Whoops.',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Widgets
|
||||||
|
'widgets' => [
|
||||||
|
'support' => 'Support Cachet',
|
||||||
|
'support_subtitle' => 'Check out our <strong><a href="https://patreon.com/jbrooksuk" target="_blank">Patreon</a></strong> page!',
|
||||||
|
'news' => 'Latest News',
|
||||||
|
'news_subtitle' => 'Get the latest updates',
|
||||||
|
],
|
||||||
|
|
||||||
// Welcome modal
|
// Welcome modal
|
||||||
'welcome' => [
|
'welcome' => [
|
||||||
'welcome' => 'Welcome to your new status page!',
|
'welcome' => 'Welcome to your new status page!',
|
||||||
|
|||||||
@@ -1,86 +1,113 @@
|
|||||||
@extends('layout.dashboard')
|
@extends('layout.dashboard')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="sidebar-toggler visible-xs">
|
<div class="sidebar-toggler visible-xs">
|
||||||
<i class="ion ion-navicon"></i>
|
<i class="ion ion-navicon"></i>
|
||||||
</div>
|
|
||||||
<span class="uppercase">
|
|
||||||
<i class="ion ion-speedometer"></i> {{ trans('dashboard.dashboard') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="content-wrapper">
|
<span class="uppercase">
|
||||||
<div class="row">
|
<i class="ion ion-speedometer"></i> {{ trans('dashboard.dashboard') }}
|
||||||
<div class="col-md-12">
|
</span>
|
||||||
<div class="alert alert-info hidden" id="update-alert">{!! trans('cachet.system.update') !!}</div>
|
</div>
|
||||||
</div>
|
<div class="content-wrapper">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="alert alert-info hidden" id="update-alert">{!! trans('cachet.system.update') !!}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
</div>
|
||||||
<div class="col-md-12">
|
<div class="row">
|
||||||
<h4 class="sub-header">{{ trans('dashboard.components.component_statuses') }}</h4>
|
<div class="col-md-12">
|
||||||
<div class="panel panel-default">
|
<h4 class="sub-header">{{ trans('dashboard.components.component_statuses') }}</h4>
|
||||||
<div class="list-group">
|
<div class="panel panel-default">
|
||||||
@forelse($components as $component)
|
<div class="list-group">
|
||||||
<div class="list-group-item">
|
@forelse($components as $component)
|
||||||
<form class='component-inline form-vertical' data-messenger="{{trans('dashboard.components.edit.success')}}">
|
<div class="list-group-item">
|
||||||
<div class="row striped-list-item">
|
<form class='component-inline form-vertical' data-messenger="{{trans('dashboard.components.edit.success')}}">
|
||||||
<div class="col-lg-4 col-md-3 col-sm-12">
|
<div class="row striped-list-item">
|
||||||
<h4>{{ $component->name }}</h4>
|
<div class="col-lg-4 col-md-3 col-sm-12">
|
||||||
</div>
|
<h4>{{ $component->name }}</h4>
|
||||||
<div class="col-lg-8 col-md-9 col-sm-12 radio-items componet-inline-update">
|
|
||||||
@foreach(trans('cachet.components.status') as $statusID => $status)
|
|
||||||
<div class="radio-inline">
|
|
||||||
<label>
|
|
||||||
<input type="radio" name="status" value="{{ $statusID }}" {{ (int) $component->status === $statusID ? 'checked' : null }}>
|
|
||||||
{{ $status }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
@endforeach
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="component_id" value="{{ $component->id }}">
|
<div class="col-lg-8 col-md-9 col-sm-12 radio-items componet-inline-update">
|
||||||
</form>
|
@foreach(trans('cachet.components.status') as $statusID => $status)
|
||||||
</div>
|
<div class="radio-inline">
|
||||||
@empty
|
<label>
|
||||||
<div class="list-group-item"><a href="{{ route('dashboard.components.add') }}">{{ trans('dashboard.components.add.message') }}</a></div>
|
<input type="radio" name="status" value="{{ $statusID }}" {{ (int) $component->status === $statusID ? 'checked' : null }}>
|
||||||
@endforelse
|
{{ $status }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="component_id" value="{{ $component->id }}">
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@empty
|
||||||
|
<div class="list-group-item"><a href="{{ route('dashboard.components.add') }}">{{ trans('dashboard.components.add.message') }}</a></div>
|
||||||
|
@endforelse
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-lg-6">
|
||||||
|
<div class="stats-widget">
|
||||||
|
<div class="stats-top">
|
||||||
|
<span class="stats-value"><a href="{{ route('dashboard.incidents.index') }}">{{ $incidents->map(function($incident) { return count($incident); })->sum() }}</a></span>
|
||||||
|
<span class="stats-label">{{ trans('dashboard.incidents.incidents') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stats-chart">
|
||||||
|
<div class="sparkline" data-type="line" data-resize="true" data-height="80" data-width="100%" data-line-width="2" data-min-spot-color="#e65100" data-max-spot-color="#ffb300" data-line-color="#3498db" data-spot-color="#00838f" data-fill-color="#3498db" data-highlight-line-color="#00acc1" data-highlight-spot-color="#ff8a65" data-spot-radius="false" data-data="[{{ $incidents->map(function ($incident) { return count($incident); } )->implode(',') }}]"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="col-sm-12 col-lg-6">
|
||||||
<div class="col-sm-12 col-lg-6">
|
<div class="stats-widget">
|
||||||
<div class="stats-widget">
|
<div class="stats-top">
|
||||||
<div class="stats-top">
|
<span class="stats-value"><a href="{{ route('dashboard.subscribers.index') }}">{{ $subscribers->map(function($subscribers) { return count($subscribers); })->sum() }}</a></span>
|
||||||
<span class="stats-value"><a href="{{ route('dashboard.incidents.index') }}">{{ $incidents->map(function($incident) { return count($incident); })->sum() }}</a></span>
|
<span class="stats-label">{{ trans('dashboard.subscribers.subscribers') }}</span>
|
||||||
<span class="stats-label">{{ trans('dashboard.incidents.incidents') }}</span>
|
</div>
|
||||||
</div>
|
<div class="stats-chart">
|
||||||
<div class="stats-chart">
|
<div class="sparkline" data-type="line" data-resize="true" data-height="80" data-width="100%" data-line-width="2" data-min-spot-color="#e65100" data-max-spot-color="#ffb300" data-line-color="#3498db" data-spot-color="#00838f" data-fill-color="#3498db" data-highlight-line-color="#00acc1" data-highlight-spot-color="#ff8a65" data-spot-radius="false" data-data="[{{ $subscribers->map(function ($subscriber) { return count($subscriber); } )->implode(',') }}]"></div>
|
||||||
<div class="sparkline" data-type="line" data-resize="true" data-height="80" data-width="100%" data-line-width="2" data-min-spot-color="#e65100" data-max-spot-color="#ffb300" data-line-color="#3498db" data-spot-color="#00838f" data-fill-color="#3498db" data-highlight-line-color="#00acc1" data-highlight-spot-color="#ff8a65" data-spot-radius="false" data-data="[{{ $incidents->map(function ($incident) { return count($incident); } )->implode(',') }}]"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12 col-lg-6">
|
<div class="row">
|
||||||
<div class="stats-widget">
|
<div class="col-sm-12 col-lg-6">
|
||||||
<div class="stats-top">
|
<div class="stats-widget">
|
||||||
<span class="stats-value"><a href="{{ route('dashboard.subscribers.index') }}">{{ $subscribers->map(function($subscribers) { return count($subscribers); })->sum() }}</a></span>
|
<div class='stats-top'>
|
||||||
<span class="stats-label">{{ trans('dashboard.subscribers.subscribers') }}</span>
|
<span class='stats-value'>{{ trans('dashboard.widgets.support') }}</span>
|
||||||
</div>
|
<span class='stats-label'>{!! trans('dashboard.widgets.support_subtitle') !!}</span>
|
||||||
<div class="stats-chart">
|
</div>
|
||||||
<div class="sparkline" data-type="line" data-resize="true" data-height="80" data-width="100%" data-line-width="2" data-min-spot-color="#e65100" data-max-spot-color="#ffb300" data-line-color="#3498db" data-spot-color="#00838f" data-fill-color="#3498db" data-highlight-line-color="#00acc1" data-highlight-spot-color="#ff8a65" data-spot-radius="false" data-data="[{{ $subscribers->map(function ($subscriber) { return count($subscriber); } )->implode(',') }}]"></div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-12 col-lg-6">
|
||||||
|
<div class="stats-widget">
|
||||||
|
<div class='stats-top'>
|
||||||
|
<span class='stats-value'>{{ trans('dashboard.widgets.news') }}</span>
|
||||||
|
<span class='stats-label'>{{ trans('dashboard.widgets.news_subtitle') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class='stats-body'>
|
||||||
|
<div class="list-group">
|
||||||
|
@foreach($entries as $entry)
|
||||||
|
<a class="list-group-item" href="{{ $entry->link }}" target="_blank">{{ $entry->title }}, <small>{{ $entry->pubDate }}</small></a>
|
||||||
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if(Session::get('setup.done'))
|
</div>
|
||||||
@include('dashboard.partials.welcome-modal')
|
@if(Session::get('setup.done'))
|
||||||
<script>
|
@include('dashboard.partials.welcome-modal')
|
||||||
$(function() {
|
<script>
|
||||||
$('#welcome-modal').modal('show');
|
$(function() {
|
||||||
});
|
$('#welcome-modal').modal('show');
|
||||||
</script>
|
});
|
||||||
@endif
|
</script>
|
||||||
|
@endif
|
||||||
@stop
|
@stop
|
||||||
|
|||||||
Reference in New Issue
Block a user