mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 09:35:49 +02:00
Added possibility to export Parts from part tables
This commit is contained in:
parent
3b36b2a4dc
commit
49944cda87
9 changed files with 161 additions and 8 deletions
|
@ -107,6 +107,13 @@ export default class extends DatatablesController {
|
|||
//Hide the select element (the tomselect button is the sibling of the select element)
|
||||
select_target.nextElementSibling.classList.add('d-none');
|
||||
}
|
||||
|
||||
//If the selected option has a data-turbo attribute, set it to the form
|
||||
if (selected_option.dataset.turbo) {
|
||||
this.element.dataset.turbo = selected_option.dataset.turbo;
|
||||
} else {
|
||||
this.element.dataset.turbo = true;
|
||||
}
|
||||
}
|
||||
|
||||
confirmDeletionAtSubmit(event) {
|
||||
|
|
63
src/Controller/PartImportExportController.php
Normal file
63
src/Controller/PartImportExportController.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Services\ImportExportSystem\EntityExporter;
|
||||
use App\Services\Parts\PartsTableActionHandler;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use InvalidArgumentException;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class PartImportExportController extends AbstractController
|
||||
{
|
||||
private EntityManagerInterface $entityManager;
|
||||
private PartsTableActionHandler $partsTableActionHandler;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, PartsTableActionHandler $partsTableActionHandler)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->partsTableActionHandler = $partsTableActionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/parts/export", name="parts_export", methods={"GET"})
|
||||
* @return Response
|
||||
*/
|
||||
public function exportParts(Request $request, EntityExporter $entityExporter): Response
|
||||
{
|
||||
$ids = $request->query->get('ids', '');
|
||||
$parts = $this->partsTableActionHandler->idStringToArray($ids);
|
||||
|
||||
if (empty($parts)) {
|
||||
throw new \RuntimeException('No parts found!');
|
||||
}
|
||||
|
||||
//Ensure that we have access to the parts
|
||||
foreach ($parts as $part) {
|
||||
$this->denyAccessUnlessGranted('read', $part);
|
||||
}
|
||||
|
||||
return $entityExporter->exportEntityFromRequest($parts, $request);
|
||||
}
|
||||
}
|
|
@ -95,6 +95,25 @@ class SelectAPIController extends AbstractController
|
|||
return $this->getResponseForClass(Project::class, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/export_level", name="select_export_level")
|
||||
*/
|
||||
public function exportLevel(): Response
|
||||
{
|
||||
$entries = [
|
||||
1 => $this->translator->trans('export.level.simple'),
|
||||
2 => $this->translator->trans('export.level.extended'),
|
||||
3 => $this->translator->trans('export.level.full'),
|
||||
];
|
||||
|
||||
return $this->json(array_map(function ($key, $value) {
|
||||
return [
|
||||
'text' => $value,
|
||||
'value' => $key,
|
||||
];
|
||||
}, array_keys($entries), $entries));
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/label_profiles", name="select_label_profiles")
|
||||
* @return Response
|
||||
|
|
|
@ -52,7 +52,7 @@ class EntityExporter
|
|||
$resolver->setDefault('format', 'csv');
|
||||
$resolver->setAllowedValues('format', ['csv', 'json', 'xml', 'yaml']);
|
||||
|
||||
$resolver->setDefault('csv_delimiter', ',');
|
||||
$resolver->setDefault('csv_delimiter', ';');
|
||||
$resolver->setAllowedTypes('csv_delimiter', 'string');
|
||||
|
||||
$resolver->setDefault('level', 'extended');
|
||||
|
|
|
@ -102,6 +102,34 @@ final class PartsTableActionHandler
|
|||
);
|
||||
}
|
||||
|
||||
//When action starts with "export_" we have to redirect to the export controller
|
||||
$matches = [];
|
||||
if (preg_match('/^export_(json|yaml|xml|csv)$/', $action, $matches)) {
|
||||
$ids = implode(',', array_map(static fn (Part $part) => $part->getID(), $selected_parts));
|
||||
switch ($target_id) {
|
||||
case 1:
|
||||
default:
|
||||
$level = 'simple';
|
||||
break;
|
||||
case 2:
|
||||
$level = 'extended';
|
||||
break;
|
||||
case 3:
|
||||
$level = 'full';
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return new RedirectResponse(
|
||||
$this->urlGenerator->generate('parts_export', [
|
||||
'format' => $matches[1],
|
||||
'level' => $level,
|
||||
'ids' => $ids,
|
||||
'_redirect' => $redirect_url
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
//Iterate over the parts and apply the action to it:
|
||||
foreach ($selected_parts as $part) {
|
||||
|
|
|
@ -63,6 +63,12 @@
|
|||
<optgroup label="{% trans %}part_list.action.action.delete{% endtrans %}">
|
||||
<option {% if not is_granted('@parts.delete') %}disabled{% endif %} value="delete">{% trans %}part_list.action.action.delete{% endtrans %}</option>
|
||||
</optgroup>
|
||||
<optgroup label="{% trans %}part_list.action.action.export{% endtrans %}">
|
||||
<option {% if not is_granted('@parts.read') %}disabled{% endif %} value="export_json" data-url="{{ path('select_export_level')}}" data-turbo="false">{% trans %}part_list.action.export_json{% endtrans %}</option>
|
||||
<option {% if not is_granted('@parts.read') %}disabled{% endif %} value="export_csv" data-url="{{ path('select_export_level')}}" data-turbo="false">{% trans %}part_list.action.export_csv{% endtrans %}</option>
|
||||
<option {% if not is_granted('@parts.read') %}disabled{% endif %} value="export_yaml" data-url="{{ path('select_export_level')}}" data-turbo="false">{% trans %}part_list.action.export_yaml{% endtrans %}</option>
|
||||
<option {% if not is_granted('@parts.read') %}disabled{% endif %} value="export_xml" data-url="{{ path('select_export_level')}}" data-turbo="false">{% trans %}part_list.action.export_xml{% endtrans %}</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
|
||||
<select class="form-select d-none" data-controller="elements--structural-entity-select" name="target" {{ stimulus_target('elements/datatables/parts', 'selectTargetPicker') }}>
|
||||
|
|
|
@ -10970,34 +10970,64 @@ Element 3</target>
|
|||
</segment>
|
||||
</unit>
|
||||
<unit id="Qt585vm" name="attachment.max_file_size">
|
||||
<segment state="translated">
|
||||
<segment>
|
||||
<source>attachment.max_file_size</source>
|
||||
<target>Maximum file size</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="tkkbiag" name="user.saml_user">
|
||||
<segment state="translated">
|
||||
<segment>
|
||||
<source>user.saml_user</source>
|
||||
<target>SSO / SAML user</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="fhepjKr" name="user.saml_user.pw_change_hint">
|
||||
<segment state="translated">
|
||||
<segment>
|
||||
<source>user.saml_user.pw_change_hint</source>
|
||||
<target>Your user uses single sign-on (SSO). You can not change the password and 2FA settings here. Configure them on your central SSO provider instead!</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="32beTBH" name="login.sso_saml_login">
|
||||
<segment state="translated">
|
||||
<segment>
|
||||
<source>login.sso_saml_login</source>
|
||||
<target>Single Sign-On Login (SSO)</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="wnMLanX" name="login.local_login_hint">
|
||||
<segment state="translated">
|
||||
<segment>
|
||||
<source>login.local_login_hint</source>
|
||||
<target>The form below is only for log in with a local user. If you want to log in via single sign-on, press the button above.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="fa76Qc9" name="part_list.action.action.export">
|
||||
<segment>
|
||||
<source>part_list.action.action.export</source>
|
||||
<target>Export parts</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="OfOI7tn" name="part_list.action.export_json">
|
||||
<segment>
|
||||
<source>part_list.action.export_json</source>
|
||||
<target>Export to JSON</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="8Y5uz7l" name="part_list.action.export_csv">
|
||||
<segment>
|
||||
<source>part_list.action.export_csv</source>
|
||||
<target>Export to CSV</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="gtllBTO" name="part_list.action.export_yaml">
|
||||
<segment>
|
||||
<source>part_list.action.export_yaml</source>
|
||||
<target>Export to YAML</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="IW9wGBS" name="part_list.action.export_xml">
|
||||
<segment>
|
||||
<source>part_list.action.export_xml</source>
|
||||
<target>Export to XML</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</segment>
|
||||
</unit>
|
||||
<unit id="Dpb9AmY" name="saml.error.cannot_login_local_user_per_saml">
|
||||
<segment state="translated">
|
||||
<segment>
|
||||
<source>saml.error.cannot_login_local_user_per_saml</source>
|
||||
<target>You can not login as local user via SSO! Use your local user password instead.</target>
|
||||
</segment>
|
||||
|
|
|
@ -300,7 +300,7 @@
|
|||
</segment>
|
||||
</unit>
|
||||
<unit id="m8kMFhf" name="validator.attachment.name_not_blank">
|
||||
<segment state="translated">
|
||||
<segment>
|
||||
<source>validator.attachment.name_not_blank</source>
|
||||
<target>Set a value here, or upload a file to automatically use its filename as name for the attachment.</target>
|
||||
</segment>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue