mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4001f54bb | ||
|
|
47d1740fdd | ||
|
|
03d1e0e097 | ||
|
|
08ee4adddd | ||
|
|
1a221fabc9 | ||
|
|
f412ca0636 | ||
|
|
6beb2584e5 | ||
|
|
8419750bdd | ||
|
|
9364cea706 | ||
|
|
6ceff80ec5 | ||
|
|
cc9e4c722a | ||
|
|
5e687e1bdb | ||
|
|
d955e058e1 | ||
|
|
0615611a49 | ||
|
|
59243813a8 | ||
|
|
195206c699 | ||
|
|
2f170fb156 | ||
|
|
e7764324dc | ||
|
|
958444984a | ||
|
|
594af4903e | ||
|
|
dd4de7c5a3 | ||
|
|
1fa49a7730 | ||
|
|
7c70d435e4 | ||
|
|
2e159635c6 | ||
|
|
23aa1e4e85 | ||
|
|
a99dbaef78 | ||
|
|
06a1fa3512 | ||
|
|
e2270b4439 | ||
|
|
4c790c6ff2 | ||
|
|
07432daa28 | ||
|
|
54901be437 | ||
|
|
fb1e73d7d2 | ||
|
|
1401a24533 | ||
|
|
27ae11c1bc | ||
|
|
8817f8d0e9 | ||
|
|
4f9e8c5ecd | ||
|
|
c201e98f5b | ||
|
|
7cd76178b1 | ||
|
|
bcf4144364 | ||
|
|
78352f77b7 | ||
|
|
e38d2f9055 | ||
|
|
a66df76f74 | ||
|
|
a6d3c92d2a | ||
|
|
9720a15e91 | ||
|
|
6a0033da75 | ||
|
|
6fcdaf8843 | ||
|
|
97fc553278 | ||
|
|
bb57080b06 | ||
|
|
1310a4b751 | ||
|
|
1c7e729036 | ||
|
|
d7af580488 | ||
|
|
37e163e883 | ||
|
|
785f7c03bf | ||
|
|
849c39d75d | ||
|
|
c57e3e7557 | ||
|
|
ddc747e192 | ||
|
|
92801d6ddc | ||
|
|
b6c464be6d | ||
|
|
be81668d6d | ||
|
|
8e85abfda4 | ||
|
|
5559194617 | ||
|
|
1cc5cd56f8 | ||
|
|
9d4759898d | ||
|
|
178ff30938 | ||
|
|
a44575926f | ||
|
|
6a367c826e | ||
|
|
83336d3289 | ||
|
|
12ba6fbdad | ||
|
|
17a4656c41 | ||
|
|
7ef3fe5ac0 | ||
|
|
06c7ffa39e | ||
|
|
9ac0163f20 | ||
|
|
0126e448cc | ||
|
|
2362622cd0 | ||
|
|
ca2df744df | ||
|
|
9420c41e7c | ||
|
|
a015466c7f | ||
|
|
89f2c28046 | ||
|
|
57d0680b6a | ||
|
|
ddd405f379 | ||
|
|
3c38909b57 | ||
|
|
e830d1718e | ||
|
|
0ab78ffab7 | ||
|
|
9685784452 | ||
|
|
2a9085151f | ||
|
|
c6fb35838a | ||
|
|
588ddf3cb3 | ||
|
|
84f96d72c8 | ||
|
|
ed2ba9a435 | ||
|
|
265d313719 | ||
|
|
21ebc55335 | ||
|
|
a7bfc6f6f6 | ||
|
|
369fc44183 | ||
|
|
6a2a56e059 | ||
|
|
b0008ebd3f | ||
|
|
c624bfeae0 | ||
|
|
624d7499a5 | ||
|
|
4f5fbb1316 | ||
|
|
7d715493a6 | ||
|
|
0c92bf8d0a | ||
|
|
ea51b93263 | ||
|
|
539556e01d | ||
|
|
43e9fc324f | ||
|
|
e6e4620e13 | ||
|
|
6963e0b507 | ||
|
|
460f8038f2 | ||
|
|
ee2e228e15 | ||
|
|
15ab54f5d5 | ||
|
|
2226b9ff39 | ||
|
|
d4b701653e | ||
|
|
34f5658516 | ||
|
|
ea7dfa832d | ||
|
|
9bbd549d93 | ||
|
|
7424cf4645 | ||
|
|
19f767a887 | ||
|
|
57d9024ed3 | ||
|
|
87dd1cdf2d | ||
|
|
fdf381d565 | ||
|
|
20e29ecd15 | ||
|
|
a6ce702487 |
@@ -5,9 +5,16 @@
|
||||
.gitignore
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
Dockerfile.debian
|
||||
dockerfiles/LICENSE
|
||||
dockerfiles/README.md
|
||||
dockerfiles/README_ES.md
|
||||
docs
|
||||
LICENSE.txt
|
||||
README.md
|
||||
CONTRIBUTING
|
||||
FUNDING.yml
|
||||
config/.gitignore
|
||||
db/.gitignore
|
||||
pialert/README.md
|
||||
pialert/README_ES.md
|
||||
|
||||
2
.github/workflows/docker_prod.yml
vendored
2
.github/workflows/docker_prod.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
tags:
|
||||
- '*.*.*'
|
||||
- '*.[1-9]+[0-9]?.[1-9]+*'
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
75
Dockerfile
75
Dockerfile
@@ -1,50 +1,53 @@
|
||||
FROM debian:bookworm-slim
|
||||
FROM alpine:3.19 as builder
|
||||
|
||||
# default UID and GID
|
||||
ENV USER=pi USER_ID=1000 USER_GID=1000 PORT=20211
|
||||
#TZ=Europe/London
|
||||
ARG INSTALL_DIR=/home/pi
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
# Todo, figure out why using a workdir instead of full paths don't work
|
||||
# Todo, do we still need all these packages? I can already see sudo which isn't needed
|
||||
RUN apk add --no-cache bash python3 \
|
||||
&& python -m venv /opt/venv
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install sudo -y
|
||||
# Enable venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
COPY . ${INSTALL_DIR}/pialert/
|
||||
|
||||
# create pi user and group
|
||||
# add root and www-data to pi group so they can r/w files and db
|
||||
RUN groupadd --gid "${USER_GID}" "${USER}" && \
|
||||
useradd \
|
||||
--uid ${USER_ID} \
|
||||
--gid ${USER_GID} \
|
||||
--create-home \
|
||||
--shell /bin/bash \
|
||||
${USER} && \
|
||||
usermod -a -G ${USER_GID} root && \
|
||||
usermod -a -G ${USER_GID} www-data
|
||||
RUN pip install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet \
|
||||
&& bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \
|
||||
&& bash -c "find ${INSTALL_DIR} -type f -exec chmod 640 {} \;" \
|
||||
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'pialert-cli' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
|
||||
|
||||
COPY --chmod=775 --chown=${USER_ID}:${USER_GID} . /home/pi/pialert/
|
||||
# second stage
|
||||
FROM alpine:3.19 as runner
|
||||
|
||||
ARG INSTALL_DIR=/home/pi
|
||||
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
|
||||
# Enable venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
# default port and listen address
|
||||
ENV PORT=20211 LISTEN_ADDR=0.0.0.0
|
||||
|
||||
# needed for s6-overlay
|
||||
ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
|
||||
|
||||
# ❗ IMPORTANT - if you modify this file modify the /install/install_dependecies.sh file as well ❗
|
||||
|
||||
RUN apt-get install -y \
|
||||
tini snmp ca-certificates curl libwww-perl arp-scan perl apt-utils cron sudo \
|
||||
nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools \
|
||||
python3 iproute2 nmap python3-pip zip systemctl usbutils traceroute
|
||||
RUN apk update --no-cache \
|
||||
&& apk add --no-cache bash zip lsblk gettext-envsubst sudo mtr s6-overlay \
|
||||
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap traceroute net-tools net-snmp-tools bind-tools awake ca-certificates \
|
||||
&& apk add --no-cache sqlite php82 php82-fpm php82-cgi php82-curl php82-sqlite3 php82-session \
|
||||
&& apk add --no-cache python3 nginx \
|
||||
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \
|
||||
&& bash -c "install -d -m 750 -o nginx -g www-data ${INSTALL_DIR} ${INSTALL_DIR}/pialert" \
|
||||
&& rm -f /etc/nginx/http.d/default.conf
|
||||
|
||||
# Alternate dependencies
|
||||
RUN apt-get install nginx nginx-core mtr php-fpm php8.2-fpm php-cli php8.2 php8.2-sqlite3 -y
|
||||
RUN phpenmod -v 8.2 sqlite3
|
||||
COPY --from=builder --chown=nginx:www-data ${INSTALL_DIR}/pialert/ ${INSTALL_DIR}/pialert/
|
||||
|
||||
# Setup virtual python environment and use pip3 to install packages
|
||||
RUN apt-get install -y python3-venv
|
||||
RUN python3 -m venv myenv
|
||||
RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet"
|
||||
|
||||
# Create a buildtimestamp.txt to later check if a new version was released
|
||||
RUN date +%s > /home/pi/pialert/front/buildtimestamp.txt
|
||||
|
||||
CMD ["/home/pi/pialert/dockerfiles/start.sh"]
|
||||
RUN ${INSTALL_DIR}/pialert/dockerfiles/pre-setup.sh
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=2 \
|
||||
CMD curl -sf -o /dev/null ${LISTEN_ADDR}:${PORT}/api/app_state.json
|
||||
|
||||
ENTRYPOINT ["/init"]
|
||||
|
||||
50
Dockerfile.debian
Executable file
50
Dockerfile.debian
Executable file
@@ -0,0 +1,50 @@
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# default UID and GID
|
||||
ENV USER=pi USER_ID=1000 USER_GID=1000 PORT=20211
|
||||
#TZ=Europe/London
|
||||
|
||||
# Todo, figure out why using a workdir instead of full paths don't work
|
||||
# Todo, do we still need all these packages? I can already see sudo which isn't needed
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install sudo -y
|
||||
|
||||
|
||||
# create pi user and group
|
||||
# add root and www-data to pi group so they can r/w files and db
|
||||
RUN groupadd --gid "${USER_GID}" "${USER}" && \
|
||||
useradd \
|
||||
--uid ${USER_ID} \
|
||||
--gid ${USER_GID} \
|
||||
--create-home \
|
||||
--shell /bin/bash \
|
||||
${USER} && \
|
||||
usermod -a -G ${USER_GID} root && \
|
||||
usermod -a -G ${USER_GID} www-data
|
||||
|
||||
COPY --chmod=775 --chown=${USER_ID}:${USER_GID} . /home/pi/pialert/
|
||||
|
||||
|
||||
# ❗ IMPORTANT - if you modify this file modify the /install/install_dependecies.debian.sh file as well ❗
|
||||
|
||||
RUN apt-get install -y \
|
||||
tini snmp ca-certificates curl libwww-perl arp-scan perl apt-utils cron sudo \
|
||||
nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools \
|
||||
python3 iproute2 nmap python3-pip zip systemctl usbutils traceroute
|
||||
|
||||
# Alternate dependencies
|
||||
RUN apt-get install nginx nginx-core mtr php-fpm php8.2-fpm php-cli php8.2 php8.2-sqlite3 -y
|
||||
RUN phpenmod -v 8.2 sqlite3
|
||||
|
||||
# Setup virtual python environment and use pip3 to install packages
|
||||
RUN apt-get install -y python3-venv
|
||||
RUN python3 -m venv myenv
|
||||
RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet"
|
||||
|
||||
# Create a buildtimestamp.txt to later check if a new version was released
|
||||
RUN date +%s > /home/pi/pialert/front/buildtimestamp.txt
|
||||
|
||||
CMD ["/home/pi/pialert/install/start.debian.sh"]
|
||||
|
||||
|
||||
27
README.md
27
README.md
@@ -1,11 +1,11 @@
|
||||
# 💻🔍 Network security scanner & notification framework
|
||||
|
||||
Get visibility of what's going on on your WIFI/LAN network. Scan for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugins](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins#readme) with auto-generated UI and in-build notification system.
|
||||
Get visibility of what's going on on your WIFI/LAN network. Schedule scans for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugins](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins#readme) with auto-generated UI and in-build notification system.
|
||||
|
||||
[](https://github.com/jokob-sk/Pi.Alert)
|
||||
[](https://hub.docker.com/r/jokobsk/pi.alert)
|
||||
[](https://hub.docker.com/r/jokobsk/pi.alert)
|
||||

|
||||
[](https://github.com/jokob-sk/Pi.Alert/releases)
|
||||
[](https://github.com/sponsors/jokob-sk)
|
||||
|
||||
| 🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/pi.alert) | 📑 [Docker guide](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md) |🆕 [Release notes](https://github.com/jokob-sk/Pi.Alert/releases) | 📚 [All Docs](https://github.com/jokob-sk/Pi.Alert/tree/main/docs) |
|
||||
@@ -35,7 +35,7 @@ Get visibility of what's going on on your WIFI/LAN network. Scan for devices, po
|
||||
|
||||
PiAlert combines several network and other scanning tools 🔍 with notifications 📧 into one user-friendly package 📦.
|
||||
|
||||
Setup a _kill switch ☠_ for your network via a smart plug with the available [Home Assistant](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/HOME_ASSISTANT.md) integration. Implement custom automations with the [CSV device Exports 📤](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins/csv_backup), [Webhooks](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_N8N.md), or [API endpoints](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md) features.
|
||||
Set up a _kill switch ☠_ for your network via a smart plug with the available [Home Assistant](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/HOME_ASSISTANT.md) integration. Implement custom automations with the [CSV device Exports 📤](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins/csv_backup), [Webhooks](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_N8N.md), or [API endpoints](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md) features.
|
||||
|
||||
Extend the app if you want to create your own scanner [Plugin](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins#readme) and handle the results and notifications in PiAlert.
|
||||
|
||||
@@ -75,7 +75,6 @@ Get visibility of what's going on on your WIFI/LAN network. Scan for devices, po
|
||||
- Regular updates to keep your data and family safe 🔄
|
||||
- Better and more functionality➕
|
||||
- Quicker and better support with issues 🆘
|
||||
- Less grumpy me 😄
|
||||
|
||||
| [](https://github.com/sponsors/jokob-sk) | [](https://www.buymeacoffee.com/jokobsk) | [](https://www.patreon.com/user?u=84385063) |
|
||||
| --- | --- | --- |
|
||||
@@ -103,11 +102,21 @@ Thank you to all the wonderful people who are sponsoring this project (=preventi
|
||||
<!-- SPONSORS-LIST DO NOT MODIFY BELOW -->
|
||||
| All Sponsors |
|
||||
|---|
|
||||
| [dtech77pl](https://github.com/dtech77pl) |
|
||||
| [Tony Hanratty](https://github.com/thanratty) |
|
||||
|
||||
<!-- SPONSORS-LIST DO NOT MODIFY ABOVE -->
|
||||
|
||||
### 🙏Contributors
|
||||
|
||||
This project would be nothing without the amazing work of the community, with special thanks to:
|
||||
|
||||
> [pucherot/Pi.Alert](https://github.com/pucherot/Pi.Alert) (the original creator of PiAlert), [leiweibau](https://github.com/leiweibau/Pi.Alert): Dark mode (and much more), [Macleykun](https://github.com/Macleykun) (Help with Dockerfile clean-up) [Final-Hawk](https://github.com/Final-Hawk) (Help with NTFY, styling and other fixes), [TeroRERO](https://github.com/terorero) (Spanish translations), [Data-Monkey](https://github.com/Data-Monkey), (Split-up of the python.py file and more), [cvc90](https://github.com/cvc90) (Spanish translation and various UI work) to name a few...
|
||||
|
||||
Here is everyone that helped and contributed to this project:
|
||||
|
||||
<a href="https://github.com/jokob-sk/pi.alert/graphs/contributors">
|
||||
<img src="https://contri-graphy.yourselfhosted.com/graph?repo=jokob-sk/pi.alert&format=svg" />
|
||||
</a>
|
||||
|
||||
## Everything else
|
||||
<!--- --------------------------------------------------------------------- --->
|
||||
|
||||
@@ -124,13 +133,7 @@ Help out and suggest languages in the [online portal of Weblate](https://hosted.
|
||||
### License
|
||||
> GPL 3.0 | [Read more here](LICENSE.txt) | Source of the [animated GIF (Loading Animation)](https://commons.wikimedia.org/wiki/File:Loading_Animation.gif) | Source of the [selfhosted Fonts](https://github.com/adobe-fonts/source-sans)
|
||||
|
||||
### Special thanks
|
||||
|
||||
This code is a collaborative body of work, with special thanks to:
|
||||
|
||||
> [pucherot/Pi.Alert](https://github.com/pucherot/Pi.Alert) (the original creator of PiAlert), [leiweibau](https://github.com/leiweibau/Pi.Alert): Dark mode (and much more), [Macleykun](https://github.com/Macleykun) (Help with Dockerfile clean-up) [Final-Hawk](https://github.com/Final-Hawk) (Help with NTFY, styling and other fixes), [TeroRERO](https://github.com/terorero) (Spanish translations), [Data-Monkey](https://github.com/Data-Monkey), (Split-up of the python.py file and more), [cvc90](https://github.com/cvc90) (Spanish translation and various UI work) to name a few...
|
||||
>
|
||||
> Please see the [Git contributors](https://github.com/jokob-sk/Pi.Alert/graphs/contributors) for a full list of people and their contributions to the project
|
||||
|
||||
<!--- --------------------------------------------------------------------- --->
|
||||
[main]: ./docs/img/devices_split.png "Main screen"
|
||||
|
||||
@@ -11,12 +11,12 @@ services:
|
||||
network_mode: host
|
||||
# restart: unless-stopped
|
||||
volumes:
|
||||
- ${APP_DATA_LOCATION}/pialert_dev/config:/home/pi/pialert/config
|
||||
# - ${APP_DATA_LOCATION}/pialert/config:/home/pi/pialert/config
|
||||
- ${APP_DATA_LOCATION}/pialert_dev/db:/home/pi/pialert/db
|
||||
# - ${APP_DATA_LOCATION}/pialert/db:/home/pi/pialert/db
|
||||
# - ${APP_DATA_LOCATION}/pialert_dev/config:/home/pi/pialert/config
|
||||
- ${APP_DATA_LOCATION}/pialert/config:/home/pi/pialert/config
|
||||
# - ${APP_DATA_LOCATION}/pialert_dev/db:/home/pi/pialert/db
|
||||
- ${APP_DATA_LOCATION}/pialert/db:/home/pi/pialert/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- ${LOGS_LOCATION}:/home/pi/pialert/front/log
|
||||
# - ${LOGS_LOCATION}:/home/pi/pialert/front/log
|
||||
# ---------------------------------------------------------------------------
|
||||
# DELETE START anyone trying to use this file: comment out / delete BELOW lines, they are only for development purposes
|
||||
- ${APP_DATA_LOCATION}/pialert/dhcp_samples/dhcp1.leases:/mnt/dhcp1.leases
|
||||
@@ -32,10 +32,10 @@ services:
|
||||
- ${DEV_LOCATION}/back/update_vendors.sh:/home/pi/pialert/back/update_vendors.sh
|
||||
- ${DEV_LOCATION}/front/lib/AdminLTE:/home/pi/pialert/front/lib/AdminLTE
|
||||
- ${DEV_LOCATION}/front/js:/home/pi/pialert/front/js
|
||||
- ${DEV_LOCATION}/dockerfiles/start.sh:/home/pi/pialert/dockerfiles/start.sh
|
||||
- ${DEV_LOCATION}/dockerfiles/user-mapping.sh:/home/pi/pialert/dockerfiles/user-mapping.sh
|
||||
- ${DEV_LOCATION}/install/install.sh:/home/pi/pialert/install/install.sh
|
||||
- ${DEV_LOCATION}/install/install_dependencies.sh:/home/pi/pialert/install/install_dependencies.sh
|
||||
- ${DEV_LOCATION}/install/start.debian.sh:/home/pi/pialert/install/start.debian.sh
|
||||
- ${DEV_LOCATION}/install/user-mapping.debian.sh:/home/pi/pialert/install/user-mapping.debian.sh
|
||||
- ${DEV_LOCATION}/install/install.debian.sh:/home/pi/pialert/install/install.debian.sh
|
||||
- ${DEV_LOCATION}/install/install_dependencies.debian.sh:/home/pi/pialert/install/install_dependencies.debian.sh
|
||||
- ${DEV_LOCATION}/front/api:/home/pi/pialert/front/api
|
||||
- ${DEV_LOCATION}/front/php:/home/pi/pialert/front/php
|
||||
- ${DEV_LOCATION}/front/deviceDetails.php:/home/pi/pialert/front/deviceDetails.php
|
||||
@@ -54,6 +54,7 @@ services:
|
||||
- ${DEV_LOCATION}/front/report.php:/home/pi/pialert/front/report.php
|
||||
- ${DEV_LOCATION}/front/workflows.php:/home/pi/pialert/front/workflows.php
|
||||
- ${DEV_LOCATION}/front/appEventsCore.php:/home/pi/pialert/front/appEventsCore.php
|
||||
- ${DEV_LOCATION}/front/multiEditCore.php:/home/pi/pialert/front/multiEditCore.php
|
||||
- ${DEV_LOCATION}/front/donations.php:/home/pi/pialert/front/donations.php
|
||||
- ${DEV_LOCATION}/front/plugins:/home/pi/pialert/front/plugins
|
||||
# DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes
|
||||
@@ -63,3 +64,5 @@ services:
|
||||
- PORT=${PORT}
|
||||
- HOST_USER_ID=${HOST_USER_ID}
|
||||
- HOST_USER_GID=${HOST_USER_GID}
|
||||
# ❗ DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders
|
||||
- ALWAYS_FRESH_INSTALL=${ALWAYS_FRESH_INSTALL}
|
||||
|
||||
@@ -42,12 +42,16 @@ docker run -d --rm --network=host \
|
||||
|`TZ` |Time zone to display stats correctly. Find your time zone [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `Europe/Berlin` |
|
||||
|`HOST_USER_GID` |User ID (UID) to map the user in the container to a server user with sufficient read&write permissions on the mapped files | `1000` |
|
||||
|`HOST_USER_ID` |User Group ID (GID) to map the user group in the container to a server user group with sufficient read&write permissions on the mapped files | `1000` |
|
||||
|`ALWAYS_FRESH_INSTALL` | Setting `ALWAYS_FRESH_INSTALL=true` will delete the content of the `/db` & `/config` folders. For testing purposes. Can be coupled with [watchtower](https://github.com/containrrr/watchtower) to have an always freshly installed `pi.alert`/`_dev` image. | `N/A` |
|
||||
|
||||
### Docker paths
|
||||
|
||||
> [!NOTE]
|
||||
> See also [Backup strategies](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/BACKUPS.md).
|
||||
|
||||
| Required | Path | Description |
|
||||
| :------------- | :------------- | :-------------|
|
||||
| ✅ | `:/home/pi/pialert/config` | Folder which will contain the `pialert.conf` file (see below for details) |
|
||||
| ✅ | `:/home/pi/pialert/config` | Folder which will contain the `pialert.conf` & `devices.csv` ([read about devices.csv](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEVICES_BULK_EDITING.md)) files (see below for details) |
|
||||
| ✅ | `:/home/pi/pialert/db` | Folder which will contain the `pialert.db` file |
|
||||
| | `:/home/pi/pialert/front/log` | Logs folder useful for debugging if you have issues setting up the container |
|
||||
| | `:/etc/pihole/pihole-FTL.db` | PiHole's `pihole-FTL.db` database file. Required if you want to use PiHole DB mapping. |
|
||||
@@ -236,7 +240,7 @@ Courtesy of [pbek](https://github.com/pbek). The volume `pialert_db` is used by
|
||||
|
||||
## 🏅 Recognitions
|
||||
|
||||
Big thanks to <a href="https://github.com/Macleykun">@Macleykun</a> for help and tips&tricks for Dockerfile(s).
|
||||
Big thanks to <a href="https://github.com/Macleykun">@Macleykun</a> & for help and tips&tricks for Dockerfile(s) and <a href="https://github.com/vladaurosh">@vladaurosh</a> for Alpine re-base help.
|
||||
|
||||
## ❤ Support me
|
||||
|
||||
|
||||
31
dockerfiles/pre-setup.sh
Executable file
31
dockerfiles/pre-setup.sh
Executable file
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
export INSTALL_DIR=/home/pi
|
||||
|
||||
# php-fpm setup
|
||||
install -d -o nginx -g www-data /run/php/
|
||||
sed -i "/^;pid/c\pid = /run/php/php8.2-fpm.pid" /etc/php82/php-fpm.conf
|
||||
sed -i "/^listen/c\listen = /run/php/php8.2-fpm.sock" /etc/php82/php-fpm.d/www.conf
|
||||
sed -i "/^;listen.owner/c\listen.owner = nginx" /etc/php82/php-fpm.d/www.conf
|
||||
sed -i "/^;listen.group/c\listen.group = www-data" /etc/php82/php-fpm.d/www.conf
|
||||
sed -i "/^user/c\user = nginx" /etc/php82/php-fpm.d/www.conf
|
||||
sed -i "/^group/c\group = www-data" /etc/php82/php-fpm.d/www.conf
|
||||
|
||||
# s6 overlay setup
|
||||
mkdir -p /etc/s6-overlay/s6-rc.d/{SetupOneshot,php-fpm/dependencies.d,nginx/dependencies.d}
|
||||
mkdir -p /etc/s6-overlay/s6-rc.d/{SetupOneshot,php-fpm/dependencies.d,nginx/dependencies.d,pialert/dependencies.d}
|
||||
echo "oneshot" > /etc/s6-overlay/s6-rc.d/SetupOneshot/type
|
||||
echo "longrun" > /etc/s6-overlay/s6-rc.d/php-fpm/type
|
||||
echo "longrun" > /etc/s6-overlay/s6-rc.d/nginx/type
|
||||
echo "longrun" > /etc/s6-overlay/s6-rc.d/pialert/type
|
||||
echo -e "${INSTALL_DIR}/pialert/dockerfiles/setup.sh" > /etc/s6-overlay/s6-rc.d/SetupOneshot/up
|
||||
echo -e "#!/bin/execlineb -P\n/usr/sbin/php-fpm82 -F" > /etc/s6-overlay/s6-rc.d/php-fpm/run
|
||||
echo -e '#!/bin/execlineb -P\nnginx -g "daemon off;"' > /etc/s6-overlay/s6-rc.d/nginx/run
|
||||
echo -e '#!/bin/execlineb -P\n\nwith-contenv\nimportas -i PORT PORT\nif { echo "[INSTALL] 🚀 Starting app - navigate to your <server IP>:${PORT}" }' > /etc/s6-overlay/s6-rc.d/pialert/run
|
||||
echo -e "python ${INSTALL_DIR}/pialert/pialert" >> /etc/s6-overlay/s6-rc.d/pialert/run
|
||||
touch /etc/s6-overlay/s6-rc.d/user/contents.d/{SetupOneshot,php-fpm,nginx} /etc/s6-overlay/s6-rc.d/{php-fpm,nginx}/dependencies.d/SetupOneshot
|
||||
touch /etc/s6-overlay/s6-rc.d/user/contents.d/{SetupOneshot,php-fpm,nginx,pialert} /etc/s6-overlay/s6-rc.d/{php-fpm,nginx,pialert}/dependencies.d/SetupOneshot
|
||||
touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/php-fpm
|
||||
touch /etc/s6-overlay/s6-rc.d/pialert/dependencies.d/nginx
|
||||
|
||||
rm -f $0
|
||||
79
dockerfiles/setup.sh
Executable file
79
dockerfiles/setup.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/with-contenv bash
|
||||
|
||||
echo "---------------------------------------------------------"
|
||||
echo "[INSTALL] Run setup.sh"
|
||||
echo "---------------------------------------------------------"
|
||||
|
||||
export INSTALL_DIR=/home/pi # Specify the installation directory here
|
||||
|
||||
# DO NOT CHANGE ANYTHING BELOW THIS LINE!
|
||||
NGINX_CONFIG_FILE=/etc/nginx/http.d/pialert.conf
|
||||
OUI_FILE="/usr/share/arp-scan/ieee-oui.txt" # Define the path to ieee-oui.txt and ieee-iab.txt
|
||||
FILEDB="${INSTALL_DIR}/pialert/db/pialert.db"
|
||||
# DO NOT CHANGE ANYTHING ABOVE THIS LINE!
|
||||
|
||||
# Check if script is run as root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
echo "This script must be run as root. Please use 'sudo'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[INSTALL] Copy starter pialert.db and pialert.conf if they don't exist"
|
||||
|
||||
# DANGER ZONE: ALWAYS_FRESH_INSTALL
|
||||
if [ "$ALWAYS_FRESH_INSTALL" = true ]; then
|
||||
echo "[INSTALL] ❗ ALERT /db and /config folders are cleared because the ALWAYS_FRESH_INSTALL is set to: $ALWAYS_FRESH_INSTALL❗"
|
||||
# Delete content of "$INSTALL_DIR/pialert/config/"
|
||||
rm -rf "$INSTALL_DIR/pialert/config/"*
|
||||
|
||||
# Delete content of "$INSTALL_DIR/pialert/db/"
|
||||
rm -rf "$INSTALL_DIR/pialert/db/"*
|
||||
fi
|
||||
|
||||
# Copy starter pialert.db and pialert.conf if they don't exist
|
||||
cp -na "${INSTALL_DIR}/pialert/back/pialert.conf" "${INSTALL_DIR}/pialert/config/pialert.conf"
|
||||
cp -na "${INSTALL_DIR}/pialert/back/pialert.db" "${FILEDB}"
|
||||
|
||||
# if custom variables not set we do not need to do anything
|
||||
if [ -n "${TZ}" ]; then
|
||||
FILECONF="${INSTALL_DIR}/pialert/config/pialert.conf"
|
||||
echo "[INSTALL] Setup timezone"
|
||||
sed -i "\#^TIMEZONE=#c\TIMEZONE='${TZ}'" "${FILECONF}"
|
||||
fi
|
||||
|
||||
echo "[INSTALL] Setup NGINX"
|
||||
echo "Setting webserver to address ($LISTEN_ADDR) and port ($PORT)"
|
||||
envsubst '$INSTALL_DIR $LISTEN_ADDR $PORT' < "${INSTALL_DIR}/pialert/install/pialert.template.conf" > "${NGINX_CONFIG_FILE}"
|
||||
|
||||
# Run the hardware vendors update at least once
|
||||
echo "[INSTALL] Run the hardware vendors update"
|
||||
|
||||
# Check if ieee-oui.txt or ieee-iab.txt exist
|
||||
if [ -f "${OUI_FILE}" ]; then
|
||||
echo "The file ieee-oui.txt exists. Skipping update_vendors.sh..."
|
||||
else
|
||||
echo "The file ieee-oui.txt does not exist. Running update_vendors..."
|
||||
|
||||
# Run the update_vendors.sh script
|
||||
if [ -f "${INSTALL_DIR}/pialert/back/update_vendors.sh" ]; then
|
||||
"${INSTALL_DIR}/pialert/back/update_vendors.sh"
|
||||
else
|
||||
echo "update_vendors.sh script not found in ${INSTALL_DIR}."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create an empty log files
|
||||
# Create the execution_queue.log and pialert_front.log files if they don't exist
|
||||
touch "${INSTALL_DIR}"/pialert/front/log/{execution_queue.log,pialert_front.log,pialert.php_errors.log,stderr.log,stdout.log}
|
||||
|
||||
echo "[INSTALL] Fixing permissions after copied starter config & DB"
|
||||
chown -R nginx:www-data "${INSTALL_DIR}"/pialert/{config,front/log,db}
|
||||
chmod 750 "${INSTALL_DIR}"/pialert/{config,front/log,db}
|
||||
chmod 640 "${INSTALL_DIR}"/pialert/{config,front/log,db}/*
|
||||
|
||||
# Check if buildtimestamp.txt doesn't exist
|
||||
if [ ! -f "${INSTALL_DIR}/pialert/front/buildtimestamp.txt" ]; then
|
||||
# Create buildtimestamp.txt
|
||||
date +%s > "${INSTALL_DIR}/pialert/front/buildtimestamp.txt"
|
||||
chown nginx:www-data "${INSTALL_DIR}/pialert/front/buildtimestamp.txt"
|
||||
fi
|
||||
82
docs/BACKUPS.md
Executable file
82
docs/BACKUPS.md
Executable file
@@ -0,0 +1,82 @@
|
||||
# 💾 Backing things up
|
||||
|
||||
> [!NOTE]
|
||||
> To backup 99% of your configuration backup at least the `/config` folder. Please read the whole page (or at least "Scenario 2: Corrupted database") for details.
|
||||
|
||||
There are 3 artifacts that can be used to backup the application:
|
||||
|
||||
| File | Description | Limitations |
|
||||
|-----------------------|-------------------------------|-------------------------------|
|
||||
| `/db/pialert.db` | Database file(s) | The database file might be in an uncommitted state or corrupted |
|
||||
| `/config/pialert.conf` | Configuration file | Doesn't contain settings from the Maintenance section |
|
||||
| `/config/devices.csv` | CSV file containing device information | Doesn't contain historical data |
|
||||
|
||||
## Data and cackup storage
|
||||
|
||||
To decide on a backup strategy, check where the data is stored:
|
||||
|
||||
### Core Configuration
|
||||
|
||||
The core application configuration is in the `pialert.conf` file (See [Settings System](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SETTINGS_SYSTEM.md) for details), such as:
|
||||
|
||||
- Notification settings
|
||||
- Scanner settings
|
||||
- Scheduled maintenance settings
|
||||
- UI configuration (80%)
|
||||
|
||||
### Core Device Data
|
||||
|
||||
The core device data is backed up to the `devices_<timestamp>.csv` file via the [CSV Backup `CSVBCKP` Plugin](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins/csv_backup). This file contains data, such as:
|
||||
|
||||
- Device names
|
||||
- Device Icons
|
||||
- Device Network configuration
|
||||
- Device categorization
|
||||
|
||||
### Historical data
|
||||
|
||||
Historical data is stored in the `pialert.db` database (See [Database overview](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DATABASE.md) for details). This data includes:
|
||||
|
||||
- Plugin objects
|
||||
- Plugin historical entries
|
||||
- History of Events, Notifications, Workflow Events
|
||||
- Presence History
|
||||
|
||||
## 🧭 Backup strategies
|
||||
|
||||
The safest approach to backups is to backup all of the above, by taking regular file system backups (I use [Kopia](https://github.com/kopia/kopia)).
|
||||
|
||||
Arguably, the most time is spent setting up the device list, so if only one file is kept I'd recommend to have a latest backup of the `devices_<timestamp>.csv` file, followed by the `pialert.conf` file.
|
||||
|
||||
### Scenario 1: Full backup
|
||||
|
||||
End-result: Full restore
|
||||
|
||||
#### Source artifacts:
|
||||
|
||||
- `/db/pialert.db` (uncorrupted)
|
||||
- `/config/pialert.conf`
|
||||
|
||||
#### Recovery:
|
||||
|
||||
To restore the application map the above files as described in the [Setup documentation](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md#docker-paths).
|
||||
|
||||
|
||||
### Scenario 2: Corrupted database
|
||||
|
||||
End-result: Partial restore (historical data & configurations from the Maintenance section will be missing)
|
||||
|
||||
#### Source artifacts:
|
||||
|
||||
- `/config/pialert.conf`
|
||||
- `/config/devices_<timestamp>.csv` or `/config/devices.csv`
|
||||
|
||||
#### Recovery:
|
||||
|
||||
Even with a corrupted database you can recover what I would argue is 99% of the configuration (except of a couple of settings under Maintenance).
|
||||
|
||||
- map the `/config/pialert.conf` file as described in the [Setup documentation](https://github.com/jokob-sk/Pi.Alert/blob/main/dockerfiles/README.md#docker-paths).
|
||||
- rename the `devices_<timestamp>.csv` to `devices.csv` and place it in the `/config` folder
|
||||
- Restore the `devices.csv` backup via the [Maintenance section](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEVICES_BULK_EDITING.md)
|
||||
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
# 🖊 Multi-editing via the UI
|
||||
|
||||
> [!NOTE]
|
||||
> Make sure you have your backups saved and restorable before doing any mass edits. Check [Backup strategies](/docs/BACKUPS.md).
|
||||
|
||||
You can select devices in the _Devices_ view by selecting devices to edit and then clicking the _Multi-edit_ button or via the _Maintenance_ > _Multi-Edit_ section.
|
||||
|
||||

|
||||
|
||||
|
||||
# 📝Bulk-edit devices via CSV Export/Import
|
||||
|
||||
> [!NOTE]
|
||||
> As always, backup everything, just in case.
|
||||
|
||||
1. In `Maintenance` > `Backup / Restore` click the `CSV Export` button.
|
||||
1. In _Maintenance_ > _Backup / Restore_ click the _CSV Export_ button.
|
||||
2. A `devices.csv` is generated in the `/config` folder
|
||||
3. Edit the `devices.csv` file however you like.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ To edit device information:
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> [Bulk-edit devices](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEVICES_BULK_EDITING.md) by using the `CSV Export` functionality in the `Maintenance` section.
|
||||
> [Bulk-edit devices](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEVICES_BULK_EDITING.md) by using the _CSV Export_ functionality in the _Maintenance_ section.
|
||||
|
||||
|
||||
![Device Details][screen1]
|
||||
|
||||
@@ -22,11 +22,12 @@ PiAlert comes with MQTT support, allowing you to show all detected devices as de
|
||||
- User
|
||||
- Password
|
||||
|
||||
4. Ope the `PiAlert` > `Settings` > `MQTT` settings group
|
||||
4. Open the _PiAlert_ > _Settings_ > _MQTT_ settings group
|
||||
- Enable MQTT
|
||||
- Fill in the details from above
|
||||
- Fill in remaining settings as per description
|
||||
|
||||
![Configuration Example][configuration]
|
||||
|
||||
## 📷 Screenshots
|
||||
|
||||
@@ -35,8 +36,9 @@ PiAlert comes with MQTT support, allowing you to show all detected devices as de
|
||||
| ![Screen 3][list] | ![Screen 4][overview] |
|
||||
|
||||
|
||||
[sensors]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Device-as-Sensors.png "sensors"
|
||||
[history]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Device-Presence-History.png "history"
|
||||
[list]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Devices-List.png "list"
|
||||
[overview]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Overview-Card.png "overview"
|
||||
[configuration]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Configuration.png "configuration"
|
||||
[sensors]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Device-as-Sensors.png "sensors"
|
||||
[history]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Device-Presence-History.png "history"
|
||||
[list]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Devices-List.png "list"
|
||||
[overview]: /docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Overview-Card.png "overview"
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ To download and install PiAlert on the hardware/server directly use the `curl` o
|
||||
A warning to the installation method below: Piping to bash is [controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash) and may
|
||||
be dangerous, as you cannot see the code that's about to be executed on your system.
|
||||
|
||||
Alternatively you can download the installation script `install/install.sh` from the repository and check the code yourself (beware other scripts are
|
||||
Alternatively you can download the installation script `install/install.debian.sh` from the repository and check the code yourself (beware other scripts are
|
||||
downloaded too - only from this repo).
|
||||
|
||||
PiAlert will be installed in `home/pi/pialert/` and run on port number `20211`.
|
||||
@@ -19,15 +19,15 @@ PiAlert will be installed in `home/pi/pialert/` and run on port number `20211`.
|
||||
Some facts about what and where something will be changed/installed by the HW install setup (may not contain everything!):
|
||||
|
||||
- `/home/pi/pialert` directory will be deleted and newly created
|
||||
- `/home/pi/pialert` will contain the whole repository (downloaded by `install/install.sh`)
|
||||
- `/home/pi/pialert` will contain the whole repository (downloaded by `install/install.debian.sh`)
|
||||
- The default NGINX site `/etc/nginx/sites-enabled/default` will be disabled (sym-link deleted or backed up to `sites-available`)
|
||||
- `/var/www/html/pialert` directory will be deleted and newly created
|
||||
- `/etc/nginx/conf.d/pialert.conf` will be sym-linked to `/home/pi/pialert/install/pialert.conf`
|
||||
- `/etc/nginx/conf.d/pialert.conf` will be sym-linked to `/home/pi/pialert/install/pialert.debian.conf`
|
||||
- Some files (IEEE device vendors info, ...) will be created in the directory where the installation script is executed
|
||||
|
||||
## Limitations
|
||||
|
||||
- No system service is provided. PiAlert must be started using `/home/pi/pialert/dockerfiles/start.sh`.
|
||||
- No system service is provided. PiAlert must be started using `/home/pi/pialert/install/start.debian.sh`.
|
||||
- No checks for other running software is done.
|
||||
- Only tested to work on Debian Bookworm (Debian 12).
|
||||
- **EXPERIMENTAL** and not recommended way to install PiAlert.
|
||||
@@ -35,15 +35,15 @@ Some facts about what and where something will be changed/installed by the HW in
|
||||
## 📥 Installation via CURL
|
||||
|
||||
```bash
|
||||
curl -o install.sh https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/install/install.sh && sudo chmod +x install.sh && sudo ./install.sh
|
||||
curl -o install.debian.sh https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/install/install.debian.sh && sudo chmod +x install.debian.sh && sudo ./install.debian.sh
|
||||
```
|
||||
|
||||
## 📥 Installation via WGET
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/install/install.sh -O install.sh && sudo chmod +x install.sh && sudo ./install.sh
|
||||
wget https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/install/install.debian.sh -O install.debian.sh && sudo chmod +x install.debian.sh && sudo ./install.debian.sh
|
||||
```
|
||||
|
||||
These commands will download the `install.sh` script from the GitHub repository, make it executable with `chmod`, and then run it using `./install.sh`.
|
||||
These commands will download the `install.debian.sh` script from the GitHub repository, make it executable with `chmod`, and then run it using `./install.debian.sh`.
|
||||
|
||||
Make sure you have the necessary permissions to execute the script.
|
||||
|
||||
@@ -17,7 +17,7 @@ Make sure you have a root device with the MAC `Internet` (No other MAC addresses
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> [Bulk-edit devices](/docs/DEVICES_BULK_EDITING.md) by using the `CSV Export` functionality in the `Maintenance` section. You can use this to fix `Internet` node assignment issues.
|
||||
> [Bulk-edit devices](/docs/DEVICES_BULK_EDITING.md) by using the _CSV Export_ functionality in the _Maintenance_ section. You can use this to fix `Internet` node assignment issues.
|
||||
|
||||
## 🔍Detailed example:
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ There is also an in-app Help / FAQ section that should be answering frequently a
|
||||
- [Custom Icon configuration and support](/docs/ICONS.md)
|
||||
- [Better name resolution with Reverse DNS](/docs/REVERSE_DNS.md)
|
||||
- [Network treemap configuration](/docs/NETWORK_TREE.md)
|
||||
- [Backups](/docs/BACKUPS.md)
|
||||
|
||||
#### 🐛 Debugging help & tips
|
||||
|
||||
@@ -106,7 +107,7 @@ If you submit a PR please:
|
||||
1. Check that your changes are backward compatible with existing installations and with a blank setup.
|
||||
2. Existing features should always be preserved.
|
||||
3. Keep the PR small, on-topic and don't change code that is not necessary for the PR to work
|
||||
4. New features code should ideally be re-usable for different purposes, not be for a very narrow use-case.
|
||||
4. New features code should ideally be re-usable for different purposes, not for a very narrow use case.
|
||||
5. New functionality should ideally be implemented via the Plugins system, if possible.
|
||||
|
||||
Suggested test cases:
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
> Submitted by amazing [cvc90](https://github.com/cvc90) 🙏
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> There are 2 NGINX files for PiAlert, one for the bare-metal Debian install (`pialert.debian.conf`), and one for the docker container (`pialert.template.conf`). Both can be found in the [install](https://github.com/jokob-sk/Pi.Alert/tree/main/install) folder. Map, or use, the one appropriate for your setup.
|
||||
|
||||
## NGINX HTTP Configuration (Direct Path)
|
||||
|
||||
1. On your NGINX server, create a new file called /etc/nginx/sites-available/pialert
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This is an explanation how settings are handled intended for anyone thinking about writing their own plugin or contributing to the project.
|
||||
|
||||
If you are a user of the app, settings should be described in the `Settings` section of the app. Open an issue if you'd like to clarify any of the settings.
|
||||
If you are a user of the app, settings have a detailed description in the _Settings_ section of the app. Open an issue if you'd like to clarify any of the settings.
|
||||
|
||||
### 🛢 Data storage
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
You need to specify the network interface and the network mask. You can also configure multiple subnets and specify VLANS (see exceptions below).
|
||||
|
||||
> [!TIP]
|
||||
> You may need to increase the time between scans `ARPSCAN_RUN_SCHD` and the timeout `ARPSCAN_RUN_TIMEOUT` settings when adding more subnets. If the timeout setting is exceeded, the scan is cancelled to prevent application hanging from rogue plugins. Check [debugging plugins](/docs/DEBUG_PLUGINS.md) for more tips.
|
||||
|
||||
## Examples
|
||||
|
||||
> [!NOTE]
|
||||
@@ -12,6 +15,7 @@ You need to specify the network interface and the network mask. You can also con
|
||||
* One subnet: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0']`
|
||||
* Two subnets: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0', '192.168.1.0/24 --interface=eth1 -vlan=107']`
|
||||
|
||||
|
||||
## Explanation
|
||||
|
||||
### Network mask
|
||||
@@ -31,7 +35,10 @@ Specify the network filter (which **significantly** speeds up the scan process).
|
||||
|
||||
The adapter will probably be `eth0` or `eth1`. (Check `System info` > `Network Hardware` or run `iwconfig` in the container to find your interface name(s))
|
||||
|
||||
> Run `ip -o link show | awk -F': ' '!/lo|vir|docker/ {print $2}'` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`).
|
||||

|
||||
|
||||
> [!TIP]
|
||||
> Alterantive to `iwconfig` run `ip -o link show | awk -F': ' '!/lo|vir|docker/ {print $2}'` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`).
|
||||
|
||||
### VLANs
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
### Create a simple n8n workflow
|
||||
|
||||
N8N can be used for more advanced conditional notification use cases. For example, you want only to get notified if two out of a specified list of devices is down. Or you can use other plugins to process the notifiations further. The below is a simple example of sending an email on a webhook.
|
||||
|
||||

|
||||
|
||||
### Specify your email template
|
||||
|
||||
BIN
docs/img/DEVICES_BULK_EDITING/MULTI-EDIT.gif
Executable file
BIN
docs/img/DEVICES_BULK_EDITING/MULTI-EDIT.gif
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 443 KiB |
BIN
docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Configuration.png
Executable file
BIN
docs/img/HOME_ASISSTANT/PiAlert-HomeAssistant-Configuration.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 291 KiB |
BIN
docs/img/SUBNETS/system_info-network_hardware.png
Executable file
BIN
docs/img/SUBNETS/system_info-network_hardware.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
@@ -725,4 +725,6 @@ input[type="password"]::-webkit-caps-lock-indicator {
|
||||
}
|
||||
.pa_semitransparent-panel{
|
||||
background-color: #000 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1030,6 +1030,53 @@ input[readonly] {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* Multi-edit adjustements */
|
||||
.box-header
|
||||
{
|
||||
min-height: 55px;
|
||||
}
|
||||
|
||||
.red-hover-border:hover
|
||||
{
|
||||
border-color: red !important;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
|
||||
}
|
||||
|
||||
.red-hover-background:hover
|
||||
{
|
||||
background-color: red !important;
|
||||
}
|
||||
|
||||
#multi-edit-form .form-group
|
||||
{
|
||||
height: 45px;
|
||||
|
||||
}
|
||||
|
||||
.pia-top-left-logo
|
||||
{
|
||||
height:50px;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
Floating edit button
|
||||
----------------------------------------------------------------------------- */
|
||||
#multiEditPlc
|
||||
{
|
||||
position: fixed;
|
||||
bottom: 50px;
|
||||
right: 0px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
|
||||
table.dataTable tbody > tr.selected
|
||||
{
|
||||
color:red;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
Donations
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
@@ -843,7 +843,7 @@ function initializeCombo (dropdownId, queryAction, txtDataField, useCache) {
|
||||
// get data from server
|
||||
$.get('php/server/devices.php?action='+queryAction, function(data) {
|
||||
|
||||
console.log(data)
|
||||
// console.log(data)
|
||||
var listData = JSON.parse(data);
|
||||
var order = 1;
|
||||
|
||||
@@ -1288,6 +1288,16 @@ function getDeviceData (readAllData=false) {
|
||||
|
||||
devicesList = getDevicesList();
|
||||
|
||||
// handle empty dev_Network_Node_MAC_ADDR
|
||||
networkParentMac = deviceData['dev_Network_Node_MAC_ADDR']
|
||||
if(networkParentMac)
|
||||
{
|
||||
networkParentMacName = getDeviceDataByMacAddress(deviceData['dev_Network_Node_MAC_ADDR'], "dev_Name")
|
||||
} else
|
||||
{
|
||||
networkParentMacName = '--'
|
||||
}
|
||||
|
||||
$('#txtMAC').val (deviceData['dev_MAC']);
|
||||
$('#txtName').val (deviceData['dev_Name']);
|
||||
$('#txtOwner').val (deviceData['dev_Owner']);
|
||||
@@ -1300,7 +1310,7 @@ function getDeviceData (readAllData=false) {
|
||||
$('#txtGroup').val (deviceData['dev_Group']);
|
||||
$('#txtLocation').val (deviceData['dev_Location']);
|
||||
$('#txtComments').val (deviceData['dev_Comments']);
|
||||
$('#txtNetworkNodeMac').val ( getDeviceDataByMacAddress(deviceData['dev_Network_Node_MAC_ADDR'], "dev_Name")) ;
|
||||
$('#txtNetworkNodeMac').val ( networkParentMacName) ;
|
||||
$('#txtNetworkNodeMac').attr ('data-mynodemac', deviceData['dev_Network_Node_MAC_ADDR']);
|
||||
$('#txtNetworkPort').val (deviceData['dev_Network_Node_port']);
|
||||
// disabling network node configuration if root Internet node
|
||||
@@ -1471,23 +1481,7 @@ function setDeviceData (direction='', refreshCallback='') {
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Calls a backend function to add a front-end event to an execution queue
|
||||
function updateApi()
|
||||
{
|
||||
|
||||
// value has to be in format event|param. e.g. run|ARPSCAN
|
||||
action = `update_api|devices,appevents`
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/util.php",
|
||||
data: { function: "addToExecutionQueue", action: action },
|
||||
success: function(data, textStatus) {
|
||||
console.log(data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
beforeSend: function() { $('#scanoutput').addClass("ajax_scripts_loading"); },
|
||||
complete: function() { $('#scanoutput').removeClass("ajax_scripts_loading"); },
|
||||
success: function(data, textStatus) {
|
||||
console.log(data);
|
||||
// console.log(data);
|
||||
$("#scanoutput").html(data);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
<div class="col-md-12">
|
||||
<div class="box" id="clients">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title"><?= lang('Device_Shortcut_OnlineChart');?> </h3>
|
||||
<h3 class="box-title"><?= lang('Device_Shortcut_OnlineChart');?> </h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="chart">
|
||||
@@ -148,7 +148,9 @@
|
||||
|
||||
<!-- box-header -->
|
||||
<div class="box-header">
|
||||
<h3 id="tableDevicesTitle" class="box-title text-gray">Devices</h3>
|
||||
<div class=" col-md-10 ">
|
||||
<h3 id="tableDevicesTitle" class="box-title text-gray "></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- table -->
|
||||
@@ -173,6 +175,7 @@
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
</section>
|
||||
<!-- /.content -->
|
||||
<div id="multiEditPlc" class="col-md-2"></div>
|
||||
</div>
|
||||
<!-- /.content-wrapper -->
|
||||
|
||||
@@ -186,9 +189,10 @@
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<!-- Datatable -->
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css">
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net/css/select.dataTables.min.css">
|
||||
<script src="lib/AdminLTE/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
|
||||
<script src="lib/AdminLTE/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
|
||||
|
||||
<script src="lib/AdminLTE/bower_components/datatables.net/js/dataTables.select.min.js"></script>
|
||||
|
||||
<!-- page script ----------------------------------------------------------- -->
|
||||
<script>
|
||||
@@ -291,7 +295,8 @@ function main () {
|
||||
}
|
||||
|
||||
// Initialize components with parameters
|
||||
initializeDatatable('my');
|
||||
|
||||
initializeDatatable(getUrlAnchor('my'));
|
||||
|
||||
|
||||
|
||||
@@ -359,19 +364,19 @@ function filterDataByStatus(data, status) {
|
||||
case 'my':
|
||||
to_display = getSetting('UI_MY_DEVICES');
|
||||
|
||||
let result = false;
|
||||
let result = true;
|
||||
|
||||
if (to_display.includes('online') && item.dev_PresentLastScan === 1) {
|
||||
result = true;
|
||||
} else if (to_display.includes('offline') && item.dev_PresentLastScan === 0) {
|
||||
result = true;
|
||||
} else if (to_display.includes('archived') && item.dev_Archived === 1) {
|
||||
result = true;
|
||||
} else if (to_display.includes('new') && item.dev_NewDevice === 1) {
|
||||
result = true;
|
||||
} else if (to_display.includes('down') && item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0) {
|
||||
result = true;
|
||||
}
|
||||
if (!to_display.includes('down') && item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0) {
|
||||
result = false;
|
||||
} else if (!to_display.includes('new') && item.dev_NewDevice === 1) {
|
||||
result = false;
|
||||
} else if (!to_display.includes('archived') && item.dev_Archived === 1) {
|
||||
result = false;
|
||||
} else if (!to_display.includes('offline') && item.dev_PresentLastScan === 0) {
|
||||
result = false;
|
||||
} else if (!to_display.includes('online') && item.dev_PresentLastScan === 1) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result; // Include all items for 'my' status
|
||||
case 'connected':
|
||||
@@ -381,7 +386,7 @@ function filterDataByStatus(data, status) {
|
||||
case 'new':
|
||||
return item.dev_NewDevice === 1;
|
||||
case 'down':
|
||||
return item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0;
|
||||
return (item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0) || item.dev_PresentLastScan === 0;
|
||||
case 'archived':
|
||||
return item.dev_Archived === 1;
|
||||
default:
|
||||
@@ -421,6 +426,11 @@ function getDeviceStatus(item)
|
||||
// -----------------------------------------------------------------------------
|
||||
function initializeDatatable (status) {
|
||||
|
||||
if(!status)
|
||||
{
|
||||
status = 'my'
|
||||
}
|
||||
|
||||
// Save status selected
|
||||
deviceStatus = status;
|
||||
|
||||
@@ -520,7 +530,8 @@ function initializeDatatable (status) {
|
||||
|
||||
// Parameters
|
||||
'pageLength' : tableRows,
|
||||
'order' : tableOrder,
|
||||
'order' : tableOrder,
|
||||
'select' : true, // Enable selection
|
||||
|
||||
'columnDefs' : [
|
||||
{visible: false, targets: tableColumnHide },
|
||||
@@ -656,6 +667,28 @@ function initializeDatatable (status) {
|
||||
setCache ('devicesList', getDevicesFromTable(table) );
|
||||
} );
|
||||
|
||||
// add multi-edit button
|
||||
$('#multiEditPlc').append(
|
||||
`<button type="submit" id="multiEdit" class="btn btn-primary" style="display:none" onclick="multiEditDevices();">
|
||||
<i class="fa fa-pencil pointer" ></i> ${getString("Device_MultiEdit")}
|
||||
</button>`)
|
||||
|
||||
// Event listener for row selection in DataTable
|
||||
$('#tableDevices').on('click', 'tr', function (e) {
|
||||
setTimeout(function(){
|
||||
// Check if any row is selected
|
||||
var anyRowSelected = $('#tableDevices tr.selected').length > 0;
|
||||
|
||||
console.log(anyRowSelected);
|
||||
|
||||
// Toggle visibility of element with ID 'multiEdit'
|
||||
$('#multiEdit').toggle(anyRowSelected);
|
||||
}, 200);
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
};
|
||||
@@ -697,7 +730,7 @@ function getNumberOfChildren(mac, devices)
|
||||
|
||||
$.each(devices, function(index, dev) {
|
||||
|
||||
if(dev.dev_Network_Node_MAC_ADDR.trim() == mac.trim())
|
||||
if(dev.dev_Network_Node_MAC_ADDR != null && dev.dev_Network_Node_MAC_ADDR.trim() == mac.trim())
|
||||
{
|
||||
childrenCount++;
|
||||
}
|
||||
@@ -729,6 +762,51 @@ function handleLoadingDialog()
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function collects selected devices in the DataTable and redirects the user to
|
||||
// the Miantenance section with a 'macs' query string identifying selected devices
|
||||
function multiEditDevices()
|
||||
{
|
||||
rows = $('#tableDevices')[0].rows
|
||||
|
||||
// Initialize an empty array to store selected rows
|
||||
var selectedRows = [];
|
||||
|
||||
console.log($('#tableDevices')[0].rows);
|
||||
|
||||
// Loop through each row in the HTML collection
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = rows[i];
|
||||
// Check if the row has the 'selected' class
|
||||
if (row.classList.contains('selected')) {
|
||||
// If selected, push the row's data to the selectedRows array
|
||||
selectedRows.push(row);
|
||||
}
|
||||
}
|
||||
|
||||
// Now, selectedRows contains all selected rows
|
||||
console.log(selectedRows);
|
||||
|
||||
var devicesDataTableData = $('#tableDevices').dataTable().fnGetData();
|
||||
|
||||
var selectedDevices = [];
|
||||
|
||||
for (var i = 0; i < selectedRows.length; i++) {
|
||||
selectedDevices.push(devicesDataTableData[selectedRows[i]._DT_RowIndex]);
|
||||
}
|
||||
|
||||
// Now, selectedDevices contains all selected devices
|
||||
console.log(selectedDevices);
|
||||
|
||||
macs = ""
|
||||
|
||||
for (var i = 0; i < selectedDevices.length; i++) {
|
||||
macs += selectedDevices[i][mapIndx(11)] + ","; // [11] == MAC
|
||||
}
|
||||
|
||||
// redirect to the Maintenance section
|
||||
window.location.href = window.location.origin + '/maintenance.php#tab_multiEdit?macs=' + macs.slice(0, -1);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
require 'php/templates/header.php';
|
||||
?>
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
<div id="donationsPage" class="content-wrapper">
|
||||
<!-- Content header--------------------------------------------------------- -->
|
||||
<section class="content-header">
|
||||
|
||||
@@ -99,8 +99,8 @@ if ($ENABLED_DARKMODE === True) {
|
||||
$BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/boxed-bg-dark.png\');"';
|
||||
} else { $BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/background.png\');"';}
|
||||
?>
|
||||
|
||||
<link rel="stylesheet" href="/front/css/offline-font.css">
|
||||
<!-- /var/www/html/pialert/css/offline-font.css -->
|
||||
<link rel="stylesheet" href="/css/offline-font.css">
|
||||
</head>
|
||||
<body class="hold-transition login-page">
|
||||
<div class="login-box login-custom">
|
||||
|
||||
18
front/js/db_methods.js
Executable file
18
front/js/db_methods.js
Executable file
@@ -0,0 +1,18 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// General utilities to interact with teh database
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Read data and place intotarget location, callback processies the results
|
||||
function readData(sqlQuery, processDataCallback, valuesArray, targetLocation) {
|
||||
var apiUrl = `php/server/dbHelper.php?action=read&rawSql=${encodeURIComponent(sqlQuery)}`;
|
||||
$.get(apiUrl, function(data) {
|
||||
// Process the JSON data using the provided callback function
|
||||
|
||||
data = JSON.parse(data)
|
||||
|
||||
var htmlResult = processDataCallback(data, valuesArray);
|
||||
|
||||
// Place the resulting HTML into the specified placeholder div
|
||||
$("#" + targetLocation).replaceWith(htmlResult);
|
||||
});
|
||||
}
|
||||
@@ -1,32 +1,70 @@
|
||||
function pia_draw_graph_online_history(pia_js_graph_online_history_time, pia_js_graph_online_history_ondev, pia_js_graph_online_history_dodev, pia_js_graph_online_history_ardev) {
|
||||
var xValues = pia_js_graph_online_history_time;
|
||||
|
||||
// alert("dev presence")
|
||||
|
||||
// Data object for online status
|
||||
onlineData = {
|
||||
label: 'Online',
|
||||
data: pia_js_graph_online_history_ondev,
|
||||
borderColor: "rgba(0, 166, 89)",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(0, 166, 89, .6)",
|
||||
pointStyle: 'circle',
|
||||
pointRadius: 3,
|
||||
pointHoverRadius: 3
|
||||
};
|
||||
|
||||
// Data object for offline status
|
||||
offlineData = {
|
||||
label: 'Offline/Down',
|
||||
data: pia_js_graph_online_history_dodev,
|
||||
borderColor: "rgba(222, 74, 56)",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(222, 74, 56, .6)",
|
||||
};
|
||||
|
||||
// Data object for archived status
|
||||
archivedData = {
|
||||
label: 'Archived',
|
||||
data: pia_js_graph_online_history_ardev,
|
||||
borderColor: "rgba(220,220,220)",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(220,220,220, .6)",
|
||||
};
|
||||
|
||||
// Array to store datasets
|
||||
datasets = [];
|
||||
|
||||
// Get UI presence settings
|
||||
showStats = getSetting("UI_PRESENCE");
|
||||
|
||||
// Check if 'online' status should be displayed
|
||||
if(showStats.includes("online"))
|
||||
{
|
||||
datasets.push(onlineData); // Add onlineData to datasets array
|
||||
}
|
||||
|
||||
// Check if 'offline' status should be displayed
|
||||
if(showStats.includes("offline"))
|
||||
{
|
||||
datasets.push(offlineData); // Add offlineData to datasets array
|
||||
}
|
||||
|
||||
// Check if 'archived' status should be displayed
|
||||
if(showStats.includes("archived"))
|
||||
{
|
||||
datasets.push(archivedData); // Add archivedData to datasets array
|
||||
}
|
||||
|
||||
|
||||
|
||||
new Chart("OnlineChart", {
|
||||
type: "bar",
|
||||
scaleIntegersOnly: true,
|
||||
data: {
|
||||
labels: xValues,
|
||||
datasets: [{
|
||||
label: 'Online',
|
||||
data: pia_js_graph_online_history_ondev,
|
||||
borderColor: "rgba(0, 166, 89)",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(0, 166, 89, .6)",
|
||||
pointStyle: 'circle',
|
||||
pointRadius: 3,
|
||||
pointHoverRadius: 3
|
||||
}, {
|
||||
label: 'Offline/Down',
|
||||
data: pia_js_graph_online_history_dodev,
|
||||
borderColor: "rgba(222, 74, 56)",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(222, 74, 56, .6)",
|
||||
}, {
|
||||
label: 'Archived',
|
||||
data: pia_js_graph_online_history_ardev,
|
||||
borderColor: "rgba(220,220,220)",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(220,220,220, .6)",
|
||||
}]
|
||||
datasets: datasets
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
|
||||
@@ -35,8 +35,8 @@ function checkIfNewVersionAvailable()
|
||||
{
|
||||
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) {
|
||||
|
||||
console.log(appState["isNewVersionChecked"])
|
||||
console.log(appState["isNewVersion"])
|
||||
// console.log(appState["isNewVersionChecked"])
|
||||
// console.log(appState["isNewVersion"])
|
||||
|
||||
// cache value
|
||||
setCookie("isNewVersion", appState["isNewVersion"], 30);
|
||||
|
||||
@@ -26,13 +26,17 @@ var settingsJSON = {}
|
||||
function getCache(key, noCookie = false)
|
||||
{
|
||||
// check cache
|
||||
if(sessionStorage.getItem(key))
|
||||
cachedValue = localStorage.getItem(key)
|
||||
|
||||
// console.log(cachedValue);
|
||||
|
||||
if(cachedValue)
|
||||
{
|
||||
// check if not expired
|
||||
if(noCookie || getCookie(key + '_session_expiry') != "")
|
||||
{
|
||||
return sessionStorage.getItem(key);
|
||||
}
|
||||
// // check if not expired
|
||||
// if(noCookie || getCookie(key + '_session_expiry') != "")
|
||||
// {
|
||||
return cachedValue;
|
||||
// }
|
||||
}
|
||||
|
||||
return "";
|
||||
@@ -41,13 +45,13 @@ function getCache(key, noCookie = false)
|
||||
// -----------------------------------------------------------------------------
|
||||
function setCache(key, data, expirationMinutes='')
|
||||
{
|
||||
sessionStorage.setItem(key, data);
|
||||
localStorage.setItem(key, data);
|
||||
|
||||
// create cookie if expiration set to handle refresh of data
|
||||
if (expirationMinutes != '')
|
||||
{
|
||||
setCookie (key + '_session_expiry', 'OK', expirationMinutes='')
|
||||
}
|
||||
// // create cookie if expiration set to handle refresh of data
|
||||
// if (expirationMinutes != '')
|
||||
// {
|
||||
// setCookie ('cache_session_expiry', 'OK', 1)
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@@ -107,24 +111,77 @@ function deleteAllCookies() {
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Get settings from the .json file generated by the python backend
|
||||
// Get settings from the .json file generated by the python backend
|
||||
// and cache them, if available, with options
|
||||
// -----------------------------------------------------------------------------
|
||||
function cacheSettings()
|
||||
{
|
||||
|
||||
$.get('api/table_settings.json', function(res) {
|
||||
|
||||
settingsJSON = res;
|
||||
|
||||
data = settingsJSON["data"];
|
||||
$.get('api/table_settings.json?nocache=' + Date.now(), function(resSet) {
|
||||
|
||||
data.forEach((set) => {
|
||||
setCache(`pia_set_${set.Code_Name}`, set.Value)
|
||||
});
|
||||
$.get('api/plugins.json?nocache=' + Date.now(), function(resPlug) {
|
||||
|
||||
pluginsData = resPlug["data"];
|
||||
settingsData = resSet["data"];
|
||||
|
||||
settingsData.forEach((set) => {
|
||||
|
||||
resolvedOptions = createArray(set.Options)
|
||||
setPlugObj = {};
|
||||
options_params = [];
|
||||
|
||||
// proceed only if first option item contains something to resolve
|
||||
if( !set.Code_Name.includes("__metadata") &&
|
||||
resolvedOptions.length != 0 &&
|
||||
resolvedOptions[0].includes("{value}"))
|
||||
{
|
||||
// get setting definition from the plugin config if available
|
||||
setPlugObj = getPluginSettingObject(pluginsData, set.Code_Name)
|
||||
|
||||
// check if options contains parameters and resolve
|
||||
if(setPlugObj != {} && setPlugObj["options_params"])
|
||||
{
|
||||
// get option_params for {value} resolution
|
||||
options_params = setPlugObj["options_params"]
|
||||
|
||||
if(options_params != [])
|
||||
{
|
||||
// handles only strings of length == 1
|
||||
resolvedOptions = `["${resolveParams(options_params, resolvedOptions[0])}"]`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setCache(`pia_set_${set.Code_Name}`, set.Value)
|
||||
setCache(`pia_set_opt_${set.Code_Name}`, resolvedOptions)
|
||||
});
|
||||
}).then(() => handleSuccess('cacheSettings')).catch(() => handleFailure('cacheSettings', cacheSettings)); // handle AJAX synchronization
|
||||
})
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Get a setting value by key
|
||||
function getSettingOptions (key) {
|
||||
|
||||
// handle initial load to make sure everything is set-up and cached
|
||||
// handleFirstLoad()
|
||||
|
||||
result = getCache(`pia_set_opt_${key}`, true);
|
||||
|
||||
if (result == "")
|
||||
{
|
||||
console.log(`Setting options with key "${key}" not found`)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Get a setting value by key
|
||||
function getSetting (key) {
|
||||
|
||||
// handle initial load to make sure everything is set-up and cached
|
||||
// handleFirstLoad()
|
||||
|
||||
result = getCache(`pia_set_${key}`, true);
|
||||
|
||||
@@ -143,10 +200,10 @@ function cacheStrings()
|
||||
{
|
||||
|
||||
// handle core strings and translations
|
||||
var allLanguages = ["en_us", "es_es", "de_de"]; // needs to be same as in lang.php
|
||||
var allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "ru_ru", "nb_no"]; // needs to be same as in lang.php
|
||||
|
||||
allLanguages.forEach(function (language_code) {
|
||||
$.get(`php/templates/language/${language_code}.json`, function (res) {
|
||||
$.get(`php/templates/language/${language_code}.json?nocache=${Date.now()}`, function (res) {
|
||||
// Iterate over each language
|
||||
Object.entries(res).forEach(([key, value]) => {
|
||||
// Store translations for each key-value pair
|
||||
@@ -157,22 +214,31 @@ function cacheStrings()
|
||||
|
||||
|
||||
// handle strings and translations from plugins
|
||||
$.get('api/table_plugins_language_strings.json', function(res) {
|
||||
$.get(`api/table_plugins_language_strings.json?nocache=${Date.now()}`, function(res) {
|
||||
|
||||
data = res["data"];
|
||||
|
||||
data.forEach((langString) => {
|
||||
setCache(`pia_lang_${langString.String_Key}_${langString.Language_Code}`, langString.String_Value)
|
||||
});
|
||||
})
|
||||
}).then(() => handleSuccess('cacheStrings')).catch(() => handleFailure('cacheStrings', cacheStrings)); // handle AJAX synchronization
|
||||
|
||||
}
|
||||
|
||||
// Get translated language string
|
||||
function getString (key) {
|
||||
|
||||
// handle initial laod to make sure everything is set-up and cached
|
||||
handleFirstLoad()
|
||||
|
||||
UI_LANG = getSetting("UI_LANG");
|
||||
|
||||
// //
|
||||
// if(UI_LANG == "")
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
lang_code = 'en_us';
|
||||
|
||||
switch(UI_LANG)
|
||||
@@ -186,6 +252,15 @@ function getString (key) {
|
||||
case 'German':
|
||||
lang_code = 'de_de';
|
||||
break;
|
||||
case 'French':
|
||||
lang_code = 'fr_fr';
|
||||
break;
|
||||
case 'Norwegian':
|
||||
lang_code = 'nb_no';
|
||||
break;
|
||||
case 'Russian':
|
||||
lang_code = 'ru_ru';
|
||||
break;
|
||||
}
|
||||
result = getCache(`pia_lang_${key}_${lang_code}`, true);
|
||||
|
||||
@@ -357,7 +432,7 @@ function handle_locked_DB(data)
|
||||
{
|
||||
if(data.includes('database is locked'))
|
||||
{
|
||||
console.log(data)
|
||||
// console.log(data)
|
||||
showSpinner()
|
||||
|
||||
setTimeout(function() {
|
||||
@@ -368,9 +443,7 @@ function handle_locked_DB(data)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function numberArrayFromString(data)
|
||||
{
|
||||
|
||||
|
||||
{
|
||||
data = JSON.parse(sanitize(data));
|
||||
return data.replace(/\[|\]/g, '').split(',').map(Number);
|
||||
}
|
||||
@@ -466,13 +539,71 @@ function settingsChanged()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Get Anchor from URL
|
||||
function getUrlAnchor(defaultValue){
|
||||
|
||||
target = defaultValue
|
||||
|
||||
var url = window.location.href;
|
||||
if (url.includes("#")) {
|
||||
|
||||
// default selection
|
||||
selectedTab = defaultValue
|
||||
|
||||
// the #target from the url
|
||||
target = window.location.hash.substr(1)
|
||||
|
||||
// get only the part between #...?
|
||||
if(target.includes('?'))
|
||||
{
|
||||
target = target.split('?')[0]
|
||||
}
|
||||
|
||||
return target
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// get query string from URL
|
||||
function getQueryString(key){
|
||||
params = new Proxy(new URLSearchParams(window.location.search), {
|
||||
get: (searchParams, prop) => searchParams.get(prop),
|
||||
});
|
||||
|
||||
tmp = params[key]
|
||||
|
||||
if(emptyArr.includes(tmp))
|
||||
{
|
||||
var queryParams = {};
|
||||
fullUrl = window.location.toString();
|
||||
|
||||
// console.log(fullUrl);
|
||||
|
||||
if (fullUrl.includes('?')) {
|
||||
var queryString = fullUrl.split('?')[1];
|
||||
|
||||
// Split the query string into individual parameters
|
||||
var paramsArray = queryString.split('&');
|
||||
|
||||
// Loop through the parameters array
|
||||
paramsArray.forEach(function(param) {
|
||||
// Split each parameter into key and value
|
||||
var keyValue = param.split('=');
|
||||
var keyTmp = decodeURIComponent(keyValue[0]);
|
||||
var value = decodeURIComponent(keyValue[1] || '');
|
||||
|
||||
// Store key-value pair in the queryParams object
|
||||
queryParams[keyTmp] = value;
|
||||
});
|
||||
}
|
||||
|
||||
// console.log(queryParams);
|
||||
|
||||
tmp = queryParams[key]
|
||||
}
|
||||
|
||||
result = emptyArr.includes(tmp) ? "" : tmp;
|
||||
|
||||
return result
|
||||
@@ -515,10 +646,37 @@ function debugTimer () {
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Open url in new tab
|
||||
function openInNewTab (url) {
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Navigate to URL if the current URL is not in the provided list of URLs
|
||||
function openUrl(urls) {
|
||||
var currentUrl = window.location.href;
|
||||
var mainUrl = currentUrl.match(/^.*?(?=#|\?|$)/)[0]; // Extract main URL
|
||||
|
||||
var isMatch = false;
|
||||
|
||||
$.each(urls,function(index, obj){
|
||||
|
||||
// remove . for comaprison if in the string, e.g.: ./devices.php
|
||||
arrayUrl = obj.replace('.','')
|
||||
|
||||
// check if we are on a url contained in the array
|
||||
if(mainUrl.includes(arrayUrl))
|
||||
{
|
||||
isMatch = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if we are not, redirect
|
||||
if (isMatch == false) {
|
||||
window.location.href = urls[0]; // Redirect to the first URL in the list if not found
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function navigateToDeviceWithIp (ip) {
|
||||
@@ -633,30 +791,51 @@ function isRandomMAC(mac)
|
||||
if (input === '[]') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Regex patterns
|
||||
|
||||
// Regex pattern for brackets
|
||||
const patternBrackets = /(^\s*\[)|(\]\s*$)/g;
|
||||
const patternQuotes = /(^\s*')|('\s*$)/g;
|
||||
const replacement = '';
|
||||
|
||||
|
||||
// Remove brackets
|
||||
const noBrackets = input.replace(patternBrackets, replacement);
|
||||
|
||||
|
||||
const options = [];
|
||||
|
||||
// Create array
|
||||
const optionsTmp = noBrackets.split(',');
|
||||
|
||||
// Handle only one item in array
|
||||
if (optionsTmp.length === 0) {
|
||||
return [noBrackets.replace(patternQuotes, replacement)];
|
||||
|
||||
// Detect the type of quote used after the opening bracket
|
||||
const firstChar = noBrackets.trim()[0];
|
||||
const isDoubleQuoted = firstChar === '"';
|
||||
const isSingleQuoted = firstChar === "'";
|
||||
|
||||
// Create array while handling commas within quoted segments
|
||||
let currentSegment = '';
|
||||
let withinQuotes = false;
|
||||
for (let i = 0; i < noBrackets.length; i++) {
|
||||
const char = noBrackets[i];
|
||||
if ((char === '"' && !isSingleQuoted) || (char === "'" && !isDoubleQuoted)) {
|
||||
withinQuotes = !withinQuotes;
|
||||
}
|
||||
if (char === ',' && !withinQuotes) {
|
||||
options.push(currentSegment.trim());
|
||||
currentSegment = '';
|
||||
} else {
|
||||
currentSegment += char;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove quotes
|
||||
optionsTmp.forEach(item => {
|
||||
options.push(item.replace(patternQuotes, replacement).trim());
|
||||
// Push the last segment
|
||||
options.push(currentSegment.trim());
|
||||
|
||||
// Remove quotes based on detected type
|
||||
options.forEach((item, index) => {
|
||||
let trimmedItem = item.trim();
|
||||
// Check if the string starts and ends with the same type of quote
|
||||
if ((isDoubleQuoted && trimmedItem.startsWith('"') && trimmedItem.endsWith('"')) ||
|
||||
(isSingleQuoted && trimmedItem.startsWith("'") && trimmedItem.endsWith("'"))) {
|
||||
// Remove the quotes
|
||||
trimmedItem = trimmedItem.substring(1, trimmedItem.length - 1);
|
||||
}
|
||||
options[index] = trimmedItem;
|
||||
});
|
||||
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -666,14 +845,14 @@ function isRandomMAC(mac)
|
||||
function getDeviceDataByMacAddress(macAddress, dbColumn) {
|
||||
|
||||
const sessionDataKey = 'devicesListAll_JSON';
|
||||
const sessionData = sessionStorage.getItem(sessionDataKey);
|
||||
const devicesCache = getCache(sessionDataKey);
|
||||
|
||||
if (!sessionData) {
|
||||
console.log(`Session variable "${sessionDataKey}" not found.`);
|
||||
if (!devicesCache || devicesCache == "") {
|
||||
console.error(`Session variable "${sessionDataKey}" not found.`);
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const devices = JSON.parse(sessionData);
|
||||
const devices = JSON.parse(devicesCache);
|
||||
|
||||
for (const device of devices) {
|
||||
if (device["dev_MAC"].toLowerCase() === macAddress.toLowerCase()) {
|
||||
@@ -692,12 +871,27 @@ function initDeviceListAll_JSON()
|
||||
|
||||
$.get('api/table_devices.json', function(data) {
|
||||
|
||||
console.log(data)
|
||||
// console.log(data)
|
||||
|
||||
devicesListAll_JSON = data["data"]
|
||||
|
||||
setCache('devicesListAll_JSON', JSON.stringify(devicesListAll_JSON))
|
||||
});
|
||||
devicesListAll_JSON_str = JSON.stringify(devicesListAll_JSON)
|
||||
|
||||
if(devicesListAll_JSON_str == "")
|
||||
{
|
||||
setTimeout(() => {
|
||||
initDeviceListAll_JSON()
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
// console.log("devicesListAll_JSON_str");
|
||||
// console.log(devicesListAll_JSON_str);
|
||||
|
||||
setCache('devicesListAll_JSON', devicesListAll_JSON_str)
|
||||
|
||||
// console.log(getCache('devicesListAll_JSON'))
|
||||
}).then(() => handleSuccess('initDeviceListAll_JSON')).catch(() => handleFailure('initDeviceListAll_JSON', initDeviceListAll_JSON)); // handle AJAX synchronization
|
||||
|
||||
}
|
||||
|
||||
@@ -722,18 +916,7 @@ function getGuid() {
|
||||
// UI
|
||||
// -----------------------------------------------------------------------------
|
||||
// -----------------------------------------------------------------------------
|
||||
// Genrate work-in-progress icons
|
||||
function workInProgress() {
|
||||
console.log()
|
||||
if($(".work-in-progress").html().trim() == "")
|
||||
{
|
||||
$(".work-in-progress").append(`
|
||||
<a href="https://github.com/jokob-sk/Pi.Alert/issues" target="_blank">
|
||||
<b class="pointer" title="${getString("Gen_Work_In_Progress")}">🦺</b>
|
||||
</a>
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Loading Spinner overlay
|
||||
@@ -741,8 +924,7 @@ function workInProgress() {
|
||||
function showSpinner(stringKey='Loading')
|
||||
{
|
||||
if($("#loadingSpinner").length)
|
||||
{
|
||||
|
||||
{
|
||||
$("#loadingSpinner").show();
|
||||
}
|
||||
else{
|
||||
@@ -768,15 +950,261 @@ function hideSpinner()
|
||||
$("#loadingSpinner").hide()
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Calls a backend function to add a front-end event to an execution queue
|
||||
function updateApi()
|
||||
{
|
||||
|
||||
// value has to be in format event|param. e.g. run|ARPSCAN
|
||||
action = `update_api|devices,appevents`
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/util.php",
|
||||
data: { function: "addToExecutionQueue", action: action },
|
||||
success: function(data, textStatus) {
|
||||
console.log(data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// handling smooth scrolling
|
||||
// -----------------------------------------------------------------------------
|
||||
function setupSmoothScrolling() {
|
||||
// Function to scroll to the element
|
||||
function scrollToElement(id) {
|
||||
$('html, body').animate({
|
||||
scrollTop: $("#" + id).offset().top - 50
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Scroll to the element when clicking on anchor links
|
||||
$('a[href*="#"]').on('click', function(event) {
|
||||
var href = $(this).attr('href');
|
||||
if (href !=='#' && href && href.includes('#') && !$(this).is('[data-toggle="collapse"]')) {
|
||||
var id = href.substring(href.indexOf("#") + 1); // Get the ID from the href attribute
|
||||
if ($("#" + id).length > 0) {
|
||||
event.preventDefault(); // Prevent default anchor behavior
|
||||
scrollToElement(id); // Scroll to the element
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check if there's an ID in the URL and scroll to it
|
||||
var url = window.location.href;
|
||||
if (url.includes("#")) {
|
||||
var idFromURL = url.substring(url.indexOf("#") + 1);
|
||||
if ($("#" + idFromURL).length > 0) {
|
||||
scrollToElement(idFromURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Function to check if options_params contains a parameter with type "sql"
|
||||
function hasSqlType(params) {
|
||||
for (let param of params) {
|
||||
if (param.type === "sql") {
|
||||
return true; // Found a parameter with type "sql"
|
||||
}
|
||||
}
|
||||
return false; // No parameter with type "sql" found
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Function to check if string is SQL query
|
||||
function isSQLQuery(query) {
|
||||
// Regular expression to match common SQL keywords and syntax with word boundaries
|
||||
var sqlRegex = /\b(SELECT|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|FROM|JOIN|WHERE|SET|VALUES|GROUP BY|ORDER BY|LIMIT)\b/i;
|
||||
|
||||
return sqlRegex.test(query);
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Get corresponding plugin setting object
|
||||
function getPluginSettingObject(pluginsData, setting_key, unique_prefix ) {
|
||||
|
||||
result = {}
|
||||
unique_prefix == undefined ? unique_prefix = setting_key.split("_")[0] : unique_prefix = unique_prefix;
|
||||
|
||||
$.each(pluginsData, function (i, plgnObj){
|
||||
// go thru plugins
|
||||
if(plgnObj.unique_prefix == unique_prefix)
|
||||
{
|
||||
// go thru plugin settings
|
||||
$.each(plgnObj["settings"], function (j, setObj){
|
||||
|
||||
if(`${unique_prefix}_${setObj.function}` == setting_key)
|
||||
{
|
||||
result = setObj
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Resolve all option parameters
|
||||
function resolveParams(params, template) {
|
||||
params.forEach(param => {
|
||||
// Check if the template includes the parameter name
|
||||
if (template.includes("{" + param.name + "}")) {
|
||||
// If the parameter type is 'setting', retrieve setting value
|
||||
if (param.type == "setting") {
|
||||
var value = getSetting(param.value);
|
||||
// Replace placeholder with setting value
|
||||
template = template.replace("{" + param.name + "}", value);
|
||||
} else {
|
||||
// If the parameter type is not 'setting', use the provided value
|
||||
template = template.replace("{" + param.name + "}", param.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Log the resolved template
|
||||
// console.log(template);
|
||||
|
||||
// Return the resolved template
|
||||
return template;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// check if two arrays contain same values even if out of order
|
||||
function arraysContainSameValues(arr1, arr2) {
|
||||
// Sort and stringify arrays, then compare
|
||||
return JSON.stringify(arr1.slice().sort()) === JSON.stringify(arr2.slice().sort());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// initialize
|
||||
// -----------------------------------------------------------------------------
|
||||
cacheSettings()
|
||||
cacheStrings()
|
||||
initDeviceListAll_JSON()
|
||||
workInProgress()
|
||||
// Define a unique key for storing the flag in sessionStorage
|
||||
var sessionStorageKey = "myScriptExecuted_pialert_common";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Clearing all the caches
|
||||
function clearCache()
|
||||
{
|
||||
resetInitializedFlag()
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function resetInitializedFlag()
|
||||
{
|
||||
// Clear local storage
|
||||
localStorage.clear();
|
||||
// Set the flag in sessionStorage to indicate that the code and cahce needs to be reloaded
|
||||
sessionStorage.setItem(sessionStorageKey, "false");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// check if cache needs to be refreshed because of setting changes
|
||||
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) {
|
||||
|
||||
console.log(appState["settingsImported"]*1000)
|
||||
|
||||
importedMiliseconds = parseInt((appState["settingsImported"]*1000));
|
||||
|
||||
lastReloaded = parseInt(sessionStorage.getItem(sessionStorageKey + '_time'));
|
||||
|
||||
if(importedMiliseconds > lastReloaded)
|
||||
{
|
||||
console.log("Cache needs to be refreshed because of setting changes");
|
||||
setTimeout(() => {
|
||||
resetInitializedFlag()
|
||||
location.reload();
|
||||
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Display spinner and reload page if not yet initialized
|
||||
function handleFirstLoad()
|
||||
{
|
||||
if(!pialert_common_init)
|
||||
{
|
||||
setTimeout(function() {
|
||||
|
||||
location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Check if the code has been executed before by checking sessionStorage
|
||||
var pialert_common_init = sessionStorage.getItem(sessionStorageKey) === "true";
|
||||
|
||||
// Define a function that will execute the code only once
|
||||
function executeOnce() {
|
||||
|
||||
if (!pialert_common_init) {
|
||||
|
||||
resetInitializedFlag()
|
||||
|
||||
showSpinner()
|
||||
|
||||
// to keep track of completed AJAX calls
|
||||
completedCalls = []
|
||||
completedCalls_final = ['cacheSettings', 'cacheStrings', 'initDeviceListAll_JSON'];
|
||||
|
||||
// Your initialization code here
|
||||
cacheSettings();
|
||||
cacheStrings();
|
||||
initDeviceListAll_JSON();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function to handle successful completion of an AJAX call
|
||||
const handleSuccess = (callName) => {
|
||||
console.log(`AJAX call ${callName} successful`);
|
||||
// store completed call
|
||||
completedCalls.push(callName)
|
||||
onAllCallsComplete();
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function to handle failure of an AJAX call
|
||||
const handleFailure = (callName, callback) => {
|
||||
// Handle AJAX call failure here
|
||||
console.error(`AJAX call ${callName} failed`);
|
||||
|
||||
// try until successful
|
||||
callback()
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function to execute when all AJAX calls have completed
|
||||
const onAllCallsComplete = () => {
|
||||
// Check if all three AJAX calls have completed
|
||||
if (arraysContainSameValues(completedCalls,completedCalls_final)) {
|
||||
|
||||
// Set the flag in sessionStorage to indicate that the code has been executed
|
||||
// and save time when last time the page was initialized
|
||||
sessionStorage.setItem(sessionStorageKey, "true");
|
||||
const millisecondsNow = Date.now();
|
||||
sessionStorage.setItem(sessionStorageKey + '_time', millisecondsNow);
|
||||
|
||||
console.log("init pialert_common.js");
|
||||
}
|
||||
};
|
||||
|
||||
// Call the function to execute the code
|
||||
executeOnce();
|
||||
|
||||
|
||||
|
||||
|
||||
console.log("init pialert_common.js")
|
||||
|
||||
|
||||
|
||||
@@ -163,4 +163,9 @@
|
||||
}
|
||||
|
||||
return true; // Return true if no schedules are found
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
194
front/js/ui_components.js
Executable file
194
front/js/ui_components.js
Executable file
@@ -0,0 +1,194 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Pi.Alert
|
||||
* Open Source Network Guard / WIFI & LAN intrusion detector
|
||||
*
|
||||
* ui_components.js - Front module. Common UI components
|
||||
*-------------------------------------------------------------------------------
|
||||
# jokob jokob@duck.com GNU GPLv3
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Initialize device selectors / pickers fields
|
||||
// -----------------------------------------------------------------------------
|
||||
function initDeviceSelectors() {
|
||||
|
||||
console.log(devicesList)
|
||||
// Retrieve device list from session variable
|
||||
var devicesListAll_JSON = getCache('devicesListAll_JSON');
|
||||
|
||||
var devicesList = JSON.parse(devicesListAll_JSON);
|
||||
|
||||
console.log(devicesList);
|
||||
|
||||
|
||||
// Check if both device list exists
|
||||
if (devicesListAll_JSON) {
|
||||
// Parse the JSON string to get the device list array
|
||||
var devicesList = JSON.parse(devicesListAll_JSON);
|
||||
|
||||
var selectorFieldsHTML = ''
|
||||
|
||||
// Loop through the devices list
|
||||
devicesList.forEach(function(device) {
|
||||
|
||||
selectorFieldsHTML += `<option value="${device.dev_MAC}">${device.dev_Name}</option>`;
|
||||
});
|
||||
|
||||
selector = `<div class="db_info_table_row col-sm-12" >
|
||||
<div class="form-group" >
|
||||
<div class="input-group col-sm-12 " >
|
||||
<select class="form-control select2 select2-hidden-accessible" multiple="" style="width: 100%;" tabindex="-1" aria-hidden="true">
|
||||
${selectorFieldsHTML}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
|
||||
// Find HTML elements with class "deviceSelector" and append selector field
|
||||
$('.deviceSelector').append(selector);
|
||||
}
|
||||
|
||||
// Initialize selected items after a delay so selected macs are available in the context
|
||||
setTimeout(function(){
|
||||
// Retrieve MAC addresses from query string or cache
|
||||
var macs = getQueryString('macs') || getCache('selectedDevices');
|
||||
|
||||
if(macs)
|
||||
{
|
||||
// Split MAC addresses if they are comma-separated
|
||||
macs = macs.split(',');
|
||||
|
||||
console.log(macs)
|
||||
|
||||
// Loop through macs to be selected list
|
||||
macs.forEach(function(mac) {
|
||||
|
||||
// Create the option and append to Select2
|
||||
var option = new Option($('.deviceSelector select option[value="' + mac + '"]').html(), mac, true, true);
|
||||
|
||||
$('.deviceSelector select').append(option).trigger('change');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}, 10);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------
|
||||
//Initialize Select2 Elements and make them sortable
|
||||
|
||||
$(function () {
|
||||
// Iterate over each Select2 dropdown
|
||||
$('.select2').each(function() {
|
||||
var selectEl = $(this).select2();
|
||||
|
||||
// Apply sortable functionality to the dropdown's dropdown-container
|
||||
selectEl.next().children().children().children().sortable({
|
||||
containment: 'parent',
|
||||
update: function () {
|
||||
var sortedValues = $(this).children().map(function() {
|
||||
return $(this).attr('title');
|
||||
}).get();
|
||||
|
||||
var sortedOptions = selectEl.find('option').sort(function(a, b) {
|
||||
return sortedValues.indexOf($(a).text()) - sortedValues.indexOf($(b).text());
|
||||
});
|
||||
|
||||
// Replace all options in selectEl
|
||||
selectEl.empty().append(sortedOptions);
|
||||
|
||||
// Trigger change event on Select2
|
||||
selectEl.trigger('change');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Initiate dropdown
|
||||
function initSettingDropdown(settingKey, valuesArray, targetLocation)
|
||||
{
|
||||
|
||||
var optionsHtml = ""
|
||||
var targetLocation_options = settingKey + "_initSettingDropdown"
|
||||
|
||||
optionsArray = createArray(getSettingOptions(settingKey))
|
||||
|
||||
// check if the result is a SQL query
|
||||
if(isSQLQuery(optionsArray[0]))
|
||||
{
|
||||
|
||||
optionsHtml += `<option id="${targetLocation_options}"></option>`;
|
||||
|
||||
readData(optionsArray[0], generateDropdownOptions, valuesArray, targetLocation_options);
|
||||
|
||||
} else // this should be already an array, e.g. from a setting or pre-defined
|
||||
{
|
||||
optionsArray.forEach(option => {
|
||||
let selected = valuesArray.includes(option) ? 'selected' : '';
|
||||
optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
|
||||
});
|
||||
|
||||
// Replace the specified placeholder div with the resulting HTML
|
||||
setTimeout(() => {
|
||||
|
||||
$("#" + targetLocation).replaceWith(optionsHtml);
|
||||
|
||||
}, 50);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Data processors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Processor to generate options for a dropdown menu
|
||||
function generateDropdownOptions(data, valuesArray) {
|
||||
var optionsHtml = "";
|
||||
data.forEach(function(item) {
|
||||
|
||||
let selected = valuesArray.includes(item.id) ? 'selected' : '';
|
||||
|
||||
optionsHtml += `<option value="${item.id}" ${selected}>${item.name}</option>`;
|
||||
});
|
||||
return `${optionsHtml}`;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Processor to generate a list
|
||||
function generateList(data, valuesArray) {
|
||||
var listHtml = "";
|
||||
data.forEach(function(item) {
|
||||
|
||||
let selected = valuesArray.includes(item.id) ? 'selected' : '';
|
||||
|
||||
listHtml += `<li ${selected}>${item.name}</li>`;
|
||||
});
|
||||
listHtml += "";
|
||||
return listHtml;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// initialize
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
initDeviceSelectors();
|
||||
|
||||
console.log("init ui_components.js")
|
||||
1
front/lib/AdminLTE/bower_components/datatables.net/css/select.dataTables.min.css
vendored
Executable file
1
front/lib/AdminLTE/bower_components/datatables.net/css/select.dataTables.min.css
vendored
Executable file
@@ -0,0 +1 @@
|
||||
table.dataTable tbody>tr.selected,table.dataTable tbody>tr>.selected{background-color:#b0bed9}table.dataTable.stripe tbody>tr.odd.selected,table.dataTable.stripe tbody>tr.odd>.selected,table.dataTable.display tbody>tr.odd.selected,table.dataTable.display tbody>tr.odd>.selected{background-color:#acbad4}table.dataTable.hover tbody>tr.selected:hover,table.dataTable.hover tbody>tr>.selected:hover,table.dataTable.display tbody>tr.selected:hover,table.dataTable.display tbody>tr>.selected:hover{background-color:#aab7d1}table.dataTable.order-column tbody>tr.selected>.sorting_1,table.dataTable.order-column tbody>tr.selected>.sorting_2,table.dataTable.order-column tbody>tr.selected>.sorting_3,table.dataTable.order-column tbody>tr>.selected,table.dataTable.display tbody>tr.selected>.sorting_1,table.dataTable.display tbody>tr.selected>.sorting_2,table.dataTable.display tbody>tr.selected>.sorting_3,table.dataTable.display tbody>tr>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody>tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody>tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody>tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody>tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody>tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody>tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody>tr.odd>.selected,table.dataTable.order-column.stripe tbody>tr.odd>.selected{background-color:#a6b4cd}table.dataTable.display tbody>tr.even>.selected,table.dataTable.order-column.stripe tbody>tr.even>.selected{background-color:#acbad5}table.dataTable.display tbody>tr.selected:hover>.sorting_1,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody>tr.selected:hover>.sorting_2,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody>tr.selected:hover>.sorting_3,table.dataTable.order-column.hover tbody>tr.selected:hover>.sorting_3{background-color:#a5b2cb}table.dataTable.display tbody>tr:hover>.selected,table.dataTable.display tbody>tr>.selected:hover,table.dataTable.order-column.hover tbody>tr:hover>.selected,table.dataTable.order-column.hover tbody>tr>.selected:hover{background-color:#a2aec7}table.dataTable tbody td.select-checkbox,table.dataTable tbody th.select-checkbox{position:relative}table.dataTable tbody td.select-checkbox:before,table.dataTable tbody td.select-checkbox:after,table.dataTable tbody th.select-checkbox:before,table.dataTable tbody th.select-checkbox:after{display:block;position:absolute;top:1.2em;left:50%;width:12px;height:12px;box-sizing:border-box}table.dataTable tbody td.select-checkbox:before,table.dataTable tbody th.select-checkbox:before{content:" ";margin-top:-6px;margin-left:-6px;border:1px solid black;border-radius:3px}table.dataTable tr.selected td.select-checkbox:after,table.dataTable tr.selected th.select-checkbox:after{content:"✓";font-size:20px;margin-top:-19px;margin-left:-6px;text-align:center;text-shadow:1px 1px #b0bed9,-1px -1px #b0bed9,1px -1px #b0bed9,-1px 1px #b0bed9}table.dataTable.compact tbody td.select-checkbox:before,table.dataTable.compact tbody th.select-checkbox:before{margin-top:-12px}table.dataTable.compact tr.selected td.select-checkbox:after,table.dataTable.compact tr.selected th.select-checkbox:after{margin-top:-16px}div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:.5em}@media screen and (max-width: 640px){div.dataTables_wrapper span.select-info,div.dataTables_wrapper span.select-item{margin-left:0;display:block}}
|
||||
38
front/lib/AdminLTE/bower_components/datatables.net/js/dataTables.select.min.js
vendored
Executable file
38
front/lib/AdminLTE/bower_components/datatables.net/js/dataTables.select.min.js
vendored
Executable file
@@ -0,0 +1,38 @@
|
||||
/*!
|
||||
Copyright 2015-2021 SpryMedia Ltd.
|
||||
|
||||
This source file is free software, available under the following license:
|
||||
MIT license - http://datatables.net/license/mit
|
||||
|
||||
This source file is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
|
||||
|
||||
For details please refer to: http://www.datatables.net/extensions/select
|
||||
Select for DataTables 1.3.3
|
||||
2015-2021 SpryMedia Ltd - datatables.net/license/mit
|
||||
*/
|
||||
(function(h){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(q){return h(q,window,document)}):"object"===typeof exports?module.exports=function(q,t){q||(q=window);t&&t.fn.dataTable||(t=require("datatables.net")(q,t).$);return h(t,q,q.document)}:h(jQuery,window,document)})(function(h,q,t,n){function E(a,b,c){var d=function(g,f){if(g>f){var k=f;f=g;g=k}var l=!1;return a.columns(":visible").indexes().filter(function(p){p===g&&(l=!0);return p===f?(l=!1,!0):l})};var e=
|
||||
function(g,f){var k=a.rows({search:"applied"}).indexes();if(k.indexOf(g)>k.indexOf(f)){var l=f;f=g;g=l}var p=!1;return k.filter(function(u){u===g&&(p=!0);return u===f?(p=!1,!0):p})};a.cells({selected:!0}).any()||c?(d=d(c.column,b.column),c=e(c.row,b.row)):(d=d(0,b.column),c=e(0,b.row));c=a.cells(c,d).flatten();a.cells(b,{selected:!0}).any()?a.cells(c).deselect():a.cells(c).select()}function A(a){var b=a.settings()[0]._select.selector;h(a.table().container()).off("mousedown.dtSelect",b).off("mouseup.dtSelect",
|
||||
b).off("click.dtSelect",b);h("body").off("click.dtSelect"+a.table().node().id.replace(/[^a-zA-Z0-9\-_]/g,"-"))}function F(a){var b=h(a.table().container()),c=a.settings()[0],d=c._select.selector,e;b.on("mousedown.dtSelect",d,function(g){if(g.shiftKey||g.metaKey||g.ctrlKey)b.css("-moz-user-select","none").one("selectstart.dtSelect",d,function(){return!1});q.getSelection&&(e=q.getSelection())}).on("mouseup.dtSelect",d,function(){b.css("-moz-user-select","")}).on("click.dtSelect",d,function(g){var f=
|
||||
a.select.items();if(e){var k=q.getSelection();if((!k.anchorNode||h(k.anchorNode).closest("table")[0]===a.table().node())&&k!==e)return}k=a.settings()[0];var l=a.settings()[0].oClasses.sWrapper.trim().replace(/ +/g,".");if(h(g.target).closest("div."+l)[0]==a.table().container()&&(l=a.cell(h(g.target).closest("td, th")),l.any())){var p=h.Event("user-select.dt");r(a,p,[f,l,g]);p.isDefaultPrevented()||(p=l.index(),"row"===f?(f=p.row,B(g,a,k,"row",f)):"column"===f?(f=l.index().column,B(g,a,k,"column",
|
||||
f)):"cell"===f&&(f=l.index(),B(g,a,k,"cell",f)),k._select_lastCell=p)}});h("body").on("click.dtSelect"+a.table().node().id.replace(/[^a-zA-Z0-9\-_]/g,"-"),function(g){!c._select.blurable||h(g.target).parents().filter(a.table().container()).length||0===h(g.target).parents("html").length||h(g.target).parents("div.DTE").length||x(c,!0)})}function r(a,b,c,d){if(!d||a.flatten().length)"string"===typeof b&&(b+=".dt"),c.unshift(a),h(a.table().node()).trigger(b,c)}function I(a){var b=a.settings()[0];if(b._select.info&&
|
||||
b.aanFeatures.i&&"api"!==a.select.style()){var c=a.rows({selected:!0}).flatten().length,d=a.columns({selected:!0}).flatten().length,e=a.cells({selected:!0}).flatten().length,g=function(f,k,l){f.append(h('<span class="select-item"/>').append(a.i18n("select."+k+"s",{_:"%d "+k+"s selected",0:"",1:"1 "+k+" selected"},l)))};h.each(b.aanFeatures.i,function(f,k){k=h(k);f=h('<span class="select-info"/>');g(f,"row",c);g(f,"column",d);g(f,"cell",e);var l=k.children("span.select-info");l.length&&l.remove();
|
||||
""!==f.text()&&k.append(f)})}}function J(a){var b=new m.Api(a);a.aoRowCreatedCallback.push({fn:function(c,d,e){d=a.aoData[e];d._select_selected&&h(c).addClass(a._select.className);c=0;for(e=a.aoColumns.length;c<e;c++)(a.aoColumns[c]._select_selected||d._selected_cells&&d._selected_cells[c])&&h(d.anCells[c]).addClass(a._select.className)},sName:"select-deferRender"});b.on("preXhr.dt.dtSelect",function(c,d){if(d===b.settings()[0]){var e=b.rows({selected:!0}).ids(!0).filter(function(f){return f!==n}),
|
||||
g=b.cells({selected:!0}).eq(0).map(function(f){var k=b.row(f.row).id(!0);return k?{row:k,column:f.column}:n}).filter(function(f){return f!==n});b.one("draw.dt.dtSelect",function(){b.rows(e).select();g.any()&&g.each(function(f){b.cells(f.row,f.column).select()})})}});b.on("draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt",function(){I(b)});b.on("destroy.dtSelect",function(){b.rows({selected:!0}).deselect();A(b);b.off(".dtSelect")})}function G(a,b,c,d){var e=a[b+"s"]({search:"applied"}).indexes();
|
||||
d=h.inArray(d,e);var g=h.inArray(c,e);if(a[b+"s"]({selected:!0}).any()||-1!==d){if(d>g){var f=g;g=d;d=f}e.splice(g+1,e.length);e.splice(0,d)}else e.splice(h.inArray(c,e)+1,e.length);a[b](c,{selected:!0}).any()?(e.splice(h.inArray(c,e),1),a[b+"s"](e).deselect()):a[b+"s"](e).select()}function x(a,b){if(b||"single"===a._select.style)a=new m.Api(a),a.rows({selected:!0}).deselect(),a.columns({selected:!0}).deselect(),a.cells({selected:!0}).deselect()}function B(a,b,c,d,e){var g=b.select.style(),f=b.select.toggleable(),
|
||||
k=b[d](e,{selected:!0}).any();if(!k||f)"os"===g?a.ctrlKey||a.metaKey?b[d](e).select(!k):a.shiftKey?"cell"===d?E(b,e,c._select_lastCell||null):G(b,d,e,c._select_lastCell?c._select_lastCell[d]:null):(a=b[d+"s"]({selected:!0}),k&&1===a.flatten().length?b[d](e).deselect():(a.deselect(),b[d](e).select())):"multi+shift"==g?a.shiftKey?"cell"===d?E(b,e,c._select_lastCell||null):G(b,d,e,c._select_lastCell?c._select_lastCell[d]:null):b[d](e).select(!k):b[d](e).select(!k)}function y(a,b){return function(c){return c.i18n("buttons."+
|
||||
a,b)}}function C(a){a=a._eventNamespace;return"draw.dt.DT"+a+" select.dt.DT"+a+" deselect.dt.DT"+a}function K(a,b){return-1!==h.inArray("rows",b.limitTo)&&a.rows({selected:!0}).any()||-1!==h.inArray("columns",b.limitTo)&&a.columns({selected:!0}).any()||-1!==h.inArray("cells",b.limitTo)&&a.cells({selected:!0}).any()?!0:!1}var m=h.fn.dataTable;m.select={};m.select.version="1.3.3";m.select.init=function(a){var b=a.settings()[0],c=b.oInit.select,d=m.defaults.select;c=c===n?d:c;d="row";var e="api",g=!1,
|
||||
f=!0,k=!0,l="td, th",p="selected",u=!1;b._select={};!0===c?(e="os",u=!0):"string"===typeof c?(e=c,u=!0):h.isPlainObject(c)&&(c.blurable!==n&&(g=c.blurable),c.toggleable!==n&&(f=c.toggleable),c.info!==n&&(k=c.info),c.items!==n&&(d=c.items),e=c.style!==n?c.style:"os",u=!0,c.selector!==n&&(l=c.selector),c.className!==n&&(p=c.className));a.select.selector(l);a.select.items(d);a.select.style(e);a.select.blurable(g);a.select.toggleable(f);a.select.info(k);b._select.className=p;h.fn.dataTable.ext.order["select-checkbox"]=
|
||||
function(z,L){return this.api().column(L,{order:"index"}).nodes().map(function(H){return"row"===z._select.items?h(H).parent().hasClass(z._select.className):"cell"===z._select.items?h(H).hasClass(z._select.className):!1})};!u&&h(a.table().node()).hasClass("selectable")&&a.select.style("os")};h.each([{type:"row",prop:"aoData"},{type:"column",prop:"aoColumns"}],function(a,b){m.ext.selector[b.type].push(function(c,d,e){d=d.selected;var g=[];if(!0!==d&&!1!==d)return e;for(var f=0,k=e.length;f<k;f++){var l=
|
||||
c[b.prop][e[f]];(!0===d&&!0===l._select_selected||!1===d&&!l._select_selected)&&g.push(e[f])}return g})});m.ext.selector.cell.push(function(a,b,c){b=b.selected;var d=[];if(b===n)return c;for(var e=0,g=c.length;e<g;e++){var f=a.aoData[c[e].row];(!0===b&&f._selected_cells&&!0===f._selected_cells[c[e].column]||!(!1!==b||f._selected_cells&&f._selected_cells[c[e].column]))&&d.push(c[e])}return d});var v=m.Api.register,w=m.Api.registerPlural;v("select()",function(){return this.iterator("table",function(a){m.select.init(new m.Api(a))})});
|
||||
v("select.blurable()",function(a){return a===n?this.context[0]._select.blurable:this.iterator("table",function(b){b._select.blurable=a})});v("select.toggleable()",function(a){return a===n?this.context[0]._select.toggleable:this.iterator("table",function(b){b._select.toggleable=a})});v("select.info()",function(a){return a===n?this.context[0]._select.info:this.iterator("table",function(b){b._select.info=a})});v("select.items()",function(a){return a===n?this.context[0]._select.items:this.iterator("table",
|
||||
function(b){b._select.items=a;r(new m.Api(b),"selectItems",[a])})});v("select.style()",function(a){return a===n?this.context[0]._select.style:this.iterator("table",function(b){b._select.style=a;b._select_init||J(b);var c=new m.Api(b);A(c);"api"!==a&&F(c);r(new m.Api(b),"selectStyle",[a])})});v("select.selector()",function(a){return a===n?this.context[0]._select.selector:this.iterator("table",function(b){A(new m.Api(b));b._select.selector=a;"api"!==b._select.style&&F(new m.Api(b))})});w("rows().select()",
|
||||
"row().select()",function(a){var b=this;if(!1===a)return this.deselect();this.iterator("row",function(c,d){x(c);c.aoData[d]._select_selected=!0;h(c.aoData[d].nTr).addClass(c._select.className)});this.iterator("table",function(c,d){r(b,"select",["row",b[d]],!0)});return this});w("columns().select()","column().select()",function(a){var b=this;if(!1===a)return this.deselect();this.iterator("column",function(c,d){x(c);c.aoColumns[d]._select_selected=!0;d=(new m.Api(c)).column(d);h(d.header()).addClass(c._select.className);
|
||||
h(d.footer()).addClass(c._select.className);d.nodes().to$().addClass(c._select.className)});this.iterator("table",function(c,d){r(b,"select",["column",b[d]],!0)});return this});w("cells().select()","cell().select()",function(a){var b=this;if(!1===a)return this.deselect();this.iterator("cell",function(c,d,e){x(c);d=c.aoData[d];d._selected_cells===n&&(d._selected_cells=[]);d._selected_cells[e]=!0;d.anCells&&h(d.anCells[e]).addClass(c._select.className)});this.iterator("table",function(c,d){r(b,"select",
|
||||
["cell",b.cells(b[d]).indexes().toArray()],!0)});return this});w("rows().deselect()","row().deselect()",function(){var a=this;this.iterator("row",function(b,c){b.aoData[c]._select_selected=!1;b._select_lastCell=null;h(b.aoData[c].nTr).removeClass(b._select.className)});this.iterator("table",function(b,c){r(a,"deselect",["row",a[c]],!0)});return this});w("columns().deselect()","column().deselect()",function(){var a=this;this.iterator("column",function(b,c){b.aoColumns[c]._select_selected=!1;var d=
|
||||
new m.Api(b),e=d.column(c);h(e.header()).removeClass(b._select.className);h(e.footer()).removeClass(b._select.className);d.cells(null,c).indexes().each(function(g){var f=b.aoData[g.row],k=f._selected_cells;!f.anCells||k&&k[g.column]||h(f.anCells[g.column]).removeClass(b._select.className)})});this.iterator("table",function(b,c){r(a,"deselect",["column",a[c]],!0)});return this});w("cells().deselect()","cell().deselect()",function(){var a=this;this.iterator("cell",function(b,c,d){c=b.aoData[c];c._selected_cells[d]=
|
||||
!1;c.anCells&&!b.aoColumns[d]._select_selected&&h(c.anCells[d]).removeClass(b._select.className)});this.iterator("table",function(b,c){r(a,"deselect",["cell",a[c]],!0)});return this});var D=0;h.extend(m.ext.buttons,{selected:{text:y("selected","Selected"),className:"buttons-selected",limitTo:["rows","columns","cells"],init:function(a,b,c){var d=this;c._eventNamespace=".select"+D++;a.on(C(c),function(){d.enable(K(a,c))});this.disable()},destroy:function(a,b,c){a.off(c._eventNamespace)}},selectedSingle:{text:y("selectedSingle",
|
||||
"Selected single"),className:"buttons-selected-single",init:function(a,b,c){var d=this;c._eventNamespace=".select"+D++;a.on(C(c),function(){var e=a.rows({selected:!0}).flatten().length+a.columns({selected:!0}).flatten().length+a.cells({selected:!0}).flatten().length;d.enable(1===e)});this.disable()},destroy:function(a,b,c){a.off(c._eventNamespace)}},selectAll:{text:y("selectAll","Select all"),className:"buttons-select-all",action:function(){this[this.select.items()+"s"]().select()}},selectNone:{text:y("selectNone",
|
||||
"Deselect all"),className:"buttons-select-none",action:function(){x(this.settings()[0],!0)},init:function(a,b,c){var d=this;c._eventNamespace=".select"+D++;a.on(C(c),function(){var e=a.rows({selected:!0}).flatten().length+a.columns({selected:!0}).flatten().length+a.cells({selected:!0}).flatten().length;d.enable(0<e)});this.disable()},destroy:function(a,b,c){a.off(c._eventNamespace)}}});h.each(["Row","Column","Cell"],function(a,b){var c=b.toLowerCase();m.ext.buttons["select"+b+"s"]={text:y("select"+
|
||||
b+"s","Select "+c+"s"),className:"buttons-select-"+c+"s",action:function(){this.select.items(c)},init:function(d){var e=this;d.on("selectItems.dt.DT",function(g,f,k){e.active(k===c)})}}});h(t).on("preInit.dt.dtSelect",function(a,b){"dt"===a.namespace&&m.select.init(new m.Api(b))});return m.select});
|
||||
@@ -243,6 +243,12 @@ $db->close();
|
||||
<?= lang('Maintenance_Tools_Tab_Logging');?>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="tab_multiEdit_id" href="#tab_multiEdit" data-toggle="tab">
|
||||
<i class="fa fa-pencil pointer" ></i>
|
||||
<?= lang('Device_MultiEdit');?>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="tab_Settings">
|
||||
@@ -501,8 +507,21 @@ $db->close();
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- ---------------------------Bulk edit -------------------------------------------- -->
|
||||
<div class="tab-pane" id="tab_multiEdit">
|
||||
<div class="db_info_table">
|
||||
<div class="box box-solid">
|
||||
<?php
|
||||
require 'multiEditCore.php';
|
||||
?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- ------------------------------------------------------------------------------ -->
|
||||
|
||||
</div>
|
||||
|
||||
<div class="box-body" style="text-align: center;">
|
||||
@@ -763,20 +782,34 @@ function performLogManage() {
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// scroll down the log areas
|
||||
function scrollDown()
|
||||
{
|
||||
var areaIDs = ['pialert_log', 'pialert_front_log', 'IP_changes_log', 'stdout_log', 'stderr_log', 'pialert_pholus_log', 'pialert_pholus_lastrun_log', 'pialert_php_log'];
|
||||
|
||||
for (let i = 0; i < areaIDs.length; i++) {
|
||||
setTimeout(() => {
|
||||
|
||||
var tempArea = $('#' + areaIDs[i]);
|
||||
|
||||
if (tempArea.length > 0)
|
||||
var elementToCheck = $("#tab_Logging_id");
|
||||
|
||||
// Check if the parent <li> is active
|
||||
if (elementToCheck.parent().hasClass("active")) {
|
||||
{
|
||||
$(tempArea[0]).scrollTop(tempArea[0].scrollHeight);
|
||||
}
|
||||
var areaIDs = ['pialert_log', 'pialert_front_log', 'IP_changes_log', 'stdout_log', 'stderr_log', 'pialert_pholus_log', 'pialert_pholus_lastrun_log', 'pialert_php_log'];
|
||||
|
||||
for (let i = 0; i < areaIDs.length; i++) {
|
||||
|
||||
}
|
||||
var tempArea = $('#' + areaIDs[i]);
|
||||
|
||||
if (tempArea.length > 0)
|
||||
{
|
||||
$(tempArea[0]).scrollTop(tempArea[0].scrollHeight);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, 200);
|
||||
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
@@ -827,37 +860,12 @@ function initializeSelectedColumns () {
|
||||
|
||||
$("#columnsSelect").append(option).trigger('change');
|
||||
|
||||
$(option).attr('eee','eee')
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
//Initialize Select2 Elements and make them sortable
|
||||
|
||||
$(function () {
|
||||
var selectEl = $('.select2').select2();
|
||||
|
||||
selectEl.next().children().children().children().sortable({
|
||||
containment: 'parent',
|
||||
update: function () {
|
||||
var sortedValues = $(this).children().map(function() {
|
||||
return $(this).attr('title');
|
||||
}).get();
|
||||
|
||||
var sortedOptions = selectEl.find('option').sort(function(a, b) {
|
||||
return sortedValues.indexOf($(a).text()) - sortedValues.indexOf($(b).text());
|
||||
});
|
||||
|
||||
// Replace all options in selectEl
|
||||
selectEl.empty().append(sortedOptions);
|
||||
|
||||
// Trigger change event on Select2
|
||||
selectEl.trigger('change');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// --------------------------------------------------------
|
||||
@@ -865,72 +873,79 @@ $(function () {
|
||||
// --------------------------------------------------------
|
||||
function initializeTabs () {
|
||||
|
||||
key = "activeMaintenanceTab"
|
||||
setTimeout(function() {
|
||||
|
||||
// default selection
|
||||
selectedTab = "tab_Settings"
|
||||
key = "activeMaintenanceTab"
|
||||
|
||||
// the #target from the url
|
||||
target = window.location.hash.substr(1)
|
||||
// default selection
|
||||
selectedTab = "tab_Settings"
|
||||
|
||||
// update cookie if target specified
|
||||
if(target != "")
|
||||
{
|
||||
setCache(key, target+'_id') // _id is added so it doesn't conflict with AdminLTE tab behavior
|
||||
}
|
||||
// the #target from the url
|
||||
target = window.location.hash.substr(1)
|
||||
|
||||
// get the tab id from the cookie (already overriden by the target)
|
||||
if(!emptyArr.includes(getCache(key)))
|
||||
{
|
||||
selectedTab = getCache(key);
|
||||
}
|
||||
|
||||
// Activate panel
|
||||
$('.nav-tabs a[id='+ selectedTab +']').tab('show');
|
||||
|
||||
// When changed save new current tab
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
setCache(key, $(e.target).attr('id'))
|
||||
});
|
||||
|
||||
// events on tab change
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
var target = $(e.target).attr("href") // activated tab
|
||||
|
||||
if(target == "#tab_Logging")
|
||||
|
||||
// get only the part between #...?
|
||||
if(target.includes('?'))
|
||||
{
|
||||
scrollDown();
|
||||
target = target.split('?')[0]
|
||||
}
|
||||
});
|
||||
|
||||
console.log(target);
|
||||
|
||||
// update cookie if target specified
|
||||
if(target != "")
|
||||
{
|
||||
|
||||
if (!selectedTab.endsWith("_id")) {
|
||||
selectedTab = target + "_id";
|
||||
}
|
||||
|
||||
setCache(key, selectedTab) // _id is added so it doesn't conflict with AdminLTE tab behavior
|
||||
}
|
||||
|
||||
// get the tab id from the cookie (already overriden by the target)
|
||||
if(!emptyArr.includes(getCache(key)))
|
||||
{
|
||||
selectedTab = getCache(key);
|
||||
}
|
||||
|
||||
// Activate panel
|
||||
$('.nav-tabs a[id='+ selectedTab +']').tab('show');
|
||||
|
||||
// When changed save new current tab
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
setCache(key, $(e.target).attr('id'))
|
||||
});
|
||||
|
||||
// events on tab change
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
var target = $(e.target).attr("href") // activated tab
|
||||
});
|
||||
|
||||
}, 50);
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
// save language in a cookie
|
||||
$('#langselector').on('change', function (e) {
|
||||
var optionSelected = $("option:selected", this);
|
||||
var valueSelected = this.value;
|
||||
setCookie("language",valueSelected )
|
||||
location.reload();
|
||||
});
|
||||
// --------------------------------------------------------
|
||||
|
||||
// load footer asynchronously not to block the page load/other sections
|
||||
window.onload = function asyncFooter()
|
||||
{
|
||||
initializeSelectedColumns();
|
||||
|
||||
scrollDown();
|
||||
|
||||
initializeTabs();
|
||||
|
||||
$("#lastCommit").append('<a href="https://github.com/jokob-sk/Pi.Alert/commits" target="_blank"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/jokob-sk/pi.alert/main?logo=github"></a>');
|
||||
|
||||
$("#lastDockerUpdate").append(
|
||||
'<a href="https://hub.docker.com/r/jokobsk/pi.alert/tags" target="_blank"><img alt="Docker last pushed" src="https://img.shields.io/badge/dynamic/json?color=blue&label=Last%20pushed&query=last_updated&url=https%3A%2F%2Fhub.docker.com%2Fv2%2Frepositories%2Fjokobsk%2Fpi.alert%2F&logo=docker&?link=http://left&link=https://hub.docker.com/repository/docker/jokobsk/pi.alert"></a>');
|
||||
'<a href="https://github.com/jokob-sk/Pi.Alert/releases" target="_blank"><img alt="Docker last pushed" src="https://img.shields.io/github/v/release/jokob-sk/Pi.Alert?color=0aa8d2&logoColor=fff&logo=GitHub&label=Latest"></a>');
|
||||
|
||||
}
|
||||
|
||||
// scroll to the latest log entrie sat teh bottom of the file
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/select2/dist/css/select2.min.css">
|
||||
@@ -938,6 +953,3 @@ window.onload = function asyncFooter()
|
||||
<script src="lib/AdminLTE/bower_components/jquery-ui/jquery-ui.min.js"></script>
|
||||
|
||||
|
||||
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
268
front/multiEditCore.php
Executable file
268
front/multiEditCore.php
Executable file
@@ -0,0 +1,268 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="box box-default">
|
||||
|
||||
<div class="box-header">
|
||||
|
||||
<h3 class="box-title"><?= lang('Gen_Selected_Devices');?></h3>
|
||||
|
||||
</div>
|
||||
<div class="deviceSelector"></div>
|
||||
|
||||
<div class="callout callout-warning">
|
||||
<h4><?= lang('Gen_Warning');?></h4>
|
||||
|
||||
<p><?= lang('Device_MultiEdit_Backup');?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="box box-default">
|
||||
|
||||
<div class="box-header">
|
||||
<h3 class="box-title"><?= lang('Device_MultiEdit_Fields');?></h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<form id="multi-edit-form">
|
||||
<!-- Form fields will be appended here -->
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="box box-default">
|
||||
<div class="box-header ">
|
||||
<h3 class="box-title"><?= lang('Device_MultiEdit_MassActions');?></h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
|
||||
<div class="col-md-2" style="">
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red" id="btnDeleteMAC" onclick="askDeleteSelectedDevices()"><?= lang('Maintenance_Tool_del_selecteddev');?></button>
|
||||
</div>
|
||||
<div class="col-md-10"><?= lang('Maintenance_Tool_del_selecteddev_text');?></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script defer>
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Get plugin and settings data from API endpoints
|
||||
function getData(){
|
||||
|
||||
$.get('api/table_settings.json?nocache=' + Date.now(), function(res) {
|
||||
|
||||
settingsData = res["data"];
|
||||
|
||||
excludedColumns = ["NEWDEV_dev_MAC", "NEWDEV_dev_FirstConnection", "NEWDEV_dev_LastConnection", "NEWDEV_dev_LastNotification", "NEWDEV_dev_LastIP", "NEWDEV_dev_StaticIP", "NEWDEV_dev_ScanCycle", "NEWDEV_dev_PresentLastScan" ]
|
||||
|
||||
const relevantColumns = settingsData.filter(set =>
|
||||
set.Group === "NEWDEV" &&
|
||||
set.Code_Name.includes("_dev_") &&
|
||||
!excludedColumns.includes(set.Code_Name) &&
|
||||
!set.Code_Name.includes("__metadata")
|
||||
);
|
||||
|
||||
const generateSimpleForm = columns => {
|
||||
const form = $('#multi-edit-form');
|
||||
const numColumns = 2; // Number of columns
|
||||
|
||||
// Calculate number of elements per column
|
||||
const elementsPerColumn = Math.ceil(columns.length / numColumns);
|
||||
|
||||
// Divide columns equally
|
||||
for (let i = 0; i < numColumns; i++) {
|
||||
const column = $('<div>').addClass('col-md-6');
|
||||
|
||||
// Append form groups to the column
|
||||
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, columns.length); j++) {
|
||||
|
||||
let inputType;
|
||||
|
||||
switch (columns[j].Type) {
|
||||
case 'integer.checkbox':
|
||||
case 'checkbox':
|
||||
inputType = 'checkbox';
|
||||
break;
|
||||
case 'text.select':
|
||||
inputType = 'text.select';
|
||||
break;
|
||||
default:
|
||||
inputType = 'text';
|
||||
break;
|
||||
}
|
||||
|
||||
// Add classes specifically for checkboxes
|
||||
if (inputType === 'text.select') {
|
||||
|
||||
targetLocation = columns[j].Code_Name + "_initSettingDropdown"
|
||||
|
||||
initSettingDropdown(columns[j].Code_Name, [], targetLocation)
|
||||
|
||||
input = `<select class="form-control"
|
||||
id="${columns[j].Code_Name}"
|
||||
data-my-column="${columns[j].Code_Name}"
|
||||
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}" >
|
||||
<option id="${targetLocation}"></option>
|
||||
</select>`
|
||||
} else {
|
||||
|
||||
if (inputType === 'checkbox') {
|
||||
inputClass = 'checkbox';
|
||||
|
||||
} else {
|
||||
inputClass = 'form-control';
|
||||
}
|
||||
|
||||
input = `<input class="${inputClass}"
|
||||
id="${columns[j].Code_Name}"
|
||||
data-my-column="${columns[j].Code_Name}"
|
||||
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}"
|
||||
type="${inputType}">`
|
||||
}
|
||||
|
||||
|
||||
const inputEntry = `<div class="form-group col-sm-12" >
|
||||
<label class="col-sm-3 control-label">${columns[j].Display_Name}</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group red-hover-border">
|
||||
${input}
|
||||
<span class="input-group-addon pointer red-hover-background" onclick="massUpdateField('${columns[j].Code_Name}');" title="${getString('Device_MultiEdit_Tooltip')}">
|
||||
<i class="fa fa-save"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
|
||||
column.append(inputEntry);
|
||||
}
|
||||
|
||||
form.append(column);
|
||||
}
|
||||
};
|
||||
|
||||
console.log(relevantColumns)
|
||||
|
||||
generateSimpleForm(relevantColumns);
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Get selected devices Macs
|
||||
function selectorMacs () {
|
||||
return $('.deviceSelector select').val().join(',');
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Update specified field over the specified DB column and selected entry(ies)
|
||||
function massUpdateField(id) {
|
||||
|
||||
// Get the input element
|
||||
var inputElement = $(`#${id}`);
|
||||
|
||||
console.log(inputElement);
|
||||
console.log(id);
|
||||
|
||||
// Initialize columnValue variable
|
||||
var columnValue;
|
||||
|
||||
// Check the type of the input element
|
||||
if (inputElement.is(':checkbox')) {
|
||||
// For checkboxes, set the value to 1 if checked, otherwise set it to 0
|
||||
columnValue = inputElement.is(':checked') ? 1 : 0;
|
||||
} else {
|
||||
// For other input types (like textboxes), simply retrieve their values
|
||||
columnValue = inputElement.val();
|
||||
}
|
||||
|
||||
var targetColumns = inputElement.attr('data-my-targetColumns');
|
||||
|
||||
|
||||
console.log(targetColumns);
|
||||
console.log(columnValue);
|
||||
|
||||
// update selected
|
||||
executeAction('update', 'dev_MAC', selectorMacs(), targetColumns, columnValue )
|
||||
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// action: Represents the action to be performed, a CRUD operation like "update", "delete", etc.
|
||||
// whereColumnName: Specifies the name of the column used in the WHERE or SELECT statement for filtering.
|
||||
// key: Represents the unique identifier of the row or record to be acted upon.
|
||||
// targetColumns: Indicates the columns to be updated or affected by the action.
|
||||
// newTargetColumnValue: Specifies the new value to be assigned to the specified column(s).
|
||||
function executeAction(action, whereColumnName, key, targetColumns, newTargetColumnValue )
|
||||
{
|
||||
$.get(`php/server/dbHelper.php?action=${action}&dbtable=Devices&columnName=${whereColumnName}&id=${key}&columns=${targetColumns}&values=${newTargetColumnValue}`, function(data) {
|
||||
// console.log(data);
|
||||
|
||||
if (sanitize(data) == 'OK') {
|
||||
showMessage(getString('Gen_DataUpdatedUITakesTime'));
|
||||
// Remove navigation prompt "Are you sure you want to leave..."
|
||||
window.onbeforeunload = null;
|
||||
|
||||
// update API endpoints to refresh the UI
|
||||
updateApi()
|
||||
|
||||
} else {
|
||||
showMessage(getString('Gen_LockedDB'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Ask to delete selected devices
|
||||
function askDeleteSelectedDevices () {
|
||||
// Ask
|
||||
showModalWarning(
|
||||
getString('Maintenance_Tool_del_alldev_noti'),
|
||||
getString('Gen_AreYouSure'),
|
||||
getString('Gen_Cancel'),
|
||||
getString('Gen_Delete'),
|
||||
'deleteSelectedDevices');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Delete selected devices
|
||||
function deleteSelectedDevices()
|
||||
{
|
||||
|
||||
executeAction('delete', 'dev_MAC', selectorMacs() )
|
||||
|
||||
}
|
||||
|
||||
|
||||
getData();
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<script src="js/ui_components.js"></script>
|
||||
<script src="js/db_methods.js"></script>
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
@@ -454,7 +454,7 @@
|
||||
|
||||
<script src="lib/treeviz/index.js"></script>
|
||||
<script src="lib/treeviz/require.js"></script>
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
|
||||
<script>
|
||||
$.get('php/server/devices.php?action=getDevicesList&status=all&forceDefaultOrder', function(data) {
|
||||
@@ -724,6 +724,9 @@
|
||||
console.log(myHierarchy)
|
||||
|
||||
myTree.refresh(myHierarchy);
|
||||
|
||||
// hide spinning icon
|
||||
hideSpinner()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -840,6 +843,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
// show spinning icon
|
||||
showSpinner()
|
||||
|
||||
// init device names where macs are used
|
||||
initDeviceNamesFromMACs();
|
||||
|
||||
@@ -56,6 +56,10 @@
|
||||
$columns = $_REQUEST['columns'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['rawSql'])) {
|
||||
$rawSql = $_REQUEST['rawSql'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['dbtable'])) {
|
||||
$dbtable = $_REQUEST['dbtable'];
|
||||
}
|
||||
@@ -64,92 +68,148 @@
|
||||
if (isset ($_REQUEST['action']) && !empty ($_REQUEST['action'])) {
|
||||
$action = $_REQUEST['action'];
|
||||
switch ($action) {
|
||||
case 'create': create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values ); break;
|
||||
// case 'read' : read($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
|
||||
case 'update': update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
|
||||
case 'create': create($defaultValue, $expireMinutes, $dbtable, $columns, $values ); break;
|
||||
case 'read' : read($rawSql); break;
|
||||
case 'update': update($columnName, $id, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
|
||||
case 'delete': delete($columnName, $id, $dbtable); break;
|
||||
default: logServerConsole ('Action: '. $action); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// read
|
||||
//------------------------------------------------------------------------------
|
||||
function read($rawSql) {
|
||||
global $db;
|
||||
|
||||
// Construct the SQL query to select values
|
||||
$sql = $rawSql;
|
||||
|
||||
// Execute the SQL query
|
||||
$result = $db->query($sql);
|
||||
|
||||
// Check if the query executed successfully
|
||||
if (! $result == TRUE) {
|
||||
// Output an error message if the query failed
|
||||
echo "Error reading data\n\n " .$sql." \n\n". $db->lastErrorMsg();
|
||||
return;
|
||||
} else
|
||||
{
|
||||
// Output $result
|
||||
// Fetching rows from the result object and storing them in an array
|
||||
$rows = array();
|
||||
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
// Converting the array to JSON
|
||||
$json = json_encode($rows);
|
||||
|
||||
// Outputting the JSON
|
||||
echo $json;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// update
|
||||
//------------------------------------------------------------------------------
|
||||
function update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
|
||||
function update($columnName, $id, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
|
||||
|
||||
global $db;
|
||||
|
||||
// handle one or multiple columns
|
||||
if(strpos($columns, ',') !== false)
|
||||
{
|
||||
$columnsArr = explode(",", $columns);
|
||||
}else
|
||||
{
|
||||
$columnsArr = array($columns);
|
||||
// Handle one or multiple columns
|
||||
if(strpos($columns, ',') !== false) {
|
||||
$columnsArr = explode(",", $columns);
|
||||
} else {
|
||||
$columnsArr = array($columns);
|
||||
}
|
||||
|
||||
// handle one or multiple values
|
||||
if(strpos($values, ',') !== false)
|
||||
{
|
||||
$valuesArr = explode(",", $values);
|
||||
} else
|
||||
{
|
||||
$valuesArr = array($values);
|
||||
// Handle one or multiple values
|
||||
if(strpos($values, ',') !== false) {
|
||||
$valuesArr = explode(",", $values);
|
||||
} else {
|
||||
$valuesArr = array($values);
|
||||
}
|
||||
|
||||
// Handle one or multiple IDs
|
||||
if(strpos($id, ',') !== false) {
|
||||
$idsArr = explode(",", $id);
|
||||
$idsPlaceholder = rtrim(str_repeat('?,', count($idsArr)), ',');
|
||||
} else {
|
||||
$idsArr = array($id);
|
||||
$idsPlaceholder = '?';
|
||||
}
|
||||
|
||||
// Build column-value pairs string
|
||||
$columnValues = '';
|
||||
|
||||
$index = 0;
|
||||
foreach($columnsArr as $column)
|
||||
{
|
||||
$columnValues = $columnValues .' "' .$column.'" = "'.$valuesArr[$index] . '",' ;
|
||||
$index = $index + 1;
|
||||
foreach($columnsArr as $column) {
|
||||
$columnValues .= '"' . $column . '" = ?,';
|
||||
}
|
||||
// Remove trailing comma
|
||||
$columnValues = rtrim($columnValues, ',');
|
||||
|
||||
// Construct the SQL query
|
||||
$sql = 'UPDATE ' . $dbtable . ' SET ' . $columnValues . ' WHERE ' . $columnName . ' IN (' . $idsPlaceholder . ')';
|
||||
|
||||
$columnValues = substr($columnValues, 0, -1);
|
||||
|
||||
// Update value
|
||||
$sql = 'UPDATE '.$dbtable.' SET '. $columnValues .'
|
||||
WHERE "'. $columnName .'"="'. $id.'"';
|
||||
$result = $db->query($sql);
|
||||
// Prepare the statement
|
||||
$stmt = $db->prepare($sql);
|
||||
|
||||
if (! $result == TRUE) {
|
||||
echo "Error updating parameter\n\n$sql \n\n". $db->lastErrorMsg();
|
||||
return;
|
||||
// Check for errors
|
||||
if(!$stmt) {
|
||||
echo "Error preparing statement: " . $db->lastErrorMsg();
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind the parameters
|
||||
$paramTypes = str_repeat('s', count($columnsArr));
|
||||
foreach($valuesArr as $i => $value) {
|
||||
$stmt->bindValue($i + 1, $value);
|
||||
}
|
||||
foreach($idsArr as $i => $idValue) {
|
||||
$stmt->bindValue(count($valuesArr) + $i + 1, $idValue);
|
||||
}
|
||||
|
||||
// Execute the statement
|
||||
$result = $stmt->execute();
|
||||
|
||||
$changes = $db->changes();
|
||||
if ($changes == 0) {
|
||||
// Insert new value
|
||||
create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values);
|
||||
create( $defaultValue, $expireMinutes, $dbtable, $columns, $values);
|
||||
}
|
||||
|
||||
// update cache
|
||||
$uniqueHash = hash('ripemd160', $dbtable . $columns);
|
||||
setCache($uniqueHash, $values, $expireMinutes);
|
||||
|
||||
echo 'OK';
|
||||
echo 'OK' ;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// create
|
||||
//------------------------------------------------------------------------------
|
||||
function create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values)
|
||||
function create( $defaultValue, $expireMinutes, $dbtable, $columns, $values)
|
||||
{
|
||||
global $db;
|
||||
|
||||
// Insert new value
|
||||
$sql = 'INSERT INTO '.$dbtable.' ('.$columns.')
|
||||
VALUES ("'. quotes($parameter) .'",
|
||||
"'. $values .'")';
|
||||
$result = $db->query($sql);
|
||||
echo "NOT IMPLEMENTED!\n\n";
|
||||
return;
|
||||
|
||||
if (! $result == TRUE) {
|
||||
echo "Error creating entry\n\n$sql \n\n". $db->lastErrorMsg();
|
||||
return;
|
||||
}
|
||||
// // Insert new value
|
||||
// $sql = 'INSERT INTO '.$dbtable.' ('.$columns.')
|
||||
// VALUES ("'. quotes($parameter) .'",
|
||||
// "'. $values .'")';
|
||||
// $result = $db->query($sql);
|
||||
|
||||
// if (! $result == TRUE) {
|
||||
// echo "Error creating entry\n\n$sql \n\n". $db->lastErrorMsg();
|
||||
// return;
|
||||
// }
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -159,35 +219,49 @@ function delete($columnName, $id, $dbtable)
|
||||
{
|
||||
global $db;
|
||||
|
||||
// handle one or multiple ids
|
||||
// Handle one or multiple ids
|
||||
if(strpos($id, ',') !== false)
|
||||
{
|
||||
$idsArr = explode(",", $id);
|
||||
}else
|
||||
} else
|
||||
{
|
||||
$idsArr = array($id);
|
||||
}
|
||||
|
||||
// Initialize an empty string to store the comma-separated list of IDs
|
||||
$idsStr = "";
|
||||
|
||||
foreach ($idsArr as $item)
|
||||
// Iterate over each ID
|
||||
foreach ($idsArr as $index => $item)
|
||||
{
|
||||
$idsStr = $idsStr . '"' .$item.'"';
|
||||
// Append the current ID to the string
|
||||
$idsStr .= '"' . $item . '"';
|
||||
|
||||
// Add a comma if the current ID is not the last one
|
||||
if ($index < count($idsArr) - 1) {
|
||||
$idsStr .= ', ';
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new value
|
||||
// Construct the SQL query to delete entries based on the given IDs
|
||||
$sql = 'DELETE FROM '.$dbtable.' WHERE "'.$columnName.'" IN ('. $idsStr .')';
|
||||
|
||||
// Execute the SQL query
|
||||
$result = $db->query($sql);
|
||||
|
||||
// Check if the query executed successfully
|
||||
if (! $result == TRUE) {
|
||||
echo "Error deleting entry\n\n$sql \n\n". $db->lastErrorMsg();
|
||||
// Output an error message if the query failed
|
||||
echo "Error deleting entry\n\n".$sql." \n\n". $db->lastErrorMsg();
|
||||
return;
|
||||
} else
|
||||
{
|
||||
echo lang('Gen_DataUpdatedUITakesTime');
|
||||
// Output 'OK' if the deletion was successful
|
||||
echo 'OK' ;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
|
||||
@@ -185,7 +185,7 @@ function displayMessage($message, $logAlert = FALSE, $logConsole = TRUE, $logFil
|
||||
echo '<script>alert(escape("'.$message.'"));</script>';
|
||||
}
|
||||
|
||||
// F12 Browser console
|
||||
// F12 Browser dev console
|
||||
if($logConsole)
|
||||
{
|
||||
echo '<script>console.log(escape("'.str_replace('"',"'",$message).'"));</script>';
|
||||
@@ -194,16 +194,26 @@ function displayMessage($message, $logAlert = FALSE, $logConsole = TRUE, $logFil
|
||||
//File
|
||||
if($logFile)
|
||||
{
|
||||
if(file_exists($logFolderPath.$log_file) != 1) // file doesn't exist, create one
|
||||
{
|
||||
$log = fopen($logFolderPath.$log_file, "w") or die("Unable to open file!");
|
||||
}else // file exists, append
|
||||
{
|
||||
$log = fopen($logFolderPath.$log_file, "a") or die("Unable to open file!");
|
||||
|
||||
if (is_writable($logFolderPath.$log_file)) {
|
||||
|
||||
|
||||
if(file_exists($logFolderPath.$log_file) != 1) // file doesn't exist, create one
|
||||
{
|
||||
$log = fopen($logFolderPath.$log_file, "w") or die("Unable to open file!");
|
||||
}else // file exists, append
|
||||
{
|
||||
$log = fopen($logFolderPath.$log_file, "a") or die("Unable to open file - Permissions issue!");
|
||||
}
|
||||
|
||||
fwrite($log, "[".$timestamp. "] " . str_replace('<br>',"\n ",str_replace('<br/>',"\n ",$message)).PHP_EOL."" );
|
||||
fclose($log);
|
||||
|
||||
} else {
|
||||
echo 'The file is not writable: '.$logFolderPath.$log_file;
|
||||
}
|
||||
|
||||
fwrite($log, "[".$timestamp. "] " . str_replace('<br>',"\n ",str_replace('<br/>',"\n ",$message)).PHP_EOL."" );
|
||||
fclose($log);
|
||||
|
||||
}
|
||||
|
||||
//echo
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<!-- <script src="lib/AdminLTE/bower_components/fastclick/lib/fastclick.js"></script> -->
|
||||
|
||||
<!-- Pi.Alert -------------------------------------------------------------- -->
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
<script src="js/handle_version.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -28,16 +28,17 @@ require dirname(__FILE__).'/security.php';
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<!-- REQUIRED JS SCRIPTS -->
|
||||
|
||||
<!-- jQuery 3 -->
|
||||
<script src="lib/AdminLTE/bower_components/jquery/dist/jquery.min.js"></script>
|
||||
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
<!-- Bootstrap 3.3.7 -->
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/bootstrap/dist/css/bootstrap.min.css">
|
||||
|
||||
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/fontawesome.min.css">
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/solid.css">
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/brands.css">
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/v5-font-face.css">
|
||||
@@ -111,7 +112,7 @@ if ($ENABLED_DARKMODE === True) {
|
||||
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<!-- Layout Boxed Yellow -->
|
||||
<body class="hold-transition <?php echo $pia_skin_selected;?> sidebar-mini" <?php echo $BACKGROUND_IMAGE_PATCH;?> onLoad="show_pia_servertime();" >
|
||||
<body class="hold-transition fixed <?php echo $pia_skin_selected;?> sidebar-mini" <?php echo $BACKGROUND_IMAGE_PATCH;?> onLoad="show_pia_servertime();" >
|
||||
<!-- Site wrapper -->
|
||||
<div class="wrapper">
|
||||
|
||||
@@ -122,9 +123,14 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<!-- Logo -->
|
||||
<a href="devices.php" class="logo">
|
||||
<!-- mini logo for sidebar mini 50x50 pixels -->
|
||||
<span class="logo-mini">P<b>a</b></span>
|
||||
<span class="logo-mini">
|
||||
<img src="img/pialertLogoWhite.png" class="pia-top-left-logo" alt="Pi.Alert Logo"/>
|
||||
</span>
|
||||
<!-- logo for regular state and mobile devices -->
|
||||
<span class="logo-lg">Pi<b>.Alert</b></span>
|
||||
<span class="logo-lg">Pi<b>.Alert</b>
|
||||
|
||||
</span>
|
||||
|
||||
</a>
|
||||
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
@@ -145,14 +151,14 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<li>
|
||||
<a id="next-button" href="javascript:history.go(1);" role="button" span class='of-bt-icon'><i class='fa fa-arrow-right'></i></a>
|
||||
</li>
|
||||
<!-- Reload -->
|
||||
<!-- Clear cache & Reload -->
|
||||
<li>
|
||||
<a id="reload-button" href='#' role="button" span class='of-bt-icon' onclick='location.reload()'><i class='fa fa-repeat'></i></a>
|
||||
<a id="reload-button" href='#' role="button" span class='of-bt-icon' onclick='clearCache()'><i class='fa fa-repeat'></i></a>
|
||||
</li>
|
||||
<!-- Full Screen -->
|
||||
<li>
|
||||
<a id="fullscreen-button" href='#' role="button" span class='of-bt-icon' onclick='toggleFullscreen()'><i class='fa fa-arrows-alt'></i></a>
|
||||
</li>
|
||||
</li>
|
||||
<!-- Server Status -->
|
||||
<li>
|
||||
<a onclick="setCache('activeMaintenanceTab', 'tab_Logging_id')" href="maintenance.php#tab_Logging">
|
||||
@@ -183,7 +189,7 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<img src="img/pialertLogoWhite.png" class="img-circle" alt="Pi.Alert Logo" style="border-color:transparent; height: 50px; width: 50px; margin-top:15px;">
|
||||
<p style="float: right; width: 200px">
|
||||
<?= lang('About_Title');?>
|
||||
<small><?= lang('About_Design');?> Raspberry Pi</small>
|
||||
<small><?= lang('About_Design');?> Docker</small>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
@@ -208,62 +214,172 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<!-- sidebar: style can be found in sidebar.less -->
|
||||
<section class="sidebar">
|
||||
|
||||
<!-- Sidebar user panel (optional) -->
|
||||
<div class="user-panel"> <a href="." class="logo">
|
||||
<img src="img/pialertLogoGray80.png" class="img-responsive" alt="Pi.Alert Logo"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- search form (Optional) -->
|
||||
<!-- DELETED -->
|
||||
|
||||
<!-- Sidebar Menu -->
|
||||
<!-- Navigation Mneu -->
|
||||
<ul class="sidebar-menu" data-widget="tree">
|
||||
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('devices.php', 'deviceDetails.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="devices.php"><i class="fa fa-laptop"></i> <span><?= lang('Navigation_Devices');?></span></a>
|
||||
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('devices.php', 'deviceDetails.php') ) ){ echo 'active menu-open'; } ?>">
|
||||
<a href="#" onclick="openUrl(['./devices.php', './deviceDetails.php'])">
|
||||
|
||||
<i class="fa fa-fw fa-laptop"></i> <span><?= lang('Navigation_Devices');?></span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu" style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('devices.php', 'deviceDetails.php') ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<li>
|
||||
<a href="devices.php#my" onclick="initializeDatatable('my')" > <?= lang("Device_Shortcut_AllDevices");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#connected" onclick="initializeDatatable('connected')" > <?= lang("Device_Shortcut_Connected");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#favorites" onclick="initializeDatatable('favorites')" > <?= lang("Device_Shortcut_Favorites");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#new" onclick="initializeDatatable('new')" > <?= lang("Device_Shortcut_NewDevices");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#down" onclick="initializeDatatable('down')" > <?= lang("Device_Shortcut_DownAlerts");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#archived" onclick="initializeDatatable('archived')" > <?= lang("Device_Shortcut_Archived");?> </a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="presence.php"><i class="fa fa-calendar"></i> <span><?= lang('Navigation_Presence');?></span></a>
|
||||
<!-- Monitoring menu item -->
|
||||
|
||||
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php', 'report.php', 'events.php' ) ) ){ echo 'active menu-open'; } ?>">
|
||||
<a href="#">
|
||||
<i class="fa fa-fw fa-chart-bar"></i> <span><?= lang('Navigation_Monitoring');?></span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php', 'report.php', 'events.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<li>
|
||||
<a href="presence.php"> <?= lang("Navigation_Presence");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="events.php"> <?= lang("Navigation_Events");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="report.php"> <?= lang("Navigation_Report");?> </a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('events.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="events.php"><i class="fa fa-bolt"></i> <span><?= lang('Navigation_Events');?></span></a>
|
||||
</li>
|
||||
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('report.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="report.php"><i class="fa fa-flag"></i> <span><?= lang('Navigation_Report');?></span></a>
|
||||
</li>
|
||||
|
||||
<!-- Network menu item -->
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('network.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="network.php"><i class="fa fa-fw fa-network-wired"></i> <span><?= lang('Navigation_Network');?></span></a>
|
||||
</li>
|
||||
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="plugins.php"><i class="fa fa-fw fa-plug"></i> <span><?= lang('Navigation_Plugins');?></span></a>
|
||||
<!-- Maintenance menu item -->
|
||||
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'active menu-open'; } ?>">
|
||||
<a href="#" onclick="openUrl(['./maintenance.php'])">
|
||||
<div class="info-icon-nav myhidden" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>">🆕</div>
|
||||
<i class="fa fa-fw fa-wrench"></i> <span><?= lang('Navigation_Maintenance');?></span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu" style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<li>
|
||||
<a href="maintenance.php#tab_Settings" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_UISettings");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="maintenance.php#tab_DBTools" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_Tools");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="maintenance.php#tab_BackupRestore" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_BackupRestore");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="maintenance.php#tab_Logging" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_Logging");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="maintenance.php#tab_multiEdit" onclick="initializeTabs()"> <?= lang("Device_MultiEdit");?> </a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'active'; } ?>">
|
||||
<div class="info-icon-nav myhidden" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>">🆕</div>
|
||||
<a href="maintenance.php"><i class="fa fa-wrench "></i> <span><?= lang('Navigation_Maintenance');?></span></a>
|
||||
<!-- Settings menu item -->
|
||||
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('settings.php') ) ){ echo 'active menu-open'; } ?>">
|
||||
<a href="#" onclick="openUrl(['./settings.php'])">
|
||||
<i class="fa fa-fw fa-cog"></i> <span><?= lang('Navigation_Settings');?></span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu" style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('settings.php') ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<li>
|
||||
<a href="settings.php#pageTitle"> <?= lang("settings_enabled");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="settings.php#core_content_header"> <?= lang("settings_core_label");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="settings.php#system_content_header"> <?= lang("settings_system_label");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="settings.php#device_scanner_content_header"> <?= lang("settings_device_scanners_label");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="settings.php#other_content_header"> <?= lang("settings_other_scanners_label");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="settings.php#publisher_content_header"> <?= lang("settings_publishers_label");?> </a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('settings.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="settings.php"><i class="fa fa-cog"></i> <span><?= lang('Navigation_Settings');?></span></a>
|
||||
|
||||
<!-- Integrations menu item -->
|
||||
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php', 'workflows.php' ) ) ){ echo 'active menu-open'; } ?>">
|
||||
<a href="#">
|
||||
<i class="fa fa-fw fa-plug"></i> <span><?= lang('Navigation_Integrations');?></span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php', 'workflows.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<li>
|
||||
<div class="info-icon-nav work-in-progress"> </div>
|
||||
<a href="workflows.php"><?= lang('Navigation_Workflows');?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="plugins.php"><?= lang("Navigation_Plugins");?> </a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('workflows.php') ) ){ echo 'active'; } ?>">
|
||||
<div class="info-icon-nav work-in-progress"> </div>
|
||||
<a href="workflows.php"><i class="fa fa-shuffle"></i> <span><?= lang('Navigation_Workflows');?></span></a>
|
||||
</li>
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('systeminfo.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="systeminfo.php"><i class="fa fa-microchip"></i> <span><?= lang('Navigation_SystemInfo');?></span></a>
|
||||
</li>
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('help_faq.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="help_faq.php"><i class="fa fa-question"></i> <span><?= lang('Navigation_HelpFAQ');?></span></a>
|
||||
</li>
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('donations.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="donations.php"><i class="fa fa-heart"></i> <span><?= lang('Navigation_Donations');?></span></a>
|
||||
|
||||
<!-- About menu item -->
|
||||
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('donations.php', 'help_faq.php', 'systeminfo.php' ) ) ){ echo 'active menu-open'; } ?>">
|
||||
<a href="#">
|
||||
<i class="fa fa-fw fa-info"></i> <span><?= lang('Navigation_About');?></span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('donations.php', 'help_faq.php', 'systeminfo.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<li>
|
||||
<a href="donations.php"> <?= lang("Navigation_Donations");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="help_faq.php"> <?= lang("Navigation_HelpFAQ");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="systeminfo.php"> <?= lang("Navigation_SystemInfo");?> </a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<!-- /.sidebar-menu -->
|
||||
@@ -271,11 +387,25 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<!-- /.sidebar -->
|
||||
</aside>
|
||||
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
<script defer>
|
||||
|
||||
// Generate work-in-progress icons
|
||||
function workInProgress() {
|
||||
|
||||
if($(".work-in-progress").html().trim() == "")
|
||||
{
|
||||
$(".work-in-progress").append(`
|
||||
<a href="https://github.com/jokob-sk/Pi.Alert/issues" target="_blank">
|
||||
<b class="pointer" title="${getString("Gen_Work_In_Progress")}">🦺</b>
|
||||
</a>
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
|
||||
|
||||
//--------------------------------------------------------------
|
||||
|
||||
function toggleFullscreen() {
|
||||
@@ -295,5 +425,6 @@ if ($ENABLED_DARKMODE === True) {
|
||||
|
||||
// Update server state in the header
|
||||
updateState()
|
||||
workInProgress()
|
||||
|
||||
</script>
|
||||
|
||||
@@ -187,6 +187,11 @@
|
||||
"DevDetail_button_OverwriteIcons_Warning": "Are you sure you want to overwrite all icons of all devices with the same device type as the current device type?",
|
||||
"DevDetail_button_Reset": "Verwerfen",
|
||||
"DevDetail_button_Save": "Speichern",
|
||||
"Device_MultiEdit": "",
|
||||
"Device_MultiEdit_Backup": "",
|
||||
"Device_MultiEdit_Fields": "",
|
||||
"Device_MultiEdit_MassActions": "",
|
||||
"Device_MultiEdit_Tooltip": "",
|
||||
"Device_Searchbox": "Suche",
|
||||
"Device_Shortcut_AllDevices": "Alle Geräte",
|
||||
"Device_Shortcut_Archived": "Archiviert",
|
||||
@@ -280,6 +285,7 @@
|
||||
"Gen_Run": "Run",
|
||||
"Gen_Save": "Speichern",
|
||||
"Gen_Saved": "Gespeichert",
|
||||
"Gen_Selected_Devices": "",
|
||||
"Gen_Switch": "Umschalten",
|
||||
"Gen_Upd": "Aktualisierung erfolgreich",
|
||||
"Gen_Upd_Fail": "Aktualisierung fehlgeschlagen",
|
||||
@@ -399,6 +405,8 @@
|
||||
"Maintenance_Tool_del_empty_macs_noti": "Geräte löschen",
|
||||
"Maintenance_Tool_del_empty_macs_noti_text": "Sind Sie sicher, dass Sie alle Geräte ohne MAC-Adresse löschen wollen?<br>(Vielleicht bevorzugen Sie eine Archivierung)",
|
||||
"Maintenance_Tool_del_empty_macs_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen. Der Vor­gang kann ohne Back­up nicht rück­gängig gemacht werden. Alle Geäte ohne MAC-Adresse werden aus der Datenbank ge­löscht.",
|
||||
"Maintenance_Tool_del_selecteddev": "",
|
||||
"Maintenance_Tool_del_selecteddev_text": "",
|
||||
"Maintenance_Tool_del_unknowndev": "Löschen der (unknown) Geräte",
|
||||
"Maintenance_Tool_del_unknowndev_noti": "Lösche (unknown) Geräte",
|
||||
"Maintenance_Tool_del_unknowndev_noti_text": "Sind Sie sicher, dass Sie alle (unknown) Geräte aus der Datenbank löschen wollen?",
|
||||
@@ -434,9 +442,6 @@
|
||||
"Maintenance_database_path": "Datenbank-Pfad",
|
||||
"Maintenance_database_rows": "Tabelle (Reihen)",
|
||||
"Maintenance_database_size": "Datenbank-Größe",
|
||||
"Maintenance_lang_de_de": "Deutsch (DE)",
|
||||
"Maintenance_lang_en_us": "Englisch (US)",
|
||||
"Maintenance_lang_es_es": "Spanisch (ES)",
|
||||
"Maintenance_lang_selector_apply": "Übernehmen",
|
||||
"Maintenance_lang_selector_empty": "Sprache wählen",
|
||||
"Maintenance_lang_selector_lable": "Sprachauswahl",
|
||||
@@ -459,12 +464,15 @@
|
||||
"NTFY_USER_name": "NTFY user",
|
||||
"NTFY_display_name": "NTFY",
|
||||
"NTFY_icon": "<i class=\"fa fa-terminal\"></i>",
|
||||
"Navigation_About": "",
|
||||
"Navigation_Devices": "Geräte",
|
||||
"Navigation_Donations": "Donations",
|
||||
"Navigation_Events": "Ereignisse",
|
||||
"Navigation_Flows": "Flows",
|
||||
"Navigation_HelpFAQ": "Hilfe / FAQ",
|
||||
"Navigation_Integrations": "",
|
||||
"Navigation_Maintenance": "Wartung",
|
||||
"Navigation_Monitoring": "",
|
||||
"Navigation_Network": "Netzwerk",
|
||||
"Navigation_Plugins": "Plugins",
|
||||
"Navigation_Presence": "Anwesenheit",
|
||||
@@ -623,6 +631,10 @@
|
||||
"Systeminfo_Network_HTTP_Referer": "HTTP-Referer:",
|
||||
"Systeminfo_Network_HTTP_Referer_String": "Kein HTTP-Referer",
|
||||
"Systeminfo_Network_Hardware": "Netzwerk Hardware",
|
||||
"Systeminfo_Network_Hardware_Interface_Mask": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Name": "",
|
||||
"Systeminfo_Network_Hardware_Interface_RX": "",
|
||||
"Systeminfo_Network_Hardware_Interface_TX": "",
|
||||
"Systeminfo_Network_IP": "IP Internet:",
|
||||
"Systeminfo_Network_IP_Connection": "IP-Verbindung:",
|
||||
"Systeminfo_Network_IP_Server": "Server-IP:",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"API_icon": "<i class=\"fa fa-arrow-down-up-across-line\"></i>",
|
||||
"About_Design": "Designed for:",
|
||||
"About_Exit": "Sign out",
|
||||
"About_Title": "Open Source Network Guard",
|
||||
"About_Title": "Network security scanner & notification framework",
|
||||
"AppEvents_DateTimeCreated": "Logged",
|
||||
"AppEvents_Extra": "Extra",
|
||||
"AppEvents_GUID": "Application Event GUID",
|
||||
@@ -175,12 +175,17 @@
|
||||
"DevDetail_button_OverwriteIcons_Warning": "Are you sure you want to overwrite all icons of all devices with the same device type as the current device type?",
|
||||
"DevDetail_button_Reset": "Reset Changes",
|
||||
"DevDetail_button_Save": "Save",
|
||||
"Device_MultiEdit": "Multi-edit",
|
||||
"Device_MultiEdit_Backup": "Careful, entering wrong values below will break your setup. Please backup your database or Devices configuration first (<a href=\"php/server/devices.php?action=ExportCSV\">click to download <i class=\"fa-solid fa-download fa-bounce\"></i></a>). Read how to recover Devices from this file in the <a href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/BACKUPS.md#scenario-2-corrupted-database\" target=\"_blank\">Backups documentation</a>.",
|
||||
"Device_MultiEdit_Fields": "Edit fields:",
|
||||
"Device_MultiEdit_MassActions": "Mass actions:",
|
||||
"Device_MultiEdit_Tooltip": "Careful. Clicking this will apply the value on the left to all devices selected above.",
|
||||
"Device_Searchbox": "Search",
|
||||
"Device_Shortcut_AllDevices": "My Devices",
|
||||
"Device_Shortcut_Archived": "Archived",
|
||||
"Device_Shortcut_Connected": "Connected",
|
||||
"Device_Shortcut_Devices": "Devices",
|
||||
"Device_Shortcut_DownAlerts": "Down Alerts",
|
||||
"Device_Shortcut_DownAlerts": "Down & Offline",
|
||||
"Device_Shortcut_Favorites": "Favorites",
|
||||
"Device_Shortcut_NewDevices": "New Devices",
|
||||
"Device_Shortcut_OnlineChart": "Device presence",
|
||||
@@ -213,7 +218,7 @@
|
||||
"Device_Title": "Devices",
|
||||
"Donations_Others": "Others",
|
||||
"Donations_Platforms": "Sponsor platforms",
|
||||
"Donations_Text": "Hey \ud83d\udc4b! </br> Thanks for clicking on this menu item \ud83d\ude05 </br> </br> I'm trying to collect some donations to make you better software. Also, it would help me not to get burned out. Me burning out might mean end of support for this app. Any small (recurring or not) sponsorship makes me want to put more effort into this app. I don't want to lock features (new plugins) behind paywalls \ud83d\udd10. </br> Currently, I'm waking up 2h before work so I contribute to the app a bit. If I had some recurring income I could shorten my workweek and in the remaining time fully focus on PiAlert. You'd get more functionality, a more polished app and less bugs. </br> </br> Thanks for reading - I'm super grateful for any support \u2764\ud83d\ude4f </br> </br> TL;DR: By supporting me you get: </br> </br> <ul><li>Regular updates to keep your data and family safe \ud83d\udd04</li><li>Less bugs \ud83d\udc1b\ud83d\udd2b</li><li>Better and more functionality\u2795</li><li>I don't get burned out \ud83d\udd25\ud83e\udd2f</li><li>Less rushed releases \ud83d\udca8</li><li>Better docs\ud83d\udcda</li><li>Quicker and better support with issues \ud83c\udd98</li><li>Less grumpy me \ud83d\ude04</li></ul> </br> \ud83d\udce7Email me to <a href='mailto:jokob@duck.com?subject=PiAlert'>jokob@duck.com</a> if you want to get in touch or if I should add other sponsorship platforms. </br>",
|
||||
"Donations_Text": "Hey \ud83d\udc4b! </br> Thanks for clicking on this menu item \ud83d\ude05 </br> </br> I'm trying to collect some donations to make you better software. Also, it would help me not to get burned out, so I can support this app longer. Any small (recurring or not) sponsorship makes me want to put more effort into this app. </br> I'd love to shorten my work week and in the remaining time fully focus on PiAlert. You'd get more functionality, a more polished app and less bugs. </br> </br> Thanks for reading - I'm grateful for any support \u2764\ud83d\ude4f </br> </br> TL;DR: By supporting me you get: </br> </br> <ul><li>Regular updates to keep your data and family safe \ud83d\udd04</li><li>Less bugs \ud83d\udc1b\ud83d\udd2b</li><li>Better and more functionality\u2795</li><li>I don't get burned out \ud83d\udd25\ud83e\udd2f</li><li>Less rushed releases \ud83d\udca8</li><li>Better docs\ud83d\udcda</li><li>Quicker and better support with issues \ud83c\udd98</li></ul> </br> \ud83d\udce7Email me to <a href='mailto:jokob@duck.com?subject=PiAlert'>jokob@duck.com</a> if you want to get in touch or if I should add other sponsorship platforms. </br>",
|
||||
"Donations_Title": "Donations",
|
||||
"ENABLE_PLUGINS_description": "Enables the <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins\">plugins</a> functionality. Loading plugins requires more hardware resources so you might want to disable them on low-powered system.",
|
||||
"ENABLE_PLUGINS_name": "Enable Plugins",
|
||||
@@ -268,6 +273,7 @@
|
||||
"Gen_Run": "Run",
|
||||
"Gen_Save": "Save",
|
||||
"Gen_Saved": "Saved",
|
||||
"Gen_Selected_Devices": "Selected Devices:",
|
||||
"Gen_Switch": "Switch",
|
||||
"Gen_Upd": "Updated successfully",
|
||||
"Gen_Upd_Fail": "Update failed",
|
||||
@@ -373,6 +379,8 @@
|
||||
"Maintenance_Tool_del_empty_macs_noti": "Delete Devices",
|
||||
"Maintenance_Tool_del_empty_macs_noti_text": "Are you sure you want to delete all devices with empty MAC addresses?<br>(maybe you prefer to archive it)",
|
||||
"Maintenance_Tool_del_empty_macs_text": "Before using this function, please make a backup. The deletion cannot be undone. All devices without MAC will be deleted from the database.",
|
||||
"Maintenance_Tool_del_selecteddev": "Delete selected devices",
|
||||
"Maintenance_Tool_del_selecteddev_text": "Before using this function, please make a backup. The deletion cannot be undone. Selected devices will be deleted from the database.",
|
||||
"Maintenance_Tool_del_unknowndev": "Delete (unknown) Devices",
|
||||
"Maintenance_Tool_del_unknowndev_noti": "Delete (unknown) Devices",
|
||||
"Maintenance_Tool_del_unknowndev_noti_text": "Are you sure you want to delete all (unknown) and (name not found) devices?",
|
||||
@@ -408,9 +416,6 @@
|
||||
"Maintenance_database_path": "Database-Path",
|
||||
"Maintenance_database_rows": "Table (Rows)",
|
||||
"Maintenance_database_size": "Database-Size",
|
||||
"Maintenance_lang_de_de": "German (DE)",
|
||||
"Maintenance_lang_en_us": "English (US)",
|
||||
"Maintenance_lang_es_es": "Spanish (ES)",
|
||||
"Maintenance_lang_selector_apply": "Apply",
|
||||
"Maintenance_lang_selector_empty": "Choose Language",
|
||||
"Maintenance_lang_selector_lable": "Select Language",
|
||||
@@ -423,11 +428,14 @@
|
||||
"Maintenance_version": "App updates",
|
||||
"NETWORK_DEVICE_TYPES_description": "Which device types are allowed to be used as network devices in the Network view. The device type has to match exactly the <code>Type</code> setting on a specific device in Device details. Do not remove existing types, only add new ones.",
|
||||
"NETWORK_DEVICE_TYPES_name": "Network device types",
|
||||
"Navigation_About": "About",
|
||||
"Navigation_Devices": "Devices",
|
||||
"Navigation_Donations": "Donations",
|
||||
"Navigation_Events": "Events",
|
||||
"Navigation_HelpFAQ": "Help / FAQ",
|
||||
"Navigation_Integrations": "Integrations",
|
||||
"Navigation_Maintenance": "Maintenance",
|
||||
"Navigation_Monitoring": "Monitoring",
|
||||
"Navigation_Network": "Network",
|
||||
"Navigation_Plugins": "Plugins",
|
||||
"Navigation_Presence": "Presence",
|
||||
@@ -554,6 +562,10 @@
|
||||
"Systeminfo_Network_HTTP_Referer": "HTTP referrer:",
|
||||
"Systeminfo_Network_HTTP_Referer_String": "No HTTP referrer",
|
||||
"Systeminfo_Network_Hardware": "Network Hardware",
|
||||
"Systeminfo_Network_Hardware_Interface_Mask": "Network Mask",
|
||||
"Systeminfo_Network_Hardware_Interface_Name": "Interface Name",
|
||||
"Systeminfo_Network_Hardware_Interface_RX": "Received",
|
||||
"Systeminfo_Network_Hardware_Interface_TX": "Transmitted",
|
||||
"Systeminfo_Network_IP": "IP Internet:",
|
||||
"Systeminfo_Network_IP_Connection": "IP connection:",
|
||||
"Systeminfo_Network_IP_Server": "Server IP:",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"APPRISE_URL_name": "URL de notificación de Apprise",
|
||||
"About_Design": "Diseñado para:",
|
||||
"About_Exit": "Salir",
|
||||
"About_Title": "Open Source Network Guard",
|
||||
"About_Title": "Escáner de seguridad de la red y marco de notificaciones",
|
||||
"AppEvents_DateTimeCreated": "Registrado",
|
||||
"AppEvents_Extra": "Extra",
|
||||
"AppEvents_GUID": "GUID del evento de aplicación",
|
||||
@@ -185,12 +185,17 @@
|
||||
"DevDetail_button_OverwriteIcons_Warning": "¿Sobreescribir todos los iconos de todos los dispositivos con el mismo tipo que el dispositivo actual?",
|
||||
"DevDetail_button_Reset": "Restablecer cambios",
|
||||
"DevDetail_button_Save": "Guardar",
|
||||
"Device_MultiEdit": "Edición múltiple",
|
||||
"Device_MultiEdit_Backup": "Tenga cuidado, ingresar valores incorrectos o romperá su configuración. Por favor, haga una copia de seguridad de su base de datos o de la configuración de los dispositivos primero (<a href=\"php/server/devices.php?action=ExportCSV\">haga clic para descargar <i class=\"fa-solid fa-download fa-bounce\"></i></a>). Lea cómo recuperar dispositivos de este archivo en la documentación de <a href=\"https://github.com/jokob-sk/Pi.Alert/blob/main/docs/BACKUPS.md#scenario-2-corrupted-database\" target=\"_blank\">Copia de seguridad</a>.",
|
||||
"Device_MultiEdit_Fields": "Editar campos:",
|
||||
"Device_MultiEdit_MassActions": "Acciones masivas:",
|
||||
"Device_MultiEdit_Tooltip": "Cuidado. Al hacer clic se aplicará el valor de la izquierda a todos los dispositivos seleccionados anteriormente.",
|
||||
"Device_Searchbox": "Búsqueda",
|
||||
"Device_Shortcut_AllDevices": "Mis dispositivos",
|
||||
"Device_Shortcut_Archived": "Archivado(s)",
|
||||
"Device_Shortcut_Connected": "Conectado(s)",
|
||||
"Device_Shortcut_Devices": "Dispositivos",
|
||||
"Device_Shortcut_DownAlerts": "Alerta(s) de caída(s)",
|
||||
"Device_Shortcut_DownAlerts": "Caído y sin conexión",
|
||||
"Device_Shortcut_Favorites": "Favorito(s)",
|
||||
"Device_Shortcut_NewDevices": "Nuevo(s)",
|
||||
"Device_Shortcut_OnlineChart": "Presencia del dispositivo a lo largo del tiempo",
|
||||
@@ -223,7 +228,7 @@
|
||||
"Device_Title": "Dispositivos",
|
||||
"Donations_Others": "Otros",
|
||||
"Donations_Platforms": "Plataforma de patrocinadores",
|
||||
"Donations_Text": "¡Hola 👋! </br> Gracias por hacer clic en esta parte del menú 😅 </br> </br> Estoy intentando recaudar algunas donaciones para mejorar el software. Además, me ayudaría a no quemarme. Que me agote podría significar el fin del soporte para esta aplicación. Cualquier pequeño patrocinio (recurrente o no) me hace querer poner más esfuerzo en esta aplicación. No quiero bloquear funciones (nuevos complementos) detrás de muros de pago 🔐. </br> Actualmente, me levanto 2 horas antes del trabajo, así que contribuyo un poco a la aplicación. Si tuviera algún ingreso recurrente podría acortar mi semana laboral y en el tiempo restante concentrarme completamente en PiAlert. Obtendrías más funcionalidad, una aplicación más pulida y menos errores. </br> </br> Gracias por leerme. Estoy muy agradecido por cualquier apoyo ❤🙏 </br> </br> TL;DR: Al apoyarme obtienes: </br> </br> <ul><li>Actualizaciones periódicas para mantener tus datos y a tu familia seguros 🔄</li><li>Menos errores 🐛🔫</li><li>Mejor y más funcionalidad➕</li><li> No me agoto 🔥🤯</li><li>Publicaciones menos apresuradas 💨</li><li>Mejores documentos📚</li><li>Soporte mejor y más rápido para problemas 🆘</li><li >Me menos gruñón 😄</li></ul> </br> 📧Envíame un correo electrónico a <a href='mailto:jokob@duck.com?subject=PiAlert'>jokob@duck.com</a> si quiero ponerme en contacto o si debo agregar otras plataformas de patrocinio. </br>",
|
||||
"Donations_Text": "¡Hola! 👋 </br> Gracias por hacer clic en este elemento 😅 del menú </br> </br>, estoy tratando de recolectar algunas donaciones para mejorar el software. Además, me ayudaría a no quemarse, por lo que puedo apoyar esta aplicación por más tiempo. Cualquier pequeño patrocinio (recurrente o no) me hace querer esforzarme más en esta aplicación. </br> Me encantaría acortar mi semana de trabajo y en el tiempo que me queda centrarme por completo en PiAlert. Obtendrías más funcionalidad, una aplicación más pulida y menos errores. </br> </br> Gracias por leer, agradezco cualquier apoyo ❤🙏 </br> </br> TL; DR: Al apoyarme, obtienes: </br> </br> <ul><li>Actualizaciones periódicas para mantener tus datos y tu familia seguros 🔄</li><li>Menos errores 🐛🔫</li><li>Mejor y más funcionalidad➕</li><li>No me quemo 🔥🤯</li><li>Lanzamientos 💨menos apresurados</li> <li>Mejores documentos📚</li><li>Soporte más rápido y mejor con problemas 🆘</li></ul> </br> 📧Envíame un correo electrónico a <a href='mailto:jokob@duck.com?subject=PiAlert'>jokob@duck.com</a> si quieres ponerte en contacto o si debo añadir otras plataformas de patrocinio. </br>",
|
||||
"Donations_Title": "Donaciones",
|
||||
"ENABLE_PLUGINS_description": "Habilita la funcionalidad de los <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins\">complementos</a>. Cargar los complementos requiere más recursos de hardware, así que quizás quieras desactivarlo en hardware poco potente.",
|
||||
"ENABLE_PLUGINS_name": "Habilitar complementos",
|
||||
@@ -278,6 +283,7 @@
|
||||
"Gen_Run": "Ejecutar",
|
||||
"Gen_Save": "Guardar",
|
||||
"Gen_Saved": "Guardado",
|
||||
"Gen_Selected_Devices": "Dispositivos seleccionados:",
|
||||
"Gen_Switch": "Cambiar",
|
||||
"Gen_Upd": "Actualizado correctamente",
|
||||
"Gen_Upd_Fail": "Fallo al actualizar",
|
||||
@@ -397,6 +403,8 @@
|
||||
"Maintenance_Tool_del_empty_macs_noti": "Eliminar dispositivos",
|
||||
"Maintenance_Tool_del_empty_macs_noti_text": "¿Estás seguro de que quieres eliminar todos los dispositivos con direcciones MAC vacías? <br> (tal vez prefiera archivarlo)",
|
||||
"Maintenance_Tool_del_empty_macs_text": "Antes de usar esta función, haga una copia de seguridad. La eliminación no se puede deshacer. Todos los dispositivos sin Mac se eliminarán de la base de datos.",
|
||||
"Maintenance_Tool_del_selecteddev": "Borrar dispositivos seleccionados",
|
||||
"Maintenance_Tool_del_selecteddev_text": "Antes de utilizar esta función, haga una copia de seguridad. La eliminación no se puede deshacer. Los dispositivos seleccionados se eliminarán de la base de datos.",
|
||||
"Maintenance_Tool_del_unknowndev": "Eliminar dispositivos (desconocidos)",
|
||||
"Maintenance_Tool_del_unknowndev_noti": "Eliminar dispositivos (desconocidos)",
|
||||
"Maintenance_Tool_del_unknowndev_noti_text": "¿Estás seguro de que quieres eliminar todos los dispositivos (desconocidos)?",
|
||||
@@ -432,9 +440,6 @@
|
||||
"Maintenance_database_path": "Ruta de la base de datos",
|
||||
"Maintenance_database_rows": "Tabla (Filas)",
|
||||
"Maintenance_database_size": "Tamaño de base de datos",
|
||||
"Maintenance_lang_de_de": "German (DE)",
|
||||
"Maintenance_lang_en_us": "English (US)",
|
||||
"Maintenance_lang_es_es": "Spanish (ES)",
|
||||
"Maintenance_lang_selector_apply": "Aplicar",
|
||||
"Maintenance_lang_selector_empty": "Elija un idioma",
|
||||
"Maintenance_lang_selector_lable": "Seleccione su idioma",
|
||||
@@ -457,12 +462,15 @@
|
||||
"NTFY_USER_name": "Usuario de NTFY",
|
||||
"NTFY_display_name": "NTFY",
|
||||
"NTFY_icon": "<i class=\"fa fa-terminal\"></i>",
|
||||
"Navigation_About": "Acerca de",
|
||||
"Navigation_Devices": "Dispositivos",
|
||||
"Navigation_Donations": "Donaciones",
|
||||
"Navigation_Events": "Eventos",
|
||||
"Navigation_Flows": "Flows",
|
||||
"Navigation_HelpFAQ": "Ayuda / FAQ",
|
||||
"Navigation_Integrations": "Integraciones",
|
||||
"Navigation_Maintenance": "Mantenimiento",
|
||||
"Navigation_Monitoring": "Supervisión",
|
||||
"Navigation_Network": "Red",
|
||||
"Navigation_Plugins": "Plugins",
|
||||
"Navigation_Presence": "Historial",
|
||||
@@ -623,6 +631,10 @@
|
||||
"Systeminfo_Network_HTTP_Referer": "Referido HTTP:",
|
||||
"Systeminfo_Network_HTTP_Referer_String": "Sin referencia HTTP",
|
||||
"Systeminfo_Network_Hardware": "Hardware de red",
|
||||
"Systeminfo_Network_Hardware_Interface_Mask": "Máscara de red",
|
||||
"Systeminfo_Network_Hardware_Interface_Name": "Nombre de la interfaz",
|
||||
"Systeminfo_Network_Hardware_Interface_RX": "Recibido",
|
||||
"Systeminfo_Network_Hardware_Interface_TX": "Transmitido",
|
||||
"Systeminfo_Network_IP": "IP Internet:",
|
||||
"Systeminfo_Network_IP_Connection": "Conexión IP:",
|
||||
"Systeminfo_Network_IP_Server": "IP del servidor:",
|
||||
|
||||
@@ -175,6 +175,11 @@
|
||||
"DevDetail_button_OverwriteIcons_Warning": "",
|
||||
"DevDetail_button_Reset": "",
|
||||
"DevDetail_button_Save": "Enregistrer",
|
||||
"Device_MultiEdit": "",
|
||||
"Device_MultiEdit_Backup": "",
|
||||
"Device_MultiEdit_Fields": "",
|
||||
"Device_MultiEdit_MassActions": "",
|
||||
"Device_MultiEdit_Tooltip": "",
|
||||
"Device_Searchbox": "Rechercher",
|
||||
"Device_Shortcut_AllDevices": "Tous les appareils",
|
||||
"Device_Shortcut_Archived": "Archiv\u00e9",
|
||||
@@ -268,6 +273,7 @@
|
||||
"Gen_Run": "Lancer",
|
||||
"Gen_Save": "Enregistrer",
|
||||
"Gen_Saved": "Enregistr\u00e9",
|
||||
"Gen_Selected_Devices": "",
|
||||
"Gen_Switch": "Basculer",
|
||||
"Gen_Upd": "",
|
||||
"Gen_Upd_Fail": "",
|
||||
@@ -373,6 +379,8 @@
|
||||
"Maintenance_Tool_del_empty_macs_noti": "",
|
||||
"Maintenance_Tool_del_empty_macs_noti_text": "",
|
||||
"Maintenance_Tool_del_empty_macs_text": "",
|
||||
"Maintenance_Tool_del_selecteddev": "",
|
||||
"Maintenance_Tool_del_selecteddev_text": "",
|
||||
"Maintenance_Tool_del_unknowndev": "",
|
||||
"Maintenance_Tool_del_unknowndev_noti": "",
|
||||
"Maintenance_Tool_del_unknowndev_noti_text": "",
|
||||
@@ -408,9 +416,6 @@
|
||||
"Maintenance_database_path": "Chemin de la base de donn\u00e9es",
|
||||
"Maintenance_database_rows": "",
|
||||
"Maintenance_database_size": "",
|
||||
"Maintenance_lang_de_de": "",
|
||||
"Maintenance_lang_en_us": "",
|
||||
"Maintenance_lang_es_es": "",
|
||||
"Maintenance_lang_selector_apply": "Appliquer",
|
||||
"Maintenance_lang_selector_empty": "",
|
||||
"Maintenance_lang_selector_lable": "",
|
||||
@@ -423,11 +428,14 @@
|
||||
"Maintenance_version": "",
|
||||
"NETWORK_DEVICE_TYPES_description": "",
|
||||
"NETWORK_DEVICE_TYPES_name": "",
|
||||
"Navigation_About": "",
|
||||
"Navigation_Devices": "Appareils",
|
||||
"Navigation_Donations": "Dons",
|
||||
"Navigation_Events": "\u00c9v\u00e8nements",
|
||||
"Navigation_HelpFAQ": "Aide / FAQ",
|
||||
"Navigation_Integrations": "",
|
||||
"Navigation_Maintenance": "",
|
||||
"Navigation_Monitoring": "",
|
||||
"Navigation_Network": "R\u00e9seau",
|
||||
"Navigation_Plugins": "Greffons",
|
||||
"Navigation_Presence": "Pr\u00e9sence",
|
||||
@@ -554,6 +562,10 @@
|
||||
"Systeminfo_Network_HTTP_Referer": "R\u00e9f\u00e9rent HTTP\u202f:",
|
||||
"Systeminfo_Network_HTTP_Referer_String": "Pas de r\u00e9f\u00e9rent HTTP",
|
||||
"Systeminfo_Network_Hardware": "Mat\u00e9riel r\u00e9seau",
|
||||
"Systeminfo_Network_Hardware_Interface_Mask": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Name": "",
|
||||
"Systeminfo_Network_Hardware_Interface_RX": "",
|
||||
"Systeminfo_Network_Hardware_Interface_TX": "",
|
||||
"Systeminfo_Network_IP": "IP Internet\u202f:",
|
||||
"Systeminfo_Network_IP_Connection": "Connexion IP\u202f:",
|
||||
"Systeminfo_Network_IP_Server": "IP du serveur\u202f:",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// ###################################
|
||||
|
||||
$defaultLang = "en_us";
|
||||
$allLanguages = ["en_us","es_es","de_de"];
|
||||
$allLanguages = ["en_us","es_es","de_de", "nb_no", "ru_ru", "fr_fr"];
|
||||
|
||||
global $db;
|
||||
|
||||
@@ -13,6 +13,9 @@ $result = $db->querySingle("SELECT Value FROM Settings WHERE Code_Name = 'UI_LAN
|
||||
switch($result){
|
||||
case 'Spanish': $pia_lang_selected = 'es_es'; break;
|
||||
case 'German': $pia_lang_selected = 'de_de'; break;
|
||||
case 'Norwegian': $pia_lang_selected = 'nb_no'; break;
|
||||
case 'Russian': $pia_lang_selected = 'ru_ru'; break;
|
||||
case 'French': $pia_lang_selected = 'fr_fr'; break;
|
||||
default: $pia_lang_selected = 'en_us'; break;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,6 @@ def merge_translations(main_file, other_files):
|
||||
|
||||
if __name__ == "__main__":
|
||||
current_path = os.path.dirname(os.path.abspath(__file__))
|
||||
json_files = ["en_us.json", "de_de.json", "es_es.json", "fr_fr.json", "nb_no.json"]
|
||||
json_files = ["en_us.json", "de_de.json", "es_es.json", "fr_fr.json", "nb_no.json", "ru_ru.json"]
|
||||
file_paths = [os.path.join(current_path, file) for file in json_files]
|
||||
merge_translations(file_paths[0], file_paths[1:])
|
||||
|
||||
@@ -175,6 +175,11 @@
|
||||
"DevDetail_button_OverwriteIcons_Warning": "",
|
||||
"DevDetail_button_Reset": "",
|
||||
"DevDetail_button_Save": "",
|
||||
"Device_MultiEdit": "",
|
||||
"Device_MultiEdit_Backup": "",
|
||||
"Device_MultiEdit_Fields": "",
|
||||
"Device_MultiEdit_MassActions": "",
|
||||
"Device_MultiEdit_Tooltip": "",
|
||||
"Device_Searchbox": "",
|
||||
"Device_Shortcut_AllDevices": "",
|
||||
"Device_Shortcut_Archived": "",
|
||||
@@ -268,6 +273,7 @@
|
||||
"Gen_Run": "",
|
||||
"Gen_Save": "",
|
||||
"Gen_Saved": "",
|
||||
"Gen_Selected_Devices": "",
|
||||
"Gen_Switch": "",
|
||||
"Gen_Upd": "",
|
||||
"Gen_Upd_Fail": "",
|
||||
@@ -373,6 +379,8 @@
|
||||
"Maintenance_Tool_del_empty_macs_noti": "",
|
||||
"Maintenance_Tool_del_empty_macs_noti_text": "",
|
||||
"Maintenance_Tool_del_empty_macs_text": "",
|
||||
"Maintenance_Tool_del_selecteddev": "",
|
||||
"Maintenance_Tool_del_selecteddev_text": "",
|
||||
"Maintenance_Tool_del_unknowndev": "",
|
||||
"Maintenance_Tool_del_unknowndev_noti": "",
|
||||
"Maintenance_Tool_del_unknowndev_noti_text": "",
|
||||
@@ -408,9 +416,6 @@
|
||||
"Maintenance_database_path": "",
|
||||
"Maintenance_database_rows": "",
|
||||
"Maintenance_database_size": "",
|
||||
"Maintenance_lang_de_de": "",
|
||||
"Maintenance_lang_en_us": "",
|
||||
"Maintenance_lang_es_es": "",
|
||||
"Maintenance_lang_selector_apply": "",
|
||||
"Maintenance_lang_selector_empty": "",
|
||||
"Maintenance_lang_selector_lable": "",
|
||||
@@ -423,11 +428,14 @@
|
||||
"Maintenance_version": "",
|
||||
"NETWORK_DEVICE_TYPES_description": "",
|
||||
"NETWORK_DEVICE_TYPES_name": "",
|
||||
"Navigation_About": "",
|
||||
"Navigation_Devices": "",
|
||||
"Navigation_Donations": "",
|
||||
"Navigation_Events": "",
|
||||
"Navigation_HelpFAQ": "",
|
||||
"Navigation_Integrations": "",
|
||||
"Navigation_Maintenance": "",
|
||||
"Navigation_Monitoring": "",
|
||||
"Navigation_Network": "",
|
||||
"Navigation_Plugins": "",
|
||||
"Navigation_Presence": "",
|
||||
@@ -554,6 +562,10 @@
|
||||
"Systeminfo_Network_HTTP_Referer": "",
|
||||
"Systeminfo_Network_HTTP_Referer_String": "",
|
||||
"Systeminfo_Network_Hardware": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Mask": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Name": "",
|
||||
"Systeminfo_Network_Hardware_Interface_RX": "",
|
||||
"Systeminfo_Network_Hardware_Interface_TX": "",
|
||||
"Systeminfo_Network_IP": "",
|
||||
"Systeminfo_Network_IP_Connection": "",
|
||||
"Systeminfo_Network_IP_Server": "",
|
||||
|
||||
650
front/php/templates/language/ru_ru.json
Executable file
650
front/php/templates/language/ru_ru.json
Executable file
@@ -0,0 +1,650 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "\u0412\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 SQL-\u0437\u0430\u043f\u0440\u043e\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b JSON, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432\u044b\u0434\u0430\u0442\u044c \u0435\u0433\u043e \u0447\u0435\u0440\u0435\u0437 <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\"><code>table_custom_endpoint.json</code> file endpoint</a>.",
|
||||
"API_CUSTOM_SQL_name": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0430\u044f \u043a\u043e\u043d\u0435\u0447\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430",
|
||||
"API_display_name": "API",
|
||||
"API_icon": "<i class=\"fa fa-arrow-down-up-across-line\"></i>",
|
||||
"About_Design": "\u0420\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d:",
|
||||
"About_Exit": "\u0417\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f",
|
||||
"About_Title": "\u0421\u0435\u0442\u0435\u0432\u0430\u044f \u0437\u0430\u0449\u0438\u0442\u0430 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c",
|
||||
"AppEvents_DateTimeCreated": "\u0416\u0443\u0440\u043d\u0430\u043b",
|
||||
"AppEvents_Extra": "\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e",
|
||||
"AppEvents_GUID": "GUID \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f",
|
||||
"AppEvents_Helper1": "\u041f\u043e\u043c\u043e\u0449\u043d\u0438\u043a 1",
|
||||
"AppEvents_Helper2": "\u041f\u043e\u043c\u043e\u0449\u043d\u0438\u043a 2",
|
||||
"AppEvents_Helper3": "\u041f\u043e\u043c\u043e\u0449\u043d\u0438\u043a 3",
|
||||
"AppEvents_ObjectForeignKey": "\u0412\u043d\u0435\u0448\u043d\u0438\u0439 \u043a\u043b\u044e\u0447",
|
||||
"AppEvents_ObjectIndex": "\u0418\u043d\u0434\u0435\u043a\u0441",
|
||||
"AppEvents_ObjectIsArchived": "\u0410\u0440\u0445\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043e (\u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443)",
|
||||
"AppEvents_ObjectIsNew": "\u041d\u043e\u0432\u044b\u0439 (\u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443)",
|
||||
"AppEvents_ObjectPlugin": "\u0421\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0439 \u043f\u043b\u0430\u0433\u0438\u043d",
|
||||
"AppEvents_ObjectPrimaryID": "\u041f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 ID",
|
||||
"AppEvents_ObjectSecondaryID": "\u0412\u0442\u043e\u0440\u0438\u0447\u043d\u044b\u0439 ID",
|
||||
"AppEvents_ObjectStatus": "\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 (\u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u0445\u043e\u0434\u0430 \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443)",
|
||||
"AppEvents_ObjectStatusColumn": "\u041a\u043e\u043b\u043e\u043d\u043a\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f",
|
||||
"AppEvents_ObjectType": "\u0422\u0438\u043f \u043e\u0431\u044a\u0435\u043a\u0442\u0430",
|
||||
"AppEvents_Plugin": "\u041f\u043b\u0430\u0433\u0438\u043d",
|
||||
"AppEvents_Type": "\u0422\u0438\u043f",
|
||||
"BackDevDetail_Actions_Ask_Run": "\u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435?",
|
||||
"BackDevDetail_Actions_Not_Registered": "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043d\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043e:\u00b7 ",
|
||||
"BackDevDetail_Actions_Title_Run": "\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435",
|
||||
"BackDevDetail_Copy_Ask": "\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u0437 \u0432\u044b\u043f\u0430\u0434\u0430\u044e\u0449\u0435\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 (\u0432\u0441\u0435 \u043d\u0430 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043e)?",
|
||||
"BackDevDetail_Copy_Title": "\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0435\u0442\u0430\u043b\u0438",
|
||||
"BackDevDetail_Tools_WOL_error": "\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u041d\u0415 \u0431\u044b\u043b\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.",
|
||||
"BackDevDetail_Tools_WOL_okay": "\u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0431\u044b\u043b\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430.",
|
||||
"BackDevices_Arpscan_disabled": "Arp \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u043e",
|
||||
"BackDevices_Arpscan_enabled": "Arp \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e",
|
||||
"BackDevices_Backup_CopError": "\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c.",
|
||||
"BackDevices_Backup_Failed": "\u0420\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0435 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e. \u0410\u0440\u0445\u0438\u0432 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d \u0438\u043b\u0438 \u043f\u0443\u0441\u0442.",
|
||||
"BackDevices_Backup_okay": "\u0420\u0435\u0437\u0435\u0440\u0432\u043d\u043e\u0435 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u0441 \u043d\u043e\u0432\u044b\u043c \u0430\u0440\u0445\u0438\u0432\u043e\u043c",
|
||||
"BackDevices_DBTools_DelDevError_a": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430",
|
||||
"BackDevices_DBTools_DelDevError_b": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432",
|
||||
"BackDevices_DBTools_DelDev_a": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0434\u0430\u043b\u0435\u043d\u043e",
|
||||
"BackDevices_DBTools_DelDev_b": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u044b",
|
||||
"BackDevices_DBTools_DelEvents": "\u0421\u043e\u0431\u044b\u0442\u0438\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u044b",
|
||||
"BackDevices_DBTools_DelEventsError": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u0439",
|
||||
"BackDevices_DBTools_ImportCSV": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 CSV \u0431\u044b\u043b\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b.",
|
||||
"BackDevices_DBTools_ImportCSVError": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0444\u0430\u0439\u043b CSV. \u0423\u0431\u0435\u0434\u0438\u0442\u0435\u0441\u044c, \u0447\u0442\u043e \u0444\u043e\u0440\u043c\u0430\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439.",
|
||||
"BackDevices_DBTools_ImportCSVMissing": "CSV-\u0444\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u0432<b>/config/devices.csv.</b>",
|
||||
"BackDevices_DBTools_Purge": "\u0421\u0430\u043c\u044b\u0435 \u0441\u0442\u0430\u0440\u044b\u0435 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u044b\u0435 \u043a\u043e\u043f\u0438\u0438 \u0431\u044b\u043b\u0438 \u0443\u0434\u0430\u043b\u0435\u043d\u044b",
|
||||
"BackDevices_DBTools_UpdDev": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043e",
|
||||
"BackDevices_DBTools_UpdDevError": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430",
|
||||
"BackDevices_DBTools_Upgrade": "\u0411\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0430",
|
||||
"BackDevices_DBTools_UpgradeError": "\u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c",
|
||||
"BackDevices_Device_UpdDevError": "\u041e\u0448\u0438\u0431\u043a\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432. \u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u043e\u0437\u0436\u0435. \u0412\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0430 \u0438\u0437-\u0437\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.",
|
||||
"BackDevices_Restore_CopError": "\u0418\u0441\u0445\u043e\u0434\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c.",
|
||||
"BackDevices_Restore_Failed": "\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u0432\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0435 \u0440\u0435\u0437\u0435\u0440\u0432\u043d\u0443\u044e \u043a\u043e\u043f\u0438\u044e \u0432\u0440\u0443\u0447\u043d\u0443\u044e.",
|
||||
"BackDevices_Restore_okay": "\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e.",
|
||||
"BackDevices_darkmode_disabled": "\u0422\u0435\u043c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d",
|
||||
"BackDevices_darkmode_enabled": "\u0422\u0435\u043c\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c \u0432\u043a\u043b\u044e\u0447\u0435\u043d",
|
||||
"DAYS_TO_KEEP_EVENTS_description": "\u042d\u0442\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f. \u0417\u0434\u0435\u0441\u044c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0434\u043d\u0435\u0439, \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0443\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c\u0441\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u043e \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445. \u0412\u0441\u0435 \u0441\u0442\u0430\u0440\u044b\u0435 \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0443\u0434\u0430\u043b\u044f\u0442\u044c\u0441\u044f. \u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c\u043e \u043a \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043f\u043b\u0430\u0433\u0438\u043d\u0430.",
|
||||
"DAYS_TO_KEEP_EVENTS_name": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0441\u0442\u0430\u0440\u0448\u0435",
|
||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> \u0421\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430",
|
||||
"DevDetail_Copy_Device_Tooltip": "\u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438\u0437 \u0440\u0430\u0441\u043a\u0440\u044b\u0432\u0430\u044e\u0449\u0435\u0433\u043e\u0441\u044f \u0441\u043f\u0438\u0441\u043a\u0430. \u0412\u0441\u0435 \u043d\u0430 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043e",
|
||||
"DevDetail_EveandAl_AlertAllEvents": "\u041e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u0435 \u043e\u0431\u043e \u0432\u0441\u0435\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u0445",
|
||||
"DevDetail_EveandAl_AlertDown": "\u041e\u043f\u043e\u0432\u0435\u0449\u0435\u043d\u0438\u0435 \u043e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e\u0441\u0442\u0438",
|
||||
"DevDetail_EveandAl_Archived": "\u0410\u0440\u0445\u0438\u0432",
|
||||
"DevDetail_EveandAl_NewDevice": "\u041d\u043e\u0432\u043e\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",
|
||||
"DevDetail_EveandAl_NewDevice_Tooltip": "\u0411\u0443\u0434\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u0443\u0441 \u00ab\u041d\u043e\u0432\u043e\u0435\u00bb \u0434\u043b\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u0438 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0435\u0433\u043e \u0432 \u0441\u043f\u0438\u0441\u043a\u0438, \u043a\u043e\u0433\u0434\u0430 \u0444\u0438\u043b\u044c\u0442\u0440 \u00ab\u041d\u043e\u0432\u044b\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u00bb \u0430\u043a\u0442\u0438\u0432\u0435\u043d. \u041d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f.",
|
||||
"DevDetail_EveandAl_RandomMAC": "\u0421\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0439 MAC-\u0430\u0434\u0440\u0435\u0441",
|
||||
"DevDetail_EveandAl_ScanCycle": "\u0421\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",
|
||||
"DevDetail_EveandAl_ScanCycle_a": "\u0421\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",
|
||||
"DevDetail_EveandAl_ScanCycle_z": "\u041d\u0435 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e",
|
||||
"DevDetail_EveandAl_Skip": "",
|
||||
"DevDetail_EveandAl_Title": "",
|
||||
"DevDetail_Events_CheckBox": "",
|
||||
"DevDetail_GoToNetworkNode": "",
|
||||
"DevDetail_Icon": "",
|
||||
"DevDetail_Icon_Descr": "",
|
||||
"DevDetail_Loading": "",
|
||||
"DevDetail_MainInfo_Comments": "",
|
||||
"DevDetail_MainInfo_Favorite": "",
|
||||
"DevDetail_MainInfo_Group": "",
|
||||
"DevDetail_MainInfo_Location": "",
|
||||
"DevDetail_MainInfo_Name": "",
|
||||
"DevDetail_MainInfo_Network": "",
|
||||
"DevDetail_MainInfo_Network_Port": "",
|
||||
"DevDetail_MainInfo_Network_Title": "",
|
||||
"DevDetail_MainInfo_Owner": "",
|
||||
"DevDetail_MainInfo_Title": "",
|
||||
"DevDetail_MainInfo_Type": "",
|
||||
"DevDetail_MainInfo_Vendor": "",
|
||||
"DevDetail_MainInfo_mac": "",
|
||||
"DevDetail_Network_Node_hover": "",
|
||||
"DevDetail_Network_Port_hover": "",
|
||||
"DevDetail_Nmap_Scans": "",
|
||||
"DevDetail_Nmap_Scans_desc": "",
|
||||
"DevDetail_Nmap_buttonDefault": "",
|
||||
"DevDetail_Nmap_buttonDefault_text": "",
|
||||
"DevDetail_Nmap_buttonDetail": "",
|
||||
"DevDetail_Nmap_buttonDetail_text": "",
|
||||
"DevDetail_Nmap_buttonFast": "",
|
||||
"DevDetail_Nmap_buttonFast_text": "",
|
||||
"DevDetail_Nmap_buttonSkipDiscovery": "",
|
||||
"DevDetail_Nmap_buttonSkipDiscovery_text": "",
|
||||
"DevDetail_Nmap_resultsLink": "",
|
||||
"DevDetail_Owner_hover": "",
|
||||
"DevDetail_Periodselect_All": "",
|
||||
"DevDetail_Periodselect_LastMonth": "",
|
||||
"DevDetail_Periodselect_LastWeek": "",
|
||||
"DevDetail_Periodselect_LastYear": "",
|
||||
"DevDetail_Periodselect_today": "",
|
||||
"DevDetail_Run_Actions_Title": "",
|
||||
"DevDetail_Run_Actions_Tooltip": "",
|
||||
"DevDetail_SessionInfo_FirstSession": "",
|
||||
"DevDetail_SessionInfo_LastIP": "",
|
||||
"DevDetail_SessionInfo_LastSession": "",
|
||||
"DevDetail_SessionInfo_StaticIP": "",
|
||||
"DevDetail_SessionInfo_Status": "",
|
||||
"DevDetail_SessionInfo_Title": "",
|
||||
"DevDetail_SessionTable_Additionalinfo": "",
|
||||
"DevDetail_SessionTable_Connection": "",
|
||||
"DevDetail_SessionTable_Disconnection": "",
|
||||
"DevDetail_SessionTable_Duration": "",
|
||||
"DevDetail_SessionTable_IP": "",
|
||||
"DevDetail_SessionTable_Order": "",
|
||||
"DevDetail_Shortcut_CurrentStatus": "",
|
||||
"DevDetail_Shortcut_DownAlerts": "",
|
||||
"DevDetail_Shortcut_Presence": "",
|
||||
"DevDetail_Shortcut_Sessions": "",
|
||||
"DevDetail_Tab_Details": "",
|
||||
"DevDetail_Tab_Events": "",
|
||||
"DevDetail_Tab_EventsTableDate": "",
|
||||
"DevDetail_Tab_EventsTableEvent": "",
|
||||
"DevDetail_Tab_EventsTableIP": "",
|
||||
"DevDetail_Tab_EventsTableInfo": "",
|
||||
"DevDetail_Tab_Nmap": "",
|
||||
"DevDetail_Tab_NmapEmpty": "",
|
||||
"DevDetail_Tab_NmapTableExtra": "",
|
||||
"DevDetail_Tab_NmapTableHeader": "",
|
||||
"DevDetail_Tab_NmapTableIndex": "",
|
||||
"DevDetail_Tab_NmapTablePort": "",
|
||||
"DevDetail_Tab_NmapTableService": "",
|
||||
"DevDetail_Tab_NmapTableState": "",
|
||||
"DevDetail_Tab_NmapTableText": "",
|
||||
"DevDetail_Tab_NmapTableTime": "",
|
||||
"DevDetail_Tab_Plugins": "",
|
||||
"DevDetail_Tab_Presence": "",
|
||||
"DevDetail_Tab_Sessions": "",
|
||||
"DevDetail_Tab_Tools": "",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Description": "",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Error": "",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Start": "",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Title": "",
|
||||
"DevDetail_Tab_Tools_Nslookup_Description": "",
|
||||
"DevDetail_Tab_Tools_Nslookup_Error": "",
|
||||
"DevDetail_Tab_Tools_Nslookup_Start": "",
|
||||
"DevDetail_Tab_Tools_Nslookup_Title": "",
|
||||
"DevDetail_Tab_Tools_Speedtest_Description": "",
|
||||
"DevDetail_Tab_Tools_Speedtest_Start": "",
|
||||
"DevDetail_Tab_Tools_Speedtest_Title": "",
|
||||
"DevDetail_Tab_Tools_Traceroute_Description": "",
|
||||
"DevDetail_Tab_Tools_Traceroute_Error": "",
|
||||
"DevDetail_Tab_Tools_Traceroute_Start": "",
|
||||
"DevDetail_Tab_Tools_Traceroute_Title": "",
|
||||
"DevDetail_Tools_WOL": "",
|
||||
"DevDetail_Tools_WOL_noti": "",
|
||||
"DevDetail_Tools_WOL_noti_text": "",
|
||||
"DevDetail_Type_hover": "",
|
||||
"DevDetail_Vendor_hover": "",
|
||||
"DevDetail_WOL_Title": "",
|
||||
"DevDetail_button_Delete": "",
|
||||
"DevDetail_button_DeleteEvents": "",
|
||||
"DevDetail_button_DeleteEvents_Warning": "",
|
||||
"DevDetail_button_OverwriteIcons": "",
|
||||
"DevDetail_button_OverwriteIcons_Tooltip": "",
|
||||
"DevDetail_button_OverwriteIcons_Warning": "",
|
||||
"DevDetail_button_Reset": "",
|
||||
"DevDetail_button_Save": "",
|
||||
"Device_MultiEdit": "",
|
||||
"Device_MultiEdit_Backup": "",
|
||||
"Device_MultiEdit_Fields": "",
|
||||
"Device_MultiEdit_MassActions": "",
|
||||
"Device_MultiEdit_Tooltip": "",
|
||||
"Device_Searchbox": "",
|
||||
"Device_Shortcut_AllDevices": "",
|
||||
"Device_Shortcut_Archived": "",
|
||||
"Device_Shortcut_Connected": "",
|
||||
"Device_Shortcut_Devices": "",
|
||||
"Device_Shortcut_DownAlerts": "",
|
||||
"Device_Shortcut_Favorites": "",
|
||||
"Device_Shortcut_NewDevices": "",
|
||||
"Device_Shortcut_OnlineChart": "",
|
||||
"Device_TableHead_Connected_Devices": "",
|
||||
"Device_TableHead_Favorite": "",
|
||||
"Device_TableHead_FirstSession": "",
|
||||
"Device_TableHead_Group": "",
|
||||
"Device_TableHead_Icon": "",
|
||||
"Device_TableHead_LastIP": "",
|
||||
"Device_TableHead_LastIPOrder": "",
|
||||
"Device_TableHead_LastSession": "",
|
||||
"Device_TableHead_Location": "",
|
||||
"Device_TableHead_MAC": "",
|
||||
"Device_TableHead_MAC_full": "",
|
||||
"Device_TableHead_Name": "",
|
||||
"Device_TableHead_Owner": "",
|
||||
"Device_TableHead_Parent_MAC": "",
|
||||
"Device_TableHead_Port": "",
|
||||
"Device_TableHead_RowID": "",
|
||||
"Device_TableHead_Rowid": "",
|
||||
"Device_TableHead_Status": "",
|
||||
"Device_TableHead_Type": "",
|
||||
"Device_TableHead_Vendor": "",
|
||||
"Device_Table_Not_Network_Device": "",
|
||||
"Device_Table_info": "",
|
||||
"Device_Table_nav_next": "",
|
||||
"Device_Table_nav_prev": "",
|
||||
"Device_Tablelenght": "",
|
||||
"Device_Tablelenght_all": "",
|
||||
"Device_Title": "",
|
||||
"Donations_Others": "",
|
||||
"Donations_Platforms": "",
|
||||
"Donations_Text": "",
|
||||
"Donations_Title": "",
|
||||
"ENABLE_PLUGINS_description": "",
|
||||
"ENABLE_PLUGINS_name": "",
|
||||
"Email_display_name": "",
|
||||
"Email_icon": "",
|
||||
"Events_Loading": "",
|
||||
"Events_Periodselect_All": "",
|
||||
"Events_Periodselect_LastMonth": "",
|
||||
"Events_Periodselect_LastWeek": "",
|
||||
"Events_Periodselect_LastYear": "",
|
||||
"Events_Periodselect_today": "",
|
||||
"Events_Searchbox": "",
|
||||
"Events_Shortcut_AllEvents": "",
|
||||
"Events_Shortcut_DownAlerts": "",
|
||||
"Events_Shortcut_Events": "",
|
||||
"Events_Shortcut_MissSessions": "",
|
||||
"Events_Shortcut_NewDevices": "",
|
||||
"Events_Shortcut_Sessions": "",
|
||||
"Events_Shortcut_VoidSessions": "",
|
||||
"Events_TableHead_AdditionalInfo": "",
|
||||
"Events_TableHead_Connection": "",
|
||||
"Events_TableHead_Date": "",
|
||||
"Events_TableHead_Device": "",
|
||||
"Events_TableHead_Disconnection": "",
|
||||
"Events_TableHead_Duration": "",
|
||||
"Events_TableHead_DurationOrder": "",
|
||||
"Events_TableHead_EventType": "",
|
||||
"Events_TableHead_IP": "",
|
||||
"Events_TableHead_IPOrder": "",
|
||||
"Events_TableHead_Order": "",
|
||||
"Events_TableHead_Owner": "",
|
||||
"Events_Table_info": "",
|
||||
"Events_Table_nav_next": "",
|
||||
"Events_Table_nav_prev": "",
|
||||
"Events_Tablelenght": "",
|
||||
"Events_Tablelenght_all": "",
|
||||
"Events_Title": "",
|
||||
"Gen_Action": "",
|
||||
"Gen_AreYouSure": "",
|
||||
"Gen_Backup": "",
|
||||
"Gen_Cancel": "",
|
||||
"Gen_Copy": "",
|
||||
"Gen_DataUpdatedUITakesTime": "",
|
||||
"Gen_Delete": "",
|
||||
"Gen_DeleteAll": "",
|
||||
"Gen_Error": "",
|
||||
"Gen_LockedDB": "",
|
||||
"Gen_Okay": "",
|
||||
"Gen_Purge": "",
|
||||
"Gen_ReadDocs": "",
|
||||
"Gen_Restore": "",
|
||||
"Gen_Run": "",
|
||||
"Gen_Save": "",
|
||||
"Gen_Saved": "",
|
||||
"Gen_Selected_Devices": "",
|
||||
"Gen_Switch": "",
|
||||
"Gen_Upd": "",
|
||||
"Gen_Upd_Fail": "",
|
||||
"Gen_Warning": "",
|
||||
"Gen_Work_In_Progress": "",
|
||||
"General_display_name": "",
|
||||
"General_icon": "",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "",
|
||||
"HRS_TO_KEEP_NEWDEV_name": "",
|
||||
"HelpFAQ_Cat_Detail": "",
|
||||
"HelpFAQ_Cat_Detail_300_head": "",
|
||||
"HelpFAQ_Cat_Detail_300_text_a": "",
|
||||
"HelpFAQ_Cat_Detail_300_text_b": "",
|
||||
"HelpFAQ_Cat_Detail_301_head_a": "",
|
||||
"HelpFAQ_Cat_Detail_301_head_b": "",
|
||||
"HelpFAQ_Cat_Detail_301_text": "",
|
||||
"HelpFAQ_Cat_Detail_302_head_a": "",
|
||||
"HelpFAQ_Cat_Detail_302_head_b": "",
|
||||
"HelpFAQ_Cat_Detail_302_text": "",
|
||||
"HelpFAQ_Cat_Detail_303_head": "",
|
||||
"HelpFAQ_Cat_Detail_303_text": "",
|
||||
"HelpFAQ_Cat_Device_200_head": "",
|
||||
"HelpFAQ_Cat_Device_200_text": "",
|
||||
"HelpFAQ_Cat_General": "",
|
||||
"HelpFAQ_Cat_General_100_head": "",
|
||||
"HelpFAQ_Cat_General_100_text_a": "",
|
||||
"HelpFAQ_Cat_General_100_text_b": "",
|
||||
"HelpFAQ_Cat_General_100_text_c": "",
|
||||
"HelpFAQ_Cat_General_101_head": "",
|
||||
"HelpFAQ_Cat_General_101_text": "",
|
||||
"HelpFAQ_Cat_General_102_head": "",
|
||||
"HelpFAQ_Cat_General_102_text": "",
|
||||
"HelpFAQ_Cat_General_102docker_head": "",
|
||||
"HelpFAQ_Cat_General_102docker_text": "",
|
||||
"HelpFAQ_Cat_General_103_head": "",
|
||||
"HelpFAQ_Cat_General_103_text": "",
|
||||
"HelpFAQ_Cat_Network_600_head": "",
|
||||
"HelpFAQ_Cat_Network_600_text": "",
|
||||
"HelpFAQ_Cat_Network_601_head": "",
|
||||
"HelpFAQ_Cat_Network_601_text": "",
|
||||
"HelpFAQ_Cat_Presence_400_head": "",
|
||||
"HelpFAQ_Cat_Presence_400_text": "",
|
||||
"HelpFAQ_Cat_Presence_401_head": "",
|
||||
"HelpFAQ_Cat_Presence_401_text": "",
|
||||
"HelpFAQ_Title": "",
|
||||
"LOG_LEVEL_description": "",
|
||||
"LOG_LEVEL_name": "",
|
||||
"Loading": "",
|
||||
"Login_Box": "",
|
||||
"Login_Default_PWD": "",
|
||||
"Login_Psw-box": "",
|
||||
"Login_Psw_alert": "",
|
||||
"Login_Psw_folder": "",
|
||||
"Login_Psw_new": "",
|
||||
"Login_Psw_run": "",
|
||||
"Login_Remember": "",
|
||||
"Login_Remember_small": "",
|
||||
"Login_Submit": "",
|
||||
"Login_Toggle_Alert_headline": "",
|
||||
"Login_Toggle_Info": "",
|
||||
"Login_Toggle_Info_headline": "",
|
||||
"Maintenance_Running_Version": "",
|
||||
"Maintenance_Status": "",
|
||||
"Maintenance_Title": "",
|
||||
"Maintenance_Tool_ExportCSV": "",
|
||||
"Maintenance_Tool_ExportCSV_noti": "",
|
||||
"Maintenance_Tool_ExportCSV_noti_text": "",
|
||||
"Maintenance_Tool_ExportCSV_text": "",
|
||||
"Maintenance_Tool_ImportCSV": "",
|
||||
"Maintenance_Tool_ImportCSV_noti": "",
|
||||
"Maintenance_Tool_ImportCSV_noti_text": "",
|
||||
"Maintenance_Tool_ImportCSV_text": "",
|
||||
"Maintenance_Tool_arpscansw": "",
|
||||
"Maintenance_Tool_arpscansw_noti": "",
|
||||
"Maintenance_Tool_arpscansw_noti_text": "",
|
||||
"Maintenance_Tool_arpscansw_text": "",
|
||||
"Maintenance_Tool_backup": "",
|
||||
"Maintenance_Tool_backup_noti": "",
|
||||
"Maintenance_Tool_backup_noti_text": "",
|
||||
"Maintenance_Tool_backup_text": "",
|
||||
"Maintenance_Tool_check_visible": "",
|
||||
"Maintenance_Tool_darkmode": "",
|
||||
"Maintenance_Tool_darkmode_noti": "",
|
||||
"Maintenance_Tool_darkmode_noti_text": "",
|
||||
"Maintenance_Tool_darkmode_text": "",
|
||||
"Maintenance_Tool_del_ActHistory": "",
|
||||
"Maintenance_Tool_del_ActHistory_noti": "",
|
||||
"Maintenance_Tool_del_ActHistory_noti_text": "",
|
||||
"Maintenance_Tool_del_ActHistory_text": "",
|
||||
"Maintenance_Tool_del_alldev": "",
|
||||
"Maintenance_Tool_del_alldev_noti": "",
|
||||
"Maintenance_Tool_del_alldev_noti_text": "",
|
||||
"Maintenance_Tool_del_alldev_text": "",
|
||||
"Maintenance_Tool_del_allevents": "",
|
||||
"Maintenance_Tool_del_allevents30": "",
|
||||
"Maintenance_Tool_del_allevents30_noti": "",
|
||||
"Maintenance_Tool_del_allevents30_noti_text": "",
|
||||
"Maintenance_Tool_del_allevents30_text": "",
|
||||
"Maintenance_Tool_del_allevents_noti": "",
|
||||
"Maintenance_Tool_del_allevents_noti_text": "",
|
||||
"Maintenance_Tool_del_allevents_text": "",
|
||||
"Maintenance_Tool_del_empty_macs": "",
|
||||
"Maintenance_Tool_del_empty_macs_noti": "",
|
||||
"Maintenance_Tool_del_empty_macs_noti_text": "",
|
||||
"Maintenance_Tool_del_empty_macs_text": "",
|
||||
"Maintenance_Tool_del_selecteddev": "",
|
||||
"Maintenance_Tool_del_selecteddev_text": "",
|
||||
"Maintenance_Tool_del_unknowndev": "",
|
||||
"Maintenance_Tool_del_unknowndev_noti": "",
|
||||
"Maintenance_Tool_del_unknowndev_noti_text": "",
|
||||
"Maintenance_Tool_del_unknowndev_text": "",
|
||||
"Maintenance_Tool_displayed_columns_text": "",
|
||||
"Maintenance_Tool_drag_me": "",
|
||||
"Maintenance_Tool_order_columns_text": "",
|
||||
"Maintenance_Tool_purgebackup": "",
|
||||
"Maintenance_Tool_purgebackup_noti": "",
|
||||
"Maintenance_Tool_purgebackup_noti_text": "",
|
||||
"Maintenance_Tool_purgebackup_text": "",
|
||||
"Maintenance_Tool_restore": "",
|
||||
"Maintenance_Tool_restore_noti": "",
|
||||
"Maintenance_Tool_restore_noti_text": "",
|
||||
"Maintenance_Tool_restore_text": "",
|
||||
"Maintenance_Tool_upgrade_database_noti": "",
|
||||
"Maintenance_Tool_upgrade_database_noti_text": "",
|
||||
"Maintenance_Tool_upgrade_database_text": "",
|
||||
"Maintenance_Tools_Tab_BackupRestore": "",
|
||||
"Maintenance_Tools_Tab_Logging": "",
|
||||
"Maintenance_Tools_Tab_Settings": "",
|
||||
"Maintenance_Tools_Tab_Tools": "",
|
||||
"Maintenance_Tools_Tab_UISettings": "",
|
||||
"Maintenance_arp_status": "",
|
||||
"Maintenance_arp_status_off": "",
|
||||
"Maintenance_arp_status_on": "",
|
||||
"Maintenance_built_on": "",
|
||||
"Maintenance_current_version": "",
|
||||
"Maintenance_database_backup": "",
|
||||
"Maintenance_database_backup_found": "",
|
||||
"Maintenance_database_backup_total": "",
|
||||
"Maintenance_database_lastmod": "",
|
||||
"Maintenance_database_path": "",
|
||||
"Maintenance_database_rows": "",
|
||||
"Maintenance_database_size": "",
|
||||
"Maintenance_lang_selector_apply": "",
|
||||
"Maintenance_lang_selector_empty": "",
|
||||
"Maintenance_lang_selector_lable": "",
|
||||
"Maintenance_lang_selector_text": "",
|
||||
"Maintenance_new_version": "",
|
||||
"Maintenance_themeselector_apply": "",
|
||||
"Maintenance_themeselector_empty": "",
|
||||
"Maintenance_themeselector_lable": "",
|
||||
"Maintenance_themeselector_text": "",
|
||||
"Maintenance_version": "",
|
||||
"NETWORK_DEVICE_TYPES_description": "",
|
||||
"NETWORK_DEVICE_TYPES_name": "",
|
||||
"Navigation_About": "",
|
||||
"Navigation_Devices": "",
|
||||
"Navigation_Donations": "",
|
||||
"Navigation_Events": "",
|
||||
"Navigation_HelpFAQ": "",
|
||||
"Navigation_Integrations": "",
|
||||
"Navigation_Maintenance": "",
|
||||
"Navigation_Monitoring": "",
|
||||
"Navigation_Network": "",
|
||||
"Navigation_Plugins": "",
|
||||
"Navigation_Presence": "",
|
||||
"Navigation_Report": "",
|
||||
"Navigation_Settings": "",
|
||||
"Navigation_SystemInfo": "",
|
||||
"Navigation_Workflows": "",
|
||||
"Network_Assign": "",
|
||||
"Network_Cant_Assign": "",
|
||||
"Network_Configuration_Error": "",
|
||||
"Network_Connected": "",
|
||||
"Network_ManageAdd": "",
|
||||
"Network_ManageAdd_Name": "",
|
||||
"Network_ManageAdd_Name_text": "",
|
||||
"Network_ManageAdd_Port": "",
|
||||
"Network_ManageAdd_Port_text": "",
|
||||
"Network_ManageAdd_Submit": "",
|
||||
"Network_ManageAdd_Type": "",
|
||||
"Network_ManageAdd_Type_text": "",
|
||||
"Network_ManageAssign": "",
|
||||
"Network_ManageDel": "",
|
||||
"Network_ManageDel_Name": "",
|
||||
"Network_ManageDel_Name_text": "",
|
||||
"Network_ManageDel_Submit": "",
|
||||
"Network_ManageDevices": "",
|
||||
"Network_ManageEdit": "",
|
||||
"Network_ManageEdit_ID": "",
|
||||
"Network_ManageEdit_ID_text": "",
|
||||
"Network_ManageEdit_Name": "",
|
||||
"Network_ManageEdit_Name_text": "",
|
||||
"Network_ManageEdit_Port": "",
|
||||
"Network_ManageEdit_Port_text": "",
|
||||
"Network_ManageEdit_Submit": "",
|
||||
"Network_ManageEdit_Type": "",
|
||||
"Network_ManageEdit_Type_text": "",
|
||||
"Network_ManageLeaf": "",
|
||||
"Network_ManageUnassign": "",
|
||||
"Network_NoAssignedDevices": "",
|
||||
"Network_NoDevices": "",
|
||||
"Network_Node": "",
|
||||
"Network_Node_Name": "",
|
||||
"Network_Parent": "",
|
||||
"Network_Root": "",
|
||||
"Network_Root_Not_Configured": "",
|
||||
"Network_Root_Unconfigurable": "",
|
||||
"Network_Table_Hostname": "",
|
||||
"Network_Table_IP": "",
|
||||
"Network_Table_State": "",
|
||||
"Network_Title": "",
|
||||
"Network_UnassignedDevices": "",
|
||||
"PIALERT_WEB_PASSWORD_description": "",
|
||||
"PIALERT_WEB_PASSWORD_name": "",
|
||||
"PIALERT_WEB_PROTECTION_description": "",
|
||||
"PIALERT_WEB_PROTECTION_name": "",
|
||||
"PLUGINS_KEEP_HIST_description": "",
|
||||
"PLUGINS_KEEP_HIST_name": "",
|
||||
"Plugins_DeleteAll": "",
|
||||
"Plugins_Filters_Mac": "",
|
||||
"Plugins_History": "",
|
||||
"Plugins_Objects": "",
|
||||
"Plugins_Out_of": "",
|
||||
"Plugins_Unprocessed_Events": "",
|
||||
"Plugins_no_control": "",
|
||||
"Presence_CalHead_day": "",
|
||||
"Presence_CalHead_lang": "",
|
||||
"Presence_CalHead_month": "",
|
||||
"Presence_CalHead_quarter": "",
|
||||
"Presence_CalHead_week": "",
|
||||
"Presence_CalHead_year": "",
|
||||
"Presence_CallHead_Devices": "",
|
||||
"Presence_Loading": "",
|
||||
"Presence_Shortcut_AllDevices": "",
|
||||
"Presence_Shortcut_Archived": "",
|
||||
"Presence_Shortcut_Connected": "",
|
||||
"Presence_Shortcut_Devices": "",
|
||||
"Presence_Shortcut_DownAlerts": "",
|
||||
"Presence_Shortcut_Favorites": "",
|
||||
"Presence_Shortcut_NewDevices": "",
|
||||
"Presence_Title": "",
|
||||
"REPORT_DASHBOARD_URL_description": "",
|
||||
"REPORT_DASHBOARD_URL_name": "",
|
||||
"REPORT_ERROR": "",
|
||||
"REPORT_MAIL_description": "",
|
||||
"REPORT_MAIL_name": "",
|
||||
"REPORT_TITLE": "",
|
||||
"RandomMAC_hover": "",
|
||||
"SCAN_SUBNETS_description": "",
|
||||
"SYSTEM_TITLE": "",
|
||||
"Setting_Override": "",
|
||||
"Setting_Override_Description": "",
|
||||
"Settings_Metadata_Toggle": "",
|
||||
"Settings_device_Scanners_desync": "",
|
||||
"Settings_device_Scanners_desync_popup": "",
|
||||
"Speedtest_Results": "",
|
||||
"Systeminfo_CPU": "",
|
||||
"Systeminfo_CPU_Cores": "",
|
||||
"Systeminfo_CPU_Name": "",
|
||||
"Systeminfo_CPU_Speed": "",
|
||||
"Systeminfo_CPU_Temp": "",
|
||||
"Systeminfo_CPU_Vendor": "",
|
||||
"Systeminfo_Client_Resolution": "",
|
||||
"Systeminfo_Client_User_Agent": "",
|
||||
"Systeminfo_General": "",
|
||||
"Systeminfo_General_Date": "",
|
||||
"Systeminfo_General_Date2": "",
|
||||
"Systeminfo_General_Full_Date": "",
|
||||
"Systeminfo_General_TimeZone": "",
|
||||
"Systeminfo_Memory": "",
|
||||
"Systeminfo_Memory_Total_Memory": "",
|
||||
"Systeminfo_Memory_Usage": "",
|
||||
"Systeminfo_Memory_Usage_Percent": "",
|
||||
"Systeminfo_Motherboard": "",
|
||||
"Systeminfo_Motherboard_BIOS": "",
|
||||
"Systeminfo_Motherboard_BIOS_Date": "",
|
||||
"Systeminfo_Motherboard_BIOS_Vendor": "",
|
||||
"Systeminfo_Motherboard_Manufactured": "",
|
||||
"Systeminfo_Motherboard_Name": "",
|
||||
"Systeminfo_Motherboard_Revision": "",
|
||||
"Systeminfo_Network": "",
|
||||
"Systeminfo_Network_Accept_Encoding": "",
|
||||
"Systeminfo_Network_Accept_Language": "",
|
||||
"Systeminfo_Network_Connection_Port": "",
|
||||
"Systeminfo_Network_HTTP_Host": "",
|
||||
"Systeminfo_Network_HTTP_Referer": "",
|
||||
"Systeminfo_Network_HTTP_Referer_String": "",
|
||||
"Systeminfo_Network_Hardware": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Mask": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Name": "",
|
||||
"Systeminfo_Network_Hardware_Interface_RX": "",
|
||||
"Systeminfo_Network_Hardware_Interface_TX": "",
|
||||
"Systeminfo_Network_IP": "",
|
||||
"Systeminfo_Network_IP_Connection": "",
|
||||
"Systeminfo_Network_IP_Server": "",
|
||||
"Systeminfo_Network_MIME": "",
|
||||
"Systeminfo_Network_Request_Method": "",
|
||||
"Systeminfo_Network_Request_Time": "",
|
||||
"Systeminfo_Network_Request_URI": "",
|
||||
"Systeminfo_Network_Secure_Connection": "",
|
||||
"Systeminfo_Network_Secure_Connection_String": "",
|
||||
"Systeminfo_Network_Server_Name": "",
|
||||
"Systeminfo_Network_Server_Name_String": "",
|
||||
"Systeminfo_Network_Server_Query": "",
|
||||
"Systeminfo_Network_Server_Query_String": "",
|
||||
"Systeminfo_Network_Server_Version": "",
|
||||
"Systeminfo_Services": "",
|
||||
"Systeminfo_Services_Description": "",
|
||||
"Systeminfo_Services_Name": "",
|
||||
"Systeminfo_Storage": "",
|
||||
"Systeminfo_Storage_Device": "",
|
||||
"Systeminfo_Storage_Mount": "",
|
||||
"Systeminfo_Storage_Size": "",
|
||||
"Systeminfo_Storage_Type": "",
|
||||
"Systeminfo_Storage_Usage": "",
|
||||
"Systeminfo_Storage_Usage_Free": "",
|
||||
"Systeminfo_Storage_Usage_Mount": "",
|
||||
"Systeminfo_Storage_Usage_Total": "",
|
||||
"Systeminfo_Storage_Usage_Used": "",
|
||||
"Systeminfo_System": "",
|
||||
"Systeminfo_System_AVG": "",
|
||||
"Systeminfo_System_Architecture": "",
|
||||
"Systeminfo_System_Kernel": "",
|
||||
"Systeminfo_System_OSVersion": "",
|
||||
"Systeminfo_System_Running_Processes": "",
|
||||
"Systeminfo_System_System": "",
|
||||
"Systeminfo_System_Uname": "",
|
||||
"Systeminfo_System_Uptime": "",
|
||||
"Systeminfo_This_Client": "",
|
||||
"Systeminfo_USB_Devices": "",
|
||||
"TIMEZONE_description": "",
|
||||
"TIMEZONE_name": "",
|
||||
"UI_LANG_description": "",
|
||||
"UI_LANG_name": "",
|
||||
"UI_MY_DEVICES_description": "",
|
||||
"UI_MY_DEVICES_name": "",
|
||||
"UI_NOT_RANDOM_MAC_description": "",
|
||||
"UI_NOT_RANDOM_MAC_name": "",
|
||||
"UI_PRESENCE_description": "",
|
||||
"UI_PRESENCE_name": "",
|
||||
"devices_old": "",
|
||||
"general_event_description": "",
|
||||
"general_event_title": "",
|
||||
"report_guid": "",
|
||||
"report_guid_missing": "",
|
||||
"report_select_format": "",
|
||||
"report_time": "",
|
||||
"run_event_icon": "",
|
||||
"run_event_tooltip": "",
|
||||
"settings_core_icon": "",
|
||||
"settings_core_label": "",
|
||||
"settings_device_scanners": "",
|
||||
"settings_device_scanners_icon": "",
|
||||
"settings_device_scanners_label": "",
|
||||
"settings_enabled": "",
|
||||
"settings_enabled_icon": "",
|
||||
"settings_expand_all": "",
|
||||
"settings_imported": "",
|
||||
"settings_imported_label": "",
|
||||
"settings_missing": "",
|
||||
"settings_missing_block": "",
|
||||
"settings_old": "",
|
||||
"settings_other_scanners": "",
|
||||
"settings_other_scanners_icon": "",
|
||||
"settings_other_scanners_label": "",
|
||||
"settings_publishers": "",
|
||||
"settings_publishers_icon": "",
|
||||
"settings_publishers_label": "",
|
||||
"settings_saved": "",
|
||||
"settings_system_icon": "",
|
||||
"settings_system_label": "",
|
||||
"test_event_icon": "",
|
||||
"test_event_tooltip": ""
|
||||
}
|
||||
@@ -4,8 +4,6 @@
|
||||
require 'php/templates/notification.php';
|
||||
?>
|
||||
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
<!-- Page ------------------------------------------------------------------ -->
|
||||
<div class="content-wrapper">
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
| | | PHOLUS | Script | ♻ other | 📚[pholus_scan](/front/plugins/pholus_scan/) |
|
||||
| | Yes | PIHOLE | External SQLite DB | 🔍dev scanner | 📚[pihole_scan](/front/plugins/pihole_scan/) |
|
||||
| | | PUSHSAFER | Script | 💬 publisher | 📚[_publisher_pushsafer](/front/plugins/_publisher_pushsafer/) |
|
||||
| | | PUSHOVER | Script | 💬 publisher | 📚[_pushover_pushsafer](/front/plugins/_publisher_pushover/) |
|
||||
| | | SETPWD | Script | ⚙ system | 📚[set_password](/front/plugins/set_password/) |
|
||||
| | | SMTP | Script | 💬 publisher | 📚[_publisher_email](/front/plugins/_publisher_email/) |
|
||||
| | Yes | SNMPDSC | Script | 🔍dev scanner | 📚[snmp_discovery](/front/plugins/snmp_discovery/) |
|
||||
@@ -647,6 +648,7 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
|
||||
| See below for information on `threshold`, `replace`. | |
|
||||
| | |
|
||||
| `options` Property | Used in conjunction with types like `threshold`, `replace`, `regex`. |
|
||||
| `options_params` Property | Used in conjunction with a `"options": "[{value}]"` template and `text.select`. Can specify SQL query (needs to return 2 columns `SELECT dev_Name as name, dev_Mac as id`) or Setting (not tested) to populate the dropdown. Check example below or have a look at the `NEWDEV` plugin `config.json` file. |
|
||||
| `threshold` | The `options` array contains objects ordered from the lowest `maximum` to the highest. The corresponding `hexColor` is used for the value background color if it's less than the specified `maximum` but more than the previous one in the `options` array. |
|
||||
| `replace` | The `options` array contains objects with an `equals` property, which is compared to the "value." If the values are the same, the string in `replacement` is displayed in the UI instead of the actual "value". |
|
||||
| `regex` | Applies a regex to the value. The `options` array contains objects with an `type` (must be set to `regex`) and `param` (must contain the regex itself) property. |
|
||||
@@ -665,6 +667,26 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
|
||||
> Supports chaining. You can chain multiple resolvers with `.`. For example `regex.url_http_https`. This will apply the `regex` resolver and then the `url_http_https` resolver.
|
||||
|
||||
|
||||
```json
|
||||
"function": "dev_DeviceType",
|
||||
"type": "text.select",
|
||||
"maxLength": 30,
|
||||
"default_value": "",
|
||||
"options": ["{value}"],
|
||||
"options_params" : [
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT '' as id, '' as name UNION SELECT dev_DeviceType as id, dev_DeviceType as name FROM (SELECT dev_DeviceType FROM Devices UNION SELECT 'Smartphone' UNION SELECT 'Tablet' UNION SELECT 'Laptop' UNION SELECT 'PC' UNION SELECT 'Printer' UNION SELECT 'Server' UNION SELECT 'NAS' UNION SELECT 'Domotic' UNION SELECT 'Game Console' UNION SELECT 'SmartTV' UNION SELECT 'Clock' UNION SELECT 'House Appliance' UNION SELECT 'Phone' UNION SELECT 'AP' UNION SELECT 'Gateway' UNION SELECT 'Firewall' UNION SELECT 'Switch' UNION SELECT 'WLAN' UNION SELECT 'Router' UNION SELECT 'Other') AS all_devices ORDER BY id;"
|
||||
},
|
||||
{
|
||||
"name" : "uilang",
|
||||
"type" : "setting",
|
||||
"value" : "UI_LANG"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -276,7 +276,7 @@
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string" : "Enable sending notifications via <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>."
|
||||
"string" : "Enable sending notifications via a self-hosted <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a> instance. Please specify the URL on which you are running your instance in the <code>APPRISE_HOST</code> setting."
|
||||
},
|
||||
{
|
||||
"language_code": "es_es",
|
||||
|
||||
@@ -464,13 +464,28 @@
|
||||
}],
|
||||
"description": [{
|
||||
"language_code": "en_us",
|
||||
"string" : "Quality of service setting for MQTT message sending. <code>0</code> - Low quality to <code>2</code> - High quality. The higher the quality the longer the delay."
|
||||
"string" : "Quality of service setting for MQTT message sending. The higher the quality the longer the delay. <br/> <code>0</code> - Low quality to <code>2</code> - High quality."
|
||||
},
|
||||
{
|
||||
"language_code": "es_es",
|
||||
"string" : "Configuración de calidad de servicio para el envío de mensajes MQTT. <code>0</code>: baja calidad a <code>2</code>: alta calidad. Cuanto mayor sea la calidad, mayor será el retraso."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "VERSION",
|
||||
"type": "integer.select",
|
||||
"default_value": 1,
|
||||
"options": [ 1, 2 ],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code": "en_us",
|
||||
"string" : "Version"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code": "en_us",
|
||||
"string" : "Paho MQTT API version. Depends on the MQTT <a href=\"https://eclipse.dev/paho/files/paho.mqtt.python/html/index.html#callbacks\" target=\"_blank\">version supported by the MQTT broker</a>. Usually set to <code>1</code>."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "DELAY_SEC",
|
||||
"type": "integer",
|
||||
|
||||
@@ -9,7 +9,9 @@ import sys
|
||||
from datetime import datetime
|
||||
import time
|
||||
import re
|
||||
from paho.mqtt import client as mqtt_client
|
||||
import paho.mqtt.client as mqtt
|
||||
# from paho.mqtt import client as mqtt_client
|
||||
# from paho.mqtt import CallbackAPIVersion as mqtt_CallbackAPIVersion
|
||||
import hashlib
|
||||
|
||||
|
||||
@@ -37,13 +39,12 @@ plugin_objects = Plugin_Objects(RESULT_FILE)
|
||||
md5_hash = hashlib.md5()
|
||||
|
||||
pluginName = 'MQTT'
|
||||
module_name = pluginName
|
||||
|
||||
# globals
|
||||
|
||||
mqtt_sensors = []
|
||||
mqtt_connected_to_broker = False
|
||||
client = None # mqtt client
|
||||
mqtt_client = None # mqtt client
|
||||
|
||||
def main():
|
||||
|
||||
@@ -51,7 +52,7 @@ def main():
|
||||
|
||||
# Check if basic config settings supplied
|
||||
if check_config() == False:
|
||||
mylog('none', [f'[{pluginName}] ⚠ ERROR: Publisher notification gateway not set up correctly. Check your pialert.conf {pluginName}_* variables.'])
|
||||
mylog('verbose', [f'[{pluginName}] ⚠ ERROR: Publisher notification gateway not set up correctly. Check your pialert.conf {pluginName}_* variables.'])
|
||||
return
|
||||
|
||||
# Create a database connection
|
||||
@@ -70,7 +71,7 @@ def main():
|
||||
#-------------------------------------------------------------------------------
|
||||
def check_config():
|
||||
if get_setting_value('MQTT_BROKER') == '' or get_setting_value('MQTT_PORT') == '' or get_setting_value('MQTT_USER') == '' or get_setting_value('MQTT_PASSWORD') == '':
|
||||
mylog('none', ['[Check Config] ⚠ ERROR: MQTT service not set up correctly. Check your pialert.conf MQTT_* variables.'])
|
||||
mylog('verbose', ['[Check Config] ⚠ ERROR: MQTT service not set up correctly. Check your pialert.conf MQTT_* variables.'])
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -130,46 +131,61 @@ class sensor_config:
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
def publish_mqtt(client, topic, message):
|
||||
def publish_mqtt(mqtt_client, topic, message):
|
||||
status = 1
|
||||
|
||||
|
||||
message = json.dumps(message).replace("'",'"')
|
||||
qos = get_setting_value('MQTT_QOS')
|
||||
|
||||
mylog('verbose', [f"[{pluginName}] Sending MQTT topic: {topic}"])
|
||||
mylog('verbose', [f"[{pluginName}] Sending MQTT message: {message}"])
|
||||
# mylog('verbose', [f"[{pluginName}] get_setting_value('MQTT_QOS'): {qos}"])
|
||||
|
||||
if mqtt_connected_to_broker == False:
|
||||
|
||||
mylog('verbose', [f"[{pluginName}] ⚠ ERROR: Not connected to broker, aborting."])
|
||||
|
||||
return False
|
||||
|
||||
while status != 0:
|
||||
result = client.publish(
|
||||
|
||||
# mylog('verbose', [f"[{pluginName}] mqtt_client.publish "])
|
||||
# mylog('verbose', [f"[{pluginName}] mqtt_client.is_connected(): {mqtt_client.is_connected()} "])
|
||||
|
||||
result = mqtt_client.publish(
|
||||
topic=topic,
|
||||
payload=message,
|
||||
qos=get_setting_value('MQTT_QOS'),
|
||||
qos=qos,
|
||||
retain=True,
|
||||
)
|
||||
|
||||
status = result[0]
|
||||
|
||||
# mylog('verbose', [f"[{pluginName}] status: {status}"])
|
||||
# mylog('verbose', [f"[{pluginName}] result: {result}"])
|
||||
|
||||
if status != 0:
|
||||
mylog('minimal', [f"[{pluginName}] Waiting to reconnect to MQTT broker"])
|
||||
mylog('verbose', [f"[{pluginName}] Waiting to reconnect to MQTT broker"])
|
||||
time.sleep(0.1)
|
||||
return True
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def create_generic_device(client):
|
||||
def create_generic_device(mqtt_client):
|
||||
|
||||
deviceName = 'PiAlert'
|
||||
deviceId = 'pialert'
|
||||
|
||||
create_sensor(client, deviceId, deviceName, 'sensor', 'online', 'wifi-check')
|
||||
create_sensor(client, deviceId, deviceName, 'sensor', 'down', 'wifi-cancel')
|
||||
create_sensor(client, deviceId, deviceName, 'sensor', 'all', 'wifi')
|
||||
create_sensor(client, deviceId, deviceName, 'sensor', 'archived', 'wifi-lock')
|
||||
create_sensor(client, deviceId, deviceName, 'sensor', 'new', 'wifi-plus')
|
||||
create_sensor(client, deviceId, deviceName, 'sensor', 'unknown', 'wifi-alert')
|
||||
create_sensor(mqtt_client, deviceId, deviceName, 'sensor', 'online', 'wifi-check')
|
||||
create_sensor(mqtt_client, deviceId, deviceName, 'sensor', 'down', 'wifi-cancel')
|
||||
create_sensor(mqtt_client, deviceId, deviceName, 'sensor', 'all', 'wifi')
|
||||
create_sensor(mqtt_client, deviceId, deviceName, 'sensor', 'archived', 'wifi-lock')
|
||||
create_sensor(mqtt_client, deviceId, deviceName, 'sensor', 'new', 'wifi-plus')
|
||||
create_sensor(mqtt_client, deviceId, deviceName, 'sensor', 'unknown', 'wifi-alert')
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
def create_sensor(client, deviceId, deviceName, sensorType, sensorName, icon, mac=""):
|
||||
def create_sensor(mqtt_client, deviceId, deviceName, sensorType, sensorName, icon, mac=""):
|
||||
|
||||
global mqtt_sensors
|
||||
|
||||
@@ -177,35 +193,37 @@ def create_sensor(client, deviceId, deviceName, sensorType, sensorName, icon, ma
|
||||
|
||||
# save if new
|
||||
if new_sensor_config.isNew:
|
||||
mylog('minimal', [f"[{pluginName}] Publishing sensor number {len(mqtt_sensors)}"])
|
||||
publish_sensor(client, new_sensor_config)
|
||||
mylog('verbose', [f"[{pluginName}] Publishing sensor number {len(mqtt_sensors)}"])
|
||||
publish_sensor(mqtt_client, new_sensor_config)
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def publish_sensor(client, sensorConfig):
|
||||
def publish_sensor(mqtt_client, sensorConfig):
|
||||
|
||||
global mqtt_sensors
|
||||
|
||||
message = '{ \
|
||||
"name":"'+sensorConfig.sensorName+'", \
|
||||
"state_topic":"system-sensors/'+sensorConfig.sensorType+'/'+sensorConfig.deviceId+'/state", \
|
||||
"value_template":"{{value_json.'+sensorConfig.sensorName+'}}", \
|
||||
"unique_id":"'+sensorConfig.deviceId+'_sensor_'+sensorConfig.sensorName+'", \
|
||||
"device": \
|
||||
{ \
|
||||
"identifiers": ["'+sensorConfig.deviceId+'_sensor"], \
|
||||
"manufacturer": "PiAlert", \
|
||||
"name":"'+sensorConfig.deviceName+'" \
|
||||
}, \
|
||||
"icon":"mdi:'+sensorConfig.icon+'" \
|
||||
}'
|
||||
icon = "mdi:" + sensorConfig.icon
|
||||
|
||||
message = {
|
||||
"name" : sensorConfig.sensorName,
|
||||
"state_topic" : "system-sensors/"+sensorConfig.sensorType+'/'+sensorConfig.deviceId+"/state",
|
||||
"value_template" : "{{value_json."+sensorConfig.sensorName+"}}",
|
||||
"unique_id" : sensorConfig.deviceId+'_sensor_'+sensorConfig.sensorName,
|
||||
"device":
|
||||
{
|
||||
"identifiers" : [sensorConfig.deviceId+"_sensor"],
|
||||
"manufacturer" : "PiAlert",
|
||||
"name" : sensorConfig.deviceName
|
||||
},
|
||||
"icon": icon
|
||||
}
|
||||
|
||||
topic='homeassistant/'+sensorConfig.sensorType+'/'+sensorConfig.deviceId+'/'+sensorConfig.sensorName+'/config'
|
||||
|
||||
# add the sensor to the global list to keep track of succesfully added sensors
|
||||
if publish_mqtt(client, topic, message):
|
||||
if publish_mqtt(mqtt_client, topic, message):
|
||||
# hack - delay adding to the queue in case the process is
|
||||
time.sleep(get_setting_value('MQTT_DELAY_SEC')) # restarted and previous publish processes aborted
|
||||
# (it takes ~2s to update a sensor config on the broker)
|
||||
@@ -213,7 +231,7 @@ def publish_sensor(client, sensorConfig):
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def mqtt_create_client():
|
||||
def on_disconnect(client, userdata, rc):
|
||||
def on_disconnect(mqtt_client, userdata, reason_code):
|
||||
|
||||
global mqtt_connected_to_broker
|
||||
|
||||
@@ -222,61 +240,63 @@ def mqtt_create_client():
|
||||
# not sure is below line is correct / necessary
|
||||
# client = mqtt_create_client()
|
||||
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
def on_connect(mqtt_client, userdata, flags, reason_code):
|
||||
|
||||
global mqtt_connected_to_broker
|
||||
|
||||
if rc == 0:
|
||||
if reason_code == 0:
|
||||
mylog('verbose', [f"[{pluginName}] Connected to broker"])
|
||||
mqtt_connected_to_broker = True # Signal connection
|
||||
else:
|
||||
mylog('none', [f"[{pluginName}] Connection failed"])
|
||||
mylog('verbose', [f"[{pluginName}] Connection failed, reason_code: {reason_code}"])
|
||||
mqtt_connected_to_broker = False
|
||||
|
||||
|
||||
global client
|
||||
global mqtt_client
|
||||
|
||||
client = mqtt_client.Client('PiAlert') # Set Connecting Client ID
|
||||
client.username_pw_set(get_setting_value('MQTT_USER'), get_setting_value('MQTT_PASSWORD'))
|
||||
client.on_connect = on_connect
|
||||
client.on_disconnect = on_disconnect
|
||||
client.connect(get_setting_value('MQTT_BROKER'), get_setting_value('MQTT_PORT'))
|
||||
client.loop_start()
|
||||
if get_setting_value('MQTT_VERSION') == 1:
|
||||
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)
|
||||
else:
|
||||
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
|
||||
|
||||
return client
|
||||
|
||||
mqtt_client.username_pw_set(get_setting_value('MQTT_USER'), get_setting_value('MQTT_PASSWORD'))
|
||||
mqtt_client.on_connect = on_connect
|
||||
mqtt_client.on_disconnect = on_disconnect
|
||||
mqtt_client.connect(get_setting_value('MQTT_BROKER'), get_setting_value('MQTT_PORT'))
|
||||
mqtt_client.loop_start()
|
||||
|
||||
return mqtt_client
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def mqtt_start(db):
|
||||
|
||||
global client, mqtt_connected_to_broker
|
||||
global mqtt_client, mqtt_connected_to_broker
|
||||
|
||||
if mqtt_connected_to_broker == False:
|
||||
mqtt_connected_to_broker = True
|
||||
client = mqtt_create_client()
|
||||
mqtt_client = mqtt_create_client()
|
||||
|
||||
# General stats
|
||||
|
||||
# Create a generic device for overal stats
|
||||
if get_setting_value('MQTT_SEND_STATS') == True:
|
||||
# Create a new device representing overall PiAlert stats
|
||||
create_generic_device(client)
|
||||
create_generic_device(mqtt_client)
|
||||
|
||||
# Get the data
|
||||
row = get_device_stats(db)
|
||||
|
||||
columns = ["online","down","all","archived","new","unknown"]
|
||||
|
||||
payload = ""
|
||||
|
||||
# Update the values
|
||||
for column in columns:
|
||||
payload += '"'+column+'": ' + str(row[column]) +','
|
||||
|
||||
# Publish (wrap into {} and remove last ',' from above)
|
||||
publish_mqtt(client, "system-sensors/sensor/pialert/state",
|
||||
'{ \
|
||||
'+ payload[:-1] +'\
|
||||
}'
|
||||
publish_mqtt(mqtt_client, "system-sensors/sensor/pialert/state",
|
||||
{
|
||||
"online": row[0],
|
||||
"down": row[1],
|
||||
"all": row[2],
|
||||
"archived": row[3],
|
||||
"new": row[4],
|
||||
"unknown": row[5]
|
||||
}
|
||||
)
|
||||
|
||||
# Generate device-specific MQTT messages if enabled
|
||||
@@ -289,37 +309,37 @@ def mqtt_start(db):
|
||||
|
||||
sec_delay = len(devices) * int(get_setting_value('MQTT_DELAY_SEC'))*5
|
||||
|
||||
mylog('minimal', [f"[{pluginName}] Estimated delay: ", (sec_delay), 's ', '(', round(sec_delay/60,1) , 'min)' ])
|
||||
mylog('verbose', [f"[{pluginName}] Estimated delay: ", (sec_delay), 's ', '(', round(sec_delay/60,1) , 'min)' ])
|
||||
|
||||
# debug_index = 0
|
||||
|
||||
for device in devices:
|
||||
|
||||
|
||||
# Create devices in Home Assistant - send config messages
|
||||
deviceId = 'mac_' + device["dev_MAC"].replace(" ", "").replace(":", "_").lower()
|
||||
deviceNameDisplay = re.sub('[^a-zA-Z0-9-_\s]', '', device["dev_Name"])
|
||||
|
||||
create_sensor(client, deviceId, deviceNameDisplay, 'sensor', 'last_ip', 'ip-network', device["dev_MAC"])
|
||||
create_sensor(client, deviceId, deviceNameDisplay, 'binary_sensor', 'is_present', 'wifi', device["dev_MAC"])
|
||||
create_sensor(client, deviceId, deviceNameDisplay, 'sensor', 'mac_address', 'folder-key-network', device["dev_MAC"])
|
||||
create_sensor(client, deviceId, deviceNameDisplay, 'sensor', 'is_new', 'bell-alert-outline', device["dev_MAC"])
|
||||
create_sensor(client, deviceId, deviceNameDisplay, 'sensor', 'vendor', 'cog', device["dev_MAC"])
|
||||
create_sensor(mqtt_client, deviceId, deviceNameDisplay, 'sensor', 'last_ip', 'ip-network', device["dev_MAC"])
|
||||
create_sensor(mqtt_client, deviceId, deviceNameDisplay, 'binary_sensor', 'is_present', 'wifi', device["dev_MAC"])
|
||||
create_sensor(mqtt_client, deviceId, deviceNameDisplay, 'sensor', 'mac_address', 'folder-key-network', device["dev_MAC"])
|
||||
create_sensor(mqtt_client, deviceId, deviceNameDisplay, 'sensor', 'is_new', 'bell-alert-outline', device["dev_MAC"])
|
||||
create_sensor(mqtt_client, deviceId, deviceNameDisplay, 'sensor', 'vendor', 'cog', device["dev_MAC"])
|
||||
|
||||
# update device sensors in home assistant
|
||||
|
||||
publish_mqtt(client, 'system-sensors/sensor/'+deviceId+'/state',
|
||||
'{ \
|
||||
"last_ip": "' + device["dev_LastIP"] +'", \
|
||||
"is_new": "' + str(device["dev_NewDevice"]) +'", \
|
||||
"vendor": "' + sanitize_string(device["dev_Vendor"]) +'", \
|
||||
"mac_address": "' + str(device["dev_MAC"]) +'" \
|
||||
}'
|
||||
publish_mqtt(mqtt_client, 'system-sensors/sensor/'+deviceId+'/state',
|
||||
{
|
||||
"last_ip": device["dev_LastIP"],
|
||||
"is_new": str(device["dev_NewDevice"]),
|
||||
"vendor": sanitize_string(device["dev_Vendor"]),
|
||||
"mac_address": str(device["dev_MAC"])
|
||||
}
|
||||
)
|
||||
|
||||
publish_mqtt(client, 'system-sensors/binary_sensor/'+deviceId+'/state',
|
||||
'{ \
|
||||
"is_present": "' + to_binary_sensor(str(device["dev_PresentLastScan"])) +'"\
|
||||
}'
|
||||
publish_mqtt(mqtt_client, 'system-sensors/binary_sensor/'+deviceId+'/state',
|
||||
{
|
||||
"is_present": to_binary_sensor(str(device["dev_PresentLastScan"]))
|
||||
}
|
||||
)
|
||||
|
||||
# delete device / topic
|
||||
|
||||
@@ -404,6 +404,28 @@
|
||||
"string": "Your Pushover APP Token."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "DEVICE_NAME",
|
||||
"type": "text",
|
||||
"default_value": "DEVICE_NAME",
|
||||
"options": [],
|
||||
"localized": [
|
||||
"name",
|
||||
"description"
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Pushover Device name"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "(Optional) When specifying a device name, notifications will be exclusively sent to the device."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,18 +71,28 @@ def send(text):
|
||||
|
||||
user_key = get_setting_value("PUSHOVER_USER_KEY")
|
||||
app_token = get_setting_value("PUSHOVER_APP_TOKEN")
|
||||
device_name = (
|
||||
None
|
||||
if get_setting_value("PUSHOVER_DEVICE_NAME") == "DEVICE_NAME"
|
||||
else get_setting_value("PUSHOVER_DEVICE_NAME")
|
||||
)
|
||||
|
||||
mylog("verbose", f'[{pluginName}] PUSHOVER_USER_KEY: "{hide_string(user_key)}"')
|
||||
mylog("verbose", f'[{pluginName}] PUSHOVER_APP_TOKEN: "{hide_string(app_token)}"')
|
||||
|
||||
data = {"token": app_token, "user": user_key, "message": text}
|
||||
# Add device_name to the data dictionary only if it is not None
|
||||
if device_name:
|
||||
data["device"] = device_name
|
||||
|
||||
try:
|
||||
response = requests.post(
|
||||
"https://api.pushover.net/1/messages.json",
|
||||
data={"token": app_token, "user": user_key, "message": text},
|
||||
)
|
||||
response = requests.post("https://api.pushover.net/1/messages.json", data=data)
|
||||
|
||||
# Update response_status_code with the actual status code from the response
|
||||
response_status_code = response.status_code
|
||||
|
||||
# Check if the request was successful (status code 200)
|
||||
if response.status_code == 200:
|
||||
if response_status_code == 200:
|
||||
response_text = response.text # This captures the response body/message
|
||||
else:
|
||||
response_text = json.dumps(response.text)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
## Overview
|
||||
|
||||
Plugin generating CSV backups of your Devices database table, including the network mappings. Can be used for importing your setup via the Maintenance > Backup / Restore > CSV Import feature.
|
||||
Plugin generating CSV backups of your Devices database table, including the network mappings. Can be used for importing your setup via the Maintenance > Backup / Restore > CSV Import feature (See also: [Devices Bulk Editing](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/DEVICES_BULK_EDITING.md)).
|
||||
|
||||
### Usage
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "List of IPs to ignore. Use <code>%</code> as a wildcard. Ignored devices will not be shown anywhere in the UI or notifications. <br/><br/>For example <code>192.168.3.%</code> to filter out a subnet."
|
||||
"string": "List of IPs to ignore. Use <code>%</code> as a wildcard. Ignored devices will not be shown anywhere in the UI or notifications. <br/><br/>For example <code>192.168.3.%</code> to filter out an IP range."
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -104,10 +104,17 @@
|
||||
},
|
||||
{
|
||||
"function": "dev_Owner",
|
||||
"type": "string",
|
||||
"type": "text.select",
|
||||
"maxLength": 30,
|
||||
"default_value": "House",
|
||||
"options": [],
|
||||
"default_value": "",
|
||||
"options": ["{value}"],
|
||||
"options_params" : [
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT DISTINCT '' as id, '' as name UNION SELECT dev_Owner as id, dev_Owner as name FROM (SELECT dev_Owner FROM Devices UNION SELECT 'House' ) AS all_devices ORDER BY id;"
|
||||
}
|
||||
],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
@@ -124,10 +131,22 @@
|
||||
},
|
||||
{
|
||||
"function": "dev_DeviceType",
|
||||
"type": "string",
|
||||
"type": "text.select",
|
||||
"maxLength": 30,
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"options": ["{value}"],
|
||||
"options_params" : [
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT '' as id, '' as name UNION SELECT dev_DeviceType as id, dev_DeviceType as name FROM (SELECT dev_DeviceType FROM Devices UNION SELECT 'Smartphone' UNION SELECT 'Tablet' UNION SELECT 'Laptop' UNION SELECT 'PC' UNION SELECT 'Printer' UNION SELECT 'Server' UNION SELECT 'NAS' UNION SELECT 'Domotic' UNION SELECT 'Game Console' UNION SELECT 'SmartTV' UNION SELECT 'Clock' UNION SELECT 'House Appliance' UNION SELECT 'Phone' UNION SELECT 'AP' UNION SELECT 'Gateway' UNION SELECT 'Firewall' UNION SELECT 'Switch' UNION SELECT 'WLAN' UNION SELECT 'Router' UNION SELECT 'Other') AS all_devices ORDER BY id;"
|
||||
},
|
||||
{
|
||||
"name" : "uilang",
|
||||
"type" : "setting",
|
||||
"value" : "UI_LANG"
|
||||
}
|
||||
],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
@@ -183,10 +202,17 @@
|
||||
},
|
||||
{
|
||||
"function": "dev_Group",
|
||||
"type": "string",
|
||||
"type": "text.select",
|
||||
"maxLength": 10,
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"options": ["{value}"],
|
||||
"options_params" : [
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT DISTINCT '' as id, '' as name UNION SELECT dev_Group as id, dev_Group as name FROM (SELECT dev_Group FROM Devices UNION SELECT 'Personal' ) AS all_devices ORDER BY id;"
|
||||
}
|
||||
],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
@@ -376,14 +402,21 @@
|
||||
},
|
||||
{
|
||||
"function": "dev_SkipRepeated",
|
||||
"type": "integer",
|
||||
"type": "text.select",
|
||||
"default_value": 0,
|
||||
"options": [],
|
||||
"options": ["{value}"],
|
||||
"options_params" : [
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT '0' as id, '0 (notify all)' as name UNION SELECT '168' as id, '1 week' as name UNION SELECT '24' as id, '1 day' as name UNION SELECT '8' as id, '8 h' as name UNION SELECT '1' as id, '1 h' as name"
|
||||
}
|
||||
],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Skip Repeated"
|
||||
"string": "Skip Repeated (h)"
|
||||
}
|
||||
],
|
||||
"description": [
|
||||
@@ -447,16 +480,23 @@
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Indicates whether the device is considered a new device. The default value of the <code>New Device</code> checkbox."
|
||||
"string": "Indicates whether the device is considered a new device. The default value of the <code>New Device</code> checkbox. If checked this will show the New status for the device and include it in lists when the New Devices filter is active. Doesn't affect notifications."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"function": "dev_Location",
|
||||
"type": "string",
|
||||
"type": "text.select",
|
||||
"maxLength": 250,
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"options": ["{value}"],
|
||||
"options_params" : [
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT DISTINCT '' as id, '' as name UNION SELECT dev_Location as id, dev_Location as name FROM (SELECT dev_Location FROM Devices where dev_Location not in (null, 'null', '') UNION SELECT 'Bathroom' UNION SELECT 'Bedroom' UNION SELECT 'Dining room' UNION SELECT 'Hall' UNION SELECT 'Kitchen' UNION SELECT 'Laundry' UNION SELECT 'Living room' UNION SELECT 'Study' UNION SELECT 'Attic' UNION SELECT 'Basement' UNION SELECT 'Garage' UNION SELECT 'Back yard' UNION SELECT 'Garden' UNION SELECT 'Terrace') AS all_devices ORDER BY id; "
|
||||
}
|
||||
],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
@@ -492,9 +532,21 @@
|
||||
},
|
||||
{
|
||||
"function": "dev_Network_Node_MAC_ADDR",
|
||||
"type": "string",
|
||||
"type": "text.select",
|
||||
"default_value": "",
|
||||
"options": [],
|
||||
"options": ["{value}"],
|
||||
"options_params" : [
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT '' as name, '' as id UNION SELECT Dev_Name as name, dev_MAC as id FROM Devices WHERE EXISTS (SELECT 1 FROM Settings WHERE Code_Name = 'NETWORK_DEVICE_TYPES' AND LOWER(value) LIKE '%' || LOWER(dev_DeviceType) || '%' AND dev_DeviceType <> '')"
|
||||
},
|
||||
{
|
||||
"name" : "target_macs",
|
||||
"type" : "setting",
|
||||
"value" : "KNWN_target_macs"
|
||||
}
|
||||
],
|
||||
"localized": ["name", "description"],
|
||||
"name": [
|
||||
{
|
||||
|
||||
@@ -95,7 +95,7 @@ def execute_nslookup (ip, timeout):
|
||||
domain_name = ''
|
||||
dns_server = ''
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] DEBUG OUTPUT : {output}'])
|
||||
# mylog('verbose', [f'[{pluginName}] DEBUG OUTPUT : {output}'])
|
||||
|
||||
# Parse output using case-insensitive regular expressions
|
||||
domain_pattern = re.compile(r'name\s*=\s*([^\s]+)', re.IGNORECASE)
|
||||
@@ -116,14 +116,20 @@ def execute_nslookup (ip, timeout):
|
||||
return domain_name, dns_server
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
# An error occured, handle it
|
||||
mylog('verbose', [f'[{pluginName}]', e.output])
|
||||
mylog('verbose', [f'[{pluginName}] ⚠ ERROR - check logs'])
|
||||
# An error occurred, handle it
|
||||
if "NXDOMAIN" in e.output:
|
||||
mylog('verbose', [f'[{pluginName}]', f"No PTR record found for IP: {ip}"])
|
||||
else:
|
||||
mylog('verbose', [f'[{pluginName}]', e.output])
|
||||
# Handle other errors here
|
||||
# mylog('verbose', [f'[{pluginName}] ⚠ ERROR - check logs'])
|
||||
|
||||
except subprocess.TimeoutExpired as timeErr:
|
||||
mylog('verbose', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached'])
|
||||
|
||||
if output == "": # check if the subprocess failed
|
||||
mylog('verbose', [f'[{pluginName}] Scan: FAIL - check logs'])
|
||||
if output == "": # check if the subprocess failed
|
||||
tmp = 1 # can't have empty
|
||||
# mylog('verbose', [f'[{pluginName}] Scan: FAIL - check logs'])
|
||||
else:
|
||||
mylog('verbose', [f'[{pluginName}] Scan: SUCCESS'])
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ function genericSaveData (id) {
|
||||
$.get(`php/server/dbHelper.php?action=update&dbtable=Plugins_Objects&columnName=Index&id=${index}&columns=UserData&values=${columnValue}`, function(data) {
|
||||
|
||||
// var result = JSON.parse(data);
|
||||
console.log(data)
|
||||
// console.log(data)
|
||||
|
||||
if(sanitize(data) == 'OK')
|
||||
{
|
||||
@@ -246,6 +246,9 @@ function getData(){
|
||||
|
||||
generateTabs()
|
||||
|
||||
// hide spinning icon
|
||||
hideSpinner()
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -580,6 +583,8 @@ function purgeVisible() {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Main sequence
|
||||
|
||||
// show spinning icon
|
||||
showSpinner()
|
||||
getData()
|
||||
updater()
|
||||
|
||||
|
||||
@@ -53,8 +53,9 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
<!-- Page ------------------------------------------------------------------ -->
|
||||
<!-- Page ------------------------------------------------------------------ -->
|
||||
|
||||
<script src="js/pialert_common.js"></script>
|
||||
<script src="js/settings_utils.js"></script>
|
||||
<script src="js/db_methods.js"></script>
|
||||
<script src="js/ui_components.js"></script>
|
||||
|
||||
|
||||
<div id="settingsPage" class="content-wrapper">
|
||||
@@ -98,35 +99,35 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
|
||||
<div class="content settingswrap " id="accordion_gen">
|
||||
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" >
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" id="core_content_header" >
|
||||
<div class ="settings-group col-sm-12">
|
||||
<i class="<?= lang("settings_core_icon");?>"></i> <?= lang("settings_core_label");?>
|
||||
</div>
|
||||
<div class =" col-sm-12" id="core_content"></div>
|
||||
</div>
|
||||
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" >
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" id="system_content_header" >
|
||||
<div class ="settings-group col-sm-12">
|
||||
<i class="<?= lang("settings_system_icon");?>"></i> <?= lang("settings_system_label");?>
|
||||
</div>
|
||||
<div class =" col-sm-12" id="system_content"></div>
|
||||
</div>
|
||||
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" >
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" id="device_scanner_content_header" >
|
||||
<div class ="settings-group col-sm-12">
|
||||
<i class="<?= lang("settings_device_scanners_icon");?>"></i> <?= lang("settings_device_scanners_label");?>
|
||||
</div>
|
||||
<div class =" col-sm-12" id="device_scanner_content"></div>
|
||||
</div>
|
||||
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" >
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" id="other_content_header">
|
||||
<div class ="settings-group col-sm-12">
|
||||
<i class="<?= lang("settings_other_scanners_icon");?>"></i> <?= lang("settings_other_scanners_label");?>
|
||||
</div>
|
||||
<div class =" col-sm-12" id="other_content"></div>
|
||||
</div>
|
||||
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" >
|
||||
<div class ="bg-grey-dark color-palette box panel panel-default col-sm-12 box-default box-info" id="publisher_content_header" >
|
||||
<div class ="settings-group col-sm-12">
|
||||
<i class="<?= lang("settings_publishers_icon");?>"></i> <?= lang("settings_publishers_label");?>
|
||||
</div>
|
||||
@@ -394,11 +395,11 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
|
||||
if(setType.includes(".select"))
|
||||
{
|
||||
inputHtml = generateInputOptions(set, inputHtml, isMultiSelect = false)
|
||||
inputHtml = generateInputOptions(pluginsData, set, inputHtml, isMultiSelect = false)
|
||||
|
||||
} else if(setType.includes(".multiselect"))
|
||||
{
|
||||
inputHtml = generateInputOptions(set, inputHtml, isMultiSelect = true)
|
||||
inputHtml = generateInputOptions(pluginsData, set, inputHtml, isMultiSelect = true)
|
||||
} else{
|
||||
|
||||
// if it's overridable set readonly accordingly
|
||||
@@ -427,7 +428,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
inputHtml = `<input onChange="settingsChanged()" my-data-type="${setType}" class="checkbox" id="${codeName}" type="checkbox" value="${val}" ${checked} ${disabled}/>`;
|
||||
} else if (setType === 'integer.select') {
|
||||
|
||||
inputHtml = generateInputOptions(set, inputHtml)
|
||||
inputHtml = generateInputOptions(pluginsData, set, inputHtml)
|
||||
|
||||
} else if (setType === 'subnets') {
|
||||
inputHtml = `
|
||||
@@ -517,29 +518,58 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
setupSmoothScrolling()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// generate a list of options for a input select
|
||||
function generateInputOptions(set, input, isMultiSelect = false)
|
||||
function generateInputOptions(pluginsData, set, input, isMultiSelect = false)
|
||||
{
|
||||
|
||||
var optionsHtml = ""
|
||||
|
||||
multi = isMultiSelect ? "multiple" : "";
|
||||
input = `<select onChange="settingsChanged()" my-data-type="${set['Type']}" class="form-control" name="${set['Code_Name']}" id="${set['Code_Name']}" ${multi}>`;
|
||||
|
||||
values = createArray(set['Value']);
|
||||
options = createArray(set['Options']);
|
||||
optionsArray = getSettingOptions(set['Code_Name'] )
|
||||
valuesArray = createArray(set['Value']);
|
||||
|
||||
// // check if the result is a SQL query - if so, dropdown will be populated async with AJAX
|
||||
// if(isSQLQuery(optionsArray))
|
||||
// {
|
||||
var targetLocation = set['Code_Name'] + "_initSettingDropdown";
|
||||
|
||||
// placeholder option which will be replaced on callback
|
||||
optionsHtml += `<option id="${targetLocation}" temporary="temporary"></option>`;
|
||||
|
||||
options.forEach(option => {
|
||||
let selected = values.includes(option) ? 'selected' : '';
|
||||
input += `<option value="${option}" ${selected}>${option}</option>`;
|
||||
});
|
||||
// execute AJAX callabck + SQL query resolution
|
||||
initSettingDropdown(set['Code_Name'] , valuesArray, targetLocation)
|
||||
|
||||
input += '</select>';
|
||||
// }
|
||||
// else // it's a string without a SQL resolution requirements
|
||||
// {
|
||||
// options = createArray(optionsArray);
|
||||
|
||||
// options.forEach(option => {
|
||||
// let selected = valuesArray.includes(option) ? 'selected' : '';
|
||||
// optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
|
||||
// });
|
||||
|
||||
// }
|
||||
|
||||
// main selection dropdown wrapper
|
||||
input += `
|
||||
<select onChange="settingsChanged()"
|
||||
my-data-type="${set['Type']}"
|
||||
class="form-control"
|
||||
name="${set['Code_Name']}"
|
||||
id="${set['Code_Name']}" ${multi}>
|
||||
${optionsHtml}
|
||||
|
||||
</select>`;
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
@@ -634,41 +664,6 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
$('#SCAN_SUBNETS').empty();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
function collectSettings()
|
||||
{
|
||||
var settingsArray = [];
|
||||
|
||||
// collect values for each of the different input form controls
|
||||
const noConversion = ['text', 'integer', 'string', 'password', 'readonly', 'text.select', 'integer.select', 'text.multiselect'];
|
||||
|
||||
settingsJSON["data"].forEach(set => {
|
||||
if (noConversion.includes(set['Type'])) {
|
||||
|
||||
settingsArray.push([set["Group"], set["Code_Name"], set["Type"], $('#'+set["Code_Name"]).val()]);
|
||||
|
||||
} else if (set['Type'] === 'boolean' || set['Type'] === 'integer.checkbox') {
|
||||
|
||||
const temp = $(`#${set["Code_Name"]}`).is(':checked') ? 1 : 0;
|
||||
settingsArray.push([set["Group"], set["Code_Name"], set["Type"], temp]);
|
||||
|
||||
} else if (set['Type'] === 'list' || set['Type'] === 'subnets') {
|
||||
const temps = [];
|
||||
$(`#${set["Code_Name"]} option`).each(function (i, selected) {
|
||||
const vl = $(selected).val();
|
||||
if (vl !== '') {
|
||||
temps.push(vl);
|
||||
}
|
||||
});
|
||||
settingsArray.push([set["Group"], set["Code_Name"], set["Type"], JSON.stringify(temps)]);
|
||||
} else if (set['Type'] === 'json') {
|
||||
const temps = $('#'+set["Code_Name"]).val();
|
||||
settingsArray.push([set["Group"], set["Code_Name"], set["Type"], temps]);
|
||||
}
|
||||
});
|
||||
|
||||
return settingsArray;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
function saveSettings() {
|
||||
@@ -677,27 +672,65 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
showModalOk('WARNING', "<?= lang("settings_missing_block")?>");
|
||||
} else
|
||||
{
|
||||
var settingsArray = [];
|
||||
|
||||
// trigger a save settings event in the backend
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/util.php",
|
||||
data: {
|
||||
function: 'savesettings',
|
||||
settings: JSON.stringify(collectSettings()) },
|
||||
success: function(data, textStatus) {
|
||||
|
||||
showModalOk ('Result', data );
|
||||
|
||||
// Remove navigation prompt "Are you sure you want to leave..."
|
||||
window.onbeforeunload = null;
|
||||
// collect values for each of the different input form controls
|
||||
const noConversion = ['text', 'integer', 'string', 'password', 'readonly', 'text.select', 'integer.select', 'text.multiselect'];
|
||||
|
||||
// Reloads the current page
|
||||
setTimeout("window.location.reload()", 3000);
|
||||
// get settings to determine setting type to store values appropriately
|
||||
$.get('api/table_settings.json', function(res) {
|
||||
|
||||
settingsJSON = res;
|
||||
|
||||
data = settingsJSON["data"];
|
||||
|
||||
data.forEach(set => {
|
||||
if (noConversion.includes(set['Type'])) {
|
||||
|
||||
settingsArray.push([set["Group"], set["Code_Name"], set["Type"], $('#'+set["Code_Name"]).val()]);
|
||||
|
||||
} else if (set['Type'] === 'boolean' || set['Type'] === 'integer.checkbox') {
|
||||
|
||||
const temp = $(`#${set["Code_Name"]}`).is(':checked') ? 1 : 0;
|
||||
settingsArray.push([set["Group"], set["Code_Name"], set["Type"], temp]);
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
} else if (set['Type'] === 'list' || set['Type'] === 'subnets') {
|
||||
const temps = [];
|
||||
$(`#${set["Code_Name"]} option`).each(function (i, selected) {
|
||||
const vl = $(selected).val();
|
||||
if (vl !== '') {
|
||||
temps.push(vl);
|
||||
}
|
||||
});
|
||||
settingsArray.push([set["Group"], set["Code_Name"], set["Type"], JSON.stringify(temps)]);
|
||||
} else if (set['Type'] === 'json') {
|
||||
const temps = $('#'+set["Code_Name"]).val();
|
||||
settingsArray.push([set["Group"], set["Code_Name"], set["Type"], temps]);
|
||||
}
|
||||
});
|
||||
|
||||
// trigger a save settings event in the backend
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/util.php",
|
||||
data: {
|
||||
function: 'savesettings',
|
||||
settings: JSON.stringify(settingsArray) },
|
||||
success: function(data, textStatus) {
|
||||
|
||||
showModalOk ('Result', data );
|
||||
|
||||
// Remove navigation prompt "Are you sure you want to leave..."
|
||||
window.onbeforeunload = null;
|
||||
|
||||
// Reloads the current page
|
||||
setTimeout("window.location.reload()", 3000);
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -747,11 +780,12 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
|
||||
} else
|
||||
{
|
||||
hideSpinner()
|
||||
hideSpinner()
|
||||
}
|
||||
|
||||
document.getElementById('lastImportedTime').innerHTML = humanReadable;
|
||||
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
@@ -763,6 +797,8 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
|
||||
<script defer>
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// handling events on the backend initiated by the front end START
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -96,17 +96,17 @@ $total_memorymb = number_format($total_memorymb, 0, '.', '.');
|
||||
$mem_used = round(memory_get_usage() / 1048576 * 100, 2);
|
||||
$memory_usage_percent = round(($mem_used / $total_memorymb), 2);
|
||||
//HDD stats
|
||||
$hdd_result = shell_exec("df | awk '{print $1}'");
|
||||
$hdd_result = shell_exec(" df -P | awk '{print $1}'");
|
||||
$hdd_devices = explode("\n", trim($hdd_result));
|
||||
$hdd_result = shell_exec("df | awk '{print $2}'");
|
||||
$hdd_result = shell_exec(" df -P | awk '{print $2}'");
|
||||
$hdd_devices_total = explode("\n", trim($hdd_result));
|
||||
$hdd_result = shell_exec("df | awk '{print $3}'");
|
||||
$hdd_result = shell_exec(" df -P | awk '{print $3}'");
|
||||
$hdd_devices_used = explode("\n", trim($hdd_result));
|
||||
$hdd_result = shell_exec("df | awk '{print $4}'");
|
||||
$hdd_result = shell_exec(" df -P | awk '{print $4}'");
|
||||
$hdd_devices_free = explode("\n", trim($hdd_result));
|
||||
$hdd_result = shell_exec("df | awk '{print $5}'");
|
||||
$hdd_result = shell_exec(" df -P | awk '{print $5}'");
|
||||
$hdd_devices_percent = explode("\n", trim($hdd_result));
|
||||
$hdd_result = shell_exec("df | awk '{print $6}'");
|
||||
$hdd_result = shell_exec(" df -P | awk '{print $6}'");
|
||||
$hdd_devices_mount = explode("\n", trim($hdd_result));
|
||||
//Network stats
|
||||
// Check Server name
|
||||
@@ -153,6 +153,55 @@ echo '<div class="box box-solid">
|
||||
</div>
|
||||
</div>';
|
||||
|
||||
// Network Hardware ----------------------------------------------------------
|
||||
echo '<div class="box box-solid">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title sysinfo_headline"><i class="fas fa-network-wired"></i> ' . lang('Systeminfo_Network_Hardware') . '</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<table id="networkTable" class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>' . lang('Systeminfo_Network_Hardware_Interface_Name') . '</th>
|
||||
<th>' . lang('Systeminfo_Network_Hardware_Interface_Mask') . '</th>
|
||||
<th>' . lang('Systeminfo_Network_Hardware_Interface_RX') . '</th>
|
||||
<th>' . lang('Systeminfo_Network_Hardware_Interface_TX') . '</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>';
|
||||
|
||||
for ($x = 0; $x < sizeof($net_interfaces); $x++) {
|
||||
$interface_name = str_replace(':', '', $net_interfaces[$x]);
|
||||
$interface_ip_temp = exec('ip addr show ' . $interface_name . ' | grep "inet "');
|
||||
$interface_ip_arr = explode(' ', trim($interface_ip_temp));
|
||||
|
||||
if (!isset($interface_ip_arr[1])) {
|
||||
$interface_ip_arr[1] = '--';
|
||||
}
|
||||
|
||||
if ($net_interfaces_rx[$x] == 0) {
|
||||
$temp_rx = 0;
|
||||
} else {
|
||||
$temp_rx = number_format(round(($net_interfaces_rx[$x] / 1024 / 1024), 2), 2, ',', '.');
|
||||
}
|
||||
if ($net_interfaces_tx[$x] == 0) {
|
||||
$temp_tx = 0;
|
||||
} else {
|
||||
$temp_tx = number_format(round(($net_interfaces_tx[$x] / 1024 / 1024), 2), 2, ',', '.');
|
||||
}
|
||||
echo '<tr>';
|
||||
echo '<td>' . $interface_name . '</td>';
|
||||
echo '<td>' . $interface_ip_arr[1] . '</td>';
|
||||
echo '<td>' . $temp_rx . ' MB</td>';
|
||||
echo '<td>' . $temp_tx . ' MB</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
|
||||
echo ' </tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>';
|
||||
|
||||
// Client ----------------------------------------------------------
|
||||
echo '<div class="box box-solid">
|
||||
<div class="box-header">
|
||||
@@ -455,32 +504,8 @@ echo '<div class="box box-solid">
|
||||
</div>
|
||||
</div>';
|
||||
|
||||
// Network Hardware ----------------------------------------------------------
|
||||
echo '<div class="box box-solid">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title sysinfo_headline"><i class="fas fa-network-wired"></i> ' . lang('Systeminfo_Network_Hardware') . '</h3>
|
||||
</div>
|
||||
<div class="box-body">';
|
||||
|
||||
for ($x = 0; $x < sizeof($net_interfaces); $x++) {
|
||||
$interface_name = str_replace(':', '', $net_interfaces[$x]);
|
||||
$interface_ip_temp = exec('ip addr show ' . $interface_name . ' | grep "inet "');
|
||||
$interface_ip_arr = explode(' ', trim($interface_ip_temp));
|
||||
|
||||
if (!isset($interface_ip_arr[1])) {$interface_ip_arr[1] = '--';}
|
||||
|
||||
if ($net_interfaces_rx[$x] == 0) {$temp_rx = 0;} else { $temp_rx = number_format(round(($net_interfaces_rx[$x] / 1024 / 1024), 2), 2, ',', '.');}
|
||||
if ($net_interfaces_tx[$x] == 0) {$temp_tx = 0;} else { $temp_tx = number_format(round(($net_interfaces_tx[$x] / 1024 / 1024), 2), 2, ',', '.');}
|
||||
echo '<div class="row">';
|
||||
echo '<div class="col-sm-2 sysinfo_network_hardware_a">' . $interface_name . '</div>';
|
||||
echo '<div class="col-sm-2 sysinfo_network_hardware_b">' . $interface_ip_arr[1] . '</div>';
|
||||
echo '<div class="col-sm-3 sysinfo_network_hardware_b">RX: <div class="sysinfo_network_value">' . $temp_rx . ' MB</div></div>';
|
||||
echo '<div class="col-sm-3 sysinfo_network_hardware_b">TX: <div class="sysinfo_network_value">' . $temp_tx . ' MB</div></div>';
|
||||
echo '</div>';
|
||||
|
||||
}
|
||||
echo ' </div>
|
||||
</div>';
|
||||
|
||||
// Services ----------------------------------------------------------
|
||||
echo '<div class="box box-solid">
|
||||
@@ -553,3 +578,32 @@ echo '<br>';
|
||||
<!-- /.content-wrapper -->
|
||||
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css">
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net/css/select.dataTables.min.css">
|
||||
<script src="lib/AdminLTE/bower_components/datatables.net/js/jquery.dataTables.min.js"></script>
|
||||
<script src="lib/AdminLTE/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- DataTable initialization -->
|
||||
<script>
|
||||
|
||||
// show spinning icon
|
||||
showSpinner()
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
$('#networkTable').DataTable({
|
||||
"searching": true,
|
||||
"order": [[0, "desc"]]
|
||||
});
|
||||
|
||||
// hide spinning icon
|
||||
hideSpinner()
|
||||
|
||||
}, 500);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
require 'php/templates/notification.php';
|
||||
?>
|
||||
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
<!-- Page ------------------------------------------------------------------ -->
|
||||
<div class="content-wrapper">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 🛑 Important: This is only used for the bare-metal install 🛑
|
||||
# Update /dockerfiles/start.sh in most cases is preferred
|
||||
# Update /install/start.debian.sh in most cases is preferred
|
||||
|
||||
echo "---------------------------------------------------------"
|
||||
echo "[INSTALL] Run install.sh"
|
||||
echo "[INSTALL] Run install.debian.sh"
|
||||
echo "---------------------------------------------------------"
|
||||
|
||||
# Set environment variables
|
||||
@@ -35,4 +35,4 @@ if [ ! -f $INSTALL_DIR/pialert/front/buildtimestamp.txt ]; then
|
||||
fi
|
||||
|
||||
# Start PiAlert
|
||||
"$INSTALL_DIR/pialert/dockerfiles/start.sh"
|
||||
"$INSTALL_DIR/pialert/install/start.debian.sh"
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "---------------------------------------------------------"
|
||||
echo "[INSTALL] Run install_dependencies.sh"
|
||||
echo "[INSTALL] Run install_dependencies.debian.sh"
|
||||
echo "---------------------------------------------------------"
|
||||
|
||||
# ❗ IMPORTANT - if you modify this file modify the root Dockerfile as well ❗
|
||||
@@ -7,6 +7,8 @@ server {
|
||||
proxy_set_header X-Forwarded-Prefix "/pialert";
|
||||
|
||||
location ~* \.php$ {
|
||||
# Set Cache-Control header to prevent caching on the first load
|
||||
add_header Cache-Control "no-store";
|
||||
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
19
install/pialert.template.conf
Executable file
19
install/pialert.template.conf
Executable file
@@ -0,0 +1,19 @@
|
||||
server {
|
||||
listen ${LISTEN_ADDR}:${PORT} default_server;
|
||||
root ${INSTALL_DIR}/pialert/front;
|
||||
index index.php;
|
||||
add_header X-Forwarded-Prefix "/pialert" always;
|
||||
proxy_set_header X-Forwarded-Prefix "/pialert";
|
||||
|
||||
location ~* \.php$ {
|
||||
# Set Cache-Control header to prevent caching on the first load
|
||||
add_header Cache-Control "no-store";
|
||||
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||
fastcgi_connect_timeout 75;
|
||||
fastcgi_send_timeout 600;
|
||||
fastcgi_read_timeout 600;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "---------------------------------------------------------"
|
||||
echo "[INSTALL] Run start.sh"
|
||||
echo "[INSTALL] Run start.debian.sh"
|
||||
echo "---------------------------------------------------------"
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ fi
|
||||
# Run setup scripts
|
||||
echo "[INSTALL] Run setup scripts"
|
||||
|
||||
"$INSTALL_DIR/pialert/dockerfiles/user-mapping.sh"
|
||||
"$INSTALL_DIR/pialert/install/install_dependencies.sh" # if modifying this file transfer the chanegs into the root Dockerfile as well!
|
||||
"$INSTALL_DIR/pialert/install/user-mapping.debian.sh"
|
||||
"$INSTALL_DIR/pialert/install/install_dependencies.debian.sh" # if modifying this file transfer the changes into the root Dockerfile.debian as well!
|
||||
|
||||
echo "[INSTALL] Setup NGINX"
|
||||
|
||||
@@ -61,7 +61,7 @@ fi
|
||||
# create symbolic link to the pialert install directory
|
||||
ln -s $INSTALL_DIR/pialert/front $WEB_UI_DIR
|
||||
# create symbolic link to NGINX configuaration coming with PiAlert
|
||||
sudo ln -s "$INSTALL_DIR/pialert/install/pialert.conf" /etc/nginx/conf.d/pialert.conf
|
||||
sudo ln -s "$INSTALL_DIR/pialert/install/pialert.debian.conf" /etc/nginx/conf.d/pialert.conf
|
||||
|
||||
# Use user-supplied port if set
|
||||
if [ -n "${PORT}" ]; then
|
||||
@@ -114,6 +114,17 @@ chmod -R a+rwx $INSTALL_DIR
|
||||
|
||||
echo "[INSTALL] Copy starter pialert.db and pialert.conf if they don't exist"
|
||||
|
||||
# DANGER ZONE: ALWAYS_FRESH_INSTALL
|
||||
if [ "$ALWAYS_FRESH_INSTALL" = true ]; then
|
||||
echo "[INSTALL] ❗ ALERT /db and /config folders are cleared because the ALWAYS_FRESH_INSTALL is set to: $ALWAYS_FRESH_INSTALL❗"
|
||||
# Delete content of "$INSTALL_DIR/pialert/config/"
|
||||
rm -rf "$INSTALL_DIR/pialert/config/"*
|
||||
|
||||
# Delete content of "$INSTALL_DIR/pialert/db/"
|
||||
rm -rf "$INSTALL_DIR/pialert/db/"*
|
||||
fi
|
||||
|
||||
|
||||
# Copy starter pialert.db and pialert.conf if they don't exist
|
||||
cp -n "$INSTALL_DIR/pialert/back/pialert.conf" "$INSTALL_DIR/pialert/config/pialert.conf"
|
||||
cp -n "$INSTALL_DIR/pialert/back/pialert.db" "$FILEDB"
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "---------------------------------------------------------"
|
||||
echo "[INSTALL] Run user-mapping.sh"
|
||||
echo "[INSTALL] Run user-mapping.debian.sh"
|
||||
echo "---------------------------------------------------------"
|
||||
|
||||
if [ -z "${USER}" ]; then
|
||||
@@ -105,7 +105,7 @@ def importConfigs (db):
|
||||
conf.PIALERT_WEB_PROTECTION = ccd('PIALERT_WEB_PROTECTION', False , c_d, 'Enable logon', 'boolean', '', 'General')
|
||||
conf.PIALERT_WEB_PASSWORD = ccd('PIALERT_WEB_PASSWORD', '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92' , c_d, 'Logon password', 'readonly', '', 'General')
|
||||
conf.REPORT_DASHBOARD_URL = ccd('REPORT_DASHBOARD_URL', 'http://pi.alert/' , c_d, 'PiAlert URL', 'text', '', 'General')
|
||||
conf.UI_LANG = ccd('UI_LANG', 'English' , c_d, 'Language Interface', 'text.select', "['English', 'German', 'Spanish']", 'General')
|
||||
conf.UI_LANG = ccd('UI_LANG', 'English' , c_d, 'Language Interface', 'text.select', "['English', 'French', 'German', 'Norwegian', 'Russian', 'Spanish' ]", 'General')
|
||||
conf.UI_PRESENCE = ccd('UI_PRESENCE', ['online', 'offline', 'archived'] , c_d, 'Include in presence', 'text.multiselect', "['online', 'offline', 'archived']", 'General')
|
||||
conf.UI_MY_DEVICES = ccd('UI_MY_DEVICES', ['online', 'offline', 'archived', 'new', 'down'] , c_d, 'Include in My Devices', 'text.multiselect', "['online', 'offline', 'archived', 'new', 'down']", 'General')
|
||||
conf.UI_NOT_RANDOM_MAC = ccd('UI_NOT_RANDOM_MAC', [] , c_d, 'Exlude from Random Prefix', 'list', "", 'General')
|
||||
|
||||
@@ -215,7 +215,7 @@ def execute_plugin(db, plugin, pluginsState = plugins_state() ):
|
||||
mylog('none', [e.output])
|
||||
mylog('none', ['[Plugins] ⚠ ERROR - enable LOG_LEVEL=debug and check logs'])
|
||||
except subprocess.TimeoutExpired as timeErr:
|
||||
mylog('none', ['[Plugins] TIMEOUT - the process forcefully terminated as timeout reached'])
|
||||
mylog('none', [f'[Plugins] ⚠ ERROR - TIMEOUT - the plugin {plugin["unique_prefix"]} forcefully terminated as timeout reached. Increase TIMEOUT setting and scan interval.'])
|
||||
|
||||
|
||||
# check the last run output
|
||||
|
||||
Reference in New Issue
Block a user