mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-02-10 19:49:35 +00:00
Added an web page to quickly add a new part from a web URL
This commit is contained in:
parent
722eb7ddab
commit
909cab0044
6 changed files with 124 additions and 5 deletions
|
|
@ -30,6 +30,7 @@ use App\Form\InfoProviderSystem\PartSearchType;
|
|||
use App\Services\InfoProviderSystem\ExistingPartFinder;
|
||||
use App\Services\InfoProviderSystem\PartInfoRetriever;
|
||||
use App\Services\InfoProviderSystem\ProviderRegistry;
|
||||
use App\Services\InfoProviderSystem\Providers\GenericWebProvider;
|
||||
use App\Settings\AppSettings;
|
||||
use App\Settings\InfoProviderSystem\InfoProviderGeneralSettings;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
|
@ -39,6 +40,7 @@ use Psr\Log\LoggerInterface;
|
|||
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\UrlType;
|
||||
use Symfony\Component\HttpClient\Exception\ClientException;
|
||||
use Symfony\Component\HttpClient\Exception\TransportException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
|
@ -208,4 +210,58 @@ class InfoProviderController extends AbstractController
|
|||
'update_target' => $update_target
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/from_url', name: 'info_providers_from_url')]
|
||||
public function fromURL(Request $request, GenericWebProvider $provider): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('@info_providers.create_parts');
|
||||
|
||||
if (!$provider->isActive()) {
|
||||
$this->addFlash('error', "Generic Web Provider is not active. Please enable it in the provider settings.");
|
||||
return $this->redirectToRoute('info_providers_list');
|
||||
}
|
||||
|
||||
$formBuilder = $this->createFormBuilder();
|
||||
$formBuilder->add('url', UrlType::class, [
|
||||
'label' => 'info_providers.from_url.url.label',
|
||||
'required' => true,
|
||||
]);
|
||||
$formBuilder->add('submit', SubmitType::class, [
|
||||
'label' => 'info_providers.search.submit',
|
||||
]);
|
||||
|
||||
$form = $formBuilder->getForm();
|
||||
$form->handleRequest($request);
|
||||
|
||||
$partDetail = null;
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
//Try to retrieve the part detail from the given URL
|
||||
$url = $form->get('url')->getData();
|
||||
try {
|
||||
$searchResult = $this->infoRetriever->searchByKeyword(
|
||||
keyword: $url,
|
||||
providers: [$provider]
|
||||
);
|
||||
|
||||
if (count($searchResult) === 0) {
|
||||
$this->addFlash('warning', t('info_providers.from_url.no_part_found'));
|
||||
} else {
|
||||
$searchResult = $searchResult[0];
|
||||
//Redirect to the part creation page with the found part detail
|
||||
return $this->redirectToRoute('info_providers_create_part', [
|
||||
'providerKey' => $searchResult->provider_key,
|
||||
'providerId' => $searchResult->provider_id,
|
||||
]);
|
||||
}
|
||||
} catch (ExceptionInterface $e) {
|
||||
$this->addFlash('error', t('info_providers.search.error.general_exception', ['%type%' => (new \ReflectionClass($e))->getShortName()]));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render('info_providers/from_url/from_url.html.twig', [
|
||||
'form' => $form,
|
||||
'partDetail' => $partDetail,
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ use App\Entity\UserSystem\User;
|
|||
use App\Helpers\Trees\TreeViewNode;
|
||||
use App\Services\Cache\UserCacheKeyGenerator;
|
||||
use App\Services\ElementTypeNameGenerator;
|
||||
use App\Services\InfoProviderSystem\Providers\GenericWebProvider;
|
||||
use App\Settings\InfoProviderSystem\GenericWebProviderSettings;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Contracts\Cache\ItemInterface;
|
||||
|
|
@ -58,6 +60,7 @@ class ToolsTreeBuilder
|
|||
protected UserCacheKeyGenerator $keyGenerator,
|
||||
protected Security $security,
|
||||
private readonly ElementTypeNameGenerator $elementTypeNameGenerator,
|
||||
private readonly GenericWebProviderSettings $genericWebProviderSettings
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +150,13 @@ class ToolsTreeBuilder
|
|||
$this->urlGenerator->generate('info_providers_search')
|
||||
))->setIcon('fa-treeview fa-fw fa-solid fa-cloud-arrow-down');
|
||||
|
||||
if ($this->genericWebProviderSettings->enabled) {
|
||||
$nodes[] = (new TreeViewNode(
|
||||
$this->translator->trans('info_providers.from_url.title'),
|
||||
$this->urlGenerator->generate('info_providers_from_url')
|
||||
))->setIcon('fa-treeview fa-fw fa-solid fa-book-atlas');
|
||||
}
|
||||
|
||||
$nodes[] = (new TreeViewNode(
|
||||
$this->translator->trans('info_providers.bulk_import.manage_jobs'),
|
||||
$this->urlGenerator->generate('bulk_info_provider_manage')
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use Jbtronics\SettingsBundle\Settings\SettingsParameter;
|
|||
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
|
||||
use Symfony\Component\Translation\TranslatableMessage as TM;
|
||||
|
||||
#[Settings(label: new TM("settings.ips.generic_web_provider"), description: new TM("settings.ips.generic_web_provider.description"))]
|
||||
#[Settings(name: "generic_web_provider", label: new TM("settings.ips.generic_web_provider"), description: new TM("settings.ips.generic_web_provider.description"))]
|
||||
#[SettingsIcon("fa-plug")]
|
||||
class GenericWebProviderSettings
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
<!-- <span class="navbar-toggler-icon"></span> -->
|
||||
<i class="fas fa-folder-open fa-lg fa-fw"></i>
|
||||
</button>
|
||||
{% if is_granted("@tools.label_scanner") %}
|
||||
{% if is_granted("@tools.label_scanner") %}
|
||||
<a href="{{ path('scan_dialog') }}" class="navbar-toggler nav-link ms-3">
|
||||
<i class="fas fa-camera-retro fa-fw"></i>
|
||||
<i class="fas fa-camera-retro fa-fw"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
@ -52,6 +52,14 @@
|
|||
{% trans %}info_providers.search.title{% endtrans %}
|
||||
</a>
|
||||
</li>
|
||||
{% if settings_instance('generic_web_provider').enabled %}
|
||||
<li>
|
||||
<a class="dropdown-item" href="{{ path('info_providers_from_url') }}">
|
||||
<i class="fa-fw fa-solid fa-book-atlas"></i>
|
||||
{% trans %}info_providers.from_url.title{% endtrans %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if is_granted('@parts.import') %}
|
||||
|
|
@ -69,7 +77,7 @@
|
|||
{% if is_granted('@parts.read') %}
|
||||
{{ search.search_form("navbar") }}
|
||||
|
||||
{# {% include "_navbar_search.html.twig" %} #}
|
||||
{# {% include "_navbar_search.html.twig" %} #}
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
|
@ -145,4 +153,4 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
|
|
|
|||
21
templates/info_providers/from_url/from_url.html.twig
Normal file
21
templates/info_providers/from_url/from_url.html.twig
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{% extends "main_card.html.twig" %}
|
||||
|
||||
{% import "info_providers/providers.macro.html.twig" as providers_macro %}
|
||||
{% import "helper.twig" as helper %}
|
||||
|
||||
{% block title %}
|
||||
{% trans %}info_providers.from_url.title{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block card_title %}
|
||||
<i class="fas fa-book-atlas"></i> {% trans %}info_providers.from_url.title{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block card_content %}
|
||||
<p class="text-muted offset-3">{% trans %}info_providers.from_url.help{% endtrans %}</p>
|
||||
|
||||
{{ form_start(form) }}
|
||||
{{ form_row(form.url) }}
|
||||
{{ form_row(form.submit) }}
|
||||
{{ form_end(form) }}
|
||||
{% endblock %}
|
||||
|
|
@ -14334,5 +14334,29 @@ Buerklin-API Authentication server:
|
|||
<target>When the provider is enabled, users can make requests to arbitary websites on behalf of the Part-DB server. Only enable this, if you are aware of the potential consequences.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="IvIOYcn" name="info_providers.from_url.title">
|
||||
<segment>
|
||||
<source>info_providers.from_url.title</source>
|
||||
<target>Create [part] from URL</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="QLL7vDC" name="info_providers.from_url.url.label">
|
||||
<segment>
|
||||
<source>info_providers.from_url.url.label</source>
|
||||
<target>URL</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="JTbTQLl" name="info_providers.from_url.no_part_found">
|
||||
<segment>
|
||||
<source>info_providers.from_url.no_part_found</source>
|
||||
<target>No part found from the given URL. Are you sure this is a valid shop URL?</target>
|
||||
</segment>
|
||||
</unit>
|
||||
<unit id="xoSvJk0" name="info_providers.from_url.help">
|
||||
<segment>
|
||||
<source>info_providers.from_url.help</source>
|
||||
<target>Creates a part based on the given URL. It tries to delegate it to an existing info provider if possible, otherwise it will be tried to extract rudimentary data from the webpage's metadata.</target>
|
||||
</segment>
|
||||
</unit>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue