Skip to content

Commit 58fed0b

Browse files
committed
[TASK] Improve
1 parent cb26a94 commit 58fed0b

15 files changed

Lines changed: 329 additions & 18 deletions

File tree

Classes/Controller/ImportExportModuleController.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Psr\Http\Message\ServerRequestInterface;
1010
use TYPO3\CMS\Backend\Attribute\AsController;
1111
use TYPO3\CMS\Backend\Routing\UriBuilder;
12+
use TYPO3\CMS\Core\Site\SiteFinder;
1213
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
1314
use TYPO3\CMS\Backend\Template\ModuleTemplate;
1415
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
@@ -26,12 +27,14 @@ public function __construct(
2627
private RedirectRepository $redirectRepository,
2728
private UriBuilder $uriBuilder,
2829
private IconFactory $iconFactory,
30+
private SiteFinder $siteFinder,
2931
) {}
3032

3133
public function handleImportRequest(ServerRequestInterface $request): ResponseInterface
3234
{
3335
$view = $this->moduleTemplateFactory->create($request);
3436
$this->addNavigationButtons($view, 'import');
37+
$view->assign('firstSiteDomain', $this->resolveFirstSiteDomain());
3538
return $view->renderResponse('ImportExport/Import');
3639
}
3740

@@ -47,6 +50,17 @@ public function handleExportRequest(ServerRequestInterface $request): ResponseIn
4750
return $view->renderResponse('ImportExport/Export');
4851
}
4952

