From fd12dec92c7e8c3bf9fbc7a065c3d41dc8f7d7ab Mon Sep 17 00:00:00 2001 From: Marcel Folaron Date: Sun, 18 Oct 2026 10:42:52 -0400 Subject: [PATCH 1/9] feat(theme): bring the ThemeBundle color schemes and backgrounds into core The ThemeBundle plugin's features are now part of core: - 11 more color schemes (Copper, Mustard, Nebula, ...). Their keys are unchanged, so schemes users picked through the plugin still apply. - A Background tab in the user's own settings (and a user menu link): the color gradient, an uploaded image or an Unsplash photo. Every change saves right away and swaps the page background in place. - Unsplash is only offered when LEAN_UNSPLASH_ACCESS_KEY is set. There is no built-in key; without one the tab offers uploads only. Changes from the plugin: - Photos are resolved on the server by their Unsplash id, so a client can no longer store an arbitrary URL as background (the plugin printed it into inline JS). Selecting a photo reports the download to Unsplash and results credit the photographer, as the Unsplash API guidelines require. - Uploads must be JPG, PNG, GIF or WebP images (SVG is refused), and a failed upload no longer crashes on the error string Files::upload returns. - The header's background diff --git a/app/Views/Templates/sections/header.blade.php b/app/Views/Templates/sections/header.blade.php index 2e4962778c..e4cb886989 100644 --- a/app/Views/Templates/sections/header.blade.php +++ b/app/Views/Templates/sections/header.blade.php @@ -143,24 +143,7 @@ functions that run on/after DOMContentLoaded (e.g. initCalendar()), never at - +@include('global::sections.backgroundStyle') @dispatchEvent('afterThemeColors') diff --git a/config/sample.env b/config/sample.env index 4ed4d25136..aa3c657801 100644 --- a/config/sample.env +++ b/config/sample.env @@ -66,6 +66,7 @@ LEAN_PRINT_LOGO_URL = '/dist/images/logo.png' # Default logo URL use for pr LEAN_DEFAULT_THEME = 'default' # Default theme LEAN_PRIMARY_COLOR = '#006d9f' # Primary Theme color LEAN_SECONDARY_COLOR =' #00a886' # Secondary Theme Color +#LEAN_UNSPLASH_ACCESS_KEY = '' # Unsplash access key (unsplash.com/developers). Lets users pick Unsplash photos as background ## File Uploads diff --git a/tests/Unit/app/Domain/Users/Services/BackgroundsTest.php b/tests/Unit/app/Domain/Users/Services/BackgroundsTest.php new file mode 100644 index 0000000000..562623e81a --- /dev/null +++ b/tests/Unit/app/Domain/Users/Services/BackgroundsTest.php @@ -0,0 +1,167 @@ +http = new HttpClient; + $this->http->preventStrayRequests(); + $this->storedImages = []; + $this->storedTypes = []; + } + + private function makeService(string $accessKey = 'test-key'): Backgrounds + { + $config = $this->make(Environment::class); + $config->unsplashAccessKey = $accessKey; + + $theme = $this->make(Theme::class, [ + 'setBackgroundImage' => function (string $url) { + $this->storedImages[] = $url; + }, + 'setBackgroundType' => function (string $type) { + $this->storedTypes[] = $type; + }, + ]); + + return new Backgrounds($theme, $config, $this->make(Files::class), $this->http); + } + + private function unsplashPhoto(string $rawUrl): array + { + return [ + 'id' => 'abc123', + 'alt_description' => 'snowy mountains', + 'urls' => ['raw' => $rawUrl, 'small' => 'https://images.unsplash.com/photo-1?w=400'], + 'user' => ['name' => 'Jane Doe', 'links' => ['html' => 'https://unsplash.com/@jane']], + 'links' => ['download_location' => 'https://api.unsplash.com/photos/abc123/download?ixid=xyz'], + ]; + } + + public function test_unsplash_is_disabled_without_access_key(): void + { + $service = $this->makeService(accessKey: ''); + + $this->assertFalse($service->isUnsplashEnabled()); + $this->assertSame([], $service->searchUnsplash('mountains')); + $this->assertFalse($service->useUnsplashPhoto('abc123')); + $this->assertCount(0, $this->http->recorded()); + } + + public function test_search_returns_photos_with_attribution_and_sends_access_key(): void + { + $this->http->fake([ + 'api.unsplash.com/search/photos*' => $this->http->response([ + 'results' => [$this->unsplashPhoto('https://images.unsplash.com/photo-1?ixid=1')], + ]), + ]); + + $photos = $this->makeService()->searchUnsplash('mountains'); + + $this->assertSame([[ + 'id' => 'abc123', + 'thumbnailUrl' => 'https://images.unsplash.com/photo-1?w=400', + 'description' => 'snowy mountains', + 'photographerName' => 'Jane Doe', + 'photographerUrl' => 'https://unsplash.com/@jane?utm_source=leantime&utm_medium=referral', + ]], $photos); + + $this->http->assertSent(fn (Request $request) => $request->hasHeader('Authorization', 'Client-ID test-key') + && $request['query'] === 'mountains'); + } + + public function test_search_returns_nothing_when_unsplash_fails(): void + { + $this->http->fake(['api.unsplash.com/*' => $this->http->response(['errors' => ['Rate Limit Exceeded']], 403)]); + + $this->assertSame([], $this->makeService()->searchUnsplash('mountains')); + } + + public function test_using_a_photo_stores_its_resized_url_and_reports_the_download(): void + { + $this->http->fake([ + 'api.unsplash.com/photos/abc123/download*' => $this->http->response(['url' => 'https://example.test']), + 'api.unsplash.com/photos/abc123' => $this->http->response($this->unsplashPhoto('https://images.unsplash.com/photo-1?ixid=1')), + ]); + + $this->assertTrue($this->makeService()->useUnsplashPhoto('abc123')); + + $this->assertSame(['https://images.unsplash.com/photo-1?ixid=1&w=2400&q=80&fm=jpg&fit=max'], $this->storedImages); + $this->http->assertSent(fn (Request $request) => str_starts_with($request->url(), 'https://api.unsplash.com/photos/abc123/download')); + } + + public function test_photo_outside_unsplash_image_host_is_refused(): void + { + $this->http->fake([ + 'api.unsplash.com/photos/abc123' => $this->http->response($this->unsplashPhoto('https://evil.test/x.jpg')), + ]); + + $this->assertFalse($this->makeService()->useUnsplashPhoto('abc123')); + $this->assertSame([], $this->storedImages); + } + + public function test_photo_id_with_path_characters_is_refused_without_a_request(): void + { + $this->assertFalse($this->makeService()->useUnsplashPhoto('../me')); + $this->assertCount(0, $this->http->recorded()); + } + + public function test_unknown_background_type_is_refused(): void + { + $service = $this->makeService(); + + $service->setBackgroundType('gradient'); + $this->assertSame(['gradient'], $this->storedTypes); + + $this->expectException(ValidationException::class); + $service->setBackgroundType('video'); + } + + public function test_upload_that_is_not_an_image_is_refused(): void + { + $fakeImage = tempnam(sys_get_temp_dir(), 'bg'); + file_put_contents($fakeImage, ''); + + session(['userdata.id' => 1]); + + $stored = $this->makeService()->useUploadedImage(['file' => [ + 'name' => 'background.jpg', + 'type' => 'image/jpeg', + 'tmp_name' => $fakeImage, + 'error' => UPLOAD_ERR_OK, + 'size' => filesize($fakeImage), + ]]); + + unlink($fakeImage); + + $this->assertFalse($stored); + $this->assertSame([], $this->storedImages); + } +} From dd1bc94d2931d44094b30a5048ce10ed1428dd33 Mon Sep 17 00:00:00 2001 From: Marcel Folaron Date: Sun, 18 Oct 2026 10:55:59 -0400 Subject: [PATCH 2/9] feat(users): webhooks get their own tab, personal access tokens come last Account settings tabs are now: Profile, Security, Settings, Notifications, Webhooks, Theme, Background, plugin tabs, Personal Access Tokens. - The personal webhook moves out of the Notifications tab into a Webhooks tab with its own form. Users::saveOwnWebhookSettings() saves it; saveOwnNotificationPreferences() no longer touches the webhook. - The Personal Access Tokens tab is rendered by the page after the plugin tabs instead of through an Auth event listener, so it is always last. The two listener classes are gone. - Clearing the webhook URL works again: ConvertEmptyStringsToNull turns the emptied field into null, which was rejected as an invalid URL. A missing or null URL now clears the webhook. - The webhook description pointed to the settings "above"; it now names the Notifications tab (all languages). Co-Authored-By: Claude Opus 5.5 --- .../Listeners/ShowPersonalTokenContent.php | 24 ----- .../Auth/Listeners/ShowPersonalTokenTab.php | 17 --- app/Domain/Auth/register.php | 13 --- app/Domain/Users/Controllers/EditOwn.php | 15 ++- app/Domain/Users/Services/Users.php | 100 ++++++++++-------- app/Domain/Users/Templates/editOwn.blade.php | 20 +++- app/Language/ar-SA.ini | 3 +- app/Language/cs-CZ.ini | 3 +- app/Language/da-DK.ini | 3 +- app/Language/de-CH.ini | 3 +- app/Language/de-DE-inf.ini | 3 +- app/Language/de-DE.ini | 3 +- app/Language/el-GR.ini | 3 +- app/Language/en-GB.ini | 3 +- app/Language/en-US.ini | 3 +- app/Language/es-419.ini | 3 +- app/Language/es-ES.ini | 3 +- app/Language/es-MX.ini | 3 +- app/Language/et-EE.ini | 3 +- app/Language/fa-IR.ini | 3 +- app/Language/fr-CH.ini | 3 +- app/Language/fr-FR.ini | 3 +- app/Language/he-IL.ini | 3 +- app/Language/hr-HR.ini | 3 +- app/Language/hu-HU.ini | 3 +- app/Language/id-ID.ini | 3 +- app/Language/is-IS.ini | 3 +- app/Language/it-IT.ini | 3 +- app/Language/ja-JP.ini | 3 +- app/Language/kaa.ini | 3 +- app/Language/km-KH.ini | 3 +- app/Language/ko-KR.ini | 3 +- app/Language/ks-IN.ini | 3 +- app/Language/lv-LV.ini | 3 +- app/Language/nl-NL.ini | 3 +- app/Language/no-NO.ini | 3 +- app/Language/pl-PL.ini | 3 +- app/Language/pt-BR.ini | 3 +- app/Language/pt-PT.ini | 3 +- app/Language/ro-RO.ini | 3 +- app/Language/ru-RU.ini | 3 +- app/Language/sk-SK.ini | 3 +- app/Language/sl-SI.ini | 3 +- app/Language/sr-SP.ini | 3 +- app/Language/sv-SE.ini | 3 +- app/Language/tr-TR.ini | 3 +- app/Language/uk-UA.ini | 3 +- app/Language/vi-VN.ini | 3 +- app/Language/zh-CN.ini | 3 +- app/Language/zh-TW.ini | 3 +- .../Domain/Users/Controllers/EditOwnTest.php | 68 ++++++++---- .../Users/Services/UsersServiceTest.php | 86 +++++++-------- 52 files changed, 253 insertions(+), 222 deletions(-) delete mode 100644 app/Domain/Auth/Listeners/ShowPersonalTokenContent.php delete mode 100644 app/Domain/Auth/Listeners/ShowPersonalTokenTab.php diff --git a/app/Domain/Auth/Listeners/ShowPersonalTokenContent.php b/app/Domain/Auth/Listeners/ShowPersonalTokenContent.php deleted file mode 100644 index cdf125716f..0000000000 --- a/app/Domain/Auth/Listeners/ShowPersonalTokenContent.php +++ /dev/null @@ -1,24 +0,0 @@ - - '; - } -} diff --git a/app/Domain/Auth/Listeners/ShowPersonalTokenTab.php b/app/Domain/Auth/Listeners/ShowPersonalTokenTab.php deleted file mode 100644 index 188415dc97..0000000000 --- a/app/Domain/Auth/Listeners/ShowPersonalTokenTab.php +++ /dev/null @@ -1,17 +0,0 @@ - '.__('tabs.personal_access_tokens').''; - } -} diff --git a/app/Domain/Auth/register.php b/app/Domain/Auth/register.php index 5f64648718..f6531fcab4 100644 --- a/app/Domain/Auth/register.php +++ b/app/Domain/Auth/register.php @@ -1,19 +1,6 @@ make(\Leantime\Core\Language::class); diff --git a/app/Domain/Users/Controllers/EditOwn.php b/app/Domain/Users/Controllers/EditOwn.php index 699250b803..8dfb3a11f9 100644 --- a/app/Domain/Users/Controllers/EditOwn.php +++ b/app/Domain/Users/Controllers/EditOwn.php @@ -44,7 +44,7 @@ public function get(): Response /** * Saves the submitted section of the own-profile form (profile, password, - * theme, locale or notifications) and redirects back to its tab with a + * theme, locale, notifications or webhook) and redirects back to its tab with a * success or error notification. * * @throws \Exception @@ -120,12 +120,21 @@ public function post(): Response if (isset($_POST['savenotifications'])) { $tab = '#notifications'; + $this->userService->saveOwnNotificationPreferences($this->userId, $_POST); + + $this->tpl->setNotification($this->language->__('notifications.changed_profile_settings_successfully'), 'success', 'profilesettings_updated'); + } + + // Save personal webhook + if (isset($_POST['savewebhooks'])) { + $tab = '#webhooks'; + try { - $this->userService->saveOwnNotificationPreferences($this->userId, $_POST); + $this->userService->saveOwnWebhookSettings($this->userId, $_POST); $this->tpl->setNotification($this->language->__('notifications.changed_profile_settings_successfully'), 'success', 'profilesettings_updated'); } catch (ValidationException $e) { - // The only validated field is the personal webhook URL; nothing was saved. + // The only validated field is the webhook URL; nothing was saved. $this->tpl->setNotification($this->language->__('notification.invalid_webhook_url'), 'error'); } catch (WebhookSettingNotSavedException $e) { // Already logged without the URL; nothing was saved. diff --git a/app/Domain/Users/Services/Users.php b/app/Domain/Users/Services/Users.php index 49b9fb9eef..996495b2f1 100644 --- a/app/Domain/Users/Services/Users.php +++ b/app/Domain/Users/Services/Users.php @@ -1310,25 +1310,11 @@ public function saveOwnLocaleSettings(int $userId, array $post): void * per-project notification levels (validated against the known relevance * levels). Cleans up the legacy muted-projects format when present. * - * Also stores the personal webhook: webhookUrl (https only, validated - * before anything is written) and the webhookEnabled checkbox. An empty - * URL disables the webhook; unchecking the box keeps the URL on file. - * Both go into one setting (Webhooks::settingKey()), written before any - * other preference and verified, so a failed save never leaves an old URL - * or opt-in live behind a success message. - * - * Omitting the webhookUrl key leaves the stored webhook unchanged, and any - * webhookEnabled sent without it is ignored. Sending webhookUrl, even as an - * empty string, sets the URL and the opt-in together, with a missing - * webhookEnabled meaning unchecked. API clients that change only the opt-in - * must therefore send the current URL as well. + * The personal webhook is saved separately, with saveOwnWebhookSettings(). * * @param int $userId The id of the user being edited. * @param array $post Raw request input. * - * @throws ValidationException When webhookUrl is not an acceptable endpoint; nothing is saved. - * @throws WebhookSettingNotSavedException When the webhook setting could not be persisted; nothing is saved. - * * @api */ public function saveOwnNotificationPreferences(int $userId, array $post): void @@ -1336,37 +1322,6 @@ public function saveOwnNotificationPreferences(int $userId, array $post): void // Self-service: pin to the authenticated user (ignore any caller-supplied id — prevents RPC IDOR). $userId = (int) session('userdata.id'); - // A payload without webhookUrl (API clients predating the webhook) leaves the webhook as it is. - if (array_key_exists('webhookUrl', $post)) { - // Validate the webhook first so a rejected URL leaves every preference untouched. - $webhookUrl = is_string($post['webhookUrl']) ? trim($post['webhookUrl']) : null; - if ($webhookUrl === null || ($webhookUrl !== '' && ! Webhooks::isValidEndpointUrl($webhookUrl))) { - throw ValidationException::withMessages(['webhookUrl' => ['notification.invalid_webhook_url']]); - } - $webhookEnabled = $webhookUrl !== '' && filter_var($post['webhookEnabled'] ?? false, FILTER_VALIDATE_BOOLEAN); - - // Persist the webhook first: if it can't be saved, fail before any other preference is touched. - $webhookSettingKey = Webhooks::settingKey($userId); - $webhookSetting = Webhooks::encodeSetting($webhookUrl, $webhookEnabled); - $webhookSaveException = null; - try { - // updateOrInsert also reports false when the row already held this exact value, so a - // false result only counts as a failure when reading the setting back shows otherwise. - $webhookSaved = $this->settingsService->saveSetting($webhookSettingKey, $webhookSetting) - || $this->settingsService->getSetting($webhookSettingKey) === $webhookSetting; - } catch (\Throwable $e) { - $webhookSaved = false; - $webhookSaveException = get_class($e); - } - - if (! $webhookSaved) { - // Class name only: the setting value and DB exception messages carry the URL and its secret. - Log::error('Personal webhook setting could not be saved', ['userId' => $userId, 'exception' => $webhookSaveException]); - - throw new WebhookSettingNotSavedException; - } - } - $row = $this->getUser($userId); $values = [ @@ -1421,6 +1376,59 @@ public function saveOwnNotificationPreferences(int $userId, array $post): void } } + /** + * Stores the current user's personal webhook: webhookUrl (https only, + * validated before anything is written) and the webhookEnabled checkbox. + * An empty or missing URL disables the webhook; unchecking the box keeps the URL on + * file. Both go into one setting (Webhooks::settingKey()), which is read + * back when the write reports no change, so a failed save never leaves an + * old URL or opt-in live behind a success message. + * + * A missing webhookEnabled means unchecked, so API clients that change + * only the opt-in must send the current URL as well. + * + * @param int $userId The id of the user being edited. + * @param array $post Raw request input. + * + * @throws ValidationException When webhookUrl is not an acceptable endpoint; nothing is saved. + * @throws WebhookSettingNotSavedException When the webhook setting could not be persisted. + * + * @api + */ + public function saveOwnWebhookSettings(int $userId, array $post): void + { + // Self-service: pin to the authenticated user (ignore any caller-supplied id — prevents RPC IDOR). + $userId = (int) session('userdata.id'); + + // A cleared field arrives as null (ConvertEmptyStringsToNull), so null clears the webhook too. + $rawWebhookUrl = $post['webhookUrl'] ?? ''; + $webhookUrl = is_string($rawWebhookUrl) ? trim($rawWebhookUrl) : null; + if ($webhookUrl === null || ($webhookUrl !== '' && ! Webhooks::isValidEndpointUrl($webhookUrl))) { + throw ValidationException::withMessages(['webhookUrl' => ['notification.invalid_webhook_url']]); + } + $webhookEnabled = $webhookUrl !== '' && filter_var($post['webhookEnabled'] ?? false, FILTER_VALIDATE_BOOLEAN); + + $webhookSettingKey = Webhooks::settingKey($userId); + $webhookSetting = Webhooks::encodeSetting($webhookUrl, $webhookEnabled); + $webhookSaveException = null; + try { + // updateOrInsert also reports false when the row already held this exact value, so a + // false result only counts as a failure when reading the setting back shows otherwise. + $webhookSaved = $this->settingsService->saveSetting($webhookSettingKey, $webhookSetting) + || $this->settingsService->getSetting($webhookSettingKey) === $webhookSetting; + } catch (\Throwable $e) { + $webhookSaved = false; + $webhookSaveException = get_class($e); + } + + if (! $webhookSaved) { + // Class name only: the setting value and DB exception messages carry the URL and its secret. + Log::error('Personal webhook setting could not be saved', ['userId' => $userId, 'exception' => $webhookSaveException]); + + throw new WebhookSettingNotSavedException; + } + } + /** * Returns the list of supported varying date-time formats used by the * profile settings screen. diff --git a/app/Domain/Users/Templates/editOwn.blade.php b/app/Domain/Users/Templates/editOwn.blade.php index 11a7f668e2..b8ba720f80 100644 --- a/app/Domain/Users/Templates/editOwn.blade.php +++ b/app/Domain/Users/Templates/editOwn.blade.php @@ -20,9 +20,11 @@
  • {!! __('tabs.security') !!}
  • {!! __('tabs.settings') !!}
  • {!! __('tabs.notifications') !!}
  • +
  • {!! __('tabs.webhooks') !!}
  • {!! __('tabs.theme') !!}
  • {!! __('tabs.background') !!}
  • @dispatchEvent('tabs') +
  • {{ __('tabs.personal_access_tokens') }}
  • @@ -458,10 +460,16 @@ class="tw-text-sm tw-border tw-border-gray-300 tw-rounded tw-px-2 tw-py-1 tw-min @endif
    -
    + + + + - {{ __('label.webhook_notifications') }} -

    {{ __('label.webhook_notifications_description') }}

    +
    +
    + +

    {{ __('label.webhook_notifications_description') }}

    +
    {{ __('label.enable_webhook_notifications') }}
    @@ -476,12 +484,16 @@ class="tw-text-sm tw-border tw-border-gray-300 tw-rounded tw-px-2 tw-py-1 tw-min
    - + @dispatchEvent('tabsContent') + +
    + +
    diff --git a/app/Language/ar-SA.ini b/app/Language/ar-SA.ini index a6651f7e2d..6e2ae70924 100644 --- a/app/Language/ar-SA.ini +++ b/app/Language/ar-SA.ini @@ -2619,7 +2619,7 @@ label.notifications_my_work_description="المهام المسندة إليك و label.notifications_muted="مكتوم" label.notifications_muted_description="لا إشعارات باستثناء الإشارات المباشرة (@)" label.webhook_notifications="ويب هوك شخصي" -label.webhook_notifications_description="أرسل إشعاراتك إلى نقطة نهاية HTTPS خاصة بك. لكل إشعار مشروع يحق لك تلقيه، يرسل Leantime طلب HTTP POST واحدًا بمحتوى JSON إلى هذا الرابط. تُرسل الطلبات في الخلفية وقد يستغرق وصولها عدة دقائق، ولا تُعاد محاولة الطلب الذي يفشل. تحدد إعدادات تلقي الإشعارات والفئات وإعدادات كل مشروع أعلاه الإشعاراتِ المؤهلة. لا يرسل الويب هوك إلا عندما يكون مفعّلاً ومحددًا له رابط. يبقى رابطك محفوظًا أثناء تعطيل الويب هوك — امسح الرابط لإزالته." +label.webhook_notifications_description="أرسل إشعاراتك إلى نقطة نهاية HTTPS خاصة بك. لكل إشعار مشروع يحق لك تلقيه، يرسل Leantime طلب HTTP POST واحدًا بمحتوى JSON إلى هذا الرابط. تُرسل الطلبات في الخلفية وقد يستغرق وصولها عدة دقائق، ولا تُعاد محاولة الطلب الذي يفشل. تحدد إعداداتك في تبويب الإشعارات (تلقي الإشعارات والفئات ومستويات كل مشروع) الإشعاراتِ المؤهلة. لا يرسل الويب هوك إلا عندما يكون مفعّلاً ومحددًا له رابط. يبقى رابطك محفوظًا أثناء تعطيل الويب هوك — امسح الرابط لإزالته." label.enable_webhook_notifications="تفعيل إشعارات الويب هوك" label.webhook_url_hint="HTTPS فقط. اتركه فارغًا لإزالة الويب هوك." label.default_notification_types="أنواع الإشعارات الافتراضية للمستخدمين الجدد" @@ -4063,3 +4063,4 @@ text.no_images_found = "لم يتم العثور على صور" notifications.background_settings_saved = "تم حفظ الخلفية" notifications.background_not_saved = "تعذر حفظ الخلفية" notifications.background_upload_failed = "تعذر حفظ الخلفية. ارفع صورة بصيغة JPG أو PNG أو GIF أو WebP." +tabs.webhooks = " ويب هوك" diff --git a/app/Language/cs-CZ.ini b/app/Language/cs-CZ.ini index 349a81bdee..fa531035a2 100644 --- a/app/Language/cs-CZ.ini +++ b/app/Language/cs-CZ.ini @@ -2622,7 +2622,7 @@ label.notifications_my_work_description="Pouze úkoly přiřazené vám, vaše p label.notifications_muted="Ztlumeno" label.notifications_muted_description="Žádná oznámení kromě přímých @zmínek" label.webhook_notifications="Osobní webhook" -label.webhook_notifications_description="Posílejte svá oznámení na vlastní HTTPS endpoint. Pro každé oznámení projektu, které byste měli dostat, odešle Leantime na tuto URL jeden požadavek HTTP POST s tělem ve formátu JSON. Požadavky se odesílají na pozadí a jejich doručení může trvat několik minut; neúspěšný požadavek se neopakuje. O tom, která oznámení se odešlou, rozhodují vaše výše uvedená nastavení příjmu oznámení, kategorií a jednotlivých projektů. Webhook odesílá pouze tehdy, je-li zapnutý a je nastavena URL. Vaše URL zůstane uložená i při vypnutém webhooku — chcete-li ji odstranit, vymažte ji." +label.webhook_notifications_description="Posílejte svá oznámení na vlastní HTTPS endpoint. Pro každé oznámení projektu, které byste měli dostat, odešle Leantime na tuto URL jeden požadavek HTTP POST s tělem ve formátu JSON. Požadavky se odesílají na pozadí a jejich doručení může trvat několik minut; neúspěšný požadavek se neopakuje. O tom, která oznámení se odešlou, rozhodují vaše nastavení na kartě Oznámení (příjem oznámení, kategorie a úrovně jednotlivých projektů). Webhook odesílá pouze tehdy, je-li zapnutý a je nastavena URL. Vaše URL zůstane uložená i při vypnutém webhooku — chcete-li ji odstranit, vymažte ji." label.enable_webhook_notifications="Zapnout oznámení přes webhook" label.webhook_url_hint="Pouze HTTPS. Ponecháním prázdného pole webhook odstraníte." label.default_notification_types="Výchozí typy oznámení pro nové uživatele" @@ -4066,3 +4066,4 @@ text.no_images_found = "Nebyly nalezeny žádné obrázky" notifications.background_settings_saved = "Pozadí bylo uloženo" notifications.background_not_saved = "Pozadí se nepodařilo uložit" notifications.background_upload_failed = "Pozadí se nepodařilo uložit. Nahrajte obrázek JPG, PNG, GIF nebo WebP." +tabs.webhooks = " Webhooky" diff --git a/app/Language/da-DK.ini b/app/Language/da-DK.ini index 0536553057..fa06eab0db 100644 --- a/app/Language/da-DK.ini +++ b/app/Language/da-DK.ini @@ -2621,7 +2621,7 @@ label.notifications_my_work_description="Kun opgaver tildelt dig, dine elementer label.notifications_muted="Lyd slået fra" label.notifications_muted_description="Ingen notifikationer undtagen direkte @omtaler" label.webhook_notifications="Personlig webhook" -label.webhook_notifications_description="Send dine notifikationer til dit eget HTTPS-endpoint. For hver projektnotifikation, du er berettiget til, sender Leantime én HTTP POST-anmodning med en JSON-body til denne URL. Anmodninger sendes i baggrunden og kan tage flere minutter om at nå frem; en anmodning, der fejler, bliver ikke forsøgt igen. Dine indstillinger ovenfor for modtagelse af notifikationer, kategorier og pr. projekt afgør, hvilke notifikationer der er berettigede. Webhooken sender kun, mens den er slået til, og en URL er angivet. Din URL forbliver gemt, mens webhooken er slået fra — ryd URL'en for at fjerne den." +label.webhook_notifications_description="Send dine notifikationer til dit eget HTTPS-endpoint. For hver projektnotifikation, du er berettiget til, sender Leantime én HTTP POST-anmodning med en JSON-body til denne URL. Anmodninger sendes i baggrunden og kan tage flere minutter om at nå frem; en anmodning, der fejler, bliver ikke forsøgt igen. Dine indstillinger under fanen Notifikationer (modtagelse af notifikationer, kategorier og niveauer pr. projekt) afgør, hvilke notifikationer der er berettigede. Webhooken sender kun, mens den er slået til, og en URL er angivet. Din URL forbliver gemt, mens webhooken er slået fra — ryd URL'en for at fjerne den." label.enable_webhook_notifications="Slå webhook-notifikationer til" label.webhook_url_hint="Kun HTTPS. Lad stå tomt for at fjerne webhooken." label.default_notification_types="Standardnotifikationstyper for nye brugere" @@ -4065,3 +4065,4 @@ text.no_images_found = "Der blev ikke fundet nogen billeder" notifications.background_settings_saved = "Baggrunden er gemt" notifications.background_not_saved = "Baggrunden kunne ikke gemmes" notifications.background_upload_failed = "Baggrunden kunne ikke gemmes. Upload et JPG-, PNG-, GIF- eller WebP-billede." +tabs.webhooks = " Webhooks" diff --git a/app/Language/de-CH.ini b/app/Language/de-CH.ini index 8bdef76169..260038eafe 100644 --- a/app/Language/de-CH.ini +++ b/app/Language/de-CH.ini @@ -1844,7 +1844,7 @@ label.notifications_my_work_description="Nur Ihnen zugewiesene Aufgaben, Ihre El label.notifications_muted="Stummgeschaltet" label.notifications_muted_description="Keine Benachrichtigungen ausser direkten @Erwähnungen" label.webhook_notifications="Persönlicher Webhook" -label.webhook_notifications_description="Senden Sie Ihre Benachrichtigungen an Ihren eigenen HTTPS-Endpunkt. Für jede Projektbenachrichtigung, die für Sie infrage kommt, sendet Leantime eine HTTP-POST-Anfrage mit einem JSON-Body an diese URL. Die Anfragen werden im Hintergrund gesendet und können einige Minuten bis zur Zustellung benötigen; fehlgeschlagene Anfragen werden nicht wiederholt. Welche Benachrichtigungen infrage kommen, bestimmen Ihre obigen Einstellungen zu Benachrichtigungsempfang, Kategorien und Projekten. Der Webhook sendet nur, solange er aktiviert und eine URL gesetzt ist. Ihre URL bleibt gespeichert, während der Webhook deaktiviert ist – leeren Sie die URL, um sie zu entfernen." +label.webhook_notifications_description="Senden Sie Ihre Benachrichtigungen an Ihren eigenen HTTPS-Endpunkt. Für jede Projektbenachrichtigung, die für Sie infrage kommt, sendet Leantime eine HTTP-POST-Anfrage mit einem JSON-Body an diese URL. Die Anfragen werden im Hintergrund gesendet und können einige Minuten bis zur Zustellung benötigen; fehlgeschlagene Anfragen werden nicht wiederholt. Welche Benachrichtigungen infrage kommen, bestimmen Ihre Einstellungen im Tab Benachrichtigungen (Benachrichtigungsempfang, Kategorien und Stufen pro Projekt). Der Webhook sendet nur, solange er aktiviert und eine URL gesetzt ist. Ihre URL bleibt gespeichert, während der Webhook deaktiviert ist – leeren Sie die URL, um sie zu entfernen." label.enable_webhook_notifications="Webhook-Benachrichtigungen aktivieren" label.webhook_url_hint="Nur HTTPS. Leer lassen, um den Webhook zu entfernen." label.default_notification_types="Standard-Benachrichtigungsarten für neue Benutzer" @@ -4172,3 +4172,4 @@ text.no_images_found = "Keine Bilder gefunden" notifications.background_settings_saved = "Hintergrund gespeichert" notifications.background_not_saved = "Der Hintergrund konnte nicht gespeichert werden" notifications.background_upload_failed = "Der Hintergrund konnte nicht gespeichert werden. Laden Sie ein Bild im Format JPG, PNG, GIF oder WebP hoch." +tabs.webhooks = " Webhooks" diff --git a/app/Language/de-DE-inf.ini b/app/Language/de-DE-inf.ini index 07d5ca05ea..4ec19c2f96 100644 --- a/app/Language/de-DE-inf.ini +++ b/app/Language/de-DE-inf.ini @@ -2621,7 +2621,7 @@ label.notifications_my_work_description="Nur dir zugewiesene Aufgaben, deine Ele label.notifications_muted="Stummgeschaltet" label.notifications_muted_description="Keine Benachrichtigungen außer direkten @Erwähnungen" label.webhook_notifications="Persönlicher Webhook" -label.webhook_notifications_description="Sende deine Benachrichtigungen an deinen eigenen HTTPS-Endpunkt. Für jede Projektbenachrichtigung, die für dich infrage kommt, sendet Leantime eine HTTP-POST-Anfrage mit einem JSON-Body an diese URL. Die Anfragen werden im Hintergrund gesendet und können einige Minuten bis zur Zustellung benötigen; fehlgeschlagene Anfragen werden nicht wiederholt. Welche Benachrichtigungen infrage kommen, bestimmen deine obigen Einstellungen zu Benachrichtigungsempfang, Kategorien und Projekten. Der Webhook sendet nur, solange er aktiviert und eine URL gesetzt ist. Deine URL bleibt gespeichert, während der Webhook deaktiviert ist – leere die URL, um sie zu entfernen." +label.webhook_notifications_description="Sende deine Benachrichtigungen an deinen eigenen HTTPS-Endpunkt. Für jede Projektbenachrichtigung, die für dich infrage kommt, sendet Leantime eine HTTP-POST-Anfrage mit einem JSON-Body an diese URL. Die Anfragen werden im Hintergrund gesendet und können einige Minuten bis zur Zustellung benötigen; fehlgeschlagene Anfragen werden nicht wiederholt. Welche Benachrichtigungen infrage kommen, bestimmen deine Einstellungen im Tab Benachrichtigungen (Benachrichtigungsempfang, Kategorien und Stufen pro Projekt). Der Webhook sendet nur, solange er aktiviert und eine URL gesetzt ist. Deine URL bleibt gespeichert, während der Webhook deaktiviert ist – leere die URL, um sie zu entfernen." label.enable_webhook_notifications="Webhook-Benachrichtigungen aktivieren" label.webhook_url_hint="Nur HTTPS. Leer lassen, um den Webhook zu entfernen." label.default_notification_types="Standard-Benachrichtigungsarten für neue Benutzer" @@ -4065,3 +4065,4 @@ text.no_images_found = "Keine Bilder gefunden" notifications.background_settings_saved = "Hintergrund gespeichert" notifications.background_not_saved = "Der Hintergrund konnte nicht gespeichert werden" notifications.background_upload_failed = "Der Hintergrund konnte nicht gespeichert werden. Lade ein Bild im Format JPG, PNG, GIF oder WebP hoch." +tabs.webhooks = " Webhooks" diff --git a/app/Language/de-DE.ini b/app/Language/de-DE.ini index a142f098fb..bda3088710 100644 --- a/app/Language/de-DE.ini +++ b/app/Language/de-DE.ini @@ -2621,7 +2621,7 @@ label.notifications_my_work_description="Nur Ihnen zugewiesene Aufgaben, Ihre El label.notifications_muted="Stummgeschaltet" label.notifications_muted_description="Keine Benachrichtigungen außer direkten @Erwähnungen" label.webhook_notifications="Persönlicher Webhook" -label.webhook_notifications_description="Senden Sie Ihre Benachrichtigungen an Ihren eigenen HTTPS-Endpunkt. Für jede Projektbenachrichtigung, die für Sie infrage kommt, sendet Leantime eine HTTP-POST-Anfrage mit einem JSON-Body an diese URL. Die Anfragen werden im Hintergrund gesendet und können einige Minuten bis zur Zustellung benötigen; fehlgeschlagene Anfragen werden nicht wiederholt. Welche Benachrichtigungen infrage kommen, bestimmen Ihre obigen Einstellungen zu Benachrichtigungsempfang, Kategorien und Projekten. Der Webhook sendet nur, solange er aktiviert und eine URL gesetzt ist. Ihre URL bleibt gespeichert, während der Webhook deaktiviert ist – leeren Sie die URL, um sie zu entfernen." +label.webhook_notifications_description="Senden Sie Ihre Benachrichtigungen an Ihren eigenen HTTPS-Endpunkt. Für jede Projektbenachrichtigung, die für Sie infrage kommt, sendet Leantime eine HTTP-POST-Anfrage mit einem JSON-Body an diese URL. Die Anfragen werden im Hintergrund gesendet und können einige Minuten bis zur Zustellung benötigen; fehlgeschlagene Anfragen werden nicht wiederholt. Welche Benachrichtigungen infrage kommen, bestimmen Ihre Einstellungen im Tab Benachrichtigungen (Benachrichtigungsempfang, Kategorien und Stufen pro Projekt). Der Webhook sendet nur, solange er aktiviert und eine URL gesetzt ist. Ihre URL bleibt gespeichert, während der Webhook deaktiviert ist – leeren Sie die URL, um sie zu entfernen." label.enable_webhook_notifications="Webhook-Benachrichtigungen aktivieren" label.webhook_url_hint="Nur HTTPS. Leer lassen, um den Webhook zu entfernen." label.default_notification_types="Standard-Benachrichtigungsarten für neue Benutzer" @@ -4065,3 +4065,4 @@ text.no_images_found = "Keine Bilder gefunden" notifications.background_settings_saved = "Hintergrund gespeichert" notifications.background_not_saved = "Der Hintergrund konnte nicht gespeichert werden" notifications.background_upload_failed = "Der Hintergrund konnte nicht gespeichert werden. Laden Sie ein Bild im Format JPG, PNG, GIF oder WebP hoch." +tabs.webhooks = " Webhooks" diff --git a/app/Language/el-GR.ini b/app/Language/el-GR.ini index da7dcc948c..b6b91cb05c 100644 --- a/app/Language/el-GR.ini +++ b/app/Language/el-GR.ini @@ -2621,7 +2621,7 @@ label.notifications_my_work_description="Μόνο εργασίες που σας label.notifications_muted="Σε σίγαση" label.notifications_muted_description="Καμία ειδοποίηση εκτός από άμεσες @αναφορές" label.webhook_notifications="Προσωπικό webhook" -label.webhook_notifications_description="Στείλτε τις ειδοποιήσεις σας στο δικό σας endpoint HTTPS. Για κάθε ειδοποίηση έργου για την οποία είστε επιλέξιμοι, το Leantime στέλνει ένα αίτημα HTTP POST με σώμα JSON σε αυτή τη διεύθυνση URL. Τα αιτήματα αποστέλλονται στο παρασκήνιο και μπορεί να χρειαστούν αρκετά λεπτά για να φτάσουν· ένα αίτημα που αποτυγχάνει δεν επαναλαμβάνεται. Οι παραπάνω ρυθμίσεις λήψης ειδοποιήσεων, κατηγοριών και ανά έργο καθορίζουν ποιες ειδοποιήσεις είναι επιλέξιμες. Το webhook στέλνει μόνο όταν είναι ενεργοποιημένο και έχει οριστεί διεύθυνση URL. Η διεύθυνση URL σας παραμένει αποθηκευμένη όσο το webhook είναι απενεργοποιημένο — διαγράψτε τη διεύθυνση URL για να την αφαιρέσετε." +label.webhook_notifications_description="Στείλτε τις ειδοποιήσεις σας στο δικό σας endpoint HTTPS. Για κάθε ειδοποίηση έργου για την οποία είστε επιλέξιμοι, το Leantime στέλνει ένα αίτημα HTTP POST με σώμα JSON σε αυτή τη διεύθυνση URL. Τα αιτήματα αποστέλλονται στο παρασκήνιο και μπορεί να χρειαστούν αρκετά λεπτά για να φτάσουν· ένα αίτημα που αποτυγχάνει δεν επαναλαμβάνεται. Οι ρυθμίσεις σας στην καρτέλα Ειδοποιήσεις (λήψη ειδοποιήσεων, κατηγορίες και επίπεδα ανά έργο) καθορίζουν ποιες ειδοποιήσεις είναι επιλέξιμες. Το webhook στέλνει μόνο όταν είναι ενεργοποιημένο και έχει οριστεί διεύθυνση URL. Η διεύθυνση URL σας παραμένει αποθηκευμένη όσο το webhook είναι απενεργοποιημένο — διαγράψτε τη διεύθυνση URL για να την αφαιρέσετε." label.enable_webhook_notifications="Ενεργοποίηση ειδοποιήσεων webhook" label.webhook_url_hint="Μόνο HTTPS. Αφήστε κενό για να αφαιρέσετε το webhook." label.default_notification_types="Προεπιλεγμένοι τύποι ειδοποιήσεων για νέους χρήστες" @@ -4065,3 +4065,4 @@ text.no_images_found = "Δεν βρέθηκαν εικόνες" notifications.background_settings_saved = "Το φόντο αποθηκεύτηκε" notifications.background_not_saved = "Δεν ήταν δυνατή η αποθήκευση του φόντου" notifications.background_upload_failed = "Δεν ήταν δυνατή η αποθήκευση του φόντου. Ανεβάστε μια εικόνα JPG, PNG, GIF ή WebP." +tabs.webhooks = " Webhooks" diff --git a/app/Language/en-GB.ini b/app/Language/en-GB.ini index 3741ad3d80..2655d61bd4 100644 --- a/app/Language/en-GB.ini +++ b/app/Language/en-GB.ini @@ -2621,7 +2621,7 @@ label.notifications_my_work_description="Only tasks assigned to you, your items, label.notifications_muted="Muted" label.notifications_muted_description="No notifications except direct @mentions" label.webhook_notifications="Personal Webhook" -label.webhook_notifications_description="Send your notifications to your own HTTPS endpoint. For every project notification you are eligible for, Leantime sends one HTTP POST request with a JSON body to this URL. Requests are sent in the background and can take several minutes to arrive; a request that fails is not retried. Your receive-notifications, category, and per-project settings above decide which notifications are eligible. The webhook only sends while it is enabled and a URL is set. Your URL stays saved while the webhook is disabled — clear the URL to remove it." +label.webhook_notifications_description="Send your notifications to your own HTTPS endpoint. For every project notification you are eligible for, Leantime sends one HTTP POST request with a JSON body to this URL. Requests are sent in the background and can take several minutes to arrive; a request that fails is not retried. Your settings in the Notifications tab (receive notifications, categories and per-project levels) decide which notifications are eligible. The webhook only sends while it is enabled and a URL is set. Your URL stays saved while the webhook is disabled — clear the URL to remove it." label.enable_webhook_notifications="Enable webhook notifications" label.webhook_url_hint="HTTPS only. Leave empty to remove the webhook." label.default_notification_types="Default notification types for new users" @@ -4065,3 +4065,4 @@ text.no_images_found = "No images found" notifications.background_settings_saved = "Background saved" notifications.background_not_saved = "The background could not be saved" notifications.background_upload_failed = "The background could not be saved. Upload a JPG, PNG, GIF or WebP image." +tabs.webhooks = " Webhooks" diff --git a/app/Language/en-US.ini b/app/Language/en-US.ini index f48aae56f5..0a0c60959f 100644 --- a/app/Language/en-US.ini +++ b/app/Language/en-US.ini @@ -918,7 +918,7 @@ label.notifications_my_work_description = "Only tasks assigned to you, your item label.notifications_muted = "Muted" label.notifications_muted_description = "No notifications except direct @mentions" label.webhook_notifications = "Personal Webhook" -label.webhook_notifications_description = "Send your notifications to your own HTTPS endpoint. For every project notification you are eligible for, Leantime sends one HTTP POST request with a JSON body to this URL. Requests are sent in the background and can take several minutes to arrive; a request that fails is not retried. Your receive-notifications, category, and per-project settings above decide which notifications are eligible. The webhook only sends while it is enabled and a URL is set. Your URL stays saved while the webhook is disabled — clear the URL to remove it." +label.webhook_notifications_description = "Send your notifications to your own HTTPS endpoint. For every project notification you are eligible for, Leantime sends one HTTP POST request with a JSON body to this URL. Requests are sent in the background and can take several minutes to arrive; a request that fails is not retried. Your settings in the Notifications tab (receive notifications, categories and per-project levels) decide which notifications are eligible. The webhook only sends while it is enabled and a URL is set. Your URL stays saved while the webhook is disabled — clear the URL to remove it." label.enable_webhook_notifications = "Enable webhook notifications" label.webhook_url_hint = "HTTPS only. Leave empty to remove the webhook." label.default_notification_types = "Default notification types for new users" @@ -4213,3 +4213,4 @@ text.no_images_found = "No images found" notifications.background_settings_saved = "Background saved" notifications.background_not_saved = "The background could not be saved" notifications.background_upload_failed = "The background could not be saved. Upload a JPG, PNG, GIF or WebP image." +tabs.webhooks = " Webhooks" diff --git a/app/Language/es-419.ini b/app/Language/es-419.ini index 5518806853..2b60fe0000 100644 --- a/app/Language/es-419.ini +++ b/app/Language/es-419.ini @@ -2621,7 +2621,7 @@ label.notifications_my_work_description="Solo las tareas asignadas a ti, tus ele label.notifications_muted="Silenciado" label.notifications_muted_description="Ninguna notificación, salvo las @menciones directas" label.webhook_notifications="Webhook personal" -label.webhook_notifications_description="Envía tus notificaciones a tu propio endpoint HTTPS. Por cada notificación de proyecto que te corresponda, Leantime envía a esta URL una solicitud HTTP POST con un cuerpo JSON. Las solicitudes se envían en segundo plano y pueden tardar varios minutos en llegar; si una solicitud falla, no se reintenta. Tu configuración de recepción de notificaciones, de categorías y por proyecto (más arriba) determina qué notificaciones te corresponden. El webhook solo envía mientras está activado y tiene una URL configurada. La URL se conserva aunque desactives el webhook; bórrala para eliminarla." +label.webhook_notifications_description="Envía tus notificaciones a tu propio endpoint HTTPS. Por cada notificación de proyecto que te corresponda, Leantime envía a esta URL una solicitud HTTP POST con un cuerpo JSON. Las solicitudes se envían en segundo plano y pueden tardar varios minutos en llegar; si una solicitud falla, no se reintenta. Tu configuración en la pestaña Notificaciones (recepción de notificaciones, categorías y niveles por proyecto) determina qué notificaciones te corresponden. El webhook solo envía mientras está activado y tiene una URL configurada. La URL se conserva aunque desactives el webhook; bórrala para eliminarla." label.enable_webhook_notifications="Activar notificaciones por webhook" label.webhook_url_hint="Solo HTTPS. Déjalo vacío para eliminar el webhook." label.default_notification_types="Tipos de notificación predeterminados para usuarios nuevos" @@ -4065,3 +4065,4 @@ text.no_images_found = "No se encontraron imágenes" notifications.background_settings_saved = "Fondo guardado" notifications.background_not_saved = "No se pudo guardar el fondo" notifications.background_upload_failed = "No se pudo guardar el fondo. Sube una imagen JPG, PNG, GIF o WebP." +tabs.webhooks = " Webhooks" diff --git a/app/Language/es-ES.ini b/app/Language/es-ES.ini index de6c2f9c22..0ff954d86d 100644 --- a/app/Language/es-ES.ini +++ b/app/Language/es-ES.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Solo las tareas asignadas a ti, tus ele label.notifications_muted="Silenciado" label.notifications_muted_description="Ninguna notificación, salvo las @menciones directas" label.webhook_notifications="Webhook personal" -label.webhook_notifications_description="Envía tus notificaciones a tu propio endpoint HTTPS. Por cada notificación de proyecto que te corresponda, Leantime envía a esta URL una petición HTTP POST con un cuerpo JSON. Las peticiones se envían en segundo plano y pueden tardar varios minutos en llegar; si una petición falla, no se reintenta. Tus ajustes de recepción de notificaciones, de categorías y por proyecto (más arriba) determinan qué notificaciones te corresponden. El webhook solo envía mientras está activado y tiene una URL configurada. La URL se conserva aunque desactives el webhook; bórrala para eliminarla." +label.webhook_notifications_description="Envía tus notificaciones a tu propio endpoint HTTPS. Por cada notificación de proyecto que te corresponda, Leantime envía a esta URL una petición HTTP POST con un cuerpo JSON. Las peticiones se envían en segundo plano y pueden tardar varios minutos en llegar; si una petición falla, no se reintenta. Tus ajustes en la pestaña Notificaciones (recepción de notificaciones, categorías y niveles por proyecto) determinan qué notificaciones te corresponden. El webhook solo envía mientras está activado y tiene una URL configurada. La URL se conserva aunque desactives el webhook; bórrala para eliminarla." label.enable_webhook_notifications="Activar notificaciones por webhook" label.webhook_url_hint="Solo HTTPS. Déjalo vacío para eliminar el webhook." label.default_notification_types="Tipos de notificación predeterminados para usuarios nuevos" @@ -4067,3 +4067,4 @@ text.no_images_found = "No se encontraron imágenes" notifications.background_settings_saved = "Fondo guardado" notifications.background_not_saved = "No se pudo guardar el fondo" notifications.background_upload_failed = "No se pudo guardar el fondo. Sube una imagen JPG, PNG, GIF o WebP." +tabs.webhooks = " Webhooks" diff --git a/app/Language/es-MX.ini b/app/Language/es-MX.ini index a12343c46d..076eb4edfa 100644 --- a/app/Language/es-MX.ini +++ b/app/Language/es-MX.ini @@ -2619,7 +2619,7 @@ label.notifications_my_work_description="Solo las tareas asignadas a ti, tus ele label.notifications_muted="Silenciado" label.notifications_muted_description="Ninguna notificación, salvo las @menciones directas" label.webhook_notifications="Webhook personal" -label.webhook_notifications_description="Envía tus notificaciones a tu propio endpoint HTTPS. Por cada notificación de proyecto que te corresponda, Leantime envía a esta URL una solicitud HTTP POST con un cuerpo JSON. Las solicitudes se envían en segundo plano y pueden tardar varios minutos en llegar; si una solicitud falla, no se reintenta. Tu configuración de recepción de notificaciones, de categorías y por proyecto (más arriba) determina qué notificaciones te corresponden. El webhook solo envía mientras está activado y tiene una URL configurada. La URL se conserva aunque desactives el webhook; bórrala para eliminarla." +label.webhook_notifications_description="Envía tus notificaciones a tu propio endpoint HTTPS. Por cada notificación de proyecto que te corresponda, Leantime envía a esta URL una solicitud HTTP POST con un cuerpo JSON. Las solicitudes se envían en segundo plano y pueden tardar varios minutos en llegar; si una solicitud falla, no se reintenta. Tu configuración en la pestaña Notificaciones (recepción de notificaciones, categorías y niveles por proyecto) determina qué notificaciones te corresponden. El webhook solo envía mientras está activado y tiene una URL configurada. La URL se conserva aunque desactives el webhook; bórrala para eliminarla." label.enable_webhook_notifications="Activar notificaciones por webhook" label.webhook_url_hint="Solo HTTPS. Déjalo vacío para eliminar el webhook." label.default_notification_types="Tipos de notificación predeterminados para usuarios nuevos" @@ -4049,3 +4049,4 @@ text.no_images_found = "No se encontraron imágenes" notifications.background_settings_saved = "Fondo guardado" notifications.background_not_saved = "No se pudo guardar el fondo" notifications.background_upload_failed = "No se pudo guardar el fondo. Sube una imagen JPG, PNG, GIF o WebP." +tabs.webhooks = " Webhooks" diff --git a/app/Language/et-EE.ini b/app/Language/et-EE.ini index 8f3b0e68b4..060b65b3c6 100644 --- a/app/Language/et-EE.ini +++ b/app/Language/et-EE.ini @@ -2621,7 +2621,7 @@ label.notifications_my_work_description="Ainult sulle määratud ülesanded, sin label.notifications_muted="Vaigistatud" label.notifications_muted_description="Teavitusi ei tule, välja arvatud otsesed @mainimised" label.webhook_notifications="Isiklik webhook" -label.webhook_notifications_description="Saada oma teavitused enda HTTPS-lõpp-punkti. Iga projektiteavituse kohta, mis sulle ette nähtud on, saadab Leantime sellele URL-ile ühe JSON-sisuga HTTP POST päringu. Päringud saadetakse taustal ja nende kohalejõudmine võib võtta mitu minutit; ebaõnnestunud päringut uuesti ei saadeta. Milliseid teavitusi saadetakse, otsustavad ülaltoodud teavituste saamise, kategooria ja projektipõhised seaded. Webhook saadab teavitusi ainult siis, kui see on lubatud ja URL on määratud. Kui webhook on keelatud, jääb sinu URL salvestatuks — selle eemaldamiseks tühjenda URL." +label.webhook_notifications_description="Saada oma teavitused enda HTTPS-lõpp-punkti. Iga projektiteavituse kohta, mis sulle ette nähtud on, saadab Leantime sellele URL-ile ühe JSON-sisuga HTTP POST päringu. Päringud saadetakse taustal ja nende kohalejõudmine võib võtta mitu minutit; ebaõnnestunud päringut uuesti ei saadeta. Milliseid teavitusi saadetakse, otsustavad sinu seaded vahekaardil Teavitused (teavituste saamine, kategooriad ja projektipõhised tasemed). Webhook saadab teavitusi ainult siis, kui see on lubatud ja URL on määratud. Kui webhook on keelatud, jääb sinu URL salvestatuks — selle eemaldamiseks tühjenda URL." label.enable_webhook_notifications="Luba webhooki teavitused" label.webhook_url_hint="Ainult HTTPS. Webhooki eemaldamiseks jäta tühjaks." label.default_notification_types="Uute kasutajate vaikimisi teavitustüübid" @@ -4065,3 +4065,4 @@ text.no_images_found = "Pilte ei leitud" notifications.background_settings_saved = "Taust salvestati" notifications.background_not_saved = "Tausta ei õnnestunud salvestada" notifications.background_upload_failed = "Tausta ei õnnestunud salvestada. Laadi üles JPG-, PNG-, GIF- või WebP-pilt." +tabs.webhooks = " Webhookid" diff --git a/app/Language/fa-IR.ini b/app/Language/fa-IR.ini index b6cb2fbe07..7c26e5572d 100644 --- a/app/Language/fa-IR.ini +++ b/app/Language/fa-IR.ini @@ -2621,7 +2621,7 @@ label.notifications_my_work_description="فقط کارهای واگذارشده label.notifications_muted="بی‌صدا شده" label.notifications_muted_description="هیچ اعلانی به‌جز @اشاره‌های مستقیم" label.webhook_notifications="وب‌هوک شخصی" -label.webhook_notifications_description="اعلان‌های خود را به نقطه پایانی HTTPS خودتان بفرستید. برای هر اعلان پروژه که واجد دریافت آن هستید، Leantime یک درخواست HTTP POST با بدنه JSON به این نشانی ارسال می‌کند. درخواست‌ها در پس‌زمینه ارسال می‌شوند و ممکن است رسیدنشان چند دقیقه طول بکشد؛ درخواستی که ناموفق باشد دوباره ارسال نمی‌شود. تنظیمات دریافت اعلان، دسته‌بندی و تنظیمات هر پروژه در بالا تعیین می‌کنند کدام اعلان‌ها واجد شرایط هستند. وب‌هوک فقط زمانی ارسال می‌کند که فعال باشد و نشانی آن تنظیم شده باشد. وقتی وب‌هوک غیرفعال است نشانی شما ذخیره می‌ماند — برای حذف آن، نشانی را پاک کنید." +label.webhook_notifications_description="اعلان‌های خود را به نقطه پایانی HTTPS خودتان بفرستید. برای هر اعلان پروژه که واجد دریافت آن هستید، Leantime یک درخواست HTTP POST با بدنه JSON به این نشانی ارسال می‌کند. درخواست‌ها در پس‌زمینه ارسال می‌شوند و ممکن است رسیدنشان چند دقیقه طول بکشد؛ درخواستی که ناموفق باشد دوباره ارسال نمی‌شود. تنظیمات شما در تب اعلان‌ها (دریافت اعلان، دسته‌بندی‌ها و سطح هر پروژه) تعیین می‌کنند کدام اعلان‌ها واجد شرایط هستند. وب‌هوک فقط زمانی ارسال می‌کند که فعال باشد و نشانی آن تنظیم شده باشد. وقتی وب‌هوک غیرفعال است نشانی شما ذخیره می‌ماند — برای حذف آن، نشانی را پاک کنید." label.enable_webhook_notifications="فعال‌سازی اعلان‌های وب‌هوک" label.webhook_url_hint="فقط HTTPS. برای حذف وب‌هوک خالی بگذارید." label.default_notification_types="انواع پیش‌فرض اعلان برای کاربران جدید" @@ -4065,3 +4065,4 @@ text.no_images_found = "تصویری یافت نشد" notifications.background_settings_saved = "پس‌زمینه ذخیره شد" notifications.background_not_saved = "پس‌زمینه ذخیره نشد" notifications.background_upload_failed = "پس‌زمینه ذخیره نشد. یک تصویر JPG، PNG، GIF یا WebP بارگذاری کنید." +tabs.webhooks = " وب‌هوک‌ها" diff --git a/app/Language/fr-CH.ini b/app/Language/fr-CH.ini index 3e424053c4..ad415ac5e2 100644 --- a/app/Language/fr-CH.ini +++ b/app/Language/fr-CH.ini @@ -1848,7 +1848,7 @@ label.notifications_my_work_description="Uniquement les tâches qui vous sont as label.notifications_muted="En sourdine" label.notifications_muted_description="Aucune notification, sauf les @mentions directes" label.webhook_notifications="Webhook personnel" -label.webhook_notifications_description="Envoyez vos notifications vers votre propre point de terminaison HTTPS. Pour chaque notification de projet à laquelle vous êtes éligible, Leantime envoie une requête HTTP POST avec un corps JSON à cette URL. Les requêtes sont envoyées en arrière-plan et peuvent mettre plusieurs minutes à arriver ; une requête en échec n'est pas relancée. Vos paramètres de réception, de catégories et par projet ci-dessus déterminent les notifications éligibles. Le webhook n'envoie que s'il est activé et qu'une URL est définie. Votre URL reste enregistrée lorsque le webhook est désactivé — videz l'URL pour la supprimer." +label.webhook_notifications_description="Envoyez vos notifications vers votre propre point de terminaison HTTPS. Pour chaque notification de projet à laquelle vous êtes éligible, Leantime envoie une requête HTTP POST avec un corps JSON à cette URL. Les requêtes sont envoyées en arrière-plan et peuvent mettre plusieurs minutes à arriver ; une requête en échec n'est pas relancée. Vos paramètres de l'onglet Notifications (réception, catégories et niveaux par projet) déterminent les notifications éligibles. Le webhook n'envoie que s'il est activé et qu'une URL est définie. Votre URL reste enregistrée lorsque le webhook est désactivé — videz l'URL pour la supprimer." label.enable_webhook_notifications="Activer les notifications par webhook" label.webhook_url_hint="HTTPS uniquement. Laissez vide pour supprimer le webhook." label.default_notification_types="Types de notification par défaut pour les nouveaux utilisateurs" @@ -3943,3 +3943,4 @@ text.no_images_found = "Aucune image trouvée" notifications.background_settings_saved = "Arrière-plan enregistré" notifications.background_not_saved = "L'arrière-plan n'a pas pu être enregistré" notifications.background_upload_failed = "L'arrière-plan n'a pas pu être enregistré. Importez une image JPG, PNG, GIF ou WebP." +tabs.webhooks = " Webhooks" diff --git a/app/Language/fr-FR.ini b/app/Language/fr-FR.ini index 1feed439a6..44589718cc 100644 --- a/app/Language/fr-FR.ini +++ b/app/Language/fr-FR.ini @@ -922,7 +922,7 @@ label.notifications_my_work_description="Uniquement les tâches qui vous sont as label.notifications_muted="En sourdine" label.notifications_muted_description="Aucune notification, sauf les @mentions directes" label.webhook_notifications="Webhook personnel" -label.webhook_notifications_description="Envoyez vos notifications vers votre propre point de terminaison HTTPS. Pour chaque notification de projet à laquelle vous êtes éligible, Leantime envoie une requête HTTP POST avec un corps JSON à cette URL. Les requêtes sont envoyées en arrière-plan et peuvent mettre plusieurs minutes à arriver ; une requête en échec n'est pas relancée. Vos paramètres de réception, de catégories et par projet ci-dessus déterminent les notifications éligibles. Le webhook n'envoie que s'il est activé et qu'une URL est définie. Votre URL reste enregistrée lorsque le webhook est désactivé — videz l'URL pour la supprimer." +label.webhook_notifications_description="Envoyez vos notifications vers votre propre point de terminaison HTTPS. Pour chaque notification de projet à laquelle vous êtes éligible, Leantime envoie une requête HTTP POST avec un corps JSON à cette URL. Les requêtes sont envoyées en arrière-plan et peuvent mettre plusieurs minutes à arriver ; une requête en échec n'est pas relancée. Vos paramètres de l'onglet Notifications (réception, catégories et niveaux par projet) déterminent les notifications éligibles. Le webhook n'envoie que s'il est activé et qu'une URL est définie. Votre URL reste enregistrée lorsque le webhook est désactivé — videz l'URL pour la supprimer." label.enable_webhook_notifications="Activer les notifications par webhook" label.webhook_url_hint="HTTPS uniquement. Laissez vide pour supprimer le webhook." label.default_notification_types="Types de notification par défaut pour les nouveaux utilisateurs" @@ -4065,3 +4065,4 @@ text.no_images_found = "Aucune image trouvée" notifications.background_settings_saved = "Arrière-plan enregistré" notifications.background_not_saved = "L'arrière-plan n'a pas pu être enregistré" notifications.background_upload_failed = "L'arrière-plan n'a pas pu être enregistré. Importez une image JPG, PNG, GIF ou WebP." +tabs.webhooks = " Webhooks" diff --git a/app/Language/he-IL.ini b/app/Language/he-IL.ini index 7d7d0525b8..148c4613a4 100644 --- a/app/Language/he-IL.ini +++ b/app/Language/he-IL.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="רק משימות שהוקצו לך, ה label.notifications_muted="מושתק" label.notifications_muted_description="ללא התראות מלבד אזכורים (@) ישירים" label.webhook_notifications="Webhook אישי" -label.webhook_notifications_description="שליחת ההתראות שלך לנקודת קצה HTTPS משלך. על כל התראת מיזם שמגיעה לך, Leantime שולח לכתובת הזאת בקשת HTTP POST אחת עם גוף JSON. הבקשות נשלחות ברקע ויכולות להגיע בעיכוב של כמה דקות; בקשה שנכשלת אינה נשלחת שוב. הגדרות קבלת ההתראות, הקטגוריות וההגדרות לפי מיזם שלמעלה קובעות אילו התראות מגיעות לך. ה־webhook שולח רק כשהוא מופעל ומוגדרת כתובת. הכתובת שלך נשארת שמורה גם כשה־webhook מושבת — כדי להסיר אותה יש לרוקן את הכתובת." +label.webhook_notifications_description="שליחת ההתראות שלך לנקודת קצה HTTPS משלך. על כל התראת מיזם שמגיעה לך, Leantime שולח לכתובת הזאת בקשת HTTP POST אחת עם גוף JSON. הבקשות נשלחות ברקע ויכולות להגיע בעיכוב של כמה דקות; בקשה שנכשלת אינה נשלחת שוב. ההגדרות שלך בלשונית התראות (קבלת התראות, קטגוריות ורמות לפי מיזם) קובעות אילו התראות מגיעות לך. ה־webhook שולח רק כשהוא מופעל ומוגדרת כתובת. הכתובת שלך נשארת שמורה גם כשה־webhook מושבת — כדי להסיר אותה יש לרוקן את הכתובת." label.enable_webhook_notifications="הפעלת התראות webhook" label.webhook_url_hint="HTTPS בלבד. יש להשאיר ריק כדי להסיר את ה־webhook." label.default_notification_types="סוגי התראות ברירת מחדל למשתמשים חדשים" @@ -4064,3 +4064,4 @@ text.no_images_found = "לא נמצאו תמונות" notifications.background_settings_saved = "הרקע נשמר" notifications.background_not_saved = "לא ניתן היה לשמור את הרקע" notifications.background_upload_failed = "לא ניתן היה לשמור את הרקע. העלו תמונת JPG, PNG, GIF או WebP." +tabs.webhooks = " Webhooks" diff --git a/app/Language/hr-HR.ini b/app/Language/hr-HR.ini index e99b1bc927..54b0fdb037 100644 --- a/app/Language/hr-HR.ini +++ b/app/Language/hr-HR.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Samo zadaci dodijeljeni vama, vaše sta label.notifications_muted="Utišano" label.notifications_muted_description="Bez obavijesti osim izravnih @spominjanja" label.webhook_notifications="Osobni webhook" -label.webhook_notifications_description="Šaljite svoje obavijesti na vlastitu HTTPS krajnju točku. Za svaku obavijest projekta na koju imate pravo Leantime šalje jedan HTTP POST zahtjev s JSON tijelom na ovaj URL. Zahtjevi se šalju u pozadini i mogu stići tek nakon nekoliko minuta; neuspjeli zahtjev ne šalje se ponovno. Vaše postavke primanja obavijesti, kategorija i postavke po projektu iznad određuju na koje obavijesti imate pravo. Webhook šalje samo dok je uključen i dok je URL postavljen. Vaš URL ostaje spremljen dok je webhook isključen — ispraznite URL da biste ga uklonili." +label.webhook_notifications_description="Šaljite svoje obavijesti na vlastitu HTTPS krajnju točku. Za svaku obavijest projekta na koju imate pravo Leantime šalje jedan HTTP POST zahtjev s JSON tijelom na ovaj URL. Zahtjevi se šalju u pozadini i mogu stići tek nakon nekoliko minuta; neuspjeli zahtjev ne šalje se ponovno. Vaše postavke na kartici Obavijesti (primanje obavijesti, kategorije i razine po projektu) određuju na koje obavijesti imate pravo. Webhook šalje samo dok je uključen i dok je URL postavljen. Vaš URL ostaje spremljen dok je webhook isključen — ispraznite URL da biste ga uklonili." label.enable_webhook_notifications="Uključi obavijesti putem webhooka" label.webhook_url_hint="Samo HTTPS. Ostavite prazno da biste uklonili webhook." label.default_notification_types="Zadane vrste obavijesti za nove korisnike" @@ -4064,3 +4064,4 @@ text.no_images_found = "Nema pronađenih slika" notifications.background_settings_saved = "Pozadina je spremljena" notifications.background_not_saved = "Pozadinu nije moguće spremiti" notifications.background_upload_failed = "Pozadinu nije moguće spremiti. Učitajte sliku u formatu JPG, PNG, GIF ili WebP." +tabs.webhooks = " Webhookovi" diff --git a/app/Language/hu-HU.ini b/app/Language/hu-HU.ini index 2b2aede736..29821337ee 100644 --- a/app/Language/hu-HU.ini +++ b/app/Language/hu-HU.ini @@ -2629,7 +2629,7 @@ label.notifications_my_work_description="Csak az Önhöz rendelt feladatok, az label.notifications_muted="Némítva" label.notifications_muted_description="Nincs értesítés, kivéve a közvetlen @említéseket" label.webhook_notifications="Személyes webhook" -label.webhook_notifications_description="Értesítéseit a saját HTTPS-végpontjára is küldheti. A Leantime minden olyan projektértesítésnél, amelyre Ön jogosult, egy HTTP POST kérést küld JSON-törzzsel erre az URL-címre. A kérések a háttérben kerülnek elküldésre, és több percig is eltarthat, mire megérkeznek; a sikertelen kéréseket a rendszer nem küldi újra. A fenti értesítésfogadási, kategória- és projektenkénti beállítások határozzák meg, mely értesítésekre jogosult. A webhook csak akkor küld, ha engedélyezve van, és meg van adva URL-cím. Az URL-cím a webhook letiltása esetén is mentve marad — az eltávolításához ürítse ki az URL mezőt." +label.webhook_notifications_description="Értesítéseit a saját HTTPS-végpontjára is küldheti. A Leantime minden olyan projektértesítésnél, amelyre Ön jogosult, egy HTTP POST kérést küld JSON-törzzsel erre az URL-címre. A kérések a háttérben kerülnek elküldésre, és több percig is eltarthat, mire megérkeznek; a sikertelen kéréseket a rendszer nem küldi újra. Az Értesítések lapon megadott beállításai (értesítésfogadás, kategóriák és projektenkénti szintek) határozzák meg, mely értesítésekre jogosult. A webhook csak akkor küld, ha engedélyezve van, és meg van adva URL-cím. Az URL-cím a webhook letiltása esetén is mentve marad — az eltávolításához ürítse ki az URL mezőt." label.enable_webhook_notifications="Webhook-értesítések engedélyezése" label.webhook_url_hint="Csak HTTPS. Hagyja üresen a webhook eltávolításához." label.default_notification_types="Alapértelmezett értesítéstípusok új felhasználók számára" @@ -4070,3 +4070,4 @@ text.no_images_found = "Nem található kép" notifications.background_settings_saved = "A háttér mentve" notifications.background_not_saved = "A hátteret nem sikerült menteni" notifications.background_upload_failed = "A hátteret nem sikerült menteni. Töltsön fel JPG, PNG, GIF vagy WebP képet." +tabs.webhooks = " Webhookok" diff --git a/app/Language/id-ID.ini b/app/Language/id-ID.ini index 9575d1f286..78dc20dd19 100644 --- a/app/Language/id-ID.ini +++ b/app/Language/id-ID.ini @@ -3516,7 +3516,7 @@ text.app_url_mismatch_warning = "Tautan dalam email mengarah ke %s, tetapi Anda text.confirm_delete = "Anda yakin ingin menghapus item ini?" label.chart_metric = "Metrik bagan" label.webhook_notifications = "Webhook Pribadi" -label.webhook_notifications_description = "Kirim notifikasi Anda ke endpoint HTTPS milik Anda sendiri. Untuk setiap notifikasi proyek yang berlaku bagi Anda, Leantime mengirim satu permintaan HTTP POST dengan isi JSON ke URL ini. Permintaan dikirim di latar belakang dan bisa memerlukan beberapa menit untuk tiba; permintaan yang gagal tidak dicoba ulang. Pengaturan terima notifikasi, kategori, dan per proyek di atas menentukan notifikasi mana yang berlaku. Webhook hanya mengirim selama diaktifkan dan URL telah diatur. URL Anda tetap tersimpan selama webhook dinonaktifkan — kosongkan URL untuk menghapusnya." +label.webhook_notifications_description ="Kirim notifikasi Anda ke endpoint HTTPS milik Anda sendiri. Untuk setiap notifikasi proyek yang berlaku bagi Anda, Leantime mengirim satu permintaan HTTP POST dengan isi JSON ke URL ini. Permintaan dikirim di latar belakang dan bisa memerlukan beberapa menit untuk tiba; permintaan yang gagal tidak dicoba ulang. Pengaturan Anda di tab Notifikasi (terima notifikasi, kategori, dan tingkat per proyek) menentukan notifikasi mana yang berlaku. Webhook hanya mengirim selama diaktifkan dan URL telah diatur. URL Anda tetap tersimpan selama webhook dinonaktifkan — kosongkan URL untuk menghapusnya." label.enable_webhook_notifications = "Aktifkan notifikasi webhook" label.webhook_url_hint = "Hanya HTTPS. Biarkan kosong untuk menghapus webhook." label.kanban_view_menu = "Kartu" @@ -4228,3 +4228,4 @@ text.no_images_found = "Gambar tidak ditemukan" notifications.background_settings_saved = "Latar belakang disimpan" notifications.background_not_saved = "Latar belakang tidak dapat disimpan" notifications.background_upload_failed = "Latar belakang tidak dapat disimpan. Unggah gambar JPG, PNG, GIF, atau WebP." +tabs.webhooks = " Webhook" diff --git a/app/Language/is-IS.ini b/app/Language/is-IS.ini index 973fd1275e..510001243e 100644 --- a/app/Language/is-IS.ini +++ b/app/Language/is-IS.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Aðeins verk sem eru úthlutuð þér, label.notifications_muted="Þaggað" label.notifications_muted_description="Engar tilkynningar nema beinar @-tilvísanir" label.webhook_notifications="Persónulegur vefkrókur" -label.webhook_notifications_description="Sendu tilkynningarnar þínar á þinn eigin HTTPS-endapunkt. Fyrir hverja verkefnatilkynningu sem á við þig sendir Leantime eina HTTP POST-beiðni með JSON-meginmáli á þessa slóð. Beiðnir eru sendar í bakgrunni og getur tekið nokkrar mínútur fyrir þær að berast; beiðni sem mistekst er ekki reynd aftur. Stillingarnar hér að ofan fyrir móttöku tilkynninga, flokka og einstök verkefni ráða því hvaða tilkynningar eiga við. Vefkrókurinn sendir aðeins þegar hann er virkur og slóð hefur verið stillt. Slóðin helst vistuð þótt vefkrókurinn sé óvirkur — hreinsaðu slóðina til að fjarlægja hana." +label.webhook_notifications_description="Sendu tilkynningarnar þínar á þinn eigin HTTPS-endapunkt. Fyrir hverja verkefnatilkynningu sem á við þig sendir Leantime eina HTTP POST-beiðni með JSON-meginmáli á þessa slóð. Beiðnir eru sendar í bakgrunni og getur tekið nokkrar mínútur fyrir þær að berast; beiðni sem mistekst er ekki reynd aftur. Stillingarnar þínar á flipanum Tilkynningar (móttaka tilkynninga, flokkar og stig fyrir einstök verkefni) ráða því hvaða tilkynningar eiga við. Vefkrókurinn sendir aðeins þegar hann er virkur og slóð hefur verið stillt. Slóðin helst vistuð þótt vefkrókurinn sé óvirkur — hreinsaðu slóðina til að fjarlægja hana." label.enable_webhook_notifications="Virkja tilkynningar í gegnum vefkrók" label.webhook_url_hint="Aðeins HTTPS. Skildu eftir autt til að fjarlægja vefkrókinn." label.default_notification_types="Sjálfgefnar tegundir tilkynninga fyrir nýja notendur" @@ -4064,3 +4064,4 @@ text.no_images_found = "Engar myndir fundust" notifications.background_settings_saved = "Bakgrunnur vistaður" notifications.background_not_saved = "Ekki tókst að vista bakgrunninn" notifications.background_upload_failed = "Ekki tókst að vista bakgrunninn. Hladdu upp JPG, PNG, GIF eða WebP mynd." +tabs.webhooks = " Vefkrókar" diff --git a/app/Language/it-IT.ini b/app/Language/it-IT.ini index 478df8ea0e..5d053b20df 100644 --- a/app/Language/it-IT.ini +++ b/app/Language/it-IT.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Solo le attività assegnate a te, i tuo label.notifications_muted="Silenziato" label.notifications_muted_description="Nessuna notifica, tranne le @menzioni dirette" label.webhook_notifications="Webhook personale" -label.webhook_notifications_description="Invia le tue notifiche al tuo endpoint HTTPS. Per ogni notifica di progetto che puoi ricevere, Leantime invia a questo URL una richiesta HTTP POST con un corpo JSON. Le richieste vengono inviate in background e possono impiegare diversi minuti ad arrivare; una richiesta non riuscita non viene ripetuta. Le impostazioni di ricezione, per categoria e per progetto qui sopra determinano quali notifiche puoi ricevere. Il webhook invia solo quando è attivo e un URL è impostato. Il tuo URL resta salvato anche con il webhook disattivato: svuota l'URL per rimuoverlo." +label.webhook_notifications_description="Invia le tue notifiche al tuo endpoint HTTPS. Per ogni notifica di progetto che puoi ricevere, Leantime invia a questo URL una richiesta HTTP POST con un corpo JSON. Le richieste vengono inviate in background e possono impiegare diversi minuti ad arrivare; una richiesta non riuscita non viene ripetuta. Le tue impostazioni nella scheda Notifiche (ricezione, categorie e livelli per progetto) determinano quali notifiche puoi ricevere. Il webhook invia solo quando è attivo e un URL è impostato. Il tuo URL resta salvato anche con il webhook disattivato: svuota l'URL per rimuoverlo." label.enable_webhook_notifications="Attiva le notifiche webhook" label.webhook_url_hint="Solo HTTPS. Lascia vuoto per rimuovere il webhook." label.default_notification_types="Tipi di notifica predefiniti per i nuovi utenti" @@ -4064,3 +4064,4 @@ text.no_images_found = "Nessuna immagine trovata" notifications.background_settings_saved = "Sfondo salvato" notifications.background_not_saved = "Non è stato possibile salvare lo sfondo" notifications.background_upload_failed = "Non è stato possibile salvare lo sfondo. Carica un'immagine JPG, PNG, GIF o WebP." +tabs.webhooks = " Webhook" diff --git a/app/Language/ja-JP.ini b/app/Language/ja-JP.ini index 5e37415ff5..c2126fd7c5 100644 --- a/app/Language/ja-JP.ini +++ b/app/Language/ja-JP.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="自分に割り当てられたタスク label.notifications_muted="ミュート中" label.notifications_muted_description="直接の@メンション以外は通知しません" label.webhook_notifications="個人用Webhook" -label.webhook_notifications_description="通知を自分のHTTPSエンドポイントに送信します。受信対象となるプロジェクト通知ごとに、LeantimeはJSON本文を含むHTTP POSTリクエストをこのURLに1件送信します。リクエストはバックグラウンドで送信されるため、届くまで数分かかることがあります。失敗したリクエストは再送されません。どの通知が対象になるかは、上記の通知の受信、カテゴリ、プロジェクトごとの設定で決まります。Webhookは有効化されていてURLが設定されている場合にのみ送信されます。Webhookを無効にしてもURLは保存されたままです。削除するにはURLを消去してください。" +label.webhook_notifications_description="通知を自分のHTTPSエンドポイントに送信します。受信対象となるプロジェクト通知ごとに、LeantimeはJSON本文を含むHTTP POSTリクエストをこのURLに1件送信します。リクエストはバックグラウンドで送信されるため、届くまで数分かかることがあります。失敗したリクエストは再送されません。どの通知が対象になるかは、通知タブの設定(通知の受信、カテゴリ、プロジェクトごとのレベル)で決まります。Webhookは有効化されていてURLが設定されている場合にのみ送信されます。Webhookを無効にしてもURLは保存されたままです。削除するにはURLを消去してください。" label.enable_webhook_notifications="Webhook通知を有効にする" label.webhook_url_hint="HTTPSのみ。空欄にするとWebhookを削除します。" label.default_notification_types="新規ユーザーのデフォルト通知種別" @@ -4064,3 +4064,4 @@ text.no_images_found = "画像が見つかりません" notifications.background_settings_saved = "背景を保存しました" notifications.background_not_saved = "背景を保存できませんでした" notifications.background_upload_failed = "背景を保存できませんでした。JPG、PNG、GIF、WebPのいずれかの画像をアップロードしてください。" +tabs.webhooks = " Webhook" diff --git a/app/Language/kaa.ini b/app/Language/kaa.ini index 94e6ced7c5..da8d14c4c4 100644 --- a/app/Language/kaa.ini +++ b/app/Language/kaa.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Tek sizge tayınlanǵan wazıypalar, si label.notifications_muted="Únsiz" label.notifications_muted_description="Tikkeley @belgilewlerden basqa xabarlandırıwlar joq" label.webhook_notifications="Jeke webhook" -label.webhook_notifications_description="Xabarlandırıwlarıńızdı óz HTTPS mánzilińizge jiberiń. Siz alıwǵa haqılı bolǵan hár bir joba xabarlandırıwı ushın Leantime usı URL mánzilge JSON mazmunlı bir HTTP POST sorawın jiberedi. Sorawlar fonda jiberiledi hám jetip barıwı ushın bir neshe minut ketiwi múmkin; sátsiz bolǵan soraw qaytalanbaydı. Qaysı xabarlandırıwlardıń jiberiletuǵının joqarıdaǵı xabarlandırıwlardı alıw, kategoriya hám jobalar boyınsha sazlawlarıńız anıqlaydı. Webhook tek qosılǵan hám URL kiritilgen waqıtta jiberedi. Webhook óshirilgen waqıtta URL saqlanıp qaladı — onı alıp taslaw ushın URL maydanın tazalań." +label.webhook_notifications_description="Xabarlandırıwlarıńızdı óz HTTPS mánzilińizge jiberiń. Siz alıwǵa haqılı bolǵan hár bir joba xabarlandırıwı ushın Leantime usı URL mánzilge JSON mazmunlı bir HTTP POST sorawın jiberedi. Sorawlar fonda jiberiledi hám jetip barıwı ushın bir neshe minut ketiwi múmkin; sátsiz bolǵan soraw qaytalanbaydı. Qaysı xabarlandırıwlardıń jiberiletuǵının Xabarlandırıwlar yorlıǵındaǵı sazlawlarıńız (xabarlandırıwlardı alıw, kategoriyalar hám jobalar boyınsha dárejeler) anıqlaydı. Webhook tek qosılǵan hám URL kiritilgen waqıtta jiberedi. Webhook óshirilgen waqıtta URL saqlanıp qaladı — onı alıp taslaw ushın URL maydanın tazalań." label.enable_webhook_notifications="Webhook xabarlandırıwların qosıw" label.webhook_url_hint="Tek HTTPS. Webhook baylanısın óshiriw ushın bos qaldırıń." label.default_notification_types="Jańa paydalanıwshılar ushın standart xabarlandırıw túrleri" @@ -4064,3 +4064,4 @@ text.no_images_found = "Súwretler tabılmadı" notifications.background_settings_saved = "Fon saqlandı" notifications.background_not_saved = "Fondı saqlaw múmkin bolmadı" notifications.background_upload_failed = "Fondı saqlaw múmkin bolmadı. JPG, PNG, GIF yamasa WebP súwretin júkleń." +tabs.webhooks = " Webhooklar" diff --git a/app/Language/km-KH.ini b/app/Language/km-KH.ini index 7167e2efb9..429456fd2a 100644 --- a/app/Language/km-KH.ini +++ b/app/Language/km-KH.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="តែកិច្ចការដែល label.notifications_muted="បានបិទសំឡេង" label.notifications_muted_description="គ្មានការជូនដំណឹង លើកលែងតែ @mentions ផ្ទាល់" label.webhook_notifications="Webhook ផ្ទាល់ខ្លួន" -label.webhook_notifications_description="ផ្ញើការជូនដំណឹងរបស់អ្នកទៅកាន់ HTTPS endpoint ផ្ទាល់ខ្លួនរបស់អ្នក។ សម្រាប់រាល់ការជូនដំណឹងគម្រោងដែលអ្នកមានសិទ្ធិទទួល Leantime ផ្ញើសំណើ HTTP POST មួយដែលមានតួ JSON ទៅកាន់ URL នេះ។ សំណើត្រូវបានផ្ញើនៅផ្ទៃខាងក្រោយ ហើយអាចចំណាយពេលច្រើននាទីដើម្បីមកដល់។ សំណើដែលបរាជ័យនឹងមិនត្រូវបានព្យាយាមម្តងទៀតទេ។ ការកំណត់ទទួលការជូនដំណឹង ប្រភេទ និងការកំណត់តាមគម្រោងខាងលើរបស់អ្នក សម្រេចថាការជូនដំណឹងណាខ្លះមានសិទ្ធិ។ Webhook ផ្ញើតែនៅពេលវាត្រូវបានបើក ហើយមាន URL កំណត់ប៉ុណ្ណោះ។ URL របស់អ្នកនៅតែរក្សាទុកពេល webhook ត្រូវបានបិទ — សម្អាត URL ដើម្បីដកវាចេញ។" +label.webhook_notifications_description="ផ្ញើការជូនដំណឹងរបស់អ្នកទៅកាន់ HTTPS endpoint ផ្ទាល់ខ្លួនរបស់អ្នក។ សម្រាប់រាល់ការជូនដំណឹងគម្រោងដែលអ្នកមានសិទ្ធិទទួល Leantime ផ្ញើសំណើ HTTP POST មួយដែលមានតួ JSON ទៅកាន់ URL នេះ។ សំណើត្រូវបានផ្ញើនៅផ្ទៃខាងក្រោយ ហើយអាចចំណាយពេលច្រើននាទីដើម្បីមកដល់។ សំណើដែលបរាជ័យនឹងមិនត្រូវបានព្យាយាមម្តងទៀតទេ។ ការកំណត់របស់អ្នកនៅក្នុងផ្ទាំង ការជូនដំណឹងច្រើន (ការទទួលការជូនដំណឹង ប្រភេទ និងកម្រិតតាមគម្រោង) សម្រេចថាការជូនដំណឹងណាខ្លះមានសិទ្ធិ។ Webhook ផ្ញើតែនៅពេលវាត្រូវបានបើក ហើយមាន URL កំណត់ប៉ុណ្ណោះ។ URL របស់អ្នកនៅតែរក្សាទុកពេល webhook ត្រូវបានបិទ — សម្អាត URL ដើម្បីដកវាចេញ។" label.enable_webhook_notifications="បើកការជូនដំណឹងតាម webhook" label.webhook_url_hint="HTTPS តែប៉ុណ្ណោះ។ ទុកទទេដើម្បីដក webhook ចេញ។" label.default_notification_types="ប្រភេទការជូនដំណឹងលំនាំដើមសម្រាប់អ្នកប្រើថ្មី" @@ -4064,3 +4064,4 @@ text.no_images_found = "រកមិនឃើញរូបភាព" notifications.background_settings_saved = "បានរក្សាទុកផ្ទៃក្រោយ" notifications.background_not_saved = "មិនអាចរក្សាទុកផ្ទៃក្រោយបានទេ" notifications.background_upload_failed = "មិនអាចរក្សាទុកផ្ទៃក្រោយបានទេ។ សូមអាប់ឡូតរូបភាព JPG, PNG, GIF ឬ WebP។" +tabs.webhooks = " Webhook" diff --git a/app/Language/ko-KR.ini b/app/Language/ko-KR.ini index d1aebeadde..bc320a3f5c 100644 --- a/app/Language/ko-KR.ini +++ b/app/Language/ko-KR.ini @@ -2740,7 +2740,7 @@ label.telegram_topic_id = "토픽 ID" label.open_in_leantime = "Leantime에서 열기" label.assigned_to = "담당자" label.webhook_notifications = "개인 웹훅" -label.webhook_notifications_description = "알림을 내 HTTPS 엔드포인트로 보내요. 내가 받을 수 있는 모든 프로젝트 알림마다 Leantime이 이 URL로 JSON 본문이 담긴 HTTP POST 요청을 하나씩 보내요. 요청은 백그라운드에서 전송되며 도착하기까지 몇 분이 걸릴 수 있고, 실패한 요청은 다시 시도하지 않아요. 받을 수 있는 알림은 위의 알림 수신, 카테고리, 프로젝트별 설정에 따라 결정돼요. 웹훅은 활성화되어 있고 URL이 설정된 경우에만 전송돼요. 웹훅을 비활성화해도 URL은 저장된 상태로 유지되니, 삭제하려면 URL을 지우세요." +label.webhook_notifications_description ="알림을 내 HTTPS 엔드포인트로 보내요. 내가 받을 수 있는 모든 프로젝트 알림마다 Leantime이 이 URL로 JSON 본문이 담긴 HTTP POST 요청을 하나씩 보내요. 요청은 백그라운드에서 전송되며 도착하기까지 몇 분이 걸릴 수 있고, 실패한 요청은 다시 시도하지 않아요. 받을 수 있는 알림은 알림 탭의 설정(알림 수신, 카테고리, 프로젝트별 수준)에 따라 결정돼요. 웹훅은 활성화되어 있고 URL이 설정된 경우에만 전송돼요. 웹훅을 비활성화해도 URL은 저장된 상태로 유지되니, 삭제하려면 URL을 지우세요." label.enable_webhook_notifications = "웹훅 알림 사용" label.webhook_url_hint = "HTTPS만 지원해요. 웹훅을 삭제하려면 비워 두세요." label.kanban_view_menu = "카드" @@ -4129,3 +4129,4 @@ text.no_images_found = "이미지를 찾을 수 없어요" notifications.background_settings_saved = "배경을 저장했어요" notifications.background_not_saved = "배경을 저장할 수 없어요" notifications.background_upload_failed = "배경을 저장할 수 없어요. JPG, PNG, GIF 또는 WebP 이미지를 업로드하세요." +tabs.webhooks = " 웹훅" diff --git a/app/Language/ks-IN.ini b/app/Language/ks-IN.ini index cfe0fefaeb..42e0edd6a8 100644 --- a/app/Language/ks-IN.ini +++ b/app/Language/ks-IN.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="صِرف تۄہہِ اسائن کٔرم label.notifications_muted="خاموش" label.notifications_muted_description="سیدِ سیود @مینشن ورٲے کانٛہہ نوٹیفکیشن نہٕ" label.webhook_notifications="ذٲتی ویب ہُک" -label.webhook_notifications_description="پننۍ نوٹیفکیشن سوزِو پننِس HTTPS اینڈ پوائنٹس پؠٹھ۔ پرٛتھ تتھ پروجیکٹ نوٹیفکیشنہٕ خٲطرٕ یَمہٕ کِس تۄہہِ حقدار چھِو، چھُ Leantime یتھ URL پؠٹھ JSON باڈی سٟتۍ اکھ HTTP POST ریکویسٹ سوزان۔ ریکویسٹ چھِ پس منظرس منٛز سوزنہٕ یِوان تہٕ واتنس ہیکن واریاہ منٹ لگِتھ؛ ناکام ریکویسٹ چھُ نہٕ دوبارہ سوزنہٕ یِوان۔ ہیٚرِ کنہِ تُہٕنٛزہٕ نوٹیفکیشن حاصل کرنٕچ، کیٹیگری تہٕ پروجیکٹ وار سیٹنگز چھےٚ فیصلہ کران زِ کَم نوٹیفکیشن چھِ اہل۔ ویب ہُک چھُ صِرف تیٚلہِ سوزان ییٚلہِ یہِ فعال آسہِ تہٕ URL سیٹ آسہِ۔ ویب ہُک غیر فعال آسنس دوران چھُ تُہُنٛد URL محفوظ روزان — اَتھ ہٹاونہٕ خٲطرٕ کٔرِو URL صاف۔" +label.webhook_notifications_description="پننۍ نوٹیفکیشن سوزِو پننِس HTTPS اینڈ پوائنٹس پؠٹھ۔ پرٛتھ تتھ پروجیکٹ نوٹیفکیشنہٕ خٲطرٕ یَمہٕ کِس تۄہہِ حقدار چھِو، چھُ Leantime یتھ URL پؠٹھ JSON باڈی سٟتۍ اکھ HTTP POST ریکویسٹ سوزان۔ ریکویسٹ چھِ پس منظرس منٛز سوزنہٕ یِوان تہٕ واتنس ہیکن واریاہ منٹ لگِتھ؛ ناکام ریکویسٹ چھُ نہٕ دوبارہ سوزنہٕ یِوان۔ نوٹیفکیشن ٹیبَس منٛز تُہٕنٛزہٕ سیٹنگز (نوٹیفکیشن حاصل کرنٕچ، کیٹیگری تہٕ پروجیکٹ وار سطحہٕ) چھِ فیصلہ کران زِ کَم نوٹیفکیشن چھِ اہل۔ ویب ہُک چھُ صِرف تیٚلہِ سوزان ییٚلہِ یہِ فعال آسہِ تہٕ URL سیٹ آسہِ۔ ویب ہُک غیر فعال آسنس دوران چھُ تُہُنٛد URL محفوظ روزان — اَتھ ہٹاونہٕ خٲطرٕ کٔرِو URL صاف۔" label.enable_webhook_notifications="ویب ہُک نوٹیفکیشن فعال کٔرِو" label.webhook_url_hint="صِرف HTTPS۔ ویب ہُک ہٹاونہٕ خٲطرٕ ترٲوِو خٲلی۔" label.default_notification_types="نوٚو یوزرن خٲطرٕ ڈیفالٹ نوٹیفکیشن قسم" @@ -4064,3 +4064,4 @@ text.no_images_found = "کانہہ تصویر ناہ لبنہٕ آمت" notifications.background_settings_saved = "پس منظر گوٚو محفوظ" notifications.background_not_saved = "پس منظر ہیوٚک نہٕ محفوظ سپدِتھ" notifications.background_upload_failed = "پس منظر ہیوٚک نہٕ محفوظ سپدِتھ۔ JPG، PNG، GIF یا WebP تصویر اپلوڈ کٔرِو۔" +tabs.webhooks = " ویب ہُک" diff --git a/app/Language/lv-LV.ini b/app/Language/lv-LV.ini index 42d4a1c60c..3908d2b1a8 100644 --- a/app/Language/lv-LV.ini +++ b/app/Language/lv-LV.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Tikai jums piešķirtie uzdevumi, jūsu label.notifications_muted="Apklusināts" label.notifications_muted_description="Nav paziņojumu, izņemot tiešos @pieminējumus" label.webhook_notifications="Personīgā tīmekļa aizķere" -label.webhook_notifications_description="Sūtiet savus paziņojumus uz savu HTTPS galapunktu. Par katru projekta paziņojumu, ko varat saņemt, Leantime nosūta uz šo URL vienu HTTP POST pieprasījumu ar JSON saturu. Pieprasījumi tiek sūtīti fonā, un to piegāde var aizņemt vairākas minūtes; neveiksmīgs pieprasījums netiek atkārtots. Jūsu iepriekš norādītie paziņojumu saņemšanas, kategoriju un projektu iestatījumi nosaka, kuri paziņojumi tiek sūtīti. Tīmekļa aizķere sūta paziņojumus tikai tad, ja tā ir iespējota un ir iestatīts URL. Kamēr tīmekļa aizķere ir atspējota, jūsu URL paliek saglabāts — lai to noņemtu, notīriet URL." +label.webhook_notifications_description="Sūtiet savus paziņojumus uz savu HTTPS galapunktu. Par katru projekta paziņojumu, ko varat saņemt, Leantime nosūta uz šo URL vienu HTTP POST pieprasījumu ar JSON saturu. Pieprasījumi tiek sūtīti fonā, un to piegāde var aizņemt vairākas minūtes; neveiksmīgs pieprasījums netiek atkārtots. Jūsu iestatījumi cilnē Paziņojumi (paziņojumu saņemšana, kategorijas un līmeņi katram projektam) nosaka, kuri paziņojumi tiek sūtīti. Tīmekļa aizķere sūta paziņojumus tikai tad, ja tā ir iespējota un ir iestatīts URL. Kamēr tīmekļa aizķere ir atspējota, jūsu URL paliek saglabāts — lai to noņemtu, notīriet URL." label.enable_webhook_notifications="Iespējot tīmekļa aizķeres paziņojumus" label.webhook_url_hint="Tikai HTTPS. Atstājiet tukšu, lai noņemtu tīmekļa aizķeri." label.default_notification_types="Noklusējuma paziņojumu veidi jauniem lietotājiem" @@ -4064,3 +4064,4 @@ text.no_images_found = "Attēli nav atrasti" notifications.background_settings_saved = "Fons saglabāts" notifications.background_not_saved = "Neizdevās saglabāt fonu" notifications.background_upload_failed = "Neizdevās saglabāt fonu. Augšupielādējiet JPG, PNG, GIF vai WebP attēlu." +tabs.webhooks = " Tīmekļa aizķeres" diff --git a/app/Language/nl-NL.ini b/app/Language/nl-NL.ini index 3b04efe343..9dc4e1a1da 100644 --- a/app/Language/nl-NL.ini +++ b/app/Language/nl-NL.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Alleen taken die aan jou zijn toegeweze label.notifications_muted="Gedempt" label.notifications_muted_description="Geen meldingen behalve directe @vermeldingen" label.webhook_notifications="Persoonlijke webhook" -label.webhook_notifications_description="Stuur je meldingen naar je eigen HTTPS-endpoint. Voor elke projectmelding die voor jou in aanmerking komt, stuurt Leantime één HTTP POST-verzoek met een JSON-body naar deze URL. Verzoeken worden op de achtergrond verzonden en kunnen enkele minuten onderweg zijn; een mislukt verzoek wordt niet opnieuw geprobeerd. Je instellingen hierboven voor het ontvangen van meldingen, per categorie en per project bepalen welke meldingen in aanmerking komen. De webhook verstuurt alleen meldingen als hij is ingeschakeld en er een URL is ingesteld. Je URL blijft bewaard zolang de webhook is uitgeschakeld — maak de URL leeg om hem te verwijderen." +label.webhook_notifications_description="Stuur je meldingen naar je eigen HTTPS-endpoint. Voor elke projectmelding die voor jou in aanmerking komt, stuurt Leantime één HTTP POST-verzoek met een JSON-body naar deze URL. Verzoeken worden op de achtergrond verzonden en kunnen enkele minuten onderweg zijn; een mislukt verzoek wordt niet opnieuw geprobeerd. Je instellingen op het tabblad Notificaties (ontvangen van meldingen, categorieën en niveaus per project) bepalen welke meldingen in aanmerking komen. De webhook verstuurt alleen meldingen als hij is ingeschakeld en er een URL is ingesteld. Je URL blijft bewaard zolang de webhook is uitgeschakeld — maak de URL leeg om hem te verwijderen." label.enable_webhook_notifications="Webhookmeldingen inschakelen" label.webhook_url_hint="Alleen HTTPS. Laat leeg om de webhook te verwijderen." label.default_notification_types="Standaard meldingstypen voor nieuwe gebruikers" @@ -4064,3 +4064,4 @@ text.no_images_found = "Geen afbeeldingen gevonden" notifications.background_settings_saved = "Achtergrond opgeslagen" notifications.background_not_saved = "De achtergrond kon niet worden opgeslagen" notifications.background_upload_failed = "De achtergrond kon niet worden opgeslagen. Upload een JPG-, PNG-, GIF- of WebP-afbeelding." +tabs.webhooks = " Webhooks" diff --git a/app/Language/no-NO.ini b/app/Language/no-NO.ini index 02a403237f..ce0a9b498c 100644 --- a/app/Language/no-NO.ini +++ b/app/Language/no-NO.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Bare oppgaver tildelt deg, dine element label.notifications_muted="Dempet" label.notifications_muted_description="Ingen varsler unntatt direkte @omtaler" label.webhook_notifications="Personlig webhook" -label.webhook_notifications_description="Send varslene dine til ditt eget HTTPS-endepunkt. For hvert prosjektvarsel du er kvalifisert for, sender Leantime én HTTP POST-forespørsel med en JSON-body til denne URL-en. Forespørsler sendes i bakgrunnen og kan bruke flere minutter på å komme frem; en forespørsel som mislykkes, blir ikke sendt på nytt. Innstillingene for mottak av varsler, kategorier og per prosjekt ovenfor bestemmer hvilke varsler som er kvalifisert. Webhooken sender bare mens den er aktivert og en URL er angitt. URL-en forblir lagret mens webhooken er deaktivert – tøm URL-en for å fjerne den." +label.webhook_notifications_description="Send varslene dine til ditt eget HTTPS-endepunkt. For hvert prosjektvarsel du er kvalifisert for, sender Leantime én HTTP POST-forespørsel med en JSON-body til denne URL-en. Forespørsler sendes i bakgrunnen og kan bruke flere minutter på å komme frem; en forespørsel som mislykkes, blir ikke sendt på nytt. Innstillingene dine i fanen Varsler (mottak av varsler, kategorier og nivåer per prosjekt) bestemmer hvilke varsler som er kvalifisert. Webhooken sender bare mens den er aktivert og en URL er angitt. URL-en forblir lagret mens webhooken er deaktivert – tøm URL-en for å fjerne den." label.enable_webhook_notifications="Aktiver webhook-varsler" label.webhook_url_hint="Bare HTTPS. La stå tomt for å fjerne webhooken." label.default_notification_types="Standard varseltyper for nye brukere" @@ -4064,3 +4064,4 @@ text.no_images_found = "Fant ingen bilder" notifications.background_settings_saved = "Bakgrunn lagret" notifications.background_not_saved = "Bakgrunnen kunne ikke lagres" notifications.background_upload_failed = "Bakgrunnen kunne ikke lagres. Last opp et JPG-, PNG-, GIF- eller WebP-bilde." +tabs.webhooks = " Webhooks" diff --git a/app/Language/pl-PL.ini b/app/Language/pl-PL.ini index a5bcc5fe91..77dc8996a9 100644 --- a/app/Language/pl-PL.ini +++ b/app/Language/pl-PL.ini @@ -2808,7 +2808,7 @@ label.notifications_my_work_description = "Tylko zadania przypisane do Ciebie, T label.notifications_muted = "Wyciszone" label.notifications_muted_description = "Brak powiadomień poza bezpośrednimi @wzmiankami" label.webhook_notifications = "Osobisty webhook" -label.webhook_notifications_description = "Wysyłaj powiadomienia do własnego punktu końcowego HTTPS. Dla każdego powiadomienia z projektu, które Ci przysługuje, Leantime wysyła na ten adres URL jedno żądanie HTTP POST z treścią w formacie JSON. Żądania są wysyłane w tle i mogą dotrzeć z kilkuminutowym opóźnieniem; nieudane żądanie nie jest ponawiane. O tym, które powiadomienia są wysyłane, decydują powyższe ustawienia odbierania powiadomień, kategorii i projektów. Webhook działa tylko wtedy, gdy jest włączony i ustawiono adres URL. Adres URL pozostaje zapisany, gdy webhook jest wyłączony — wyczyść go, aby go usunąć." +label.webhook_notifications_description ="Wysyłaj powiadomienia do własnego punktu końcowego HTTPS. Dla każdego powiadomienia z projektu, które Ci przysługuje, Leantime wysyła na ten adres URL jedno żądanie HTTP POST z treścią w formacie JSON. Żądania są wysyłane w tle i mogą dotrzeć z kilkuminutowym opóźnieniem; nieudane żądanie nie jest ponawiane. O tym, które powiadomienia są wysyłane, decydują Twoje ustawienia na karcie Powiadomienia (odbieranie powiadomień, kategorie i poziomy dla poszczególnych projektów). Webhook działa tylko wtedy, gdy jest włączony i ustawiono adres URL. Adres URL pozostaje zapisany, gdy webhook jest wyłączony — wyczyść go, aby go usunąć." label.enable_webhook_notifications = "Włącz powiadomienia przez webhook" label.webhook_url_hint = "Tylko HTTPS. Zostaw puste, aby usunąć webhook." label.default_notification_types = "Domyślne typy powiadomień dla nowych użytkowników" @@ -4244,3 +4244,4 @@ text.no_images_found = "Nie znaleziono obrazów" notifications.background_settings_saved = "Tło zapisane" notifications.background_not_saved = "Nie udało się zapisać tła" notifications.background_upload_failed = "Nie udało się zapisać tła. Prześlij obraz JPG, PNG, GIF lub WebP." +tabs.webhooks = " Webhooki" diff --git a/app/Language/pt-BR.ini b/app/Language/pt-BR.ini index 4ca6e0d545..0e46cf87ca 100644 --- a/app/Language/pt-BR.ini +++ b/app/Language/pt-BR.ini @@ -2678,7 +2678,7 @@ label.telegram_topic_id="ID do tópico" label.open_in_leantime="Abrir no Leantime" label.assigned_to="Atribuído a" label.webhook_notifications="Webhook pessoal" -label.webhook_notifications_description="Envie suas notificações para o seu próprio endpoint HTTPS. Para cada notificação de projeto que você pode receber, o Leantime envia uma requisição HTTP POST com um corpo JSON para esta URL. As requisições são enviadas em segundo plano e podem levar vários minutos para chegar; uma requisição que falhar não é repetida. Suas configurações acima de recebimento de notificações, categorias e por projeto determinam quais notificações são enviadas. O webhook só envia enquanto estiver ativado e houver uma URL definida. Sua URL permanece salva enquanto o webhook estiver desativado — limpe a URL para removê-la." +label.webhook_notifications_description="Envie suas notificações para o seu próprio endpoint HTTPS. Para cada notificação de projeto que você pode receber, o Leantime envia uma requisição HTTP POST com um corpo JSON para esta URL. As requisições são enviadas em segundo plano e podem levar vários minutos para chegar; uma requisição que falhar não é repetida. Suas configurações na aba Notificações (recebimento de notificações, categorias e níveis por projeto) determinam quais notificações são enviadas. O webhook só envia enquanto estiver ativado e houver uma URL definida. Sua URL permanece salva enquanto o webhook estiver desativado — limpe a URL para removê-la." label.enable_webhook_notifications="Ativar notificações por webhook" label.webhook_url_hint="Somente HTTPS. Deixe em branco para remover o webhook." label.kanban_view_menu="Cartões" @@ -4064,3 +4064,4 @@ text.no_images_found = "Nenhuma imagem encontrada" notifications.background_settings_saved = "Plano de fundo salvo" notifications.background_not_saved = "Não foi possível salvar o plano de fundo" notifications.background_upload_failed = "Não foi possível salvar o plano de fundo. Envie uma imagem JPG, PNG, GIF ou WebP." +tabs.webhooks = " Webhooks" diff --git a/app/Language/pt-PT.ini b/app/Language/pt-PT.ini index 15b0189857..3901598949 100644 --- a/app/Language/pt-PT.ini +++ b/app/Language/pt-PT.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Apenas tarefas atribuídas a si, os seu label.notifications_muted="Silenciado" label.notifications_muted_description="Sem notificações, exceto @menções diretas" label.webhook_notifications="Webhook pessoal" -label.webhook_notifications_description="Envie as suas notificações para o seu próprio endpoint HTTPS. Para cada notificação de projeto que lhe seja destinada, o Leantime envia um pedido HTTP POST com um corpo JSON para este URL. Os pedidos são enviados em segundo plano e podem demorar vários minutos a chegar; um pedido que falhe não é repetido. As definições de receção de notificações, de categorias e por projeto acima determinam que notificações lhe são destinadas. O webhook só envia enquanto estiver ativado e houver um URL definido. O seu URL fica guardado enquanto o webhook estiver desativado — limpe o URL para o remover." +label.webhook_notifications_description="Envie as suas notificações para o seu próprio endpoint HTTPS. Para cada notificação de projeto que lhe seja destinada, o Leantime envia um pedido HTTP POST com um corpo JSON para este URL. Os pedidos são enviados em segundo plano e podem demorar vários minutos a chegar; um pedido que falhe não é repetido. As suas definições no separador Notificações (receção de notificações, categorias e níveis por projeto) determinam que notificações lhe são destinadas. O webhook só envia enquanto estiver ativado e houver um URL definido. O seu URL fica guardado enquanto o webhook estiver desativado — limpe o URL para o remover." label.enable_webhook_notifications="Ativar notificações por webhook" label.webhook_url_hint="Apenas HTTPS. Deixe vazio para remover o webhook." label.default_notification_types="Tipos de notificação predefinidos para novos utilizadores" @@ -4064,3 +4064,4 @@ text.no_images_found = "Nenhuma imagem encontrada" notifications.background_settings_saved = "Fundo guardado" notifications.background_not_saved = "Não foi possível guardar o fundo" notifications.background_upload_failed = "Não foi possível guardar o fundo. Carregue uma imagem JPG, PNG, GIF ou WebP." +tabs.webhooks = " Webhooks" diff --git a/app/Language/ro-RO.ini b/app/Language/ro-RO.ini index 1f2bc7311a..5b17c8d527 100644 --- a/app/Language/ro-RO.ini +++ b/app/Language/ro-RO.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Doar sarcinile atribuite dvs., elemente label.notifications_muted="Dezactivate" label.notifications_muted_description="Nicio notificare, cu excepția @mențiunilor directe" label.webhook_notifications="Webhook personal" -label.webhook_notifications_description="Trimiteți notificările către propriul endpoint HTTPS. Pentru fiecare notificare de proiect pentru care sunteți eligibil, Leantime trimite o cerere HTTP POST cu un corp JSON către acest URL. Cererile sunt trimise în fundal și pot ajunge după câteva minute; o cerere eșuată nu este reîncercată. Setările de mai sus pentru primirea notificărilor, categorii și proiecte decid ce notificări sunt eligibile. Webhookul trimite doar cât timp este activat și are un URL setat. URL-ul rămâne salvat cât timp webhookul este dezactivat — goliți URL-ul pentru a-l elimina." +label.webhook_notifications_description="Trimiteți notificările către propriul endpoint HTTPS. Pentru fiecare notificare de proiect pentru care sunteți eligibil, Leantime trimite o cerere HTTP POST cu un corp JSON către acest URL. Cererile sunt trimise în fundal și pot ajunge după câteva minute; o cerere eșuată nu este reîncercată. Setările dumneavoastră din fila Notificări (primirea notificărilor, categorii și niveluri pe proiect) decid ce notificări sunt eligibile. Webhookul trimite doar cât timp este activat și are un URL setat. URL-ul rămâne salvat cât timp webhookul este dezactivat — goliți URL-ul pentru a-l elimina." label.enable_webhook_notifications="Activați notificările prin webhook" label.webhook_url_hint="Doar HTTPS. Lăsați gol pentru a elimina webhookul." label.default_notification_types="Tipuri implicite de notificări pentru utilizatorii noi" @@ -4064,3 +4064,4 @@ text.no_images_found = "Nu s-au găsit imagini" notifications.background_settings_saved = "Fundal salvat" notifications.background_not_saved = "Fundalul nu a putut fi salvat" notifications.background_upload_failed = "Fundalul nu a putut fi salvat. Încărcați o imagine JPG, PNG, GIF sau WebP." +tabs.webhooks = " Webhook-uri" diff --git a/app/Language/ru-RU.ini b/app/Language/ru-RU.ini index 1eb17b00ff..8f975a8201 100644 --- a/app/Language/ru-RU.ini +++ b/app/Language/ru-RU.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Только назначенные в label.notifications_muted="Без уведомлений" label.notifications_muted_description="Никаких уведомлений, кроме прямых @упоминаний" label.webhook_notifications="Персональный вебхук" -label.webhook_notifications_description="Отправляйте свои уведомления на собственный HTTPS-адрес. Для каждого уведомления проекта, которое вам положено, Leantime отправляет на этот URL один HTTP POST-запрос с телом в формате JSON. Запросы отправляются в фоновом режиме и могут доходить через несколько минут; неудачный запрос повторно не отправляется. Какие уведомления вам положены, определяют указанные выше настройки получения уведомлений, категорий и проектов. Вебхук работает, только если он включен и указан URL. Пока вебхук отключен, ваш URL остается сохраненным — очистите поле, чтобы удалить его." +label.webhook_notifications_description="Отправляйте свои уведомления на собственный HTTPS-адрес. Для каждого уведомления проекта, которое вам положено, Leantime отправляет на этот URL один HTTP POST-запрос с телом в формате JSON. Запросы отправляются в фоновом режиме и могут доходить через несколько минут; неудачный запрос повторно не отправляется. Какие уведомления вам положены, определяют ваши настройки на вкладке «Уведомления» (получение уведомлений, категории и уровни по проектам). Вебхук работает, только если он включен и указан URL. Пока вебхук отключен, ваш URL остается сохраненным — очистите поле, чтобы удалить его." label.enable_webhook_notifications="Включить уведомления через вебхук" label.webhook_url_hint="Только HTTPS. Оставьте пустым, чтобы удалить вебхук." label.default_notification_types="Типы уведомлений по умолчанию для новых пользователей" @@ -4068,3 +4068,4 @@ text.no_images_found = "Изображения не найдены" notifications.background_settings_saved = "Фон сохранён" notifications.background_not_saved = "Не удалось сохранить фон" notifications.background_upload_failed = "Не удалось сохранить фон. Загрузите изображение JPG, PNG, GIF или WebP." +tabs.webhooks = " Вебхуки" diff --git a/app/Language/sk-SK.ini b/app/Language/sk-SK.ini index 6b23289734..1d046b772f 100644 --- a/app/Language/sk-SK.ini +++ b/app/Language/sk-SK.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Iba úlohy priradené vám, vaše polo label.notifications_muted="Stlmené" label.notifications_muted_description="Žiadne upozornenia okrem priamych @zmienok" label.webhook_notifications="Osobný webhook" -label.webhook_notifications_description="Posielajte svoje upozornenia na vlastný HTTPS endpoint. Pre každé projektové upozornenie, na ktoré máte nárok, odošle Leantime na túto URL adresu jednu požiadavku HTTP POST s telom vo formáte JSON. Požiadavky sa odosielajú na pozadí a ich doručenie môže trvať niekoľko minút; neúspešná požiadavka sa neopakuje. O tom, na ktoré upozornenia máte nárok, rozhodujú vyššie uvedené nastavenia prijímania upozornení, kategórií a jednotlivých projektov. Webhook odosiela, iba keď je zapnutý a je nastavená URL adresa. Kým je webhook vypnutý, vaša URL adresa zostáva uložená — ak ju chcete odstrániť, vymažte ju." +label.webhook_notifications_description="Posielajte svoje upozornenia na vlastný HTTPS endpoint. Pre každé projektové upozornenie, na ktoré máte nárok, odošle Leantime na túto URL adresu jednu požiadavku HTTP POST s telom vo formáte JSON. Požiadavky sa odosielajú na pozadí a ich doručenie môže trvať niekoľko minút; neúspešná požiadavka sa neopakuje. O tom, na ktoré upozornenia máte nárok, rozhodujú vaše nastavenia na karte Oznámenia (prijímanie upozornení, kategórie a úrovne jednotlivých projektov). Webhook odosiela, iba keď je zapnutý a je nastavená URL adresa. Kým je webhook vypnutý, vaša URL adresa zostáva uložená — ak ju chcete odstrániť, vymažte ju." label.enable_webhook_notifications="Zapnúť upozornenia cez webhook" label.webhook_url_hint="Iba HTTPS. Ak chcete webhook odstrániť, ponechajte pole prázdne." label.default_notification_types="Predvolené typy upozornení pre nových používateľov" @@ -4064,3 +4064,4 @@ text.no_images_found = "Nenašli sa žiadne obrázky" notifications.background_settings_saved = "Pozadie bolo uložené" notifications.background_not_saved = "Pozadie sa nepodarilo uložiť" notifications.background_upload_failed = "Pozadie sa nepodarilo uložiť. Nahrajte obrázok JPG, PNG, GIF alebo WebP." +tabs.webhooks = " Webhooky" diff --git a/app/Language/sl-SI.ini b/app/Language/sl-SI.ini index 40b15bea8f..1c212c5a42 100644 --- a/app/Language/sl-SI.ini +++ b/app/Language/sl-SI.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Samo opravila, dodeljena vam, vaši ele label.notifications_muted="Utišano" label.notifications_muted_description="Brez obvestil, razen neposrednih @omemb" label.webhook_notifications="Osebni webhook" -label.webhook_notifications_description="Pošiljajte svoja obvestila na lastno končno točko HTTPS. Za vsako obvestilo projekta, do katerega ste upravičeni, Leantime na ta URL pošlje eno zahtevo HTTP POST s telesom JSON. Zahteve se pošiljajo v ozadju in lahko prispejo šele po nekaj minutah; neuspela zahteva se ne ponovi. Katera obvestila so upravičena, določajo zgornje nastavitve prejemanja obvestil, kategorij in posameznih projektov. Webhook pošilja samo, ko je omogočen in je URL nastavljen. Vaš URL ostane shranjen, tudi ko je webhook onemogočen — za odstranitev počistite URL." +label.webhook_notifications_description="Pošiljajte svoja obvestila na lastno končno točko HTTPS. Za vsako obvestilo projekta, do katerega ste upravičeni, Leantime na ta URL pošlje eno zahtevo HTTP POST s telesom JSON. Zahteve se pošiljajo v ozadju in lahko prispejo šele po nekaj minutah; neuspela zahteva se ne ponovi. Katera obvestila so upravičena, določajo vaše nastavitve v zavihku Obvestila (prejemanje obvestil, kategorije in ravni posameznih projektov). Webhook pošilja samo, ko je omogočen in je URL nastavljen. Vaš URL ostane shranjen, tudi ko je webhook onemogočen — za odstranitev počistite URL." label.enable_webhook_notifications="Omogoči obvestila prek webhooka" label.webhook_url_hint="Samo HTTPS. Pustite prazno, da odstranite webhook." label.default_notification_types="Privzete vrste obvestil za nove uporabnike" @@ -4064,3 +4064,4 @@ text.no_images_found = "Ni najdenih slik" notifications.background_settings_saved = "Ozadje je shranjeno" notifications.background_not_saved = "Ozadja ni bilo mogoče shraniti" notifications.background_upload_failed = "Ozadja ni bilo mogoče shraniti. Naložite sliko JPG, PNG, GIF ali WebP." +tabs.webhooks = " Webhooki" diff --git a/app/Language/sr-SP.ini b/app/Language/sr-SP.ini index f04c2ebdcd..84ad335a0a 100644 --- a/app/Language/sr-SP.ini +++ b/app/Language/sr-SP.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Само задаци додељени label.notifications_muted="Утишано" label.notifications_muted_description="Без обавештења, осим директних @помињања" label.webhook_notifications="Лични webhook" -label.webhook_notifications_description="Шаљите своја обавештења на сопствену HTTPS крајњу тачку. За свако обавештење о пројекту које испуњава услове, Leantime шаље један HTTP POST захтев са JSON телом на ову URL адресу. Захтеви се шаљу у позадини и могу стићи и после неколико минута; неуспели захтев се не понавља. Ваша подешавања за примање обавештења, категорије и пројекте изнад одређују која обавештења испуњавају услове. Webhook шаље само док је укључен и док је URL адреса подешена. Ваша URL адреса остаје сачувана док је webhook искључен — испразните URL адресу да бисте је уклонили." +label.webhook_notifications_description="Шаљите своја обавештења на сопствену HTTPS крајњу тачку. За свако обавештење о пројекту које испуњава услове, Leantime шаље један HTTP POST захтев са JSON телом на ову URL адресу. Захтеви се шаљу у позадини и могу стићи и после неколико минута; неуспели захтев се не понавља. Ваша подешавања на картици Обавештења (примање обавештења, категорије и нивои по пројектима) одређују која обавештења испуњавају услове. Webhook шаље само док је укључен и док је URL адреса подешена. Ваша URL адреса остаје сачувана док је webhook искључен — испразните URL адресу да бисте је уклонили." label.enable_webhook_notifications="Укључи webhook обавештења" label.webhook_url_hint="Само HTTPS. Оставите празно да бисте уклонили webhook." label.default_notification_types="Подразумеване врсте обавештења за нове кориснике" @@ -4064,3 +4064,4 @@ text.no_images_found = "Нису пронађене слике" notifications.background_settings_saved = "Позадина је сачувана" notifications.background_not_saved = "Позадину није могуће сачувати" notifications.background_upload_failed = "Позадину није могуће сачувати. Отпремите JPG, PNG, GIF или WebP слику." +tabs.webhooks = " Webhooks" diff --git a/app/Language/sv-SE.ini b/app/Language/sv-SE.ini index 49b63f51bf..9fd19e1197 100644 --- a/app/Language/sv-SE.ini +++ b/app/Language/sv-SE.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Endast uppgifter som tilldelats dig, di label.notifications_muted="Tystad" label.notifications_muted_description="Inga aviseringar förutom direkta @omnämnanden" label.webhook_notifications="Personlig webhook" -label.webhook_notifications_description="Skicka dina aviseringar till din egen HTTPS-endpoint. För varje projektavisering du har rätt till skickar Leantime en HTTP POST-förfrågan med en JSON-body till den här URL:en. Förfrågningarna skickas i bakgrunden och kan ta flera minuter att komma fram; en förfrågan som misslyckas skickas inte igen. Dina inställningar ovan för att ta emot aviseringar, kategorier och per projekt avgör vilka aviseringar som omfattas. Webhooken skickar endast när den är aktiverad och en URL är angiven. Din URL förblir sparad medan webhooken är inaktiverad — töm URL:en för att ta bort den." +label.webhook_notifications_description="Skicka dina aviseringar till din egen HTTPS-endpoint. För varje projektavisering du har rätt till skickar Leantime en HTTP POST-förfrågan med en JSON-body till den här URL:en. Förfrågningarna skickas i bakgrunden och kan ta flera minuter att komma fram; en förfrågan som misslyckas skickas inte igen. Dina inställningar på fliken Aviseringar (ta emot aviseringar, kategorier och nivåer per projekt) avgör vilka aviseringar som omfattas. Webhooken skickar endast när den är aktiverad och en URL är angiven. Din URL förblir sparad medan webhooken är inaktiverad — töm URL:en för att ta bort den." label.enable_webhook_notifications="Aktivera webhook-aviseringar" label.webhook_url_hint="Endast HTTPS. Lämna tomt för att ta bort webhooken." label.default_notification_types="Standardaviseringstyper för nya användare" @@ -4064,3 +4064,4 @@ text.no_images_found = "Inga bilder hittades" notifications.background_settings_saved = "Bakgrunden sparades" notifications.background_not_saved = "Bakgrunden kunde inte sparas" notifications.background_upload_failed = "Bakgrunden kunde inte sparas. Ladda upp en JPG-, PNG-, GIF- eller WebP-bild." +tabs.webhooks = " Webhooks" diff --git a/app/Language/tr-TR.ini b/app/Language/tr-TR.ini index cabedb48f5..cbcf4fa14c 100644 --- a/app/Language/tr-TR.ini +++ b/app/Language/tr-TR.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Yalnızca size atanan görevler, öğel label.notifications_muted="Sessizde" label.notifications_muted_description="Doğrudan @bahsetmeler dışında bildirim yok" label.webhook_notifications="Kişisel Webhook" -label.webhook_notifications_description="Bildirimlerinizi kendi HTTPS uç noktanıza gönderin. Alma hakkınız olan her proje bildirimi için Leantime bu URL'ye JSON gövdeli bir HTTP POST isteği gönderir. İstekler arka planda gönderilir ve ulaşması birkaç dakika sürebilir; başarısız olan bir istek yeniden denenmez. Hangi bildirimlerin gönderileceğini yukarıdaki bildirim alma, kategori ve proje bazındaki ayarlarınız belirler. Webhook yalnızca etkinken ve bir URL ayarlanmışken gönderim yapar. Webhook devre dışıyken URL'niz kayıtlı kalır — kaldırmak için URL'yi temizleyin." +label.webhook_notifications_description="Bildirimlerinizi kendi HTTPS uç noktanıza gönderin. Alma hakkınız olan her proje bildirimi için Leantime bu URL'ye JSON gövdeli bir HTTP POST isteği gönderir. İstekler arka planda gönderilir ve ulaşması birkaç dakika sürebilir; başarısız olan bir istek yeniden denenmez. Hangi bildirimlerin gönderileceğini Bildirimler sekmesindeki ayarlarınız (bildirim alma, kategoriler ve proje bazındaki düzeyler) belirler. Webhook yalnızca etkinken ve bir URL ayarlanmışken gönderim yapar. Webhook devre dışıyken URL'niz kayıtlı kalır — kaldırmak için URL'yi temizleyin." label.enable_webhook_notifications="Webhook bildirimlerini etkinleştir" label.webhook_url_hint="Yalnızca HTTPS. Webhook'u kaldırmak için boş bırakın." label.default_notification_types="Yeni kullanıcılar için varsayılan bildirim türleri" @@ -4064,3 +4064,4 @@ text.no_images_found = "Görsel bulunamadı" notifications.background_settings_saved = "Arka plan kaydedildi" notifications.background_not_saved = "Arka plan kaydedilemedi" notifications.background_upload_failed = "Arka plan kaydedilemedi. JPG, PNG, GIF veya WebP görseli yükleyin." +tabs.webhooks = " Web Kancaları" diff --git a/app/Language/uk-UA.ini b/app/Language/uk-UA.ini index 552cd4fcca..764b2996d2 100644 --- a/app/Language/uk-UA.ini +++ b/app/Language/uk-UA.ini @@ -2620,7 +2620,7 @@ label.notifications_my_work_description="Лише призначені вам з label.notifications_muted="Приглушено" label.notifications_muted_description="Жодних сповіщень, крім прямих @згадок" label.webhook_notifications="Персональний вебхук" -label.webhook_notifications_description="Надсилайте свої сповіщення на власну HTTPS-адресу. Для кожного сповіщення проєкту, яке вам належить отримати, Leantime надсилає на цю URL-адресу один HTTP POST-запит із тілом у форматі JSON. Запити надсилаються у фоновому режимі, і їх доставка може тривати кілька хвилин; невдалий запит не повторюється. Які сповіщення надсилатимуться, визначають наведені вище налаштування отримання сповіщень, категорій і проєктів. Вебхук надсилає дані лише тоді, коли його ввімкнено й задано URL-адресу. Поки вебхук вимкнено, ваша URL-адреса залишається збереженою — очистіть її, щоб видалити." +label.webhook_notifications_description="Надсилайте свої сповіщення на власну HTTPS-адресу. Для кожного сповіщення проєкту, яке вам належить отримати, Leantime надсилає на цю URL-адресу один HTTP POST-запит із тілом у форматі JSON. Запити надсилаються у фоновому режимі, і їх доставка може тривати кілька хвилин; невдалий запит не повторюється. Які сповіщення надсилатимуться, визначають ваші налаштування на вкладці «Сповіщення» (отримання сповіщень, категорії та рівні для проєктів). Вебхук надсилає дані лише тоді, коли його ввімкнено й задано URL-адресу. Поки вебхук вимкнено, ваша URL-адреса залишається збереженою — очистіть її, щоб видалити." label.enable_webhook_notifications="Увімкнути сповіщення через вебхук" label.webhook_url_hint="Лише HTTPS. Залиште порожнім, щоб видалити вебхук." label.default_notification_types="Типи сповіщень за замовчуванням для нових користувачів" @@ -4064,3 +4064,4 @@ text.no_images_found = "Зображень не знайдено" notifications.background_settings_saved = "Тло збережено" notifications.background_not_saved = "Не вдалося зберегти тло" notifications.background_upload_failed = "Не вдалося зберегти тло. Завантажте зображення JPG, PNG, GIF або WebP." +tabs.webhooks = " Вебхуки" diff --git a/app/Language/vi-VN.ini b/app/Language/vi-VN.ini index 43aad009c5..2e716814e4 100644 --- a/app/Language/vi-VN.ini +++ b/app/Language/vi-VN.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="Chỉ các công việc được giao c label.notifications_muted="Đã tắt tiếng" label.notifications_muted_description="Không có thông báo, trừ các lượt @nhắc đến trực tiếp" label.webhook_notifications="Webhook cá nhân" -label.webhook_notifications_description="Gửi thông báo đến endpoint HTTPS của riêng bạn. Với mỗi thông báo dự án mà bạn đủ điều kiện nhận, Leantime gửi một yêu cầu HTTP POST với nội dung JSON đến URL này. Các yêu cầu được gửi ở chế độ nền và có thể mất vài phút mới đến nơi; yêu cầu bị lỗi sẽ không được gửi lại. Các cài đặt nhận thông báo, danh mục và theo dự án ở trên quyết định thông báo nào đủ điều kiện. Webhook chỉ gửi khi được bật và đã đặt URL. URL của bạn vẫn được lưu khi webhook bị tắt — hãy xóa URL để gỡ bỏ nó." +label.webhook_notifications_description="Gửi thông báo đến endpoint HTTPS của riêng bạn. Với mỗi thông báo dự án mà bạn đủ điều kiện nhận, Leantime gửi một yêu cầu HTTP POST với nội dung JSON đến URL này. Các yêu cầu được gửi ở chế độ nền và có thể mất vài phút mới đến nơi; yêu cầu bị lỗi sẽ không được gửi lại. Các cài đặt của bạn trong tab Thông Báo (nhận thông báo, danh mục và mức theo dự án) quyết định thông báo nào đủ điều kiện. Webhook chỉ gửi khi được bật và đã đặt URL. URL của bạn vẫn được lưu khi webhook bị tắt — hãy xóa URL để gỡ bỏ nó." label.enable_webhook_notifications="Bật thông báo qua webhook" label.webhook_url_hint="Chỉ HTTPS. Để trống để gỡ bỏ webhook." label.default_notification_types="Loại thông báo mặc định cho người dùng mới" @@ -4064,3 +4064,4 @@ text.no_images_found = "Không tìm thấy hình ảnh" notifications.background_settings_saved = "Đã lưu hình nền" notifications.background_not_saved = "Không thể lưu hình nền" notifications.background_upload_failed = "Không thể lưu hình nền. Hãy tải lên hình ảnh JPG, PNG, GIF hoặc WebP." +tabs.webhooks = " Webhook" diff --git a/app/Language/zh-CN.ini b/app/Language/zh-CN.ini index f47968c618..fbe41a1a62 100644 --- a/app/Language/zh-CN.ini +++ b/app/Language/zh-CN.ini @@ -2645,7 +2645,7 @@ label.notifications_my_work_description="仅限分配给你的任务、你的条 label.notifications_muted="已静音" label.notifications_muted_description="除直接 @提及外不接收任何通知" label.webhook_notifications="个人 Webhook" -label.webhook_notifications_description="将你的通知发送到你自己的 HTTPS 端点。对于你有资格接收的每条项目通知,Leantime 都会向此 URL 发送一个带 JSON 正文的 HTTP POST 请求。请求在后台发送,可能需要几分钟才能送达;失败的请求不会重试。上方的接收通知、类别和按项目设置决定哪些通知符合条件。仅当 Webhook 已启用且已设置 URL 时才会发送。禁用 Webhook 后 URL 仍会保留——清空 URL 即可将其删除。" +label.webhook_notifications_description="将你的通知发送到你自己的 HTTPS 端点。对于你有资格接收的每条项目通知,Leantime 都会向此 URL 发送一个带 JSON 正文的 HTTP POST 请求。请求在后台发送,可能需要几分钟才能送达;失败的请求不会重试。你在通知选项卡中的设置(接收通知、类别和按项目的级别)决定哪些通知符合条件。仅当 Webhook 已启用且已设置 URL 时才会发送。禁用 Webhook 后 URL 仍会保留——清空 URL 即可将其删除。" label.enable_webhook_notifications="启用 Webhook 通知" label.webhook_url_hint="仅支持 HTTPS。留空即可删除 Webhook。" label.default_notification_types="新用户的默认通知类型" @@ -4077,3 +4077,4 @@ text.no_images_found = "未找到图片" notifications.background_settings_saved = "背景已保存" notifications.background_not_saved = "无法保存背景" notifications.background_upload_failed = "无法保存背景。请上传 JPG、PNG、GIF 或 WebP 图片。" +tabs.webhooks = " Webhook" diff --git a/app/Language/zh-TW.ini b/app/Language/zh-TW.ini index c00ef9a893..1c5fa2d036 100644 --- a/app/Language/zh-TW.ini +++ b/app/Language/zh-TW.ini @@ -2623,7 +2623,7 @@ label.notifications_my_work_description="僅限分配給您的任務、您的項 label.notifications_muted="已靜音" label.notifications_muted_description="除了直接 @提及外,不接收任何通知" label.webhook_notifications="個人 Webhook" -label.webhook_notifications_description="將您的通知傳送到您自己的 HTTPS 端點。對於您有資格接收的每則專案通知,Leantime 都會向此 URL 傳送一個帶有 JSON 內容的 HTTP POST 請求。請求會在背景傳送,可能需要幾分鐘才會送達;失敗的請求不會重試。上方的接收通知、類別和個別專案設定決定哪些通知符合資格。Webhook 只有在啟用且已設定 URL 時才會傳送。停用 Webhook 時,您的 URL 仍會保留 — 清除 URL 即可將其移除。" +label.webhook_notifications_description="將您的通知傳送到您自己的 HTTPS 端點。對於您有資格接收的每則專案通知,Leantime 都會向此 URL 傳送一個帶有 JSON 內容的 HTTP POST 請求。請求會在背景傳送,可能需要幾分鐘才會送達;失敗的請求不會重試。您在通知分頁中的設定(接收通知、類別和個別專案層級)決定哪些通知符合資格。Webhook 只有在啟用且已設定 URL 時才會傳送。停用 Webhook 時,您的 URL 仍會保留 — 清除 URL 即可將其移除。" label.enable_webhook_notifications="啟用 Webhook 通知" label.webhook_url_hint="僅限 HTTPS。留空即可移除 Webhook。" label.default_notification_types="新使用者的預設通知類型" @@ -4064,3 +4064,4 @@ text.no_images_found = "找不到圖片" notifications.background_settings_saved = "背景已儲存" notifications.background_not_saved = "無法儲存背景" notifications.background_upload_failed = "無法儲存背景。請上傳 JPG、PNG、GIF 或 WebP 圖片。" +tabs.webhooks = " Webhook" diff --git a/tests/Unit/app/Domain/Users/Controllers/EditOwnTest.php b/tests/Unit/app/Domain/Users/Controllers/EditOwnTest.php index bbdf8b55f6..a2e2c208b5 100644 --- a/tests/Unit/app/Domain/Users/Controllers/EditOwnTest.php +++ b/tests/Unit/app/Domain/Users/Controllers/EditOwnTest.php @@ -12,9 +12,9 @@ use Unit\TestCase; /** - * The notifications tab of the own-profile form: a rejected webhook URL or an - * unsaved webhook setting must surface as an error, never as the "saved" - * confirmation. + * The notifications and webhooks tabs of the own-profile form: a rejected + * webhook URL or an unsaved webhook setting must surface as an error, never as + * the "saved" confirmation. */ class EditOwnTest extends TestCase { @@ -46,19 +46,16 @@ protected function tearDown(): void } /** - * Submits the notifications tab through the real controller with the given - * (stubbed) user service. + * Submits a tab of the own-profile form through the real controller with the + * given (stubbed) user service and returns the redirect target. + * + * @param array $post The tab's fields, without the form token. */ - private function submitNotificationsTab(UserService $userService): void + private function submitTab(UserService $userService, array $post): string { app()->instance(UserService::class, $userService); - $_POST = [ - 'formToken' => 'expected-token', - 'savenotifications' => '1', - 'webhookUrl' => 'http://insecure.example.com/hook', - 'webhookEnabled' => '1', - ]; + $_POST = ['formToken' => 'expected-token'] + $post; $tpl = $this->make(Template::class, [ 'setNotification' => function (string $message, string $type) { @@ -69,13 +66,24 @@ private function submitNotificationsTab(UserService $userService): void $response = (new EditOwn($this->make(IncomingRequest::class), $tpl, $language))->post(); - $this->assertStringEndsWith('/users/editOwn#notifications', $response->headers->get('Location')); + return (string) $response->headers->get('Location'); + } + + private function submitWebhooksTab(UserService $userService): void + { + $location = $this->submitTab($userService, [ + 'savewebhooks' => '1', + 'webhookUrl' => 'http://insecure.example.com/hook', + 'webhookEnabled' => '1', + ]); + + $this->assertStringEndsWith('/users/editOwn#webhooks', $location); } public function test_rejected_webhook_url_shows_an_error_and_no_success(): void { - $this->submitNotificationsTab($this->make(UserService::class, [ - 'saveOwnNotificationPreferences' => function () { + $this->submitWebhooksTab($this->make(UserService::class, [ + 'saveOwnWebhookSettings' => function () { throw ValidationException::withMessages(['webhookUrl' => ['notification.invalid_webhook_url']]); }, ])); @@ -85,8 +93,8 @@ public function test_rejected_webhook_url_shows_an_error_and_no_success(): void public function test_unsaved_webhook_setting_shows_a_generic_error_and_no_success(): void { - $this->submitNotificationsTab($this->make(UserService::class, [ - 'saveOwnNotificationPreferences' => function () { + $this->submitWebhooksTab($this->make(UserService::class, [ + 'saveOwnWebhookSettings' => function () { throw new WebhookSettingNotSavedException; }, ])); @@ -99,19 +107,35 @@ public function test_unexpected_failures_are_not_swallowed_into_a_notification() // Only the known outcomes are handled; anything else (e.g. a DB error) must reach the exception handler. $this->expectExceptionObject(new \RuntimeException('database went away')); - $this->submitNotificationsTab($this->make(UserService::class, [ - 'saveOwnNotificationPreferences' => function () { + $this->submitWebhooksTab($this->make(UserService::class, [ + 'saveOwnWebhookSettings' => function () { throw new \RuntimeException('database went away'); }, ])); } - public function test_accepted_preferences_show_the_success_confirmation(): void + public function test_accepted_webhook_shows_the_success_confirmation(): void { - $this->submitNotificationsTab($this->make(UserService::class, [ - 'saveOwnNotificationPreferences' => fn () => null, + $this->submitWebhooksTab($this->make(UserService::class, [ + 'saveOwnWebhookSettings' => fn () => null, ])); $this->assertSame([['notifications.changed_profile_settings_successfully', 'success']], $this->notifications); } + + public function test_notification_preferences_are_saved_without_the_webhook(): void + { + $webhookSaves = 0; + + $location = $this->submitTab($this->make(UserService::class, [ + 'saveOwnNotificationPreferences' => fn () => null, + 'saveOwnWebhookSettings' => function () use (&$webhookSaves) { + $webhookSaves++; + }, + ]), ['savenotifications' => '1', 'notifications' => 'on']); + + $this->assertStringEndsWith('/users/editOwn#notifications', $location); + $this->assertSame(0, $webhookSaves); + $this->assertSame([['notifications.changed_profile_settings_successfully', 'success']], $this->notifications); + } } diff --git a/tests/Unit/app/Domain/Users/Services/UsersServiceTest.php b/tests/Unit/app/Domain/Users/Services/UsersServiceTest.php index 903ebbf009..d6a8389eb5 100644 --- a/tests/Unit/app/Domain/Users/Services/UsersServiceTest.php +++ b/tests/Unit/app/Domain/Users/Services/UsersServiceTest.php @@ -471,7 +471,7 @@ public function test_self_service_methods_ignore_caller_supplied_id_and_pin_to_s } // --------------------------------------------------------------------- - // Personal notification webhook (saveOwnNotificationPreferences / + // Personal notification webhook (saveOwnWebhookSettings / // getNotificationPreferences). // --------------------------------------------------------------------- @@ -526,25 +526,22 @@ private function webhookSetting(string $url, bool $enabled): string return json_encode(['url' => $url, 'enabled' => $enabled]); } - public function test_save_notification_preferences_stores_the_webhook_for_the_session_user_only(): void + public function test_save_webhook_settings_stores_the_webhook_for_the_session_user_only(): void { session(['userdata' => ['id' => 7]]); $saved = []; $this->makeService($this->profileRepo(), ['settingsService' => $this->settingsStore([], $saved)]) - ->saveOwnNotificationPreferences(99, [ + ->saveOwnWebhookSettings(99, [ 'webhookUrl' => ' https://hooks.example.com/services/abc?token=xyz ', 'webhookEnabled' => 'on', ]); + $this->assertSame(['usersettings.7.webhook'], array_keys($saved), 'Only the session user\'s webhook is written'); $this->assertSame( ['url' => 'https://hooks.example.com/services/abc?token=xyz', 'enabled' => true], json_decode($saved['usersettings.7.webhook'], true) ); - $this->assertSame('usersettings.7.webhook', array_key_first($saved), 'The webhook is saved before any other preference'); - foreach (array_keys($saved) as $key) { - $this->assertStringNotContainsString('usersettings.99.', $key, 'A caller-supplied id must never be written to'); - } } public function test_unchecking_the_webhook_keeps_the_url_but_disables_delivery(): void @@ -552,7 +549,7 @@ public function test_unchecking_the_webhook_keeps_the_url_but_disables_delivery( $saved = []; $this->makeService($this->profileRepo(), ['settingsService' => $this->settingsStore([], $saved)]) - ->saveOwnNotificationPreferences(1, ['webhookUrl' => 'https://hooks.example.com/abc']); + ->saveOwnWebhookSettings(1, ['webhookUrl' => 'https://hooks.example.com/abc']); $this->assertSame(['url' => 'https://hooks.example.com/abc', 'enabled' => false], json_decode($saved['usersettings.1.webhook'], true)); } @@ -562,24 +559,39 @@ public function test_an_empty_webhook_url_disables_delivery_even_when_checked(): $saved = []; $this->makeService($this->profileRepo(), ['settingsService' => $this->settingsStore([], $saved)]) - ->saveOwnNotificationPreferences(1, ['webhookUrl' => '', 'webhookEnabled' => '1']); + ->saveOwnWebhookSettings(1, ['webhookUrl' => '', 'webhookEnabled' => '1']); $this->assertSame(['url' => '', 'enabled' => false], json_decode($saved['usersettings.1.webhook'], true)); } - /** - * @dataProvider payloadWithoutWebhookUrlProvider - */ - public function test_omitting_the_webhook_url_leaves_the_stored_webhook_unchanged(array $post): void + public function test_a_cleared_webhook_url_arriving_as_null_disables_delivery(): void { - // API clients built before the webhook existed only send the older preferences. + // ConvertEmptyStringsToNull turns the cleared form field into null. + $saved = []; + + $this->makeService($this->profileRepo(), ['settingsService' => $this->settingsStore([], $saved)]) + ->saveOwnWebhookSettings(1, ['webhookUrl' => null]); + + $this->assertSame(['url' => '', 'enabled' => false], json_decode($saved['usersettings.1.webhook'], true)); + } + + public function test_notification_preferences_never_touch_the_webhook(): void + { + // The webhook has its own tab and save; webhook fields sent along with the + // notification preferences are ignored. $storedWebhook = $this->webhookSetting('https://hooks.example.com/abc', true); $saved = []; $editOwnCalls = 0; $settings = $this->settingsStore(['usersettings.1.webhook' => $storedWebhook], $saved); $this->makeService($this->profileRepo($editOwnCalls), ['settingsService' => $settings]) - ->saveOwnNotificationPreferences(1, $post); + ->saveOwnNotificationPreferences(1, [ + 'notifications' => '1', + 'messagesfrequency' => '60', + 'enabledEventTypes' => ['tasks'], + 'webhookUrl' => '', + 'webhookEnabled' => '0', + ]); $this->assertSame($storedWebhook, $settings->getSetting('usersettings.1.webhook'), 'The stored webhook must be left exactly as it was'); $this->assertArrayNotHasKey('usersettings.1.webhook', $saved, 'Nothing may be written to the webhook setting'); @@ -588,22 +600,11 @@ public function test_omitting_the_webhook_url_leaves_the_stored_webhook_unchange $this->assertSame(json_encode(['tasks']), $saved['usersettings.1.notificationEventTypes']); } - public static function payloadWithoutWebhookUrlProvider(): array - { - $olderPreferences = ['notifications' => '1', 'messagesfrequency' => '60', 'enabledEventTypes' => ['tasks']]; - - return [ - 'older preferences only' => [$olderPreferences], - 'opt-in without a url' => [$olderPreferences + ['webhookEnabled' => '0']], - ]; - } - public function test_a_failed_webhook_disable_is_not_reported_as_saved(): void { // The user unchecks the box, but the write reports false and the store still holds // the enabled webhook — delivery would keep going, so this must not look saved. $saved = []; - $editOwnCalls = 0; $logged = []; Log::shouldReceive('error')->andReturnUsing(function ($message, $context = []) use (&$logged) { $logged[] = $message.' '.json_encode($context); @@ -615,15 +616,13 @@ public function test_a_failed_webhook_disable_is_not_reported_as_saved(): void ); try { - $this->makeService($this->profileRepo($editOwnCalls), ['settingsService' => $settings]) - ->saveOwnNotificationPreferences(1, ['notifications' => '1', 'webhookUrl' => 'https://hooks.example.com/secret-token']); + $this->makeService($this->profileRepo(), ['settingsService' => $settings]) + ->saveOwnWebhookSettings(1, ['webhookUrl' => 'https://hooks.example.com/secret-token']); $this->fail('A webhook setting that did not persist must throw'); } catch (WebhookSettingNotSavedException $e) { $this->assertStringNotContainsString('secret-token', $e->getMessage()); } - $this->assertSame(['usersettings.1.webhook'], array_keys($saved), 'No other preference may be written after the webhook failed'); - $this->assertSame(0, $editOwnCalls); $this->assertCount(1, $logged); $this->assertStringNotContainsString('secret-token', $logged[0], 'The webhook URL must never reach the logs'); } @@ -631,7 +630,6 @@ public function test_a_failed_webhook_disable_is_not_reported_as_saved(): void public function test_a_webhook_write_that_throws_is_reported_without_the_url(): void { $saved = []; - $editOwnCalls = 0; $logged = []; Log::shouldReceive('error')->andReturnUsing(function ($message, $context = []) use (&$logged) { $logged[] = $message.' '.json_encode($context); @@ -642,16 +640,14 @@ public function test_a_webhook_write_that_throws_is_reported_without_the_url(): }); try { - $this->makeService($this->profileRepo($editOwnCalls), ['settingsService' => $settings]) - ->saveOwnNotificationPreferences(1, ['webhookUrl' => 'https://hooks.example.com/secret-token', 'webhookEnabled' => '1']); + $this->makeService($this->profileRepo(), ['settingsService' => $settings]) + ->saveOwnWebhookSettings(1, ['webhookUrl' => 'https://hooks.example.com/secret-token', 'webhookEnabled' => '1']); $this->fail('A webhook setting that could not be written must throw'); } catch (WebhookSettingNotSavedException $e) { $this->assertNull($e->getPrevious(), 'The DB exception (and the URL in its message) must not be chained'); $this->assertStringNotContainsString('secret-token', $e->getMessage()); } - $this->assertSame(['usersettings.1.webhook'], array_keys($saved)); - $this->assertSame(0, $editOwnCalls); $this->assertCount(1, $logged); $this->assertStringContainsString('RuntimeException', $logged[0]); $this->assertStringNotContainsString('secret-token', $logged[0], 'The webhook URL must never reach the logs'); @@ -661,18 +657,16 @@ public function test_an_unchanged_webhook_counts_as_saved_when_the_write_reports { // updateOrInsert returns false when the row already holds the identical value. $saved = []; - $editOwnCalls = 0; $settings = $this->settingsStore( ['usersettings.1.webhook' => $this->webhookSetting('https://hooks.example.com/abc', true)], $saved, fn () => false, ); - $this->makeService($this->profileRepo($editOwnCalls), ['settingsService' => $settings]) - ->saveOwnNotificationPreferences(1, ['webhookUrl' => 'https://hooks.example.com/abc', 'webhookEnabled' => '1', 'messagesfrequency' => '60']); + $this->makeService($this->profileRepo(), ['settingsService' => $settings]) + ->saveOwnWebhookSettings(1, ['webhookUrl' => 'https://hooks.example.com/abc', 'webhookEnabled' => '1']); - $this->assertSame(1, $editOwnCalls, 'The remaining preferences are still saved'); - $this->assertSame(60, $saved['usersettings.1.messageFrequency']); + $this->assertSame(['usersettings.1.webhook'], array_keys($saved), 'No exception: the unchanged webhook counts as saved'); } /** @@ -681,14 +675,10 @@ public function test_an_unchanged_webhook_counts_as_saved_when_the_write_reports public function test_an_invalid_webhook_url_is_rejected_before_anything_is_saved(mixed $webhookUrl): void { $saved = []; - $editOwnCalls = 0; - $service = $this->makeService($this->profileRepo($editOwnCalls), ['settingsService' => $this->settingsStore([], $saved)]); + $service = $this->makeService($this->profileRepo(), ['settingsService' => $this->settingsStore([], $saved)]); try { - $service->saveOwnNotificationPreferences(1, [ - 'notifications' => '1', - 'messagesfrequency' => '60', - 'enabledEventTypes' => ['tasks'], + $service->saveOwnWebhookSettings(1, [ 'webhookUrl' => $webhookUrl, 'webhookEnabled' => '1', ]); @@ -697,8 +687,7 @@ public function test_an_invalid_webhook_url_is_rejected_before_anything_is_saved $this->assertSame(['webhookUrl' => ['notification.invalid_webhook_url']], $e->getErrorData()); } - $this->assertSame([], $saved, 'No preference may be written when the webhook URL is rejected'); - $this->assertSame(0, $editOwnCalls, 'The notifications flag must not be written either'); + $this->assertSame([], $saved, 'Nothing may be written when the webhook URL is rejected'); } public static function invalidWebhookUrlProvider(): array @@ -711,7 +700,6 @@ public static function invalidWebhookUrlProvider(): array 'not a url' => ['hooks.example.com/abc'], 'too long' => ['https://hooks.example.com/'.str_repeat('a', 2048)], 'array instead of string' => [['https://hooks.example.com/abc']], - 'explicit null' => [null], ]; } From f9081bfbd917dcde1f8faa06967e1dc1917303b0 Mon Sep 17 00:00:00 2001 From: Marcel Folaron Date: Sun, 18 Oct 2026 11:15:17 -0400 Subject: [PATCH 3/9] fix(users): address review on the background tab - Background changes (type, Unsplash photo, upload) only act on POST and read the type from the body. The Frontcontroller dispatches GET to these actions too and the origin check skips GET, so a cross-site link could reset a user's background. - Uploads are checked by the detected image type too: getimagesize() also recognizes TIFF, BMP and others, which browsers can't show. - The gradient/image tiles are native buttons with aria-pressed, so the choice works with the keyboard (global::selectable is mouse-only). - "Show other images" asks for the next page of a typed search instead of the same results; typing a new query starts at page 1 again. - The Background tab loads when it is shown (intersect once), so visits to other account settings tabs no longer query Unsplash. Co-Authored-By: Claude Opus 5.5 --- app/Domain/Users/Hxcontrollers/Background.php | 31 ++++-- app/Domain/Users/Services/Backgrounds.php | 33 ++++-- app/Domain/Users/Templates/editOwn.blade.php | 3 +- .../Templates/partials/background.blade.php | 52 ++++----- .../Users/Hxcontrollers/BackgroundTest.php | 101 ++++++++++++++++++ .../Domain/Users/Services/BackgroundsTest.php | 43 +++++++- 6 files changed, 217 insertions(+), 46 deletions(-) create mode 100644 tests/Unit/app/Domain/Users/Hxcontrollers/BackgroundTest.php diff --git a/app/Domain/Users/Hxcontrollers/Background.php b/app/Domain/Users/Hxcontrollers/Background.php index ae8af239f9..d0b0b0c602 100644 --- a/app/Domain/Users/Hxcontrollers/Background.php +++ b/app/Domain/Users/Hxcontrollers/Background.php @@ -7,6 +7,7 @@ use Leantime\Core\UI\Theme; use Leantime\Domain\Users\Services\Backgrounds; use Leantime\Views\Composers\Header; +use Symfony\Component\HttpFoundation\Response; /** * The "Background" tab of the user's own settings: choose the color gradient, an uploaded @@ -14,6 +15,8 @@ * * Every change saves right away and answers with the "current background" fragment, which also * swaps the page's background style out of band, so the new background shows without a reload. + * Changes only accept POST: the Frontcontroller dispatches these actions on GET too, and a GET + * skips the request-origin check, so a cross-site link could otherwise reset the background. */ class Background extends HtmxController { @@ -41,15 +44,19 @@ public function get(): void } /** - * Unsplash search results for the "q" query. + * Unsplash search results for the "q" query. "page" is sent by the refresh button only, so a + * changed query starts at page 1. */ public function search(): string { $query = (string) $this->incomingRequest->query->get('q', ''); $page = (int) $this->incomingRequest->query->get('page', 1); + $searchResult = $this->backgrounds->searchUnsplash($query, $page); + $this->assignTabVariables(); - $this->tpl->assign('photos', $this->backgrounds->searchUnsplash($query, $page)); + $this->tpl->assign('photos', $searchResult['photos']); + $this->tpl->assign('nextPage', $searchResult['nextPage']); return 'unsplash-results'; } @@ -57,10 +64,14 @@ public function search(): string /** * Switch between gradient and image. */ - public function setType(): string + public function setType(): string|Response { + if (! $this->incomingRequest->isMethod('POST')) { + return $this->tpl->emptyResponse(405); + } + try { - $this->backgrounds->setBackgroundType((string) $this->incomingRequest->input('type', '')); + $this->backgrounds->setBackgroundType((string) $this->incomingRequest->request->get('type', '')); $this->tpl->setNotification(__('notifications.background_settings_saved'), 'success'); } catch (ValidationException $exception) { $this->tpl->setNotification(__('notifications.background_not_saved'), 'error'); @@ -74,8 +85,12 @@ public function setType(): string /** * Use the Unsplash photo with the posted "photoId". */ - public function useUnsplashPhoto(): string + public function useUnsplashPhoto(): string|Response { + if (! $this->incomingRequest->isMethod('POST')) { + return $this->tpl->emptyResponse(405); + } + $photoId = (string) $this->incomingRequest->request->get('photoId', ''); if ($this->backgrounds->useUnsplashPhoto($photoId)) { @@ -92,8 +107,12 @@ public function useUnsplashPhoto(): string /** * Use the uploaded image ("file") as background. */ - public function upload(): string + public function upload(): string|Response { + if (! $this->incomingRequest->isMethod('POST')) { + return $this->tpl->emptyResponse(405); + } + if ($this->backgrounds->useUploadedImage($_FILES)) { $this->tpl->setNotification(__('notifications.background_settings_saved'), 'success'); } else { diff --git a/app/Domain/Users/Services/Backgrounds.php b/app/Domain/Users/Services/Backgrounds.php index e44fbdbcaa..7070b76ce4 100644 --- a/app/Domain/Users/Services/Backgrounds.php +++ b/app/Domain/Users/Services/Backgrounds.php @@ -41,6 +41,9 @@ class Backgrounds extends BaseService private const IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'webp']; + /** Image formats every browser can show; getimagesize() also detects TIFF, BMP and others. */ + private const IMAGE_TYPES = [IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP]; + public function __construct( private Theme $theme, private Environment $config, @@ -89,17 +92,21 @@ public function setBackgroundType(string $type): void /** * Search Unsplash for landscape photos. Without a query a random page of wallpapers is returned. - * Empty when Unsplash is not configured or the request fails. + * No photos when Unsplash is not configured or the request fails. nextPage is the page that + * shows other photos for the same query (back to 1 after the last one). * - * @return array + * @return array{photos: array, nextPage: int} */ public function searchUnsplash(string $query, int $page = 1): array { + $noPhotos = ['photos' => [], 'nextPage' => 1]; + if (! $this->isUnsplashEnabled()) { - return []; + return $noPhotos; } $query = trim($query); + $page = max(1, $page); if ($query === '') { $query = self::UNSPLASH_DEFAULT_QUERY; $page = random_int(1, self::UNSPLASH_DEFAULT_QUERY_PAGES); @@ -108,7 +115,7 @@ public function searchUnsplash(string $query, int $page = 1): array try { $response = $this->unsplashRequest()->get('/search/photos', [ 'query' => $query, - 'page' => max(1, $page), + 'page' => $page, 'per_page' => self::UNSPLASH_RESULTS_PER_PAGE, 'orientation' => 'landscape', 'content_filter' => 'high', @@ -116,13 +123,13 @@ public function searchUnsplash(string $query, int $page = 1): array } catch (\Throwable $exception) { Log::warning('Unsplash search failed: '.$exception->getMessage()); - return []; + return $noPhotos; } if (! $response->successful()) { Log::warning('Unsplash search failed with status '.$response->status()); - return []; + return $noPhotos; } $photos = []; @@ -140,7 +147,12 @@ public function searchUnsplash(string $query, int $page = 1): array ]; } - return $photos; + $totalPages = (int) $response->json('total_pages', 1); + + return [ + 'photos' => $photos, + 'nextPage' => $page < $totalPages ? $page + 1 : 1, + ]; } /** @@ -256,7 +268,8 @@ private function isUnsplashImageUrl(string $url): bool } /** - * Whether an uploaded file is a raster image. SVG is refused, it can carry scripts. + * Whether an uploaded file is a JPEG, PNG, GIF or WebP image, by name and by content. + * SVG is refused, it can carry scripts. * * @param array $upload One entry of $_FILES. */ @@ -271,7 +284,9 @@ private function isImageUpload(array $upload): bool return false; } - return @getimagesize((string) $upload['tmp_name']) !== false; + $imageInfo = @getimagesize((string) $upload['tmp_name']); + + return $imageInfo !== false && in_array($imageInfo[2], self::IMAGE_TYPES, true); } /** diff --git a/app/Domain/Users/Templates/editOwn.blade.php b/app/Domain/Users/Templates/editOwn.blade.php index b8ba720f80..fbcc7abb29 100644 --- a/app/Domain/Users/Templates/editOwn.blade.php +++ b/app/Domain/Users/Templates/editOwn.blade.php @@ -352,7 +352,8 @@
    - + {{-- Loads when the tab is shown, so Unsplash is only queried by users who open it. --}} +
    diff --git a/app/Domain/Users/Templates/partials/background.blade.php b/app/Domain/Users/Templates/partials/background.blade.php index 5011b4849f..08c8731b7e 100644 --- a/app/Domain/Users/Templates/partials/background.blade.php +++ b/app/Domain/Users/Templates/partials/background.blade.php @@ -13,35 +13,30 @@ @fragment('current-background')
    -
    - - - + {{-- Native buttons (not global::selectable) so the choice works with the keyboard. --}} +
    + @if($backgroundType === 'image' && $backgroundImage !== '') - -