You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
changedetection.io/changedetectionio/templates/edit.html

674 lines
26 KiB

{% extends 'base.html' %} {% block content %} {% from '_helpers.jinja' import
render_field, render_checkbox_field, render_button %} {% from
'_common_fields.jinja' import render_common_settings_form %}
<script
type="text/javascript"
src="{{url_for('static_content', group='js', filename='tabs.js')}}"
defer
></script>
<script>
const notification_base_url="{{url_for('ajax_callback_send_notification_test')}}";
const watch_visual_selector_data_url="{{url_for('static_content', group='visual_selector_data', filename=uuid)}}";
const screenshot_url="{{url_for('static_content', group='screenshot', filename=uuid)}}";
const playwright_enabled={% if playwright_enabled %} true {% else %} false {% endif %};
{% if emailprefix %}
const email_notification_prefix=JSON.parse('{{ emailprefix|tojson }}');
{% endif %}
const browser_steps_config=JSON.parse('{{ browser_steps_config|tojson }}');
const browser_steps_sync_url="{{url_for('browser_steps.browsersteps_ui_update', uuid=uuid)}}";
</script>
<script
type="text/javascript"
src="{{url_for('static_content', group='js', filename='watch-settings.js')}}"
defer
></script>
<script
type="text/javascript"
src="{{url_for('static_content', group='js', filename='limit.js')}}"
defer
></script>
<script
type="text/javascript"
src="{{url_for('static_content', group='js', filename='notifications.js')}}"
defer
></script>
<script
type="text/javascript"
src="{{url_for('static_content', group='js', filename='visual-selector.js')}}"
defer
></script>
{% if playwright_enabled %}
<script
type="text/javascript"
src="{{url_for('static_content', group='js', filename='browser-steps.js')}}"
defer
></script>
{% endif %}
<div class="edit-form monospaced-textarea">
<div class="tabs collapsable">
<ul>
<li class="tab" id=""><a href="#general">General</a></li>
<li class="tab"><a href="#request">Request</a></li>
{% if playwright_enabled %}
<li class="tab">
<a id="browsersteps-tab" href="#browser-steps">Browser Steps</a>
</li>
{% endif %}
<li class="tab">
<a id="visualselector-tab" href="#visualselector"
>Visual Filter Selector</a
>
</li>
<li class="tab">
<a href="#filters-and-triggers">Filters &amp; Triggers</a>
</li>
<li class="tab"><a href="#notifications">Notifications</a></li>
</ul>
</div>
<div class="box-wrap inner">
<form
class="pure-form pure-form-stacked"
action="{{ url_for('edit_page', uuid=uuid, next = request.args.get('next'), unpause_on_save = request.args.get('unpause_on_save')) }}"
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") }}
<span class="pure-form-message-inline"
>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
></span
><br />
<span class="pure-form-message-inline"
>You can use variables in the URL, perfect for inserting the
current date and other logic,
<a
href="https://github.com/dgtlmoon/changedetection.io/wiki/Handling-variables-in-the-watched-URL"
>help and examples here</a
></span
><br />
</div>
<div class="pure-control-group">
{{ render_field(form.title, class="m-d") }}
</div>
<div class="pure-control-group">
{{ render_field(form.tag) }}
<span class="pure-form-message-inline"
>Organisational tag/group name used in the main listing page</span
>
</div>
<div class="pure-control-group">
{{ render_field(form.time_between_check, class="time-check-widget")
}} {% if has_empty_checktime %}
<span class="pure-form-message-inline"
>Currently using the
<a href="{{ url_for('settings_page', uuid=uuid) }}"
>default global settings</a
>, change to another value if you want to be specific.</span
>
{% else %}
<span class="pure-form-message-inline"
>Set to blank to use the
<a href="{{ url_for('settings_page', uuid=uuid) }}"
>default global settings</a
>.</span
>
{% endif %}
</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 BrightData Proxies, find out more here.</a
>
</span>
</div>
{% if form.proxy %}
<div class="pure-control-group inline-radio">
{{ render_field(form.proxy, class="fetch-backend-proxy") }}
<span class="pure-form-message-inline">
Choose a proxy for this watch
</span>
</div>
{% endif %}
<div class="pure-control-group inline-radio">
{{ render_checkbox_field(form.ignore_status_codes) }}
</div>
<fieldset id="webdriver-override-options">
<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">
{{ 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>
<fieldset class="pure-group" id="requests-override-options">
{% if not playwright_enabled %}
<div class="pure-form-message-inline">
<strong
>Request override is currently only used by the
<i>Basic fast Plaintext/HTTP Client</i> method.</strong
>
</div>
{% endif %}
<div class="pure-control-group" id="request-method">
{{ render_field(form.method) }}
</div>
<div class="pure-control-group" id="request-headers">
{{ render_field(form.headers, rows=5, placeholder="Example
Cookie: foobar User-Agent: wonderbra 1.0") }}
</div>
<div class="pure-control-group" id="request-body">
{{ render_field(form.body, rows=5, placeholder="Example
{\"name\":\"John\", \"age\":30, \"car\":null }") }}
</div>
</fieldset>
</div>
{% if playwright_enabled %}
<div class="tab-pane-inner" id="browser-steps">
<img
class="beta-logo"
src="{{url_for('static_content', group='images', filename='beta-logo.png')}}"
/>
<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"
style="width: 100%; background-color: #eee; border-radius: 5px"
>
<div
class="noselect"
id="browsersteps-selector-wrapper"
style="width: 100%"
>
<span class="loader">
<span id="browsersteps-click-start">
<h2>Click here to Start</h2>
Please allow 10-15 seconds for the browser to connect.
</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"
style="padding-left: 1em; width: 350px; font-size: 80%"
>
<span id="browserless-seconds-remaining">Loading</span>
<span style="font-size: 80%">
(<a
target="_new"
href="https://github.com/dgtlmoon/changedetection.io/pull/478/files#diff-1a79d924d1840c485238e66772391268a89c95b781d69091384cf1ea1ac146c9R4"
>?</a
>)
</span>
{{ render_field(form.browser_steps) }}
</div>
</div>
</div>
</fieldset>
</div>
{% endif %}
<div class="tab-pane-inner" id="notifications">
<fieldset>
<div class="pure-control-group inline-radio">
{{ render_checkbox_field(form.notification_muted) }}
</div>
{% if is_html_webdriver %}
<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_page')}}#notifications"
>system-wide notification URLs enabled</a
>, this form will override notification settings for this watch
only &dash; 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) }}
</div>
</fieldset>
</div>
<div class="tab-pane-inner" id="filters-and-triggers">
<div class="pure-control-group">
<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>
<fieldset>
<div 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
>
</div>
</fieldset>
<div class="pure-control-group">
{% set field = render_field(form.include_filters, rows=5,
placeholder="#example
xpath://body/div/span[contains(@class, 'example-class')]", class="m-d") %} {{ field }} {% if '/text()' in
field %}
<span class="pure-form-message-inline"
><strong
>Note!: //text() function does not work where the &lt;element&gt;
contains &lt;![CDATA[]]&gt;</strong
></span
><br />
{% endif %}
<span class="pure-form-message-inline"
>One rule per line, <i>any</i> rules that matches will be used.<br />
<ul>
<li>
CSS - Limit text to this CSS rule, only text matching this CSS
rule is included.
</li>
<li>
JSON - Limit text to this JSON rule, using either
<a href="https://pypi.org/project/jsonpath-ng/" target="new"
>JSONPath</a
>
or
<a href="https://stedolan.github.io/jq/" target="new">jq</a> (if
installed).
<ul>
<li>
JSONPath: Prefix with <code>json:</code>, use
<code>json:$</code> to force re-formatting if required,
<a href="https://jsonpath.com/" target="new"
>test your JSONPath here</a
>.
</li>
{% if jq_support %}
<li>
jq: Prefix with <code>jq:</code> and
<a href="https://jqplay.org/" target="new"
>test your jq here</a
>. Using
<a href="https://stedolan.github.io/jq/" target="new">jq</a>
allows for complex filtering and processing of JSON data
with built-in functions, regex, filtering, and more. See
examples and documentation
<a href="https://stedolan.github.io/jq/manual/" target="new"
>here</a
>.
</li>
{% else %}
<li>jq support not installed</li>
{% endif %}
</ul>
</li>
<li>
XPath - Limit text to this XPath rule, simply start with a
forward-slash,
<ul>
<li>
Example: <code>//*[contains(@class, 'sametext')]</code> or
<code>xpath://*[contains(@class, 'sametext')]</code>,
<a href="http://xpather.com/" target="new"
>test your XPath here</a
>
</li>
<li>
Example: Get all titles from an RSS feed
<code>//title/text()</code>
</li>
</ul>
</li>
</ul>
Please be sure that you thoroughly understand how to write CSS,
JSONPath, XPath{% if jq_support %}, or jq selector{%endif%} rules
before filing an issue on GitHub!
<a
href="https://github.com/dgtlmoon/changedetection.io/wiki/CSS-Selector-help"
>here for more CSS selector help</a
>.<br />
</span>
</div>
<div class="pure-control-group">
{{ render_field(form.subtractive_selectors, rows=5,
placeholder="header footer nav .stockticker") }}
<span class="pure-form-message-inline">
<ul>
<li>
Remove HTML element(s) by CSS selector before text conversion.
</li>
<li>
Add multiple elements or CSS selectors per line to ignore
multiple parts of the HTML.
</li>
</ul>
</span>
</div>
<fieldset class="pure-group">
{{ render_field(form.ignore_text, rows=5, placeholder="Some text to ignore in a line /some.regex\d{2}/ for case-INsensitive regex ") }}
<span class="pure-form-message-inline">
<ul>
<li>
Each line processed separately, any line matching will be
ignored (removed before creating the checksum)
</li>
<li>
Regular Expression support, wrap the entire line in forward
slash <code>/regex/</code>
</li>
<li>
Changing this will affect the comparison checksum which may
trigger an alert
</li>
<li>Use the preview/show current tab to see ignores</li>
</ul>
</span>
</fieldset>
<fieldset>
<div class="pure-control-group">
{{ render_field(form.trigger_text, rows=5, placeholder="Some text to wait for in a line /some.regex\d{2}/ for case-INsensitive regex ")
}}
<span class="pure-form-message-inline">
<ul>
<li>
Text to wait for before triggering a change/notification, all
text and regex are tested <i>case-insensitive</i>.
</li>
<li>
Trigger text is processed from the result-text that comes out
of any CSS/JSON Filters for this watch
</li>
<li>
Each line is processed separately (think of each line as "OR")
</li>
<li>
Note: Wrap in forward slash / to use regex example:
<code>/foo\d/</code>
</li>
</ul>
</span>
</div>
</fieldset>
<fieldset>
<div class="pure-control-group">
{{ render_field(form.text_should_not_be_present, rows=5,
placeholder="For example:
Out of stock
Sold out
Not in stock
Unavailable") }}
<span class="pure-form-message-inline">
<ul>
<li>
Block change-detection while this text is on the page, all
text and regex are tested <i>case-insensitive</i>, good for
waiting for when a product is available again
</li>
<li>
Block text is processed from the result-text that comes out of
any CSS/JSON Filters for this watch
</li>
<li>
All lines here must not exist (think of each line as "OR")
</li>
<li>
Note: Wrap in forward slash / to use regex example:
<code>/foo\d/</code>
</li>
</ul>
</span>
</div>
</fieldset>
<fieldset>
<div class="pure-control-group">
{{ render_field(form.extract_text, rows=5, placeholder="\d+ online")
}}
<span class="pure-form-message-inline">
<ul>
<li>
Extracts text in the final output (line by line) after other
filters using regular expressions;
<ul>
<li>
Regular expression &dash; example
<code>/reports.+?2022/i</code>
</li>
<li>
Use <code>//(?aiLmsux))</code> type flags (more
<a
href="https://docs.python.org/3/library/re.html#index-15"
>information here</a
>)<br />
</li>
<li>
Keyword example &dash; example <code>Out of stock</code>
</li>
<li>
Use groups to extract just that text &dash; example
<code>/reports.+?(\d+)/i</code> returns a list of years
only
</li>
</ul>
</li>
<li>One line per regular-expression/ string match</li>
</ul>
</span>
</div>
</fieldset>
</div>
<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')}}"
/>
<fieldset>
<div class="pure-control-group">
{% if visualselector_enabled %}
<span class="pure-form-message-inline">
The Visual Selector tool lets you select the <i>text</i> elements
that will be used for the change detection &dash; after the
<i>Browser Steps</i> has completed.<br /><br />
</span>
<div id="selector-header">
<a
id="clear-selector"
class="pure-button button-secondary button-xsmall"
style="font-size: 70%"
>Clear selection</a
>
<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>&nbsp;<span class="text"
>Loading...</span
>
</div>
{% else %}
<span class="pure-form-message-inline">
<p>
Sorry, this functionality only works with Playwright/Chrome
enabled watches.
</p>
<p>
Enable the Playwright Chrome fetcher, or alternatively try our
<a href="https://lemonade.changedetection.io/start"
>very affordable subscription based service</a
>.
</p>
<p>
This is because Selenium/WebDriver can not extract full page
screenshots reliably.
</p>
</span>
{% endif %}
</div>
</fieldset>
</div>
<div id="actions">
<div class="pure-control-group">
{{ render_button(form.save_button) }}
<a
href="{{url_for('form_delete', uuid=uuid)}}"
class="pure-button button-warning"
>Delete</a
>
<a
href="{{url_for('clear_watch_history', uuid=uuid)}}"
class="pure-button button-warning"
>Clear History</a
>
<a href="{{url_for('form_clone', uuid=uuid)}}" class="pure-button"
>Create Copy</a
>
</div>
</div>
</form>
</div>
</div>
{% endblock %}