Skip to content

Commit 34f718a

Browse files
committed
Finish mount admin view page, add unique index to migration
1 parent 0db7deb commit 34f718a

File tree

4 files changed

+276
-3
lines changed

4 files changed

+276
-3
lines changed

app/Http/Controllers/Admin/MountController.php

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Pterodactyl\Http\Controllers\Admin;
44

5+
use Illuminate\Http\Request;
56
use Pterodactyl\Models\Mount;
67
use Prologue\Alerts\AlertsMessageBag;
78
use Pterodactyl\Exceptions\DisplayException;
@@ -11,6 +12,8 @@
1112
use Pterodactyl\Services\Mounts\MountDeletionService;
1213
use Pterodactyl\Http\Requests\Admin\MountFormRequest;
1314
use Pterodactyl\Repositories\Eloquent\MountRepository;
15+
use Pterodactyl\Contracts\Repository\NestRepositoryInterface;
16+
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
1417

1518
class MountController extends Controller
1619
{
@@ -19,6 +22,16 @@ class MountController extends Controller
1922
*/
2023
protected $alert;
2124

25+
/**
26+
* @var \Pterodactyl\Contracts\Repository\NestRepositoryInterface
27+
*/
28+
protected $nestRepository;
29+
30+
/**
31+
* @var \Pterodactyl\Contracts\Repository\LocationRepositoryInterface
32+
*/
33+
protected $locationRepository;
34+
2235
/**
2336
* @var \Pterodactyl\Repositories\Eloquent\MountRepository
2437
*/
@@ -43,19 +56,25 @@ class MountController extends Controller
4356
* MountController constructor.
4457
*
4558
* @param \Prologue\Alerts\AlertsMessageBag $alert
59+
* @param \Pterodactyl\Contracts\Repository\NestRepositoryInterface $nestRepository
60+
* @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $locationRepository
4661
* @param \Pterodactyl\Repositories\Eloquent\MountRepository $repository
4762
* @param \Pterodactyl\Services\Mounts\MountCreationService $creationService
4863
* @param \Pterodactyl\Services\Mounts\MountDeletionService $deletionService
4964
* @param \Pterodactyl\Services\Mounts\MountUpdateService $updateService
5065
*/
5166
public function __construct(
5267
AlertsMessageBag $alert,
68+
NestRepositoryInterface $nestRepository,
69+
LocationRepositoryInterface $locationRepository,
5370
MountRepository $repository,
5471
MountCreationService $creationService,
5572
MountDeletionService $deletionService,
5673
MountUpdateService $updateService
5774
) {
5875
$this->alert = $alert;
76+
$this->nestRepository = $nestRepository;
77+
$this->locationRepository = $locationRepository;
5978
$this->repository = $repository;
6079
$this->creationService = $creationService;
6180
$this->deletionService = $deletionService;
@@ -84,8 +103,16 @@ public function index()
84103
*/
85104
public function view($id)
86105
{
106+
$nests = $this->nestRepository->all();
107+
$nests->load('eggs');
108+
109+
$locations = $this->locationRepository->all();
110+
$locations->load('nodes');
111+
87112
return view('admin.mounts.view', [
88113
'mount' => $this->repository->getWithRelations($id),
114+
'nests' => $nests,
115+
'locations' => $locations,
89116
]);
90117
}
91118

@@ -146,4 +173,74 @@ public function delete(Mount $mount)
146173

147174
return redirect()->route('admin.mounts.view', $mount->id);
148175
}
176+
177+
/**
178+
* Adds eggs to the mount's many to many relation.
179+
*
180+
* @param \Illuminate\Http\Request $request
181+
* @param \Pterodactyl\Models\Mount $mount
182+
* @return \Illuminate\Http\RedirectResponse
183+
*/
184+
public function addEggs(Request $request, Mount $mount)
185+
{
186+
$validatedData = $request->validate([
187+
'eggs' => 'required|exists:eggs,id',
188+
]);
189+
190+
$eggs = $validatedData['eggs'] ?? [];
191+
if (sizeof($eggs) > 0) {
192+
$mount->eggs()->attach(array_map('intval', $eggs));
193+
$this->alert->success('Mount was updated successfully.')->flash();
194+
}
195+
196+
return redirect()->route('admin.mounts.view', $mount->id);
197+
}
198+
199+
/**
200+
* Adds nodes to the mount's many to many relation.
201+
*
202+
* @param \Illuminate\Http\Request $request
203+
* @param \Pterodactyl\Models\Mount $mount
204+
* @return \Illuminate\Http\RedirectResponse
205+
*/
206+
public function addNodes(Request $request, Mount $mount)
207+
{
208+
$validatedData = $request->validate([
209+
'nodes' => 'required|exists:nodes,id',
210+
]);
211+
212+
$nodes = $validatedData['nodes'] ?? [];
213+
if (sizeof($nodes) > 0) {
214+
$mount->nodes()->attach(array_map('intval', $nodes));
215+
$this->alert->success('Mount was updated successfully.')->flash();
216+
}
217+
218+
return redirect()->route('admin.mounts.view', $mount->id);
219+
}
220+
221+
/**
222+
* Deletes an egg from the mount's many to many relation.
223+
*
224+
* @param \Pterodactyl\Models\Mount $mount
225+
* @param int $egg_id
226+
* @return \Illuminate\Http\Response
227+
*/
228+
public function deleteEgg(Mount $mount, int $egg_id)
229+
{
230+
$mount->eggs()->detach($egg_id);
231+
return response('', 204);
232+
}
233+
234+
/**
235+
* Deletes an node from the mount's many to many relation.
236+
*
237+
* @param \Pterodactyl\Models\Mount $mount
238+
* @param int $node_id
239+
* @return \Illuminate\Http\Response
240+
*/
241+
public function deleteNode(Mount $mount, int $node_id)
242+
{
243+
$mount->nodes()->detach($node_id);
244+
return response('', 204);
245+
}
149246
}

