From 452a7d47c44217ab94ab717b86c9388f0c1dce47 Mon Sep 17 00:00:00 2001 From: Ramakant Gangwar Date: Tue, 8 Mar 2022 00:34:13 +0530 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20Add=20import=20Voyager=20Action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit +Console Command +Export DataTypeTemplate +Export AllDataTypesTemplate --- .github/workflows/php-cs-fixer.yml | 4 +- .github/workflows/run-tests.yml | 2 +- .gitignore | 1 + README.md | 4 +- composer.json | 8 +- config/voyager-import.php | 44 ++ renovate.json | 8 + src/Actions/ImportAction.php | 99 +++++ src/Console/Commands/AllDataTypesImport.php | 73 ++++ .../Commands/AllDataTypesTemplateExport.php | 73 ++++ src/Console/Commands/DataTypeImport.php | 94 +++++ .../Commands/DataTypeTemplateExport.php | 94 +++++ src/Exports/AllDataTypesTemplateExport.php | 56 +++ src/Exports/DataTypeTemplateExport.php | 391 ++++++++++++++++++ src/Imports/AllDataTypesImport.php | 56 +++ src/Imports/DataTypeImport.php | 380 +++++++++++++++++ src/VoyagerExportServiceProvider.php | 119 ++++++ src/helper.php | 17 + 18 files changed, 1516 insertions(+), 7 deletions(-) create mode 100644 src/Actions/ImportAction.php create mode 100644 src/Console/Commands/AllDataTypesImport.php create mode 100644 src/Console/Commands/AllDataTypesTemplateExport.php create mode 100644 src/Console/Commands/DataTypeImport.php create mode 100644 src/Console/Commands/DataTypeTemplateExport.php create mode 100644 src/Exports/AllDataTypesTemplateExport.php create mode 100644 src/Exports/DataTypeTemplateExport.php create mode 100644 src/Imports/AllDataTypesImport.php create mode 100644 src/Imports/DataTypeImport.php create mode 100644 src/VoyagerExportServiceProvider.php diff --git a/.github/workflows/php-cs-fixer.yml b/.github/workflows/php-cs-fixer.yml index e4e2f8d..15c7524 100644 --- a/.github/workflows/php-cs-fixer.yml +++ b/.github/workflows/php-cs-fixer.yml @@ -8,7 +8,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Fix style uses: docker://oskarstark/php-cs-fixer-ga @@ -21,7 +21,7 @@ jobs: id: extract_branch - name: Commit changes - uses: stefanzweifel/git-auto-commit-action@v2.3.0 + uses: stefanzweifel/git-auto-commit-action@v4.13.1 with: commit_message: Fix styling branch: ${{ steps.extract_branch.outputs.branch }} diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1dd5567..e4d7149 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -28,7 +28,7 @@ jobs: run: sudo apt-get update --fix-missing - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.gitignore b/.gitignore index 076d2cd..58d9fda 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ vendor/* .php-cs-fixer.cache +composer.lock diff --git a/README.md b/README.md index f1440f7..288e54f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This [Laravel](https://laravel.com/)/[Voyager](https://voyager.devdojo.com/) mod By 🐼 [Ramakant Gangwar](https://github.com/rxcod9). -[![Screenshot](https://raw.githubusercontent.com/rxcod9/joy-voyager-import/main/cover.jpg)](https://joy-voyager-import.herokuapp.com) +[![Screenshot](https://raw.githubusercontent.com/rxcod9/joy-voyager-import/main/cover.jpg)](https://joy-voyager.herokuapp.com/) [![Latest Version](https://img.shields.io/github/v/release/rxcod9/joy-voyager-import?style=flat-square)](https://github.com/rxcod9/joy-voyager-import/releases) ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/rxcod9/joy-voyager-import/run-tests?label=tests) @@ -44,7 +44,7 @@ In order to override views delivered by Voyager DataTable, copy contents from `` ## Working Example -You can try laravel demo here [https://joy-voyager-import.herokuapp.com/api/documentation](https://joy-voyager-import.herokuapp.com/api/documentation). +You can try laravel demo here [https://joy-voyager.herokuapp.com/admin/users](https://joy-voyager.herokuapp.com/admin/users). ## Documentation diff --git a/composer.json b/composer.json index 93d0db0..9de39bc 100644 --- a/composer.json +++ b/composer.json @@ -21,8 +21,9 @@ "require": { "php": "^7.3|^8.0", "illuminate/support": "^7|^8", - "joy/voyager-core": "dev-main", + "joy/voyager-core": "^1.0", "larapack/hooks": "^1.0.5", + "maatwebsite/excel": "^3.1", "tcg/voyager": "^1.4" }, "require-dev": { @@ -60,7 +61,10 @@ } }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "henzeb/composer-link": true + } }, "minimum-stability": "dev", "prefer-stable": true diff --git a/config/voyager-import.php b/config/voyager-import.php index b5cae89..d16ceb2 100644 --- a/config/voyager-import.php +++ b/config/voyager-import.php @@ -2,6 +2,29 @@ return [ + /* + * If enabled for voyager-import package. + */ + 'enabled' => env('VOYAGER_IMPORT_ENABLED', true), + + /* + | Here you can specify for which data type slugs import is enabled + | + | Supported: "*", or data type slugs "users", "roles" + | + */ + + 'allowed_slugs' => array_filter(explode(',', env('VOYAGER_IMPORT_ALLOWED_SLUGS', '*'))), + + /* + | Here you can specify for which data type slugs import is not allowed + | + | Supported: "*", or data type slugs "users", "roles" + | + */ + + 'not_allowed_slugs' => array_filter(explode(',', env('VOYAGER_IMPORT_NOT_ALLOWED_SLUGS', ''))), + /* * The config_key for voyager-import package. */ @@ -24,4 +47,25 @@ 'controllers' => [ 'namespace' => 'Joy\\VoyagerImport\\Http\\Controllers', ], + + /* + | The default import disk. + */ + 'disk' => env('VOYAGER_IMPORT_DISK', null), + + /* + | The default import readerType. + | + | Supported: "Xlsx", "Csv", "Csv", "Ods", "Xls", + | "Slk", "Xml", "Gnumeric", "Html", "Mpdf", "Dompdf", "Tcpdf" + */ + 'readerType' => env('VOYAGER_IMPORT_READER_TYPE', 'Xlsx'), + + /* + | The default import writerType. + | + | Supported: "Xlsx", "Csv", "Csv", "Ods", "Xls", + | "Slk", "Xml", "Gnumeric", "Html", "Mpdf", "Dompdf", "Tcpdf" + */ + 'writerType' => env('VOYAGER_IMPORT_WRITER_TYPE', 'Xlsx'), ]; diff --git a/renovate.json b/renovate.json index f45d8f1..3274dc4 100644 --- a/renovate.json +++ b/renovate.json @@ -1,4 +1,12 @@ { + "packageRules": [ + { + "matchPackagePatterns": [ + "*" + ], + "rangeStrategy": "replace" + } + ], "extends": [ "config:base" ] diff --git a/src/Actions/ImportAction.php b/src/Actions/ImportAction.php new file mode 100644 index 0000000..b92c0c5 --- /dev/null +++ b/src/Actions/ImportAction.php @@ -0,0 +1,99 @@ + 'btn btn-sm btn-primary', + ]; + } + + public function getDefaultRoute() + { + // return route('my.route'); + } + + public function shouldActionDisplayOnDataType() + { + return config('joy-voyager-import.enabled', true) !== false + && isInPatterns( + $this->dataType->slug, + config('joy-voyager-import.allowed_slugs', ['*']) + ) + && !isInPatterns( + $this->dataType->slug, + config('joy-voyager-import.not_allowed_slugs', []) + ); + } + + public function massAction($ids, $comingFrom) + { + // GET THE SLUG, ex. 'posts', 'pages', etc. + $slug = $this->getSlug(request()); + + // GET THE DataType based on the slug + $dataType = Voyager::model('DataType')->where('slug', '=', $slug)->first(); + + // Check permission + Gate::authorize('browse', app($dataType->model_name)); + + $readerType = $this->readerType ?? config('joy-voyager-import.readerType', Excel::XLSX); + $fileName = $this->fileName ?? ($dataType->slug . '.' . Str::lower($readerType)); + + return (new DataTypeImport( + $dataType, + array_filter($ids), + request()->all(), + ))->import( + $fileName, + $readerType + ); + } + + protected function getSlug(Request $request) + { + if (isset($this->slug)) { + $slug = $this->slug; + } else { + $slug = explode('.', $request->route()->getName())[1]; + } + + return $slug; + } +} diff --git a/src/Console/Commands/AllDataTypesImport.php b/src/Console/Commands/AllDataTypesImport.php new file mode 100644 index 0000000..1c46152 --- /dev/null +++ b/src/Console/Commands/AllDataTypesImport.php @@ -0,0 +1,73 @@ +output->title('Starting import'); + $this->importAllDataTypes( + $this->option('disk'), + $this->option('readerType') + ); + $this->output->success('Import successful'); + } + + protected function importAllDataTypes( + string $disk = null, + string $readerType = Excel::XLSX + ) { + $path = 'public/imports/' . (($filePath ?? 'import-all') . '-' . date('YmdHis') . '.' . Str::lower($readerType)); + + $url = config('app.url') . Storage::disk($disk)->url($path); + + $this->output->info(sprintf( + 'Importing to >>' . PHP_EOL . 'path : %s' . PHP_EOL . 'url : %s', + storage_path($path), + $url + )); + + (new ImportsAllDataTypesImport())->withOutput($this->output)->import( + $path, + $disk, + $readerType + ); + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return [ + [ + 'disk', + 'd', + InputOption::VALUE_OPTIONAL, + 'The disk to where you want to import', + config('joy-voyager-import.disk') + ], + [ + 'readerType', + 'w', + InputOption::VALUE_OPTIONAL, + 'The readerType in which format you want to import', + config('joy-voyager-import.readerType', 'Xlsx') + ], + ]; + } +} diff --git a/src/Console/Commands/AllDataTypesTemplateExport.php b/src/Console/Commands/AllDataTypesTemplateExport.php new file mode 100644 index 0000000..144fefa --- /dev/null +++ b/src/Console/Commands/AllDataTypesTemplateExport.php @@ -0,0 +1,73 @@ +output->title('Starting export'); + $this->exportAllDataTypes( + $this->option('disk'), + $this->option('writerType') + ); + $this->output->success('Export successful'); + } + + protected function exportAllDataTypes( + string $disk = null, + string $writerType = Excel::XLSX + ) { + $path = 'public/imports/' . (($filePath ?? 'import-all') . '-' . date('YmdHis') . '.' . Str::lower($writerType)); + + $url = config('app.url') . Storage::disk($disk)->url($path); + + $this->output->info(sprintf( + 'Exporting to >>' . PHP_EOL . 'path : %s' . PHP_EOL . 'url : %s', + storage_path($path), + $url + )); + + (new ExportsAllDataTypesTemplateExport())->withOutput($this->output)->store( + $path, + $disk, + $writerType + ); + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return [ + [ + 'disk', + 'd', + InputOption::VALUE_OPTIONAL, + 'The disk to where you want to export', + config('joy-voyager-export.disk') + ], + [ + 'writerType', + 'w', + InputOption::VALUE_OPTIONAL, + 'The writerType in which format you want to export', + config('joy-voyager-export.writerType', 'Xlsx') + ], + ]; + } +} diff --git a/src/Console/Commands/DataTypeImport.php b/src/Console/Commands/DataTypeImport.php new file mode 100644 index 0000000..e405e8f --- /dev/null +++ b/src/Console/Commands/DataTypeImport.php @@ -0,0 +1,94 @@ +output->title('Starting import'); + + $dataType = Voyager::model('DataType')->whereSlug($this->argument('slug'))->firstOrFail(); + + $this->importDataType( + $dataType, + $this->option('disk'), + $this->option('readerType') + ); + + $this->output->success('Import successful'); + } + + protected function importDataType( + DataType $dataType, + string $disk = null, + string $readerType = Excel::XLSX + ) { + $path = 'public/imports/' . $dataType->slug . '-' . date('YmdHis') . '.' . Str::lower($readerType); + + $url = config('app.url') . Storage::disk($disk)->url($path); + + $this->output->info(sprintf( + 'Importing to >>' . PHP_EOL . 'path : %s' . PHP_EOL . 'url : %s', + storage_path($path), + $url + )); + + (new ImportsDataTypeImport($dataType))->withOutput($this->output)->import( + $path, + $disk, + $readerType + ); + } + + /** + * Get the console command arguments. + * + * @return array + */ + protected function getArguments() + { + return [ + ['slug', InputArgument::REQUIRED, 'The DataType slug which you want to import'], + ]; + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return [ + [ + 'disk', + 'd', + InputOption::VALUE_OPTIONAL, + 'The disk to where you want to import', + config('joy-voyager-import.disk') + ], + [ + 'readerType', + 'w', + InputOption::VALUE_OPTIONAL, + 'The readerType in which format you want to import', + config('joy-voyager-import.readerType', 'Xlsx') + ], + ]; + } +} diff --git a/src/Console/Commands/DataTypeTemplateExport.php b/src/Console/Commands/DataTypeTemplateExport.php new file mode 100644 index 0000000..e2001a1 --- /dev/null +++ b/src/Console/Commands/DataTypeTemplateExport.php @@ -0,0 +1,94 @@ +output->title('Starting export'); + + $dataType = Voyager::model('DataType')->whereSlug($this->argument('slug'))->firstOrFail(); + + $this->exportDataType( + $dataType, + $this->option('disk'), + $this->option('writerType') + ); + + $this->output->success('Export successful'); + } + + protected function exportDataType( + DataType $dataType, + string $disk = null, + string $writerType = Excel::XLSX + ) { + $path = 'public/imports/' . $dataType->slug . '-' . date('YmdHis') . '.' . Str::lower($writerType); + + $url = config('app.url') . Storage::disk($disk)->url($path); + + $this->output->info(sprintf( + 'Exporting to >>' . PHP_EOL . 'path : %s' . PHP_EOL . 'url : %s', + storage_path($path), + $url + )); + + (new ExportsDataTypeTemplateExport($dataType))->withOutput($this->output)->store( + $path, + $disk, + $writerType + ); + } + + /** + * Get the console command arguments. + * + * @return array + */ + protected function getArguments() + { + return [ + ['slug', InputArgument::REQUIRED, 'The DataType slug which you want to export'], + ]; + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return [ + [ + 'disk', + 'd', + InputOption::VALUE_OPTIONAL, + 'The disk to where you want to export', + config('joy-voyager-import.disk') + ], + [ + 'writerType', + 'w', + InputOption::VALUE_OPTIONAL, + 'The writerType in which format you want to export', + config('joy-voyager-import.writerType', 'Xlsx') + ], + ]; + } +} diff --git a/src/Exports/AllDataTypesTemplateExport.php b/src/Exports/AllDataTypesTemplateExport.php new file mode 100644 index 0000000..42b7ad9 --- /dev/null +++ b/src/Exports/AllDataTypesTemplateExport.php @@ -0,0 +1,56 @@ +get(); + + foreach ($dataTypes as $dataType) { + $sheets[] = new DataTypeExport($dataType); + } + + return $sheets; + } + + /** + * @param OutputStyle $output + * @return $this + */ + public function withOutput(OutputStyle $output) + { + $this->output = $output; + + return $this; + } + + /** + * @return OutputStyle + */ + public function getConsoleOutput(): OutputStyle + { + if (!$this->output instanceof OutputStyle) { + $this->output = new OutputStyle(new StringInput(''), new NullOutput()); + } + + return $this->output; + } +} diff --git a/src/Exports/DataTypeTemplateExport.php b/src/Exports/DataTypeTemplateExport.php new file mode 100644 index 0000000..93cde08 --- /dev/null +++ b/src/Exports/DataTypeTemplateExport.php @@ -0,0 +1,391 @@ +dataType = $dataType; + $this->ids = $ids; + $this->input = $input; + $this->dataTypeContent = app($this->dataType->model_name); + } + + public function query() + { + $orderBy = Arr::get( + $this->input, + 'columns.' . Arr::get($this->input, 'order.0.column', 0) . '.name', + $this->dataType->order_column + ); + $sortOrder = Arr::get($this->input, 'order.0.dir', $this->dataType->order_direction); + $usesSoftDeletes = false; + $showSoftDeleted = false; + + // Next Get or Paginate the actual content from the MODEL that corresponds to the slug DataType + if (strlen($this->dataType->model_name) != 0) { + $model = app($this->dataType->model_name); + + $query = $model::select($this->dataType->name . '.*'); + + if ($this->dataType->scope && $this->dataType->scope != '' && method_exists($model, 'scope' . ucfirst($this->dataType->scope))) { + $query->{$this->dataType->scope}(); + } + + // Use withTrashed() if model uses SoftDeletes and if toggle is selected + if ($model && in_array(SoftDeletes::class, class_uses_recursive($model)) && Auth::user()->can('delete', app($this->dataType->model_name))) { + $usesSoftDeletes = true; + + if (Arr::get($this->input, 'showSoftDeleted')) { + $showSoftDeleted = true; + $query->withTrashed(); + } + } + + // If a column has a relationship associated with it, we do not want to show that field + $this->removeRelationshipField($this->dataType, 'browse'); + + // Export only selected + if ($this->ids) { + $query->whereKey($this->ids); + } + + $query->limit(1); + + $row = $this->dataType->rows->where('field', $orderBy)->firstWhere('type', 'relationship'); + if ($orderBy && (in_array($orderBy, $this->dataType->fields()) || !empty($row))) { + $querySortOrder = (!empty($sortOrder)) ? $sortOrder : 'desc'; + if (!empty($row)) { + $query->select([ + $this->dataType->name . '.*', + 'joined.' . $row->details->label . ' as ' . $orderBy, + ])->leftJoin( + $row->details->table . ' as joined', + $this->dataType->name . '.' . $row->details->column, + 'joined.' . $row->details->key + ); + } + + $query->orderBy($orderBy, $querySortOrder); + } elseif ($model->timestamps) { + $query->latest($model::CREATED_AT); + } else { + $query->orderBy($model->getKeyName(), 'DESC'); + } + } else { + // If Model doesn't exist, get data from table name + $query = DB::table($this->dataType->name); + + // Export only selected + if ($this->ids) { + $query->whereKey($this->ids); + } + + $query->limit(1); + $model = false; + } + + return $query; + } + + public function headings(): array + { + // If a column has a relationship associated with it, we do not want to show that field + $this->removeRelationshipField($this->dataType, 'browse'); + + $headings = []; + $headings[] = '#'; // index column + foreach ($this->dataType->browseRows as $row) { + $headings[] = $row->getTranslatedAttribute('display_name'); + } + return $headings; + } + + /** + * @var Model $data + */ + public function map($data): array + { + // If a column has a relationship associated with it, we do not want to show that field + $this->removeRelationshipField($this->dataType, 'browse'); + + $columns = []; + $columns[] = $data->id; + foreach ($this->dataType->browseRows as $row) { + $column = null; + if ($data->{$row->field . '_export'}) { + $data->{$row->field} = $data->{$row->field . '_export'}; + } elseif ($data->{$row->field . '_browse'}) { + $data->{$row->field} = $data->{$row->field . '_browse'}; + } + + if (isset($row->details->view)) { + $column = trim(strip_tags((string) view($row->details->view, [ + 'row' => $row, + 'dataType' => $this->dataType, + 'data' => $data, + 'dataTypeContent' => $this->dataTypeContent, + 'content' => $data->{$row->field}, + 'action' => 'browse', + 'view' => 'browse', + 'options' => $row->details + ]))); + } elseif ($row->type == 'image') { + if (!filter_var($data->{$row->field}, FILTER_VALIDATE_URL)) { + $column = Voyager::image($data->{$row->field}); + } else { + $column = $data->{$row->field}; + } + } elseif ($row->type == 'relationship') { + $column = trim(strip_tags((string) view('voyager::formfields.relationship', [ + 'view' => 'browse', + 'row' => $row, + 'dataType' => $this->dataType, + 'data' => $data, + 'dataTypeContent' => $this->dataTypeContent, + 'content' => $data->{$row->field}, + 'action' => 'browse', + 'view' => 'browse', + 'options' => $row->details + ]))); + } elseif ($row->type == 'select_multiple') { + $values = []; + if (property_exists($row->details, 'relationship')) { + foreach ($data->{$row->field} as $item) { + $values[] = $item->{$row->field}; + } + } elseif (property_exists($row->details, 'options')) { + if (!empty(json_decode($data->{$row->field}))) { + foreach (json_decode($data->{$row->field}) as $item) { + if (@$row->details->options->{$item}) { + $values[] = $row->details->options->{$item}; + } + } + } else { + $values[] = __('voyager::generic.none'); + } + } + $column = implode(', ', $values); + } elseif ($row->type == 'multiple_checkbox' && property_exists($row->details, 'options')) { + $values = []; + if (@count(json_decode($data->{$row->field})) > 0) { + foreach (json_decode($data->{$row->field}) as $item) { + if (@$row->details->options->{$item}) { + $values[] = $row->details->options->{$item}; + } + } + } else { + $values[] = __('voyager::generic.none'); + } + $column = implode(', ', $values); + } elseif (($row->type == 'select_dropdown' || $row->type == 'radio_btn') && property_exists($row->details, 'options')) { + $column = $row->details->options->{$data->{$row->field}} ?? ''; + } elseif ($row->type == 'date' || $row->type == 'timestamp') { + if (property_exists($row->details, 'format') && !is_null($data->{$row->field})) { + $column = \Carbon\Carbon::parse($data->{$row->field})->isoFormat($row->details->format); + } else { + $column = $data->{$row->field}; + } + } elseif ($row->type == 'checkbox') { + if (property_exists($row->details, 'on') && property_exists($row->details, 'off')) { + if ($data->{$row->field}) { + $column = $row->details->on; + } else { + $column = $row->details->off; + } + } else { + $column = $data->{$row->field}; + } + } elseif ($row->type == 'color') { + $column = $data->{$row->field}; + } elseif ($row->type == 'text') { + // view('voyager::multilingual.input-hidden-bread-browse'); + // $column = mb_strlen( $data->{$row->field} ) > 200 ? mb_substr($data->{$row->field}, 0, 200) . ' ...' : $data->{$row->field}; + $column = $data->{$row->field}; + } elseif ($row->type == 'text_area') { + // view('voyager::multilingual.input-hidden-bread-browse'); + // $column = mb_strlen( $data->{$row->field} ) > 200 ? mb_substr($data->{$row->field}, 0, 200) . ' ...' : $data->{$row->field}; + $column = $data->{$row->field}; + } elseif ($row->type == 'file' && !empty($data->{$row->field})) { + $values = []; + // view('voyager::multilingual.input-hidden-bread-browse'); + if (json_decode($data->{$row->field}) !== null) { + foreach (json_decode($data->{$row->field}) as $file) { + $values[] = Storage::disk(config('voyager.storage.disk'))->url($file->download_link) ?: ''; + } + } else { + $values[] = Storage::disk(config('voyager.storage.disk'))->url($data->{$row->field}); + } + $column = implode(', ', $values); + } elseif ($row->type == 'rich_text_box') { + // view('voyager::multilingual.input-hidden-bread-browse'); + // $column = mb_strlen( strip_tags($data->{$row->field}, '') ) > 200 ? mb_substr(strip_tags($data->{$row->field}, ''), 0, 200) . ' ...' : strip_tags($data->{$row->field}, ''); + $column = strip_tags($data->{$row->field}, ''); + } elseif ($row->type == 'coordinates') { + $url = 'https://maps.googleapis.com/maps/api/staticmap?zoom=' . config('voyager.googlemaps.zoom') . '&size=400x100&maptype=roadmap&'; + foreach ($data->getCoordinates() as $point) { + $url .= 'markers=color:red%7C' . $point['lat'] . ',' . $point['lng'] . '¢er=' . $point['lat'] . ',' . $point['lng']; + } + $url .= '&key=' . config('voyager.googlemaps.key'); + // $column = view('voyager::partials.coordinates-static-image'); + $column = $url; + } elseif ($row->type == 'multiple_images') { + $values = []; + $images = json_decode($data->{$row->field}); + if ($images) { + $images = array_slice($images, 0, 3); + foreach ($images as $image) { + if (!filter_var($image, FILTER_VALIDATE_URL)) { + $values[] = Voyager::image($image); + } else { + $values[] = $image; + } + } + } + $column = implode(', ', $values); + } elseif ($row->type == 'media_picker') { + $values = []; + + if (is_array($data->{$row->field})) { + $files = $data->{$row->field}; + } else { + $files = json_decode($data->{$row->field}); + } + + if ($files) { + if (property_exists($row->details, 'show_as_images') && $row->details->show_as_images) { + foreach (array_slice($files, 0, 3) as $file) { + if (!filter_var($file, FILTER_VALIDATE_URL)) { + $values[] = Voyager::image($file); + } else { + $values[] = $file; + } + } + } else { + foreach (array_slice($files, 0, 3) as $file) { + $values[] = $file; + } + } + if (count($files) > 3) { + $values[] = __('voyager::media.files_more', ['count' => (count($files) - 3)]); + } + } elseif (is_array($files) && count($files) == 0) { + $values[] = trans_choice('voyager::media.files', 0); + } elseif ($data->{$row->field} != '') { + if (property_exists($row->details, 'show_as_images') && $row->details->show_as_images) { + if (!filter_var($data->{$row->field}, FILTER_VALIDATE_URL)) { + $values[] = Voyager::image($data->{$row->field}); + } else { + $values[] = $data->{$row->field}; + } + } else { + $values[] = $data->{$row->field}; + } + } else { + $values[] = trans_choice('voyager::media.files', 0); + } + $column = implode(', ', $values); + } else { + // view('voyager::multilingual.input-hidden-bread-browse'); + $values[] = $data->{$row->field}; + } + $columns[] = $column; + } + return $columns; + } + + /** + * @return string + */ + public function title(): string + { + return $this->dataType->getTranslatedAttribute('display_name_plural'); + } + + /** + * @param OutputStyle $output + * @return $this + */ + public function withOutput(OutputStyle $output) + { + $this->output = $output; + + return $this; + } + + /** + * @return OutputStyle + */ + public function getConsoleOutput(): OutputStyle + { + if (!$this->output instanceof OutputStyle) { + $this->output = new OutputStyle(new StringInput(''), new NullOutput()); + } + + return $this->output; + } +} diff --git a/src/Imports/AllDataTypesImport.php b/src/Imports/AllDataTypesImport.php new file mode 100644 index 0000000..1b40fb6 --- /dev/null +++ b/src/Imports/AllDataTypesImport.php @@ -0,0 +1,56 @@ +get(); + + foreach ($dataTypes as $dataType) { + $sheets[] = new DataTypeImport($dataType); + } + + return $sheets; + } + + /** + * @param OutputStyle $output + * @return $this + */ + public function withOutput(OutputStyle $output) + { + $this->output = $output; + + return $this; + } + + /** + * @return OutputStyle + */ + public function getConsoleOutput(): OutputStyle + { + if (!$this->output instanceof OutputStyle) { + $this->output = new OutputStyle(new StringInput(''), new NullOutput()); + } + + return $this->output; + } +} diff --git a/src/Imports/DataTypeImport.php b/src/Imports/DataTypeImport.php new file mode 100644 index 0000000..180efde --- /dev/null +++ b/src/Imports/DataTypeImport.php @@ -0,0 +1,380 @@ +dataType = $dataType; + $this->ids = $ids; + $this->input = $input; + $this->dataTypeContent = app($this->dataType->model_name); + } + + public function query() + { + $orderBy = Arr::get( + $this->input, + 'columns.' . Arr::get($this->input, 'order.0.column', 0) . '.name', + $this->dataType->order_column + ); + $sortOrder = Arr::get($this->input, 'order.0.dir', $this->dataType->order_direction); + $usesSoftDeletes = false; + $showSoftDeleted = false; + + // Next Get or Paginate the actual content from the MODEL that corresponds to the slug DataType + if (strlen($this->dataType->model_name) != 0) { + $model = app($this->dataType->model_name); + + $query = $model::select($this->dataType->name . '.*'); + + if ($this->dataType->scope && $this->dataType->scope != '' && method_exists($model, 'scope' . ucfirst($this->dataType->scope))) { + $query->{$this->dataType->scope}(); + } + + // Use withTrashed() if model uses SoftDeletes and if toggle is selected + if ($model && in_array(SoftDeletes::class, class_uses_recursive($model)) && Auth::user()->can('delete', app($this->dataType->model_name))) { + $usesSoftDeletes = true; + + if (Arr::get($this->input, 'showSoftDeleted')) { + $showSoftDeleted = true; + $query->withTrashed(); + } + } + + // If a column has a relationship associated with it, we do not want to show that field + $this->removeRelationshipField($this->dataType, 'browse'); + + // Import only selected + if ($this->ids) { + $query->whereKey($this->ids); + } + + $row = $this->dataType->rows->where('field', $orderBy)->firstWhere('type', 'relationship'); + if ($orderBy && (in_array($orderBy, $this->dataType->fields()) || !empty($row))) { + $querySortOrder = (!empty($sortOrder)) ? $sortOrder : 'desc'; + if (!empty($row)) { + $query->select([ + $this->dataType->name . '.*', + 'joined.' . $row->details->label . ' as ' . $orderBy, + ])->leftJoin( + $row->details->table . ' as joined', + $this->dataType->name . '.' . $row->details->column, + 'joined.' . $row->details->key + ); + } + + $query->orderBy($orderBy, $querySortOrder); + } elseif ($model->timestamps) { + $query->latest($model::CREATED_AT); + } else { + $query->orderBy($model->getKeyName(), 'DESC'); + } + } else { + // If Model doesn't exist, get data from table name + $query = DB::table($this->dataType->name); + $model = false; + } + + return $query; + } + + public function headings(): array + { + // If a column has a relationship associated with it, we do not want to show that field + $this->removeRelationshipField($this->dataType, 'browse'); + + $headings = []; + $headings[] = '#'; // index column + foreach ($this->dataType->browseRows as $row) { + $headings[] = $row->getTranslatedAttribute('display_name'); + } + return $headings; + } + + /** + * @var Model $data + */ + public function map($data): array + { + // If a column has a relationship associated with it, we do not want to show that field + $this->removeRelationshipField($this->dataType, 'browse'); + + $columns = []; + $columns[] = $data->id; + foreach ($this->dataType->browseRows as $row) { + $column = null; + if ($data->{$row->field . '_browse'}) { + $data->{$row->field} = $data->{$row->field . '_browse'}; + } + + if (isset($row->details->view)) { + $column = trim(strip_tags((string) view($row->details->view, [ + 'row' => $row, + 'dataType' => $this->dataType, + 'data' => $data, + 'dataTypeContent' => $this->dataTypeContent, + 'content' => $data->{$row->field}, + 'action' => 'browse', + 'view' => 'browse', + 'options' => $row->details + ]))); + } elseif ($row->type == 'image') { + if (!filter_var($data->{$row->field}, FILTER_VALIDATE_URL)) { + $column = Voyager::image($data->{$row->field}); + } else { + $column = $data->{$row->field}; + } + } elseif ($row->type == 'relationship') { + $column = trim(strip_tags((string) view('voyager::formfields.relationship', [ + 'view' => 'browse', + 'row' => $row, + 'dataType' => $this->dataType, + 'data' => $data, + 'dataTypeContent' => $this->dataTypeContent, + 'content' => $data->{$row->field}, + 'action' => 'browse', + 'view' => 'browse', + 'options' => $row->details + ]))); + } elseif ($row->type == 'select_multiple') { + $values = []; + if (property_exists($row->details, 'relationship')) { + foreach ($data->{$row->field} as $item) { + $values[] = $item->{$row->field}; + } + } elseif (property_exists($row->details, 'options')) { + if (!empty(json_decode($data->{$row->field}))) { + foreach (json_decode($data->{$row->field}) as $item) { + if (@$row->details->options->{$item}) { + $values[] = $row->details->options->{$item}; + } + } + } else { + $values[] = __('voyager::generic.none'); + } + } + $column = implode(', ', $values); + } elseif ($row->type == 'multiple_checkbox' && property_exists($row->details, 'options')) { + $values = []; + if (@count(json_decode($data->{$row->field})) > 0) { + foreach (json_decode($data->{$row->field}) as $item) { + if (@$row->details->options->{$item}) { + $values[] = $row->details->options->{$item}; + } + } + } else { + $values[] = __('voyager::generic.none'); + } + $column = implode(', ', $values); + } elseif (($row->type == 'select_dropdown' || $row->type == 'radio_btn') && property_exists($row->details, 'options')) { + $column = $row->details->options->{$data->{$row->field}} ?? ''; + } elseif ($row->type == 'date' || $row->type == 'timestamp') { + if (property_exists($row->details, 'format') && !is_null($data->{$row->field})) { + $column = \Carbon\Carbon::parse($data->{$row->field})->isoFormat($row->details->format); + } else { + $column = $data->{$row->field}; + } + } elseif ($row->type == 'checkbox') { + if (property_exists($row->details, 'on') && property_exists($row->details, 'off')) { + if ($data->{$row->field}) { + $column = $row->details->on; + } else { + $column = $row->details->off; + } + } else { + $column = $data->{$row->field}; + } + } elseif ($row->type == 'color') { + $column = $data->{$row->field}; + } elseif ($row->type == 'text') { + // view('voyager::multilingual.input-hidden-bread-browse'); + // $column = mb_strlen( $data->{$row->field} ) > 200 ? mb_substr($data->{$row->field}, 0, 200) . ' ...' : $data->{$row->field}; + $column = $data->{$row->field}; + } elseif ($row->type == 'text_area') { + // view('voyager::multilingual.input-hidden-bread-browse'); + // $column = mb_strlen( $data->{$row->field} ) > 200 ? mb_substr($data->{$row->field}, 0, 200) . ' ...' : $data->{$row->field}; + $column = $data->{$row->field}; + } elseif ($row->type == 'file' && !empty($data->{$row->field})) { + $values = []; + // view('voyager::multilingual.input-hidden-bread-browse'); + if (json_decode($data->{$row->field}) !== null) { + foreach (json_decode($data->{$row->field}) as $file) { + $values[] = Storage::disk(config('voyager.storage.disk'))->url($file->download_link) ?: ''; + } + } else { + $values[] = Storage::disk(config('voyager.storage.disk'))->url($data->{$row->field}); + } + $column = implode(', ', $values); + } elseif ($row->type == 'rich_text_box') { + // view('voyager::multilingual.input-hidden-bread-browse'); + // $column = mb_strlen( strip_tags($data->{$row->field}, '') ) > 200 ? mb_substr(strip_tags($data->{$row->field}, ''), 0, 200) . ' ...' : strip_tags($data->{$row->field}, ''); + $column = strip_tags($data->{$row->field}, ''); + } elseif ($row->type == 'coordinates') { + $url = 'https://maps.googleapis.com/maps/api/staticmap?zoom=' . config('voyager.googlemaps.zoom') . '&size=400x100&maptype=roadmap&'; + foreach ($data->getCoordinates() as $point) { + $url .= 'markers=color:red%7C' . $point['lat'] . ',' . $point['lng'] . '¢er=' . $point['lat'] . ',' . $point['lng']; + } + $url .= '&key=' . config('voyager.googlemaps.key'); + // $column = view('voyager::partials.coordinates-static-image'); + $column = $url; + } elseif ($row->type == 'multiple_images') { + $values = []; + $images = json_decode($data->{$row->field}); + if ($images) { + $images = array_slice($images, 0, 3); + foreach ($images as $image) { + if (!filter_var($image, FILTER_VALIDATE_URL)) { + $values[] = Voyager::image($image); + } else { + $values[] = $image; + } + } + } + $column = implode(', ', $values); + } elseif ($row->type == 'media_picker') { + $values = []; + + if (is_array($data->{$row->field})) { + $files = $data->{$row->field}; + } else { + $files = json_decode($data->{$row->field}); + } + + if ($files) { + if (property_exists($row->details, 'show_as_images') && $row->details->show_as_images) { + foreach (array_slice($files, 0, 3) as $file) { + if (!filter_var($file, FILTER_VALIDATE_URL)) { + $values[] = Voyager::image($file); + } else { + $values[] = $file; + } + } + } else { + foreach (array_slice($files, 0, 3) as $file) { + $values[] = $file; + } + } + if (count($files) > 3) { + $values[] = __('voyager::media.files_more', ['count' => (count($files) - 3)]); + } + } elseif (is_array($files) && count($files) == 0) { + $values[] = trans_choice('voyager::media.files', 0); + } elseif ($data->{$row->field} != '') { + if (property_exists($row->details, 'show_as_images') && $row->details->show_as_images) { + if (!filter_var($data->{$row->field}, FILTER_VALIDATE_URL)) { + $values[] = Voyager::image($data->{$row->field}); + } else { + $values[] = $data->{$row->field}; + } + } else { + $values[] = $data->{$row->field}; + } + } else { + $values[] = trans_choice('voyager::media.files', 0); + } + $column = implode(', ', $values); + } else { + // view('voyager::multilingual.input-hidden-bread-browse'); + $values[] = $data->{$row->field}; + } + $columns[] = $column; + } + return $columns; + } + + /** + * @return string + */ + public function title(): string + { + return $this->dataType->getTranslatedAttribute('display_name_plural'); + } + + /** + * @param OutputStyle $output + * @return $this + */ + public function withOutput(OutputStyle $output) + { + $this->output = $output; + + return $this; + } + + /** + * @return OutputStyle + */ + public function getConsoleOutput(): OutputStyle + { + if (!$this->output instanceof OutputStyle) { + $this->output = new OutputStyle(new StringInput(''), new NullOutput()); + } + + return $this->output; + } +} diff --git a/src/VoyagerExportServiceProvider.php b/src/VoyagerExportServiceProvider.php new file mode 100644 index 0000000..d9954a3 --- /dev/null +++ b/src/VoyagerExportServiceProvider.php @@ -0,0 +1,119 @@ + + * @copyright 2021 Copyright (c) Ramakant Gangwar (https://github.com/rxcod9) + * @license http://github.com/rxcod9/joy-voyager-import/blob/main/LICENSE New BSD License + * @link https://github.com/rxcod9/joy-voyager-import + */ +class VoyagerImportServiceProvider extends ServiceProvider +{ + /** + * Boot + * + * @return void + */ + public function boot() + { + Voyager::addAction(\Joy\VoyagerImport\Actions\ImportAction::class); + + $this->registerPublishables(); + + $this->loadViewsFrom(__DIR__ . '/../resources/views', 'joy-voyager-import'); + + $this->mapApiRoutes(); + + $this->mapWebRoutes(); + + $this->loadMigrationsFrom(__DIR__ . '/../database/migrations'); + + $this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'joy-voyager-import'); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web') + ->group(__DIR__ . '/../routes/web.php'); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::prefix(config('joy-voyager-import.route_prefix', 'api')) + ->middleware('api') + ->group(__DIR__ . '/../routes/api.php'); + } + + /** + * Register any application services. + * + * @return void + */ + public function register() + { + $this->mergeConfigFrom(__DIR__ . '/../config/voyager-import.php', 'joy-voyager-import'); + + if ($this->app->runningInConsole()) { + $this->registerCommands(); + } + } + + /** + * Register publishables. + * + * @return void + */ + protected function registerPublishables(): void + { + $this->publishes([ + __DIR__ . '/../config/voyager-import.php' => config_path('joy-voyager-import.php'), + ], 'config'); + + $this->publishes([ + __DIR__ . '/../resources/views' => resource_path('views/vendor/joy-voyager-import'), + ], 'views'); + + $this->publishes([ + __DIR__ . '/../resources/lang' => resource_path('lang/vendor/joy-voyager-import'), + ], 'translations'); + } + + protected function registerCommands(): void + { + $this->app->singleton('command.joy-voyager.import', function () { + return new DataTypeImport(); + }); + + $this->app->singleton('command.joy-voyager.import-all', function () { + return new AllDataTypesImport(); + }); + + $this->commands([ + 'command.joy-voyager.import', + 'command.joy-voyager.import-all' + ]); + } +} diff --git a/src/helper.php b/src/helper.php index 3e153b4..2b6d058 100644 --- a/src/helper.php +++ b/src/helper.php @@ -1,5 +1,7 @@