From 1037f438ce89c48d3858cf917debdcc5986d9523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 29 Jul 2018 20:19:19 +0200 Subject: [PATCH] rudimentary export options --- app/Http/Controllers/TableController.php | 35 ++++- app/Tables/BaseExporter.php | 125 ++++++++++++++++++ app/Tables/CStructArrayExporter.php | 118 +++++++++++++++++ app/{Utils => Tables}/Column.php | 6 +- app/Tables/CsvExporter.php | 52 ++++++++ app/Tables/JsonExporter.php | 44 ++++++ public/fonts/fa-dtbl-1-preview.html | 113 +++++++++------- public/fonts/fa-dtbl-1.css | 49 +++---- public/fonts/fa-dtbl-1.eot | Bin 9156 -> 9364 bytes public/fonts/fa-dtbl-1.svg | 56 ++++---- public/fonts/fa-dtbl-1.ttf | Bin 8976 -> 9184 bytes public/fonts/fa-dtbl-1.woff2 | Bin 4540 -> 4656 bytes resources/views/table/_panel-export.blade.php | 22 +++ resources/views/table/view.blade.php | 4 +- routes/web.php | 1 + 15 files changed, 522 insertions(+), 103 deletions(-) create mode 100644 app/Tables/BaseExporter.php create mode 100644 app/Tables/CStructArrayExporter.php rename app/{Utils => Tables}/Column.php (96%) create mode 100644 app/Tables/CsvExporter.php create mode 100644 app/Tables/JsonExporter.php create mode 100644 resources/views/table/_panel-export.blade.php diff --git a/app/Http/Controllers/TableController.php b/app/Http/Controllers/TableController.php index 9de980e..535554f 100644 --- a/app/Http/Controllers/TableController.php +++ b/app/Http/Controllers/TableController.php @@ -6,10 +6,14 @@ use App\Models\Revision; use App\Models\Row; use App\Models\Table; use App\Models\User; -use App\Utils\Column; +use App\Tables\Column; +use App\Tables\CStructArrayExporter; +use App\Tables\CsvExporter; +use App\Tables\JsonExporter; use Illuminate\Http\Request; use Illuminate\Validation\Rule; use MightyPork\Exceptions\NotApplicableException; +use MightyPork\Utils\Utils; class TableController extends Controller { @@ -258,4 +262,33 @@ class TableController extends Controller } } } + + 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 'c': + $exporter = new CStructArrayExporter($tableModel); + break; + + default: + abort(400, "Unspecified or unknown format."); + } + + $dl = Utils::parseBool($request->get('dl', false)); + $exporter->exportToBrowser($dl); + } } diff --git a/app/Tables/BaseExporter.php b/app/Tables/BaseExporter.php new file mode 100644 index 0000000..c5c949c --- /dev/null +++ b/app/Tables/BaseExporter.php @@ -0,0 +1,125 @@ +table = $table; + $this->columns = Column::columnsFromJson($table->revision->columns); + } + + /** + * @return string - mime type for the downloaded file + */ + protected abstract function getMimeType(); + + /** + * @return string - file extension for the downloaded file + */ + protected abstract function getFileExtension(); + + /** + * @return string - file name without extension, by default the table name + */ + protected function getFileBasename() + { + return $this->table->name; + } + + /** + * Generate a response that appears as a file download to the browser + * + * @param bool $download - force download, otherwise the browser may show it in the window + */ + public function exportToBrowser($download = true) + { + $this->wantDownload = $download; + + $mimeType = $this->getMimeType(); + $filename = $this->getFileBasename() . '.' . $this->getFileExtension(); + + ob_end_clean(); + + // Redirect output to a client’s web browser + header("Content-Type: $mimeType; charset=utf-8"); + + if ($download) { + header("Content-Disposition: attachment;filename=\"$filename\""); + } + + // Cache headers + header('Cache-Control: max-age=0'); + // IE9 + header('Cache-Control: max-age=1'); + // Other IE headers + header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified + header('Cache-Control: cache, must-revalidate'); // HTTP/1.1 + header('Pragma: no-cache'); // HTTP/1.0 + + $this->writeDocument(); + exit; + } + + /** + * Generator that produces PHP arrays for all rows, fetched from the DB in given chunks. + * + * @param int $chunkSize - size of one chunk + * @return \Generator|array[] + */ + protected function iterateRows($chunkSize = 1000) + { + $revision = $this->table->revision; + + $count = $revision->rows()->count(); + + $start = 0; + while ($start < $count) { + $rows = $revision->rows()->offset($start)->limit($chunkSize)->get(); + + foreach ($rows as $row) { + $data = json_decode($row->data); + + // column renaming, value formatting... + + yield $data; + } + + $start += $chunkSize; + } + } + + /** + * Write the document to stdout ('php://output') + */ + protected abstract function writeDocument(); +} diff --git a/app/Tables/CStructArrayExporter.php b/app/Tables/CStructArrayExporter.php new file mode 100644 index 0000000..d8055ca --- /dev/null +++ b/app/Tables/CStructArrayExporter.php @@ -0,0 +1,118 @@ +wantDownload ? 'text/x-csrc' : 'text/plain'; + } + + /** + * @return string - file extension for the downloaded file + */ + protected function getFileExtension() + { + return 'c'; + } + + private function cify($name) + { + $name = preg_replace('/[^a-z0-9_]/i', '_', $name); + if (ctype_digit($name[0])) { + $name = '_' . $name; + } + return $name; + } + + /** + * Write the document to stdout ('php://output') + */ + protected function writeDocument() + { + $fields = array_map(function (Column $c) { + $type = $c->type; + if ($type == 'string') { + $type = 'const char *'; + } + + $name = $this->cify($c->name); + + return (object)['name' => $c->name, 'type' => $c->type, 'ctype' => $type, 'cname' => $name]; + }, $this->columns); + + $ctablename = $this->cify($this->table->name); + + // preamble + echo "#include \n\n"; + + echo "struct " . $ctablename . " {\n"; + foreach ($fields as $field) { + echo " " . $field->ctype . " " . $field->cname . ";\n"; + } + echo "};\n\n"; + + // Now the table itself + echo "const struct ".$ctablename.' tbl_'.$ctablename."[] = {\n"; + + $first = true; + foreach ($this->iterateRows() as $row) { + if ($first) { + $first = false; + } else { + echo ",\n"; + } + + echo " {"; + + $firstf = true; + foreach ($fields as $field) { + if ($firstf) { + $firstf = false; + } else { + echo ", "; + } + + $val = 0; + switch ($field->type) { + case 'string': + $val = ""; + break; + + case 'bool': + $val = false; + break; + } + + if (isset($row->{$field->name})) { + $val = $row->{$field->name}; + } + + // export to C format + switch ($field->type) { + case 'string': + echo '"' . addcslashes($val, "\0..\37!@\@\177..\377") . '"'; + break; + + case 'bool': + echo (int)$val; + break; + + case 'int': + case 'float': + echo $val; + break; + } + } + echo "}"; + } + + echo "\n};\n"; + } +} diff --git a/app/Utils/Column.php b/app/Tables/Column.php similarity index 96% rename from app/Utils/Column.php rename to app/Tables/Column.php index 6c172dc..a8c41a0 100644 --- a/app/Utils/Column.php +++ b/app/Tables/Column.php @@ -1,7 +1,7 @@ wantDownload ? 'text/csv' : 'text/plain'; + } + + /** + * @return string - file extension for the downloaded file + */ + protected function getFileExtension() + { + return 'csv'; + } + + /** + * Write the document to stdout ('php://output') + */ + protected function writeDocument() + { + $handle = fopen('php://output', 'w'); + + $columnNames = array_map(function (Column $c) { + return $c->title; + }, $this->columns); + + fputcsv($handle, $columnNames); + + foreach ($this->iterateRows() as $row) { + $items = []; + foreach ($this->columns as $column) { + if (isset($row->{$column->name})) { + $items[] = $row->{$column->name}; + } else { + $items[] = null; + } + } + fputcsv($handle, $items); + } + + fclose($handle); + } +} diff --git a/app/Tables/JsonExporter.php b/app/Tables/JsonExporter.php new file mode 100644 index 0000000..f95bbfe --- /dev/null +++ b/app/Tables/JsonExporter.php @@ -0,0 +1,44 @@ +iterateRows() as $row) { + if ($first) { + $first = false; + } else { + echo ",\n"; + } + echo json_encode($row, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); + } + + echo "\n]\n"; + } +} diff --git a/public/fonts/fa-dtbl-1-preview.html b/public/fonts/fa-dtbl-1-preview.html index 237ed4b..c171c3a 100644 --- a/public/fonts/fa-dtbl-1-preview.html +++ b/public/fonts/fa-dtbl-1-preview.html @@ -165,6 +165,7 @@ .fa-calendar:before, .fa-code-fork:before, .fa-comment:before, +.fa-download:before, .fa-eye:before, .fa-facebook-square:before, .fa-floppy-o:before, @@ -207,30 +208,31 @@ .fa-calendar:before { content: "\f101"; } .fa-code-fork:before { content: "\f102"; } .fa-comment:before { content: "\f103"; } -.fa-eye:before { content: "\f104"; } -.fa-facebook-square:before { content: "\f105"; } -.fa-floppy-o:before { content: "\f106"; } -.fa-github:before { content: "\f107"; } -.fa-globe:before { content: "\f108"; } -.fa-google:before { content: "\f109"; } -.fa-history:before { content: "\f10a"; } -.fa-home:before { content: "\f10b"; } -.fa-inbox:before { content: "\f10c"; } -.fa-key-modern:before { content: "\f10d"; } -.fa-link:before { content: "\f10e"; } -.fa-pencil:before { content: "\f10f"; } -.fa-question-circle:before { content: "\f110"; } -.fa-sign-in:before { content: "\f111"; } -.fa-sign-out:before { content: "\f112"; } -.fa-star:before { content: "\f113"; } -.fa-star-o:before { content: "\f114"; } -.fa-table:before { content: "\f115"; } -.fa-th-list:before { content: "\f116"; } -.fa-user:before { content: "\f117"; } -.fa-user-circle-o:before { content: "\f118"; } -.fa-user-plus:before { content: "\f119"; } -.fa-users:before { content: "\f11a"; } -.fa-wrench:before { content: "\f11b"; } +.fa-download:before { content: "\f104"; } +.fa-eye:before { content: "\f105"; } +.fa-facebook-square:before { content: "\f106"; } +.fa-floppy-o:before { content: "\f107"; } +.fa-github:before { content: "\f108"; } +.fa-globe:before { content: "\f109"; } +.fa-google:before { content: "\f10a"; } +.fa-history:before { content: "\f10b"; } +.fa-home:before { content: "\f10c"; } +.fa-inbox:before { content: "\f10d"; } +.fa-key-modern:before { content: "\f10e"; } +.fa-link:before { content: "\f10f"; } +.fa-pencil:before { content: "\f110"; } +.fa-question-circle:before { content: "\f111"; } +.fa-sign-in:before { content: "\f112"; } +.fa-sign-out:before { content: "\f113"; } +.fa-star:before { content: "\f114"; } +.fa-star-o:before { content: "\f115"; } +.fa-table:before { content: "\f116"; } +.fa-th-list:before { content: "\f117"; } +.fa-user:before { content: "\f118"; } +.fa-user-circle-o:before { content: "\f119"; } +.fa-user-plus:before { content: "\f11a"; } +.fa-users:before { content: "\f11b"; } +.fa-wrench:before { content: "\f11c"; } @@ -246,7 +248,7 @@
-

fa-dtbl-1 contains 28 glyphs:

+

fa-dtbl-1 contains 29 glyphs:

Toggle Preview Characters
@@ -304,6 +306,19 @@
+
+
+ PpPpPpPpPpPpPpPpPpPp +
+
+ 12141618212436486072 +
+
+ + +
+
+
PpPpPpPpPpPpPpPpPpPp @@ -313,7 +328,7 @@
- +
@@ -326,7 +341,7 @@
- +
@@ -340,7 +355,7 @@
- +
@@ -353,7 +368,7 @@
- +
@@ -366,7 +381,7 @@
- +
@@ -379,7 +394,7 @@
- +
@@ -392,7 +407,7 @@
- +
@@ -405,7 +420,7 @@
- +
@@ -418,7 +433,7 @@
- +
@@ -431,7 +446,7 @@
- +
@@ -444,7 +459,7 @@
- +
@@ -457,7 +472,7 @@
- +
@@ -470,7 +485,7 @@
- +
@@ -483,7 +498,7 @@
- +
@@ -496,7 +511,7 @@
- +
@@ -509,7 +524,7 @@
- +
@@ -522,7 +537,7 @@
- +
@@ -535,7 +550,7 @@
- +
@@ -548,7 +563,7 @@
- +
@@ -561,7 +576,7 @@
- +
@@ -574,7 +589,7 @@
- +
@@ -587,7 +602,7 @@
- +
@@ -600,7 +615,7 @@
- +
@@ -613,7 +628,7 @@
- +
diff --git a/public/fonts/fa-dtbl-1.css b/public/fonts/fa-dtbl-1.css index f9433b7..134b96f 100644 --- a/public/fonts/fa-dtbl-1.css +++ b/public/fonts/fa-dtbl-1.css @@ -42,27 +42,28 @@ .fa-calendar::before { content: "\f101"; } .fa-code-fork::before { content: "\f102"; } .fa-comment::before { content: "\f103"; } -.fa-eye::before { content: "\f104"; } -.fa-facebook-square::before { content: "\f105"; } -.fa-floppy-o::before, .fa-save::before { content: "\f106"; } -.fa-github::before { content: "\f107"; } -.fa-globe::before { content: "\f108"; } -.fa-google::before { content: "\f109"; } -.fa-history::before { content: "\f10a"; } -.fa-home::before { content: "\f10b"; } -.fa-inbox::before { content: "\f10c"; } -.fa-key-modern::before { content: "\f10d"; } -.fa-link::before { content: "\f10e"; } -.fa-pencil::before { content: "\f10f"; } -.fa-question-circle::before { content: "\f110"; } -.fa-sign-in::before { content: "\f111"; } -.fa-sign-out::before { content: "\f112"; } -.fa-star::before { content: "\f113"; } -.fa-star-o::before { content: "\f114"; } -.fa-table::before { content: "\f115"; } -.fa-th-list::before { content: "\f116"; } -.fa-user::before { content: "\f117"; } -.fa-user-circle-o::before { content: "\f118"; } -.fa-user-plus::before { content: "\f119"; } -.fa-users::before { content: "\f11a"; } -.fa-wrench::before { content: "\f11b"; } +.fa-download::before { content: "\f104"; } +.fa-eye::before { content: "\f105"; } +.fa-facebook-square::before { content: "\f106"; } +.fa-floppy-o::before, .fa-save::before { content: "\f107"; } +.fa-github::before { content: "\f108"; } +.fa-globe::before { content: "\f109"; } +.fa-google::before { content: "\f10a"; } +.fa-history::before { content: "\f10b"; } +.fa-home::before { content: "\f10c"; } +.fa-inbox::before { content: "\f10d"; } +.fa-key-modern::before { content: "\f10e"; } +.fa-link::before { content: "\f10f"; } +.fa-pencil::before { content: "\f110"; } +.fa-question-circle::before { content: "\f111"; } +.fa-sign-in::before { content: "\f112"; } +.fa-sign-out::before { content: "\f113"; } +.fa-star::before { content: "\f114"; } +.fa-star-o::before { content: "\f115"; } +.fa-table::before { content: "\f116"; } +.fa-th-list::before { content: "\f117"; } +.fa-user::before { content: "\f118"; } +.fa-user-circle-o::before { content: "\f119"; } +.fa-user-plus::before { content: "\f11a"; } +.fa-users::before { content: "\f11b"; } +.fa-wrench::before { content: "\f11c"; } diff --git a/public/fonts/fa-dtbl-1.eot b/public/fonts/fa-dtbl-1.eot index 39d233d6e3618a95874f7767cac96fa0af82f212..30831906aafe49935c8aa70ebcbe124b42646a3a 100644 GIT binary patch delta 638 zcmYLFPiRtc9RL2F&)<9MYJ2Le9&YnJ-P1`FGv{P8_6~z+mx7=TI<%K=6Q)yC)`CEx zD2br#*&zrP9d;<#AuEC;gzOr2>LMf^Vgwz#ba?iC(g?m^etiCYzdye1bM^DqdH|qT zv(B>KUAh`d zsIwbdG2icl1@leB{6^-+`trA%pHS}rh%4zeE#+}FUZK{-j-`<}TH|~;%Z)gY&X#sQ zlwU#+3H$`y&g7EX`sb_@2i!-znALXjP#}w_#}WIswCviG;zKLIcnJ5;=X1r_AfuIY!sqcEP4kvztgF^z~>#a9e4&VMS!{C9MP97Kly-k9X(jX}j7Yv&|lSjt% zsBp?8fTh#=2(v)E`b8$e(29PKc|g1k(K17(Us%8rvpi!9)&m-#kLXV>#1(kTFY&K! zPFvo#ZwGtVam`V8{201C)ID;tQDGmmW!9WaD-?jkwUclbPZCvR>h?qqUzgyfeo0nl zglO!Hbj%Z3AX3CbJRG5NkSMcA4wQ&zRFEQ8P@;+wCD>Sd@z5VN6px3ug0+$VQS1!T`tlZn+lSas3@YL nHrzJt1d;S@S!Kw${#Fj#_*CxtRwk#V^q%}tf9^kS|9|+u#4U zZeqbBe$oF73``b4F|XvZ5(NfpmV5>V<`$qlOL}5)!T^fCh8``A)fs6$K21j4nWVpaU2b@)C1XXBJOjWnhp^0ord}kY8N#$mRPf1_s3i zKs7!Lj6hXD{QvLjoALZMUm3XBfdUK+*PC0v!RY^Q|NY}&U<0Y-U|?ckn0$m$T~z_B zQ-M*Kfghv}Bn}h-sc~S?0|_v&u<1<}V6tbFo*ckrGkF42FQfEkIc8Hv!v##&m>pPz zSUXscuo}^OYm~JnuU9@ixmZP<3*k-x&Yx~o diff --git a/public/fonts/fa-dtbl-1.svg b/public/fonts/fa-dtbl-1.svg index dbd2c25..d2153ea 100644 --- a/public/fonts/fa-dtbl-1.svg +++ b/public/fonts/fa-dtbl-1.svg @@ -5,7 +5,7 @@ --> -Created by FontForge 20170805 at Sun Jul 29 16:55:50 2018 +Created by FontForge 20170805 at Sun Jul 29 20:18:03 2018 By ondra The Fork Awesome font is licensed under the SIL OFL 1.1 (http://scripts.sil.org/OFL). Fork Awesome is a fork based of off Font Awesome 4.7.0 by Dave Gandy. More info on licenses at https://forkawesome.github.io @@ -22,7 +22,7 @@ The Fork Awesome font is licensed under the SIL OFL 1.1 (http://scripts.sil.org/ bbox="-0.14014 -256.168 2048 1536.01" underline-thickness="89.6" underline-position="-179.2" - unicode-range="U+0020-F11B" + unicode-range="U+0020-F11C" /> - + - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/fonts/fa-dtbl-1.ttf b/public/fonts/fa-dtbl-1.ttf index e30cf85594c0d0e06bc932d125e2d7e5c139f5da..24830b531e66aa14f105ac91c097220181020f5d 100644 GIT binary patch delta 599 zcmXv~UuaTs6h7yw_g-~tU3FHXoAne zAA;6yJp_Y#+C#w}G9hS$^ye+YUg||?^bn(m-g@alJF^DoaQMFOeCPbmZzHyMt_lbM zgHQtlT#UsopI>jRyaMpOC)R!8(y3sgy|M#r^A z)JZokYvp3EN0Y#}OPF8I-O4O}yZwRmHh^m>o7PfJd*cOZ9m;Svo33g{9&lb7<{)A$ zo3E_Bc@BQU^axnb6_Q%!Q{F}c?h&5PYimU);XLUP!roOapMG54FaeAxw12i(C|CAX zZ4W?7QO{!kI681wf8xG26#WfWj%)y5?=<88D$|T_;xs4rJ4O90dT>&|igW1Jw=l}E zHT?_TM|UH`OyT4+0~o@Fr^b?LpB-a2*`J)km3Wq4;9r_;=AwDW0+x>Ty7i&;$H0|= z&frAjqv4UUYCKrQ)>r@zhTtTep-V%u#5$b8!)g~CtWT8WDIpX-Ego|Q=THnf(a9kz z`B9!GvabZ4!-5zzfgFZXN0dLR5{W4I`R>ftOdZ=wr2ASJ?W(r0t*efzf)GHE zT?j52A+UYm`5)-*w)#%;*CP~+Fz)H9c00B_<9=+pWZBhK8wp-JfujCaR2dx687W}q YQ-zzWxq_C`C!}}!FX_1D@I#yNzmiFeFaQ7m delta 390 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 presets: + + diff --git a/resources/views/table/view.blade.php b/resources/views/table/view.blade.php index 5f517f0..ec2ae45 100644 --- a/resources/views/table/view.blade.php +++ b/resources/views/table/view.blade.php @@ -35,14 +35,14 @@
- Export … coming soon + @include('table._panel-export')
{{-- End of tab panels wrapper --}} {{-- Right column with menu --}}