database/migrations/2020_05_20_234655_add_mounts_table.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,15 @@ public function up()
2727
Schema::create('egg_mount', function (Blueprint $table) {
2828
$table->integer('egg_id');
2929
$table->char('mount_id', 36);
30+
31+
$table->unique(['egg_id', 'mount_id']);
3032
});
3133

3234
Schema::create('mount_node', function (Blueprint $table) {
3335
$table->integer('node_id');
3436
$table->char('mount_id', 36);
37+
38+
$table->unique(['node_id', 'mount_id']);
3539
});
3640
}
3741

resources/views/admin/mounts/view.blade.php

Lines changed: 169 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<div class="col-sm-6">
2525
<div class="box box-primary">
2626
<div class="box-header with-border">
27-
<h3 class="box-title">Location Details</h3>
27+
<h3 class="box-title">Mount Details</h3>
2828
</div>
2929

3030
<form action="{{ route('admin.mounts.view', $mount->id) }}" method="POST">
@@ -101,19 +101,27 @@
101101
<div class="box">
102102
<div class="box-header with-border">
103103
<h3 class="box-title">Eggs</h3>
104+
105+
<div class="box-tools">
106+
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#addEggsModal">Add Eggs</button>
107+
</div>
104108
</div>
105109

106110
<div class="box-body table-responsive no-padding">
107111
<table class="table table-hover">
108112
<tr>
109113
<th>ID</th>
110114
<th>Name</th>
115+
<th></th>
111116
</tr>
112117

113-
@foreach($mount->eggs as $egg)
118+
@foreach ($mount->eggs as $egg)
114119
<tr>
115120
<td><code>{{ $egg->id }}</code></td>
116121
<td><a href="{{ route('admin.nests.egg.view', $egg->id) }}">{{ $egg->name }}</a></td>
122+
<td class="col-sm-1 middle">
123+
<button data-action="detach-egg" data-id="{{ $egg->id }}" class="btn btn-sm btn-danger"><i class="fa fa-trash-o"></i></button>
124+
</td>
117125
</tr>
118126
@endforeach
119127
</table>
@@ -123,6 +131,10 @@
123131
<div class="box">
124132
<div class="box-header with-border">
125133
<h3 class="box-title">Nodes</h3>
134+
135+
<div class="box-tools">
136+
<button class="btn btn-sm btn-primary" data-toggle="modal" data-target="#addNodesModal">Add Nodes</button>
137+
</div>
126138
</div>
127139

128140
<div class="box-body table-responsive no-padding">
@@ -131,18 +143,172 @@
131143
<th>ID</th>
132144
<th>Name</th>
133145
<th>FQDN</th>
146+
<th></th>
134147
</tr>
135148

