{% extends 'base.html' %} {% load i18n %} {% block body %} {% if STATEFUL_MODE != 'disabled' %}

{% trans "Management for Config ID:" %} {{ key }}

{% if not CONFIG_LOCK %}
{% trans "Getting Started" %}
  1. {% blocktrans %} Here is where you can store your Apprise configuration associated with the key {{key}}. {% endblocktrans %} For some examples on how to build a development environment around this, click here.
  2. {% blocktrans %} In the future you can return to this configuration screen at any time by placing the following into your browser: {% endblocktrans %} {{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/cfg/{{key}}
  3. {% blocktrans %} Use the settings Configuration section to prepare and save your Apprise configuration. {% endblocktrans %}
    • {% blocktrans %} lightbulb_outlineYou can always refer to the Apprise Wiki if you're having troubles assembling your URL(s). {% endblocktrans %}
  4. {% blocktrans %} Use the web Review section to review what was parsed/detected from your defined configuration. {% endblocktrans %}
  5. {% blocktrans %} Use the announcement Notifications section to test out your saved configuration. {% endblocktrans %}
{% else %}
{% trans "Apprise Configuration is Locked" %}

{% blocktrans %}At this time, the administrator of this server has locked down all configuration. This means That pre-created configuration is securely hidden for the purpose of notification transmission only. New configuration can not be set, and existing configuration can not be modified or viewed. {% endblocktrans %}

{% endif %}
{% trans "Working Remotely With Your Configuration" %}
{% trans "Using The Apprise CLI" %}

{% blocktrans %}The following command would cause apprise to directly notify all of your services:{% endblocktrans %}

apprise --body="Test Message" \
    apprise{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/{{key}}/?tags=all
{% blocktrans %}Send one or more attachments like this:{% endblocktrans %}
apprise --body="Test Message" \
    apprise{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/{{key}}/?tags=all \
    --attach=/path/to/an/attachment.jpeg \
    --attach=https://raw.githubusercontent.com/caronc/apprise/master/apprise/assets/themes/default/apprise-logo.png

{% if not CONFIG_LOCK %}

{% blocktrans %}The following command would cause apprise to retrieve the configuration loaded and send a test notification to all of your added services:{% endblocktrans %}

apprise --body="Test Message" --tag=all \
    --config={{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/get/{{key}}
{% blocktrans %}You may also create an Apprise configuration file that contains this line somewhere in it:{% endblocktrans %}
include {{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/get/{{key}}
{% blocktrans %}By leveraging the include directive, it will automatically be referenced for future calls to the apprise tool. All future calls using Apprise now simplify to:{% endblocktrans %}
apprise --body="Test Message" --tag=all

{% endif %}
{% trans "Using CURL" %}

{% blocktrans %}The following command would cause the apprise api to notify all of your services:{% endblocktrans %}

curl -X POST \
    -F "body=Test Message" \
    -F "tags=all" \
    http{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/{{key}}
{% blocktrans %}Send one or more attachments like this:{% endblocktrans %}
curl -X POST \
    -F "tags=all" \
    -F "body=Test Message" \
    -F attach1=@Screenshot-1.png \
    -F attach2=@/my/path/to/Apprise.doc \
    http{% if request.is_secure %}s{% endif %}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/{{key}}
{% blocktrans %}Sends a notification to our endpoints with an attachment{% endblocktrans %}

        curl -X POST \
    -F "tag=all" \
    -F "attach=https://raw.githubusercontent.com/caronc/apprise/master/apprise/assets/themes/default/apprise-logo.png" \
    "{{request.scheme}}://{{request.META.HTTP_HOST}}{{BASE_URL}}/notify/{{key}}"

{% if not CONFIG_LOCK %}

{% blocktrans %}Define your configuration below:{% endblocktrans %}

{{ form_cfg }}

{% else %}
{% trans "Your Configuration Is Locked" %}

{% blocktrans %}Access to your configuration has been disabled by your administrator.{% endblocktrans %} {% endif %}

{% if not CONFIG_LOCK %}

info {% blocktrans %}Once you have successfully loaded at least one Apprise URL on the settings Configuration section, your loaded entries will display here. {% endblocktrans %}

{% trans "Loaded Configuration" %}

{% blocktrans %}Tip: Click on a tag (🏷️) to pre-select in the announcementNotifications section with the tag already pre-selected.{% endblocktrans %}

{% else %}
{% trans "Your Configuration Is Locked" %}

{% blocktrans %}Access to your configuration has been disabled by your administrator.{% endblocktrans %} {% endif %}

{% blocktrans %} You can send a notification using the loaded configuration: {% endblocktrans %}

{{ form_notify }}

{% else %}

{% trans "Persistent Store Endpoints" %}

{% blocktrans %}The administrator of this system has disabled persistent storage.{% endblocktrans %}

{% endif %} {% endblock %} {% block jsfooter %} {% if STATEFUL_MODE != 'disabled' %} async function main_init(){ const params = new Proxy(new URLSearchParams(window.location.search), { get: (searchParams, prop) => searchParams.get(prop), }); if (params.title) { document.querySelector('#id_title').value = params.title; } if (params.body) { document.querySelector('#id_body').value = params.body; } {% if not CONFIG_LOCK %} // disable the notification tab until we know for certain // a notification is possible document.querySelector('.config-overview li a[href="#notify"]') .parentNode.classList.add('disabled'); // Disable any has-config entries document.querySelectorAll('.has-config').forEach(function(e){ e.style.display = 'none' }); // Ensure we show our progress loader and reset our url list document.querySelector('#url-list').textContent = '' document.querySelector('#url-list-progress').style.display = null; // Ensure no-config sections are visible document.querySelectorAll('.no-config').forEach(function(e){ e.style.display = null; }); // perform a tag retrieval; start with 'all' let tags = ['all']; let jsonResponse = await fetch('{% url "json_urls" key %}/?privacy=1', { method: 'GET', }) if(jsonResponse.status == 204) { // Take an early exit document.querySelector('#url-list-progress').style.display = 'none'; document.querySelector('#url-list').textContent = '' return; } else if(jsonResponse.status != 200) { // Take an early exit document.querySelector('#url-list-progress').style.display = 'none'; document.querySelector('#url-list').textContent = '{% trans "💣 An error occurred retrieving the list of loaded Apprise URL(s)" %}' return; } // Initialize our tags making it easy for an end user to // choose from. Tags are based off ones found in the saved // configuration. const data = await jsonResponse.json(); const external_data = tags.concat(data.tags).reduce(function(result, item) { result[item] = null; return result; }, {}) const chipElement = document.querySelector('.chips'); M.Chips.init(chipElement, { placeholder: '{% trans "Optional Tag" %}', secondaryPlaceholder: '{% trans "Another Tag" %}', autocompleteOptions: { data: external_data, minLength: 0 }, onChipAdd: function(e, data) { var $this = this; const chip = data.childNodes[0].textContent; document.querySelectorAll(`#url-list .chip[name=${chip}]`).forEach(function(e){ if (!e.classList.contains('selected')) { e.classList.add('selected'); } }) document.querySelectorAll(`#url-list .chip.chip-notag`).forEach(function(e){ if (e.classList.contains('selected')) { e.classList.remove('selected'); } }) $this.chipsData.forEach(function(e, index) { if(!(e.tag in external_data)) $this.deleteChip(index); }) }, onChipDelete: function(e, data) { var $this = this; const chip = data.childNodes[0].textContent; document.querySelectorAll(`#url-list .chip[name=${chip}]`).forEach(function(e){ if (e.classList.contains('selected')) { e.classList.remove('selected'); } }) if ($this.chipsData.length == 0){ // last item document.querySelectorAll(`#url-list .chip.chip-notag`).forEach(function(e){ if (!e.classList.contains('selected')) { e.classList.add('selected'); } }) } } }); {% else %} {# Empty external data set #} const data = { urls: [] }; M.Chips.init(chipElement, { placeholder: '{% trans "Optional Tag" %}', secondaryPlaceholder: '{% trans "Another Tag" %}' }); {% endif %} const chipInstance = M.Chips.getInstance(chipElement); if (params.tag) { // our GET parameters to be treated as template values var tagRe = new RegExp('[^[A-Za-z0-9_-]+'); params.tag.split(tagRe).forEach(function (tag, index) { chipInstance.addChip({tag: tag, image: ''}); }); } {% if not CONFIG_LOCK %} // Now build our our loaded list of configuration for our welcome page let urlList = document.createElement('ul'); // Create a list item for each url retrieved data.urls.forEach(function (entry) { let code = document.createElement('code'); let li = document.createElement('li'); code.textContent = entry.url; li.setAttribute('class', 'card-panel'); li.appendChild(code); urlList.appendChild(li); // Store `all` tag entry.tags.unshift('all') if (entry.tags.length === 1){ // This entry triggers when no tags are defined // Store '' (empty) tag for notice generation entry.tags.unshift('') } // Get our tags associate with the URL entry.tags.forEach(function (tag) { let chip = document.createElement('div'); chip.setAttribute('class', `chip`); if(tag.length > 0) { // we're dealing with a valid tag chip.setAttribute('name', tag); chip.textContent = `🏷️ ${tag}`; const index = chipInstance.chipsData.findIndex(function (e) { return e.tag === tag;}) if (index >= 0) { chip.classList.add('selected'); } li.appendChild(chip); chip.addEventListener('click', function(e) { e.preventDefault(); const index = chipInstance.chipsData.findIndex(function (e) { return e.tag === tag; }); if(index < 0) { chipInstance.addChip({tag: this.getAttribute('name'), image: ''}); } else { chipInstance.deleteChip(index); } }, false); } else { // no tags were defined for this element chip.classList.add('chip-notag'); chip.textContent = '🔖 ' + 'no-tag'; if (chipInstance.chipsData.length === 0) { chip.classList.add('selected'); } li.appendChild(chip); chip.addEventListener('click', function(e) { e.preventDefault(); while(chipInstance.chipsData.length > 0){ chipInstance.deleteChip(0); } chip.classList.add('selected'); }, false); } }); }); {% endif %} {% if not CONFIG_LOCK %} // Store our new list document.querySelector('#url-list-progress').style.display = 'none'; document.querySelector('#url-list').textContent = '' if(urlList.childNodes.length > 0) { // Ensure has-config sections are visible document.querySelectorAll('.has-config').forEach(function(e){ e.style.display = null; }); // Remove our restrictions on sending notifications document.querySelector('.config-overview li a[href="#notify"]') .parentNode.classList.remove('disabled'); // Disable any no-config entries document.querySelectorAll('.no-config').forEach(function(e){ e.style.display = 'none'; }); // Save our list to the screen document.querySelector('#url-list').appendChild(urlList); // // Load our configuration now into the configuration tab // let response = await fetch('{% url "get" key %}', { method: 'POST', headers: { 'Content-Type': 'application/json;charset=utf-8' }, }); if(response.status == 204) { // no problem; we simply have no content to retrieve return ''; } else if(response.status == 200) { // configuration found // get our results let result = await response.json(); // Set our configuration so it's visible document.querySelector('#id_config').value = result.config; // Set our format document.querySelector('#id_format').value = result.format; // dispatch our event to update our select box if (typeof(Event) === 'function') { var event = new Event('change'); } else { // for IE11 var event = document.createEvent('Event'); event.initEvent('change', true, true); } document.querySelector('#id_format').dispatchEvent(event); } } else { document.querySelector('#url-list').textContent = '{% trans "There are no Apprise URL(s) loaded." %}' } {% endif %} return null; } {% if not CONFIG_LOCK %} function config_init() { // over-ride manual submit for a nicer user experience document.querySelector('#addconfig').onsubmit = function(event) { event.preventDefault(); const form = this; const body = new URLSearchParams(new FormData(form)); content = document.querySelector('#id_config') .value.replace(/^\s+|\s+$/gm,''); if(content.length) { // perform our status check let response = fetch('{% url "add" key %}', { method: 'POST', body: body, }).then(function(response) { if(response.status == 200) { // update our settings main_init(); // user notification Swal.fire( '{% trans "Save" %}', '{% trans "Successfully saved the specified URL(s)." %}', 'success' ); } else if(response.status == 500) { // Disk issue Swal.fire( '{% trans "Save" %}', '{% trans "There was an issue writing the configuration to disk. Check your file permissions and try again." %}', 'error' ); } else { // user notification Swal.fire( '{% trans "Save" %}', '{% trans "Failed to save the specified URL(s). Check your syntax and try again." %}', 'error' ); } }); } else { // Perform Delete // perform our status check let response = fetch('{% url "del" key %}', { method: 'POST', body: body, }).then(function(response) { if(response.status == 200 || response.status == 204) { // update our settings main_init(); // user notification Swal.fire( '{% trans "Delete" %}', '{% trans "Successfully removed configuration." %}', 'success' ); } else { // user notification Swal.fire( '{% trans "Delete" %}', '{% trans "There was an issue removing the configuration." %}', 'error' ); } }); } return false; } } {% endif %} function form_file_input_hack() { /* A small hack to remformat all `` upload options to a more cleaner and presentable look by wrapping it like this: cloud_upload Browse */ document.querySelectorAll('input[type=file]').forEach(function (entry) { const div = document.createElement('div'); const selected = document.createElement('div'); selected.style.display = 'none'; selected.setAttribute('class', 'file-selected'); const span = document.createElement('span') span.setAttribute('class', 'btn btn-file waves-effect waves-light'); const i = document.createElement('i') span.textContent = '{% trans "Browse" %}'; i.setAttribute('class', 'material-icons right'); i.textContent = 'folder'; div.appendChild(span); span.appendChild(i); entry.before(div) span.appendChild(entry); span.after(selected); entry.addEventListener('change', function(e){ const selected = this.parentNode.nextElementSibling; const file = e.target.files[0]; selected.style.display = 'block' selected.textContent = file.name; }); }); } function notify_init() { // over-ride manual submit for a nicer user experience document.querySelector('#donotify').onsubmit = function(event) { event.preventDefault(); const chipElement = document.querySelector('.chips'); const chipInput = document.querySelector('.chips > input'); if(chipInput.value) { // This code just handles text typed in the tag section that was // not submitted. This forces any lingering un-committed text // into a tag just prior to it's submission const ev = new KeyboardEvent('keydown', { altKey:false, bubbles: true, cancelBubble: false, cancelable: true, charCode: 0, code: "Enter", composed: true, ctrlKey: false, currentTarget: null, defaultPrevented: true, detail: 0, eventPhase: 0, isComposing: false, isTrusted: true, key: "Enter", keyCode: 13, location: 0, metaKey: false, repeat: false, returnValue: false, shiftKey: false, type: "keydown", which: 13 }); chipInput.dispatchEvent(ev); } // store tags (as comma separated string) from materialize chip type into form document.querySelector('#id_tag').value = M.Chips.getInstance(chipElement).chipsData.reduce( function(s, a){ s.push(a.tag) return s; }, []).join(",") // our Form const form = new FormData(this); // perform our notification Swal.fire( '{% trans "Notification" %}', '{% trans "Sending notification(s)..." %}', ); Swal.showLoading() let response = fetch('{% url "notify" key %}', { method: 'POST', body: form, headers: { 'Accept': 'text/html', 'X-Apprise-Log-Level': 'info' } }).then(function(response) { response.text().then(function (html) { if(response.status == 200) { // user notification Swal.fire( '{% trans "Notification" %}', '{% trans "Successfully sent the notification(s)." %}' + html, 'success' ); } else if(response.status == 424) { // user notification Swal.fire( '{% trans "Notification" %}', '{% trans "One or more of the notification(s) were not sent." %}' + html, 'warning' ); } else { // user notification Swal.fire( '{% trans "Notification" %}', '{% trans "Failed to send the notification(s)." %}' + html, 'error' ); } }); }); return false; } } {% endif %} {% endblock %} {% block onload %} {% if STATEFUL_MODE != 'disabled' %} {{ block.super }} document.querySelector('label [for="id_tag"]') { // create a new div with the class 'chips' assigned to it const element = document.createElement('div') let refNode = document.querySelector('label[for="id_tag"]') element.classList.add("chips") element.style.margin = '0' refNode.parentNode.insertBefore(element, refNode.nextSibling) } // Hide tag field since we use the pretty Materialize Chip setup instead document.querySelector('#id_tag').style.display = 'none'; /* Initialize our page */ main_init(); {% if not CONFIG_LOCK %} /* Initialze our configuration */ config_init(); {% endif %} notify_init(); form_file_input_hack(); {% endif %} {% endblock %}