implements a modular view system

This commit is contained in:
Connor S. Parks
2016-08-18 13:33:43 +01:00
parent 445c1ed2d0
commit fa1b32c11a
30 changed files with 809 additions and 181 deletions
-1
View File
@@ -6,7 +6,6 @@ php:
- 5.6
- 7.0
- 7.1
- hhvm
sudo: false
+71
View File
@@ -0,0 +1,71 @@
<?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;
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);
}
}
@@ -9,40 +9,20 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Composers;
namespace CachetHQ\Cachet\Composers\Modules;
use CachetHQ\Cachet\Integrations\Core\System;
use CachetHQ\Cachet\Models\Component;
use CachetHQ\Cachet\Models\ComponentGroup;
use CachetHQ\Cachet\Models\Incident;
use Illuminate\Contracts\View\View;
/**
* This is the status page composer.
*
* @author James Brooks <james@alt-three.com>
* @author Connor S. Parks <connor@connorvg.tv>
*/
class StatusPageComposer
class ComponentsComposer
{
/**
* The system instance.
*
* @var \CachetHQ\Cachet\Integrations\Contracts\System
*/
protected $system;
/**
* Create a new status page composer instance.
*
* @param \CachetHQ\Cachet\Integrations\Contracts\System $system
*
* @return void
*/
public function __construct(System $system)
{
$this->system = $system;
}
/**
* Index page view composer.
*
@@ -52,19 +32,12 @@ class StatusPageComposer
*/
public function compose(View $view)
{
$status = $this->system->getStatus();
// Scheduled maintenance code.
$scheduledMaintenance = Incident::scheduled()->orderBy('scheduled_at')->get();
// Component & Component Group lists.
$usedComponentGroups = Component::enabled()->where('group_id', '>', 0)->groupBy('group_id')->pluck('group_id');
$componentGroups = ComponentGroup::whereIn('id', $usedComponentGroups)->orderBy('order')->get();
$ungroupedComponents = Component::enabled()->where('group_id', 0)->orderBy('order')->orderBy('created_at')->get();
$view->with($status)
->withComponentGroups($componentGroups)
->withUngroupedComponents($ungroupedComponents)
->withScheduledMaintenance($scheduledMaintenance);
$view->withComponentGroups($componentGroups)
->withUngroupedComponents($ungroupedComponents);
}
}
@@ -9,7 +9,7 @@
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Composers;
namespace CachetHQ\Cachet\Composers\Modules;
use CachetHQ\Cachet\Models\Metric;
use Illuminate\Contracts\Config\Repository;
@@ -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\Composers\Modules;
use CachetHQ\Cachet\Models\Incident;
use Illuminate\Contracts\View\View;
/**
* This is the status page composer.
*
* @author James Brooks <james@alt-three.com>
* @author Connor S. Parks <connor@connorvg.tv>
*/
class ScheduledComposer
{
/**
* Index page view composer.
*
* @param \Illuminate\Contracts\View\View $view
*
* @return void
*/
public function compose(View $view)
{
$scheduledMaintenance = Incident::scheduled()->orderBy('scheduled_at')->get();
$view->withScheduledMaintenance($scheduledMaintenance);
}
}
+55
View File
@@ -0,0 +1,55 @@
<?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\Modules;
use CachetHQ\Cachet\Integrations\Contracts\System;
use Illuminate\Contracts\View\View;
/**
* This is the status page composer.
*
* @author James Brooks <james@alt-three.com>
* @author Connor S. Parks <connor@connorvg.tv>
*/
class StatusComposer
{
/**
* The system instance.
*
* @var \CachetHQ\Cachet\Integrations\Contracts\System
*/
protected $system;
/**
* Create a new status page composer instance.
*
* @param \CachetHQ\Cachet\Integrations\Contracts\System $system
*
* @return void
*/
public function __construct(System $system)
{
$this->system = $system;
}
/**
* Index page view composer.
*
* @param \Illuminate\Contracts\View\View $view
*
* @return void
*/
public function compose(View $view)
{
$view->with($this->system->getStatus());
}
}
@@ -0,0 +1,35 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Cachet\Composers\Modules;
use Illuminate\Contracts\View\View;
/**
* This is the status page composer.
*
* @author James Brooks <james@alt-three.com>
* @author Connor S. Parks <connor@connorvg.tv>
*/
class TimelineComposer
{
/**
* Index page view composer.
*
* @param \Illuminate\Contracts\View\View $view
*
* @return void
*/
public function compose(View $view)
{
// ...
}
}
@@ -14,8 +14,12 @@ namespace CachetHQ\Cachet\Foundation\Providers;
use CachetHQ\Cachet\Composers\AppComposer;
use CachetHQ\Cachet\Composers\CurrentUserComposer;
use CachetHQ\Cachet\Composers\DashboardComposer;
use CachetHQ\Cachet\Composers\MetricsComposer;
use CachetHQ\Cachet\Composers\StatusPageComposer;
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\TimelineComposer as TimelineModuleComposer;
use CachetHQ\Cachet\Composers\ThemeComposer;
use CachetHQ\Cachet\Composers\TimezoneLocaleComposer;
use Illuminate\Contracts\View\Factory;
@@ -32,11 +36,16 @@ class ComposerServiceProvider extends ServiceProvider
{
$factory->composer('*', AppComposer::class);
$factory->composer('*', CurrentUserComposer::class);
$factory->composer(['index'], MetricsComposer::class);
$factory->composer(['index', 'single-incident', 'subscribe', 'signup'], StatusPageComposer::class);
$factory->composer(['index', 'single-incident', 'subscribe.*', 'signup', 'dashboard.settings.theme', 'emails.*'], ThemeComposer::class);
$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.scheduled', ScheduledModuleComposer::class);
$factory->composer('partials.modules.status', StatusModuleComposer::class);
$factory->composer('partials.modules.timeline', TimelineModuleComposer::class);
}
/**
@@ -11,8 +11,9 @@
namespace CachetHQ\Cachet\Foundation\Providers;
use Illuminate\View\Compilers\BladeCompiler;
use CachetHQ\Cachet\Services\Modules\Renderer as ModulesRenderer;
use Illuminate\Support\ServiceProvider;
use Illuminate\View\Compilers\BladeCompiler;
class ModuleServiceProvider extends ServiceProvider
{
@@ -44,7 +45,7 @@ class ModuleServiceProvider extends ServiceProvider
'components' => 30000,
'metrics' => 40000,
'scheduled' => 50000,
'timeline' => 60000,
'timeline' => 60000,
],
];
@@ -56,26 +57,17 @@ class ModuleServiceProvider extends ServiceProvider
public function boot(BladeCompiler $blade)
{
$blade->directive('modules', function ($group = null) {
$code = [
'foreach (array_numeric_sort($modules) as $group => $parts) {',
'foreach (array_numeric_sort($parts) as $part) {',
'if (empty($part[\'partial\'])) {',
'continue;',
'}',
'$params = array_except(get_defined_vars(), [\'__data\', \'__path\']);',
'$params = array_merge(array_get($part, \'parameters\', []), $params);',
'echo $__env->make($part[\'partial\'], $params)->render();',
'}',
'}',
];
if ($group) {
$code[0] = 'if (!empty($modules['.$group.'])) {'.PHP_EOL;
$code[0] .= '$parts = $modules['.$group.'];';
}
return '<?php '.implode(PHP_EOL, $code).'?>';
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',
$group === null ? 'null' : $group
);
});
}
+80
View File
@@ -0,0 +1,80 @@
<?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\Services\Modules;
/**
* This is the module manager class.
*
* @author Connor S. Parks <connor@connorvg.tv>
*/
class Manager
{
/**
* Groups the modules.
*
* @param array $modules
* @param array $groups
*
* @return array
*/
public function groupModules(array $modules, array $groups = [])
{
$grouped = [];
$length = count($modules);
foreach ($modules as $order => $module) {
if (!is_array($module) || empty($module['group'])) {
$order = array_get($module, 'order', PHP_INT_MAX - $length + $order);
$grouped[] = [$module, 'order' => $order];
continue;
}
$group = $module['group'];
if (empty($grouped[$group])) {
$grouped[$group] = [$module];
continue;
}
$grouped[$group][] = $module;
}
foreach ($groups as $group => $order) {
if (empty($grouped[$group])) {
continue;
}
$grouped[$group]['order'] = $order;
}
return $grouped;
}
/**
* Orders the modules.
*
* @param array $modules
*
* @return array
*/
public function orderModules($modules)
{
$modules = array_numeric_sort($modules);
foreach ($modules as $group => $subModules) {
$modules[$group] = array_numeric_sort($subModules);
}
return $modules;
}
}
+75
View File
@@ -0,0 +1,75 @@
<?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\Services\Modules;
use Illuminate\Contracts\View\Factory;
/**
* This is the module renderer class.
*
* @author Connor S. Parks <connor@connorvg.tv>
*/
class Renderer
{
/**
* Render the modules.
*
* @param \Illuminate\Contracts\View\Factory $factory
* @param array $data
* @param array $modules
* @param string|null $group
*
* @return string
*/
public function renderModules(Factory $factory, array $data, array $modules, $group = null)
{
if ($group !== null) {
if (empty($modules[$group])) {
return '';
}
return $this->renderModulesGroup($factory, $data, $modules[$group]);
}
return array_reduce(
array_numeric_sort($modules),
function ($reduce, $module) use ($factory, $data) {
return $reduce.$this->renderModulesGroup($factory, $data, $module);
},
''
);
}
/**
* Render a group of modules.
*
* @param \Illuminate\Contracts\View\Factory $factory
* @param array $data
* @param array $modules
*
* @return string
*/
protected function renderModulesGroup(Factory $factory, array $data, array $modules)
{
return array_reduce(
array_numeric_sort($modules),
function ($reduce, $module) use ($factory, $data) {
if (empty($module['partial'])) {
return $reduce;
}
return $reduce.$factory->make($module['partial'], $data)->render();
},
''
);
}
}
+24
View File
@@ -115,3 +115,27 @@ if (!function_exists('color_contrast')) {
return ($yiq >= 128) ? 'black' : 'white';
}
}
if (!function_exists('array_numeric_sort')) {
/**
* Numerically sort an array based on a specific key.
*
* @param array $array
* @param string $key
*
* @return array
*/
function array_numeric_sort(array $array = [], $key = 'order')
{
uasort($array, function ($a, $b) use ($key) {
$a = array_get($a, $key, PHP_INT_MAX);
$b = array_get($b, $key, PHP_INT_MAX);
$default = PHP_MAJOR_VERSION < 7 && !defined('HHVM_VERSION') ? 1 : 0;
return $a < $b ? -1 : ($a === $b ? $default : 1);
});
return $array;
}
}
+2 -2
View File
@@ -31,8 +31,8 @@ $app = require_once __DIR__.'/bootstrap/app.php';
$kernel = $app->make('Illuminate\Contracts\Console\Kernel');
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
new Symfony\Component\Console\Output\ConsoleOutput
$input = new Symfony\Component\Console\Input\ArgvInput(),
new Symfony\Component\Console\Output\ConsoleOutput()
);
/*
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,4 +1,4 @@
{
"dist/css/all.css": "dist/css/all-8b09a6139d.css",
"dist/js/all.js": "dist/js/all-914268da61.js"
"dist/css/all.css": "dist/css/all-9f109896fb.css",
"dist/js/all.js": "dist/js/all-a2296565be.js"
}
+16 -41
View File
@@ -161,63 +161,38 @@ $(function() {
}
});
// Sortable components.
var componentList = document.getElementById("component-list");
if (componentList) {
new Sortable(componentList, {
group: "omega",
handle: ".drag-handle",
// Sortable models.
var orderableLists = document.querySelectorAll('[data-orderable-list]');
$.each(orderableLists, function (k, list) {
var url = $(list).data('orderableList');
var notifier = new Cachet.Notifier();
new Sortable(list, {
group: 'omega',
handle: '.drag-handle',
onUpdate: function() {
var orderedComponentIds = $.map($('#component-list .striped-list-item'), function(elem) {
return $(elem).data('component-id');
var orderedIds = $.map(list.querySelectorAll('[data-orderable-id]'), function(elem) {
return $(elem).data('orderable-id');
});
$.ajax({
async: true,
url: '/dashboard/api/components/order',
url: url,
type: 'POST',
data: {
ids: orderedComponentIds
ids: orderedIds
},
success: function() {
(new Cachet.Notifier()).notify('Component orders updated.', 'success');
notifier.notify('Ordering updated.', 'success');
},
error: function() {
(new Cachet.Notifier()).notify('Component orders not updated.', 'error');
notifier.notify('Ordering not updated.', 'error');
}
});
}
});
}
// Sortable Component Groups
var componentGroupList = document.getElementById("component-group-list");
if (componentGroupList) {
new Sortable(componentGroupList, {
group: "omega",
handle: ".drag-handle",
onUpdate: function() {
var orderedComponentGroupsIds = $.map(
$('#component-group-list .striped-list-item'),
function(elem) {
return $(elem).data('group-id');
}
);
$.ajax({
async: true,
url: '/dashboard/api/components/groups/order',
type: 'POST',
data: {ids: orderedComponentGroupsIds},
success: function() {
(new Cachet.Notifier()).notify('Component groups order has been updated.', 'success');
},
error: function() {
(new Cachet.Notifier()).notify('Component groups order could not be updated.', 'error');
}
});
}
});
}
// Toggle inline component statuses.
$('form.component-inline').on('click', 'input[type=radio]', function() {
@@ -17,9 +17,9 @@
</div>
@include('dashboard.partials.errors')
<div class="row">
<div class="col-sm-12 striped-list" id="component-group-list">
<div class="col-sm-12 striped-list" data-orderable-list="/dashboard/api/components/groups/order">
@forelse($groups as $group)
<div class="row striped-list-item" data-group-id="{{ $group->id }}">
<div class="row striped-list-item" data-orderable-id="{{ $group->id }}">
<div class="col-xs-6">
<h4>
@if($groups->count() > 1)
@@ -17,9 +17,9 @@
</div>
@include('dashboard.partials.errors')
<div class="row">
<div class="col-sm-12 striped-list" id="component-list">
<div class="col-sm-12 striped-list" data-orderable-list="/dashboard/api/components/order">
@forelse($components as $component)
<div class="row striped-list-item {{ !$component->enabled ? 'bg-warning' : null }}" data-component-id="{{ $component->id }}">
<div class="row striped-list-item {{ !$component->enabled ? 'bg-warning' : null }}" data-orderable-id="{{ $component->id }}">
<div class="col-xs-6">
<h4>
@if($components->count() > 1)
+1 -56
View File
@@ -1,60 +1,5 @@
@extends('layout.master')
@section('content')
<div class="section-messages">
@include('dashboard.partials.errors')
</div>
<div class="section-status">
<div class="alert alert-{{ $system_status }}">{{ $system_message }}</div>
</div>
@include('partials.about-app')
@if(!$component_groups->isEmpty() || !$ungrouped_components->isEmpty())
<div class="section-components">
@include('partials.components')
</div>
@endif
@if($display_metrics && $app_graphs)
<div class="section-metrics">
@include('partials.metrics')
</div>
@endif
@if(!$scheduled_maintenance->isEmpty())
<div class="section-scheduled">
@include('partials.schedule')
</div>
@endif
@if($days_to_show > 0)
<div class="section-timeline">
<h1>{{ trans('cachet.incidents.past') }}</h1>
@foreach($all_incidents as $date => $incidents)
@include('partials.incidents', [compact($date), compact($incidents)])
@endforeach
</div>
<nav>
<ul class="pager">
@if($can_page_backward)
<li class="previous">
<a href="{{ route('status-page') }}?start_date={{ $previous_date }}" class="links">
<span aria-hidden="true">&larr;</span> {{ trans('cachet.incidents.previous_week') }}
</a>
</li>
@endif
@if($can_page_forward)
<li class="next">
<a href="{{ route('status-page') }}?start_date={{ $next_date }}" class="links">
{{ trans('cachet.incidents.next_week') }} <span aria-hidden="true">&rarr;</span>
</a>
</li>
@endif
</ul>
</nav>
@endif
@modules
@stop
@@ -0,0 +1,5 @@
@if(!$component_groups->isEmpty() || !$ungrouped_components->isEmpty())
<div class="section-components">
@include('partials.components')
</div>
@endif
@@ -0,0 +1,3 @@
<div class="section-messages">
@include('dashboard.partials.errors')
</div>
@@ -0,0 +1,5 @@
@if($display_metrics && $app_graphs)
<div class="section-metrics">
@include('partials.metrics')
</div>
@endif
@@ -0,0 +1,5 @@
@if(!$scheduled_maintenance->isEmpty())
<div class="section-scheduled">
@include('partials.schedule')
</div>
@endif
@@ -0,0 +1,3 @@
<div class="section-status">
<div class="alert alert-{{ $system_status }}">{{ $system_message }}</div>
</div>
@@ -0,0 +1,27 @@
@if($days_to_show > 0)
<div class="section-timeline">
<h1>{{ trans('cachet.incidents.past') }}</h1>
@foreach($all_incidents as $date => $incidents)
@include('partials.incidents', [compact($date), compact($incidents)])
@endforeach
</div>
<nav>
<ul class="pager">
@if($can_page_backward)
<li class="previous">
<a href="{{ route('status-page') }}?start_date={{ $previous_date }}" class="links">
<span aria-hidden="true">&larr;</span> {{ trans('cachet.incidents.previous_week') }}
</a>
</li>
@endif
@if($can_page_forward)
<li class="next">
<a href="{{ route('status-page') }}?start_date={{ $next_date }}" class="links">
{{ trans('cachet.incidents.next_week') }} <span aria-hidden="true">&rarr;</span>
</a>
</li>
@endif
</ul>
</nav>
@endif
+166
View File
@@ -0,0 +1,166 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Services\Modules;
use CachetHQ\Tests\Cachet\AbstractTestCase;
use Mockery as m;
/**
* This is the modules manager service test class.
*
* @author Connor S. Parks <connor@connorvg.tv>
*/
class ManagerTest extends AbstractTestCase
{
public function testGroupNoModules()
{
$manager = m::mock('CachetHQ\Cachet\Services\Modules\Manager[groupModules]');
$manager->shouldReceive('groupModules')->once()->passthru();
$grouped = $manager->groupModules([]);
$this->assertSame([], $grouped);
}
public function testGroupModulesNoGroups()
{
$manager = m::mock('CachetHQ\Cachet\Services\Modules\Manager[groupModules]');
$manager->shouldReceive('groupModules')->once()->passthru();
$grouped = $manager->groupModules($this->getModules(), []);
$this->assertSame($this->getGroupedModules(), $grouped);
}
public function testGroupModules()
{
$manager = m::mock('CachetHQ\Cachet\Services\Modules\Manager[groupModules]');
$manager->shouldReceive('groupModules')->once()->passthru();
$grouped = $manager->groupModules($this->getModules(), $this->getGroups());
$this->assertSame($this->getGroupedModulesWithOrders(), $grouped);
}
public function testOrderModules()
{
$manager = m::mock('CachetHQ\Cachet\Services\Modules\Manager[orderModules]');
$manager->shouldReceive('orderModules')->once()->passthru();
$ordered = $manager->orderModules($this->getGroupedModules());
$this->assertSame($this->getOrderedModules(), $ordered);
}
public function testOrderModulesWithOrders()
{
$manager = m::mock('CachetHQ\Cachet\Services\Modules\Manager[orderModules]');
$manager->shouldReceive('orderModules')->once()->passthru();
$ordered = $manager->orderModules($this->getGroupedModulesWithOrders());
$this->assertSame($this->getOrderedModulesWithOrders(), $ordered);
}
protected function getModules()
{
return [
['group' => 'two', 'partial' => 'partials.two.a'],
['partial' => 'partials.a', 'order' => 8],
['group' => 'one', 'partial' => 'partials.one.c'],
['partial' => 'partials.c'],
['group' => 'one', 'partial' => 'partials.one.a', 'order' => 1],
['group' => 'one', 'partial' => 'partials.one.b', 'order' => 2],
['partial' => 'partials.b', 'order' => 15],
];
}
protected function getGroups()
{
return [
'one' => 1,
'two' => 2,
];
}
protected function getGroupedModules()
{
return [
'two' => [
['group' => 'two', 'partial' => 'partials.two.a'],
],
[['partial' => 'partials.a', 'order' => 8], 'order' => 8],
'one' => [
['group' => 'one', 'partial' => 'partials.one.c'],
['group' => 'one', 'partial' => 'partials.one.a', 'order' => 1],
['group' => 'one', 'partial' => 'partials.one.b', 'order' => 2],
],
[['partial' => 'partials.c'], 'order' => PHP_INT_MAX - 4],
[['partial' => 'partials.b', 'order' => 15], 'order' => 15],
];
}
protected function getGroupedModulesWithOrders()
{
return [
'two' => [
['group' => 'two', 'partial' => 'partials.two.a'],
'order' => 2,
],
[['partial' => 'partials.a', 'order' => 8], 'order' => 8],
'one' => [
['group' => 'one', 'partial' => 'partials.one.c'],
['group' => 'one', 'partial' => 'partials.one.a', 'order' => 1],
['group' => 'one', 'partial' => 'partials.one.b', 'order' => 2],
'order' => 1,
],
[['partial' => 'partials.c'], 'order' => PHP_INT_MAX - 4],
[['partial' => 'partials.b', 'order' => 15], 'order' => 15],
];
}
protected function getOrderedModules()
{
return [
[['partial' => 'partials.a', 'order' => 8], 'order' => 8],
2 => [['partial' => 'partials.b', 'order' => 15], 'order' => 15],
1 => [['partial' => 'partials.c'], 'order' => PHP_INT_MAX - 4],
'two' => [
['group' => 'two', 'partial' => 'partials.two.a'],
],
'one' => [
1 => ['group' => 'one', 'partial' => 'partials.one.a', 'order' => 1],
2 => ['group' => 'one', 'partial' => 'partials.one.b', 'order' => 2],
0 => ['group' => 'one', 'partial' => 'partials.one.c'],
],
];
}
protected function getOrderedModulesWithOrders()
{
return [
'one' => [
1 => ['group' => 'one', 'partial' => 'partials.one.a', 'order' => 1],
2 => ['group' => 'one', 'partial' => 'partials.one.b', 'order' => 2],
0 => ['group' => 'one', 'partial' => 'partials.one.c'],
'order' => 1,
],
'two' => [
['group' => 'two', 'partial' => 'partials.two.a'],
'order' => 2,
],
[['partial' => 'partials.a', 'order' => 8], 'order' => 8],
2 => [['partial' => 'partials.b', 'order' => 15], 'order' => 15],
1 => [['partial' => 'partials.c'], 'order' => PHP_INT_MAX - 4],
];
}
}
+143
View File
@@ -0,0 +1,143 @@
<?php
/*
* This file is part of Cachet.
*
* (c) Alt Three Services Limited
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace CachetHQ\Tests\Cachet\Services\Modules;
use CachetHQ\Tests\Cachet\AbstractTestCase;
use Mockery as m;
/**
* This is the modules renderer service test class.
*
* @author Connor S. Parks <connor@connorvg.tv>
*/
class RendererTest extends AbstractTestCase
{
public function testRenderNoModules()
{
$renderer = m::mock('CachetHQ\Cachet\Services\Modules\Renderer[renderModules]');
$renderer->shouldReceive('renderModules')->once()->passthru();
$output = $renderer->renderModules(
$this->getFactory(),
[],
[],
null
);
$this->assertSame('', $output);
}
public function testRenderNonExistentModulesGroup()
{
$renderer = m::mock('CachetHQ\Cachet\Services\Modules\Renderer[renderModules]');
$renderer->shouldReceive('renderModules')->once()->passthru();
$output = $renderer->renderModules(
$this->getFactory(),
[],
$this->getModules(),
'non-existent group'
);
$this->assertSame('', $output);
}
public function testRenderModules()
{
$renderer = m::mock('CachetHQ\Cachet\Services\Modules\Renderer[renderModules]');
$renderer->shouldReceive('renderModules')->once()->passthru();
$factory = $this->getFactory([
'partial.a', 'partial.b', 'partial.c',
'partial.one.a', 'partial.one.b', 'partial.one.c',
'partial.two.a',
'partial.three.a', 'partial.three.b',
]);
$output = $renderer->renderModules(
$factory,
['a' => 'b', 'c' => 'd'],
$this->getModules(),
null
);
$this->assertSame('partial.apartial.one.apartial.one.bpartial.one.cpartial.two.apartial.bpartial.cpartial.three.apartial.three.b', $output);
}
public function testRenderModuleGroups()
{
$renderer = m::mock('CachetHQ\Cachet\Services\Modules\Renderer[renderModules]');
$renderer->shouldReceive('renderModules')->once()->passthru();
$factory = $this->getFactory([
'partial.one.a', 'partial.one.b', 'partial.one.c',
]);
$output = $renderer->renderModules(
$factory,
['a' => 'b', 'c' => 'd'],
$this->getModules(),
'one'
);
$this->assertSame('partial.one.apartial.one.bpartial.one.c', $output);
}
protected function getModules()
{
return [
'two' => [
'order' => 3,
['partial' => 'partial.two.a'],
],
[
['partial' => 'partial.c'],
['partial' => 'partial.b', 'order' => 1],
],
[
'order' => 1,
['partial' => 'partial.a'],
],
'one' => [
'order' => 2,
['partial' => 'partial.one.c'],
['partial' => 'partial.one.a', 'order' => 1],
['partial' => 'partial.one.b', 'order' => 2],
],
'three' => [
['partial' => 'partial.three.a'],
['partial' => 'partial.three.b'],
],
];
}
protected function getFactory($views = [])
{
$factory = m::mock('Illuminate\View\Factory[make]', $this->getFactoryArgs());
foreach ($views as $view) {
$factory->shouldReceive('make')->once()->with($view, ['a' => 'b', 'c' => 'd'])->andReturn($mockView = m::mock('StdClass'));
$mockView->shouldReceive('render')->once()->andReturn($view);
}
return $factory;
}
protected function getFactoryArgs()
{
return [
m::mock('Illuminate\View\Engines\EngineResolver'),
m::mock('Illuminate\View\ViewFinderInterface'),
m::mock('Illuminate\Contracts\Events\Dispatcher'),
];
}
}