53+
private function resolveFirstSiteDomain(): string
54+
{
55+
$sites = $this->siteFinder->getAllSites();
56+
$site = reset($sites);
57+
if ($site === false) {
58+
return 'https://example.com';
59+
}
60+
$base = $site->getBase();
61+
return $base->getScheme() . '://' . $base->getHost();
62+
}
63+
5064
private function addNavigationButtons(ModuleTemplate $view, string $active): void
5165
{
5266
$buttonBar = $view->getDocHeaderComponent()->getButtonBar();

Classes/Controller/ImportModuleController.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,32 @@
88
use Psr\Http\Message\ServerRequestInterface;
99
use TYPO3\CMS\Backend\Attribute\AsController;
1010
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
11+
use TYPO3\CMS\Core\Site\SiteFinder;
1112

1213
#[AsController]
1314
readonly class ImportModuleController
1415
{
1516
public function __construct(
1617
private ModuleTemplateFactory $moduleTemplateFactory,
18+
private SiteFinder $siteFinder,
1719
) {}
1820

1921
public function handleRequest(ServerRequestInterface $request): ResponseInterface
2022
{
2123
$view = $this->moduleTemplateFactory->create($request);
2224
$view->makeDocHeaderModuleMenu();
25+
$view->assign('firstSiteDomain', $this->resolveFirstSiteDomain());
2326
return $view->renderResponse('Import/Overview');
2427
}
28+
29+
private function resolveFirstSiteDomain(): string
30+
{
31+
$sites = $this->siteFinder->getAllSites();
32+
$site = reset($sites);
33+
if ($site === false) {
34+
return 'https://example.com';
35+
}
36+
$base = $site->getBase();
37+
return $base->getScheme() . '://' . $base->getHost();
38+
}
2539
}

Configuration/Backend/Modules.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
'parent' => 'link_management',
3333
'access' => 'user',
3434
'path' => '/module/link-management/redirect-import',
35-
'iconIdentifier' => 'actions-upload',
35+
'iconIdentifier' => 'actions-database-import',
3636
'labels' => 'redirect_generator.modules.import',
3737
'routes' => [
3838
'_default' => [
@@ -44,7 +44,7 @@
4444
'parent' => 'link_management',
4545
'access' => 'user',
4646
'path' => '/module/link-management/redirect-export',
47-
'iconIdentifier' => 'actions-download',
47+
'iconIdentifier' => 'actions-database-export',
4848
'labels' => 'redirect_generator.modules.export',
4949
'routes' => [
5050
'_default' => [

Readme.md

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Target URLs are automatically resolved to `t3://page?uid=X` links via TYPO3's ro
66

77
## Requirements
88

9-
- TYPO3 13.4 LTS or 14.x
9+
- TYPO3 13.4 LTS or 14 LTS
1010
- EXT:redirects
1111

1212
## Installation
@@ -15,12 +15,6 @@ Target URLs are automatically resolved to `t3://page?uid=X` links via TYPO3's ro
1515
composer require georgringer/redirect-generator
1616
```
1717

18-
For the backend UI modules (import/export forms), also install:
19-
20-
```bash
21-
composer require georgringer/redirect-generator-ui
22-
```
23-
2418
## Configuration
2519

2620
Extension configuration options (Admin → Settings → Extension Configuration):
@@ -90,12 +84,45 @@ Options:
9084

9185
> This command is schedulable.
9286
93-
## Backend Modules
87+
## Backend UI — `redirect_generator_ui`
9488

95-
Install `redirect_generator_ui` to get two modules under *Link Management*:
89+
The companion extension **`georgringer/redirect-generator-ui`** adds a full backend module under *Link Management* with import and export forms — no CLI access required.
90+
91+
```bash
92+
composer require georgringer/redirect-generator-ui
93+
```
9694

97-
- **Import Redirects** — paste CSV directly into a textarea, choose delimiter and status code, optional dry run
98-
- **Export Redirects** — filter by redirect type or creation type, resolve target URLs, copy CSV from textarea
95+
<table>
96+
<tr>
97+
<td align="center" width="50%">
98+
<img src="Resources/Public/Screenshots/ui/module-link-management.png" alt="Link Management module" /><br/>
99+
<sub>Module in the backend navigation</sub>
100+
</td>
101+
<td align="center" width="50%">
102+
<img src="Resources/Public/Screenshots/ui/export.png" alt="Export module" /><br/>
103+
<sub>Export — filter by type, resolve target URLs</sub>
104+
</td>
105+
</tr>
106+
<tr>
107+
<td align="center" width="50%">
108+
<img src="Resources/Public/Screenshots/ui/import-success.png" alt="Import success" /><br/>
109+
<sub>Import — live result with per-row feedback</sub>
110+
</td>
111+
<td align="center" width="50%">
112+
<img src="Resources/Public/Screenshots/ui/import-error.png" alt="Import errors" /><br/>
113+
<sub>Import — conflicts and errors shown inline</sub>
114+
</td>
115+
</tr>
116+
</table>
117+
118+
**Features:**
119+
120+
- Paste CSV directly into a textarea — no file upload or server access needed
121+
- Choose delimiter (`;` `,` `tab`) and default status code per import run
122+
- Dry-run mode to preview results before writing
123+
- Per-row result feedback: imported, skipped, conflicting duplicates, errors
124+
- Export with optional filter by redirect type or creation type
125+
- Resolve stored `t3://page` links to readable URLs on export
99126

100127
## PSR-14 Events
101128

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
3+
<file source-language="en" target-language="de" datatype="plaintext" original="EXT:redirect_generator/Resources/Private/Language/Modules/export.xlf" product-name="redirect_generator">
4+
<header/>
5+
<body>
6+
<trans-unit id="title">
7+
<source>Export Redirects</source>
8+
<target>Weiterleitungen exportieren</target>
9+
</trans-unit>
10+
<trans-unit id="short_description">
11+
<source>Export redirects as CSV</source>
12+
<target>Weiterleitungen als CSV exportieren</target>
13+
</trans-unit>
14+
<trans-unit id="description">
15+
<source>Export existing redirects to CSV for review or re-import.</source>
16+
<target>Vorhandene Weiterleitungen zur Überprüfung oder zum Reimport als CSV exportieren.</target>
17+
</trans-unit>
18+
<trans-unit id="heading">
19+
<source>Export Redirects</source>
20+
<target>Weiterleitungen exportieren</target>
21+
</trans-unit>
22+
<trans-unit id="form.redirectType">
23+
<source>Filter by redirect type</source>
24+
<target>Nach Weiterleitungstyp filtern</target>
25+
</trans-unit>
26+
<trans-unit id="form.redirectType.all">
27+
<source>All</source>
28+
<target>Alle</target>
29+
</trans-unit>
30+
<trans-unit id="form.creationType">
31+
<source>Filter by creation type</source>
32+
<target>Nach Erstellungstyp filtern</target>
33+
</trans-unit>
34+
<trans-unit id="form.creationType.all">
35+
<source>All</source>
36+
<target>Alle</target>
37+
</trans-unit>
38+
<trans-unit id="form.resolveTarget">
39+
<source>Resolve target URLs</source>
40+
<target>Ziel-URLs auflösen</target>
41+
</trans-unit>
42+
<trans-unit id="form.resolveTarget.description">
43+
<source>Convert stored t3://page links to readable URLs.</source>
44+
<target>Gespeicherte t3://page-Links in lesbare URLs umwandeln.</target>
45+
</trans-unit>
46+
<trans-unit id="form.submit">
47+
<source>Export</source>
48+
<target>Exportieren</target>
49+
</trans-unit>
50+
<trans-unit id="result.empty">
51+
<source>No redirects found for the selected filter.</source>
52+
<target>Keine Weiterleitungen für den gewählten Filter gefunden.</target>
53+
</trans-unit>
54+
<trans-unit id="result.count">
55+
<source>redirects exported</source>
56+
<target>Weiterleitungen exportiert</target>
57+
</trans-unit>
58+
</body>
59+
</file>
60+
</xliff>
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
3+
<file source-language="en" target-language="de" datatype="plaintext" original="EXT:redirect_generator/Resources/Private/Language/Modules/import.xlf" product-name="redirect_generator">
4+
<header/>
5+
<body>
6+
<trans-unit id="title">
7+
<source>Import Redirects</source>
8+
<target>Weiterleitungen importieren</target>
9+
</trans-unit>
10+
<trans-unit id="short_description">
11+
<source>Import redirects from CSV</source>
12+
<target>Weiterleitungen aus CSV importieren</target>
13+
</trans-unit>
14+
<trans-unit id="description">
15+
<source>Paste CSV content to bulk-import redirects into the redirect table.</source>
16+
<target>CSV-Inhalt einfügen, um Weiterleitungen in die Weiterleitungstabelle zu importieren.</target>
17+
</trans-unit>
18+
<trans-unit id="heading">
19+
<source>Import Redirects from CSV</source>
20+
<target>Weiterleitungen aus CSV importieren</target>
21+
</trans-unit>
22+
<trans-unit id="form.csvContent">
23+
<source>CSV Content</source>
24+
<target>CSV-Inhalt</target>
25+
</trans-unit>
26+
<trans-unit id="form.csvContent.description">
27+
<source>Paste CSV rows here. Column 1: source path, column 2: target URL. No header row needed.</source>
28+
<target>CSV-Zeilen hier einfügen. Spalte 1: Quellpfad, Spalte 2: Ziel-URL. Keine Kopfzeile erforderlich.</target>
29+
</trans-unit>
30+
<trans-unit id="form.delimiter">
31+
<source>Delimiter</source>
32+
<target>Trennzeichen</target>
33+
</trans-unit>
34+
<trans-unit id="form.delimiter.semicolon">
35+
<source>Semicolon (;)</source>
36+
<target>Semikolon (;)</target>
37+
</trans-unit>
38+
<trans-unit id="form.delimiter.comma">
39+
<source>Comma (,)</source>
40+
<target>Komma (,)</target>
41+
</trans-unit>
42+
<trans-unit id="form.delimiter.tab">
43+
<source>Tab</source>
44+
<target>Tabulator</target>
45+
</trans-unit>
46+
<trans-unit id="form.statusCode">
47+
<source>Default Redirect Type</source>
48+
<target>Standard-Weiterleitungstyp</target>
49+
</trans-unit>
50+
<trans-unit id="form.dryRun">
51+
<source>Dry Run</source>
52+
<target>Testlauf</target>
53+
</trans-unit>
54+
<trans-unit id="form.submit">
55+
<source>Import</source>
56+
<target>Importieren</target>
57+
</trans-unit>
58+
<trans-unit id="result.dryRun">
59+
<source>Dry run – no redirects were written to the database.</source>
60+
<target>Testlauf – keine Weiterleitungen wurden in die Datenbank geschrieben.</target>
61+
</trans-unit>
62+
<trans-unit id="result.ok">
63+
<source>Successfully imported redirects</source>
64+
<target>Weiterleitungen erfolgreich importiert</target>
65+
</trans-unit>
66+
<trans-unit id="result.error">
67+
<source>Errors</source>
68+
<target>Fehler</target>
69+
</trans-unit>
70+
<trans-unit id="result.skipped">
71+
<source>Skipped (source equals target)</source>
72+
<target>Übersprungen (Quelle entspricht Ziel)</target>
73+
</trans-unit>
74+
<trans-unit id="result.duplicates.conflicting">
75+
<source>Conflicting duplicates (same source, different target)</source>
76+
<target>Konflikte (gleiche Quelle, unterschiedliches Ziel)</target>
77+
</trans-unit>
78+
<trans-unit id="result.duplicates.non_conflicting">
79+
<source>Already exists (identical source and target)</source>
80+
<target>Bereits vorhanden (identische Quelle und identisches Ziel)</target>
81+
</trans-unit>
82+
<trans-unit id="help.title">
83+
<source>Examples</source>
84+
<target>Beispiele</target>
85+
</trans-unit>
86+
<trans-unit id="help.description">
87+
<source>Each line contains one redirect. Columns are separated by the chosen delimiter. The fourth column (1) marks a target as external and skips URL resolving.</source>
88+
<target>Jede Zeile enthält eine Weiterleitung. Spalten werden durch das gewählte Trennzeichen getrennt. Die vierte Spalte (1) markiert ein Ziel als extern und überspringt die URL-Auflösung.</target>
89+
</trans-unit>
90+
<trans-unit id="help.example.basic.title">
91+
<source>Basic (source; target)</source>
92+
<target>Einfach (Quelle; Ziel)</target>
93+
</trans-unit>
94+
<trans-unit id="help.example.statuscode.title">
95+
<source>With status code (source; target; status code)</source>
96+
<target>Mit Statuscode (Quelle; Ziel; Statuscode)</target>
97+
</trans-unit>
98+
<trans-unit id="help.example.external.title">
99+
<source>External target (source; target; status code; external)</source>
100+
<target>Externes Ziel (Quelle; Ziel; Statuscode; extern)</target>
101+
</trans-unit>
102+
</body>
103+
</file>
104+
</xliff>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
3+
<file source-language="en" target-language="de" datatype="plaintext" original="EXT:redirect_generator/Resources/Private/Language/Modules/importexport.xlf" product-name="redirect_generator">
4+
<header/>
5+
<body>
6+
<trans-unit id="title">
7+
<source>Import/Export Redirects</source>
8+
<target>Weiterleitungen importieren/exportieren</target>
9+
</trans-unit>
10+
<trans-unit id="short_description">
11+
<source>Import and export redirects via CSV</source>
12+
<target>Weiterleitungen per CSV importieren und exportieren</target>
13+
</trans-unit>
14+
<trans-unit id="description">
15+
<source>Import redirects from CSV or export existing redirects to CSV.</source>
16+
<target>Weiterleitungen aus CSV importieren oder vorhandene Weiterleitungen als CSV exportieren.</target>
17+
</trans-unit>
18+
</body>
19+
</file>
20+
</xliff>

Resources/Private/Language/Modules/import.xlf

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@
3939
<trans-unit id="form.dryRun">
4040
<source>Dry Run</source>
4141
</trans-unit>
42-
<trans-unit id="form.dryRun.description">
43-
<source>Simulate the import without writing to the database.</source>
44-
</trans-unit>
4542
<trans-unit id="form.submit">
4643
<source>Import</source>
4744
</trans-unit>
@@ -63,6 +60,21 @@
6360
<trans-unit id="result.duplicates.non_conflicting">
6461
<source>Already exists (identical source and target)</source>
6562
</trans-unit>
63+
<trans-unit id="help.title">
64+
<source>Examples</source>
65+
</trans-unit>
66+
<trans-unit id="help.description">
67+
<source>Each line contains one redirect. Columns are separated by the chosen delimiter. The fourth column (1) marks a target as external and skips URL resolving.</source>
68+
</trans-unit>
69+
<trans-unit id="help.example.basic.title">
70+
<source>Basic (source; target)</source>
71+
</trans-unit>
72+
<trans-unit id="help.example.statuscode.title">
73+
<source>With status code (source; target; status code)</source>
74+
</trans-unit>
75+
<trans-unit id="help.example.external.title">
76+
<source>External target (source; target; status code; external)</source>
77+
</trans-unit>
6678
</body>
6779
</file>
6880
</xliff>

0 commit comments

Comments
 (0)