136-
@foreach($mount->nodes as $node)
149+
@foreach ($mount->nodes as $node)
137150
<tr>
138151
<td><code>{{ $node->id }}</code></td>
139152
<td><a href="{{ route('admin.nodes.view', $node->id) }}">{{ $node->name }}</a></td>
140153
<td><code>{{ $node->fqdn }}</code></td>
154+
<td class="col-sm-1 middle">
155+
<button data-action="detach-node" data-id="{{ $node->id }}" class="btn btn-sm btn-danger"><i class="fa fa-trash-o"></i></button>
156+
</td>
141157
</tr>
142158
@endforeach
143159
</table>
144160
</div>
145161
</div>
146162
</div>
147163
</div>
164+
165+
<div class="modal fade" id="addEggsModal" tabindex="-1" role="dialog">
166+
<div class="modal-dialog" role="document">
167+
<div class="modal-content">
168+
<form action="{{ route('admin.mounts.eggs', $mount->id) }}" method="POST">
169+
<div class="modal-header">
170+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
171+
<span aria-hidden="true" style="color: #FFFFFF">&times;</span>
172+
</button>
173+
174+
<h4 class="modal-title">Add Eggs</h4>
175+
</div>
176+
177+
<div class="modal-body">
178+
<div class="row">
179+
<div class="form-group col-md-12">
180+
<label for="pEggs">Eggs</label>
181+
<select id="pEggs" name="eggs[]" class="form-control" multiple>
182+
@foreach ($nests as $nest)
183+
<optgroup label="{{ $nest->name }}">
184+
@foreach ($nest->eggs as $egg)
185+
186+
@if (! in_array($egg->id, $mount->eggs->pluck('id')->toArray()))
187+
<option value="{{ $egg->id }}">{{ $egg->name }}</option>
188+
@endif
189+
190+
@endforeach
191+
</optgroup>
192+
@endforeach
193+
</select>
194+
</div>
195+
</div>
196+
</div>
197+
198+
<div class="modal-footer">
199+
{!! csrf_field() !!}
200+
201+
<button type="button" class="btn btn-default btn-sm pull-left" data-dismiss="modal">Cancel</button>
202+
<button type="submit" class="btn btn-primary btn-sm">Add</button>
203+
</div>
204+
</form>
205+
</div>
206+
</div>
207+
</div>
208+
209+
<div class="modal fade" id="addNodesModal" tabindex="-1" role="dialog">
210+
<div class="modal-dialog" role="document">
211+
<div class="modal-content">
212+
<form action="{{ route('admin.mounts.nodes', $mount->id) }}" method="POST">
213+
<div class="modal-header">
214+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
215+
<span aria-hidden="true" style="color: #FFFFFF">&times;</span>
216+
</button>
217+
218+
<h4 class="modal-title">Add Nodes</h4>
219+
</div>
220+
221+
<div class="modal-body">
222+
<div class="row">
223+
<div class="form-group col-md-12">
224+
<label for="pNodes">Nodes</label>
225+
<select id="pNodes" name="nodes[]" class="form-control" multiple>
226+
@foreach ($locations as $location)
227+
<optgroup label="{{ $location->long }} ({{ $location->short }})">
228+
@foreach ($location->nodes as $node)
229+
230+
@if (! in_array($node->id, $mount->nodes->pluck('id')->toArray()))
231+
<option value="{{ $node->id }}">{{ $node->name }}</option>
232+
@endif
233+
234+
@endforeach
235+
</optgroup>
236+
@endforeach
237+
</select>
238+
</div>
239+
</div>
240+
</div>
241+
242+
<div class="modal-footer">
243+
{!! csrf_field() !!}
244+
245+
<button type="button" class="btn btn-default btn-sm pull-left" data-dismiss="modal">Cancel</button>
246+
<button type="submit" class="btn btn-primary btn-sm">Add</button>
247+
</div>
248+
</form>
249+
</div>
250+
</div>
251+
</div>
252+
@endsection
253+
254+
@section('footer-scripts')
255+
@parent
256+
257+
<script type="application/javascript">
258+
$(document).ready(function() {
259+
$('#pEggs').select2({
260+
placeholder: 'Select eggs..',
261+
});
262+
263+
$('#pNodes').select2({
264+
placeholder: 'Select nodes..',
265+
});
266+
267+
$('button[data-action="detach-egg"]').click(function (event) {
268+
event.preventDefault();
269+
270+
const element = $(this);
271+
const eggId = $(this).data('id');
272+
273+
$.ajax({
274+
method: 'DELETE',
275+
url: '/admin/mounts/' + {{ $mount->id }} + '/eggs/' + eggId,
276+
headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') },
277+
}).done(function () {
278+
element.parent().parent().addClass('warning').delay(100).fadeOut();
279+
swal({ type: 'success', title: 'Egg detached.' });
280+
}).fail(function (jqXHR) {
281+
console.error(jqXHR);
282+
swal({
283+
title: 'Whoops!',
284+
text: jqXHR.responseJSON.error,
285+
type: 'error'
286+
});
287+
});
288+
});
289+
290+
$('button[data-action="detach-node"]').click(function (event) {
291+
event.preventDefault();
292+
293+
const element = $(this);
294+
const nodeId = $(this).data('id');
295+
296+
$.ajax({
297+
method: 'DELETE',
298+
url: '/admin/mounts/' + {{ $mount->id }} + '/nodes/' + nodeId,
299+
headers: { 'X-CSRF-TOKEN': $('meta[name="_token"]').attr('content') },
300+
}).done(function () {
301+
element.parent().parent().addClass('warning').delay(100).fadeOut();
302+
swal({ type: 'success', title: 'Node detached.' });
303+
}).fail(function (jqXHR) {
304+
console.error(jqXHR);
305+
swal({
306+
title: 'Whoops!',
307+
text: jqXHR.responseJSON.error,
308+
type: 'error'
309+
});
310+
});
311+
});
312+
});
313+
</script>
148314
@endsection

0 commit comments

Comments
 (0)