Website-URLs ohne Protokoll akzeptieren

This commit is contained in:
gitea_admin committed 2026-09-27 13:48:29 +02:00
1 parent b963b798df
commit ae741aae61
5 files changed
+39 -3

No files matched your search

@@ -138,12 +138,12 @@ Das Ergebnis steht bei `tools/call` als JSON im Textinhalt des ersten
- liefert Marktdurchführungen inklusive Markttagen, Kosten und berechneten Verkaufswerten - liefert Marktdurchführungen inklusive Markttagen, Kosten und berechneten Verkaufswerten
- `market_management.create_market` - `market_management.create_market`
- erforderlich: `name`, `category_id`, `organizer_id` - erforderlich: `name`, `category_id`, `organizer_id`
- optional: `website_url` als HTTP(S)-URL des Marktes; Eingaben ohne Schema werden als `https://` normalisiert
- `market_management.list_market_organizers` - `market_management.list_market_organizers`
- liefert aktive Veranstalterorganisationen - liefert aktive Veranstalterorganisationen
- `market_management.create_market_organizer` - `market_management.create_market_organizer`
- erforderlich: `name` - erforderlich: `name`
- optional: `email`, `phone` - optional: `email`, `phone`
- optional: `website_url` als HTTP(S)-URL des Marktes
- `market_management.update_market` - `market_management.update_market`
- erforderlich: `market_id` - erforderlich: `market_id`
- änderbar: `name`, `category_id`, `website_url` - änderbar: `name`, `category_id`, `website_url`
+2 -2
View File
@@ -75,8 +75,8 @@ function market_management_mcp_tools(): array
['name' => 'market_management.search_markets', 'description' => 'Märkte suchen', 'inputSchema' => ['type' => 'object', 'properties' => $searchProperties]], ['name' => 'market_management.search_markets', 'description' => 'Märkte suchen', 'inputSchema' => ['type' => 'object', 'properties' => $searchProperties]],
['name' => 'market_management.search_occurrences', 'description' => 'Marktdurchführungen suchen und auflisten', 'inputSchema' => ['type' => 'object', 'properties' => $searchProperties]], ['name' => 'market_management.search_occurrences', 'description' => 'Marktdurchführungen suchen und auflisten', 'inputSchema' => ['type' => 'object', 'properties' => $searchProperties]],
['name' => 'market_management.get_market', 'description' => 'Markt mit Marktdurchführungen lesen', 'inputSchema' => ['type' => 'object', 'required' => ['market_id'], 'properties' => ['market_id' => ['type' => 'integer'], 'include_archived' => ['type' => 'boolean']]]], ['name' => 'market_management.get_market', 'description' => 'Markt mit Marktdurchführungen lesen', 'inputSchema' => ['type' => 'object', 'required' => ['market_id'], 'properties' => ['market_id' => ['type' => 'integer'], 'include_archived' => ['type' => 'boolean']]]],
['name' => 'market_management.create_market', 'description' => 'Markt mit verpflichtendem Veranstalter, Kategorie und optionaler Website-URL anlegen', 'inputSchema' => ['type' => 'object', 'required' => ['name', 'category_id', 'organizer_id'], 'properties' => ['name' => ['type' => 'string'], 'category_id' => ['type' => 'integer'], 'organizer_id' => ['type' => 'integer'], 'website_url' => ['type' => ['string', 'null'], 'format' => 'uri']]]], ['name' => 'market_management.create_market', 'description' => 'Markt mit verpflichtendem Veranstalter, Kategorie und optionaler Website-URL anlegen', 'inputSchema' => ['type' => 'object', 'required' => ['name', 'category_id', 'organizer_id'], 'properties' => ['name' => ['type' => 'string'], 'category_id' => ['type' => 'integer'], 'organizer_id' => ['type' => 'integer'], 'website_url' => ['type' => ['string', 'null']]]]],
['name' => 'market_management.update_market', 'description' => 'Markt inklusive Veranstalter und Website-URL ändern', 'inputSchema' => ['type' => 'object', 'required' => ['market_id'], 'properties' => ['market_id' => ['type' => 'integer'], 'name' => ['type' => 'string'], 'category_id' => ['type' => 'integer'], 'organizer_id' => ['type' => 'integer'], 'website_url' => ['type' => ['string', 'null'], 'format' => 'uri']]]], ['name' => 'market_management.update_market', 'description' => 'Markt inklusive Veranstalter und Website-URL ändern', 'inputSchema' => ['type' => 'object', 'required' => ['market_id'], 'properties' => ['market_id' => ['type' => 'integer'], 'name' => ['type' => 'string'], 'category_id' => ['type' => 'integer'], 'organizer_id' => ['type' => 'integer'], 'website_url' => ['type' => ['string', 'null']]]]],
['name' => 'market_management.archive_market', 'description' => 'Markt archivieren', 'inputSchema' => ['type' => 'object', 'required' => ['market_id'], 'properties' => ['market_id' => ['type' => 'integer']]]], ['name' => 'market_management.archive_market', 'description' => 'Markt archivieren', 'inputSchema' => ['type' => 'object', 'required' => ['market_id'], 'properties' => ['market_id' => ['type' => 'integer']]]],
['name' => 'market_management.reactivate_market', 'description' => 'Markt reaktivieren', 'inputSchema' => ['type' => 'object', 'required' => ['market_id'], 'properties' => ['market_id' => ['type' => 'integer']]]], ['name' => 'market_management.reactivate_market', 'description' => 'Markt reaktivieren', 'inputSchema' => ['type' => 'object', 'required' => ['market_id'], 'properties' => ['market_id' => ['type' => 'integer']]]],
['name' => 'market_management.list_categories', 'description' => 'Bestehende Kategorien lesen', 'inputSchema' => ['type' => 'object', 'properties' => (object) []]], ['name' => 'market_management.list_categories', 'description' => 'Bestehende Kategorien lesen', 'inputSchema' => ['type' => 'object', 'properties' => (object) []]],
+4
View File
@@ -352,6 +352,10 @@ function market_management_optional_url(array $input, string $field): ?string
return null; return null;
} }
if (!preg_match('/^https?:\/\//i', $value)) {
$value = 'https://' . $value;
}
if (filter_var($value, FILTER_VALIDATE_URL) === false || !preg_match('/^https?:\/\//i', $value)) { if (filter_var($value, FILTER_VALIDATE_URL) === false || !preg_match('/^https?:\/\//i', $value)) {
throw new InvalidArgumentException($field . ' muss eine gültige HTTP(S)-URL sein.'); throw new InvalidArgumentException($field . ' muss eine gültige HTTP(S)-URL sein.');
} }
+1
View File
@@ -1322,6 +1322,7 @@ function render_auth_home_page(array $user, array $otcProducts = [], array $otcM
JS; JS;
echo " prepareMarketDefinitionDeleteCard();"; echo " prepareMarketDefinitionDeleteCard();";
echo " const sharedCard = drawer.querySelector('[data-market-definition-card]');"; echo " const sharedCard = drawer.querySelector('[data-market-definition-card]');";
echo " const websiteInput = form.querySelector('[data-market-definition-website]'); if (websiteInput) { websiteInput.type = 'text'; }";
echo " if (window.SharedEditableActionCard) { window.SharedEditableActionCard.init(definitionContent); }"; echo " if (window.SharedEditableActionCard) { window.SharedEditableActionCard.init(definitionContent); }";
echo " const setDrawerState = (open) => { const drawerOpen = Boolean(open); if (!drawerOpen && marketDefinitionMobileOpen) { marketDefinitionMobileDetail?.restore(); marketDefinitionMobileHost = null; marketDefinitionMobileOpen = false; } const backdropOpen = drawerOpen && !marketDefinitionMobileOpen; drawer.dataset.open = String(drawerOpen); drawer.setAttribute('aria-hidden', String(!drawerOpen)); if (activeMarket?.trigger) { activeMarket.trigger.setAttribute('aria-expanded', String(drawerOpen)); } const backdrop = definitionContent.querySelector('[data-market-definition-drawer-backdrop]'); if (backdrop) { backdrop.dataset.open = String(backdropOpen); backdrop.setAttribute('aria-hidden', String(!backdropOpen)); } document.body.classList.toggle('sg-card-list-page-drawer-open', backdropOpen); };"; echo " const setDrawerState = (open) => { const drawerOpen = Boolean(open); if (!drawerOpen && marketDefinitionMobileOpen) { marketDefinitionMobileDetail?.restore(); marketDefinitionMobileHost = null; marketDefinitionMobileOpen = false; } const backdropOpen = drawerOpen && !marketDefinitionMobileOpen; drawer.dataset.open = String(drawerOpen); drawer.setAttribute('aria-hidden', String(!drawerOpen)); if (activeMarket?.trigger) { activeMarket.trigger.setAttribute('aria-expanded', String(drawerOpen)); } const backdrop = definitionContent.querySelector('[data-market-definition-drawer-backdrop]'); if (backdrop) { backdrop.dataset.open = String(backdropOpen); backdrop.setAttribute('aria-hidden', String(!backdropOpen)); } document.body.classList.toggle('sg-card-list-page-drawer-open', backdropOpen); };";
echo " const restoreMarketDefinitionMobileDetail = () => { marketDefinitionMobileDetail?.restore(); marketDefinitionMobileHost = null; marketDefinitionMobileOpen = false; };"; echo " const restoreMarketDefinitionMobileDetail = () => { marketDefinitionMobileDetail?.restore(); marketDefinitionMobileHost = null; marketDefinitionMobileOpen = false; };";
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../modules/market_management/service.php';
function fail_test(string $message): void
{
fwrite(STDERR, $message . PHP_EOL);
exit(1);
}
function assert_test(bool $condition, string $message): void
{
if (!$condition) {
fail_test($message);
}
}
$normalized = market_management_optional_url(['website_url' => 'www.stadtaffoltern.ch'], 'website_url');
assert_test($normalized === 'https://www.stadtaffoltern.ch', 'Website without scheme must be normalized to HTTPS');
$existingScheme = market_management_optional_url(['website_url' => 'http://www.stadtaffoltern.ch'], 'website_url');
assert_test($existingScheme === 'http://www.stadtaffoltern.ch', 'Existing HTTP scheme must be preserved');
try {
market_management_optional_url(['website_url' => 'not a url'], 'website_url');
fail_test('Invalid website URL must be rejected');
} catch (InvalidArgumentException) {
}
fwrite(STDOUT, "market_management_url_test: OK\n");