datatable.directory codebase https://datatable.directory/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
datatable.directory/app/Http/Controllers/TableEditController.php

368 lines
12 KiB

<?php
namespace App\Http\Controllers;
use App\Models\Proposal;
use App\Models\Table;
use App\Models\User;
use App\Tables\Changeset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
use MightyPork\Exceptions\SimpleValidationException;
use MightyPork\Utils\Utils;
/**
* Draft composing
*/
class TableEditController extends Controller
{
/**
* Initialize the session-stored changeset, if not set yet
*
* @param Table $table
* @return Changeset
*/
private function getChangeset(Table $table)
{
if (Input::has('reset')) {
session()->forget($table->draftSessionKey);
}
/** @var Changeset $changeset */
return session()->remember($table->draftSessionKey, function () use ($table) {
$changeset = new Changeset();
$changeset->table = $table;
$changeset->revision = $table->revision;
return $changeset;
});
}
/**
* Store the changeset to session
*
* @param Changeset $changeset
*/
private function storeChangeset(Changeset $changeset)
{
session()->put($changeset->table->draftSessionKey, $changeset);
}
/**
* Discard draft and redirect to table view
*/
public function discard(User $user, string $table)
{
/** @var Table $tableModel */
$tableModel = $user->tables()->where('name', $table)->first();
if ($tableModel === null) abort(404, "No such table.");
session()->forget($tableModel->draftSessionKey);
return redirect($tableModel->viewRoute);
}
#region Draft tabs
/**
* Show the table edit view with tabs
*
* @param User $user - table owner
* @param string $table - table name
* @param string|null $tab - page tab name, kebab-case
* @return \Illuminate\View\View;
*/
public function draft(User $user, string $table, $tab = null)
{
/** @var Table $tableModel */
$tableModel = $user->tables()->where('name', $table)->first();
if ($tableModel === null) abort(404, "No such table.");
if ($tab == null) $tab = 'edit-rows';
$method = camel_case('tab-'.$tab);
if (!method_exists($this, $method)) abort(404, "No such tab: $tab");
$changeset = $this->getChangeset($tableModel);
if (Input::has('dump')) {
dd($changeset);
}
return $this->$method($changeset);
}
/** @noinspection PhpUnusedPrivateMethodInspection */
private function tabEditRows(Changeset $changeset)
{
$revision = $changeset->revision;
$columns = $changeset->fetchAndTransformColumns();
$rows = $revision->rowsData($columns, true, false)->paginate(25, []);
return view('table.propose.edit-rows', [
'changeset' => $changeset,
'table' => $changeset->table,
'columns' => collect($columns),
'rows' => $rows,
]);
}
/** @noinspection PhpUnusedPrivateMethodInspection */
private function tabAddRows(Changeset $changeset)
{
$columns = $changeset->fetchAndTransformColumns();
$rows = $changeset->getAddedRows(25);
return view('table.propose.add-rows', [
'changeset' => $changeset,
'table' => $changeset->table,
'columns' => collect($columns),
'rows' => $rows,
]);
}
/** @noinspection PhpUnusedPrivateMethodInspection */
private function tabAddRowsCsv(Changeset $changeset)
{
$columns = $changeset->fetchAndTransformColumns();
return view('table.propose.add-rows-csv', [
'changeset' => $changeset,
'table' => $changeset->table,
'columns' => collect($columns)->where('toRemove', false),
]);
}
/** @noinspection PhpUnusedPrivateMethodInspection */
private function tabManageColumns(Changeset $changeset)
{
$columns = $changeset->fetchAndTransformColumns();
return view('table.propose.manage-columns', [
'changeset' => $changeset,
'table' => $changeset->table,
'columns' => collect($columns),
]);
}
/** @noinspection PhpUnusedPrivateMethodInspection */
private function tabReview(Changeset $changeset)
{
return view('table.propose.review', [
'changeset' => $changeset,
'table' => $changeset->table,
]);
}
#endregion
/**
* API hook called by AJAX or via forms.
* Generally applies modifications to the Changeset stored in session.
*
* @param Request $request
* @param User $user
* @param string $table
* @return \Symfony\Component\HttpFoundation\Response
*/
public function draftUpdate(Request $request, User $user, string $table)
{
/** @var Table $tableModel */
$tableModel = $user->tables()->where('name', $table)->first();
if ($tableModel === null) abort(404, "No such table.");
$changeset = $this->getChangeset($tableModel);
$input = objBag($request->all(), false);
$resp = null;
$code = 200;
try {
switch ($input->action) {
case 'row.update':
$data = (object)$input->data;
$resp = $changeset->rowUpdate($data);
break;
case 'row.update-many':
$newVals = $input->data;
$updated = [];
foreach ($newVals as $rowUpdate) {
$r = $changeset->rowUpdate($rowUpdate);
$updated[$r->_id] = $r;
}
$resp = $updated;
break;
case 'row.add-csv':
$fname = 'csv-file';
if ($request->hasFile($fname)) {
try {
$file = $request->file($fname);
if ($file->isValid() && $file->isReadable()) {
$handle = $file->openFile();
$csv = Utils::csvToArray($handle);
if (empty($csv)) throw new \Exception("Failed to parse CSV file.");
$changeset->addFilledRows($csv);
$handle = null;
} else {
throw new \Exception("File not valid.");
}
} catch (\Exception $e) {
return $this->backWithErrors(['csv-file' => $e->getMessage()]);
}
}
else {
try {
$text = trim($input->data);
if (empty($text)) throw new \Exception("Empty CSV field and no file uploaded.");
$changeset->addFilledRows(Utils::csvToArray($text));
} catch (\Exception $e) {
return $this->backWithErrors(['data' => $e->getMessage()]);
}
}
break;
case 'row.remove':
$isNew = $changeset->isNewRow($input->id);
$changeset->rowRemove($input->id);
$resp = $isNew ? null : $changeset->fetchAndTransformRow($input->id);
break;
case 'row.remove-empty-new':
$changeset->removeEmptyNewRows();
break;
case 'row.restore':
$changeset->rowRestore($input->id);
$resp = $changeset->fetchAndTransformRow($input->id);
break;
case 'rows.add':
$changeset->addBlankRows($input->count);
break;
case 'col.update':
$data = (object)$input->data;
$resp = $changeset->columnUpdate($data);
break;
case 'col.remove':
$isNew = $changeset->isNewColumn($input->id);
$changeset->columnRemove($input->id);
$resp = $isNew ? null : $changeset->fetchAndTransformColumn($input->id);
break;
case 'col.restore':
$changeset->columnRestore($input->id);
$resp = $changeset->fetchAndTransformColumn($input->id);
break;
case 'col.add':
$resp = $changeset->addBlankCol();
break;
case 'col.sort':
$changeset->setColOrder($input->order);
$resp = !empty($changeset->columnOrder); // return flag if order is changed
break;
case 'note.set':
$changeset->note = $input->text;
break;
case 'reset.col-order': // called via POST or GET
$changeset->resetColumnOrder();
$resp = $changeset->fetchAndTransformColumns(); // return all columns
break;
case 'reset.col-remove': // called via GET
$changeset->resetRemovedColumns();
break;
case 'reset.col-add': // called via GET
$changeset->resetAddedColumns();
break;
case 'reset.col-update': // called via GET
$changeset->resetUpdatedColumns();
break;
case 'reset.row-remove': // called via GET
$changeset->resetRemovedRows();
break;
case 'reset.row-add': // called via GET
$changeset->resetAddedRows();
break;
case 'reset.row-update': // called via GET
$changeset->resetUpdatedRows();
break;
default:
$resp = "Bad Action";
$code = 400;
break;
}
} catch (SimpleValidationException $e) {
return $this->jsonResponse(['errors' => $e->getMessageBag()->getMessages()], 400);
}
$this->storeChangeset($changeset);
if ($request->method() == 'GET') {
return back();
}
// Redirect requested via form
if ($code == 200 && $input->has('redirect')) {
return redirect($input->redirect);
}
return $this->jsonResponse($resp, $code);
}
/**
* Submit the form
*
* @param Request $request
* @param User $user
* @param string $table
* @return \Symfony\Component\HttpFoundation\Response
*/
public function draftSubmit(Request $request, User $user, string $table)
{
/** @var Table $tableModel */
$tableModel = $user->tables()->where('name', $table)->first();
if ($tableModel === null) abort(404, "No such table.");
$input = $this->validate($request, [
'note' => 'required|string'
]);
$changeset = $this->getChangeset($tableModel);
$changeset->note = trim($input->note);
if (empty($changeset->note)) throw new SimpleValidationException('note', 'Note is required.');
if (!$changeset->getRowChangeCounts()->any && !$changeset->getColumnChangeCounts()->any) {
flash()->error("There are no changes to submit.");
return back();
}
$proposal = Proposal::fromChangeset($changeset);
$proposal->saveOrFail();
if (\user()->ownsTable($tableModel)) {
$proposal->createRevision();
} else {
// TODO send a notification to the table owner
}
session()->forget($tableModel->draftSessionKey);
return redirect($tableModel->viewRoute);
}
}