mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-09-28 08:16:03 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26110850b7 | ||
|
|
7c1e9ce8df | ||
|
|
ad2ce6e865 | ||
|
|
821b538ae6 | ||
|
|
c104179ab5 | ||
|
|
2f60135cd0 | ||
|
|
f6d710ca85 | ||
|
|
7f3645f4a8 | ||
|
|
455e0228ca | ||
|
|
cd15016465 | ||
|
|
d6996b7a7e | ||
|
|
f1c681185f | ||
|
|
d901efc8e7 | ||
|
|
1dc7850d6c |
@@ -141,6 +141,7 @@ class Watch(Resource):
|
||||
watch['last_changed'] = watch_obj.last_changed
|
||||
watch['viewed'] = watch_obj.viewed
|
||||
watch['link'] = watch_obj.link
|
||||
watch['open_link'] = watch_obj.open_link
|
||||
|
||||
# Resolved processor config: tag override wins over watch-level config (mirrors restock processor logic)
|
||||
import json
|
||||
@@ -293,6 +294,20 @@ class WatchHistory(Resource):
|
||||
abort(404, message='No watch exists with the UUID of {}'.format(uuid))
|
||||
return watch.history, 200
|
||||
|
||||
# Delete all history/snapshots for a watch, but keep the watch itself
|
||||
# curl -X DELETE http://localhost:5000/api/v1/watch/<uuid_str:uuid>/history
|
||||
@auth.check_token
|
||||
@validate_openapi_request('deleteWatchHistory')
|
||||
def delete(self, uuid):
|
||||
"""Clear all snapshot history for a watch (the watch itself is kept)."""
|
||||
if not self.datastore.data['watching'].get(uuid):
|
||||
abort(404, message='No watch exists with the UUID of {}'.format(uuid))
|
||||
|
||||
# Same call as the UI "Clear history" button - wipes snapshots/screenshots and
|
||||
# resets last_checked etc, while preserving the watch and its processor config
|
||||
self.datastore.clear_watch_history(uuid)
|
||||
return 'OK', 204
|
||||
|
||||
|
||||
class WatchSingleHistory(Resource):
|
||||
def __init__(self, **kwargs):
|
||||
@@ -596,6 +611,7 @@ class CreateWatch(Resource):
|
||||
'last_checked': watch['last_checked'],
|
||||
'last_error': watch['last_error'],
|
||||
'link': watch.link,
|
||||
'open_link': watch.open_link,
|
||||
'page_title': watch['page_title'],
|
||||
'tags': [*tags], # Unpack dict keys to list (can't use list() since variable named 'list')
|
||||
'title': watch['title'],
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
"""Left-rail (action sidebar) display modes.
|
||||
|
||||
A leaf module on purpose: forms.py, flask_app.py and model/App.py all need these, and
|
||||
model/App.py seeding its default from forms.py would drag the whole form stack (~550
|
||||
modules) into the model layer.
|
||||
"""
|
||||
from flask_babel import lazy_gettext as _l
|
||||
|
||||
# The complete set of left-rail modes - flask_app.get_sidebar_mode_class() maps these
|
||||
# (and only these) onto body classes, so a new mode here needs a new mapping there.
|
||||
MENU_SIDEBAR_ACTIONMODES = [
|
||||
('expandable', _l('Expand on hover')), # Slim icon rail that expands on hover/focus
|
||||
('pinned-expanded', _l('Always expanded')), # Always expanded, never collapses
|
||||
('minimal', _l('Stays minimal')), # Always small, never expands
|
||||
]
|
||||
MENU_SIDEBAR_ACTIONMODES_DEFAULT = 'expandable'
|
||||
@@ -24,4 +24,4 @@ RSS_TEMPLATE_PLAINTEXT_DEFAULT = "<pre>{{watch_label}} had a change.\n\n{{diff}}
|
||||
|
||||
# @todo add some [edit]/[history]/[goto] etc links
|
||||
# @todo need {{watch_edit_link}} + delete + history link token
|
||||
RSS_TEMPLATE_HTML_DEFAULT = "<html><body>\n<h4><a href=\"{{watch_url}}\">{{watch_label}}</a></h4>\n<p>{{diff}}</p>\n</body></html>\n"
|
||||
RSS_TEMPLATE_HTML_DEFAULT = "<html><body>\n<h4><a href=\"{{watch_open_url}}\">{{watch_label}}</a></h4>\n<p>{{diff}}</p>\n</body></html>\n"
|
||||
|
||||
@@ -105,7 +105,7 @@ def construct_single_watch_routes(rss_blueprint, datastore):
|
||||
fe = fg.add_entry()
|
||||
title_suffix = f"Change @ {res['original_context']['change_datetime']}"
|
||||
populate_feed_entry(fe, watch, res.get('body', ''), guid, timestamp_to,
|
||||
link={'href': watch.get('url')}, title_suffix=title_suffix)
|
||||
link={'href': watch.open_link_override or watch.get('url')}, title_suffix=title_suffix)
|
||||
add_watch_categories(fe, watch, datastore)
|
||||
|
||||
response = make_response(fg.rss_str())
|
||||
|
||||
@@ -70,6 +70,8 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
# use the same as when it is triggered, but then override it with the form test values
|
||||
n_object = NotificationContextData({
|
||||
'watch_url': request.form.get('window_url', "https://changedetection.io"),
|
||||
# Falls back to watch_url when the watch has no 'Link to Open' set
|
||||
'watch_open_url': watch.open_link_override or request.form.get('window_url', "https://changedetection.io"),
|
||||
'notification_urls': notification_urls
|
||||
})
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
||||
output = render_template("preview.html",
|
||||
capabilities=capabilities,
|
||||
content=content,
|
||||
current_diff_url=watch['url'],
|
||||
current_diff_url=watch.open_link,
|
||||
current_version=timestamp,
|
||||
extra_stylesheets=extra_stylesheets,
|
||||
extra_title=f" - {gettext('Diff')} - {watch.label} @ {timestamp}",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{% endif %}
|
||||
|
||||
const highlight_submit_ignore_url="{{url_for('ui.ui_edit.highlight_submit_ignore_url', uuid=uuid)}}";
|
||||
const watch_url= {{watch_a.link|tojson}};
|
||||
const watch_url= {{watch_a.open_link|tojson}};
|
||||
|
||||
// Initial scroll position: if set, scroll to this line number in #difference on page load
|
||||
const initialScrollToLineNumber = {{ initial_scroll_line_number|default('null') }};
|
||||
|
||||
@@ -100,6 +100,23 @@
|
||||
{{ render_field(form.title, class="m-d", placeholder=watch.label) }}
|
||||
<span class="pure-form-message-inline">{{ _('Automatically uses the page title if found, you can also use your own title/description here') }}</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
<details class="form-wrapper">
|
||||
<summary>{{ _('Extra Page Title and Link options') }}</summary>
|
||||
<br>
|
||||
<div class=" border-fieldset">
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.link_to_open, placeholder="https://...", class="m-d") }}
|
||||
<span class="pure-form-message-inline">{{ _('Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked.') }}</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_ternary_field(form.use_page_title_in_list) }}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group time-between-check border-fieldset">
|
||||
|
||||
{{ render_checkbox_field(form.time_between_check_use_default, class="use-default-timecheck") }}
|
||||
@@ -119,13 +136,6 @@
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<div class="pure-control-group">
|
||||
{{ render_checkbox_field(form.filter_failure_notification_send) }}
|
||||
<span class="pure-form-message-inline">
|
||||
{{ _('Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and your filter will not work anymore.') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_field(form.history_snapshot_max_length, class="history_snapshot_max_length") }}
|
||||
<span class="pure-form-message-inline">{{ _('Limit collection of history snapshots for each watch to this number of history items.') }}
|
||||
@@ -133,9 +143,7 @@
|
||||
{{ _('Set to empty to use system settings default') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pure-control-group">
|
||||
{{ render_ternary_field(form.use_page_title_in_list) }}
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
@@ -250,7 +258,7 @@ Math: {{ 1 + 1 }}") }}
|
||||
<div class="flex-wrapper" >
|
||||
|
||||
<div id="browser-steps-ui" class="noselect">
|
||||
<div class="noselect" id="browsersteps-selector-wrapper" style="width: 100%">
|
||||
<div class="noselect" id="browsersteps-selector-wrapper">
|
||||
<span class="loader" >
|
||||
<span id="browsersteps-click-start">
|
||||
<h2 >{{ _('Click here to Start') }}</h2>
|
||||
@@ -295,6 +303,12 @@ Math: {{ 1 + 1 }}") }}
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="pure-control-group">
|
||||
{{ render_checkbox_field(form.filter_failure_notification_send) }}
|
||||
<span class="pure-form-message-inline">
|
||||
{{ _('Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and your filter will not work anymore.') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="field-group" id="notification-field-group">
|
||||
{% if has_default_notification_urls %}
|
||||
<div class="inline-warning">
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
{%- set checking_now = is_checking_now(watch) -%}
|
||||
{%- set history_n = watch.history_n -%}
|
||||
{%- set favicon = watch.get_favicon_filename() -%}
|
||||
{# Resolved once per row: each read re-validates the URL and, for a Jinja2 templated
|
||||
URL, builds a fresh sandboxed environment - see validate_url.py #}
|
||||
{%- set open_link = watch.open_link -%}
|
||||
{%- set error_texts = watch.compile_error_texts(has_proxies=has_proxies) -%}
|
||||
{%- set system_use_url_watchlist = datastore.data['settings']['application']['ui'].get('use_page_title_in_list') -%}
|
||||
{# Class settings mirrored in changedetectionio/static/js/realtime.js for the frontend #}
|
||||
@@ -48,7 +51,7 @@
|
||||
<div class="grid-wrapper">
|
||||
{% if 'favicons_enabled' not in ui_settings or ui_settings['favicons_enabled'] %}
|
||||
<div class="favicon">
|
||||
<a target="_blank" rel="noopener" href="{{ watch.link.replace('source:','') }}">
|
||||
<a target="_blank" rel="noopener" href="{{ open_link }}">
|
||||
{# Intersection Observer lazy loading: store real URL in data-src, load only when visible in viewport #}
|
||||
<img alt="Favicon thumbnail"
|
||||
class="favicon lazy-favicon"
|
||||
@@ -74,7 +77,7 @@
|
||||
{% else %}
|
||||
{{ watch.get('title') or watch.link }}
|
||||
{% endif %}
|
||||
<a class="external" target="_blank" rel="noopener" href="{{ watch.link.replace('source:','') }}"> </a>
|
||||
<a class="external" target="_blank" rel="noopener" href="{{ open_link }}" title="{{ open_link }}"> </a>
|
||||
</span>
|
||||
|
||||
{%- for watch_tag_uuid, watch_tag in datastore.get_all_tags_for_watch(watch['uuid']).items() -%}
|
||||
|
||||
@@ -56,6 +56,7 @@ from changedetectionio.api import (
|
||||
WatchSingleHistory,
|
||||
)
|
||||
from changedetectionio.api.Search import Search
|
||||
from changedetectionio.blueprint.menu_modes import MENU_SIDEBAR_ACTIONMODES, MENU_SIDEBAR_ACTIONMODES_DEFAULT
|
||||
from changedetectionio.favicon_utils import get_favicon_mime_type
|
||||
from changedetectionio.languages import (
|
||||
get_available_languages,
|
||||
@@ -286,20 +287,32 @@ def _filter_url(**overrides):
|
||||
|
||||
@app.template_global()
|
||||
def get_sidebar_mode_class():
|
||||
"""Body class that drives the left-rail behaviour (see parts/_action_sidebar.scss).
|
||||
"""Body class(es) that drive the left-rail behaviour (see parts/_action_sidebar.scss).
|
||||
|
||||
'collapsed' -> slim icon rail that expands on hover/focus (actionsidebar-minimal)
|
||||
'pinned' -> rail always expanded with labels visible (actionside-bar-on)
|
||||
Only the modes offered by MENU_SIDEBAR_ACTIONMODES are honoured - anything else in the
|
||||
datastore (a stale value from an older release, hand-edited JSON) falls back to
|
||||
MENU_SIDEBAR_ACTIONMODES_DEFAULT rather than leaking through as a body class.
|
||||
|
||||
'expandable' -> icon-only rail, rolls out over the content on hover/focus
|
||||
'pinned-expanded' -> rail always expanded, labels visible at rest
|
||||
'minimal' -> icon-only rail that never expands
|
||||
"""
|
||||
mode = datastore.data['settings']['application'].get('ui', {}).get('sidebar_mode', 'collapsed')
|
||||
# Pinned mode is permanently expanded, so it carries 'action-side-bar-expanded'
|
||||
# from the start. In collapsed mode that class is toggled on hover/focus by
|
||||
# static/js/sidebar.js.
|
||||
return (
|
||||
'actionside-bar-on action-side-bar-expanded'
|
||||
if mode == 'pinned'
|
||||
else 'actionsidebar-minimal'
|
||||
)
|
||||
|
||||
# 'actionsidebar-minimal' - collapsed icon rail (hover-to-expand lives in CSS + static/js/sidebar.js)
|
||||
# 'actionsidebar-no-expand' - opts that rail out of hover-to-expand
|
||||
# 'actionside-bar-on' - always-open rail
|
||||
# 'action-side-bar-expanded'- expanded logo/stats block
|
||||
body_classes = {
|
||||
'expandable': 'actionsidebar-minimal',
|
||||
'pinned-expanded': 'actionside-bar-on action-side-bar-expanded',
|
||||
'minimal': 'actionsidebar-minimal actionsidebar-no-expand',
|
||||
}
|
||||
|
||||
mode = datastore.data['settings']['application'].get('ui', {}).get('sidebar_mode')
|
||||
if mode not in {choice for choice, _label in MENU_SIDEBAR_ACTIONMODES} or mode not in body_classes:
|
||||
mode = MENU_SIDEBAR_ACTIONMODES_DEFAULT
|
||||
|
||||
return body_classes[mode]
|
||||
|
||||
|
||||
@app.template_global()
|
||||
|
||||
@@ -4,6 +4,7 @@ from loguru import logger
|
||||
from wtforms.widgets.core import TimeInput
|
||||
from flask_babel import lazy_gettext as _l, gettext
|
||||
|
||||
from changedetectionio.blueprint.menu_modes import MENU_SIDEBAR_ACTIONMODES, MENU_SIDEBAR_ACTIONMODES_DEFAULT
|
||||
from changedetectionio.blueprint.rss import RSS_FORMAT_TYPES, RSS_TEMPLATE_TYPE_OPTIONS, RSS_TEMPLATE_HTML_DEFAULT
|
||||
from changedetectionio.llm.ui_strings import LLM_INTENT_WATCH_PLACEHOLDER
|
||||
from changedetectionio.llm.evaluator import (
|
||||
@@ -939,6 +940,7 @@ class SingleBrowserStep(Form):
|
||||
class processor_text_json_diff_form(commonSettingsForm):
|
||||
|
||||
url = StringField(_l('Web Page URL'), validators=[validateURL()])
|
||||
link_to_open = StringField(_l('Open Link Override'), validators=[validators.Optional(), validateURL()], default='')
|
||||
tags = StringTagUUID(_l('Group Tag'), [validators.Optional()], default='')
|
||||
|
||||
time_between_check = EnhancedFormField(
|
||||
@@ -1046,6 +1048,19 @@ class processor_text_json_diff_form(commonSettingsForm):
|
||||
self.url.errors.append(gettext('Invalid template syntax: %(error)s') % {'error': e})
|
||||
result = False
|
||||
|
||||
# Attempt to validate jinja2 templates in the optional "Link to Open"
|
||||
if self.link_to_open.data and self.link_to_open.data.strip():
|
||||
try:
|
||||
jinja_render(template_str=self.link_to_open.data)
|
||||
except ModuleNotFoundError as e:
|
||||
logger.error(e)
|
||||
self.link_to_open.errors.append(gettext('Invalid template syntax configuration: %(error)s') % {'error': e})
|
||||
result = False
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
self.link_to_open.errors.append(gettext('Invalid template syntax: %(error)s') % {'error': e})
|
||||
result = False
|
||||
|
||||
# Attempt to validate jinja2 templates in the body
|
||||
if self.body.data and self.body.data.strip():
|
||||
try:
|
||||
@@ -1164,9 +1179,8 @@ class globalSettingsApplicationUIForm(Form):
|
||||
choices=[('long', _l('Long (1 minute ago)')), ('short', _l('Short (1m ago)'))],
|
||||
default='long', validators=[validators.Optional()])
|
||||
sidebar_mode = SelectField(_l('Navigation sidebar'),
|
||||
choices=[('collapsed', _l('Collapsed icon rail (expands on hover)')),
|
||||
('pinned', _l('Always expanded'))],
|
||||
default='collapsed', validators=[validators.Optional()])
|
||||
choices=MENU_SIDEBAR_ACTIONMODES,
|
||||
default=MENU_SIDEBAR_ACTIONMODES_DEFAULT, validators=[validators.Optional()])
|
||||
|
||||
# datastore.data['settings']['application']..
|
||||
class globalSettingsApplicationForm(commonSettingsForm):
|
||||
|
||||
@@ -19,6 +19,7 @@ def get_timeago_locale(flask_locale, short=False):
|
||||
- Swedish: Flask uses 'sv', timeago uses 'sv_SE'
|
||||
- Norwegian: Flask uses 'no', timeago uses 'nb_NO' or 'nn_NO'
|
||||
- Hindi: Flask uses 'hi', timeago uses 'in_HI'
|
||||
- Indonesian: Flask uses 'id', timeago uses 'in_ID'
|
||||
- Czech: Flask uses 'cs', but timeago doesn't support Czech - fallback to English
|
||||
|
||||
Args:
|
||||
@@ -46,6 +47,7 @@ def get_timeago_locale(flask_locale, short=False):
|
||||
'sv': 'sv_SE', # Swedish
|
||||
'no': 'nb_NO', # Norwegian Bokmål
|
||||
'hi': 'in_HI', # Hindi
|
||||
'id': 'in_ID', # Indonesian
|
||||
'cs': 'en', # Czech not supported by timeago, fallback to English
|
||||
'ja': 'ja', # Japanese
|
||||
'uk': 'uk', # Ukrainian
|
||||
@@ -156,6 +158,7 @@ LANGUAGE_DATA = {
|
||||
'tr': {'flag': 'fi fi-tr fis', 'name': 'Türkçe'},
|
||||
'ar': {'flag': 'fi fi-sa fis', 'name': 'العربية'},
|
||||
'hi': {'flag': 'fi fi-in fis', 'name': 'हिन्दी'},
|
||||
'id': {'flag': 'fi fi-id fis', 'name': 'Bahasa Indonesia'},
|
||||
'uk': {'flag': 'fi fi-ua fis', 'name': 'Українська'},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from os import getenv
|
||||
from copy import deepcopy
|
||||
|
||||
from changedetectionio.blueprint.menu_modes import MENU_SIDEBAR_ACTIONMODES_DEFAULT
|
||||
from changedetectionio.blueprint.rss import RSS_FORMAT_TYPES, RSS_CONTENT_FORMAT_DEFAULT
|
||||
from changedetectionio.model.Tags import TagsDict
|
||||
|
||||
@@ -80,7 +81,7 @@ class model(dict):
|
||||
'socket_io_enabled': True,
|
||||
'favicons_enabled': True,
|
||||
'timeago_format': 'long', # 'long' = "1 minute ago", 'short' = "1m ago"
|
||||
'sidebar_mode': 'collapsed', # 'collapsed' = slim icon rail, expands on hover; 'pinned' = always expanded
|
||||
'sidebar_mode': MENU_SIDEBAR_ACTIONMODES_DEFAULT, # one of blueprint.menu_modes.MENU_SIDEBAR_ACTIONMODES
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,10 +272,12 @@ class model(EntityPersistenceMixin, watch_base):
|
||||
def has_unviewed(self):
|
||||
return int(self.newest_history_key) > int(self['last_viewed']) and self.__history_n >= 2
|
||||
|
||||
@property
|
||||
def link(self):
|
||||
def _resolve_link(self, url, flash_on_template_error=True):
|
||||
"""Validate, Jinja2-render and de-'source:' a URL so it is safe to put in an href.
|
||||
|
||||
url = self.get('url', '')
|
||||
Returns 'DISABLED' when the URL is missing/unsafe, or '' when its Jinja2 template
|
||||
could not be rendered.
|
||||
"""
|
||||
if not is_safe_valid_url(url):
|
||||
return 'DISABLED'
|
||||
|
||||
@@ -286,11 +288,15 @@ class model(EntityPersistenceMixin, watch_base):
|
||||
ready_url = jinja_render(template_str=url)
|
||||
except Exception as e:
|
||||
logger.critical(f"Invalid URL template for: '{url}' - {str(e)}")
|
||||
from flask import flash, url_for
|
||||
from markupsafe import Markup
|
||||
message = Markup('<a href="{}#general">The URL {} is invalid and cannot be used, click to edit</a>'.format(
|
||||
url_for('ui.ui_edit.edit_page', uuid=self.get('uuid')), self.get('url', '')))
|
||||
flash(message, 'error')
|
||||
# has_request_context() guard: this is also reached from worker threads and the
|
||||
# notification path, where flash() would raise "Working outside of request context".
|
||||
from flask import has_request_context
|
||||
if flash_on_template_error and has_request_context():
|
||||
from flask import flash, url_for
|
||||
from markupsafe import Markup
|
||||
message = Markup('<a href="{}#general">The URL {} is invalid and cannot be used, click to edit</a>'.format(
|
||||
url_for('ui.ui_edit.edit_page', uuid=self.get('uuid')), url))
|
||||
flash(message, 'error')
|
||||
return ''
|
||||
|
||||
if ready_url.startswith('source:'):
|
||||
@@ -301,6 +307,34 @@ class model(EntityPersistenceMixin, watch_base):
|
||||
return 'DISABLED'
|
||||
return ready_url
|
||||
|
||||
@property
|
||||
def link(self):
|
||||
"""The URL that actually gets fetched/checked."""
|
||||
return self._resolve_link(self.get('url', ''))
|
||||
|
||||
@property
|
||||
def open_link_override(self):
|
||||
"""The resolved optional 'Link to Open', or '' when unset/unusable."""
|
||||
override = str(self.get('link_to_open') or '').strip()
|
||||
if not override:
|
||||
return ''
|
||||
|
||||
# Don't flash on a broken template here - `link` already surfaces that for the watched URL,
|
||||
# and a bad "Link to Open" should never stop the page rendering.
|
||||
ready = self._resolve_link(override, flash_on_template_error=False)
|
||||
if not ready or ready == 'DISABLED':
|
||||
return ''
|
||||
return ready
|
||||
|
||||
@property
|
||||
def open_link(self):
|
||||
"""The URL a human should be sent to when they click through to "the site".
|
||||
|
||||
Some watches point at an API endpoint or RSS feed that is useless in a browser -
|
||||
'Link to Open' lets the user store the real page for those. Falls back to `link`.
|
||||
"""
|
||||
return self.open_link_override or self.link
|
||||
|
||||
@property
|
||||
def domain_only_from_link(self):
|
||||
from urllib.parse import urlparse
|
||||
|
||||
@@ -207,6 +207,7 @@ class watch_base(dict):
|
||||
'last_error': False,
|
||||
'last_notification_error': None,
|
||||
'last_viewed': 0, # history key value of the last viewed via the [diff] link
|
||||
'link_to_open': '', # Optional human-facing URL to open instead of 'url' (eg. watch an API/RSS endpoint, open the real page)
|
||||
'llm_backend_profile': True, # @note - now its just a bool but in the near future we can select a LLM profile or 'off'/false
|
||||
'llm_change_summary': '', # Prompt for AI Change Summary — replaces {{ diff }} in notifications
|
||||
'llm_change_summary_mode': 'replace', # 'replace' the inherited prompt, or 'append' to it
|
||||
|
||||
@@ -241,6 +241,7 @@ class NotificationContextData(dict):
|
||||
'watch_tag': None,
|
||||
'watch_title': None,
|
||||
'watch_url': 'https://WATCH-PLACE-HOLDER/',
|
||||
'watch_open_url': 'https://WATCH-PLACE-HOLDER/', # watch['link_to_open'] when set, otherwise the same as watch_url
|
||||
'watch_uuid': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', # Converted to 'watch_uuid' in create_notification_parameters
|
||||
})
|
||||
|
||||
@@ -350,6 +351,7 @@ def set_basic_notification_vars(current_snapshot, prev_snapshot, watch, triggere
|
||||
'triggered_text': triggered_text,
|
||||
'uuid': watch.get('uuid') if watch else None,
|
||||
'watch_url': watch.get('url') if watch else None,
|
||||
'watch_open_url': (watch.open_link_override or watch.get('url')) if watch else None,
|
||||
'watch_uuid': watch.get('uuid') if watch else None,
|
||||
'watch_mime_type': watch.get('content-type')
|
||||
}
|
||||
@@ -511,6 +513,7 @@ Thanks - Your omniscient changedetection.io installation.
|
||||
if 'notification_urls' in n_object:
|
||||
n_object.update({
|
||||
'watch_url': watch['url'],
|
||||
'watch_open_url': watch.open_link_override or watch['url'],
|
||||
'uuid': watch_uuid,
|
||||
'screenshot': None
|
||||
})
|
||||
@@ -560,6 +563,7 @@ Thanks - Your omniscient changedetection.io installation.
|
||||
if 'notification_urls' in n_object:
|
||||
n_object.update({
|
||||
'watch_url': watch['url'],
|
||||
'watch_open_url': watch.open_link_override or watch['url'],
|
||||
'uuid': watch_uuid
|
||||
})
|
||||
self.notification_q.put(n_object)
|
||||
|
||||
@@ -430,7 +430,7 @@ def render(watch, datastore, request, url_for, render_template, flash, redirect)
|
||||
change_percentage=change_percentage,
|
||||
comparison_data=comparison_data, # Full history for charts/visualization
|
||||
comparison_method=method_display,
|
||||
current_diff_url=watch['url'],
|
||||
current_diff_url=watch.open_link,
|
||||
from_version=from_version,
|
||||
percentage_different=change_percentage,
|
||||
threshold=pixel_difference_threshold_sensitivity,
|
||||
|
||||
@@ -106,5 +106,5 @@ def render(watch, datastore, request, url_for, render_template, flash, redirect)
|
||||
uuid=watch.get('uuid'),
|
||||
versions=versions,
|
||||
timestamp=timestamp,
|
||||
current_diff_url=watch['url']
|
||||
current_diff_url=watch.open_link
|
||||
)
|
||||
|
||||
@@ -191,7 +191,7 @@ def render(watch, datastore, request, url_for, render_template, flash, redirect,
|
||||
'restock_diff/difference.html',
|
||||
uuid=uuid,
|
||||
watch=watch,
|
||||
current_diff_url=watch['url'],
|
||||
current_diff_url=watch.open_link,
|
||||
extra_title=f" - {watch.label} - {gettext('Price history')}",
|
||||
last_error=watch['last_error'],
|
||||
screenshot=watch.get_screenshot(),
|
||||
|
||||
@@ -216,7 +216,7 @@ def render(watch, datastore, request, url_for, render_template, flash, redirect,
|
||||
#initial_scroll_line_number=100,
|
||||
bottom_horizontal_offscreen_contents=offscreen_content,
|
||||
content=content,
|
||||
current_diff_url=watch['url'],
|
||||
current_diff_url=watch.open_link,
|
||||
diff_cell_grid=diff_cell_grid,
|
||||
diff_prefs=diff_prefs,
|
||||
extra_classes=' '.join(filter(None, ['difference-page', 'llm-configured' if llm_configured else ''])),
|
||||
|
||||
@@ -78,7 +78,9 @@ $(document).ready(function () {
|
||||
// bootstrap it, this will trigger everything else
|
||||
$('#browsersteps-img').bind('load', function () {
|
||||
$('body').addClass('full-width');
|
||||
console.log("Loaded background...");
|
||||
console.log(`Loaded background ${this.naturalWidth}px` );
|
||||
// For the UI width of the whole edit area
|
||||
document.documentElement.style.setProperty('--browser-steps-max-width', `${this.naturalWidth+200}px` );
|
||||
|
||||
document.getElementById("browsersteps-selector-canvas");
|
||||
c = document.getElementById("browsersteps-selector-canvas");
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
// Left-rail expand/collapse state.
|
||||
// Adds `action-side-bar-expanded` to <body> whenever the rail is showing its
|
||||
// labels. In pinned mode (body.actionside-bar-on) the class is already present
|
||||
// from page load; in collapsed mode (body.actionsidebar-minimal) the rail only
|
||||
// expands on hover/focus, so we toggle the class to match.
|
||||
// labels. In 'pinned-expanded' mode (body.actionside-bar-on) the class is already
|
||||
// present from page load; in 'expandable' mode (body.actionsidebar-minimal) the rail
|
||||
// only expands on hover/focus, so we toggle the class to match. 'minimal' mode is
|
||||
// also the collapsed rail but carries `actionsidebar-no-expand` and never rolls out.
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
if (!document.body.classList.contains('actionsidebar-minimal')) {
|
||||
if (!document.body.classList.contains('actionsidebar-minimal') ||
|
||||
document.body.classList.contains('actionsidebar-no-expand')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,9 @@ window.initVisualSelector = function (opts) {
|
||||
$('#selector-current-xpath, #clear-selector').hide();
|
||||
})
|
||||
.on('load', () => {
|
||||
console.log("Loaded background...");
|
||||
console.log(`Loaded background ${$selectorBackgroundElem[0].naturalWidth}px`);
|
||||
// For the UI width of the whole edit area
|
||||
document.documentElement.style.setProperty('--visualselector-max-width', `${$selectorBackgroundElem[0].naturalWidth}px` );
|
||||
c = $selectorCanvasElem[0];
|
||||
xctx = c.getContext("2d");
|
||||
ctx = c.getContext("2d");
|
||||
|
||||
@@ -32,21 +32,27 @@ $action-sidebar-content-slot: 1100px;
|
||||
// Hidden on mobile: the mobile drawer (hamburger) carries these items
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
body.actionside-bar-on {
|
||||
.action-sidebar {
|
||||
/* width: $action-sidebar-width-expanded; // reserve enough to host the expanded inner block*/
|
||||
// MINIMAL MODE: the rail's footprint in the flex row is frozen at the
|
||||
// collapsed width (+ the inner block's horizontal padding, which is
|
||||
// content-box). The inner block is taken out of flow (absolute, below) so
|
||||
// its hover expansion rolls out ON TOP of the page content instead of
|
||||
// widening this flex item and reflowing `.app-main`.
|
||||
body.actionsidebar-minimal & {
|
||||
flex: 0 0 auto;
|
||||
width: calc(#{$action-sidebar-width-collapsed} + #{$common-gap * 2});
|
||||
overflow: visible; // let the expanded inner block escape the frame
|
||||
}
|
||||
}
|
||||
|
||||
// The actual interactive item block — wraps only the items, not the whole height.
|
||||
//
|
||||
// Two body-level modes drive the rail's display state:
|
||||
// body.actionside-bar-on → always expanded (icons + labels visible all the time)
|
||||
// body.actionsidebar-minimal → icon-only collapsed rail; expands on hover/focus
|
||||
// `actionside-bar-on` is the default applied in templates/base.html. Both the
|
||||
// hover-to-expand width animation and the label fade-in are gated on the
|
||||
// Body-level classes drive the rail's display state (emitted by
|
||||
// flask_app.get_sidebar_mode_class() from the `sidebar_mode` setting):
|
||||
// body.actionside-bar-on → always expanded (icons + labels visible all the time)
|
||||
// body.actionsidebar-minimal → icon-only collapsed rail; expands on hover/focus
|
||||
// body.actionsidebar-no-expand → alongside the above: stays collapsed, no hover-out
|
||||
// Both the hover-to-expand width animation and the label fade-in are gated on the
|
||||
// minimal class so that the always-expanded mode (or no mode) doesn't trigger
|
||||
// a needless layout jump when the user mouses across the rail.
|
||||
.action-sidebar-inner {
|
||||
@@ -64,12 +70,40 @@ body.actionside-bar-on {
|
||||
padding-left: $common-gap;
|
||||
padding-right: $common-gap;
|
||||
|
||||
// Hover-to-expand only fires in minimal mode.
|
||||
body.actionsidebar-minimal &:hover,
|
||||
body.actionsidebar-minimal &:focus-within {
|
||||
// MINIMAL MODE: lift the block out of the flex flow so growing it can't
|
||||
// resize the rail (and therefore can't shift the content column). The rail
|
||||
// is `position: sticky`, i.e. a positioned element, so top/left/bottom here
|
||||
// anchor to it; top+bottom keep the column full-height, which the footer
|
||||
// list's `margin-top: auto` still needs.
|
||||
//
|
||||
// Once it floats above the page it needs its own backdrop, or the content
|
||||
// it rolls over would read straight through the rail's translucent 5% white.
|
||||
// It can't just be a flat colour: the page background is a fixed, viewport-
|
||||
// sized gradient (`body:after`), so the rail repaints that same gradient with
|
||||
// `background-attachment: fixed` — which makes the viewport the positioning
|
||||
// area — and lays the rail's own 5% white on top. Result: opaque, but
|
||||
// pixel-identical to the translucent rail it replaces, at rest and rolled out.
|
||||
body.actionsidebar-minimal & {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
background-color: var(--color-background-page); // fallback under the gradient
|
||||
background-image:
|
||||
linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.05)),
|
||||
var(--page-background-gradient);
|
||||
background-attachment: fixed, fixed;
|
||||
}
|
||||
|
||||
// Hover-to-expand only fires in minimal mode, and not for 'minimal' (no-expand).
|
||||
body.actionsidebar-minimal:not(.actionsidebar-no-expand) &:hover,
|
||||
body.actionsidebar-minimal:not(.actionsidebar-no-expand) &:focus-within {
|
||||
width: $action-sidebar-width-expanded;
|
||||
// Enter is smoother.
|
||||
transition: width 0.22s cubic-bezier(0.2, 0.7, 0.2, 1);
|
||||
// Lift it off the page while it's rolled out over the content.
|
||||
box-shadow: var(--color-sidebar-shadow);
|
||||
}
|
||||
|
||||
// Always-expanded mode: no hover gate; the rail just sits open.
|
||||
@@ -250,8 +284,8 @@ ul.action-sidebar-list {
|
||||
// When the inner block expands in MINIMAL mode, reveal every label/badge
|
||||
// together with a smooth enter. Always-expanded mode handles labels via the
|
||||
// `body.actionside-bar-on` block below — no hover gate needed there.
|
||||
body.actionsidebar-minimal .action-sidebar-inner:hover,
|
||||
body.actionsidebar-minimal .action-sidebar-inner:focus-within {
|
||||
body.actionsidebar-minimal:not(.actionsidebar-no-expand) .action-sidebar-inner:hover,
|
||||
body.actionsidebar-minimal:not(.actionsidebar-no-expand) .action-sidebar-inner:focus-within {
|
||||
.action-sidebar-item {
|
||||
.action-label {
|
||||
opacity: 1;
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
width: 100%;
|
||||
overflow-y: scroll;
|
||||
position: relative;
|
||||
height: 80vh;
|
||||
|
||||
> img {
|
||||
position: absolute;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#language-selector-flag {
|
||||
display: inline-block;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
vertical-align: middle;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
|
||||
&.toast-top-center {
|
||||
top: 100px;
|
||||
top: 120px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,12 @@
|
||||
--color-background-gradient-first: #5ad8f7;
|
||||
--color-background-gradient-second: #2f50af;
|
||||
--color-background-gradient-third: #9150bf;
|
||||
// The page's atmosphere gradient, shared so anything that needs to sit ON the
|
||||
// page background (e.g. the left rail rolling out over content) can repaint
|
||||
// an identical copy instead of approximating it with a flat colour. The
|
||||
// referenced gradient stops are re-declared per theme below, so this single
|
||||
// definition follows light/dark automatically.
|
||||
--page-background-gradient: linear-gradient(130deg, var(--color-background-gradient-first), var(--color-background-gradient-second) 41.07%, var(--color-background-gradient-third) 84.05%);
|
||||
--color-background: var(--color-white);
|
||||
--color-text: var(--color-grey-200);
|
||||
--color-link: #1b98f8;
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
body:has(#browser-steps:target) .edit-form {
|
||||
max-width: min(var(--browser-steps-max-width, 1280px), 95vw);
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
body:has(#visualselector:target) .edit-form {
|
||||
width: 95%;
|
||||
max-width: min(var(--visualselector-max-width, 1280px), 95vw);
|
||||
}
|
||||
|
||||
#selector-wrapper {
|
||||
height: 100%;
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
@use "parts/sub_tabs";
|
||||
|
||||
// Smooth transitions for theme switching
|
||||
body,
|
||||
// Disabled - people complained
|
||||
/*body,
|
||||
.pure-table,
|
||||
.pure-table thead,
|
||||
.pure-table td,
|
||||
@@ -65,7 +66,7 @@ code,
|
||||
a,
|
||||
.watch-controls img {
|
||||
transition: color 0.4s ease, background-color 0.4s ease, background 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;
|
||||
}
|
||||
}*/
|
||||
|
||||
body {
|
||||
color: var(--color-text);
|
||||
@@ -305,7 +306,7 @@ code {
|
||||
|
||||
body:after {
|
||||
content: "";
|
||||
background: linear-gradient(130deg, var(--color-background-gradient-first), var(--color-background-gradient-second) 41.07%, var(--color-background-gradient-third) 84.05%);
|
||||
background: var(--page-background-gradient);
|
||||
}
|
||||
|
||||
body:after,
|
||||
@@ -697,12 +698,12 @@ footer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (max-width: 760px),
|
||||
(min-device-width: 768px) and (max-device-width: $desktop-wide-breakpoint) {
|
||||
.edit-form {
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#nav-menu {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,6 +25,10 @@
|
||||
<td><code>{{ '{{watch_url}}' }}</code></td>
|
||||
<td>{{ _('The URL being watched.') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>{{ '{{watch_open_url}}' }}</code></td>
|
||||
<td>{{ _('The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API endpoint or RSS feed but you want to link to the real page.') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>{{ '{{watch_uuid}}' }}</code></td>
|
||||
<td>{{ _('The UUID of the watch.') }}</td>
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<li class="action-sidebar-li" id="action-sidebar-logo">
|
||||
|
||||
{%- if has_password and not current_user.is_authenticated -%}
|
||||
<a id="cdio-logo" href="https://changedetection.io" rel="noopener">
|
||||
<a id="cdio-logo" href="https://changedetection.io" rel="noopener" title="ChangeDetection.io intelligent web page change detection.">
|
||||
<span id="logo-expanded"><strong>Change</strong>Detection.io</span>
|
||||
<span id="logo-short"><strong>CD</strong>IO</span>
|
||||
</a>
|
||||
{%- else -%}
|
||||
<a id="cdio-logo" href="{{url_for('watchlist.index')}}">
|
||||
<a id="cdio-logo" href="{{url_for('watchlist.index')}}" title="ChangeDetection.io intelligent web page change detection.">
|
||||
<span id="logo-expanded"><strong>Change</strong>Detection.io</span>
|
||||
<span id="logo-short"><strong>CD</strong>IO</span>
|
||||
</a>
|
||||
|
||||
@@ -338,6 +338,84 @@ def test_api_simple(client, live_server, measure_memory_usage, datastore_path):
|
||||
)
|
||||
assert len(res.json) == 0, "Watch list should be empty"
|
||||
|
||||
def test_api_delete_watch_history(client, live_server, measure_memory_usage, datastore_path):
|
||||
"""DELETE /api/v1/watch/<uuid>/history should wipe the snapshots but keep the watch (#4397)"""
|
||||
|
||||
api_key = live_server.app.config['DATASTORE'].data['settings']['application'].get('api_access_token')
|
||||
|
||||
set_original_response(datastore_path=datastore_path)
|
||||
test_url = url_for('test_endpoint', _external=True)
|
||||
|
||||
res = client.post(
|
||||
url_for("createwatch"),
|
||||
data=json.dumps({"url": test_url}),
|
||||
headers={'content-type': 'application/json', 'x-api-key': api_key},
|
||||
follow_redirects=True
|
||||
)
|
||||
assert res.status_code == 201
|
||||
watch_uuid = res.json.get('uuid')
|
||||
wait_for_all_checks(client)
|
||||
|
||||
# A second snapshot so we know we're clearing more than one
|
||||
set_modified_response(datastore_path=datastore_path)
|
||||
client.get(url_for("watch", uuid=watch_uuid, recheck='1'), headers={'x-api-key': api_key})
|
||||
wait_for_all_checks(client)
|
||||
|
||||
res = client.get(
|
||||
url_for("watchhistory", uuid=watch_uuid),
|
||||
headers={'x-api-key': api_key},
|
||||
)
|
||||
assert len(res.json) == 2, "Should have two history entries before clearing"
|
||||
|
||||
# Unknown watch UUID should 404 and not blow up
|
||||
res = client.delete(
|
||||
url_for("watchhistory", uuid='4d8b5b4a-8e0b-4d4a-9f57-3f2b1c0d9e11'),
|
||||
headers={'x-api-key': api_key},
|
||||
)
|
||||
assert res.status_code == 404
|
||||
|
||||
# Requires the API key
|
||||
res = client.delete(url_for("watchhistory", uuid=watch_uuid))
|
||||
assert res.status_code == 403
|
||||
|
||||
# Pause it first - clearing resets last_checked to 0 which otherwise makes the ticker
|
||||
# queue an instant recheck, and that would race with the assertions below
|
||||
client.get(url_for("watch", uuid=watch_uuid, paused='paused'), headers={'x-api-key': api_key})
|
||||
|
||||
# Now really clear it
|
||||
res = client.delete(
|
||||
url_for("watchhistory", uuid=watch_uuid),
|
||||
headers={'x-api-key': api_key},
|
||||
)
|
||||
assert res.status_code == 204
|
||||
|
||||
res = client.get(
|
||||
url_for("watchhistory", uuid=watch_uuid),
|
||||
headers={'x-api-key': api_key},
|
||||
)
|
||||
assert res.json == {}, "History should be empty after DELETE"
|
||||
|
||||
# The watch itself must survive, with its state reset
|
||||
res = client.get(
|
||||
url_for("watch", uuid=watch_uuid),
|
||||
headers={'x-api-key': api_key}
|
||||
)
|
||||
assert res.status_code == 200
|
||||
assert res.json.get('url') == test_url
|
||||
assert res.json.get('history_n') == 0
|
||||
assert res.json.get('last_checked') == 0
|
||||
assert res.json.get('previous_md5') == False
|
||||
|
||||
# And a snapshot fetch now has nothing to give
|
||||
res = client.get(
|
||||
url_for("watchsinglehistory", uuid=watch_uuid, timestamp='latest'),
|
||||
headers={'x-api-key': api_key},
|
||||
)
|
||||
assert res.status_code == 404
|
||||
|
||||
delete_all_watches(client)
|
||||
|
||||
|
||||
def test_roundtrip_API(client, live_server, measure_memory_usage, datastore_path):
|
||||
"""
|
||||
Test the full round trip, this way we test the default Model fits back into OpenAPI spec
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
Tests for the optional per-watch "Link to Open" (`link_to_open`).
|
||||
|
||||
A watch may point at something that is useless in a browser - an API endpoint or an RSS
|
||||
feed - while the human-readable page lives somewhere else. `link_to_open` stores that page,
|
||||
and every "go to the site" affordance (watch list, history/preview header, the
|
||||
{{watch_open_url}} notification token) uses it in preference to the watched URL.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from flask import url_for
|
||||
|
||||
from .util import set_original_response, set_modified_response, wait_for_all_checks, \
|
||||
wait_for_notification_endpoint_output
|
||||
|
||||
OPEN_URL = "https://example.com/the-real-human-page"
|
||||
|
||||
|
||||
def _add_watch(client, test_url):
|
||||
res = client.post(
|
||||
url_for("ui.ui_views.form_quick_watch_add"),
|
||||
data={"url": test_url, "tags": ''},
|
||||
follow_redirects=True
|
||||
)
|
||||
assert b"Watch added" in res.data
|
||||
wait_for_all_checks(client)
|
||||
|
||||
|
||||
def _edit_watch(client, test_url, **extra):
|
||||
data = {
|
||||
"url": test_url,
|
||||
"tags": "",
|
||||
"headers": "",
|
||||
"fetch_backend": "html_requests",
|
||||
"time_between_check_use_default": "y",
|
||||
}
|
||||
data.update(extra)
|
||||
return client.post(url_for("ui.ui_edit.edit_page", uuid="first"), data=data, follow_redirects=True)
|
||||
|
||||
|
||||
def test_link_to_open_is_saved_and_used_in_the_watch_list(client, live_server, measure_memory_usage, datastore_path):
|
||||
set_original_response(datastore_path=datastore_path)
|
||||
test_url = url_for('test_endpoint', _external=True)
|
||||
_add_watch(client, test_url)
|
||||
|
||||
# Without an override, the watch list links to the watched URL
|
||||
res = client.get(url_for("watchlist.index"))
|
||||
assert f'href="{test_url}"'.encode('utf-8') in res.data
|
||||
assert OPEN_URL.encode('utf-8') not in res.data
|
||||
|
||||
res = _edit_watch(client, test_url, link_to_open=OPEN_URL)
|
||||
assert b"Updated watch." in res.data
|
||||
|
||||
# It round-trips back into the edit form
|
||||
res = client.get(url_for("ui.ui_edit.edit_page", uuid="first"))
|
||||
assert OPEN_URL.encode('utf-8') in res.data
|
||||
|
||||
# ...and the watch list now points at it instead of the watched URL
|
||||
res = client.get(url_for("watchlist.index"))
|
||||
assert f'href="{OPEN_URL}"'.encode('utf-8') in res.data
|
||||
assert f'href="{test_url}"'.encode('utf-8') not in res.data
|
||||
|
||||
|
||||
def test_link_to_open_used_in_history_header(client, live_server, measure_memory_usage, datastore_path):
|
||||
set_original_response(datastore_path=datastore_path)
|
||||
test_url = url_for('test_endpoint', _external=True)
|
||||
_add_watch(client, test_url)
|
||||
|
||||
set_modified_response(datastore_path=datastore_path)
|
||||
client.post(url_for("ui.form_watch_checknow"), follow_redirects=True)
|
||||
wait_for_all_checks(client)
|
||||
|
||||
assert b"Updated watch." in _edit_watch(client, test_url, link_to_open=OPEN_URL).data
|
||||
|
||||
uuid = next(iter(client.application.config.get('DATASTORE').data['watching'].keys()))
|
||||
|
||||
# Top-of-page link on the history/diff page
|
||||
res = client.get(url_for("ui.ui_diff.diff_history_page", uuid=uuid))
|
||||
assert f'class="current-diff-url" href="{OPEN_URL}"'.encode('utf-8') in res.data
|
||||
|
||||
# ...and on the preview page
|
||||
res = client.get(url_for("ui.ui_preview.preview_page", uuid=uuid))
|
||||
assert f'class="current-diff-url" href="{OPEN_URL}"'.encode('utf-8') in res.data
|
||||
|
||||
|
||||
def test_invalid_link_to_open_is_rejected(client, live_server, measure_memory_usage, datastore_path):
|
||||
set_original_response(datastore_path=datastore_path)
|
||||
test_url = url_for('test_endpoint', _external=True)
|
||||
_add_watch(client, test_url)
|
||||
|
||||
res = _edit_watch(client, test_url, link_to_open="javascript:alert(1)")
|
||||
assert b"Updated watch." not in res.data
|
||||
assert b"Watch protocol is not permitted or invalid URL format" in res.data
|
||||
|
||||
# Blank is fine - it means "use the watched URL"
|
||||
res = _edit_watch(client, test_url, link_to_open="")
|
||||
assert b"Updated watch." in res.data
|
||||
watch = list(client.application.config.get('DATASTORE').data['watching'].values())[0]
|
||||
assert watch.open_link == test_url
|
||||
|
||||
|
||||
def test_watch_open_url_notification_token(client, live_server, measure_memory_usage, datastore_path):
|
||||
set_original_response(datastore_path=datastore_path)
|
||||
notification_file = os.path.join(datastore_path, "notification.txt")
|
||||
if os.path.isfile(notification_file):
|
||||
os.unlink(notification_file)
|
||||
|
||||
test_url = url_for('test_endpoint', _external=True)
|
||||
_add_watch(client, test_url)
|
||||
|
||||
notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'post://')
|
||||
|
||||
res = _edit_watch(
|
||||
client,
|
||||
test_url,
|
||||
link_to_open=OPEN_URL,
|
||||
notification_urls=notification_url,
|
||||
notification_title="Test",
|
||||
notification_body="watched={{watch_url}}\nopen={{watch_open_url}}",
|
||||
notification_format="text",
|
||||
)
|
||||
assert b"Updated watch." in res.data
|
||||
wait_for_all_checks(client)
|
||||
|
||||
set_modified_response(datastore_path=datastore_path)
|
||||
client.post(url_for("ui.form_watch_checknow"), follow_redirects=True)
|
||||
wait_for_all_checks(client)
|
||||
assert wait_for_notification_endpoint_output(datastore_path=datastore_path)
|
||||
|
||||
with open(notification_file, 'r') as f:
|
||||
body = f.read()
|
||||
|
||||
assert f"watched={test_url}" in body
|
||||
assert f"open={OPEN_URL}" in body
|
||||
os.unlink(notification_file)
|
||||
|
||||
|
||||
def test_watch_open_url_token_falls_back_to_watch_url(client, live_server, measure_memory_usage, datastore_path):
|
||||
"""With no 'Link to Open' set, {{watch_open_url}} must still render the watched URL."""
|
||||
set_original_response(datastore_path=datastore_path)
|
||||
notification_file = os.path.join(datastore_path, "notification.txt")
|
||||
if os.path.isfile(notification_file):
|
||||
os.unlink(notification_file)
|
||||
|
||||
test_url = url_for('test_endpoint', _external=True)
|
||||
_add_watch(client, test_url)
|
||||
|
||||
notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'post://')
|
||||
|
||||
res = _edit_watch(
|
||||
client,
|
||||
test_url,
|
||||
notification_urls=notification_url,
|
||||
notification_title="Test",
|
||||
notification_body="open={{watch_open_url}}",
|
||||
notification_format="text",
|
||||
)
|
||||
assert b"Updated watch." in res.data
|
||||
wait_for_all_checks(client)
|
||||
|
||||
set_modified_response(datastore_path=datastore_path)
|
||||
client.post(url_for("ui.form_watch_checknow"), follow_redirects=True)
|
||||
wait_for_all_checks(client)
|
||||
assert wait_for_notification_endpoint_output(datastore_path=datastore_path)
|
||||
|
||||
with open(notification_file, 'r') as f:
|
||||
body = f.read()
|
||||
|
||||
assert f"open={test_url}" in body
|
||||
os.unlink(notification_file)
|
||||
@@ -370,6 +370,23 @@ class TestHistoryPathTraversal(unittest.TestCase):
|
||||
history = watch.history
|
||||
self.assertEqual(history, {}, "Path traversal entry must be rejected")
|
||||
|
||||
def test_parent_dir_entry_is_rejected_by_the_containment_check(self):
|
||||
"""A bare '..' is the shortest entry that reaches and fails the containment check.
|
||||
|
||||
os.path.basename() reduces '/etc/passwd' and '../../etc/passwd' to
|
||||
'passwd', so those two stop at the os.path.exists() check below the
|
||||
guard rather than at the guard itself. '..' survives basename() intact
|
||||
and resolves to the parent of data_dir, which does exist, so the
|
||||
containment check is what rejects it. Not the only such input —
|
||||
'../..' and 'foo/..' collapse to the same thing — and not the only
|
||||
reason the check exists: it also blocks a filename inside data_dir
|
||||
that is itself a symlink pointing outside.
|
||||
"""
|
||||
watch = self._make_watch()
|
||||
self._write_history_txt(watch, ['1000000000,..\n'])
|
||||
history = watch.history
|
||||
self.assertEqual(history, {}, "Parent-directory entry must be rejected")
|
||||
|
||||
def test_normal_snapshot_entry_is_accepted(self):
|
||||
"""A bare filename written by save_history_blob must still load correctly."""
|
||||
import uuid as uuid_builder
|
||||
@@ -377,8 +394,11 @@ class TestHistoryPathTraversal(unittest.TestCase):
|
||||
watch.save_history_blob(contents="hello world", timestamp=1000000000, snapshot_id=str(uuid_builder.uuid4()))
|
||||
history = watch.history
|
||||
self.assertEqual(len(history), 1, "Normal snapshot entry must be accepted")
|
||||
# Watch.history resolves entries with os.path.realpath, so compare against a
|
||||
# resolved data_dir. On macOS the datastore lives under /tmp, which is a symlink
|
||||
# to /private/tmp, and an unresolved comparison fails there for a correct path.
|
||||
self.assertTrue(
|
||||
list(history.values())[0].startswith(watch.data_dir),
|
||||
list(history.values())[0].startswith(os.path.realpath(watch.data_dir)),
|
||||
"Resolved path must be inside the watch data directory"
|
||||
)
|
||||
|
||||
@@ -408,5 +428,47 @@ class TestHistoryPathTraversal(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestLinkToOpen(unittest.TestCase):
|
||||
"""`link_to_open` - the optional human-facing URL used instead of the watched URL."""
|
||||
|
||||
def _make_watch(self, **fields):
|
||||
mock_datastore = {'settings': {'application': {}}, 'watching': {}}
|
||||
return Watch.model(datastore_path='/tmp', __datastore=mock_datastore, default=fields)
|
||||
|
||||
def test_defaults_to_the_watched_url(self):
|
||||
watch = self._make_watch(url='https://example.com/feed.xml')
|
||||
assert watch.open_link_override == ''
|
||||
assert watch.open_link == 'https://example.com/feed.xml'
|
||||
assert watch.open_link == watch.link
|
||||
|
||||
def test_override_wins_when_set(self):
|
||||
watch = self._make_watch(url='https://example.com/api/v1/items.json',
|
||||
link_to_open='https://example.com/shop/items')
|
||||
assert watch.link == 'https://example.com/api/v1/items.json'
|
||||
assert watch.open_link == 'https://example.com/shop/items'
|
||||
assert watch.open_link_override == 'https://example.com/shop/items'
|
||||
|
||||
def test_whitespace_only_override_is_ignored(self):
|
||||
watch = self._make_watch(url='https://example.com/feed.xml', link_to_open=' ')
|
||||
assert watch.open_link_override == ''
|
||||
assert watch.open_link == 'https://example.com/feed.xml'
|
||||
|
||||
def test_unsafe_override_falls_back_to_the_watched_url(self):
|
||||
watch = self._make_watch(url='https://example.com/feed.xml',
|
||||
link_to_open='javascript:alert(1)')
|
||||
assert watch.open_link_override == ''
|
||||
assert watch.open_link == 'https://example.com/feed.xml'
|
||||
|
||||
def test_source_prefix_is_stripped_from_the_override(self):
|
||||
watch = self._make_watch(url='source:https://example.com/feed.xml',
|
||||
link_to_open='source:https://example.com/page')
|
||||
assert watch.open_link == 'https://example.com/page'
|
||||
|
||||
def test_jinja2_in_the_override_is_rendered(self):
|
||||
watch = self._make_watch(url='https://example.com/feed.xml',
|
||||
link_to_open='https://example.com/page?id={{ 1+1 }}')
|
||||
assert watch.open_link == 'https://example.com/page?id=2'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -198,6 +198,7 @@ Never fix one language and move on.
|
||||
| `en_US` | English (US) |
|
||||
| `es` | Spanish (Español) |
|
||||
| `fr` | French (Français) |
|
||||
| `id` | Indonesian (Bahasa Indonesia) |
|
||||
| `it` | Italian (Italiano) |
|
||||
| `ja` | Japanese (日本語) |
|
||||
| `ko` | Korean (한국어) |
|
||||
|
||||
@@ -376,6 +376,18 @@ msgstr "Znovu zkontrolovat po (minuty)"
|
||||
msgid "Import"
|
||||
msgstr "Importovat"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1947,15 +1959,17 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Interval/doba mezi jednotlivými kontrolami."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Interval/doba mezi jednotlivými kontrolami."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
msgstr ""
|
||||
@@ -2054,6 +2068,12 @@ msgstr "musíte"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "Nastavte metodu načítání"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
@@ -3188,6 +3208,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3461,14 +3485,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "Kontrola zabezpečení přístupového tokenu API povolena"
|
||||
@@ -4020,6 +4036,12 @@ msgstr ""
|
||||
msgid "The URL being watched."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "UUID monitoru."
|
||||
|
||||
@@ -382,6 +382,18 @@ msgstr "Nachprüfzeit (Minuten)"
|
||||
msgid "Import"
|
||||
msgstr "IMPORT"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1974,16 +1986,16 @@ msgstr ""
|
||||
"Beschreibung verwenden."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Das Intervall/die Zeitdauer zwischen den einzelnen Überprüfungen."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
"Sendet eine Benachrichtigung, wenn der Filter auf der Seite nicht mehr sichtbar ist. So wissen Sie, wann sich die "
|
||||
"Seite geändert hat und Ihr Filter nicht mehr funktioniert."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Das Intervall/die Zeitdauer zwischen den einzelnen Überprüfungen."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2087,6 +2099,14 @@ msgstr "Das musst du"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "Legen Sie die Fetch-Methode fest"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
"Sendet eine Benachrichtigung, wenn der Filter auf der Seite nicht mehr sichtbar ist. So wissen Sie, wann sich die "
|
||||
"Seite geändert hat und Ihr Filter nicht mehr funktioniert."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
@@ -3237,6 +3257,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3511,14 +3535,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "Sicherheitsüberprüfung des API-Zugriffstokens aktiviert"
|
||||
@@ -4072,6 +4088,12 @@ msgstr ""
|
||||
msgid "The URL being watched."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "Die UUID der Überwachung."
|
||||
|
||||
@@ -374,6 +374,18 @@ msgstr ""
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1941,13 +1953,15 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
@@ -2048,6 +2062,12 @@ msgstr ""
|
||||
msgid "Set the fetch method"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
@@ -3180,6 +3200,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3453,14 +3477,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr ""
|
||||
@@ -4012,6 +4028,12 @@ msgstr ""
|
||||
msgid "The URL being watched."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr ""
|
||||
|
||||
@@ -374,6 +374,18 @@ msgstr ""
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1941,13 +1953,15 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
@@ -2048,6 +2062,12 @@ msgstr ""
|
||||
msgid "Set the fetch method"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
@@ -3180,6 +3200,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3453,14 +3477,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr ""
|
||||
@@ -4012,6 +4028,12 @@ msgstr ""
|
||||
msgid "The URL being watched."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr ""
|
||||
|
||||
@@ -380,6 +380,18 @@ msgstr "Tiempo de revisión (minutos)"
|
||||
msgid "Import"
|
||||
msgstr "Importar"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1992,16 +2004,16 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr "Utiliza automáticamente el título de la página si la encuentra, también puede usar su propio título/descripción aquí"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "El intervalo/cantidad de tiempo entre cada verificación."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
"Envía una notificación cuando el filtro ya no se puede ver en la página, lo cual es bueno para saber cuándo cambió la"
|
||||
" página y su filtro ya no funcionará."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "El intervalo/cantidad de tiempo entre cada verificación."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2103,6 +2115,14 @@ msgstr "Necesitas"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "Establecer el método de recuperación"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
"Envía una notificación cuando el filtro ya no se puede ver en la página, lo cual es bueno para saber cuándo cambió la"
|
||||
" página y su filtro ya no funcionará."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "Utilice el botón de verificación ( ✓ ) para probar si una condición se cumple con la instantánea actual."
|
||||
@@ -3253,6 +3273,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3526,14 +3550,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "Comprobación de seguridad del token de acceso API habilitada"
|
||||
@@ -4085,6 +4101,12 @@ msgstr "La URL de la instancia de changetection.io que está ejecutando."
|
||||
msgid "The URL being watched."
|
||||
msgstr "La URL que se está viendo."
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "El UUID del monitor."
|
||||
|
||||
@@ -378,6 +378,18 @@ msgstr "Temps de revérification (minutes)"
|
||||
msgid "Import"
|
||||
msgstr "IMPORTER"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1952,15 +1964,17 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "L'intervalle/la durée entre chaque vérification."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "L'intervalle/la durée entre chaque vérification."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
msgstr ""
|
||||
@@ -2059,6 +2073,12 @@ msgstr "Vous devez"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "Définir la méthode de récupération"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
@@ -3193,6 +3213,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3466,14 +3490,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "Contrôle de sécurité du jeton d'accès à l'API activé"
|
||||
@@ -4025,6 +4041,12 @@ msgstr ""
|
||||
msgid "The URL being watched."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "L'UUID du moniteur."
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -376,6 +376,18 @@ msgstr "Tempo di ricontrollo (minuti)"
|
||||
msgid "Import"
|
||||
msgstr "Importa"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1943,13 +1955,15 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
@@ -2050,6 +2064,12 @@ msgstr ""
|
||||
msgid "Set the fetch method"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
@@ -3182,6 +3202,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3455,14 +3479,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "Controllo sicurezza token API attivo"
|
||||
@@ -4014,6 +4030,12 @@ msgstr ""
|
||||
msgid "The URL being watched."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "L'UUID del monitor."
|
||||
|
||||
@@ -378,6 +378,18 @@ msgstr "再チェック時間(分)"
|
||||
msgid "Import"
|
||||
msgstr "インポート"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1952,14 +1964,16 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr "ページタイトルが見つかった場合は自動的に使用されます。ここで独自のタイトル/説明を使用することもできます。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "各チェック間の間隔/時間。"
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr "フィルタがページで見つからなくなったときに通知を送信します。ページが変更されてフィルタが機能しなくなったことを把握するのに便利です。"
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "各チェック間の間隔/時間。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2059,6 +2073,12 @@ msgstr "次の操作が必要です:"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "取得方式を設定する"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr "フィルタがページで見つからなくなったときに通知を送信します。ページが変更されてフィルタが機能しなくなったことを把握するのに便利です。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "確認ボタン(✓)を使用して、現在のスナップショットに対して条件が満たされるかテストできます。"
|
||||
@@ -3199,6 +3219,10 @@ msgstr "値"
|
||||
msgid "Web Page URL"
|
||||
msgstr "ウェブページ URL"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr "グループタグ"
|
||||
@@ -3472,14 +3496,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "APIアクセストークンのセキュリティチェックを有効化"
|
||||
@@ -4031,6 +4047,12 @@ msgstr "実行中の changedetection.io インスタンスのURL。"
|
||||
msgid "The URL being watched."
|
||||
msgstr "監視中のURL。"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "ウォッチのUUID。"
|
||||
|
||||
@@ -376,6 +376,18 @@ msgstr "재확인 시간(분)"
|
||||
msgid "Import"
|
||||
msgstr "가져오기"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1951,14 +1963,16 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr "페이지 제목을 찾으면 자동으로 사용하며, 여기에서 직접 제목/설명을 입력할 수도 있습니다."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "각 확인 사이의 간격/시간입니다."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr "필터가 페이지에 더 이상 보이지 않을 때 알림을 보냅니다. 페이지가 바뀌어 필터가 더 이상 동작하지 않는 상황을 파악하는 데 유용합니다."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "각 확인 사이의 간격/시간입니다."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2058,6 +2072,12 @@ msgstr "먼저"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "가져오기 방식을"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr "필터가 페이지에 더 이상 보이지 않을 때 알림을 보냅니다. 페이지가 바뀌어 필터가 더 이상 동작하지 않는 상황을 파악하는 데 유용합니다."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "체크(✓) 버튼을 눌러 현재 스냅샷에서 조건이 통과하는지 테스트해 보세요."
|
||||
@@ -3190,6 +3210,10 @@ msgstr "값"
|
||||
msgid "Web Page URL"
|
||||
msgstr "웹페이지 URL"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr "그룹 태그"
|
||||
@@ -3463,14 +3487,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "API 액세스 토큰 보안 확인 활성화"
|
||||
@@ -4022,6 +4038,12 @@ msgstr "현재 사용 중인 changedetection.io 인스턴스의 URL입니다."
|
||||
msgid "The URL being watched."
|
||||
msgstr "모니터링 중인 URL입니다."
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "모니터링 UUID입니다."
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: changedetection.io 0.60.3\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2026-09-04 14:11+0200\n"
|
||||
"POT-Creation-Date: 2026-09-09 16:37+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -373,6 +373,18 @@ msgstr ""
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1940,13 +1952,15 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
@@ -2047,6 +2061,12 @@ msgstr ""
|
||||
msgid "Set the fetch method"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr ""
|
||||
@@ -3179,6 +3199,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3452,14 +3476,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr ""
|
||||
@@ -4011,6 +4027,12 @@ msgstr ""
|
||||
msgid "The URL being watched."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr ""
|
||||
|
||||
Binary file not shown.
@@ -384,6 +384,18 @@ msgstr "Czas ponownej kontroli (w minutach)"
|
||||
msgid "Import"
|
||||
msgstr "Importuj"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr "Zawsze rozwinięte"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -2077,16 +2089,16 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr "Jeśli tytuł strony zostanie znaleziony, zostanie on automatycznie użyty; można tu również wpisać własny tytuł/opis"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Odstęp czasowy między kolejnymi sprawdzeniami."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
"Wysyła powiadomienie, gdy filtr przestaje być widoczny na stronie; przydaje się to do stwierdzenia, kiedy strona "
|
||||
"uległa zmianie i filtr przestanie działać."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Odstęp czasowy między kolejnymi sprawdzeniami."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2190,6 +2202,14 @@ msgstr "Musisz"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "Ustaw metodę pobierania"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
"Wysyła powiadomienie, gdy filtr przestaje być widoczny na stronie; przydaje się to do stwierdzenia, kiedy strona "
|
||||
"uległa zmianie i filtr przestanie działać."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "Użyj przycisku „Sprawdź” (✓), aby sprawdzić, czy warunek jest spełniony w odniesieniu do bieżącej migawki."
|
||||
@@ -3339,6 +3359,10 @@ msgstr "Wartość"
|
||||
msgid "Web Page URL"
|
||||
msgstr "Adres URL strony internetowej"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr "Etykieta grupy"
|
||||
@@ -3612,14 +3636,6 @@ msgstr "Krótki (1 minuta temu)"
|
||||
msgid "Navigation sidebar"
|
||||
msgstr "Pasek nawigacyjny"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr "Zwiń pasek ikon (rozwija się po najechaniu kursorem)"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr "Zawsze rozwinięte"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "Włączono kontrolę bezpieczeństwa tokenu dostępu do API"
|
||||
@@ -4173,6 +4189,12 @@ msgstr "Adres URL instancji serwisu changedetection.io, którą uruchomiłeś."
|
||||
msgid "The URL being watched."
|
||||
msgstr "Obserwowany adres URL."
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "Identyfikator UUID obserwacji."
|
||||
|
||||
@@ -377,6 +377,18 @@ msgstr "Tempo de rechecagem (minutos)"
|
||||
msgid "Import"
|
||||
msgstr "Importar"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1977,16 +1989,16 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr "Usa automaticamente o título da página se encontrado. Você também pode usar seu próprio título/descrição aqui"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "O intervalo/quantidade de tempo entre cada verificação."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
"Envia uma notificação quando o filtro não pode mais ser visto na página, útil para saber quando a página mudou e seu "
|
||||
"filtro não funcionará mais."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "O intervalo/quantidade de tempo entre cada verificação."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2088,6 +2100,14 @@ msgstr "Você precisa"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "Definir o método de busca"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
"Envia uma notificação quando o filtro não pode mais ser visto na página, útil para saber quando a página mudou e seu "
|
||||
"filtro não funcionará mais."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "Use o botão de verificar (✓) para testar se uma condição passa contra o instantâneo atual."
|
||||
@@ -3230,6 +3250,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3503,14 +3527,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "Verificação de segurança do token de acesso à API ativada"
|
||||
@@ -4062,6 +4078,12 @@ msgstr "A URL da instância do changedetection.io que você está rodando."
|
||||
msgid "The URL being watched."
|
||||
msgstr "A URL que está sendo monitorada."
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "O UUID do monitoramento."
|
||||
|
||||
@@ -381,6 +381,18 @@ msgstr "Время перепроверки (минуты)"
|
||||
msgid "Import"
|
||||
msgstr "Импорт"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -2039,16 +2051,16 @@ msgstr ""
|
||||
"заголовок/описание."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Интервал/количество времени между каждой проверкой."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
"Отправляет уведомление, когда фильтр больше не виден на странице. Это полезно для того, чтобы узнать, когда страница "
|
||||
"изменилась и ваш фильтр больше не будет работать."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Интервал/количество времени между каждой проверкой."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2150,6 +2162,14 @@ msgstr "Вам нужно"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "Установите метод выборки"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
"Отправляет уведомление, когда фильтр больше не виден на странице. Это полезно для того, чтобы узнать, когда страница "
|
||||
"изменилась и ваш фильтр больше не будет работать."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "Используйте кнопку проверки (✓), чтобы проверить, соответствует ли условие текущему снимку."
|
||||
@@ -3298,6 +3318,10 @@ msgstr "Ценить"
|
||||
msgid "Web Page URL"
|
||||
msgstr "URL-адрес веб-страницы"
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr "Групповой тег"
|
||||
@@ -3571,14 +3595,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "Проверка безопасности токена доступа к API включена"
|
||||
@@ -4130,6 +4146,12 @@ msgstr "URL-адрес запущенного вами экземпляраchang
|
||||
msgid "The URL being watched."
|
||||
msgstr "URL-адрес, который просматривается."
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "UUID часов."
|
||||
|
||||
@@ -383,6 +383,18 @@ msgstr "Yeniden kontrol süresi (dakika)"
|
||||
msgid "Import"
|
||||
msgstr "İçe Aktar"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1980,16 +1992,16 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr "Bulunursa otomatik olarak sayfa başlığını kullanır, ayrıca kendi başlığınızı/açıklamanızı da burada kullanabilirsiniz"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Her kontrol arasındaki zaman aralığı/miktarı."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
"Filtre artık sayfada görülemediğinde bir bildirim gönderir, sayfanın ne zaman değiştiğini ve filtrenizin artık "
|
||||
"çalışmayacağını bilmek için iyidir."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Her kontrol arasındaki zaman aralığı/miktarı."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2091,6 +2103,14 @@ msgstr "Yapmanız gerekenler"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "Getirme yöntemini ayarla"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
"Filtre artık sayfada görülemediğinde bir bildirim gönderir, sayfanın ne zaman değiştiğini ve filtrenizin artık "
|
||||
"çalışmayacağını bilmek için iyidir."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "Bir koşulun mevcut anlık görüntüye göre geçip geçmediğini test etmek için doğrula (✓) düğmesini kullanın."
|
||||
@@ -3233,6 +3253,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3506,14 +3530,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "API erişim belirteci güvenlik kontrolü etkin"
|
||||
@@ -4065,6 +4081,12 @@ msgstr "Çalıştırdığınız changedetection.io örneğinin URL'si."
|
||||
msgid "The URL being watched."
|
||||
msgstr "İzlenen URL."
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "İzleyicinin UUID'si."
|
||||
|
||||
@@ -375,6 +375,18 @@ msgstr "Час перевірки (хвилини)"
|
||||
msgid "Import"
|
||||
msgstr "Імпорт"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1961,16 +1973,16 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr "Автоматично використовує заголовок сторінки, якщо знайдено. Ви також можете вказати тут свою назву/опис."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Інтервал часу між перевірками."
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
"Надсилає сповіщення, коли фільтр більше не видно на сторінці. Корисно, щоб дізнатися, що сторінка змінилася і ваш "
|
||||
"фільтр більше не працює."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "Інтервал часу між перевірками."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2072,6 +2084,14 @@ msgstr "Вам потрібно"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "Встановити метод завантаження"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr ""
|
||||
"Надсилає сповіщення, коли фільтр більше не видно на сторінці. Корисно, щоб дізнатися, що сторінка змінилася і ваш "
|
||||
"фільтр більше не працює."
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "Використовуйте кнопку перевірки (✓), щоб протестувати умову на поточному знімку."
|
||||
@@ -3212,6 +3232,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3485,14 +3509,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "Перевірку безпеки токена доступу API увімкнено"
|
||||
@@ -4044,6 +4060,12 @@ msgstr "URL екземпляра changedetection.io, який ви викори
|
||||
msgid "The URL being watched."
|
||||
msgstr "URL, за яким ведеться спостереження."
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "UUID завдання."
|
||||
|
||||
@@ -379,6 +379,18 @@ msgstr "复检间隔(分钟)"
|
||||
msgid "Import"
|
||||
msgstr "导入"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1946,14 +1958,16 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr "若检测到页面标题将自动使用,你也可以在此自定义标题/描述"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "每次检查之间的时间间隔。"
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr "当页面上找不到该过滤器时发送通知,便于知晓页面已变化且过滤器不再适用。"
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "每次检查之间的时间间隔。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2053,6 +2067,12 @@ msgstr "你需要"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "设置抓取方式"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr "当页面上找不到该过滤器时发送通知,便于知晓页面已变化且过滤器不再适用。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "使用验证(✓)按钮测试条件是否符合当前快照。"
|
||||
@@ -3186,6 +3206,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3459,14 +3483,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "已启用 API 访问令牌安全检查"
|
||||
@@ -4018,6 +4034,12 @@ msgstr "你正在运行的 changedetection.io 实例的 URL。"
|
||||
msgid "The URL being watched."
|
||||
msgstr "被监控的 URL。"
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "监视器的UUID。"
|
||||
|
||||
@@ -378,6 +378,18 @@ msgstr "複查時間(分鐘)"
|
||||
msgid "Import"
|
||||
msgstr "匯入"
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Expand on hover"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/menu_modes.py
|
||||
msgid "Stays minimal"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/rss/single_watch.py
|
||||
#, python-format
|
||||
msgid "Watch with UUID %(uuid)s not found"
|
||||
@@ -1945,14 +1957,16 @@ msgid "Automatically uses the page title if found, you can also use your own tit
|
||||
msgstr "如果找到頁面標題將自動使用,您也可以在此使用您自己的標題 / 描述"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "每次檢查之間的間隔 / 時間量。"
|
||||
msgid "Extra Page Title and Link options"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr "當頁面上找不到過濾器時發送通知,這有助於了解頁面何時變更導致您的過濾器失效。"
|
||||
msgid "Optional - links in the list, history and notifications open this URL instead. Does not change the URL being checked."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "The interval/amount of time between each check."
|
||||
msgstr "每次檢查之間的間隔 / 時間量。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Set to empty to use system settings default"
|
||||
@@ -2052,6 +2066,12 @@ msgstr "您需要"
|
||||
msgid "Set the fetch method"
|
||||
msgstr "設定抓取方式"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid ""
|
||||
"Sends a notification when the filter can no longer be seen on the page, good for knowing when the page changed and "
|
||||
"your filter will not work anymore."
|
||||
msgstr "當頁面上找不到過濾器時發送通知,這有助於了解頁面何時變更導致您的過濾器失效。"
|
||||
|
||||
#: changedetectionio/blueprint/ui/templates/edit.html
|
||||
msgid "Use the verify (✓) button to test if a condition passes against the current snapshot."
|
||||
msgstr "使用驗證 (✓) 按鈕測試條件是否符合目前的快照。"
|
||||
@@ -3186,6 +3206,10 @@ msgstr ""
|
||||
msgid "Web Page URL"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Open Link Override"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Group Tag"
|
||||
msgstr ""
|
||||
@@ -3459,14 +3483,6 @@ msgstr ""
|
||||
msgid "Navigation sidebar"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Collapsed icon rail (expands on hover)"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "Always expanded"
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/forms.py
|
||||
msgid "API access token security check enabled"
|
||||
msgstr "已啟用 API 存取權杖安全檢查"
|
||||
@@ -4018,6 +4034,12 @@ msgstr ""
|
||||
msgid "The URL being watched."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid ""
|
||||
"The Open Link Override set on the watch, otherwise the same as watch_url. Use this when the watched URL is an API "
|
||||
"endpoint or RSS feed but you want to link to the real page."
|
||||
msgstr ""
|
||||
|
||||
#: changedetectionio/templates/_common_fields.html
|
||||
msgid "The UUID of the watch."
|
||||
msgstr "監測任務的 UUID。"
|
||||
|
||||
+48
-1
@@ -28,7 +28,7 @@ info:
|
||||
|
||||
For example: `x-api-key: YOUR_API_KEY`
|
||||
|
||||
version: 0.1.8
|
||||
version: 0.1.9
|
||||
contact:
|
||||
name: ChangeDetection.io
|
||||
url: https://github.com/dgtlmoon/changedetection.io
|
||||
@@ -293,6 +293,13 @@ components:
|
||||
format: uri
|
||||
description: URL to monitor for changes
|
||||
maxLength: 5000
|
||||
link_to_open:
|
||||
type: string
|
||||
description: |
|
||||
Optional human-facing URL to open instead of `url`. Use when `url` points at something
|
||||
that is not meant to be read in a browser (an API endpoint or RSS feed, for example).
|
||||
An empty string means "use `url`". Exposed rendered as `open_link`.
|
||||
maxLength: 5000
|
||||
title:
|
||||
type: [string, 'null']
|
||||
description: Custom title for the web page change monitor (watch), not to be confused with page_title
|
||||
@@ -719,6 +726,12 @@ components:
|
||||
description: The watch URL rendered in case of any Jinja2 markup, always use this for listing.
|
||||
readOnly: true
|
||||
x-computed: true
|
||||
open_link:
|
||||
type: string
|
||||
format: string
|
||||
description: The URL a human should be sent to - `link_to_open` when set, otherwise the same as `link`.
|
||||
readOnly: true
|
||||
x-computed: true
|
||||
page_title:
|
||||
type: [string, 'null']
|
||||
description: HTML <title> tag extracted from the page
|
||||
@@ -1271,6 +1284,40 @@ paths:
|
||||
'404':
|
||||
description: Web page change monitor (watch) not found
|
||||
|
||||
delete:
|
||||
operationId: deleteWatchHistory
|
||||
tags: [Watch History]
|
||||
summary: Delete watch history
|
||||
description: |
|
||||
Delete/clear all historical snapshots for a web page change monitor (watch), the watch itself is kept.
|
||||
This also resets the "last checked"/"last changed" state, so the watch will be re-checked on the next cycle.
|
||||
x-code-samples:
|
||||
- lang: 'curl'
|
||||
source: |
|
||||
curl -X DELETE "http://localhost:5000/api/v1/watch/095be615-a8ad-4c33-8e9c-c7612fbf6c9f/history" \
|
||||
-H "x-api-key: YOUR_API_KEY"
|
||||
- lang: 'Python'
|
||||
source: |
|
||||
import requests
|
||||
|
||||
headers = {'x-api-key': 'YOUR_API_KEY'}
|
||||
uuid = '095be615-a8ad-4c33-8e9c-c7612fbf6c9f'
|
||||
response = requests.delete(f'http://localhost:5000/api/v1/watch/{uuid}/history', headers=headers)
|
||||
print(response.status_code)
|
||||
parameters:
|
||||
- name: uuid
|
||||
in: path
|
||||
required: true
|
||||
description: Web page change monitor (watch) unique ID
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
'204':
|
||||
description: History deleted successfully
|
||||
'404':
|
||||
description: Web page change monitor (watch) not found
|
||||
|
||||
/watch/{uuid}/history/{timestamp}:
|
||||
get:
|
||||
operationId: getWatchSnapshot
|
||||
|
||||
+24
-6
File diff suppressed because one or more lines are too long
+1
-1
@@ -40,7 +40,7 @@ orjson~=3.11
|
||||
# jq not available on Windows so must be installed manually
|
||||
|
||||
# Notification library
|
||||
apprise==1.9.9
|
||||
apprise==1.13.1
|
||||
|
||||
diff_match_patch
|
||||
|
||||
|
||||
Reference in New Issue
Block a user