datatable.directory codebase
				https://datatable.directory/
			
			
		
			Você não pode selecionar mais de 25 tópicos
			Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
		
		
		
		
		
			
		
			
				
					
					
						
							331 linhas
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							331 linhas
						
					
					
						
							11 KiB
						
					
					
				| <?php
 | |
| 
 | |
| namespace App\Http\Controllers;
 | |
| 
 | |
| use App\Models\Revision;
 | |
| use App\Models\Table;
 | |
| use App\Models\User;
 | |
| use App\Tables\Changeset;
 | |
| use App\Tables\Column;
 | |
| use App\Tables\ColumnNumerator;
 | |
| use App\Tables\CStructArrayExporter;
 | |
| use App\Tables\CsvExporter;
 | |
| use App\Tables\CXMacroExporter;
 | |
| use App\Tables\JsonExporter;
 | |
| use App\Tables\PhpExporter;
 | |
| use App\Tables\RowNumerator;
 | |
| use Illuminate\Http\Request;
 | |
| use Illuminate\Validation\Rule;
 | |
| use MightyPork\Exceptions\NotApplicableException;
 | |
| use MightyPork\Utils\Utils;
 | |
| 
 | |
| class TableController extends Controller
 | |
| {
 | |
|     public function view(Request $request, User $user, string $table)
 | |
|     {
 | |
|         $input = $this->validate($request, [
 | |
|             'rev' => 'nullable|int'
 | |
|         ]);
 | |
| 
 | |
|         /** @var Table $tableModel */
 | |
|         $tableModel = $user->tables()->withCount(['favourites', 'forks', 'revisions', 'comments'])
 | |
|             ->where('name', $table)->first();
 | |
|         if ($tableModel === null) abort(404, "No such table.");
 | |
| 
 | |
|         // option to show other revisions
 | |
|         if ($input->has('rev')) {
 | |
|             $rev = (int)$input->rev;
 | |
|             $revision = $tableModel->revisions()->orderBy('created_at')->skip($rev - 1)->first();
 | |
|             if ($revision === null) abort(404, "No such revision");
 | |
|         } else {
 | |
|             $revision = $tableModel->revision;
 | |
|         }
 | |
| 
 | |
|         $this->countTableVisit($request, $tableModel);
 | |
| 
 | |
|         $columns = Column::columnsFromJson($revision->columns);
 | |
| 
 | |
|         $rows = $revision->rowsData($columns)->paginate(25, []);
 | |
| 
 | |
|         return view('table.view', [
 | |
|             'table' => $tableModel,
 | |
|             'revision' => $revision,
 | |
|             'proposals_count' => $tableModel->proposals()->unmerged($tableModel)->count(),
 | |
|             'columns' => $columns,
 | |
|             'rows' => $rows,
 | |
|         ]);
 | |
|     }
 | |
| 
 | |
|     public function delete(Request $request, User $user, string $table)
 | |
|     {
 | |
|         /** @var Table $tableModel */
 | |
|         $tableModel = $user->tables()->where('name', $table)->first();
 | |
|         if ($tableModel === null) abort(404, "No such table.");
 | |
|         $this->authorize('delete', $tableModel);
 | |
| 
 | |
|         if ($request->get('table-name', null) !== $table) {
 | |
|             return $this->backWithErrors(['table-name' => 'Fill table name']);
 | |
|         }
 | |
| 
 | |
|         $tableModel->delete();
 | |
| 
 | |
|         return redirect(route('profile.view', $user->name));
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * SHow a form for creating a new table
 | |
|      *
 | |
|      * @return \Illuminate\Http\Response
 | |
|      */
 | |
|     public function create()
 | |
|     {
 | |
|         $exampleData =
 | |
|             "Mercenaria mercenaria,hard clam,40\n" .
 | |
|             "Magallana gigas,pacific oyster,30\n" .
 | |
|             "Patella vulgata,common limpet,20";
 | |
| 
 | |
|         $columns = Column::columnsFromJson([
 | |
|             // fake 'id' to satisfy the check in Column constructor
 | |
|             ['id' => 1, 'name' => 'latin',    'type' => 'string', 'title' => 'Latin Name'],
 | |
|             ['id' => 2, 'name' => 'common',   'type' => 'string', 'title' => 'Common Name'],
 | |
|             ['id' => 3, 'name' => 'lifespan', 'type' => 'int',    'title' => 'Lifespan (years)']
 | |
|         ]);
 | |
| 
 | |
|         return view('table.create', [
 | |
|             'exampleColumns' => '',
 | |
|             'columns' => $columns,
 | |
|             'exampleData' => $exampleData,
 | |
|         ]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Show table settings edit form
 | |
|      */
 | |
|     public function settings(Request $request, User $user, string $table)
 | |
|     {
 | |
|         /** @var Table $tableModel */
 | |
|         $tableModel = $user->tables()->where('name', $table)->first();
 | |
|         if ($tableModel === null) abort(404, "No such table.");
 | |
|         $this->authorize('edit', $tableModel);
 | |
| 
 | |
|         return view('table.conf', [
 | |
|             'table' => $tableModel,
 | |
|         ]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Store modified table settings
 | |
|      */
 | |
|     public function storeSettings(Request $request, User $user, string $table)
 | |
|     {
 | |
|         /** @var Table $tableModel */
 | |
|         $tableModel = $user->tables()->where('name', $table)->first();
 | |
|         if ($tableModel === null) abort(404, "No such table.");
 | |
|         $this->authorize('edit', $tableModel);
 | |
| 
 | |
|         $input = $this->validate($request, [
 | |
|             'name' => [
 | |
|                 'required',
 | |
|                 VALI_NAME,
 | |
|                 Rule::unique('tables')->ignoreModel($tableModel),
 | |
|             ],
 | |
|             'title' => ['required', VALI_LINE],
 | |
|             'description' => ['nullable', VALI_TEXT],
 | |
|             'license' => ['nullable', VALI_TEXT],
 | |
|             'origin' => ['nullable', VALI_TEXT],
 | |
|         ]);
 | |
| 
 | |
|         $tableModel->fill($input->all());
 | |
|         $tableModel->save();
 | |
| 
 | |
|         flash()->success('Table settings saved');
 | |
| 
 | |
|         return redirect($tableModel->viewRoute); // the route now changed
 | |
|     }
 | |
| 
 | |
|     public function storeNew(Request $request)
 | |
|     {
 | |
|         /** @var User $u */
 | |
|         $u = \Auth::user();
 | |
| 
 | |
|         $input = $this->validate($request, [
 | |
|             'name' => [
 | |
|                 'required',
 | |
|                 VALI_NAME,
 | |
|                 Rule::unique('tables'),
 | |
|             ],
 | |
|             'title' => ['required', VALI_LINE],
 | |
|             'description' => ['nullable', VALI_TEXT],
 | |
|             'license' => ['nullable', VALI_TEXT],
 | |
|             'origin' => ['nullable', VALI_TEXT],
 | |
|             'columns' => 'required|json',
 | |
|             'data' => 'string|nullable',
 | |
|         ]);
 | |
| 
 | |
|         // Check if table name is unique for user
 | |
|         if ($u->hasTable($input->name)) {
 | |
|             return $this->backWithErrors([
 | |
|                 'name' => "A table called \"$input->name\" already exists in your account.",
 | |
|             ]);
 | |
|         }
 | |
| 
 | |
|         // --- COLUMNS ---
 | |
|         // Parse and validate the columns specification
 | |
|         /** @var Column[] $columns */
 | |
|         $columns = [];
 | |
|         $col_names = []; // for checking duplicates
 | |
|         $colsArray = fromJSON($input->columns);
 | |
| 
 | |
|         // prevent griefing via long list of columns
 | |
|         if (count($colsArray) > 100) return $this->backWithErrors(['columns' => "Too many columns"]);
 | |
| 
 | |
|         foreach ($colsArray as $colObj) {
 | |
|             // ensure column has a title
 | |
|             if (!isset($colObj->title)) {
 | |
|                 $colObj->title = $colObj->name;
 | |
|             }
 | |
| 
 | |
|             try {
 | |
|                 if (in_array($colObj->name, $col_names)) {
 | |
|                     return $this->backWithErrors(['columns' => "Duplicate column: $colObj->name"]);
 | |
|                 }
 | |
| 
 | |
|                 $col_names[] = $colObj->name;
 | |
|                 $columns[] = new Column($colObj);
 | |
|             } catch (\Exception $e) {
 | |
|                 // validation errors from the Column constructor
 | |
|                 return $this->backWithErrors(['columns' => $e->getMessage()]);
 | |
|             }
 | |
|         }
 | |
|         if (count($columns) == 0) {
 | |
|             return $this->backWithErrors(['columns' => "Define at least one column"]);
 | |
|         }
 | |
| 
 | |
|         // Now assign column IDs
 | |
|         $columnNumerator = new ColumnNumerator(count($columns));
 | |
|         foreach ($columns as $column) {
 | |
|             $column->setID($columnNumerator->next());
 | |
|         }
 | |
| 
 | |
|         // --- DATA ---
 | |
|         $dataCsvLines = Utils::csvToArray($input->data);
 | |
| 
 | |
|         // Preparing data to insert into the Rows table
 | |
|         $rowsToInsert = null;
 | |
|         $rowNumerator = null;
 | |
|         try {
 | |
|             $rowsToInsert = (new Changeset)->csvToRowsArray($columns, $dataCsvLines, true, false)->all();
 | |
|         } catch (\Exception $e) {
 | |
|             return $this->backWithErrors(['data' => $e->getMessage()]);
 | |
|         }
 | |
| 
 | |
|         // --- STORE TO DB ---
 | |
| 
 | |
|         /* Fields for the new Revision instance */
 | |
|         $revisionFields = [
 | |
|             'note' => "Initial revision of table $u->name/$input->name",
 | |
|             'columns' => $columns,
 | |
|             'row_count' => count($rowsToInsert),
 | |
|         ];
 | |
| 
 | |
|         /* Fields for the new Table instance */
 | |
|         $tableFields = [
 | |
|             'owner_id' => $u->id,
 | |
|             'name' => $input->name,
 | |
|             'title' => $input->title,
 | |
|             'description' => $input->description,
 | |
|             'license' => $input->license,
 | |
|             'origin' => $input->origin,
 | |
|         ];
 | |
| 
 | |
|         /** @var Table $table */
 | |
|         $table = null;
 | |
|         \DB::transaction(function () use ($revisionFields, $tableFields, $rowsToInsert, &$table) {
 | |
|             $revision = Revision::create($revisionFields);
 | |
| 
 | |
|             $tableFields['revision_id'] = $revision->id; // current revision (not-null constraint on this FK)
 | |
|             $table = Table::create($tableFields);
 | |
| 
 | |
|             // Attach the revision to the table, set as current
 | |
|             $table->revisions()->attach($revision);
 | |
|             $table->revision()->associate($revision);
 | |
| 
 | |
|             // Spawn rows, linked to the revision
 | |
|             $revision->rows()->createMany($rowsToInsert);
 | |
|         });
 | |
| 
 | |
|         return redirect($table->viewRoute);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Check unique visit, filter bots / scripts, and increment visits count.
 | |
|      *
 | |
|      * @param Request $request
 | |
|      * @param Table $table
 | |
|      */
 | |
|     private function countTableVisit(Request $request, Table $table)
 | |
|     {
 | |
|         $cookieName = "view_{$table->owner->name}_{$table->name}";
 | |
|         if (!$request->cookie($cookieName, false)) {
 | |
|             $ua = $request->userAgent();
 | |
|             // Filter out suspicious user agents
 | |
|             if (! str_contains(strtolower($ua), Controller::BOT_USER_AGENTS)) {
 | |
|                 $table->countVisit();
 | |
|                 \Cookie::queue($cookieName, true, 24*60); // in minutes
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Simple export via a preset
 | |
|      *
 | |
|      * @param Request $request
 | |
|      * @param User $user
 | |
|      * @param string $table
 | |
|      */
 | |
|     public function export(Request $request, User $user, string $table)
 | |
|     {
 | |
|         /** @var Table $tableModel */
 | |
|         $tableModel = $user->tables()->where('name', $table)->first();
 | |
|         if ($tableModel === null) abort(404, "No such table.");
 | |
| 
 | |
|         $exporter = null;
 | |
| 
 | |
|         switch ($request->get('format')) {
 | |
|             case 'json':
 | |
|                 $exporter = new JsonExporter($tableModel);
 | |
|                 break;
 | |
| 
 | |
|             case 'csv':
 | |
|                 $exporter = new CsvExporter($tableModel);
 | |
|                 break;
 | |
| 
 | |
|             case 'csv-tab':
 | |
|                 $exporter = (new CsvExporter($tableModel))->withDelimiter("\t");
 | |
|                 break;
 | |
| 
 | |
|             case 'c':
 | |
|                 $exporter = new CStructArrayExporter($tableModel);
 | |
|                 break;
 | |
| 
 | |
|             case 'xmacro':
 | |
|                 $noq = explode(',', $request->input('noq',''));
 | |
|                 $exporter = (new CXMacroExporter($tableModel))->noQuotesAround($noq);
 | |
|                 break;
 | |
| 
 | |
|             case 'php':
 | |
|                 $exporter = new PhpExporter($tableModel);
 | |
|                 break;
 | |
| 
 | |
|             case 'js':
 | |
|                 $exporter = (new JsonExporter($tableModel))->withJsWrapper();
 | |
|                 break;
 | |
| 
 | |
|             default:
 | |
|                 abort(400, "Unspecified or unknown format.");
 | |
|         }
 | |
| 
 | |
|         $dl = Utils::parseBool($request->get('dl', false));
 | |
|         $exporter->exportToBrowser($dl);
 | |
|     }
 | |
| }
 | |
| 
 |