Spaces:
Paused
Paused
| {% extends 'base.html' %} | |
| {% block content %} | |
| {% from '_helpers.html' import render_field, render_checkbox_field, render_button, render_time_schedule_form, playwright_warning, only_playwright_type_watches_warning, render_conditions_fieldlist_of_formfields_as_table %} | |
| {% from '_common_fields.html' import render_common_settings_form %} | |
| <script src="{{url_for('static_content', group='js', filename='tabs.js')}}" defer></script> | |
| <script src="{{url_for('static_content', group='js', filename='vis.js')}}" defer></script> | |
| <script src="{{url_for('static_content', group='js', filename='global-settings.js')}}" defer></script> | |
| <script src="{{url_for('static_content', group='js', filename='scheduler.js')}}" defer></script> | |
| <script src="{{url_for('static_content', group='js', filename='conditions.js')}}" defer></script> | |
| <script> | |
| </script> | |
| <script src="{{url_for('static_content', group='js', filename='plugins.js')}}" defer></script> | |
| <script src="{{url_for('static_content', group='js', filename='watch-settings.js')}}" defer></script> | |
| <script src="{{url_for('static_content', group='js', filename='notifications.js')}}" defer></script> | |
| <script src="{{url_for('static_content', group='js', filename='visual-selector.js')}}" defer></script> | |
| {% if playwright_enabled %} | |
| <script src="{{url_for('static_content', group='js', filename='browser-steps.js')}}" defer></script> | |
| {% endif %} | |
| {% set has_tag_filters_extra="WARNING: Watch has tag/groups set with special filters\n" if has_special_tag_options else '' %} | |
| <script src="{{url_for('static_content', group='js', filename='recheck-proxy.js')}}" defer></script> | |
| <div class="edit-form monospaced-textarea"> | |
| <div class="tabs collapsable"> | |
| <ul> | |
| <li class="tab"><a href="#general">General</a></li> | |
| <li class="tab"><a href="#request">Request</a></li> | |
| {% if extra_tab_content %} | |
| <li class="tab"><a href="#extras_tab">{{ extra_tab_content }}</a></li> | |
| {% endif %} | |
| <li class="tab"><a id="browsersteps-tab" href="#browser-steps">Browser Steps</a></li> | |
| <!-- should goto extra forms? --> | |
| {% if watch['processor'] == 'text_json_diff' %} | |
| <li class="tab"><a id="visualselector-tab" href="#visualselector">Visual Filter Selector</a></li> | |
| <li class="tab" id="filters-and-triggers-tab"><a href="#filters-and-triggers">Filters & Triggers</a></li> | |
| <li class="tab" id="conditions-tab"><a href="#conditions">Conditions</a></li> | |
| {% endif %} | |
| <li class="tab"><a href="#notifications">Notifications</a></li> | |
| <li class="tab"><a href="#stats">Stats</a></li> | |
| </ul> | |
| </div> | |
| <div class="box-wrap inner"> | |
| <form class="pure-form pure-form-stacked" | |
| action="{{ url_for('ui.ui_edit.edit_page', uuid=uuid, next = request.args.get('next'), unpause_on_save = request.args.get('unpause_on_save'), tag = request.args.get('tag')) }}" method="POST"> | |
| <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> | |
| <div class="tab-pane-inner" id="general"> | |
| <fieldset> | |
| <div class="pure-control-group"> | |
| {{ render_field(form.url, placeholder="https://...", required=true, class="m-d") }} | |
| <div class="pure-form-message">Some sites use JavaScript to create the content, for this you should <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Fetching-pages-with-WebDriver">use the Chrome/WebDriver Fetcher</a></div> | |
| <div class="pure-form-message">Variables are supported in the URL (<a href="https://github.com/dgtlmoon/changedetection.io/wiki/Handling-variables-in-the-watched-URL">help and examples here</a>).</div> | |
| </div> | |
| <div class="pure-control-group inline-radio"> | |
| {{ render_field(form.processor) }} | |
| </div> | |
| <div class="pure-control-group"> | |
| {{ render_field(form.title, class="m-d") }} | |
| </div> | |
| <div class="pure-control-group"> | |
| {{ render_field(form.tags) }} | |
| <span class="pure-form-message-inline">Organisational tag/group name used in the main listing page</span> | |
| </div> | |
| <div class="pure-control-group time-between-check border-fieldset"> | |
| {{ render_checkbox_field(form.time_between_check_use_default, class="use-default-timecheck") }} | |
| <br> | |
| <div id="time-check-widget-wrapper"> | |
| {{ render_field(form.time_between_check, class="time-check-widget") }} | |
| <span class="pure-form-message-inline"> | |
| The interval/amount of time between each check. | |
| </span> | |
| </div> | |
| <div id="time-between-check-schedule"> | |
| <!-- Start Time and End Time --> | |
| <div id="limit-between-time"> | |
| {{ render_time_schedule_form(form, available_timezones, timezone_default_config) }} | |
| </div> | |
| </div> | |
| <br> | |
| </div> | |
| <div class="pure-control-group"> | |
| {{ render_checkbox_field(form.extract_title_as_title) }} | |
| </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> | |
| </fieldset> | |
| </div> | |
| <div class="tab-pane-inner" id="request"> | |
| <div class="pure-control-group inline-radio"> | |
| {{ render_field(form.fetch_backend, class="fetch-backend") }} | |
| <span class="pure-form-message-inline"> | |
| <p>Use the <strong>Basic</strong> method (default) where your watched site doesn't need Javascript to render.</p> | |
| <p>The <strong>Chrome/Javascript</strong> method requires a network connection to a running WebDriver+Chrome server, set by the ENV var 'WEBDRIVER_URL'. </p> | |
| Tip: <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Proxy-configuration#brightdata-proxy-support">Connect using Bright Data and Oxylabs Proxies, find out more here.</a> | |
| </span> | |
| </div> | |
| {% if form.proxy %} | |
| <div class="pure-control-group inline-radio"> | |
| <div>{{ form.proxy.label }} <a href="" id="check-all-proxies" class="pure-button button-secondary button-xsmall" >Check/Scan all</a></div> | |
| <div>{{ form.proxy(class="fetch-backend-proxy") }}</div> | |
| <span class="pure-form-message-inline"> | |
| Choose a proxy for this watch | |
| </span> | |
| </div> | |
| {% endif %} | |
| <!-- webdriver always --> | |
| <fieldset data-visible-for="fetch_backend=html_webdriver" style="display: none;"> | |
| <div class="pure-control-group"> | |
| {{ render_field(form.webdriver_delay) }} | |
| <div class="pure-form-message-inline"> | |
| <strong>If you're having trouble waiting for the page to be fully rendered (text missing etc), try increasing the 'wait' time here.</strong> | |
| <br> | |
| This will wait <i>n</i> seconds before extracting the text. | |
| {% if using_global_webdriver_wait %} | |
| <br><strong>Using the current global default settings</strong> | |
| {% endif %} | |
| </div> | |
| </div> | |
| <div class="pure-control-group"> | |
| <a class="pure-button button-secondary button-xsmall show-advanced">Show advanced options</a> | |
| </div> | |
| <div class="advanced-options" style="display: none;"> | |
| {{ render_field(form.webdriver_js_execute_code) }} | |
| <div class="pure-form-message-inline"> | |
| Run this code before performing change detection, handy for filling in fields and other | |
| actions <a | |
| href="https://github.com/dgtlmoon/changedetection.io/wiki/Run-JavaScript-before-change-detection">More | |
| help and examples here</a> | |
| </div> | |
| </div> | |
| </fieldset> | |
| <!-- html requests always --> | |
| <fieldset data-visible-for="fetch_backend=html_requests"> | |
| <div class="pure-control-group"> | |
| <a class="pure-button button-secondary button-xsmall show-advanced">Show advanced options</a> | |
| </div> | |
| <div class="advanced-options" style="display: none;"> | |
| <div class="pure-control-group" id="request-method"> | |
| {{ render_field(form.method) }} | |
| </div> | |
| <div id="request-body"> | |
| {{ render_field(form.body, rows=7, placeholder="Example | |
| { | |
| \"name\":\"John\", | |
| \"age\":30, | |
| \"car\":null, | |
| \"year\":{% now 'Europe/Berlin', '%Y' %} | |
| }") }} | |
| </div> | |
| <div class="pure-form-message">Variables are supported in the request body (<a href="https://github.com/dgtlmoon/changedetection.io/wiki/Handling-variables-in-the-watched-URL">help and examples here</a>).</div> | |
| </div> | |
| </fieldset> | |
| <!-- hmm --> | |
| <div class="pure-control-group advanced-options" style="display: none;"> | |
| {{ render_field(form.headers, rows=7, placeholder="Example | |
| Cookie: foobar | |
| User-Agent: wonderbra 1.0 | |
| Math: {{ 1 + 1 }}") }} | |
| <div class="pure-form-message">Variables are supported in the request header values (<a href="https://github.com/dgtlmoon/changedetection.io/wiki/Handling-variables-in-the-watched-URL">help and examples here</a>).</div> | |
| <div class="pure-form-message-inline"> | |
| {% if has_extra_headers_file %} | |
| <strong>Alert! Extra headers file found and will be added to this watch!</strong> | |
| {% else %} | |
| Headers can be also read from a file in your data-directory <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Adding-headers-from-an-external-file">Read more here</a> | |
| {% endif %} | |
| <br> | |
| (Not supported by Selenium browser) | |
| </div> | |
| </div> | |
| <fieldset data-visible-for="fetch_backend=html_requests fetch_backend=html_webdriver" > | |
| <div class="pure-control-group inline-radio advanced-options" style="display: none;"> | |
| {{ render_checkbox_field(form.ignore_status_codes) }} | |
| </div> | |
| </fieldset> | |
| </div> | |
| <div class="tab-pane-inner" id="browser-steps"> | |
| {% if watch_needs_selenium_or_playwright %} | |
| {# Only works with playwright #} | |
| {% if system_has_playwright_configured %} | |
| <img class="beta-logo" src="{{url_for('static_content', group='images', filename='beta-logo.png')}}" alt="New beta functionality"> | |
| <fieldset> | |
| <div class="pure-control-group"> | |
| <!-- | |
| Too hard right now, better to just send the events to the fetcher for now and leave it in the final screenshot | |
| and/or report an error | |
| <a id="play-steps" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Play steps ▶</a> | |
| --> | |
| <!--- Do this later --> | |
| <div class="checkbox" style="display: none;"> | |
| <input type=checkbox id="include_text_elements" > <label for="include_text_elements">Turn on text finder</label> | |
| </div> | |
| <div id="loading-status-text" style="display: none;">Please wait, first browser step can take a little time to load..<div class="spinner"></div></div> | |
| <div class="flex-wrapper" > | |
| <div id="browser-steps-ui" class="noselect"> | |
| <div class="noselect" id="browsersteps-selector-wrapper" style="width: 100%"> | |
| <span class="loader" > | |
| <span id="browsersteps-click-start"> | |
| <h2 >Click here to Start</h2> | |
| <svg style="height: 3.5rem;" version="1.1" viewBox="0 0 32 32" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="start"/><g id="play_x5F_alt"><path d="M16,0C7.164,0,0,7.164,0,16s7.164,16,16,16s16-7.164,16-16S24.836,0,16,0z M10,24V8l16.008,8L10,24z" style="fill: var(--color-grey-400);"/></g></svg><br> | |
| Please allow 10-15 seconds for the browser to connect.<br> | |
| </span> | |
| <div class="spinner" style="display: none;"></div> | |
| </span> | |
| <img class="noselect" id="browsersteps-img" src="" style="max-width: 100%; width: 100%;" > | |
| <canvas class="noselect" id="browsersteps-selector-canvas" style="max-width: 100%; width: 100%;"></canvas> | |
| </div> | |
| </div> | |
| <div id="browser-steps-fieldlist" > | |
| <span id="browser-seconds-remaining">Press "Play" to start.</span> <span style="font-size: 80%;"> (<a target="newwindow" href="https://github.com/dgtlmoon/changedetection.io/pull/478/files#diff-1a79d924d1840c485238e66772391268a89c95b781d69091384cf1ea1ac146c9R4">?</a>) </span> | |
| {{ render_field(form.browser_steps) }} | |
| </div> | |
| </div> | |
| </div> | |
| </fieldset> | |
| {% else %} | |
| {# it's configured to use selenium or chrome but system says its not configured #} | |
| {{ playwright_warning() }} | |
| {% if system_has_webdriver_configured %} | |
| <strong>Selenium/Webdriver cant be used here because it wont fetch screenshots reliably.</strong> | |
| {% endif %} | |
| {% endif %} | |
| {% else %} | |
| {# "This functionality needs chrome.." #} | |
| {{ only_playwright_type_watches_warning() }} | |
| {% endif %} | |
| </div> | |
| <div class="tab-pane-inner" id="notifications"> | |
| <fieldset> | |
| <div class="pure-control-group inline-radio"> | |
| {{ render_checkbox_field(form.notification_muted) }} | |
| </div> | |
| {% if watch_needs_selenium_or_playwright %} | |
| <div class="pure-control-group inline-radio"> | |
| {{ render_checkbox_field(form.notification_screenshot) }} | |
| <span class="pure-form-message-inline"> | |
| <strong>Use with caution!</strong> This will easily fill up your email storage quota or flood other storages. | |
| </span> | |
| </div> | |
| {% endif %} | |
| <div class="field-group" id="notification-field-group"> | |
| {% if has_default_notification_urls %} | |
| <div class="inline-warning"> | |
| <img class="inline-warning-icon" src="{{url_for('static_content', group='images', filename='notice.svg')}}" alt="Look out!" title="Lookout!" > | |
| There are <a href="{{ url_for('settings.settings_page')}}#notifications">system-wide notification URLs enabled</a>, this form will override notification settings for this watch only ‐ an empty Notification URL list here will still send notifications. | |
| </div> | |
| {% endif %} | |
| <a href="#notifications" id="notification-setting-reset-to-default" class="pure-button button-xsmall" style="right: 20px; top: 20px; position: absolute; background-color: #5f42dd; border-radius: 4px; font-size: 70%; color: #fff">Use system defaults</a> | |
| {{ render_common_settings_form(form, emailprefix, settings_application, extra_notification_token_placeholder_info) }} | |
| </div> | |
| </fieldset> | |
| </div> | |
| {% if watch['processor'] == 'text_json_diff' %} | |
| <div class="tab-pane-inner" id="conditions"> | |
| <script> | |
| </script> | |
| <div class="pure-control-group"> | |
| {{ render_field(form.conditions_match_logic) }} | |
| {{ render_conditions_fieldlist_of_formfields_as_table(form.conditions) }} | |
| <div class="pure-form-message-inline"> | |
| <p id="verify-state-text">Use the verify (✓) button to test if a condition passes against the current snapshot.</p> | |
| Read a quick tutorial about <a href="https://changedetection.io/tutorial/conditional-actions-web-page-changes">using conditional web page changes here</a>.<br> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="tab-pane-inner" id="filters-and-triggers"> | |
| <span id="activate-text-preview" class="pure-button pure-button-primary button-xsmall">Activate preview</span> | |
| <div> | |
| <div id="edit-text-filter"> | |
| <div class="pure-control-group" id="pro-tips"> | |
| <strong>Pro-tips:</strong><br> | |
| <ul> | |
| <li> | |
| Use the preview page to see your filters and triggers highlighted. | |
| </li> | |
| <li> | |
| Some sites use JavaScript to create the content, for this you should <a href="https://github.com/dgtlmoon/changedetection.io/wiki/Fetching-pages-with-WebDriver">use the Chrome/WebDriver Fetcher</a> | |
| </li> | |
| </ul> | |
| </div> | |
| {% include "edit/include_subtract.html" %} | |
| <div class="text-filtering border-fieldset"> | |
| <fieldset class="pure-group" id="text-filtering-type-options"> | |
| <h3>Text filtering</h3> | |
| Limit trigger/ignore/block/extract to;<br> | |
| {{ render_checkbox_field(form.filter_text_added) }} | |
| {{ render_checkbox_field(form.filter_text_replaced) }} | |
| {{ render_checkbox_field(form.filter_text_removed) }} | |
| <span class="pure-form-message-inline">Note: Depending on the length and similarity of the text on each line, the algorithm may consider an <strong>addition</strong> instead of <strong>replacement</strong> for example.</span><br> | |
| <span class="pure-form-message-inline"> So it's always better to select <strong>Added</strong>+<strong>Replaced</strong> when you're interested in new content.</span><br> | |
| <span class="pure-form-message-inline"> When content is merely moved in a list, it will also trigger an <strong>addition</strong>, consider enabling <code><strong>Only trigger when unique lines appear</strong></code></span> | |
| </fieldset> | |
| <fieldset class="pure-control-group"> | |
| {{ render_checkbox_field(form.check_unique_lines) }} | |
| <span class="pure-form-message-inline">Good for websites that just move the content around, and you want to know when NEW content is added, compares new lines against all history for this watch.</span> | |
| </fieldset> | |
| <fieldset class="pure-control-group"> | |
| {{ render_checkbox_field(form.remove_duplicate_lines) }} | |
| <span class="pure-form-message-inline">Remove duplicate lines of text</span> | |
| </fieldset> | |
| <fieldset class="pure-control-group"> | |
| {{ render_checkbox_field(form.sort_text_alphabetically) }} | |
| <span class="pure-form-message-inline">Helps reduce changes detected caused by sites shuffling lines around, combine with <i>check unique lines</i> below.</span> | |
| </fieldset> | |
| <fieldset class="pure-control-group"> | |
| {{ render_checkbox_field(form.trim_text_whitespace) }} | |
| <span class="pure-form-message-inline">Remove any whitespace before and after each line of text</span> | |
| </fieldset> | |
| {% include "edit/text-options.html" %} | |
| </div> | |
| </div> | |
| <div id="text-preview" style="display: none;" > | |
| <script> | |
| </script> | |
| <br> | |
| {#<div id="text-preview-controls"><span id="text-preview-refresh" class="pure-button button-xsmall">Refresh</span></div>#} | |
| <div class="minitabs-wrapper"> | |
| <div class="minitabs-content"> | |
| <div id="text-preview-inner" class="monospace-preview"> | |
| <p>Loading...</p> | |
| </div> | |
| <div id="text-preview-before-inner" style="display: none;" class="monospace-preview"> | |
| <p>Loading...</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {% endif %} | |
| {# rendered sub Template #} | |
| {% if extra_form_content %} | |
| <div class="tab-pane-inner" id="extras_tab"> | |
| {{ extra_form_content|safe }} | |
| </div> | |
| {% endif %} | |
| {% if watch['processor'] == 'text_json_diff' %} | |
| <div class="tab-pane-inner visual-selector-ui" id="visualselector"> | |
| <img class="beta-logo" src="{{url_for('static_content', group='images', filename='beta-logo.png')}}" alt="New beta functionality"> | |
| <fieldset> | |
| <div class="pure-control-group"> | |
| {% if watch_needs_selenium_or_playwright %} | |
| {% if system_has_playwright_configured %} | |
| <span class="pure-form-message-inline" id="visual-selector-heading"> | |
| The Visual Selector tool lets you select the <i>text</i> elements that will be used for the change detection. It automatically fills-in the filters in the "CSS/JSONPath/JQ/XPath Filters" box of the <a href="#filters-and-triggers">Filters & Triggers</a> tab. Use <strong>Shift+Click</strong> to select multiple items. | |
| </span> | |
| <div id="selector-header"> | |
| <a id="clear-selector" class="pure-button button-secondary button-xsmall" style="font-size: 70%">Clear selection</a> | |
| <!-- visual selector IMG will try to load, it will either replace this or on error replace it with some handy text --> | |
| <i class="fetching-update-notice" style="font-size: 80%;">One moment, fetching screenshot and element information..</i> | |
| </div> | |
| <div id="selector-wrapper" style="display: none"> | |
| <!-- request the screenshot and get the element offset info ready --> | |
| <!-- use img src ready load to know everything is ready to map out --> | |
| <!-- @todo: maybe something interesting like a field to select 'elements that contain text... and their parents n' --> | |
| <img id="selector-background" > | |
| <canvas id="selector-canvas"></canvas> | |
| </div> | |
| <div id="selector-current-xpath" style="overflow-x: hidden"><strong>Currently:</strong> <span class="text">Loading...</span></div> | |
| {% else %} | |
| {# The watch needed chrome but system says that playwright is not ready #} | |
| {{ playwright_warning() }} | |
| {% endif %} | |
| {% if system_has_webdriver_configured %} | |
| <strong>Selenium/Webdriver cant be used here because it wont fetch screenshots reliably.</strong> | |
| {% endif %} | |
| {% else %} | |
| {# "This functionality needs chrome.." #} | |
| {{ only_playwright_type_watches_warning() }} | |
| {% endif %} | |
| </div> | |
| </fieldset> | |
| </div> | |
| {% endif %} | |
| <div class="tab-pane-inner" id="stats"> | |
| <div class="pure-control-group"> | |
| <style> | |
| #stats-table tr > td:first-child { | |
| font-weight: bold; | |
| } | |
| </style> | |
| <table class="pure-table" id="stats-table"> | |
| <tbody> | |
| <tr> | |
| <td>Check count</td> | |
| <td>{{ "{:,}".format( watch.check_count) }}</td> | |
| </tr> | |
| <tr> | |
| <td>Consecutive filter failures</td> | |
| <td>{{ "{:,}".format( watch.consecutive_filter_failures) }}</td> | |
| </tr> | |
| <tr> | |
| <td>History length</td> | |
| <td>{{ "{:,}".format(watch.history|length) }}</td> | |
| </tr> | |
| <tr> | |
| <td>Last fetch duration</td> | |
| <td>{{ watch.fetch_time }}s</td> | |
| </tr> | |
| <tr> | |
| <td>Notification alert count</td> | |
| <td>{{ watch.notification_alert_count }}</td> | |
| </tr> | |
| <tr> | |
| <td>Server type reply</td> | |
| <td>{{ watch.get('remote_server_reply') }}</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| {% if ui_edit_stats_extras %} | |
| <div class="plugin-stats-extras"> <!-- from pluggy plugin --> | |
| {{ ui_edit_stats_extras|safe }} | |
| </div> | |
| {% endif %} | |
| {% if watch.history_n %} | |
| <p> | |
| <a href="{{url_for('ui.ui_edit.watch_get_latest_html', uuid=uuid)}}" class="pure-button button-small">Download latest HTML snapshot</a> | |
| </p> | |
| {% endif %} | |
| </div> | |
| </div> | |
| <div id="actions"> | |
| <div class="pure-control-group"> | |
| {{ render_button(form.save_button) }} | |
| <a href="{{url_for('ui.form_delete', uuid=uuid)}}" | |
| class="pure-button button-small button-error ">Delete</a> | |
| {% if watch.history_n %}<a href="{{url_for('ui.clear_watch_history', uuid=uuid)}}" | |
| class="pure-button button-small button-error ">Clear History</a>{% endif %} | |
| <a href="{{url_for('ui.form_clone', uuid=uuid)}}" | |
| class="pure-button button-small ">Clone & Edit</a> | |
| </div> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| {% endblock %} | |