Release 0.9.0
This commit is contained in:
208
bot/modules/dom_management/__init__.py
Normal file
208
bot/modules/dom_management/__init__.py
Normal file
@@ -0,0 +1,208 @@
|
||||
from bot.module import Module
|
||||
from bot import loaded_modules_dict
|
||||
|
||||
|
||||
class DomManagement(Module):
|
||||
# region Standard module stuff
|
||||
def __init__(self):
|
||||
setattr(self, "default_options", {
|
||||
"module_name": self.get_module_identifier()[7:]
|
||||
})
|
||||
|
||||
setattr(self, "required_modules", [
|
||||
"module_dom",
|
||||
"module_webserver"
|
||||
])
|
||||
|
||||
self.run_observer_interval = 5
|
||||
Module.__init__(self)
|
||||
|
||||
@staticmethod
|
||||
def get_module_identifier():
|
||||
return "module_dom_management"
|
||||
|
||||
def setup(self, options=dict):
|
||||
Module.setup(self, options)
|
||||
# endregion
|
||||
|
||||
# region Tools and workers
|
||||
@staticmethod
|
||||
def sanitize_for_html_id(value):
|
||||
"""
|
||||
Sanitize a string for use in HTML IDs.
|
||||
Replaces spaces with underscores and converts to lowercase.
|
||||
|
||||
Args:
|
||||
value: String to sanitize
|
||||
|
||||
Returns:
|
||||
Sanitized string safe for HTML IDs
|
||||
"""
|
||||
return str(value).replace(" ", "_").lower()
|
||||
|
||||
def occurrences_of_key_in_nested_mapping(self, key, value):
|
||||
for k, v in value.items():
|
||||
if k == key:
|
||||
yield v
|
||||
elif isinstance(v, dict):
|
||||
for result in self.occurrences_of_key_in_nested_mapping(key, v):
|
||||
yield result
|
||||
|
||||
def get_dict_element_by_path(self, d, l):
|
||||
if len(l) == 1:
|
||||
return d.get(l[0], [])
|
||||
return self.get_dict_element_by_path(d.get(l[0], {}), l[1:])
|
||||
# endregion
|
||||
|
||||
# region Template functions
|
||||
@staticmethod
|
||||
def get_selection_dom_element(*args, **kwargs):
|
||||
module = args[0]
|
||||
return module.template_render_hook(
|
||||
module,
|
||||
template=module.dom_management.templates.get_template('control_select_link.html'),
|
||||
dom_element_select_root=kwargs.get("dom_element_select_root"),
|
||||
target_module=kwargs.get("target_module"),
|
||||
dom_element_entry_selected=kwargs.get("dom_element_entry_selected"),
|
||||
dom_element=kwargs.get("dom_element"),
|
||||
dom_action_inactive=kwargs.get("dom_action_inactive"),
|
||||
dom_action_active=kwargs.get("dom_action_active")
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_delete_button_dom_element(*args, **kwargs):
|
||||
module = args[0]
|
||||
return module.template_render_hook(
|
||||
module,
|
||||
template=module.dom_management.templates.get_template('control_action_delete_button.html'),
|
||||
count=kwargs.get("count"),
|
||||
target_module=kwargs.get("target_module"),
|
||||
dom_element_root=kwargs.get("dom_element_root"),
|
||||
dom_element_select_root=kwargs.get("dom_element_select_root"),
|
||||
dom_action=kwargs.get("dom_action"),
|
||||
delete_selected_entries_active=kwargs.get("count") >= 1,
|
||||
dom_element_id=kwargs.get("dom_element_id"),
|
||||
confirmed=kwargs.get("confirmed", "False")
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_delete_confirm_modal(*args, **kwargs):
|
||||
module = args[0]
|
||||
return module.template_render_hook(
|
||||
module,
|
||||
template=module.dom_management.templates.get_template('modal_confirm_delete.html'),
|
||||
count=kwargs.get("count"),
|
||||
target_module=kwargs.get("target_module"),
|
||||
dom_element_root=kwargs.get("dom_element_root"),
|
||||
dom_element_select_root=kwargs.get("dom_element_select_root"),
|
||||
dom_action=kwargs.get("dom_action"),
|
||||
delete_selected_entries_active=kwargs.get("count") >= 1,
|
||||
dom_element_id=kwargs.get("dom_element_id"),
|
||||
confirmed=kwargs.get("confirmed", "False")
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def update_selection_status(*args, **kwargs):
|
||||
module = args[0]
|
||||
updated_values_dict = kwargs.get("updated_values_dict", None)
|
||||
target_module = kwargs.get("target_module", None)
|
||||
dom_element_root = kwargs.get("dom_element_root", [])
|
||||
dom_action_active = kwargs.get("dom_action_active", None)
|
||||
dom_action_inactive = kwargs.get("dom_action_inactive", None)
|
||||
dom_element_select_root = kwargs.get("dom_element_select_root", ["selected_by"])
|
||||
dom_element_id = kwargs.get("dom_element_id", None)
|
||||
|
||||
# Use unsanitized dataset_original for DOM lookups (if available)
|
||||
dom_element_origin = updated_values_dict.get("dataset_original", updated_values_dict.get("dataset"))
|
||||
dom_element_owner = updated_values_dict["owner"]
|
||||
|
||||
dispatchers_steamid = kwargs.get("dispatchers_steamid", None)
|
||||
|
||||
# getting the base root for all elements. it's always this path if the module wants to use these built
|
||||
# in functions
|
||||
dom_element = (
|
||||
module.dom.data
|
||||
.get(target_module.get_module_identifier(), {})
|
||||
.get("elements", {})
|
||||
.get(dom_element_origin, {})
|
||||
.get(dom_element_owner, {})
|
||||
)
|
||||
|
||||
# get the individual element path, as provided by the module
|
||||
for sub_dict in dom_element_root:
|
||||
dom_element = dom_element.get(sub_dict)
|
||||
|
||||
dom_element_is_selected_by = dom_element.get("selected_by", [])
|
||||
dom_element_entry_selected = False
|
||||
if dispatchers_steamid in dom_element_is_selected_by:
|
||||
dom_element_entry_selected = True
|
||||
|
||||
control_select_link = module.dom_management.get_selection_dom_element(
|
||||
module,
|
||||
target_module=target_module.get_module_identifier(),
|
||||
dom_element_select_root=dom_element_select_root,
|
||||
dom_element=dom_element,
|
||||
dom_element_entry_selected=dom_element_entry_selected,
|
||||
dom_action_inactive=dom_action_inactive,
|
||||
dom_action_active=dom_action_active
|
||||
)
|
||||
|
||||
module.webserver.send_data_to_client_hook(
|
||||
module,
|
||||
payload=control_select_link,
|
||||
data_type="element_content",
|
||||
clients=[dispatchers_steamid],
|
||||
method="update",
|
||||
target_element=dom_element_id
|
||||
)
|
||||
|
||||
def update_delete_button_status(self, *args, **kwargs):
|
||||
module = args[0]
|
||||
|
||||
target_module = kwargs.get("target_module", None)
|
||||
dom_action = kwargs.get("dom_action", None)
|
||||
dom_element_id = kwargs.get("dom_element_id", None)
|
||||
|
||||
template_action_delete_button = module.dom_management.templates.get_template('control_action_delete_button.html')
|
||||
|
||||
all_available_elements = (
|
||||
module.dom.data
|
||||
.get(target_module.get_module_identifier(), {})
|
||||
.get("elements", {})
|
||||
)
|
||||
|
||||
for clientid in module.webserver.connected_clients.keys():
|
||||
all_selected_elements = 0
|
||||
for dom_element_is_selected_by in self.occurrences_of_key_in_nested_mapping(
|
||||
"selected_by",
|
||||
all_available_elements
|
||||
):
|
||||
if clientid in dom_element_is_selected_by:
|
||||
all_selected_elements += 1
|
||||
|
||||
data_to_emit = module.template_render_hook(
|
||||
module,
|
||||
template=template_action_delete_button,
|
||||
dom_action=dom_action,
|
||||
dom_element_root=kwargs.get("dom_element_root", []),
|
||||
dom_element_select_root=kwargs.get("dom_element_select_root", []),
|
||||
target_module=target_module.get_module_identifier(),
|
||||
count=all_selected_elements,
|
||||
delete_selected_entries_active=all_selected_elements >= 1,
|
||||
dom_element_id=dom_element_id["id"],
|
||||
confirmed=kwargs.get("confirmed", "False")
|
||||
)
|
||||
|
||||
module.webserver.send_data_to_client_hook(
|
||||
module,
|
||||
payload=data_to_emit,
|
||||
data_type="element_content",
|
||||
clients=[clientid],
|
||||
method="replace",
|
||||
target_element=dom_element_id
|
||||
)
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
loaded_modules_dict[DomManagement().get_module_identifier()] = DomManagement()
|
||||
66
bot/modules/dom_management/actions/delete.py
Normal file
66
bot/modules/dom_management/actions/delete.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from bot import loaded_modules_dict
|
||||
from os import path, pardir
|
||||
|
||||
module_name = path.basename(path.normpath(path.join(path.abspath(__file__), pardir, pardir)))
|
||||
action_name = path.basename(path.abspath(__file__))[:-3]
|
||||
|
||||
|
||||
def main_function(module, event_data, dispatchers_steamid):
|
||||
action = event_data[1].get("action", None)
|
||||
target_module = event_data[1].get("target_module", None)
|
||||
action_is_confirmed = event_data[1].get("confirmed", "False")
|
||||
event_data[1]["action_identifier"] = action_name
|
||||
|
||||
if action == "delete_selected_dom_elements":
|
||||
if action_is_confirmed == "True":
|
||||
stuff_to_delete = []
|
||||
for path, dom_element_key, dom_element in module.dom.get_dom_element_by_query(
|
||||
target_module=target_module,
|
||||
query="selected_by"
|
||||
):
|
||||
if dispatchers_steamid in dom_element:
|
||||
stuff_to_delete.append([target_module, "elements"] + path)
|
||||
|
||||
for dom_element_to_delete in stuff_to_delete:
|
||||
module.dom.data.remove_key_by_path(
|
||||
dom_element_to_delete,
|
||||
dispatchers_steamid=dispatchers_steamid
|
||||
)
|
||||
|
||||
module.callback_success(callback_success, module, event_data, dispatchers_steamid)
|
||||
return
|
||||
else:
|
||||
loaded_modules_dict[target_module].set_current_view(dispatchers_steamid, {
|
||||
"current_view": "delete-modal"
|
||||
})
|
||||
return
|
||||
|
||||
elif action == "cancel_delete_selected_dom_elements":
|
||||
module.callback_success(callback_success, module, event_data, dispatchers_steamid)
|
||||
return
|
||||
|
||||
module.callback_fail(callback_fail, module, event_data, dispatchers_steamid)
|
||||
return
|
||||
|
||||
|
||||
def callback_success(module, event_data, dispatchers_steamid, match=None):
|
||||
target_module = event_data[1].get("target_module", None)
|
||||
loaded_modules_dict[target_module].set_current_view(dispatchers_steamid, {
|
||||
"current_view": "frontend",
|
||||
})
|
||||
|
||||
|
||||
def callback_fail(module, event_data, dispatchers_steamid):
|
||||
pass
|
||||
|
||||
|
||||
action_meta = {
|
||||
"description": "tools to help managing dom elements in the webinterface",
|
||||
"main_function": main_function,
|
||||
"callback_success": callback_success,
|
||||
"callback_fail": callback_fail,
|
||||
"requires_telnet_connection": False,
|
||||
"enabled": True
|
||||
}
|
||||
|
||||
loaded_modules_dict["module_" + module_name].register_action(action_name, action_meta)
|
||||
120
bot/modules/dom_management/actions/select.py
Normal file
120
bot/modules/dom_management/actions/select.py
Normal file
@@ -0,0 +1,120 @@
|
||||
from bot import loaded_modules_dict
|
||||
from bot.logger import get_logger
|
||||
from os import path, pardir
|
||||
|
||||
module_name = path.basename(path.normpath(path.join(path.abspath(__file__), pardir, pardir)))
|
||||
action_name = path.basename(path.abspath(__file__))[:-3]
|
||||
logger = get_logger("dom_management.select")
|
||||
|
||||
|
||||
def main_function(module, event_data, dispatchers_steamid):
|
||||
action = event_data[1].get("action", None)
|
||||
target_module = event_data[1].get("target_module", None)
|
||||
event_data[1]["action_identifier"] = action_name
|
||||
event_data[1]["fail_reason"] = []
|
||||
|
||||
dom_element_select_root = event_data[1].get("dom_element_select_root", ["selected_by"])
|
||||
dom_element_origin = event_data[1].get("dom_element_origin", None)
|
||||
dom_element_owner = event_data[1].get("dom_element_owner", None)
|
||||
dom_element_identifier = event_data[1].get("dom_element_identifier", None)
|
||||
|
||||
if all([
|
||||
action is not None,
|
||||
dom_element_origin is not None,
|
||||
dom_element_owner is not None,
|
||||
dom_element_identifier is not None
|
||||
]):
|
||||
if action in [ # only proceed with known commands
|
||||
"select_dom_element",
|
||||
"deselect_dom_element"
|
||||
]:
|
||||
general_root = [target_module, "elements", dom_element_origin, dom_element_owner]
|
||||
full_root = general_root + dom_element_select_root
|
||||
|
||||
selected_by_dict_element = module.dom_management.get_dict_element_by_path(module.dom.data, full_root)
|
||||
|
||||
# CRITICAL: Make a COPY of the list! Otherwise we modify the original list,
|
||||
# and then upsert sees old_value == new_value (both are references to the same list)
|
||||
# This would cause the callback to NOT fire because method="unchanged"
|
||||
selected_by_dict_element = list(selected_by_dict_element)
|
||||
|
||||
try:
|
||||
if action == "select_dom_element":
|
||||
if dispatchers_steamid not in selected_by_dict_element:
|
||||
selected_by_dict_element.append(dispatchers_steamid)
|
||||
elif action == "deselect_dom_element":
|
||||
if dispatchers_steamid in selected_by_dict_element:
|
||||
selected_by_dict_element.remove(dispatchers_steamid)
|
||||
except ValueError as error:
|
||||
logger.error("select_list_manipulation_failed",
|
||||
user=dispatchers_steamid,
|
||||
action=action,
|
||||
target_module=target_module,
|
||||
origin=dom_element_origin,
|
||||
owner=dom_element_owner,
|
||||
identifier=dom_element_identifier,
|
||||
error=str(error))
|
||||
|
||||
# Build data payload
|
||||
data_payload = {
|
||||
"selected_by": selected_by_dict_element,
|
||||
"dataset": dom_element_origin,
|
||||
"dataset_original": dom_element_origin,
|
||||
"owner": dom_element_owner,
|
||||
"identifier": dom_element_identifier
|
||||
}
|
||||
|
||||
# Build nested structure dynamically based on dom_element_select_root
|
||||
# All keys except the last one (which is "selected_by") define nesting levels
|
||||
nested_keys = dom_element_select_root[:-1] # Remove "selected_by"
|
||||
|
||||
# Build nested dict from inside out
|
||||
nested_data = data_payload
|
||||
for key in reversed(nested_keys):
|
||||
nested_data = {key: nested_data}
|
||||
|
||||
module.dom.data.upsert({
|
||||
target_module: {
|
||||
"elements": {
|
||||
dom_element_origin: {
|
||||
dom_element_owner: nested_data
|
||||
}
|
||||
}
|
||||
}
|
||||
}, dispatchers_steamid=dispatchers_steamid, min_callback_level=4)
|
||||
|
||||
module.callback_success(callback_success, module, event_data, dispatchers_steamid)
|
||||
return
|
||||
|
||||
else:
|
||||
event_data[1]["fail_reason"].append("unknown action")
|
||||
else:
|
||||
event_data[1]["fail_reason"].append("required options not set")
|
||||
|
||||
module.callback_fail(callback_fail, module, event_data, dispatchers_steamid)
|
||||
return
|
||||
|
||||
|
||||
""" these will not be called directly. Always call the modules_callback and that will call this local function:
|
||||
module.callback_fail(callback_fail, module, event_data, dispatchers_steamid)
|
||||
"""
|
||||
|
||||
|
||||
def callback_success(module, event_data, dispatchers_steamid, match=None):
|
||||
pass
|
||||
|
||||
|
||||
def callback_fail(module, event_data, dispatchers_steamid):
|
||||
pass
|
||||
|
||||
|
||||
action_meta = {
|
||||
"description": "handles selecting or deselecting an element in the dom for further actions",
|
||||
"main_function": main_function,
|
||||
"callback_success": callback_success,
|
||||
"callback_fail": callback_fail,
|
||||
"requires_telnet_connection": False,
|
||||
"enabled": True
|
||||
}
|
||||
|
||||
loaded_modules_dict["module_" + module_name].register_action(action_name, action_meta)
|
||||
@@ -0,0 +1,13 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
{% set count_string = count|string %}
|
||||
<div id="{{ dom_element_id }}" class="delete_button">
|
||||
{{ construct_toggle_link(
|
||||
delete_selected_entries_active,
|
||||
label|default("delete") + " (" + count_string + ")", ['widget_event', ['dom_management', ['delete', {
|
||||
"target_module": target_module,
|
||||
"dom_element_root": dom_element_root,
|
||||
'action': dom_action,
|
||||
'confirmed': confirmed
|
||||
}]]]
|
||||
) }}
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
{% set owner = dom_element.owner|default("null") %}
|
||||
{% set identifier = dom_element.identifier|default("null") %}
|
||||
{% set dataset = dom_element.dataset|default("null") %}
|
||||
{% set dataset_original = dom_element.dataset_original|default(dataset) %}
|
||||
{{ construct_toggle_link(
|
||||
dom_element_entry_selected,
|
||||
"☑", ['widget_event', ['dom_management', ['select', {
|
||||
"dom_element_select_root": dom_element_select_root,
|
||||
"target_module": target_module,
|
||||
"dom_element_owner": owner,
|
||||
"dom_element_identifier": identifier,
|
||||
"dom_element_origin": dataset_original,
|
||||
'action': dom_action_active
|
||||
}]]],
|
||||
"☐", ['widget_event', ['dom_management', ['select', {
|
||||
"dom_element_select_root": dom_element_select_root,
|
||||
"target_module": target_module,
|
||||
"dom_element_owner": owner,
|
||||
"dom_element_identifier": identifier,
|
||||
"dom_element_origin": dataset_original,
|
||||
"action": dom_action_inactive
|
||||
}]]]
|
||||
) }}
|
||||
20
bot/modules/dom_management/templates/jinja2_macros.html
Normal file
20
bot/modules/dom_management/templates/jinja2_macros.html
Normal file
@@ -0,0 +1,20 @@
|
||||
{%- macro construct_toggle_link(bool, active_text, deactivate_event, inactive_text, activate_event) -%}
|
||||
{%- set bool = bool|default(false) -%}
|
||||
{%- set active_text = active_text|default(none) -%}
|
||||
{%- set deactivate_event = deactivate_event|default(none) -%}
|
||||
{%- set inactive_text = inactive_text|default(none) -%}
|
||||
{%- set activate_event = activate_event|default(none) -%}
|
||||
{%- if bool == true -%}
|
||||
{%- if deactivate_event != none and activate_event != none -%}
|
||||
<span class="active"><a href="#" onclick="window.socket.emit('{{ deactivate_event[0] }}', {{ deactivate_event[1] }}); return false;">{{ active_text }}</a></span>
|
||||
{%- elif deactivate_event != none and activate_event == none -%}
|
||||
<span class="active"><a href="#" onclick="window.socket.emit('{{ deactivate_event[0] }}', {{ deactivate_event[1] }}); return false;">{{ active_text }}</a></span>
|
||||
{%- endif -%}
|
||||
{%- else -%}
|
||||
{%- if deactivate_event != none and activate_event != none -%}
|
||||
<span class="inactive"><a href="#" onclick="window.socket.emit('{{ activate_event[0] }}', {{ activate_event[1] }}); return false;">{{ inactive_text }}</a></span>
|
||||
{%- elif deactivate_event != none and activate_event == none -%}
|
||||
<span class="inactive"><a href="#" onclick="window.socket.emit('{{ deactivate_event[0] }}', {{ deactivate_event[1] }}); return false;">{{ active_text }}</a></span>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
@@ -0,0 +1,75 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
{% set count_string = count|string %}
|
||||
<div class="delete_modal">
|
||||
<header>
|
||||
<p>Make sure you've got the right stuff selected! Deletions can not be undone.</p>
|
||||
</header>
|
||||
<div>
|
||||
<p>You have <span class="selected_dom_elements">{{ count_string }} elements</span> selected for deletion:</p>
|
||||
</div>
|
||||
<div class="dynamic_content_size">
|
||||
<p>This will soon be an actual list of actual elements to delete</p>
|
||||
<ul>
|
||||
<li>Element 1</li>
|
||||
<li>Element 2</li>
|
||||
<li>Element 3</li>
|
||||
<li>Element 4</li>
|
||||
<li>Element 5</li>
|
||||
<li>Element 6</li>
|
||||
<li>Element 7</li>
|
||||
<li>Element 8</li>
|
||||
<li>Element 9</li>
|
||||
<li>Element 10</li>
|
||||
<li>Element 11</li>
|
||||
<li>Element 12</li>
|
||||
<li>Element 13</li>
|
||||
<li>Element 14</li>
|
||||
<li>Element 15</li>
|
||||
<li>Element 16</li>
|
||||
<li>Element 17</li>
|
||||
<li>Element 18</li>
|
||||
<li>Element 19</li>
|
||||
<li>Element 20</li>
|
||||
<li>Element 21</li>
|
||||
<li>Element 22</li>
|
||||
<li>Element 23</li>
|
||||
<li>Element 24</li>
|
||||
<li>Element 25</li>
|
||||
<li>Element 26</li>
|
||||
<li>Element 27</li>
|
||||
<li>Element 28</li>
|
||||
<li>Element 29</li>
|
||||
<li>Element 30</li>
|
||||
<li>Element 31</li>
|
||||
<li>Element 32</li>
|
||||
<li>Element 33</li>
|
||||
<li>Element 34</li>
|
||||
<li>Element 35</li>
|
||||
<li>Element 36</li>
|
||||
<li>Element 37</li>
|
||||
<li>Element 38</li>
|
||||
<li>Element 39</li>
|
||||
<li>Element 40</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<section>
|
||||
<p>
|
||||
By clicking [confirm] you will continue to proceed deleting
|
||||
<span class="selected_dom_elements">{{ count_string }} elements</span>.
|
||||
</p>
|
||||
<p>
|
||||
{% include "modal_confirm_delete_confirm_button.html" %}
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<p>
|
||||
Clicking [cancel] will abort the deletion process,
|
||||
it will keep the selection intact.
|
||||
</p>
|
||||
<p>
|
||||
{% include "modal_confirm_delete_cancel_button.html" %}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
<div id="{{ dom_element_id }}_cancel" class="modal_cancel_button">
|
||||
{{ construct_toggle_link(
|
||||
delete_selected_entries_active,
|
||||
label|default("cancel"), ['widget_event', ['dom_management', ['delete', {
|
||||
"target_module": target_module,
|
||||
"dom_element_root": dom_element_root,
|
||||
'action': "cancel_delete_selected_dom_elements"
|
||||
}]]]
|
||||
) }}
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
{%- from 'jinja2_macros.html' import construct_toggle_link with context -%}
|
||||
<div id="{{ dom_element_id }}_confirm" class="modal_delete_button">
|
||||
{{ construct_toggle_link(
|
||||
delete_selected_entries_active,
|
||||
label|default("confirm"), ['widget_event', ['dom_management', ['delete', {
|
||||
"target_module": target_module,
|
||||
"dom_element_root": dom_element_root,
|
||||
'action': dom_action,
|
||||
'confirmed': confirmed
|
||||
}]]]
|
||||
) }}
|
||||
</div>
|
||||
Reference in New Issue
Block a user