Merge pull request #8 from jokob-sk/main

jokob-sk > development
This commit is contained in:
((void*)0)
2022-07-31 08:32:38 +00:00
committed by GitHub
3573 changed files with 251563 additions and 138782 deletions

10
.env Normal file
View File

@@ -0,0 +1,10 @@
#GLOBAL
APP_DATA_LOCATION=/path/to/docker_appdata
APP_CONFIG_LOCATION=/path/to/docker_config
LOGS_LOCATION=/path/to/docker_logs
TZ=Europe/Paris
HOST_USER_ID=1000
HOST_USER_GID=1000
PORT=20211

64
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
---
name: docker
on:
schedule:
- cron: 0 14 * * 0 # every sunday at 14:00
push:
branches:
- '**'
tags:
- '*.*.*'
pull_request:
branches:
- master
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set up dynamic build ARGs
id: getargs
run: echo "::set-output name=version::$(cat ./stable/VERSION)"
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
# list of Docker images to use as base name for tags
images: |
jokobsk/pi.alert
# generate Docker tags based on the following events/attributes
tags: |
type=raw,value=latest
type=schedule
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.DS_Store
config/pialert.conf
db/

39
Dockerfile Normal file
View File

@@ -0,0 +1,39 @@
FROM debian:buster-slim
# default UID and GID
ENV USER=pi USER_ID=1000 USER_GID=1000 TZ=Europe/London PORT=20211
# 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 \
&& apt-get install --no-install-recommends ca-certificates curl libwww-perl arp-scan perl apt-utils cron sudo lighttpd php php-cgi php-fpm php-sqlite3 sqlite3 dnsutils net-tools python iproute2 nmap python-pip zip -y \
&& pip install requests \
&& apt-get clean autoclean \
&& apt-get autoremove \
&& rm -rf /var/lib/apt/lists/* \
&& ln -s /home/pi/pialert/install/index.html /var/www/html/index.html \
&& ln -s /home/pi/pialert/front /var/www/html/pialert \
&& lighttpd-enable-mod fastcgi-php
# now creating user
RUN groupadd --gid "${USER_GID}" "${USER}" && \
useradd \
--uid ${USER_ID} \
--gid ${USER_GID} \
--create-home \
--shell /bin/bash \
${USER}
COPY . /home/pi/pialert
# Pi.Alert
RUN python /home/pi/pialert/back/pialert.py update_vendors \
&& sed -ie 's/= 80/= '${PORT}'/g' /etc/lighttpd/lighttpd.conf \
&& (crontab -l 2>/dev/null; cat /home/pi/pialert/install/pialert.cron) | crontab -
# it's easy for permissions set in Git to be overridden, so doing it manually
RUN chmod -R a+rxw /home/pi/pialert/
CMD ["/home/pi/pialert/dockerfiles/start.sh"]

View File

@@ -4,13 +4,27 @@
WIFI / LAN intruder detector.
Scan the devices connected to your WIFI / LAN and alert you the connection of
unknown devices. It also warns the disconnection of "always connected" devices.
unknown devices. It also warns if a "always connected" devices disconnects.
![Main screen][main]
*(Apologies for my English and my limited knowledge of Python, php and
JavaScript)*
# Docker image 🐳
[![Docker](https://github.com/jokob-sk/Pi.Alert/actions/workflows/docker.yml/badge.svg)](https://github.com/jokob-sk/Pi.Alert/actions/workflows/docker.yml)
[![Docker Image Size](https://img.shields.io/docker/image-size/jokobsk/pi.alert?logo=Docker)](https://hub.docker.com/r/jokobsk/pi.alert)
<a href="https://hub.docker.com/r/jokobsk/pi.alert">
<img src="https://img.shields.io/docker/pulls/jokobsk/pi.alert?logo=docker&color=0aa8d2&logoColor=fff" alt="Docker Pulls">
</a>
🥇 Pi.Alert credit goes to [pucherot/Pi.Alert](https://github.com/pucherot/Pi.Alert). <br/>
🐳 Docker Image: [jokobsk/Pi.Alert](https://registry.hub.docker.com/r/jokobsk/pi.alert). <br/>
📄 [Dockerfile](https://github.com/jokob-sk/Pi.Alert/blob/main/Dockerfile) <br/>
📚 [Dockerfile instructions](https://github.com/jokob-sk/Pi.Alert/blob/main//dockerfiles/README.md).
![Main screen dark][main_dark]
Dark mode (and Device presence over time) within this fork courtesy of [leiweibau](https://github.com/leiweibau/Pi.Alert)
## How it works
The system continuously scans the network for:
- New devices
@@ -41,13 +55,18 @@ In charge of:
- Scan the network searching connected devices using the scanning methods
described
- Store the information in the DB
- Report the changes detected by e-mail
- Report the changes detected by e-mail and/or other services (Pushsafer, NTFY, Gotify)
- Optional speedtest for Device "Internet"
| ![Report 1][report1] | ![Report 2][report2] |
| -------------------- | -------------------- |
### Front
A web frontal that allows:
There is a configurable login to prevent unauthorized use.
> * Set `PIALERT_WEB_PROTECTION = True` in `pialert.conf` to enable
A web frontend that allows:
- Manage the devices inventory and the characteristics
- Display in a visual way all the information collected by the back
- Sessions
@@ -58,12 +77,25 @@ A web frontal that allows:
- Concurrent devices
- Down alerts
- IP's
- Manual Nmap scans
- Optional speedtest for Device "Internet"
- ...
| ![Screen 1][screen1] | ![Screen 2][screen2] |
| -------------------- | -------------------- |
| ![Screen 3][screen3] | ![Screen 4][screen4] |
| ![Screen 5][screen5] | ![Screen 6][screen6] |
With the work of [jokob-sk/Pi.Alert](https://github.com/jokob-sk/Pi.Alert) and own extensions, the new maintenance page was added with various possibilities for maintenance and settings:
- Status Infos (active scans, database size, backup counter)
- Theme Selection (blue, red, green, yellow, black, purple)
- Language Selection (english, german, spanish)
- Light/Dark-Mode Switch
- Pause arp-scan
- DB maintenance tools
- DB Backup and Restore
![Maintain screen dark][maintain_dark]
# Installation
<!--- --------------------------------------------------------------------- --->
@@ -71,7 +103,10 @@ Initially designed to run on a Raspberry Pi, probably it can run on many other
Linux distributions.
- One-step Automated Install:
#### `curl -sSL https://github.com/pucherot/Pi.Alert/raw/main/install/pialert_install.sh | bash`
#### `curl -sSL https://github.com/leiweibau/Pi.Alert/raw/main/install/pialert_install.sh | bash`
- One-step Automated Install without Webserver if another Webserver is already installed. (not recommended):
#### `curl -sSL https://github.com/leiweibau/Pi.Alert/raw/main/install/pialert_install_no_webserver.sh | bash`
- [Installation Guide (step by step)](docs/INSTALL.md)
@@ -79,7 +114,7 @@ Linux distributions.
# Update
<!--- --------------------------------------------------------------------- --->
- One-step Automated Update:
#### `curl -sSL https://github.com/pucherot/Pi.Alert/raw/main/install/pialert_update.sh | bash`
#### `curl -sSL https://github.com/leiweibau/Pi.Alert/raw/main/install/pialert_update.sh | bash`
# Uninstall process
<!--- --------------------------------------------------------------------- --->
@@ -97,29 +132,48 @@ Linux distributions.
### [Versions History](docs/VERSIONS_HISTORY.md)
### Powered by:
| Product | Objetive |
| ------------ | -------------------------------------- |
| Python | Programming language for the Back |
| PHP | Programming language for the Front-end |
| JavaScript | Programming language for the Front-end |
| Bootstrap | Front-end framework |
| Admin.LTE | Bootstrap template |
| FullCalendar | Calendar component |
| Sqlite | DB engine |
| Lighttpd | Webserver |
| arp-scan | Scan network using arp commands |
| Pi.hole | DNS Server with Ad-block |
| dnsmasq | DHCP Server |
| Product | Objetive |
| ------------- | ------------------------------------------------------- |
| Python | Programming language for the Back |
| PHP | Programming language for the Front-end |
| JavaScript | Programming language for the Front-end |
| Bootstrap | Front-end framework |
| Admin.LTE | Bootstrap template |
| FullCalendar | Calendar component |
| Sqlite | DB engine |
| Lighttpd | Webserver |
| arp-scan | Scan network using arp commands |
| Pi.hole | DNS Server with Ad-block |
| dnsmasq | DHCP Server |
| nmap | Network Scanner |
| zip | Filecompression Tool |
| speedtest-cli | Python SpeedTest https://github.com/sivel/speedtest-cli |
### 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
### Contact
pi.alert.application@gmail.com
***Suggestions and comments are welcome***
### Special thanks 🥇
This code is a collaborative body of work, with special thanks to:
- [leiweibau](https://github.com/leiweibau/Pi.Alert): Dark mode (and Last X scans activity chart)
- [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 translation
- [jokob-sk](https://github.com/jokob-sk/Pi.Alert): DB Maintenance tools
- Please see the [Git commit history](https://github.com/jokob-sk/Pi.Alert/commits/main) for a full list of people and their contributions to the project
<!--- --------------------------------------------------------------------- --->
[main]: ./docs/img/1_devices.jpg "Main screen"
@@ -127,6 +181,10 @@ Linux distributions.
[screen2]: ./docs/img/2_2_device_sessions.jpg "Screen 2"
[screen3]: ./docs/img/2_3_device_presence.jpg "Screen 3"
[screen4]: ./docs/img/3_presence.jpg "Screen 4"
[screen5]: ./docs/img/2_4_device_nmap.jpg "Screen 5"
[screen6]: ./docs/img/2_5_device_nmap_ready.jpg "Screen 6"
[report1]: ./docs/img/4_report_1.jpg "Report sample 1"
[report2]: ./docs/img/4_report_2.jpg "Report sample 2"
[main_dark]: /docs/img/1_devices_dark.jpg "Main screen dark"
[maintain_dark]: /docs/img/5_maintain.jpg "Maintain screen dark"

View File

@@ -27,13 +27,14 @@ import socket
import io
import smtplib
import csv
import requests
#===============================================================================
# CONFIG CONSTANTS
#===============================================================================
PIALERT_BACK_PATH = os.path.dirname(os.path.abspath(__file__))
PIALERT_PATH = PIALERT_BACK_PATH + "/.."
STOPARPSCAN = PIALERT_PATH + "/db/setting_stoparpscan"
if (sys.version_info > (3,0)):
exec(open(PIALERT_PATH + "/config/version.conf").read())
@@ -74,6 +75,9 @@ def main ():
return
cycle = str(sys.argv[1])
## Upgrade DB if needed
upgradeDB()
## Main Commands
if cycle == 'internet_IP':
res = check_internet_IP()
@@ -81,8 +85,10 @@ def main ():
res = update_devices_MAC_vendors()
elif cycle == 'update_vendors_silent':
res = update_devices_MAC_vendors('-s')
else :
elif os.path.exists(STOPARPSCAN) == False :
res = scan_network()
elif os.path.exists(STOPARPSCAN) == True :
res = 0
# Check error
if res != 0 :
@@ -350,15 +356,12 @@ def scan_network ():
# ScanCycle data
cycle_interval = scanCycle_data['cic_EveryXmin']
arpscan_retries = scanCycle_data['cic_arpscanCycles']
# TESTING - Fast scan
# arpscan_retries = 1
# arp-scan command
print ('\nScanning...')
print (' arp-scan Method...')
print_log ('arp-scan starts...')
arpscan_devices = execute_arpscan (arpscan_retries)
arpscan_devices = execute_arpscan ()
print_log ('arp-scan ends')
# DEBUG - print number of rows updated
# print (arpscan_devices)
@@ -444,20 +447,12 @@ def query_ScanCycle_Data (pOpenCloseDB = False):
return sqlRow
#-------------------------------------------------------------------------------
def execute_arpscan (pRetries):
def execute_arpscan ():
# #101 - arp-scan subnet configuration
# Prepare command arguments
subnets = SCAN_SUBNETS.strip().split()
arpscan_args = ['sudo', 'arp-scan', '--ignoredups', '--retry=' + str(pRetries)] + subnets
# arpscan_args = ['sudo', 'arp-scan', SCAN_SUBNETS, '--ignoredups', '--retry=' + str(pRetries)]
# print (arpscan_args)
# TESTING - Fast Scan
# arpscan_args = ['sudo', 'arp-scan', '--localnet', '--ignoredups', '--retry=1']
# DEBUG - arp-scan command
# print (" ".join (arpscan_args))
# Retry is 6 to avoid false offline devices
arpscan_args = ['sudo', 'arp-scan', '--ignoredups', '--retry=6'] + subnets
# Execute command
arpscan_output = subprocess.check_output (arpscan_args, universal_newlines=True)
@@ -472,9 +467,6 @@ def execute_arpscan (pRetries):
devices_list = [device.groupdict()
for device in re.finditer (re_pattern, arpscan_output)]
# Bugfix #5 - Delete duplicated MAC's with different IP's
# TEST - Force duplicated device
# devices_list.append(devices_list[0])
# Delete duplicate MAC
unique_mac = []
unique_devices = []
@@ -688,6 +680,23 @@ def print_scan_stats ():
(cycle,))
print (' IP Changes.........: ' + str ( sql.fetchone()[0]) )
# Add to History
sql.execute("SELECT * FROM Devices")
History_All = sql.fetchall()
History_All_Devices = len(History_All)
sql.execute("SELECT * FROM Devices WHERE dev_Archived = 1")
History_Archived = sql.fetchall()
History_Archived_Devices = len(History_Archived)
sql.execute("""SELECT * FROM CurrentScan WHERE cur_ScanCycle = ? """, (cycle,))
History_Online = sql.fetchall()
History_Online_Devices = len(History_Online)
History_Offline_Devices = History_All_Devices - History_Archived_Devices - History_Online_Devices
sql.execute ("INSERT INTO Online_History (Scan_Date, Online_Devices, Down_Devices, All_Devices, Archived_Devices) "+
"VALUES ( ?, ?, ?, ?, ?)", (startTime, History_Online_Devices, History_Offline_Devices, History_All_Devices, History_Archived_Devices ) )
#-------------------------------------------------------------------------------
def create_new_devices ():
# arpscan - Insert events for new devices
@@ -702,6 +711,16 @@ def create_new_devices ():
WHERE dev_MAC = cur_MAC) """,
(startTime, cycle) )
print_log ('New devices - Insert Connection into session table')
sql.execute ("""INSERT INTO Sessions (ses_MAC, ses_IP, ses_EventTypeConnection, ses_DateTimeConnection,
ses_EventTypeDisconnection, ses_DateTimeDisconnection, ses_StillConnected, ses_AdditionalInfo)
SELECT cur_MAC, cur_IP,'Connected',?, NULL , NULL ,1, cur_Vendor
FROM CurrentScan
WHERE cur_ScanCycle = ?
AND NOT EXISTS (SELECT 1 FROM Sessions
WHERE ses_MAC = cur_MAC) """,
(startTime, cycle) )
# arpscan - Create new devices
print_log ('New devices - 2 Create devices')
sql.execute ("""INSERT INTO Devices (dev_MAC, dev_name, dev_Vendor,
@@ -929,17 +948,10 @@ def update_devices_data_from_scan ():
sql.executemany ("UPDATE Devices SET dev_Vendor = ? WHERE dev_MAC = ? ",
recordsToUpdate )
# New Apple devices -> Cycle 15
print_log ('Update devices - 6 Cycle for Apple devices')
sql.execute ("""UPDATE Devices SET dev_ScanCycle = 15
WHERE dev_FirstConnection = ?
AND UPPER(dev_Vendor) LIKE '%APPLE%' """,
(startTime,) )
print_log ('Update devices end')
#-------------------------------------------------------------------------------
# Feature #43 - Resoltion name for unknown devices
# Feature #43 - Resolve name for unknown devices
def update_devices_names ():
# Initialize variables
recordsToUpdate = []
@@ -1162,11 +1174,22 @@ def skip_repeated_notifications ():
def email_reporting ():
global mail_text
global mail_html
# Reporting section
print ('\nReporting...')
openDB()
# Disable reporting on events for devices where reporting is disabled based on the MAC address
sql.execute ("""UPDATE Events SET eve_PendingAlertEmail = 0
WHERE eve_PendingAlertEmail = 1 AND eve_EventType != 'Device Down' AND eve_MAC IN
(
SELECT dev_MAC FROM Devices WHERE dev_AlertEvents = 0
)""")
sql.execute ("""UPDATE Events SET eve_PendingAlertEmail = 0
WHERE eve_PendingAlertEmail = 1 AND eve_EventType = 'Device Down' AND eve_MAC IN
(
SELECT dev_MAC FROM Devices WHERE dev_AlertDeviceDown = 0
)""")
# Open text Template
template_file = open(PIALERT_BACK_PATH + '/report_template.txt', 'r')
mail_text = template_file.read()
@@ -1182,27 +1205,27 @@ def email_reporting ():
mail_text = mail_text.replace ('<REPORT_DATE>', timeFormated)
mail_html = mail_html.replace ('<REPORT_DATE>', timeFormated)
mail_text = mail_text.replace ('<SCAN_CYCLE>', cycle )
# mail_text = mail_text.replace ('<SCAN_CYCLE>', cycle )
mail_html = mail_html.replace ('<SCAN_CYCLE>', cycle )
mail_text = mail_text.replace ('<SERVER_NAME>', socket.gethostname() )
mail_html = mail_html.replace ('<SERVER_NAME>', socket.gethostname() )
mail_text = mail_text.replace ('<PIALERT_VERSION>', VERSION )
mail_html = mail_html.replace ('<PIALERT_VERSION>', VERSION )
# mail_text = mail_text.replace ('<PIALERT_VERSION>', VERSION )
# mail_html = mail_html.replace ('<PIALERT_VERSION>', VERSION )
mail_text = mail_text.replace ('<PIALERT_VERSION_DATE>', VERSION_DATE )
mail_html = mail_html.replace ('<PIALERT_VERSION_DATE>', VERSION_DATE )
# mail_text = mail_text.replace ('<PIALERT_VERSION_DATE>', VERSION_DATE )
# mail_html = mail_html.replace ('<PIALERT_VERSION_DATE>', VERSION_DATE )
mail_text = mail_text.replace ('<PIALERT_YEAR>', VERSION_YEAR )
mail_html = mail_html.replace ('<PIALERT_YEAR>', VERSION_YEAR )
# mail_text = mail_text.replace ('<PIALERT_YEAR>', VERSION_YEAR )
# mail_html = mail_html.replace ('<PIALERT_YEAR>', VERSION_YEAR )
# Compose Internet Section
print (' Formating report...')
mail_section_Internet = False
mail_text_Internet = ''
mail_html_Internet = ''
text_line_template = ' {} \t{}\t{}\t{}\n'
text_line_template = '{} \t{}\t{}\t{}\n'
html_line_template = '<tr>\n'+ \
' <td> <a href="{}{}"> {} </a> </td>\n <td> {} </td>\n'+ \
' <td style="font-size: 24px; color:#D02020"> {} </td>\n'+ \
@@ -1212,16 +1235,18 @@ def email_reporting ():
WHERE eve_PendingAlertEmail = 1 AND eve_MAC = 'Internet'
ORDER BY eve_DateTime""")
for eventAlert in sql :
mail_section_Internet = True
mail_text_Internet += text_line_template.format (
eventAlert['eve_EventType'], eventAlert['eve_DateTime'],
eventAlert['eve_IP'], eventAlert['eve_AdditionalInfo'])
'Event:', eventAlert['eve_EventType'], 'Time:', eventAlert['eve_DateTime'],
'IP:', eventAlert['eve_IP'], 'More Info:', eventAlert['eve_AdditionalInfo'])
mail_html_Internet += html_line_template.format (
REPORT_DEVICE_URL, eventAlert['eve_MAC'],
eventAlert['eve_EventType'], eventAlert['eve_DateTime'],
eventAlert['eve_IP'], eventAlert['eve_AdditionalInfo'])
format_report_section (mail_section_Internet, 'SECTION_INTERNET',
'TABLE_INTERNET', mail_text_Internet, mail_html_Internet)
@@ -1229,7 +1254,7 @@ def email_reporting ():
mail_section_new_devices = False
mail_text_new_devices = ''
mail_html_new_devices = ''
text_line_template = ' {}\t{}\t{}\t{}\t{}\n'
text_line_template = '{}\t{}\n\t{}\t{}\n\t{}\t{}\n\t{}\t{}\n\t{}\t{}\n\n'
html_line_template = '<tr>\n'+ \
' <td> <a href="{}{}"> {} </a> </td>\n <td> {} </td>\n'+\
' <td> {} </td>\n <td> {} </td>\n <td> {} </td>\n</tr>\n'
@@ -1242,9 +1267,8 @@ def email_reporting ():
for eventAlert in sql :
mail_section_new_devices = True
mail_text_new_devices += text_line_template.format (
eventAlert['eve_MAC'], eventAlert['eve_DateTime'],
eventAlert['eve_IP'], eventAlert['dev_Name'],
eventAlert['eve_AdditionalInfo'])
'Name: ', eventAlert['dev_Name'], 'MAC: ', eventAlert['eve_MAC'], 'IP: ', eventAlert['eve_IP'],
'Time: ', eventAlert['eve_DateTime'], 'More Info: ', eventAlert['eve_AdditionalInfo'])
mail_html_new_devices += html_line_template.format (
REPORT_DEVICE_URL, eventAlert['eve_MAC'], eventAlert['eve_MAC'],
eventAlert['eve_DateTime'], eventAlert['eve_IP'],
@@ -1257,7 +1281,7 @@ def email_reporting ():
mail_section_devices_down = False
mail_text_devices_down = ''
mail_html_devices_down = ''
text_line_template = ' {}\t{}\t{}\t{}\n'
text_line_template = '{}\t{}\n\t{}\t{}\n\t{}\t{}\n\t{}\t{}\n\n'
html_line_template = '<tr>\n'+ \
' <td> <a href="{}{}"> {} </a> </td>\n <td> {} </td>\n'+ \
' <td> {} </td>\n <td> {} </td>\n</tr>\n'
@@ -1270,8 +1294,8 @@ def email_reporting ():
for eventAlert in sql :
mail_section_devices_down = True
mail_text_devices_down += text_line_template.format (
eventAlert['eve_MAC'], eventAlert['eve_DateTime'],
eventAlert['eve_IP'], eventAlert['dev_Name'])
'Name: ', eventAlert['dev_Name'], 'MAC: ', eventAlert['eve_MAC'],
'Time: ', eventAlert['eve_DateTime'],'IP: ', eventAlert['eve_IP'])
mail_html_devices_down += html_line_template.format (
REPORT_DEVICE_URL, eventAlert['eve_MAC'], eventAlert['eve_MAC'],
eventAlert['eve_DateTime'], eventAlert['eve_IP'],
@@ -1284,7 +1308,7 @@ def email_reporting ():
mail_section_events = False
mail_text_events = ''
mail_html_events = ''
text_line_template = ' {}\t{}\t{}\t{}\t{}\t{}\n'
text_line_template = '{}\t{}\n\t{}\t{}\n\t{}\t{}\n\t{}\t{}\n\t{}\t{}\n\t{}\t{}\n\n'
html_line_template = '<tr>\n <td>'+ \
' <a href="{}{}"> {} </a> </td>\n <td> {} </td>\n'+ \
' <td> {} </td>\n <td> {} </td>\n <td> {} </td>\n'+ \
@@ -1299,9 +1323,9 @@ def email_reporting ():
for eventAlert in sql :
mail_section_events = True
mail_text_events += text_line_template.format (
eventAlert['eve_MAC'], eventAlert['eve_DateTime'],
eventAlert['eve_IP'], eventAlert['eve_EventType'],
eventAlert['dev_Name'], eventAlert['eve_AdditionalInfo'])
'Name: ', eventAlert['dev_Name'], 'MAC: ', eventAlert['eve_MAC'],
'IP: ', eventAlert['eve_IP'],'Time: ', eventAlert['eve_DateTime'],
'Event: ', eventAlert['eve_EventType'],'More Info: ', eventAlert['eve_AdditionalInfo'])
mail_html_events += html_line_template.format (
REPORT_DEVICE_URL, eventAlert['eve_MAC'], eventAlert['eve_MAC'],
eventAlert['eve_DateTime'], eventAlert['eve_IP'],
@@ -1324,10 +1348,21 @@ def email_reporting ():
send_email (mail_text, mail_html)
else :
print (' Skip mail...')
if REPORT_NTFY :
print (' Sending report by NTFY...')
send_ntfy (mail_text)
else :
print (' Skip NTFY...')
if REPORT_PUSHSAFER :
print (' Sending report by PUSHSAFER...')
send_pushsafer (mail_text)
else :
print (' Skip PUSHSAFER...')
else :
print (' No changes to report...')
# Clean Pending Alert Events
sql.execute ("""UPDATE Devices SET dev_LastNotification = ?
WHERE dev_MAC IN (SELECT eve_MAC FROM Events
@@ -1342,6 +1377,32 @@ def email_reporting ():
# Commit changes
sql_connection.commit()
closeDB()
#-------------------------------------------------------------------------------
def send_ntfy (_Text):
requests.post("https://ntfy.sh/{}".format(NTFY_TOPIC),
data=_Text,
headers={
"Title": "Pi.Alert Notification",
"Actions": "view, Open Dashboard, "+ REPORT_DASHBOARD_URL,
"Priority": "urgent",
"Tags": "warning"
})
def send_pushsafer (_Text):
url = 'https://www.pushsafer.com/api'
post_fields = {
"t" : 'Pi.Alert Message',
"m" : _Text,
"s" : 11,
"v" : 3,
"i" : 148,
"c" : '#ef7f7f',
"d" : 'a',
"u" : REPORT_DASHBOARD_URL,
"ut" : 'Open Pi.Alert',
"k" : PUSHSAFER_TOKEN,
}
requests.post(url, data=post_fields)
#-------------------------------------------------------------------------------
def format_report_section (pActive, pSection, pTable, pText, pHTML):
@@ -1413,16 +1474,66 @@ def send_email (pText, pHTML):
# Send mail
smtp_connection = smtplib.SMTP (SMTP_SERVER, SMTP_PORT)
smtp_connection.ehlo()
smtp_connection.starttls()
smtp_connection.ehlo()
smtp_connection.login (SMTP_USER, SMTP_PASS)
# smtp_connection.starttls()
# smtp_connection.ehlo()
# smtp_connection.login (SMTP_USER, SMTP_PASS)
if not SafeParseGlobalBool("SMTP_SKIP_TLS"):
smtp_connection.starttls()
smtp_connection.ehlo()
if not SafeParseGlobalBool("SMTP_SKIP_LOGIN"):
smtp_connection.login (SMTP_USER, SMTP_PASS)
smtp_connection.sendmail (REPORT_FROM, REPORT_TO, msg.as_string())
smtp_connection.quit()
#-------------------------------------------------------------------------------
def SafeParseGlobalBool(boolVariable):
if boolVariable in globals():
return eval(boolVariable)
return False
#===============================================================================
# DB
#===============================================================================
def upgradeDB ():
openDB()
# indicates, if Online_History table is available
onlineHistoryAvailable = sql.execute("""
SELECT name FROM sqlite_master WHERE type='table'
AND name='Online_History';
""").fetchall() != []
# Check if it is incompatible (Check if table has all required columns)
isIncompatible = False
if onlineHistoryAvailable :
isIncompatible = sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Online_History') WHERE name='Archived_Devices'
""").fetchone()[0] == 0
# Drop table if available, but incompatible
if onlineHistoryAvailable and isIncompatible:
print_log ('Table is incompatible, Dropping the Online_History table)')
sql.execute("DROP TABLE Online_History;")
onlineHistoryAvailable = False
if onlineHistoryAvailable == False :
sql.execute("""
CREATE TABLE "Online_History" (
"Index" INTEGER,
"Scan_Date" TEXT,
"Online_Devices" INTEGER,
"Down_Devices" INTEGER,
"All_Devices" INTEGER,
"Archived_Devices" INTEGER,
PRIMARY KEY("Index" AUTOINCREMENT)
);
""")
#-------------------------------------------------------------------------------
def openDB ():
global sql_connection
global sql

View File

@@ -14,20 +14,19 @@
<body>
<font face=sans-serif>
<table align=center width=80% border=1 bordercolor=#909090 cellpadding=0 cellspacing=0 style="border-collapse: collapse; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.5)">
<table align=center width=100% cellpadding=0 cellspacing=0 style="border-radius: 5px;">
<tr>
<td bgcolor=#EFB956 align=center style="padding: 20px 10px 10px 10px; font-size: 36px; font-weight: bold; color:#7F6000; text-shadow: 4px 4px 6px #909090">
<td bgcolor=#EFB956 align=center style="padding: 20px 10px 10px 10px; font-size: 30px; font-weight: bold; color:#000000; border-top-right-radius: 5px; border-top-left-radius: 5px; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2)">
Pi.Alert Report
</td>
</tr>
<tr>
<td>
<table width=100% border=0 bgcolor=#FFD966 cellpadding=5px cellspacing=0 style="border-collapse: collapse; font-size: 16px; text-align:center; color:#5F5000">
<tr>
<td width=33%> Report Date: <b><REPORT_DATE></b> </td>
<td width=34%> Scan Cycle: <b><SCAN_CYCLE></b> </td>
<td width=33%> Server: <b><SERVER_NAME></b> </td>
<table width=100% border=0 bgcolor=#FFD966 cellpadding=5px cellspacing=0 style="border-collapse: collapse; font-size: 15px; text-align:center; color:#404040"> <tr>
<td width=50%> Report Date: <b><REPORT_DATE></b> </td>
<td width=50%> Scan Cycle: <b><SCAN_CYCLE></b> </td>
</tr>
</table>
</td>
@@ -36,9 +35,9 @@
<tr>
<td bgcolor=#F5F5F5 height=200 valign=top style="padding: 10px">
<SECTION_INTERNET>
<p style="font-size: 24px; font-weight: bold; color:#C04040; text-shadow: 2px 2px 4px #A0A0A0"> Internet: </p>
<p style="font-size: 24px; font-weight: bold; color:#C04040"> Internet: </p>
<table width=100% border=1 bordercolor=#C0C0C0 cellpadding=3px cellspacing=0 style="border-collapse: collapse; font-size: 12px; color:#707070; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2)">
<table width=100% border=1 bordercolor=#C0C0C0 cellpadding=3px cellspacing=0 style="border-collapse: collapse; font-size: 12px; color:#707070">
<tr bgcolor=#909090 style="color:#F0F0F0">
<th width=140> Event Type </th>
<th width=130> Datetime </th>
@@ -53,11 +52,11 @@
</SECTION_INTERNET>
<SECTION_NEW_DEVICES>
<p style="font-size: 14px; font-weight: bold; color:#C04040; text-shadow: 2px 2px 4px #A0A0A0"> New Devices: </p>
<p style="font-size: 14px; font-weight: bold; color:#C04040"> New Devices: </p>
<table width=100% border=1 bordercolor=#C0C0C0 cellpadding=3px cellspacing=0 style="border-collapse: collapse; font-size: 12px; color:#707070; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2)">
<table width=100% border=1 bordercolor=#C0C0C0 cellpadding=3px cellspacing=0 style="border-collapse: collapse; font-size: 12px; color:#707070">
<tr bgcolor=#909090 style="color:#F0F0F0">
<th width=140> MAC </th>
<th width=150> MAC </th>
<th width=130> Datetime </th>
<th width=100> IP </th>
<th width=140> Device Name </th>
@@ -71,9 +70,9 @@
</SECTION_NEW_DEVICES>
<SECTION_DEVICES_DOWN>
<p style="font-size: 14px; font-weight: bold; color:#C04040; text-shadow: 2px 2px 4px #A0A0A0"> Devices Down: </p>
<p style="font-size: 14px; font-weight: bold; color:#C04040"> Devices Down: </p>
<table width=100% border=1 bordercolor=#C0C0C0 cellpadding=3px cellspacing=0 style="border-collapse: collapse; font-size: 12px; color:#707070; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2)">
<table width=100% border=1 bordercolor=#C0C0C0 cellpadding=3px cellspacing=0 style="border-collapse: collapse; font-size: 12px; color:#707070">
<tr bgcolor=#909090 style="color:#F0F0F0">
<th width=140> MAC </th>
<th width=130> Datetime </th>
@@ -88,9 +87,9 @@
</SECTION_DEVICES_DOWN>
<SECTION_EVENTS>
<p style="font-size: 14px; font-weight: bold; color:#409040; text-shadow: 2px 2px 4px #A0A0A0"> Events: </p>
<p style="font-size: 14px; font-weight: bold; color:#409040"> Events: </p>
<table width=100% border=1 bordercolor=#C0C0C0 cellpadding=3px cellspacing=0 style="border-collapse: collapse; font-size: 12px; color:#707070; box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2)">
<table width=100% border=1 bordercolor=#C0C0C0 cellpadding=3px cellspacing=0 style="border-collapse: collapse; font-size: 12px; color:#707070">
<tr bgcolor=#909090 style="color:#F0F0F0">
<th width=140> MAC </th>
<th width=130> Datetime </th>
@@ -108,11 +107,10 @@
<tr>
<td>
<table width=100% border=0 bgcolor=#70AD47 cellpadding=5px cellspacing=0 style="border-collapse: collapse; font-size: 12px; font-weight: bold; color:#385723">
<table width=100% bgcolor=#46802e cellpadding=5px cellspacing=0 style="font-size: 13px; font-weight: bold; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px;">
<tr>
<td width=25% style="text-align:Left"> <span style="display:inline-block; transform: rotate(180deg)">&copy;</span> <PIALERT_YEAR> Puche</td>
<td width=50% style="text-align:center"> Pi.Alert - <SERVER_NAME></td>
<td width=50% style="text-align:center"> Pi.Alert <PIALERT_VERSION> &nbsp; &nbsp; (<PIALERT_VERSION_DATE>) </td>
<td width=25% style="text-align:right"> GNU GPLv3</td>
</tr>
</table>
</td>

View File

@@ -1,27 +1,19 @@
========================================
Pi.Alert Report
========================================
Report Date: <REPORT_DATE>
Scan Cycle: <SCAN_CYCLE>
Server: <SERVER_NAME>
<SECTION_INTERNET>
Internet
----------------------------------------------------------------------
<TABLE_INTERNET>
</SECTION_INTERNET><SECTION_NEW_DEVICES>
Report Date: <REPORT_DATE>
Server: <SERVER_NAME>
<SECTION_NEW_DEVICES>
New Devices
----------------------------------------------------------------------
----------------------
<TABLE_NEW_DEVICES>
</SECTION_NEW_DEVICES><SECTION_DEVICES_DOWN>
Devices Down
----------------------------------------------------------------------
----------------------
<TABLE_DEVICES_DOWN>
</SECTION_DEVICES_DOWN><SECTION_EVENTS>
Events
----------------------------------------------------------------------
----------------------
<TABLE_EVENTS>
</SECTION_EVENTS>
----------------------------------------------------------------------
(?) <PIALERT_YEAR> Puche Pi.Alert <PIALERT_VERSION> (<PIALERT_VERSION_DATE>) GNU GPLv3
</SECTION_EVENTS><SECTION_INTERNET>
Internet
----------------------
<TABLE_INTERNET>
</SECTION_INTERNET>

2013
back/speedtest-cli Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -24,17 +24,17 @@ sudo mkdir -p 2_backup
sudo cp *.txt 2_backup
sudo cp *.csv 2_backup
sudo curl $1 -# -O http://standards-oui.ieee.org/iab/iab.csv
sudo curl $1 -# -O http://standards-oui.ieee.org/iab/iab.txt
sudo curl $1 -# -O https://standards-oui.ieee.org/iab/iab.csv
sudo curl $1 -# -O https://standards-oui.ieee.org/iab/iab.txt
sudo curl $1 -# -O http://standards-oui.ieee.org/oui28/mam.csv
sudo curl $1 -# -O http://standards-oui.ieee.org/oui28/mam.txt
sudo curl $1 -# -O https://standards-oui.ieee.org/oui28/mam.csv
sudo curl $1 -# -O https://standards-oui.ieee.org/oui28/mam.txt
sudo curl $1 -# -O http://standards-oui.ieee.org/oui36/oui36.csv
sudo curl $1 -# -O http://standards-oui.ieee.org/oui36/oui36.txt
sudo curl $1 -# -O https://standards-oui.ieee.org/oui36/oui36.csv
sudo curl $1 -# -O https://standards-oui.ieee.org/oui36/oui36.txt
sudo curl $1 -# -O http://standards-oui.ieee.org/oui/oui.csv
sudo curl $1 -# -O http://standards-oui.ieee.org/oui/oui.txt
sudo curl $1 -# -O https://standards-oui.ieee.org/oui/oui.csv
sudo curl $1 -# -O https://standards-oui.ieee.org/oui/oui.txt
# ----------------------------------------------------------------------

View File

@@ -7,34 +7,51 @@
# Puche 2021 pi.alert.application@gmail.com GNU GPLv3
#-------------------------------------------------------------------------------
PIALERT_PATH = '/home/pi/pialert'
DB_PATH = PIALERT_PATH + '/db/pialert.db'
LOG_PATH = PIALERT_PATH + '/log'
VENDORS_DB = '/usr/share/arp-scan/ieee-oui.txt'
PRINT_LOG = False
PIALERT_PATH = '/home/pi/pialert'
DB_PATH = PIALERT_PATH + '/db/pialert.db'
LOG_PATH = PIALERT_PATH + '/log'
VENDORS_DB = '/usr/share/arp-scan/ieee-oui.txt'
PRINT_LOG = False
TIMEZONE = 'Europe/Berlin'
PIALERT_WEB_PROTECTION = False
PIALERT_WEB_PASSWORD = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
SMTP_USER = 'user@gmail.com'
SMTP_PASS = 'password'
# EMAIL settings
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
SMTP_USER = 'user@gmail.com'
SMTP_PASS = 'password'
SMTP_SKIP_TLS = False
SMTP_SKIP_LOGIN = False
REPORT_MAIL = False
REPORT_FROM = 'Pi.Alert <' + SMTP_USER +'>'
REPORT_TO = 'user@gmail.com'
REPORT_DEVICE_URL = 'http://pi.alert/deviceDetails.php?mac='
REPORT_MAIL = False
REPORT_FROM = 'Pi.Alert <' + SMTP_USER +'>'
REPORT_TO = 'user@gmail.com'
REPORT_DEVICE_URL = 'http://pi.alert/deviceDetails.php?mac='
REPORT_DASHBOARD_URL = 'http://pi.alert/'
# QUERY_MYIP_SERVER = 'https://diagnostic.opendns.com/myip'
QUERY_MYIP_SERVER = 'http://ipv4.icanhazip.com'
DDNS_ACTIVE = False
DDNS_DOMAIN = 'your_domain.freeddns.org'
DDNS_USER = 'dynu_user'
DDNS_PASSWORD = 'A0000000B0000000C0000000D0000000'
DDNS_UPDATE_URL = 'https://api.dynu.com/nic/update?'
# NTFY (https://ntfy.sh/) settings
REPORT_NTFY = False
NTFY_TOPIC = 'replace_my_secure_topicname_91h889f28'
REPORT_DASHBOARD_URL = 'http://pi.alert/'
PIHOLE_ACTIVE = False
PIHOLE_DB = '/etc/pihole/pihole-FTL.db'
DHCP_ACTIVE = False
DHCP_LEASES = '/etc/pihole/dhcp.leases'
# PUSHSAFER (https://www.pushsafer.com/) settings
REPORT_PUSHSAFER = False
PUSHSAFER_TOKEN = 'ApiKey'
# QUERY_MYIP_SERVER = 'https://diagnostic.opendns.com/myip'
QUERY_MYIP_SERVER = 'http://ipv4.icanhazip.com'
DDNS_ACTIVE = False
DDNS_DOMAIN = 'your_domain.freeddns.org'
DDNS_USER = 'dynu_user'
DDNS_PASSWORD = 'A0000000B0000000C0000000D0000000'
DDNS_UPDATE_URL = 'https://api.dynu.com/nic/update?'
# PIHOLE settings
PIHOLE_ACTIVE = False
PIHOLE_DB = '/etc/pihole/pihole-FTL.db'
DHCP_ACTIVE = False
DHCP_LEASES = '/etc/pihole/dhcp.leases'
# arp-scan options & samples
#
@@ -47,4 +64,4 @@ DHCP_LEASES = '/etc/pihole/dhcp.leases'
# Scan using interface eth0
# SCAN_SUBNETS = '--localnet --interface=eth0'
SCAN_SUBNETS = '--localnet'
SCAN_SUBNETS = '--localnet'

32
config/reset_password.sh Normal file
View File

@@ -0,0 +1,32 @@
#!/bin/sh
PIA_CONF_FILE='pialert.conf'
PIA_PASS=$1
echo "Check of key PIALERT_WEB_PROTECTION exists:"
CHECK_PROT=$(grep "PIALERT_WEB_PROTECTION" $PIA_CONF_FILE | wc -l)
if [ $CHECK_PROT -eq 0 ]
then
cp $PIA_CONF_FILE $PIA_CONF_FILE.bak1
echo " Key not found. Key 'PIALERT_WEB_PROTECTION' will be created."
echo " Check Config after the script is finished."
sed -i "/^VENDORS_DB.*/a PIALERT_WEB_PROTECTION = False" $PIA_CONF_FILE > pialert.tmp
else
echo " Key exists. Nothing to do."
fi
echo ""
echo "Check of key PIALERT_WEB_PASSWORD exists:"
CHECK_PWD=$(grep "PIALERT_WEB_PASSWORD" $PIA_CONF_FILE | wc -l)
if [ $CHECK_PWD -eq 0 ]
then
cp $PIA_CONF_FILE $PIA_CONF_FILE.bak2
echo " Key not found. Key 'PIALERT_WEB_PASSWORD' will be created."
echo " Check Config after the script is finished."
echo " If the key is just created, please run the script again to set a new password".
sed -i "/^PIALERT_WEB_PROTECTION.*/a PIALERT_WEB_PASSWORD = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'" $PIA_CONF_FILE
else
echo " The password '$1' is hashed"
PIA_PASS_HASH=$(echo -n $PIA_PASS | sha256sum | awk '{print $1}')
echo " The hashed password is:"
echo " $PIA_PASS_HASH"
sed -i "/PIALERT_WEB_PASSWORD/c\PIALERT_WEB_PASSWORD = '$PIA_PASS_HASH'" $PIA_CONF_FILE
echo " The hash was saved in the configuration file"
fi

View File

@@ -1,3 +1,3 @@
VERSION = '3.02'
VERSION_YEAR = '2021'
VERSION_DATE = '2021-04-24'
VERSION = '3.5_leiweibau'
VERSION_YEAR = '2022'
VERSION_DATE = '2022-07-07'

16
docker-compose.yml Normal file
View File

@@ -0,0 +1,16 @@
version: "3"
services:
pialert:
build: .
container_name: pialert
network_mode: "host"
restart: always
volumes:
- ${APP_DATA_LOCATION}/pialert/config:/home/pi/pialert/config
- ${APP_DATA_LOCATION}/pialert/db/pialert.db:/home/pi/pialert/db/pialert.db
- ${LOGS_LOCATION}/tmp:/home/pi/pialert/log
environment:
- TZ=${TZ}
- PORT=${PORT}
- HOST_USER_ID=${HOST_USER_ID}
- HOST_USER_GID=${HOST_USER_GID}

View File

@@ -1,21 +1,23 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
@@ -60,7 +72,7 @@ modification follow.
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@@ -633,29 +635,40 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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
GNU Affero General Public License for more details.
GNU General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

114
dockerfiles/README.md Normal file
View File

@@ -0,0 +1,114 @@
[![Docker](https://github.com/jokob-sk/Pi.Alert/actions/workflows/docker.yml/badge.svg)](https://github.com/jokob-sk/Pi.Alert/actions/workflows/docker.yml)
[![Docker Image Size](https://img.shields.io/docker/image-size/jokobsk/pi.alert?logo=Docker)](https://hub.docker.com/r/jokobsk/pi.alert)
<a href="https://hub.docker.com/r/jokobsk/pi.alert">
<img src="https://img.shields.io/docker/pulls/jokobsk/pi.alert?logo=docker&color=0aa8d2&logoColor=fff" alt="Docker Pulls">
</a>
# 🐳 A docker image for Pi.Alert
🥇 Pi.Alert credit goes to [pucherot/Pi.Alert](https://github.com/pucherot/Pi.Alert). <br/>
🐳 Docker Image: [jokobsk/Pi.Alert](https://registry.hub.docker.com/r/jokobsk/pi.alert). <br/>
📄 [Dockerfile](https://github.com/jokob-sk/Pi.Alert/blob/main/Dockerfile) <br/>
📚 [Dockerfile instructions](https://github.com/jokob-sk/Pi.Alert/blob/main//dockerfiles/README.md).
Big thanks to <a href="https://github.com/Macleykun">@Macleykun</a> for help and tips&tricks for Dockerfile(s):
<a href="https://github.com/Macleykun">
<img src="https://avatars.githubusercontent.com/u/26381427?size=50">
</a>
## Usage
pialert.conf
- Everytime you rebuilt the container with a new image check if new settings have been added in [pialert.conf](https://github.com/jokob-sk/Pi.Alert/blob/main/config/pialert.conf).
Network
- You will have to run the container on the host network, e.g: `sudo docker run --rm --net=host jokobsk/pi.alert`
Default Port
- The app is accessible on the port `:20211`.
> Please note - the cronjob is executed every 3 and 5 minutes so wait that long for all of the scans to run.
## 💾 Setup and Backups
1. (**required**) Download `pialert.conf` and `version.conf` from [here](https://github.com/jokob-sk/Pi.Alert/tree/main/config).
2. (**required**) In `pialert.conf` specify your network adapter (will probably be `eth0` or `eth1`) and the network filter (which **significantly** speeds up the scan process), e.g. if your DHCP server assigns IPs in the 192.168.1.0 to 192.168.1.255 range specify it the following way:
* `SCAN_SUBNETS = '192.168.1.0/24 --interface=eth0'`
3. (**required**) Use your configuration by:
* Mapping the container folder `/home/pi/pialert/config` to a persistent folder containing `pialert.conf` and `version.conf`,
* ... or by mapping the files individually `pialert.conf:/home/pi/pialert/config/pialert.conf` and `version.conf:/home/pi/pialert/config/version.conf`
4. Set the `TZ` environment variable to your current time zone (e.g.`Europe/Paris`). Find your time zone [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
5. Database backup
* The DB is stored under `/home/pi/pialert/db/pialert.db`. Map this file to a persistent location (see [Examples](https://github.com/jokob-sk/Pi.Alert/tree/main/dockerfiles#page_facing_up-examples) for details). If facing issues (AJAX errors, can't write to DB, etc, make sure permissions are set correctly, alternatively check the logs under `/home/pi/pialert/log`)
6. The container supports mapping to local User nad Group IDs. Specify the enviroment variables `HOST_USER_ID` and `HOST_USER_GID` if needed.
7. You can override the port by specifying the `PORT` env variable.
Config examples can be found below.
## 📄 Examples
### Example 1
`docker-compose.yml`
```yaml
version: "3"
services:
pialert:
container_name: pialert
image: "jokobsk/pi.alert:latest"
network_mode: "host"
restart: always
volumes:
- ${APP_DATA_LOCATION}/pialert/config:/home/pi/pialert/config
- ${APP_DATA_LOCATION}/pialert/db/pialert.db:/home/pi/pialert/db/pialert.db
- ${LOGS_LOCATION}/tmp:/home/pi/pialert/log
environment:
- TZ=${TZ}
- PORT=${PORT}
- HOST_USER_ID=${HOST_USER_ID}
- HOST_USER_GID=${HOST_USER_GID}
```
`.env` file
```yaml
#GLOBAL
APP_DATA_LOCATION=/path/to/docker_appdata
APP_CONFIG_LOCATION=/path/to/docker_config
LOGS_LOCATION=/path/to/docker_logs
TZ=Europe/Paris
HOST_USER_ID=1000
HOST_USER_GID=1000
PORT=20211
```
To run the container execute: `sudo docker-compose --env-file /path/to/.env up`
### Example 2
Courtesy of [pbek](https://github.com/pbek). The volume `pialert_db` is used by the db directory. The two config files are mounted directly from a local folder to their places in the config folder. You can backup the `docker-compose.yaml` folder and the docker volumes folder.
```yaml
pialert:
image: jokobsk/pi.alert
ports:
- "80:20211/tcp"
environment:
- TZ=Europe/Vienna
networks:
local:
ipv4_address: 192.168.1.2
restart: unless-stopped
volumes:
- pialert_db:/home/pi/pialert/db
- ./pialert/pialert.conf:/home/pi/pialert/config/pialert.conf
- ./pialert/version.conf:/home/pi/pialert/config/version.conf
```
## ☕ Support
> Disclaimer: This is my second container and I might have used unconventional hacks so if anyone is more experienced, feel free to fork/create pull requests. Also, please only donate if you don't have any debt yourself. Support yourself first, then others.
<a href="https://www.buymeacoffee.com/jokobsk" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 117px !important;" width="150px" ></a>

15
dockerfiles/start.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
/home/pi/pialert/dockerfiles/user-mapping.sh
# if custom variables not set we do not need to do anything
if [ -n "${TZ}" ]; then
sed -ie "s|Europe/Berlin|${TZ}|g" /home/pi/pialert/install/pialert.cron
sed -ie "s|Europe/Berlin|${TZ}|g" /home/pi/pialert/config/pialert.conf
crontab < /home/pi/pialert/install/pialert.cron
fi
if [ -n "${PORT}" ]; then
sed -ie 's/= 20211/= '${PORT}'/g' /etc/lighttpd/lighttpd.conf
fi
/etc/init.d/lighttpd start
cron -f

View File

@@ -0,0 +1,29 @@
#!/bin/bash
if [ -z "${USER}" ]; then
echo "We need USER to be set!"; exit 100
fi
# if both not set we do not need to do anything
if [ -z "${HOST_USER_ID}" -a -z "${HOST_USER_GID}" ]; then
echo "Nothing to do here." ; exit 0
fi
# reset user_?id to either new id or if empty old (still one of above
# might not be set)
USER_ID=${HOST_USER_ID:=$USER_ID}
USER_GID=${HOST_USER_GID:=$USER_GID}
LINE=$(grep -F "${USER}" /etc/passwd)
# replace all ':' with a space and create array
array=( ${LINE//:/ } )
# home is 5th element
USER_HOME=${array[4]}
sed -i -e "s/^${USER}:\([^:]*\):[0-9]*:[0-9]*/${USER}:\1:${USER_ID}:${USER_GID}/" /etc/passwd
sed -i -e "s/^${USER}:\([^:]*\):[0-9]*/${USER}:\1:${USER_GID}/" /etc/group
chown -R ${USER_ID}:${USER_GID} ${USER_HOME}
exec su - "${USER}"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 231 KiB

BIN
docs/img/1_devices_dark.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 290 KiB

BIN
docs/img/5_maintain.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

View File

@@ -0,0 +1,17 @@
.fc-sat {
background-color: #444D56; }
.fc-sun {
background-color: #444D56; }
.fc-today {
background-color: #8D9AAC !important;
border: none !important;
}
.fc-cell-content {
background-color: #272c30;
}
.fc-widget-header {
background-color: #353c42;
}
.fc-unthemed .fc-content, .fc-unthemed .fc-divider, .fc-unthemed .fc-list-heading td, .fc-unthemed .fc-list-view, .fc-unthemed .fc-popover, .fc-unthemed .fc-row, .fc-unthemed tbody, .fc-unthemed td, .fc-unthemed th, .fc-unthemed thead{
border-color: #353c42 !important;
}

725
front/css/dark-patch.css Normal file
View File

@@ -0,0 +1,725 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2020 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license.
*
* The colors used in this theme has been inspired by
* https://github.com/anvyst/adminlte-skin-midnight
*
* Additional fixes For Pi.Alert UI by leiweibau */
:root {
--datatable-bgcolor: rgba(64, 76, 88, 0.8);
}
html {
background-color: #353c42;
}
body {
background-color: #353c42;
color: #bec5cb;
}
h4 {
color: #44def1;
}
.content-header > .breadcrumb > li > a {
color: #bec5cb;
}
.table > thead > tr > th,
.table > tbody > tr > th,
.table > tfoot > tr > th,
.table > thead > tr > td,
.table > tbody > tr > td,
.table > tfoot > tr > td {
border-top: 0;
}
.table > thead > tr.odd,
.table > tbody > tr.odd,
.table > tfoot > tr.odd {
background-color: #2a2f34;
}
.table > thead > tr.odd:hover,
.table > tbody > tr.odd:hover,
.table > tfoot > tr.odd:hover,
.table > thead > tr.even:hover,
.table > tbody > tr.even:hover,
.table > tfoot > tr.even:hover {
background-color: #1e2226;
}
.table-bordered,
.table-bordered > thead > tr > th,
.table-bordered > tbody > tr > th,
.table-bordered > tfoot > tr > th,
.table-bordered > thead > tr > td,
.table-bordered > tbody > tr > td,
.table-bordered > tfoot > tr > td {
border: 1px solid #353c42;
}
.dataTables_wrapper input[type="search"] {
border-radius: 4px;
background-color: #353c42;
border: 0;
color: #bec5cb;
}
.dataTables_paginate .pagination li > a {
background-color: #353c42;
border-color: #353c42;
}
.pagination > .disabled > a,
.pagination > .disabled > a:focus,
.pagination > .disabled > a:hover,
.pagination > .disabled > span,
.pagination > .disabled > span:focus,
.pagination > .disabled > span:hover {
cursor: not-allowed;
color: #bec5cb;
background-color: #353c42;
border-color: #353c42;
}
.pagination > li > a:focus,
.pagination > li > a:hover,
.pagination > li > span:focus,
.pagination > li > span:hover {
z-index: 2;
color: #bec5cb;
background-color: #54606b;
border-color: #54606b;
}
.wrapper,
.main-sidebar,
.left-side {
background-color: #272c30;
}
.user-panel > .info,
.user-panel > .info > a {
color: #fff;
}
.sidebar-menu > li.header {
color: #556068;
background-color: #1e2225;
}
.sidebar-menu > li > a {
border-left: 3px solid transparent;
}
.sidebar-menu > li:hover > a,
.sidebar-menu > li > a:focus,
.sidebar-menu > li.active > a {
color: #fff;
background-color: #22272a;
border-color: #3c8dbc;
}
.sidebar-menu > li > .treeview-menu {
margin: 0 1px;
background-color: #32393e;
}
.sidebar a {
color: #bec5cb;
}
.sidebar a:hover {
text-decoration: none;
}
.treeview-menu > li > a {
color: #949fa8;
}
.treeview-menu > li.active > a,
.treeview-menu > li > a:hover,
.treeview-menu > li > a:focus {
color: #fff;
}
.sidebar-form {
border-radius: 3px;
border: 1px solid #3e464c;
margin: 10px;
}
.sidebar-form input[type="text"],
.sidebar-form .btn {
box-shadow: none;
background-color: #3e464c;
border: 1px solid transparent;
height: 35px;
}
.sidebar-form input[type="text"] {
color: #666;
border-top-left-radius: 2px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 2px;
}
.sidebar-form input[type="text"]:focus,
.sidebar-form input[type="text"]:focus + .input-group-btn .btn {
background-color: #fff;
color: #666;
}
.sidebar-form input[type="text"]:focus + .input-group-btn .btn {
border-left-color: #fff;
}
.sidebar-form .btn {
color: #999;
border-top-left-radius: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom-left-radius: 0;
}
.box,
.box-footer,
.info-box,
.box-comment,
.comment-text,
.comment-text .username {
color: #bec5cb;
background-color: #272c30;
}
.box-comments .box-comment {
border-bottom-color: #353c42;
}
.box-footer {
border-top: 1px solid #353c42;
}
.box-header.with-border {
border-bottom: 1px solid #353c42;
}
.box-solid,
.box {
border: 1px solid #272c30;
}
.box-solid > .box-header,
.box > .box-header {
color: #bec5cb;
}
.box-solid > .box-header .btn,
.box > .box-header .btn {
color: #bec5cb;
}
.box.box-info,
.box.box-primary,
.box.box-success,
.box.box-warning,
.box.box-danger {
border-top-width: 3px;
}
.main-header .navbar {
background-color: #272c30;
}
.main-header .navbar .nav > li > a,
.main-header .navbar .nav > li > .navbar-text {
color: #bec5cb;
max-height: 50px;
}
.main-header .navbar .nav > li > a:hover,
.main-header .navbar .nav > li > a:active,
.main-header .navbar .nav > li > a:focus,
.main-header .navbar .nav .open > a,
.main-header .navbar .nav .open > a:hover,
.main-header .navbar .nav .open > a:focus,
.main-header .navbar .nav > .active > a {
background-color: rgba(0, 0, 0, 0.1);
color: #f6f6f6;
}
.main-header .navbar .sidebar-toggle {
color: #bec5cb;
}
.main-header .navbar .sidebar-toggle:hover {
color: #f6f6f6;
background-color: rgba(0, 0, 0, 0.1);
}
.timeline li .timeline-item {
color: #bec5cb;
background-color: #272c30;
border-color: #353c42;
}
.timeline li .timeline-header {
border-bottom-color: #353c42;
}
.nav-stacked > li > a {
color: #bec5cb;
}
.nav-stacked > li > a:hover {
color: white;
background-color: #1e2226;
}
.content-wrapper,
.right-side {
background-color: #353c42;
}
.main-footer,
.nav-tabs-custom {
background-color: #272c30;
border-top-color: #353c42;
color: #bec5cb;
}
.main-footer .nav-tabs,
.nav-tabs-custom .nav-tabs {
background-color: #30383f;
border-bottom-color: #2f363b;
}
.main-footer .tab-content,
.nav-tabs-custom .tab-content {
background-color: #30383f;
}
.nav-tabs-custom > .nav-tabs {
background: rgba(64, 72, 80, 0.666);
}
.nav-tabs-custom > .nav-tabs > li {
margin-right: 1px;
color: #bec5cb;
}
.nav-tabs-custom > .nav-tabs > li.active > a,
.nav-tabs-custom > .nav-tabs > li.active:hover > a {
border-left-color: #30383f;
border-right-color: #30383f;
background-color: #30383f;
color: #bec5cb;
}
.nav-tabs-custom > .nav-tabs > li:not(.active):hover {
border-top-color: #d2d6de;
background-color: transparent;
}
.nav-tabs-custom > .nav-tabs > li > a {
color: #8e959b;
}
.nav-tabs-custom > .nav-tabs > li > a:focus {
color: #3c8dbc;
}
.nav-tabs-custom > .nav-tabs > li:hover > a,
.nav-tabs-custom > .nav-tabs > li.active:hover > a {
background-color: #353c42;
color: #bec5cb;
}
.list-group {
color: #bec5cb;
background-color: #272c30;
}
.list-group .list-group-item {
border-color: #353c42;
background-color: #272c30;
}
.input-group .input-group-addon {
border-right: 1px solid #272c30;
}
.select2 .select2-selection {
background-color: #353c42;
color: #bec5cb;
border: 1px solid #353c42;
}
.select2 .select2-selection .select2-container--default,
.select2 .select2-selection .select2-selection--single,
.select2 .select2-selection .select2-selection--multiple,
.select2 .select2-selection .select2-selection__rendered {
color: #bec5cb;
}
.select2-dropdown {
background-color: #353c42;
color: #bec5cb;
border: 1px solid #353c42;
}
.select2-dropdown .select2-search__field {
background-color: #272c30;
color: #bec5cb;
border: 1px solid #353c42;
}
.select2-container--default.select2-container--open {
background-color: #272c30;
}
.layout-boxed {
background: url("../../img/boxed-bg-dark.png") repeat fixed;
}
.not-used {
background-color: #eee;
}
.not-used:hover {
background-color: #c5c5c5;
}
.used {
background-color: #fff;
}
.used:hover {
background-color: #ddd;
}
.graphs-grid {
background-color: rgba(255, 255, 255, 0.2);
}
.graphs-ticks {
color: #b8c7ce;
}
.queries-permitted {
background-color: #00a65a;
}
.queries-blocked {
background-color: #999;
}
.progress {
background-color: #333;
}
.bg-green {
background-color: #005c32 !important;
}
.bg-aqua {
background-color: #007997 !important;
}
.bg-yellow {
background-color: #b1720c !important;
}
.bg-red {
background-color: #913225 !important;
}
code,
pre {
padding: 2px 4px;
font-size: 90%;
color: #bec5cb;
background-color: #353c42;
border-radius: 4px;
}
/* Used in the Query Log table */
.text-green-light {
color: #5ca314 !important;
}
.text-green {
color: #00aa60 !important;
}
.text-orange {
color: #b1720c !important;
}
.text-red {
color: #bd2c19 !important;
}
.text-vivid-blue {
color: #007997 !important;
}
td.highlight {
background-color: rgba(255, 204, 0, 0.333);
}
.btn-default {
box-shadow: none;
background-color: #3e464c;
color: #bec5cb;
border: 1px solid #353c42;
}
/* Used in debug log page */
.log-red {
color: #ff4038;
}
.log-green {
color: #4c4;
}
.log-yellow {
color: #fb0;
}
.log-blue {
color: #48f;
}
.log-purple {
color: #b8e;
}
.log-cyan {
color: #0df;
}
.log-gray {
color: #999;
}
#output {
border-color: #505458;
background: #272c30;
}
/* Used by the long-term pages */
.daterangepicker {
background-color: #3e464c;
border-radius: 4px;
border: 1px solid #353c42;
}
.daterangepicker .ranges li:hover {
background-color: #353c42;
}
.daterangepicker .ranges li.active {
background-color: #1e2226; /* Color also used in table pagination */
}
.daterangepicker .calendar-table {
background-color: #3e464c;
border-radius: 4px;
border: 1px solid #353c42;
}
.daterangepicker td.off,
.daterangepicker td.off.in-range,
.daterangepicker td.off.start-date,
.daterangepicker td.off.end-date {
background-color: #485158;
}
.daterangepicker td.available:hover,
.daterangepicker th.available:hover {
background-color: #1e2226;
}
.daterangepicker td.active,
.daterangepicker td.active:hover,
.daterangepicker td.in-range:hover {
background-color: #225e92;
}
.daterangepicker td.in-range {
background-color: #1e2226;
color: #bec5cb;
}
input,
select,
select.form-control,
.form-group .input-group-addon,
.input-group .input-group-addon,
.form-group input,
.input-group input,
.form-group textarea,
.input-group textarea,
.daterangepicker select.hourselect,
.daterangepicker select.minuteselect,
.daterangepicker select.secondselect,
.daterangepicker select.ampmselect,
.form-control,
div.dataTables_wrapper div.dataTables_length select {
background-color: #353c42;
color: #bec5cb;
border: 1px solid #3d444b;
}
.form-control[disabled],
.form-control[readonly],
fieldset[disabled] .form-control {
background-color: #353c42;
opacity: 1;
}
.navbar-custom-menu > .navbar-nav > li > .dropdown-menu {
background-color: #4c5761;
color: #bec5cb;
border: 1px solid #171c20;
}
.table-striped > tbody > tr:nth-of-type(2n + 1) {
background-color: #2d343a;
}
.panel,
.panel-body,
.panel-default > .panel-heading {
background-color: #3e464c;
border-radius: 4px;
border: 1px solid #353c42;
color: #bec5cb;
}
.box.box-solid.box-info,
.box.box-solid.box-info > .box-header {
color: #bec5cb;
background-color: #367fa9 !important;
border: 1px solid #367fa9;
}
input[type="password"]::-webkit-credentials-auto-fill-button {
background: #bfc5ca;
}
input[type="password"]::-webkit-caps-lock-indicator {
filter: invert(100%);
}
.network-never {
background-color: #661b02;
}
.network-recent {
background-color: #114100;
}
.network-old {
background-color: #525200;
}
.network-older {
background-color: #502b00;
}
.network-gradient {
background-image: linear-gradient(to right, #114100 0%, #525200 100%);
}
.icheckbox_polaris,
.icheckbox_futurico,
.icheckbox_minimal-blue {
margin-right: 10px;
}
.iradio_polaris,
.iradio_futurico,
.iradio_minimal-blue {
margin-right: 8px;
}
/* Overlay box with spinners as shown during data collection for graphs */
.box .overlay,
.overlay-wrapper .overlay {
z-index: 50;
background-color: rgba(53, 60, 66, 0.733);
border-radius: 3px;
}
.box .overlay > .fa,
.overlay-wrapper .overlay > .fa,
.navbar-nav > .user-menu > .dropdown-menu > .user-body a {
color: #bec5cb !important;
}
.navbar-nav > .user-menu > .dropdown-menu > .user-footer {
background-color: #353c42bb;
padding: 10px;
}
.modal-content {
background: #272c30;
}
.modal-header {
border-bottom-color: #353c42;
}
.modal-footer {
border-top-color: #353c42;
}
.close {
color: #383838;
}
/*** Fix login input visual misalignment ***/
#loginform,
#loginform input {
color: rgb(120, 127, 133);
}
.login-options input,
.login-options [class*="icheck-"] > input:first-child + input[type="hidden"] + label::before,
.login-options [class*="icheck-"] > input:first-child + label::before {
background: none;
border-color: rgb(120, 127, 133);
}
/*** Additional fixes For Pi.Alert UI ***/
.small-box {
border-radius: 10px;
border-top: 0px;
}
.pa-small-box-aqua .inner {
background-color: rgb(45,108,133);
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.pa-small-box-green .inner {
background-color: rgb(31,76,46);
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.pa-small-box-yellow .inner {
background-color: rgb(151,104,37);
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.pa-small-box-red .inner {
background-color: rgb(120,50,38);
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.pa-small-box-gray .inner {
background-color: #777;
/* color: rgba(20,20,20,30%); */
border-top-left-radius: 10px;
border-top-right-radius: 10px;
}
.pa-small-box-gray .inner h3 {
color: #bbb;
}
.text-gray-20 {
color: rgba(220,220,220,30%);
}
.bg-gray {
background-color: #888888 !important;
}
.badge.bg-green {
background-color: #00A000 !important;
}
.badge.bg-gray {
background-color: #888 !important;
}
#txtRecord {
background-color: #353c42;
border-color: #888888;
}
.table-hover tbody tr:hover td, .table-hover tbody tr:hover th {
background-color: rgb(189,192,198);
color: #444;
}
.db_info_table_cell:nth-child(1) {background: #272c30}
.db_info_table_cell:nth-child(2) {background: #272c30}
.db_tools_table_cell_a:nth-child(1) {background: #272c30}
.db_tools_table_cell_a:nth-child(2) {background: #272c30}
.db_tools_table_cell_b:nth-child(1) {background: #272c30}
.db_tools_table_cell_b:nth-child(2) {background: #272c30}
.db_info_table {
display: table;
border-spacing: 0em;
font-weight: 400;
font-size: 15px;
width: 100%;
margin: auto;
}
.nav-tabs-custom > .nav-tabs > li:hover > a, .nav-tabs-custom > .nav-tabs > li.active:hover > a {
background-color: #272c30;
color: #bec5cb;
}
.nav-tabs-custom > .nav-tabs > li.active > a, .nav-tabs-custom > .nav-tabs > li.active:hover > a {
border-left-color: #30383f;
border-right-color: #30383f;
background-color: #272c30;
color: #bec5cb;
}
.nav-tabs-custom > .nav-tabs {
background-color: #353c42;
}
.nav-tabs-custom .tab-content {
background-color: #272c30;
}
.top_small_box_gray_text {
color: white !important;
}
/* remove white border that appears on mobile screen sizes */
.box-body {
border: 0px;
}
/* remove white border that appears on mobile screen sizes */
.table-responsive {
border: 0px;
}
.login-page {
background-color: transparent;
}
.login-logo a {
color: white;
}
.login-box-body {
color: #bec5cb;
background-color: #272c30;
}
/* Add border radius to bottom of the status boxes*/
.pa-small-box-footer {
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
}
.small-box > .inner h3, .small-box > .inner p {
margin-bottom: 0px;
margin-left: 0px;
}
.small-box:hover .icon {
font-size: 3.74em;
}
.small-box .icon {
top: 0.01em;
font-size: 3.25em;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
@font-face {
font-family: 'Source Sans Pro';
src: url('font/SourceSans3-Regular.otf.woff2') format('woff2');
}

View File

@@ -11,50 +11,49 @@
Global Variables
----------------------------------------------------------------------------- */
:root {
--color-aqua: #00c0ef;
--color-green: #00a65a;
--color-aqua: #00c0ef;
--color-green: #00a65a;
--color-yellow: #f39c12;
--color-red: #dd4b39;
--color-red: #dd4b39;
}
/* -----------------------------------------------------------------------------
Text Classes
----------------------------------------------------------------------------- */
.text-center {
text-align: center;
text-align: center;
}
.text-right {
text-align: right;
text-align: right;
}
.text-white {
color: white;
color: white;
}
.text-gray50 {
color: #808080;
color: #808080;
}
.text-gray-20 {
color: rgba(192,192,192,20%);
color: rgba(192, 192, 192, 20%);
}
.text-aqua-20 {
color: rgba(0,192,239,20%);
color: rgba(0, 192, 239, 20%);
}
.text-green-20 {
color: rgba(0,166,90,20%);
color: rgba(0, 166, 90, 20%);
}
.text-yellow-20 {
color: rgba(243,156,18,20%);
color: rgba(243, 156, 18, 20%);
}
.text-red-20 {
color: rgba(221,75,57,20%);
color: rgba(221, 75, 57, 20%);
}
.no-border {
@@ -65,82 +64,82 @@
Main Sections
----------------------------------------------------------------------------- */
.content-header {
padding-top: 5px;
padding-top: 5px;
}
.content-header > .breadcrumb {
background: transparent;
.content-header>.breadcrumb {
background: transparent;
}
.content {
padding-bottom: 0px;
padding-bottom: 0px;
}
.box-body {
padding-top: 0px;
padding-bottom: 0px;
padding-top: 0px;
padding-bottom: 0px;
}
.main-footer {
padding: 5px;
color: gray;
padding: 5px;
color: gray;
}
/* -----------------------------------------------------------------------------
Customized Main Menu
----------------------------------------------------------------------------- */
.main-header .logo {
width: 150px;
width: 150px;
}
.main-header > .navbar {
margin-left: 150px;
.main-header>.navbar {
margin-left: 150px;
}
.main-sidebar, .left-side {
width: 150px;
.main-sidebar,
.left-side {
width: 150px;
}
.content-wrapper, .right-side, .main-footer {
margin-left: 150px;
.content-wrapper,
.right-side,
.main-footer {
margin-left: 150px;
}
@media (max-width: 767px) {
.main-header .logo {
width: 100%;
}
.main-header .logo {
width: 100%;
}
.main-header .navbar {
margin: 0;
}
.content-wrapper, .main-footer {
margin-left: 0px;
}
.main-header .navbar {
margin: 0;
}
.content-wrapper,
.main-footer {
margin-left: 0px;
}
}
.sidebar-open .content-wrapper, .sidebar-open .main-footer {
.sidebar-open .content-wrapper,
.sidebar-open .main-footer {
-webkit-transform: translate(150px, 0);
-ms-transform: translate(150px, 0);
-o-transform: translate(150px, 0);
transform: translate(150px, 0)
}
.skin-yellow-light .sidebar-menu > li > a:hover {
.skin-yellow-light .sidebar-menu>li>a:hover {
background: #f0f0f0;
border-left-color: rgb(243, 156, 18);
}
.skin-yellow-light .sidebar-menu > li.active > a {
.skin-yellow-light .sidebar-menu>li.active>a {
background: #e0e0e0;
border-left-color: rgb(243, 156, 18);
}
/* -----------------------------------------------------------------------------
Customized Boxes
----------------------------------------------------------------------------- */
@@ -151,185 +150,183 @@
}
.pa-small-box-2 .inner h3 {
margin-left: 0em;
margin-bottom: 1.3em;
margin-left: 0em;
margin-bottom: 1.3em;
}
.pa-small-box-footer {
color: white !important;
font-size: 18px;
color: white !important;
font-size: 18px;
}
/* -------------------------------------------------------------------------- */
.pa-small-box-aqua {
border-top: 3px solid #00c0ef;
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
/* border-top: 3px solid #00c0ef; */
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
}
.pa-small-box-aqua .inner {
color: #00c0ef;
background-color:#FFFFFF;
color: #00c0ef;
background-color: #FFFFFF;
}
.pa-small-box-aqua .inner h3 {
margin-left: 0.5em;
margin-left: 0.5em;
}
.pa-small-box-aqua .icon {
color: #00c0ef;
color: #00c0ef;
}
/* -------------------------------------------------------------------------- */
.pa-small-box-green {
border-top: 3px solid #00a65a;
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
/* border-top: 3px solid #00a65a; */
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
}
.pa-small-box-green .inner {
color: #00a65a;
background-color:#FFFFFF;
color: #00a65a;
background-color: #FFFFFF;
}
.pa-small-box-green .inner h3 {
margin-left: 0.5em;
margin-left: 0.5em;
}
.pa-small-box-green .icon {
color: #00a65a;
color: #00a65a;
}
/* -------------------------------------------------------------------------- */
.pa-small-box-yellow {
border-top: 3px solid #f39c12;
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
/* border-top: 3px solid #f39c12; */
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
}
.pa-small-box-yellow .inner {
color: #f39c12;
background-color:#FFFFFF;
color: #f39c12;
background-color: #FFFFFF;
}
.pa-small-box-yellow .inner h3 {
margin-left: 0.5em;
margin-left: 0.5em;
}
.pa-small-box-yellow .icon {
color: #f39c12;
color: #f39c12;
}
/* -------------------------------------------------------------------------- */
.pa-small-box-red {
border-top: 3px solid #dd4b39;
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
/* border-top: 3px solid #dd4b39; */
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
}
.pa-small-box-red .inner {
color: #dd4b39;
background-color:#FFFFFF;
color: #dd4b39;
background-color: #FFFFFF;
}
.pa-small-box-red .inner h3 {
margin-left: 0.5em;
margin-left: 0.5em;
}
.pa-small-box-red .icon {
color: #dd4b39;
color: #dd4b39;
}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
.pa-small-box-gray {
border-top: 3px solid #a0a0a0;
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
/* border-top: 3px solid #a0a0a0; */
box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1);
}
.pa-small-box-gray .inner {
color: #a0a0a0;
background-color:#FFFFFF;
color: #a0a0a0;
background-color: #FFFFFF;
}
.pa-small-box-gray .inner h3 {
margin-left: 0.5em;
margin-left: 0.5em;
}
.pa-small-box-gray .icon {
color: #a0a0a0;
color: #a0a0a0;
}
/* -----------------------------------------------------------------------------
Customized Box Borders
----------------------------------------------------------------------------- */
.box.box-aqua {
border-top-color: #00c0ef;
/* .box.box-aqua {
border-top-color: #00c0ef;
}
.box.box-green {
border-top-color: #00a65a;
border-top-color: #00a65a;
}
.box.box-yellow {
border-top-color: #f39c12;
border-top-color: #f39c12;
}
.box.box-red {
border-top-color: #dd4b39;
}
border-top-color: #dd4b39;
} */
/* -----------------------------------------------------------------------------
Custom Border
----------------------------------------------------------------------------- */
.bottom-border-aqua {
border-bottom-color: #00c0ef;
border-bottom-style: solid;
border-bottom-width: 3px
border-bottom-color: #00c0ef;
border-bottom-style: solid;
border-bottom-width: 3px
}
.bottom-border-primary {
border-bottom-color: #3c8dbc;
border-bottom-style: solid;
border-bottom-width: 3px
border-bottom-color: #3c8dbc;
border-bottom-style: solid;
border-bottom-width: 3px
}
/* -----------------------------------------------------------------------------
Customized Tabs
----------------------------------------------------------------------------- */
.nav-tabs-custom {
background: transparent;
background: transparent;
}
.nav > li > a {
.nav>li>a {
position: relative;
display: block;
padding: 10px 10px;
}
/* -----------------------------------------------------------------------------
Customized Menu dropdown
----------------------------------------------------------------------------- */
.dropdown-menu {
max-height: 250px;
overflow-x: hidden;
box-shadow: 0px 3px 20px rgba(0,0,0,0.2);
box-shadow: 0px 3px 20px rgba(0, 0, 0, 0.2);
}
/* -----------------------------------------------------------------------------
Default Table config
----------------------------------------------------------------------------- */
.table > tbody > tr > td {
padding:4px;
.table>tbody>tr>td {
padding: 4px;
}
.table-hover tbody tr:hover td, .table-hover tbody tr:hover th {
background-color: #FFFFD0;
.table-hover tbody tr:hover td,
.table-hover tbody tr:hover th {
background-color: #FFFFD0;
}
.dataTables_info, .dataTables_paginate, .dataTables_length, .dataTables_filter {
color: #B0B0B0;
.dataTables_info,
.dataTables_paginate,
.dataTables_length,
.dataTables_filter {
color: #B0B0B0;
}
/* -----------------------------------------------------------------------------
@@ -341,27 +338,29 @@
}
.pa-btn-delete {
border-color:#ffb060;
background-color:#ffd080;
border-color: #ffb060;
background-color: #ffd080;
}
.pa-btn-delete:hover {
border-color:#ffb060;
background-color:#ffb060;
border-color: #ffb060;
background-color: #ffb060;
}
.pa-btn-records, .pa-btn-records:hover, .pa-btn-records:focus, .pa-btn-records:active {
border-color:#ddd;
background-color:#f4f4f4;
.pa-btn-records,
.pa-btn-records:hover,
.pa-btn-records:focus,
.pa-btn-records:active {
border-color: #ddd;
background-color: #f4f4f4;
cursor: default;
}
/* -----------------------------------------------------------------------------
Customized Full Calendar
----------------------------------------------------------------------------- */
.fc h2 {
font-size: 20px;
font-size: 20px;
}
.fc-weekend {
@@ -380,7 +379,9 @@
background-color: #FFF0E0;
}
.fc-resized-row { height: 26px !important; }
.fc-resized-row {
height: 26px !important;
}
.fc-transparent-border {
border-Color: transparent !important;
@@ -395,60 +396,149 @@
border-right: 5px solid #606060;
}
/* -----------------------------------------------------------------------------
Spin
----------------------------------------------------------------------------- */
.pa_semitransparent-panel {
position: absolute;
width: 100%; //calc (100% -40px);
height: 100%;
width: 100%; //calc (100% -40px);
height: 100%;
left: 0;
top: 0;
display: block;
display: block;
opacity: 0.8;
opacity: 0.8;
background-color: #fff;
z-index: 99;
z-index: 99;
}
.pa_spinner {
position: absolute;
left: 0;
right: 0;
top: 20px;
margin-left: auto;
position: absolute;
left: 0;
right: 0;
top: 20px;
margin-left: auto;
margin-right: auto;
padding: 15px;
width: 200px;
padding: 15px;
width: 200px;
background-color: #fff;
z-index: 100;
z-index: 100;
}
/* -----------------------------------------------------------------------------
Notification float banner
----------------------------------------------------------------------------- */
.pa_alert_notification {
text-align: center;
font-size: large;
font-weight: bold;
color: #258744;
text-align: center;
font-size: large;
font-weight: bold;
color: #258744;
background-color: #d4edda;
border-color: #c3e6cb;
border-radius: 5px;
border-color: #c3e6cb;
border-radius: 5px;
max-width: 1000px; /* 80% wrapper 1250px */
width: 80%;
z-index: 9999;
max-width: 1000px;
/* 80% wrapper 1250px */
width: 80%;
z-index: 9999;
position: fixed;
top: 30px;
margin: auto;
transform: translate(0,0);
position: fixed;
top: 30px;
margin: auto;
transform: translate(0, 0);
display: none;
display: none;
}
.dbtools-button {
display: inline-block;
width: 160px;
height: 60px;
white-space: normal;
word-wrap: break-word;
font-size: 16px;
padding: 4px;
}
.db_info_table_cell:nth-child(1) {
background: white
}
.db_info_table_cell:nth-child(2) {
background: white
}
.db_tools_table_cell_a:nth-child(1) {
background: white
}
.db_tools_table_cell_a:nth-child(2) {
background: white
}
.db_tools_table_cell_b:nth-child(1) {
background: white
}
.db_tools_table_cell_b:nth-child(2) {
background: white
}
.db_info_table {
display: table;
border-spacing: 0em;
font-weight: 400;
font-size: 15px;
width: 100%;
margin: auto;
}
.db_info_table_row {
display: table-row;
padding: 3px;
}
.db_info_table_cell {
display: table-cell;
padding: 3px;
padding-left: 10px;
}
.db_tools_table_cell_a {
display: table-cell;
text-align: center;
padding: 10px;
min-width: 180px;
width: 20%;
vertical-align: middle;
}
.db_tools_table_cell_b {
display: table-cell;
text-align: justify;
font-size: 16px;
vertical-align: middle;
padding: 10px;
}
.ajax_scripts_loading {
background-image: url('../img/Loading_Animation.gif');
background-repeat: no-repeat;
background-position: center;
height: 50px;
}
.nav-tabs-custom .tab-content {
background-color: white;
}
.top_small_box_gray_text {
color: white !important;
}
.bg-gray {
background-color: #b2b6be !important;
}
.infobox_label {
font-size: 16px !important;
}

View File

@@ -8,6 +8,14 @@
#--------------------------------------------------------------------------- -->
<?php
session_start();
if ($_SESSION["login"] != 1)
{
header('Location: /pialert/index.php');
exit;
}
require 'php/templates/header.php';
?>
@@ -25,11 +33,11 @@
<!-- period selector -->
<span class="breadcrumb" style="top: 0px;">
<select class="form-control" id="period" onchange="javascript: periodChanged();">
<option value="1 day">Today</option>
<option value="7 days">Last Week</option>
<option value="1 month" selected>Last Month</option>
<option value="1 year">Last Year</option>
<option value="100 years">All info</option>
<option value="1 day"><?php echo $pia_lang['DevDetail_Periodselect_today'];?></option>
<option value="7 days"><?php echo $pia_lang['DevDetail_Periodselect_LastWeek'];?></option>
<option value="1 month" selected><?php echo $pia_lang['DevDetail_Periodselect_LastMonth'];?></option>
<option value="1 year"><?php echo $pia_lang['DevDetail_Periodselect_LastYear'];?></option>
<option value="100 years"><?php echo $pia_lang['DevDetail_Periodselect_All'];?></option>
</select>
</span>
</section>
@@ -42,10 +50,11 @@
<div class="col-lg-3 col-sm-6 col-xs-6">
<a href="#" onclick="javascript: $('#tabDetails').trigger('click')">
<div class="small-box bg-aqua pa-small-box-aqua pa-small-box-2">
<div class="inner"> <h3 id="deviceStatus" style="margin-left: 0em"> -- </h3> </div>
<div class="icon"> <i id="deviceStatusIcon" class=""></i> </div>
<div class="small-box-footer pa-small-box-footer"> Current Status <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-aqua">
<div class="inner"> <h3 id="deviceStatus" style="margin-left: 0em"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['DevDetail_Shortcut_CurrentStatus'];?></p>
</div>
<div class="icon"> <i id="deviceStatusIcon" class=""></i></div>
</div>
</a>
</div>
@@ -53,10 +62,11 @@
<!-- top small box 2 ------------------------------------------------------- -->
<div class="col-lg-3 col-sm-6 col-xs-6">
<a href="#" onclick="javascript: $('#tabSessions').trigger('click');">
<div class="small-box bg-green pa-small-box-green pa-small-box-2">
<div class="inner"> <h3 id="deviceSessions"> -- </h3> </div>
<div class="small-box bg-green">
<div class="inner"> <h3 id="deviceSessions"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['DevDetail_Shortcut_Sessions'];?></p>
</div>
<div class="icon"> <i class="fa fa-plug"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Sesions <i class="fa fa-arrow-circle-right"></i> </div>
</div>
</a>
</div>
@@ -64,10 +74,11 @@
<!-- top small box 3 ------------------------------------------------------- -->
<div class="col-lg-3 col-sm-6 col-xs-6">
<a href="#" onclick="javascript: $('#tabPresence').trigger('click')">
<div class="small-box bg-yellow pa-small-box-yellow pa-small-box-2">
<div class="inner"> <h3 id="deviceEvents" style="margin-left: 0em"> -- </h3> </div>
<div class="small-box bg-yellow">
<div class="inner"> <h3 id="deviceEvents" style="margin-left: 0em"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['DevDetail_Shortcut_Presence'];?></p>
</div>
<div id="deviceEventsIcon" class="icon"> <i class="fa fa-calendar"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Presence <i class="fa fa-arrow-circle-right"></i> </div>
</div>
</a>
</div>
@@ -75,10 +86,11 @@
<!-- top small box 4 ------------------------------------------------------ -->
<div class="col-lg-3 col-sm-6 col-xs-6">
<a href="#" onclick="javascript: $('#tabEvents').trigger('click');">
<div class="small-box bg-red pa-small-box-red pa-small-box-2">
<div class="inner"> <h3 id="deviceDownAlerts"> -- </h3> </div>
<div class="small-box bg-red">
<div class="inner"> <h3 id="deviceDownAlerts"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['DevDetail_Shortcut_DownAlerts'];?></p>
</div>
<div class="icon"> <i class="fa fa-warning"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Down Alerts <i class="fa fa-arrow-circle-right"></i> </div>
</div>
</a>
</div>
@@ -94,10 +106,14 @@
<div id="navDevice" class="nav-tabs-custom">
<ul class="nav nav-tabs" style="fon t-size:16px;">
<li> <a id="tabDetails" href="#panDetails" data-toggle="tab"> Details </a></li>
<li> <a id="tabSessions" href="#panSessions" data-toggle="tab"> Sessions </a></li>
<li> <a id="tabPresence" href="#panPresence" data-toggle="tab"> Presence </a></li>
<li> <a id="tabEvents" href="#panEvents" data-toggle="tab"> Events </a></li>
<li> <a id="tabDetails" href="#panDetails" data-toggle="tab"> <?php echo $pia_lang['DevDetail_Tab_Details'];?> </a></li>
<?php
if ($_REQUEST['mac'] == 'Internet') { $DevDetail_Tap_temp = "Tools"; } else { $DevDetail_Tap_temp = $pia_lang['DevDetail_Tab_Nmap'];}
?>
<li> <a id="tabNmap" href="#panNmap" data-toggle="tab"> <?php echo $DevDetail_Tap_temp;?> </a></li>
<li> <a id="tabSessions" href="#panSessions" data-toggle="tab"> <?php echo $pia_lang['DevDetail_Tab_Sessions'];?> </a></li>
<li> <a id="tabPresence" href="#panPresence" data-toggle="tab"> <?php echo $pia_lang['DevDetail_Tab_Presence'];?> </a></li>
<li> <a id="tabEvents" href="#panEvents" data-toggle="tab"> <?php echo $pia_lang['DevDetail_Tab_Events'];?> </a></li>
<div class="btn-group pull-right">
<button type="button" class="btn btn-default" style="padding: 10px; min-width: 30px;"
@@ -113,7 +129,7 @@
<div class="tab-content" style="min-height: 430px">
<div class="tab-content" style="min-height: 430px;">
<!-- tab page 1 ------------------------------------------------------------ -->
<!--
@@ -124,12 +140,12 @@
<div class="row">
<!-- column 1 -->
<div class="col-lg-4 col-sm-6 col-xs-12">
<h4 class="bottom-border-aqua">Main Info</h4>
<h4 class="bottom-border-aqua"><?php echo $pia_lang['DevDetail_MainInfo_Title'];?></h4>
<div class="box-body form-horizontal">
<!-- MAC -->
<div class="form-group">
<label class="col-sm-3 control-label">MAC</label>
<label class="col-sm-3 control-label"><?php echo $pia_lang['DevDetail_MainInfo_mac'];?></label>
<div class="col-sm-9">
<input class="form-control" id="txtMAC" type="text" readonly value="--">
</div>
@@ -137,7 +153,7 @@
<!-- Name -->
<div class="form-group">
<label class="col-sm-3 control-label">Name</label>
<label class="col-sm-3 control-label"><?php echo $pia_lang['DevDetail_MainInfo_Name'];?></label>
<div class="col-sm-9">
<input class="form-control" id="txtName" type="text" value="--">
</div>
@@ -145,7 +161,7 @@
<!-- Owner -->
<div class="form-group">
<label class="col-sm-3 control-label">Owner</label>
<label class="col-sm-3 control-label"><?php echo $pia_lang['DevDetail_MainInfo_Owner'];?></label>
<div class="col-sm-9">
<div class="input-group">
<input class="form-control" id="txtOwner" type="text" value="--">
@@ -161,7 +177,7 @@
<!-- Type -->
<div class="form-group">
<label class="col-sm-3 control-label">Type</label>
<label class="col-sm-3 control-label"><?php echo $pia_lang['DevDetail_MainInfo_Type'];?></label>
<div class="col-sm-9">
<div class="input-group">
<input class="form-control" id="txtDeviceType" type="text" value="--">
@@ -181,7 +197,7 @@
<!-- Vendor -->
<div class="form-group">
<label class="col-sm-3 control-label">Vendor</label>
<label class="col-sm-3 control-label"><?php echo $pia_lang['DevDetail_MainInfo_Vendor'];?></label>
<div class="col-sm-9">
<input class="form-control" id="txtVendor" type="text" value="--">
</div>
@@ -189,7 +205,7 @@
<!-- Favorite -->
<div class="form-group">
<label class="col-sm-3 control-label">Favorite</label>
<label class="col-sm-3 control-label"><?php echo $pia_lang['DevDetail_MainInfo_Favorite'];?></label>
<div class="col-sm-9" style="padding-top:6px;">
<input class="checkbox blue hidden" id="chkFavorite" type="checkbox">
</div>
@@ -197,7 +213,7 @@
<!-- Group -->
<div class="form-group">
<label class="col-sm-3 control-label">Group</label>
<label class="col-sm-3 control-label"><?php echo $pia_lang['DevDetail_MainInfo_Group'];?></label>
<div class="col-sm-9">
<div class="input-group">
<input class="form-control" id="txtGroup" type="text" value="--">
@@ -218,7 +234,7 @@
<!-- Location -->
<div class="form-group">
<label class="col-sm-3 control-label">Location</label>
<label class="col-sm-3 control-label"><?php echo $pia_lang['DevDetail_MainInfo_Location'];?></label>
<div class="col-sm-9">
<div class="input-group">
<input class="form-control" id="txtLocation" type="text" value="--">
@@ -241,7 +257,7 @@
<!-- Comments -->
<div class="form-group">
<label class="col-sm-3 control-label">Comments</label>
<label class="col-sm-3 control-label"><?php echo $pia_lang['DevDetail_MainInfo_Comments'];?></label>
<div class="col-sm-9">
<textarea class="form-control" rows="3" id="txtComments"></textarea>
</div>
@@ -252,12 +268,12 @@
<!-- column 2 -->
<div class="col-lg-4 col-sm-6 col-xs-12">
<h4 class="bottom-border-aqua">Session Info</h4>
<h4 class="bottom-border-aqua"><?php echo $pia_lang['DevDetail_SessionInfo_Title'];?></h4>
<div class="box-body form-horizontal">
<!-- Status -->
<div class="form-group">
<label class="col-sm-5 control-label">Status</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_SessionInfo_Status'];?></label>
<div class="col-sm-7">
<input class="form-control" id="txtStatus" type="text" readonly value="--">
</div>
@@ -265,7 +281,7 @@
<!-- First Session -->
<div class="form-group">
<label class="col-sm-5 control-label">First Session</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_SessionInfo_FirstSession'];?></label>
<div class="col-sm-7">
<input class="form-control" id="txtFirstConnection" type="text" readonly value="--">
</div>
@@ -273,7 +289,7 @@
<!-- Last Session -->
<div class="form-group">
<label class="col-sm-5 control-label">Last Session</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_SessionInfo_LastSession'];?></label>
<div class="col-sm-7">
<input class="form-control" id="txtLastConnection" type="text" readonly value="--">
</div>
@@ -281,7 +297,7 @@
<!-- Last IP -->
<div class="form-group">
<label class="col-sm-5 control-label">Last IP</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_SessionInfo_LastIP'];?></label>
<div class="col-sm-7">
<input class="form-control" id="txtLastIP" type="text" readonly value="--">
</div>
@@ -289,7 +305,7 @@
<!-- Static IP -->
<div class="form-group">
<label class="col-sm-5 control-label">Static IP</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_SessionInfo_StaticIP'];?></label>
<div class="col-sm-7" style="padding-top:6px;">
<input class="checkbox blue hidden" id="chkStaticIP" type="checkbox">
</div>
@@ -300,22 +316,22 @@
<!-- column 3 -->
<div class="col-lg-4 col-sm-6 col-xs-12">
<h4 class="bottom-border-aqua">Events & Alerts config</h4>
<h4 class="bottom-border-aqua"><?php echo $pia_lang['DevDetail_EveandAl_Title'];?></h4>
<div class="box-body form-horizontal">
<!-- Scan Cycle -->
<div class="form-group">
<label class="col-sm-5 control-label">Scan Cycle</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_EveandAl_ScanCycle'];?></label>
<div class="col-sm-7">
<div class="input-group">
<input class="form-control" id="txtScanCycle" type="text" value="--" readonly style="background-color: #fff;">
<input class="form-control" id="txtScanCycle" type="text" value="--" readonly >
<div class="input-group-btn">
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false" id="dropdownButtonScanCycle">
<span class="fa fa-caret-down"></span></button>
<ul id="dropdownScanCycle" class="dropdown-menu dropdown-menu-right">
<li><a href="javascript:void(0)" onclick="setTextValue('txtScanCycle','1 min')"> Scan 1 min every 5 min</a></li>
<li><a href="javascript:void(0)" onclick="setTextValue('txtScanCycle','15 min');"> Scan 12 min every 15 min</a></li>
<li><a href="javascript:void(0)" onclick="setTextValue('txtScanCycle','0 min');"> Don't Scan</a></li>
<li><a href="javascript:void(0)" onclick="setTextValue('txtScanCycle','1 min')"><?php echo $pia_lang['DevDetail_EveandAl_ScanCycle_a'];?></a></li>
<!-- <li><a href="javascript:void(0)" onclick="setTextValue('txtScanCycle','15 min');"> Scan 12 min every 15 min</a></li> -->
<li><a href="javascript:void(0)" onclick="setTextValue('txtScanCycle','0 min');"><?php echo $pia_lang['DevDetail_EveandAl_ScanCycle_z'];?></a></li>
</ul>
</div>
</div>
@@ -324,7 +340,7 @@
<!-- Alert events -->
<div class="form-group">
<label class="col-sm-5 control-label">Alert All Events</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_EveandAl_AlertAllEvents'];?></label>
<div class="col-sm-7" style="padding-top:6px;">
<input class="checkbox blue hidden" id="chkAlertEvents" type="checkbox">
</div>
@@ -332,7 +348,7 @@
<!-- Alert Down -->
<div class="form-group">
<label class="col-sm-5 control-label">Alert Down</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_EveandAl_AlertDown'];?></label>
<div class="col-sm-7" style="padding-top:6px;">
<input class="checkbox red hidden" id="chkAlertDown" type="checkbox">
</div>
@@ -340,10 +356,10 @@
<!-- Skip Notifications -->
<div class="form-group">
<label class="col-sm-5 control-label" style="padding-top: 0px; padding-left: 0px;">Skip repeated notifications during</label>
<label class="col-sm-5 control-label" style="padding-top: 0px; padding-left: 0px;"><?php echo $pia_lang['DevDetail_EveandAl_Skip'];?></label>
<div class="col-sm-7">
<div class="input-group">
<input class="form-control" id="txtSkipRepeated" type="text" value="--" readonly style="background-color: #fff;">
<input class="form-control" id="txtSkipRepeated" type="text" value="--" readonly >
<div class="input-group-btn">
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-expanded="false" id="dropdownButtonSkipRepeated">
<span class="fa fa-caret-down"></span></button>
@@ -361,7 +377,7 @@
<!-- New Device -->
<div class="form-group">
<label class="col-sm-5 control-label">New Device:</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_EveandAl_NewDevice'];?>:</label>
<div class="col-sm-7" style="padding-top:6px;">
<input class="checkbox orange hidden" id="chkNewDevice" type="checkbox">
</div>
@@ -369,7 +385,7 @@
<!-- Archived -->
<div class="form-group">
<label class="col-sm-5 control-label">Archived:</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_EveandAl_Archived'];?>:</label>
<div class="col-sm-7" style="padding-top:6px;">
<input class="checkbox blue hidden" id="chkArchived" type="checkbox">
</div>
@@ -377,7 +393,7 @@
<!-- Randomized MAC -->
<div class="form-group" >
<label class="col-sm-5 control-label">Random MAC:</label>
<label class="col-sm-5 control-label"><?php echo $pia_lang['DevDetail_EveandAl_RandomMAC'];?>:</label>
<div class="col-sm-7" style="padding-top:6px;">
<span id="iconRandomMACinactive" data-toggle="tooltip" data-placement="right" title="Random MAC is Inactive">
<i style="font-size: 24px;" class="text-gray glyphicon glyphicon-random"></i> &nbsp &nbsp </span>
@@ -385,7 +401,7 @@
<span id="iconRandomMACactive" data-toggle="tooltip" data-placement="right" title="Random MAC is Active" class="hidden">
<i style="font-size: 24px;" class="text-yellow glyphicon glyphicon-random"></i> &nbsp &nbsp </span>
<a href="https://github.com/pucherot/Pi.Alert/blob/main/docs/RAMDOM_MAC.md" target="_blank" style="color: #777;">
<a href="https://github.com/leiweibau/Pi.Alert/blob/main/docs/RAMDOM_MAC.md" target="_blank" style="color: #777;">
<i class="fa fa-info-circle"></i> </a>
</div>
</div>
@@ -397,11 +413,13 @@
<div class="col-xs-12">
<div class="pull-right">
<button type="button" class="btn btn-default pa-btn pa-btn-delete" style="margin-left:0px;"
id="btnDelete" onclick="askDeleteDevice()"> Delete Device </button>
id="btnDeleteEvents" onclick="askDeleteDeviceEvents()"> <?php echo $pia_lang['DevDetail_button_DeleteEvents'];?> </button>
<button type="button" class="btn btn-default pa-btn pa-btn-delete" style="margin-left:0px;"
id="btnDelete" onclick="askDeleteDevice()"> <?php echo $pia_lang['DevDetail_button_Delete'];?> </button>
<button type="button" class="btn btn-default pa-btn" style="margin-left:6px;"
id="btnRestore" onclick="getDeviceData(true)"> Reset Changes </button>
id="btnRestore" onclick="getDeviceData(true)"> <?php echo $pia_lang['DevDetail_button_Reset'];?> </button>
<button type="button" disabled class="btn btn-primary pa-btn" style="margin-left:6px; "
id="btnSave" onclick="setDeviceData()" > Save </button>
id="btnSave" onclick="setDeviceData()" > <?php echo $pia_lang['DevDetail_button_Save'];?> </button>
</div>
</div>
@@ -415,17 +433,97 @@
<table id="tableSessions" class="table table-bordered table-hover table-striped ">
<thead>
<tr>
<th>Order</th>
<th>Connection</th>
<th>Disconnection</th>
<th>Duration</th>
<th>IP</th>
<th>Additional info</th>
<th><?php echo $pia_lang['DevDetail_SessionTable_Order'];?></th>
<th><?php echo $pia_lang['DevDetail_SessionTable_Connection'];?></th>
<th><?php echo $pia_lang['DevDetail_SessionTable_Disconnection'];?></th>
<th><?php echo $pia_lang['DevDetail_SessionTable_Duration'];?></th>
<th><?php echo $pia_lang['DevDetail_SessionTable_IP'];?></th>
<th><?php echo $pia_lang['DevDetail_SessionTable_Additionalinfo'];?></th>
</tr>
</thead>
</table>
</div>
<!-- tab page 5 ------------------------------------------------------------ -->
<div class="tab-pane fade" id="panNmap">
<?php
if ($_REQUEST['mac'] == 'Internet') {
?>
<h4 class="">Online Speedtest</h4>
<div style="width:100%; text-align: center; margin-bottom: 50px;">
<button type="button" id="speedtestcli" class="btn btn-default pa-btn" style="margin: auto;" onclick="speedtestcli()">Start Speedtest</button>
</div>
<script>
function speedtestcli() {
$( "#scanoutput" ).empty();
$.ajax({
method: "POST",
url: "./php/server/speedtestcli.php",
beforeSend: function() { $('#scanoutput').addClass("ajax_scripts_loading"); },
complete: function() { $('#scanoutput').removeClass("ajax_scripts_loading"); },
success: function(data, textStatus) {
$("#scanoutput").html(data);
}
})
}
</script>
<?php
}
?>
<h4 class="">Nmap Scans</h4>
<div style="width:100%; text-align: center;">
<script>
setTimeout(function(){
document.getElementById('piamanualnmap_fast').innerHTML='<?php echo $pia_lang['DevDetail_Nmap_buttonFast'];?> (' + document.getElementById('txtLastIP').value +')';
document.getElementById('piamanualnmap_normal').innerHTML='<?php echo $pia_lang['DevDetail_Nmap_buttonDefault'];?> (' + document.getElementById('txtLastIP').value +')';
document.getElementById('piamanualnmap_detail').innerHTML='<?php echo $pia_lang['DevDetail_Nmap_buttonDetail'];?> (' + document.getElementById('txtLastIP').value +')';
}, 2000);
</script>
<button type="button" id="piamanualnmap_fast" class="btn btn-default pa-btn" style="margin: auto;" onclick="manualnmapscan(document.getElementById('txtLastIP').value, 'fast')">Loading...</button>
<button type="button" id="piamanualnmap_normal" class="btn btn-default pa-btn" style="margin: auto;" onclick="manualnmapscan(document.getElementById('txtLastIP').value, 'normal')">Loading...</button>
<button type="button" id="piamanualnmap_detail" class="btn btn-default pa-btn" style="margin: auto;" onclick="manualnmapscan(document.getElementById('txtLastIP').value, 'detail')">Loading...</button>
<div style="margin-top: 20px; text-align: left;">
<ul style="padding:20px;">
<li><?php echo $pia_lang['DevDetail_Nmap_buttonFast_text'];?></li>
<li><?php echo $pia_lang['DevDetail_Nmap_buttonDefault_text'];?></li>
<li><?php echo $pia_lang['DevDetail_Nmap_buttonDetail_text'];?></li>
</ul>
</div>
</div>
<div id="scanoutput" style="margin-top: 30px;"></div>
<script>
function manualnmapscan(targetip, mode) {
$( "#scanoutput" ).empty();
$.ajax({
method: "POST",
url: "./php/server/nmap_scan.php",
data: { scan: targetip, mode: mode },
beforeSend: function() { $('#scanoutput').addClass("ajax_scripts_loading"); },
complete: function() { $('#scanoutput').removeClass("ajax_scripts_loading"); },
success: function(data, textStatus) {
$("#scanoutput").html(data);
}
})
}
</script>
</div>
<!-- ----------------------------------------------------------------------- -->
<!-- tab page 3 ------------------------------------------------------------ -->
<div class="tab-pane fade table-responsive" id="panPresence">
@@ -449,7 +547,7 @@
<div class="text-center">
<label>
<input class="checkbox blue hidden" id="chkHideConnectionEvents" type="checkbox" checked>
Hide Connection Events
<?php echo $pia_lang['DevDetail_Events_CheckBox'];?>
</label>
</div>
@@ -477,7 +575,6 @@
</div>
<!-- /.row -->
<!-- ----------------------------------------------------------------------- -->
</section>
<!-- /.content -->
</div>
@@ -505,7 +602,14 @@
<link rel="stylesheet" href="lib/AdminLTE/bower_components/fullcalendar/dist/fullcalendar.print.min.css" media="print">
<script src="lib/AdminLTE/bower_components/moment/moment.js"></script>
<script src="lib/AdminLTE/bower_components/fullcalendar/dist/fullcalendar.min.js"></script>
<script src="lib/AdminLTE/bower_components/fullcalendar/dist/locale-all.js"></script>
<!-- Dark-Mode Patch -->
<?php
if ($ENABLED_DARKMODE === True) {
echo '<link rel="stylesheet" href="css/dark-patch-cal.css">';
}
?>
<!-- page script ----------------------------------------------------------- -->
<script>
@@ -585,7 +689,6 @@ function main () {
// Read Cookies
devicesList = getCookie('devicesList');
deleteCookie ('devicesList');
if (devicesList != '') {
devicesList = JSON.parse (devicesList);
} else {
@@ -777,7 +880,14 @@ function initializeDatatables () {
processing: '<table><td width="130px" align="middle">Loading...</td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data'
emptyTable: 'No data',
"lengthMenu": "<?php echo $pia_lang['Events_Tablelenght'];?>",
"search": "<?php echo $pia_lang['Events_Searchbox'];?>: ",
"paginate": {
"next": "<?php echo $pia_lang['Events_Table_nav_next'];?>",
"previous": "<?php echo $pia_lang['Events_Table_nav_prev'];?>"
},
"info": "<?php echo $pia_lang['Events_Table_info'];?>",
}
});
@@ -809,7 +919,14 @@ function initializeDatatables () {
processing: '<table><td width="130px" align="middle">Loading...</td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data'
emptyTable: 'No data',
"lengthMenu": "<?php echo $pia_lang['Events_Tablelenght'];?>",
"search": "<?php echo $pia_lang['Events_Searchbox'];?>: ",
"paginate": {
"next": "<?php echo $pia_lang['Events_Table_nav_next'];?>",
"previous": "<?php echo $pia_lang['Events_Table_nav_prev'];?>"
},
"info": "<?php echo $pia_lang['Events_Table_info'];?>",
}
});
@@ -848,7 +965,7 @@ function initializeCalendar () {
slotLabelInterval : '04:00:00',
slotLabelFormat : 'H:mm',
timeFormat : 'H:mm',
locale : '<?php echo $pia_lang['Presence_CalHead_lang'];?>',
header: {
left : 'prev,next today',
center : 'title',
@@ -859,14 +976,14 @@ function initializeCalendar () {
agendaYear: {
type : 'agenda',
duration : { year: 1 },
buttonText : 'year',
buttonText : '<?php echo $pia_lang['Presence_CalHead_year'];?>',
columnHeaderFormat : ''
},
agendaMonth: {
type : 'agenda',
duration : { month: 1 },
buttonText : 'month',
buttonText : '<?php echo $pia_lang['Presence_CalHead_month'];?>',
columnHeaderFormat : 'D'
}
},
@@ -1027,7 +1144,7 @@ function getDeviceData (readAllData=false) {
}
// Status
$('#deviceStatus').html (deviceData['dev_Status']);
$('#deviceStatus').html (deviceData['dev_Status'].replace('-', ''));
switch (deviceData['dev_Status']) {
case 'On-line': icon='fa fa-check'; color='text-green'; break;
case 'Off-line': icon='fa fa-close'; color='text-gray'; break;
@@ -1058,6 +1175,13 @@ function getDeviceData (readAllData=false) {
mac =deviceData['dev_MAC'];
// update the mac parameter in the URL, this makes the selected device persistent when the page is reloaded
var searchParams = new URLSearchParams(window.location.search);
searchParams.set("mac", mac);
var newRelativePathQuery = window.location.pathname + '?' + searchParams.toString();
history.pushState(null, '', newRelativePathQuery);
getSessionsPresenceEvents();
$('#txtMAC').val (deviceData['dev_MAC']);
$('#txtName').val (deviceData['dev_Name']);
$('#txtOwner').val (deviceData['dev_Owner']);
@@ -1072,7 +1196,7 @@ function getDeviceData (readAllData=false) {
$('#txtFirstConnection').val (deviceData['dev_FirstConnection']);
$('#txtLastConnection').val (deviceData['dev_LastConnection']);
$('#txtLastIP').val (deviceData['dev_LastIP']);
$('#txtStatus').val (deviceData['dev_Status']);
$('#txtStatus').val (deviceData['dev_Status'].replace('-', ''));
if (deviceData['dev_StaticIP'] == 1) {$('#chkStaticIP').iCheck('check');} else {$('#chkStaticIP').iCheck('uncheck');}
$('#txtScanCycle').val (deviceData['dev_ScanCycle'] +' min');
@@ -1224,6 +1348,33 @@ function skipNotifications () {
activateSaveRestoreData();
}
// -----------------------------------------------------------------------------
function askDeleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device Events
showModalWarning ('<?php echo $pia_lang['DevDetail_button_DeleteEvents'];?>', '<?php echo $pia_lang['DevDetail_button_DeleteEvents_Warning'];?>',
'<?php echo $pia_lang['Gen_Cancel'];?>', '<?php echo $pia_lang['Gen_Delete'];?>', 'deleteDeviceEvents');
}
function deleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Delete device events
$.get('php/server/devices.php?action=deleteDeviceEvents&mac='+ mac, function(msg) {
showMessage (msg);
});
// Deactivate controls
$('#panDetails :input').attr('disabled', true);
}
// -----------------------------------------------------------------------------
function askDeleteDevice () {
// Check MAC
@@ -1256,13 +1407,16 @@ function deleteDevice () {
// -----------------------------------------------------------------------------
function getSessionsPresenceEvents () {
// Check MAC in url
var urlParams = new URLSearchParams(window.location.search);
mac = urlParams.get ('mac');
// Define Sessions datasource and query dada
$('#tableSessions').DataTable().ajax.url('php/server/events.php?action=getDeviceSessions&mac=' + mac +'&period='+ period).load();
// Define Presence datasource and query data
$('#calendar').fullCalendar('removeEventSources');
$('#calendar').fullCalendar('addEventSource',
{ url: 'php/server/events.php?action=getDevicePresence&mac=' + mac +'&period='+ period });
{ url: 'php/server/events.php?action=getDevicePresence&mac=' + mac});
// Query events
getDeviceEvents();

View File

@@ -8,7 +8,16 @@
#--------------------------------------------------------------------------- -->
<?php
session_start();
if ($_SESSION["login"] != 1)
{
header('Location: /pialert/index.php');
exit;
}
require 'php/templates/header.php';
require 'php/templates/graph.php';
?>
<!-- Page ------------------------------------------------------------------ -->
@@ -17,7 +26,7 @@
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
Devices
<?php echo $pia_lang['Device_Title'];?>
</h1>
</section>
@@ -26,74 +35,105 @@
<!-- top small box 1 ------------------------------------------------------- -->
<div class="row">
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('all');">
<div class="small-box bg-aqua pa-small-box-aqua pa-small-box-2">
<div class="inner"> <h3 id="devicesAll"> -- </h3> </div>
<div class="icon"> <i class="fa fa-laptop text-aqua-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> All Devices <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-aqua">
<div class="inner"><h3 id="devicesAll"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Device_Shortcut_AllDevices'];?></p>
</div>
<div class="icon"><i class="fa fa-laptop text-aqua-40"></i></div>
</div>
</a>
</div>
<!-- top small box 2 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('connected');">
<div class="small-box bg-green pa-small-box-green pa-small-box-2">
<div class="inner"> <h3 id="devicesConnected"> -- </h3> </div>
<div class="icon"> <i class="fa fa-plug text-green-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Connected <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-green">
<div class="inner"><h3 id="devicesConnected"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Device_Shortcut_Connected'];?></p>
</div>
<div class="icon"><i class="fa fa-plug text-green-40"></i></div>
</div>
</a>
</div>
<!-- top small box 3 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('favorites');">
<div class="small-box bg-yellow pa-small-box-yellow pa-small-box-2">
<div class="inner"> <h3 id="devicesFavorites"> -- </h3> </div>
<div class="icon"> <i class="fa fa-star text-yellow-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Favorites <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-yellow">
<div class="inner"><h3 id="devicesFavorites"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Device_Shortcut_Favorites'];?></p>
</div>
<div class="icon"><i class="fa fa-star text-yellow-40"></i></div>
</div>
</a>
</div>
<!-- top small box 4 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('new');">
<div class="small-box bg-yellow pa-small-box-yellow pa-small-box-2">
<div class="inner"> <h3 id="devicesNew"> -- </h3> </div>
<div class="icon"> <i class="ion ion-plus-round text-yellow-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> New Devices <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-yellow">
<div class="inner"><h3 id="devicesNew"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Device_Shortcut_NewDevices'];?></p>
</div>
<div class="icon"><i class="ion ion-plus-round text-yellow-40"></i></div>
</div>
</a>
</div>
<!-- top small box 5 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('down');">
<div class="small-box bg-red pa-small-box-red pa-small-box-2">
<div class="inner"> <h3 id="devicesDown"> -- </h3> </div>
<div class="icon"> <i class="fa fa-warning text-red-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Down Alerts <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-red">
<div class="inner"><h3 id="devicesDown"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Device_Shortcut_DownAlerts'];?></p>
</div>
<div class="icon"><i class="fa fa-warning text-red-40"></i></div>
</div>
</a>
</div>
<!-- top small box 6 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('archived');">
<div class="small-box bg-gray pa-small-box-gray pa-small-box-2">
<div class="inner"> <h3 id="devicesArchived"> -- </h3> </div>
<div class="icon"> <i class="fa fa-eye-slash text-gray-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Archived <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-gray top_small_box_gray_text">
<div class="inner"><h3 id="devicesArchived"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Device_Shortcut_Archived'];?></p>
</div>
<div class="icon"><i class="fa fa-eye-slash text-gray-40"></i></div>
</div>
</a>
</div>
</div>
<!-- /.row -->
<!-- Activity Chart ------------------------------------------------------- -->
<div class="row">
<div class="col-md-12">
<div class="box" id="clients">
<div class="box-header with-border">
<h3 class="box-title"><?php echo $pia_lang['Device_Shortcut_OnlineChart'];?> </h3>
</div>
<div class="box-body">
<div class="chart">
<script src="lib/AdminLTE/bower_components/chart.js/Chart.js"></script>
<canvas id="OnlineChart" style="width:100%; height: 150px; margin-bottom: 15px;"></canvas>
</div>
</div>
<!-- /.box-body -->
</div>
</div>
</div>
<script src="js/graph_online_history.js"></script>
<script>
var pia_js_online_history_time = [<?php pia_graph_devices_data($Pia_Graph_Device_Time); ?>];
var pia_js_online_history_ondev = [<?php pia_graph_devices_data($Pia_Graph_Device_Online); ?>];
var pia_js_online_history_dodev = [<?php pia_graph_devices_data($Pia_Graph_Device_Down); ?>];
var pia_js_online_history_ardev = [<?php pia_graph_devices_data($Pia_Graph_Device_Arch); ?>];
pia_draw_graph_online_history(pia_js_online_history_time, pia_js_online_history_ondev, pia_js_online_history_dodev, pia_js_online_history_ardev);
</script>
<!-- datatable ------------------------------------------------------------- -->
<div class="row">
@@ -110,19 +150,19 @@
<table id="tableDevices" class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Name</th>
<th>Owner</th>
<th>Type</th>
<th>Favorite</th>
<th>Group</th>
<th>First Session</th>
<th>Last Session</th>
<th>Last IP</th>
<th>MAC</th>
<th>Status</th>
<th>MAC</th>
<th>Last IP Order</th>
<th>Rowid</th>
<th><?php echo $pia_lang['Device_TableHead_Name'];?></th>
<th><?php echo $pia_lang['Device_TableHead_Owner'];?></th>
<th><?php echo $pia_lang['Device_TableHead_Type'];?></th>
<th><?php echo $pia_lang['Device_TableHead_Favorite'];?></th>
<th><?php echo $pia_lang['Device_TableHead_Group'];?></th>
<th><?php echo $pia_lang['Device_TableHead_FirstSession'];?></th>
<th><?php echo $pia_lang['Device_TableHead_LastSession'];?></th>
<th><?php echo $pia_lang['Device_TableHead_LastIP'];?></th>
<th><?php echo $pia_lang['Device_TableHead_MAC'];?></th>
<th><?php echo $pia_lang['Device_TableHead_Status'];?></th>
<th><?php echo $pia_lang['Device_TableHead_MAC'];?></th>
<th><?php echo $pia_lang['Device_TableHead_LastIPOrder'];?></th>
<th><?php echo $pia_lang['Device_TableHead_Rowid'];?></th>
</tr>
</thead>
</table>
@@ -197,12 +237,19 @@ function main () {
// -----------------------------------------------------------------------------
function initializeDatatable () {
// If the device has a small width (mobile) only show name, ip, and status columns.
if (window.screen.width < 400) {
var tableColumnShow = [10,11,12,1,2,3,4,5,6,8];
} else {
var tableColumnShow = [10, 11, 12];
};
var table=
$('#tableDevices').DataTable({
'paging' : true,
'lengthChange' : true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, '<?php echo $pia_lang['Device_Tablelenght_all'];?>']],
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : false,
@@ -213,7 +260,7 @@ function initializeDatatable () {
// 'order' : [[3,'desc'], [0,'asc']],
'columnDefs' : [
{visible: false, targets: [10, 11, 12] },
{visible: false, targets: tableColumnShow },
{className: 'text-center', targets: [3, 8, 9] },
{width: '80px', targets: [5, 6] },
{width: '0px', targets: 9 },
@@ -263,7 +310,7 @@ function initializeDatatable () {
default: color='aqua'; break;
};
$(td).html ('<a href="deviceDetails.php?mac='+ rowData[10] +'" class="badge bg-'+ color +'">'+ cellData +'</a>');
$(td).html ('<a href="deviceDetails.php?mac='+ rowData[10] +'" class="badge bg-'+ color +'">'+ cellData.replace('-', '') +'</a>');
} },
],
@@ -271,7 +318,14 @@ function initializeDatatable () {
'processing' : true,
'language' : {
processing: '<table> <td width="130px" align="middle">Loading...</td><td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td> </table>',
emptyTable: 'No data'
emptyTable: 'No data',
"lengthMenu": "<?php echo $pia_lang['Device_Tablelenght'];?>",
"search": "<?php echo $pia_lang['Device_Searchbox'];?>: ",
"paginate": {
"next": "<?php echo $pia_lang['Device_Table_nav_next'];?>",
"previous": "<?php echo $pia_lang['Device_Table_nav_prev'];?>"
},
"info": "<?php echo $pia_lang['Device_Table_info'];?>",
}
});
@@ -288,6 +342,7 @@ function initializeDatatable () {
$('#tableDevices').on( 'search.dt', function () {
setCookie ('devicesList', JSON.stringify (table.column(12, { 'search': 'applied' }).data().toArray()) );
} );
};
@@ -320,13 +375,13 @@ function getDevicesList (status) {
// Define color & title for the status selected
switch (deviceStatus) {
case 'all': tableTitle = 'All Devices'; color = 'aqua'; break;
case 'connected': tableTitle = 'Connected Devices'; color = 'green'; break;
case 'favorites': tableTitle = 'Favorites'; color = 'yellow'; break;
case 'new': tableTitle = 'New Devices'; color = 'yellow'; break;
case 'down': tableTitle = 'Down Alerts'; color = 'red'; break;
case 'archived': tableTitle = 'Archived Devices'; color = 'gray'; break;
default: tableTitle = 'Devices'; color = 'gray'; break;
case 'all': tableTitle = '<?php echo $pia_lang['Device_Shortcut_AllDevices']?>'; color = 'aqua'; break;
case 'connected': tableTitle = '<?php echo $pia_lang['Device_Shortcut_Connected']?>'; color = 'green'; break;
case 'favorites': tableTitle = '<?php echo $pia_lang['Device_Shortcut_Favorites']?>'; color = 'yellow'; break;
case 'new': tableTitle = '<?php echo $pia_lang['Device_Shortcut_NewDevices']?>'; color = 'yellow'; break;
case 'down': tableTitle = '<?php echo $pia_lang['Device_Shortcut_DownAlerts']?>'; color = 'red'; break;
case 'archived': tableTitle = '<?php echo $pia_lang['Device_Shortcut_Archived']?>'; color = 'gray'; break;
default: tableTitle = '<?php echo $pia_lang['Device_Shortcut_Devices']?>'; color = 'gray'; break;
}
// Set title and color

View File

@@ -8,6 +8,14 @@
#--------------------------------------------------------------------------- -->
<?php
session_start();
if ($_SESSION["login"] != 1)
{
header('Location: /pialert/index.php');
exit;
}
require 'php/templates/header.php';
?>
@@ -17,17 +25,17 @@
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
Events
<?php echo $pia_lang['Events_Title'];?>
</h1>
<!-- period selector -->
<span class="breadcrumb" style="top: 0px;">
<select class="form-control" id="period" onchange="javascript: periodChanged();">
<option value="1 day">Today</option>
<option value="7 days">Last Week</option>
<option value="1 month" selected>Last Month</option>
<option value="1 year">Last Year</option>
<option value="100 years">All info</option>
<option value="1 day"><?php echo $pia_lang['Events_Periodselect_today'];?></option>
<option value="7 days"><?php echo $pia_lang['Events_Periodselect_LastWeek'];?></option>
<option value="1 month" selected><?php echo $pia_lang['Events_Periodselect_LastMonth'];?></option>
<option value="1 year"><?php echo $pia_lang['Events_Periodselect_LastYear'];?></option>
<option value="100 years"><?php echo $pia_lang['Events_Periodselect_All'];?></option>
</select>
</span>
</section>
@@ -40,10 +48,11 @@
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getEvents('all');">
<div class="small-box bg-aqua pa-small-box-aqua pa-small-box-2">
<div class="inner"> <h3 id="eventsAll"> -- </h3> </div>
<div class="icon"> <i class="fa fa-bolt text-aqua-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> All events <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-aqua">
<div class="inner"> <h3 id="eventsAll"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Events_Shortcut_AllEvents'];?></p>
</div>
<div class="icon"> <i class="fa fa-bolt text-aqua-40"></i> </div>
</div>
</a>
</div>
@@ -51,10 +60,11 @@
<!-- top small box --------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getEvents('sessions');">
<div class="small-box bg-green pa-small-box-green pa-small-box-2">
<div class="inner"> <h3 id="eventsSessions"> -- </h3> </div>
<div class="icon"> <i class="fa fa-plug text-green-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Sessions <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-green">
<div class="inner"> <h3 id="eventsSessions"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Events_Shortcut_Sessions'];?></p>
</div>
<div class="icon"> <i class="fa fa-plug text-green-40"></i> </div>
</div>
</a>
</div>
@@ -62,10 +72,11 @@
<!-- top small box --------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getEvents('missing');">
<div class="small-box bg-yellow pa-small-box-yellow pa-small-box-2">
<div class="inner"> <h3 id="eventsMissing"> -- </h3> </div>
<div class="icon"> <i class="fa fa-exchange text-yellow-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Missing Sessions <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-yellow">
<div class="inner"> <h3 id="eventsMissing"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Events_Shortcut_MissSessions'];?></p>
</div>
<div class="icon"> <i class="fa fa-exchange text-yellow-40"></i> </div>
</div>
</a>
</div>
@@ -73,10 +84,11 @@
<!-- top small box --------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getEvents('voided');">
<div class="small-box bg-yellow pa-small-box-yellow pa-small-box-2">
<div class="inner"> <h3 id="eventsVoided"> -- </h3> </div>
<div class="icon text-aqua-20"> <i class="fa fa-exclamation-circle text-yellow-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Voided Sessions <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-yellow">
<div class="inner"> <h3 id="eventsVoided"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Events_Shortcut_VoidSessions'];?></p>
</div>
<div class="icon"> <i class="fa fa-exclamation-circle text-yellow-40"></i> </div>
</div>
</a>
</div>
@@ -84,10 +96,11 @@
<!-- top small box --------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getEvents('new');">
<div class="small-box bg-yellow pa-small-box-yellow pa-small-box-2">
<div class="inner"> <h3 id="eventsNewDevices"> -- </h3> </div>
<div class="icon"> <i class="ion ion-plus-round text-yellow-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> New Devices <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-yellow">
<div class="inner"> <h3 id="eventsNewDevices"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Events_Shortcut_NewDevices'];?></p>
</div>
<div class="icon"> <i class="ion ion-plus-round text-yellow-40"></i> </div>
</div>
</a>
</div>
@@ -95,10 +108,11 @@
<!-- top small box --------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getEvents('down');">
<div class="small-box bg-red pa-small-box-red pa-small-box-2">
<div class="inner"> <h3 id="eventsDown"> -- </h3> </div>
<div class="icon"> <i class="fa fa-warning text-red-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Down Alerts <i class="fa fa-arrow-circle-right"></i> </div>
<div class="small-box bg-red">
<div class="inner"> <h3 id="eventsDown"> -- </h3>
<p class="infobox_label"><?php echo $pia_lang['Events_Shortcut_DownAlerts'];?></p>
</div>
<div class="icon"> <i class="fa fa-warning text-red-40"></i> </div>
</div>
</a>
</div>
@@ -121,18 +135,18 @@
<table id="tableEvents" class="table table-bordered table-hover table-striped ">
<thead>
<tr>
<th>Order</th>
<th>Device</th>
<th>Owner</th>
<th>Date</th>
<th>Event Type</th>
<th>Connection</th>
<th>Disconnection</th>
<th>Duration</th>
<th>Duration Order</th>
<th>IP</th>
<th>IP Order</th>
<th>Additional Info</th>
<th><?php echo $pia_lang['Events_TableHead_Order'];?></th>
<th><?php echo $pia_lang['Events_TableHead_Device'];?></th>
<th><?php echo $pia_lang['Events_TableHead_Owner'];?></th>
<th><?php echo $pia_lang['Events_TableHead_Date'];?></th>
<th><?php echo $pia_lang['Events_TableHead_EventType'];?></th>
<th><?php echo $pia_lang['Events_TableHead_Connection'];?></th>
<th><?php echo $pia_lang['Events_TableHead_Disconnection'];?></th>
<th><?php echo $pia_lang['Events_TableHead_Duration'];?></th>
<th><?php echo $pia_lang['Events_TableHead_DurationOrder'];?></th>
<th><?php echo $pia_lang['Events_TableHead_IP'];?></th>
<th><?php echo $pia_lang['Events_TableHead_IPOrder'];?></th>
<th><?php echo $pia_lang['Events_TableHead_AdditionalInfo'];?></th>
</tr>
</thead>
</table>
@@ -245,7 +259,14 @@ function initializeDatatable () {
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle">Loading...</td><td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td></table>',
emptyTable: 'No data'
emptyTable: 'No data',
"lengthMenu": "<?php echo $pia_lang['Events_Tablelenght'];?>",
"search": "<?php echo $pia_lang['Events_Searchbox'];?>: ",
"paginate": {
"next": "<?php echo $pia_lang['Events_Table_nav_next'];?>",
"previous": "<?php echo $pia_lang['Events_Table_nav_prev'];?>"
},
"info": "<?php echo $pia_lang['Events_Table_info'];?>",
}
});
@@ -297,13 +318,13 @@ function getEvents (p_eventsType) {
// Define color & title for the status selected
switch (eventsType) {
case 'all': tableTitle = 'All Events'; color = 'aqua'; sesionCols = false; break;
case 'sessions': tableTitle = 'Sessions'; color = 'green'; sesionCols = true; break;
case 'missing': tableTitle = 'Missing Events'; color = 'yellow'; sesionCols = true; break;
case 'voided': tableTitle = 'Voided Events'; color = 'yellow'; sesionCols = false; break;
case 'new': tableTitle = 'New Devices Events'; color = 'yellow'; sesionCols = false; break;
case 'down': tableTitle = 'Down Alerts'; color = 'red'; sesionCols = false; break;
default: tableTitle = 'Events'; boxClass = ''; sesionCols = false; break;
case 'all': tableTitle = '<?php echo $pia_lang['Events_Shortcut_AllEvents'];?>'; color = 'aqua'; sesionCols = false; break;
case 'sessions': tableTitle = '<?php echo $pia_lang['Events_Shortcut_Sessions'];?>'; color = 'green'; sesionCols = true; break;
case 'missing': tableTitle = '<?php echo $pia_lang['Events_Shortcut_MissSessions'];?>'; color = 'yellow'; sesionCols = true; break;
case 'voided': tableTitle = '<?php echo $pia_lang['Events_Shortcut_VoidSessions'];?>'; color = 'yellow'; sesionCols = false; break;
case 'new': tableTitle = '<?php echo $pia_lang['Events_Shortcut_NewDevices'];?>'; color = 'yellow'; sesionCols = false; break;
case 'down': tableTitle = '<?php echo $pia_lang['Events_Shortcut_DownAlerts'];?>'; color = 'red'; sesionCols = false; break;
default: tableTitle = '<?php echo $pia_lang['Events_Shortcut_Events'];?>'; boxClass = ''; sesionCols = false; break;
}
// Set title and color

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

BIN
front/img/boxed-bg-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

14
front/img/manifest.json Normal file
View File

@@ -0,0 +1,14 @@
{
"name": "Pi-Alert Console",
"short_name": "Pi-Alert",
"display": "standalone",
"icons": [
{
"src": "",
"sizes": "180x180",
"type": "image/png"
}
],
"theme_color": "#000",
"background_color": "#000"
}

View File

@@ -1,342 +1,159 @@
<!-- ---------------------------------------------------------------------------
# Pi.Alert
# Open Source Network Guard / WIFI & LAN intrusion detector
#
# devices.php - Front module. Devices list page
#-------------------------------------------------------------------------------
# Puche 2021 pi.alert.application@gmail.com GNU GPLv3
#--------------------------------------------------------------------------- -->
<?php
require 'php/templates/header.php';
session_start();
if ($_REQUEST['action'] == 'logout') {
session_destroy();
header('Location: /pialert/index.php');
}
// ##################################################
// ## Login Processing start
// ##################################################
$config_file = "../config/pialert.conf";
$config_file_lines = file($config_file);
// ###################################
// ## PIALERT_WEB_PROTECTION FALSE
// ###################################
$config_file_lines_bypass = array_values(preg_grep('/^PIALERT_WEB_PROTECTION\s.*/', $config_file_lines));
$protection_line = explode("=", $config_file_lines_bypass[0]);
$Pia_WebProtection = strtolower(trim($protection_line[1]));
if ($Pia_WebProtection != 'true')
{
header('Location: /pialert/devices.php');
$_SESSION["login"] = 1;
exit;
}
// ###################################
// ## PIALERT_WEB_PROTECTION TRUE
// ###################################
$config_file_lines = array_values(preg_grep('/^PIALERT_WEB_PASSWORD\s.*/', $config_file_lines));
$password_line = explode("'", $config_file_lines[0]);
$Pia_Password = $password_line[1];
if ($Pia_Password == hash('sha256',$_POST["loginpassword"]))
{
header('Location: /pialert/devices.php');
$_SESSION["login"] = 1;
}
if ($_SESSION["login"] == 1)
{
header('Location: /pialert/devices.php');
}
if ($_SESSION["login"] != 1)
{
if (file_exists('../db/setting_darkmode')) {$ENABLED_DARKMODE = True;}
if ($Pia_Password == '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92') {$login_info = 'Defaultpassword "123456" is still active';
}
// ##################################################
// ## Login Processing end
// ##################################################
?>
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Pi-Alert | Log in</title>
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<!-- 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/font-awesome.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="lib/AdminLTE/bower_components/Ionicons/css/ionicons.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="lib/AdminLTE/dist/css/AdminLTE.min.css">
<!-- iCheck -->
<link rel="stylesheet" href="lib/AdminLTE/plugins/iCheck/square/blue.css">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
<h1 id="pageTitle">
Devices
</h1>
</section>
<!-- Dark-Mode Patch -->
<?php
if ($ENABLED_DARKMODE === True) {
echo '<link rel="stylesheet" href="css/dark-patch.css">';
$BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/boxed-bg-dark.png\');"';
} else { $BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/background.png\');"';}
?>
<!-- Main content ---------------------------------------------------------- -->
<section class="content">
<!-- top small box 1 ------------------------------------------------------- -->
<div class="row">
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('all');">
<div class="small-box bg-aqua pa-small-box-aqua pa-small-box-2">
<div class="inner"> <h3 id="devicesAll"> -- </h3> </div>
<div class="icon"> <i class="fa fa-laptop text-aqua-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> All Devices <i class="fa fa-arrow-circle-right"></i> </div>
</div>
</a>
</div>
<!-- top small box 2 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('connected');">
<div class="small-box bg-green pa-small-box-green pa-small-box-2">
<div class="inner"> <h3 id="devicesConnected"> -- </h3> </div>
<div class="icon"> <i class="fa fa-plug text-green-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Connected <i class="fa fa-arrow-circle-right"></i> </div>
</div>
</a>
</div>
<!-- top small box 3 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('favorites');">
<div class="small-box bg-yellow pa-small-box-yellow pa-small-box-2">
<div class="inner"> <h3 id="devicesFavorites"> -- </h3> </div>
<div class="icon"> <i class="fa fa-star text-yellow-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Favorites <i class="fa fa-arrow-circle-right"></i> </div>
</div>
</a>
</div>
<!-- top small box 4 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('new');">
<div class="small-box bg-yellow pa-small-box-yellow pa-small-box-2">
<div class="inner"> <h3 id="devicesNew"> -- </h3> </div>
<div class="icon"> <i class="ion ion-plus-round text-yellow-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> New Devices <i class="fa fa-arrow-circle-right"></i> </div>
</div>
</a>
</div>
<!-- top small box 5 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('down');">
<div class="small-box bg-red pa-small-box-red pa-small-box-2">
<div class="inner"> <h3 id="devicesDown"> -- </h3> </div>
<div class="icon"> <i class="fa fa-warning text-red-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Down Alerts <i class="fa fa-arrow-circle-right"></i> </div>
</div>
</a>
</div>
<!-- top small box 6 ------------------------------------------------------- -->
<div class="col-lg-2 col-sm-4 col-xs-6">
<a href="#" onclick="javascript: getDevicesList('archived');">
<div class="small-box bg-gray pa-small-box-gray pa-small-box-2">
<div class="inner"> <h3 id="devicesArchived"> -- </h3> </div>
<div class="icon"> <i class="fa fa-eye-slash text-gray-20"></i> </div>
<div class="small-box-footer pa-small-box-footer"> Archived <i class="fa fa-arrow-circle-right"></i> </div>
</div>
</a>
</div>
<link rel="stylesheet" href="/front/css/offline-font.css">
</head>
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo">
<a href="/pialert/index2.php">Pi.<b>Alert</b></a>
</div>
<!-- /.login-logo -->
<div class="login-box-body">
<p class="login-box-msg">Sign in to start your session</p>
<form action="/pialert/index.php" method="post">
<div class="form-group has-feedback">
<input type="password" class="form-control" placeholder="Password" name="loginpassword">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<!-- /.row -->
<!-- datatable ------------------------------------------------------------- -->
<div class="row">
<div class="col-xs-12">
<div id="tableDevicesBox" class="box">
<!-- box-header -->
<div class="box-header">
<h3 id="tableDevicesTitle" class="box-title text-gray">Devices</h3>
</div>
<!-- table -->
<div class="box-body table-responsive">
<table id="tableDevices" class="table table-bordered table-hover table-striped">
<thead>
<tr>
<th>Name</th>
<th>Owner</th>
<th>Type</th>
<th>Favorite</th>
<th>Group</th>
<th>First Session</th>
<th>Last Session</th>
<th>Last IP</th>
<th>MAC</th>
<th>Status</th>
<th>MAC</th>
<th>Last IP Order</th>
<th>Rowid</th>
</tr>
</thead>
</table>
</div>
<!-- /.box-body -->
<div class="col-xs-8">
<div class="checkbox icheck">
<label>
<input type="checkbox" disabled> Remember Me
</label>
</div>
<!-- /.box -->
</div>
<!-- /.col -->
<div class="col-xs-4">
<button type="submit" class="btn btn-primary btn-block btn-flat">Sign In</button>
</div>
<!-- /.col -->
</div>
<!-- /.row -->
</form>
<!-- ----------------------------------------------------------------------- -->
</section>
<!-- /.content -->
</div>
<!-- /.content-wrapper -->
<!-- /.login-box-body -->
<div class="box-body" style="margin-top: 50px;">
<div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<h4><i class="icon fa fa-ban"></i>Password Alert!</h4>
<p><?php echo $login_info;?></p>
<p>To set a new password run:<br><span style="border: solid 1px yellow; padding: 2px;">./reset_password.sh yournewpassword</span><br>in the config folder.</p>
</div>
</div>
<!-- ----------------------------------------------------------------------- -->
<?php
require 'php/templates/footer.php';
?>
</div>
<!-- /.login-box -->
<!-- ----------------------------------------------------------------------- -->
<!-- Datatable -->
<link rel="stylesheet" href="lib/AdminLTE/bower_components/datatables.net-bs/css/dataTables.bootstrap.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>
<!-- page script ----------------------------------------------------------- -->
<!-- jQuery 3 -->
<script src="lib/AdminLTE/bower_components/jquery/dist/jquery.min.js"></script>
<!-- Bootstrap 3.3.7 -->
<script src="lib/AdminLTE/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- iCheck -->
<script src="lib/AdminLTE/plugins/iCheck/icheck.min.js"></script>
<script>
var deviceStatus = 'all';
var parTableRows = 'Front_Devices_Rows';
var parTableOrder = 'Front_Devices_Order';
var tableRows = 10;
var tableOrder = [[3,'desc'], [0,'asc']];
// Read parameters & Initialize components
main();
// -----------------------------------------------------------------------------
function main () {
// get parameter value
$.get('php/server/parameters.php?action=get&parameter='+ parTableRows, function(data) {
var result = JSON.parse(data);
if (Number.isInteger (result) ) {
tableRows = result;
}
// get parameter value
$.get('php/server/parameters.php?action=get&parameter='+ parTableOrder, function(data) {
var result = JSON.parse(data);
result = JSON.parse(result);
if (Array.isArray (result) ) {
tableOrder = result;
}
// Initialize components with parameters
initializeDatatable();
// query data
getDevicesTotals();
getDevicesList (deviceStatus);
});
});
}
// -----------------------------------------------------------------------------
function initializeDatatable () {
var table=
$('#tableDevices').DataTable({
'paging' : true,
'lengthChange' : true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : false,
// Parameters
'pageLength' : tableRows,
'order' : tableOrder,
// 'order' : [[3,'desc'], [0,'asc']],
'columnDefs' : [
{visible: false, targets: [10, 11, 12] },
{className: 'text-center', targets: [3, 8, 9] },
{width: '80px', targets: [5, 6] },
{width: '0px', targets: 9 },
{orderData: [11], targets: 7 },
// Device Name
{targets: [0],
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html ('<b><a href="deviceDetails.php?mac='+ rowData[10] +'" class="">'+ cellData +'</a></b>');
} },
// Favorite
{targets: [3],
'createdCell': function (td, cellData, rowData, row, col) {
if (cellData == 1){
$(td).html ('<i class="fa fa-star text-yellow" style="font-size:16px"></i>');
} else {
$(td).html ('');
}
} },
// Dates
{targets: [5, 6],
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} },
// Random MAC
{targets: [8],
'createdCell': function (td, cellData, rowData, row, col) {
if (cellData == 1){
$(td).html ('<i data-toggle="tooltip" data-placement="right" title="Random MAC" style="font-size: 16px;" class="text-yellow glyphicon glyphicon-random"></i>');
} else {
$(td).html ('');
}
} },
// Status color
{targets: [9],
'createdCell': function (td, cellData, rowData, row, col) {
switch (cellData) {
case 'Down': color='red'; break;
case 'New': color='yellow'; break;
case 'On-line': color='green'; break;
case 'Off-line': color='gray text-white'; break;
case 'Archived': color='gray text-white'; break;
default: color='aqua'; break;
};
$(td).html ('<a href="deviceDetails.php?mac='+ rowData[10] +'" class="badge bg-'+ color +'">'+ cellData +'</a>');
} },
],
// Processing
'processing' : true,
'language' : {
processing: '<table> <td width="130px" align="middle">Loading...</td><td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td> </table>',
emptyTable: 'No data'
}
$(function () {
$('input').iCheck({
checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue',
increaseArea: '20%' /* optional */
});
});
// Save cookie Rows displayed, and Parameters rows & order
$('#tableDevices').on( 'length.dt', function ( e, settings, len ) {
setParameter (parTableRows, len);
} );
$('#tableDevices').on( 'order.dt', function () {
setParameter (parTableOrder, JSON.stringify (table.order()) );
setCookie ('devicesList',JSON.stringify (table.column(12, { 'search': 'applied' }).data().toArray()) );
} );
$('#tableDevices').on( 'search.dt', function () {
setCookie ('devicesList', JSON.stringify (table.column(12, { 'search': 'applied' }).data().toArray()) );
} );
};
// -----------------------------------------------------------------------------
function getDevicesTotals () {
// stop timer
stopTimerRefreshData();
// get totals and put in boxes
$.get('php/server/devices.php?action=getDevicesTotals', function(data) {
var totalsDevices = JSON.parse(data);
$('#devicesAll').html (totalsDevices[0].toLocaleString());
$('#devicesConnected').html (totalsDevices[1].toLocaleString());
$('#devicesFavorites').html (totalsDevices[2].toLocaleString());
$('#devicesNew').html (totalsDevices[3].toLocaleString());
$('#devicesDown').html (totalsDevices[4].toLocaleString());
$('#devicesArchived').html (totalsDevices[5].toLocaleString());
// Timer for refresh data
newTimerRefreshData (getDevicesTotals);
} );
}
// -----------------------------------------------------------------------------
function getDevicesList (status) {
// Save status selected
deviceStatus = status;
// Define color & title for the status selected
switch (deviceStatus) {
case 'all': tableTitle = 'All Devices'; color = 'aqua'; break;
case 'connected': tableTitle = 'Connected Devices'; color = 'green'; break;
case 'favorites': tableTitle = 'Favorites'; color = 'yellow'; break;
case 'new': tableTitle = 'New Devices'; color = 'yellow'; break;
case 'down': tableTitle = 'Down Alerts'; color = 'red'; break;
case 'archived': tableTitle = 'Archived Devices'; color = 'gray'; break;
default: tableTitle = 'Devices'; color = 'gray'; break;
}
// Set title and color
$('#tableDevicesTitle')[0].className = 'box-title text-'+ color;
$('#tableDevicesBox')[0].className = 'box box-'+ color;
$('#tableDevicesTitle').html (tableTitle);
// Define new datasource URL and reload
$('#tableDevices').DataTable().ajax.url(
'php/server/devices.php?action=getDevicesList&status=' + deviceStatus).load();
};
</script>
</body>
</html>
<?php
}
?>

View File

@@ -0,0 +1,63 @@
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;
new Chart("OnlineChart", {
type: "bar",
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)",
}]
},
options: {
legend: {
display: true,
labels: {
fontColor: "#A0A0A0",
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
fontColor: '#A0A0A0'
},
gridLines: {
color: "rgba(0, 0, 0, 0)",
},
stacked: true,
}],
xAxes: [{
ticks: {
fontColor: '#A0A0A0',
},
gridLines: {
color: "rgba(0, 0, 0, 0)",
},
stacked: true,
}],
},
tooltips: {
mode: 'index'
}
}
});
}

View File

@@ -0,0 +1,2 @@
documentation/
composer.json

View File

@@ -0,0 +1,24 @@
language: node_js
node_js:
- 8
- 9
- 10
- 11
- 12
env:
- INSTALL=bower
- INSTALL=yarn
- INSTALL=npm
matrix:
fast_finish: true
install:
- if [ "bower" == $INSTALL ]; then yarn global add bower && bower install; fi
- if [ "yarn" == $INSTALL ]; then yarn install; fi
- if [ "npm" == $INSTALL ]; then npm install; fi
script:
- echo 'Tests must be configured'

View File

@@ -0,0 +1,312 @@
// AdminLTE Gruntfile
module.exports = function (grunt) { // jshint ignore:line
'use strict';
grunt.initConfig({
pkg : grunt.file.readJSON('package.json'),
watch : {
less : {
// Compiles less files upon saving
files: ['build/less/*.less'],
tasks: ['less:development', 'less:production', 'replace', 'notify:less']
},
js : {
// Compile js files upon saving
files: ['build/js/*.js'],
tasks: ['js', 'notify:js']
},
skins: {
// Compile any skin less files upon saving
files: ['build/less/skins/*.less'],
tasks: ['less:skins', 'less:minifiedSkins', 'notify:less']
}
},
// Notify end of tasks
notify: {
less: {
options: {
title : 'AdminLTE',
message: 'LESS finished running'
}
},
js : {
options: {
title : 'AdminLTE',
message: 'JS bundler finished running'
}
}
},
// 'less'-task configuration
// This task will compile all less files upon saving to create both AdminLTE.css and AdminLTE.min.css
less : {
// Development not compressed
development : {
files: {
// compilation.css : source.less
'dist/css/AdminLTE.css' : 'build/less/AdminLTE.less',
// AdminLTE without plugins
'dist/css/alt/AdminLTE-without-plugins.css' : 'build/less/AdminLTE-without-plugins.less',
// Separate plugins
'dist/css/alt/AdminLTE-select2.css' : 'build/less/select2.less',
'dist/css/alt/AdminLTE-fullcalendar.css' : 'build/less/fullcalendar.less',
'dist/css/alt/AdminLTE-bootstrap-social.css': 'build/less/bootstrap-social.less'
}
},
// Production compressed version
production : {
options: {
compress: true
},
files : {
// compilation.css : source.less
'dist/css/AdminLTE.min.css' : 'build/less/AdminLTE.less',
// AdminLTE without plugins
'dist/css/alt/AdminLTE-without-plugins.min.css' : 'build/less/AdminLTE-without-plugins.less',
// Separate plugins
'dist/css/alt/AdminLTE-select2.min.css' : 'build/less/select2.less',
'dist/css/alt/AdminLTE-fullcalendar.min.css' : 'build/less/fullcalendar.less',
'dist/css/alt/AdminLTE-bootstrap-social.min.css': 'build/less/bootstrap-social.less'
}
},
// Non minified skin files
skins : {
files: {
'dist/css/skins/skin-blue.css' : 'build/less/skins/skin-blue.less',
'dist/css/skins/skin-black.css' : 'build/less/skins/skin-black.less',
'dist/css/skins/skin-yellow.css' : 'build/less/skins/skin-yellow.less',
'dist/css/skins/skin-green.css' : 'build/less/skins/skin-green.less',
'dist/css/skins/skin-red.css' : 'build/less/skins/skin-red.less',
'dist/css/skins/skin-purple.css' : 'build/less/skins/skin-purple.less',
'dist/css/skins/skin-blue-light.css' : 'build/less/skins/skin-blue-light.less',
'dist/css/skins/skin-black-light.css' : 'build/less/skins/skin-black-light.less',
'dist/css/skins/skin-yellow-light.css': 'build/less/skins/skin-yellow-light.less',
'dist/css/skins/skin-green-light.css' : 'build/less/skins/skin-green-light.less',
'dist/css/skins/skin-red-light.css' : 'build/less/skins/skin-red-light.less',
'dist/css/skins/skin-purple-light.css': 'build/less/skins/skin-purple-light.less',
'dist/css/skins/_all-skins.css' : 'build/less/skins/_all-skins.less'
}
},
// Skins minified
minifiedSkins: {
options: {
compress: true
},
files : {
'dist/css/skins/skin-blue.min.css' : 'build/less/skins/skin-blue.less',
'dist/css/skins/skin-black.min.css' : 'build/less/skins/skin-black.less',
'dist/css/skins/skin-yellow.min.css' : 'build/less/skins/skin-yellow.less',
'dist/css/skins/skin-green.min.css' : 'build/less/skins/skin-green.less',
'dist/css/skins/skin-red.min.css' : 'build/less/skins/skin-red.less',
'dist/css/skins/skin-purple.min.css' : 'build/less/skins/skin-purple.less',
'dist/css/skins/skin-blue-light.min.css' : 'build/less/skins/skin-blue-light.less',
'dist/css/skins/skin-black-light.min.css' : 'build/less/skins/skin-black-light.less',
'dist/css/skins/skin-yellow-light.min.css': 'build/less/skins/skin-yellow-light.less',
'dist/css/skins/skin-green-light.min.css' : 'build/less/skins/skin-green-light.less',
'dist/css/skins/skin-red-light.min.css' : 'build/less/skins/skin-red-light.less',
'dist/css/skins/skin-purple-light.min.css': 'build/less/skins/skin-purple-light.less',
'dist/css/skins/_all-skins.min.css' : 'build/less/skins/_all-skins.less'
}
}
},
// Uglify task info. Compress the js files.
uglify: {
options : {
mangle : true,
output: {
comments: 'some'
},
},
production: {
files: {
'dist/js/adminlte.min.js': ['dist/js/adminlte.js']
}
}
},
// Concatenate JS Files
concat: {
options: {
separator: '\n\n',
banner : '/*! AdminLTE app.js\n'
+ '* ================\n'
+ '* Main JS application file for AdminLTE v2. This file\n'
+ '* should be included in all pages. It controls some layout\n'
+ '* options and implements exclusive AdminLTE plugins.\n'
+ '*\n'
+ '* @author Colorlib\n'
+ '* @support <https://github.com/ColorlibHQ/AdminLTE/issues>\n'
+ '* @version <%= pkg.version %>\n'
+ '* @repository <%= pkg.repository.url %>\n'
+ '* @license MIT <http://opensource.org/licenses/MIT>\n'
+ '*/\n\n'
+ '// Make sure jQuery has been loaded\n'
+ 'if (typeof jQuery === \'undefined\') {\n'
+ 'throw new Error(\'AdminLTE requires jQuery\')\n'
+ '}\n\n'
},
dist : {
src : [
'build/js/BoxRefresh.js',
'build/js/BoxWidget.js',
'build/js/ControlSidebar.js',
'build/js/DirectChat.js',
'build/js/PushMenu.js',
'build/js/TodoList.js',
'build/js/Tree.js',
'build/js/Layout.js',
],
dest: 'dist/js/adminlte.js'
}
},
// Replace image paths in AdminLTE without plugins
replace: {
withoutPlugins : {
src : ['dist/css/alt/AdminLTE-without-plugins.css'],
dest : 'dist/css/alt/AdminLTE-without-plugins.css',
replacements: [
{
from: '../img',
to : '../../img'
}
]
},
withoutPluginsMin: {
src : ['dist/css/alt/AdminLTE-without-plugins.min.css'],
dest : 'dist/css/alt/AdminLTE-without-plugins.min.css',
replacements: [
{
from: '../img',
to : '../../img'
}
]
}
},
// Build the documentation files
includes: {
build: {
src : ['*.html'], // Source files
dest : 'documentation/', // Destination directory
flatten: true,
cwd : 'documentation/build',
options: {
silent : true,
includePath: 'documentation/build/include'
}
}
},
// Optimize images
image: {
dynamic: {
files: [
{
expand: true,
cwd : 'build/img/',
src : ['**/*.{png,jpg,gif,svg,jpeg}'],
dest : 'dist/img/'
}
]
}
},
// Validate JS code
jshint: {
options: {
jshintrc: 'build/js/.jshintrc'
},
grunt : {
options: {
jshintrc: 'build/grunt/.jshintrc'
},
src : 'Gruntfile.js'
},
core : {
src: 'build/js/*.js'
},
demo : {
src: 'dist/js/demo.js'
},
pages : {
src: 'dist/js/pages/*.js'
}
},
jscs: {
options: {
config: 'build/js/.jscsrc'
},
core : {
src: '<%= jshint.core.src %>'
},
pages : {
src: '<%= jshint.pages.src %>'
}
},
// Validate CSS files
csslint: {
options: {
csslintrc: 'build/less/.csslintrc'
},
dist : [
'dist/css/AdminLTE.css'
]
},
// Validate Bootstrap HTML
bootlint: {
options: {
relaxerror: ['W005']
},
files : ['pages/**/*.html', '*.html']
},
// Delete images in build directory
// After compressing the images in the build/img dir, there is no need
// for them
clean: {
build: ['build/img/*']
}
});
// Load all grunt tasks
// LESS Compiler
grunt.loadNpmTasks('grunt-contrib-less');
// Watch File Changes
grunt.loadNpmTasks('grunt-contrib-watch');
// Compress JS Files
grunt.loadNpmTasks('grunt-contrib-uglify');
// Include Files Within HTML
grunt.loadNpmTasks('grunt-includes');
// Optimize images
grunt.loadNpmTasks('grunt-image');
// Validate JS code
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-jscs');
// Delete not needed files
grunt.loadNpmTasks('grunt-contrib-clean');
// Lint CSS
grunt.loadNpmTasks('grunt-contrib-csslint');
// Lint Bootstrap
grunt.loadNpmTasks('grunt-bootlint');
// Concatenate JS files
grunt.loadNpmTasks('grunt-contrib-concat');
// Notify
grunt.loadNpmTasks('grunt-notify');
// Replace
grunt.loadNpmTasks('grunt-text-replace');
// Linting task
grunt.registerTask('lint', ['jshint', 'csslint', 'bootlint']);
// JS task
grunt.registerTask('js', ['concat', 'uglify']);
// CSS Task
grunt.registerTask('css', ['less:development', 'less:production', 'replace']);
// The default task (running 'grunt' in console) is 'watch'
grunt.registerTask('default', ['watch']);
};

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014-2017 Abdullah Almsaeed
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,55 @@
Introduction
============
[![Build Status](https://img.shields.io/travis/ColorlibHQ/AdminLTE.svg)](https://travis-ci.org/ColorlibHQ/AdminLTE)
![Bower version](https://img.shields.io/bower/v/adminlte.svg)
[![npm version](https://img.shields.io/npm/v/admin-lte.svg)](https://www.npmjs.com/package/admin-lte)
[![Packagist](https://img.shields.io/packagist/v/almasaeed2010/adminlte.svg)](https://packagist.org/packages/almasaeed2010/adminlte)
[![CDNJS](https://img.shields.io/cdnjs/v/admin-lte.svg)](https://cdnjs.com/libraries/admin-lte)
**AdminLTE** -- is a fully responsive admin template. Based on **[Bootstrap 3 & 4](https://github.com/twbs/bootstrap)** framework. Highly customizable and easy to use. Fits many screen resolutions from small mobile devices to large desktops. Check out the live preview now and see for yourself.
**Download & Preview on [AdminLTE.IO](https://adminlte.io)**
### Looking for More Templates?
- **[Admin Templates](http://dashboardpack.com/)** by DashboardPack
- **[Bootstrap Templates](https://colorlib.com/wp/cat/bootstrap/)** by Colorlib
- **[Admin Dashboards](https://colorlib.com/wp/free-bootstrap-admin-dashboard-templates/)** by varios template designers and developers based on Bootstrap, Vue, React, Angular and more.
## Documentation & Installation Guide
Visit the [online documentation](https://adminlte.io/docs) for the most
updated guide.
!["AdminLTE Presentation"](https://adminlte.io/AdminLTE2.png "AdminLTE Presentation")
### Contribution
Contribution are always **welcome and recommended**! Here is how:
- Fork the repository ([here is the guide](https://help.github.com/articles/fork-a-repo/)).
- Clone to your machine ```git clone https://github.com/YOUR_USERNAME/AdminLTE.git```
- Make your changes
- Create a pull request
#### Contribution Requirements:
- When you contribute, you agree to give a non-exclusive license to AdminLTE.IO to use that contribution in any context as we (AdminLTE.IO) see appropriate.
- If you use content provided by another party, it must be appropriately licensed using an [open source](http://opensource.org/licenses) license.
- Contributions are only accepted through Github pull requests.
- Finally, contributed code must work in all supported browsers (see above for browser support).
### License
AdminLTE is an open source project by [AdminLTE.IO](https://adminlte.io) that is licensed under [MIT](http://opensource.org/licenses/MIT). AdminLTE.IO
reserves the right to change the license of future releases. Wondering what you can or can't do? View the [license guide](https://adminlte.io/docs/license).
### Legacy Releases
AdminLTE 1.x can be easily upgraded to 2.x using [this guide](https://adminlte.io/themes/AdminLTE/documentation/index.html#upgrade), but if you intend to keep using AdminLTE 1.x, you can download the latest release from the [releases](https://github.com/ColorlibHQ/AdminLTE/releases) section above.
### Change log
**For the most recent change log, visit the [releases page](https://github.com/ColorlibHQ/AdminLTE/releases).** We will add detailed release notes to each new release.
### Image Credits
- [Pixeden](http://www.pixeden.com/psd-web-elements/flat-responsive-showcase-psd)
- [Graphicsfuel](http://www.graphicsfuel.com/2013/02/13-high-resolution-blur-backgrounds/)
- [Pickaface](http://pickaface.net/)
- [Unsplash](https://unsplash.com/)
- [Uifaces](http://uifaces.com/)

View File

@@ -0,0 +1,64 @@
{
"name": "admin-lte",
"homepage": "https://adminlte.io",
"authors": [
"Abdullah Almsaeed <abdullah@almsaeedstudio.com>"
],
"description": "Admin dashboard and control panel template",
"main": [
"index2.html",
"dist/css/AdminLTE.css",
"dist/js/adminlte.js",
"build/less/AdminLTE.less"
],
"keywords": [
"css",
"js",
"html",
"template",
"admin",
"bootstrap",
"theme",
"backend",
"responsive"
],
"license": "MIT",
"ignore": [
"/.*",
"node_modules",
"bower_components",
"composer.json",
"documentation"
],
"dependencies": {
"chart.js": "^1.0",
"ckeditor": "^4.7",
"bootstrap-colorpicker": "^2.5.1",
"bootstrap": "^3.4",
"jquery": "^3.4.1",
"datatables.net": "^1.10.15",
"datatables.net-bs": "^2.1.1",
"bootstrap-datepicker": "^1.7",
"bootstrap-daterangepicker": "^2.1.25",
"moment": "^2.18.1",
"fastclick": "^1.0.6",
"Flot": "flot#^0.8.3",
"fullcalendar": "^3.4",
"inputmask": "jquery.inputmask#^3.3.7",
"ion.rangeSlider": "ionrangeslider#^2.2",
"jvectormap": "^1.2.2",
"jquery-knob": "^1.2.13",
"morris.js": "^0.5.1",
"PACE": "pace#^1.0.2",
"select2": "^4.0.7",
"jquery-slimscroll": "slimscroll#^1.3.8",
"jquery-sparkline": "^2.1.3",
"font-awesome": "^4.7",
"Ionicons": "ionicons#^2.0.1",
"jquery-ui": "^1.12.1",
"seiyria-bootstrap-slider": "^10.6.2"
},
"resolutions": {
"jquery": "^3.4.1"
}
}

View File

@@ -1,3 +0,0 @@
*.min.js
!excanvas.min.js
node_modules/

View File

@@ -1,29 +1,12 @@
{
"name": "bootstrap-colorpicker",
"main": [
"dist/css/bootstrap-colorpicker.css",
"dist/js/bootstrap-colorpicker.js"
],
"dependencies": {
"jquery": ">=1.10"
},
"ignore": [
"\\.*",
"/index.html",
"/package.json",
"/composer.json",
"/Gruntfile.js",
"/.travis.yml",
"/travis.sh",
"/server.js"
],
"homepage": "https://github.com/itsjavi/bootstrap-colorpicker",
"version": "2.5.2",
"_release": "2.5.2",
"version": "2.5.3",
"_release": "2.5.3",
"_resolution": {
"type": "version",
"tag": "2.5.2",
"commit": "d56d0d3d5bee59904d48bce3c47a0029741e10e6"
"tag": "2.5.3",
"commit": "525cd6a0aa26ae95803bbf34d231c4163136a314"
},
"_source": "https://github.com/itsjavi/bootstrap-colorpicker.git",
"_target": "^2.5.1",

View File

@@ -0,0 +1,11 @@
# EditorConfig is awesome: http://EditorConfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

View File

@@ -0,0 +1,47 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team members through their social media sites or at _git @ itsjavi.com_.
The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -0,0 +1,42 @@
# Contributing
## Support
The issue tracker is not the place for support requests. If you get stuck with bootstrap-colorpicker, it's very likely
that the fine folks at [StackOverflow](http://stackoverflow.com/) will be able to help you; simply describe the problem
you're having and provide them a link to the repo (so they know what code you're using).
## Issues
For feature requests, suggestions or ideas, add `[SUGGESTION]` before the title of the issue, for anything else follow
the following guidelines.
### Steps to submit an issue
These steps are mandatory. Issues that are not clear or are not clearly reproduceable with a live example will be closed.
- Reproduce your problem in a separated environment, like in JSFiddle,
[here is a template for it](http://jsfiddle.net/0vopxm13/157/), that you can fork in the same page.
It already includes the required JS and CSS files.
- Before posting your issue, consider adding this information:
* Expected behaviour: what should happen?
* Actual behaviour: what happens instead?
* Your context: Where it happens? In which browser and version (if applicable)?
* Plugin version (and/or commit reference).
* jQuery version you use and list of all other plugins/scripts you are using with this one and may cause some conflict.
* A link to your JSFiddle (or similar tool) demo where you reproduced the problem (if applicable).
## Pull Requests
Patches and new features are welcome!
- Prerequisites: having `node`, `npm`, `yarn` and `grunt` installed in your machine.
- After a fresh clone for your fork, you need to run `yarn install` inside the project's root folder.
- For checking your changes in the browser you can execute `node serve` and navigate to http://localhost:5000/
- Before any commit run always `grunt` inside the project's root folder, to update the dist files
(never modify them manually).
- Do not change the plugin coding style.
- Check that the index.html demos aren't broken (modify if necessary).
- Test your code at least in Chrome, Firefox and Edge.
- Any new feature should come with updated docs if applicable (a demonstration).
- Generate the `/dist` files executing `grunt` before your Pull Request.
- Push to your fork and submit the pull request.

View File

@@ -0,0 +1,51 @@
<!--
THIS TEMPLATE IS MANDATORY!!
Thank you for your contribution to bootstrap-colorpicker! Please replace {Please write here *} with your description.
Please note that issues not following this template may be potentially discarded if they are not easily reproduceable
with live examples (in case of code issues) and/or the description is not clear enough.
-->
### Brief description
{Please write here a summary of the issue}
<!--
The following sections are only mandatory for bug reports.
For feature requests and other kind of tickets, you only need to fill the first section,
optionally with additional comments and examples.
-->
### Which software are you using?
- bootstrap-colorpicker version: {Please write here}
- bootstrap version: {Please write here}
- jQuery version: {Please write here}
- Browser name and version: {Please write here}
- Operative System name an version: {Please write here}
### What's the expected or desirable behavior?
{Please write here in case of code-related issues or remove this section}
### What's the actual current behavior?
{Please write here in case of code-related issues or remove this section}
### Are there some other related issues or PRs?
{Please write here if applicable the issue numbers or remove this section}
### Steps to reproduce
{Please write here in case of code-related issues or remove this section}
<!--
Live examples are MANDATORY for code-related issues
You have a JsFiddle template here: http://jsfiddle.net/0vopxm13/157/ which is using the latest master version of the library.
-->
*Live example*: {Please write here a link to your JsFiddle example}
### Additional Comments (if any)
{Please write here}

View File

@@ -0,0 +1,38 @@
<!--
THIS TEMPLATE IS MANDATORY!!
Thank you for your contribution to bootstrap-colorpicker! Please replace {Please write here} with your description.
Please note that PRs not following this template may be potentially discarded if they are not clear enough.
-->
### Is your PR fixing an issue or introduces a new feature?
{Please write here}
### In case of fix, how this PR fixes the problem?
{Please write here only in case of fix PR or remove the section}
{Please also mention the related issue numbers you are trying to close, if applicable}
### In case of new feature, what are the benefits and use cases?
{Please write here only in case of feature PR or remove the section}
### Check list
Please mark with `x` inside the `[ ]` for anything that applies to this PR.
- [ ] All tests passed in travis-ci
- [ ] Regenerated the `dist` files via `grunt`
- [ ] All documentation examples are still working after testing them via `node serve`
- [ ] Added an example in the documentation for the newly introduced feature
- [ ] Provided an example via JsFiddle in the description of this PR
- [ ] Tested at least with latest Chrome, Firefox and Mobile (iOS Safari and/or Chrome for Android)
- [ ] This PR also introduces coding style changes (indentation, etc), in a separated commit
- [ ] The commit history is understandable and grouped into the minimum number of commits possible
- [ ] I've followed all other [`CONTRIBUTING.md`](.github/CONTRIBUTING.md#pull-requests) guidelines for Pull Requests.
### Additional Comments (if any)
{Please write here}

View File

@@ -0,0 +1,18 @@
{
"bitwise": false,
"browser": true,
"curly": true,
"eqeqeq": true,
"eqnull": true,
"esnext": true,
"immed": true,
"jquery": true,
"latedef": true,
"newcap": true,
"noarg": true,
"node": true,
"strict": false,
"trailing": true,
"undef": true,
"predef" : ["define"]
}

View File

@@ -0,0 +1,21 @@
*~
.DS_Store
.DS_Store*
ehthumbs.db
Thumbs.db
*.log
/node_modules/
/bower_components/
/nbproject/
/gh-pages/
/package-lock.json
Gruntfile.js
/build
/docs
/tests
/spec
.*
/src/docs
*.psd
*.ai
composer.json

View File

@@ -0,0 +1,13 @@
language: node_js
node_js:
- "6"
- "8"
before_script:
- npm install -g grunt-cli
- curl -o- -L https://yarnpkg.com/install.sh | bash
script:
- yarn install
- grunt --verbose
# Check that files didn't change after running grunt. It should be run before pushing any code change.
- if ! git diff --name-only --quiet -- dist docs src index.html --; then echo \"Files where modified after grunt execution!!...\"; exit 1; fi

View File

@@ -0,0 +1,188 @@
'use strict';
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
less: {
dist: {
options: {
strictMath: true,
sourceMap: true,
outputSourceFiles: true,
sourceMapURL: '<%= pkg.name %>.css.map',
sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'
},
src: 'src/less/colorpicker.less',
dest: 'dist/css/<%= pkg.name %>.css'
}
},
cssmin: {
options: {
compatibility: 'ie8',
keepSpecialComments: '*',
sourceMap: true,
advanced: false
},
dist: {
src: 'dist/css/<%= pkg.name %>.css',
dest: 'dist/css/<%= pkg.name %>.min.css'
}
},
jshint: {
options: {
jshintrc: '.jshintrc'
},
files: [
'Gruntfile.js',
'docs/docs.js',
'dist/js/<%= pkg.name %>.js'
]
},
jsbeautifier: {
options: {
js: {
braceStyle: "collapse",
breakChainedMethods: false,
e4x: false,
evalCode: false,
indentChar: " ",
indentLevel: 0,
indentSize: 2,
indentWithTabs: false,
jslintHappy: false,
keepArrayIndentation: false,
keepFunctionIndentation: false,
maxPreserveNewlines: 2,
preserveNewlines: true,
spaceBeforeConditional: true,
spaceInParen: false,
unescapeStrings: false,
wrapLineLength: 0,
endWithNewline: true
}
},
src: ['src/js/*.js', 'docs/docs.js'],
dist: ['dist/js/<%= pkg.name %>.js']
},
combine: {
js: {
input: 'src/js/colorpicker-plugin-wrapper.js',
output: 'dist/js/<%= pkg.name %>.js',
tokens: [{
token: "//@version",
string: '<%= pkg.version %>'
}, {
token: "//@colorpicker-color",
file: 'src/js/colorpicker-color.js'
}, {
token: "//@colorpicker-defaults",
file: 'src/js/colorpicker-defaults.js'
}, {
token: "//@colorpicker-component",
file: 'src/js/colorpicker-component.js'
}]
},
less: {
input: 'src/less/colorpicker.less',
output: 'src/less/colorpicker.less',
tokens: [{
token: "//@version",
string: '<%= pkg.version %>'
}]
}
},
strip_code: {
src: {
src: 'dist/js/*.js'
}
},
uglify: {
options: {
banner: '/*!\n * Bootstrap Colorpicker v<%= pkg.version %>\n' +
' * https://itsjavi.com/bootstrap-colorpicker/\n */\n'
},
dist: {
files: {
'dist/js/<%= pkg.name %>.min.js': [
'dist/js/<%= pkg.name %>.js'
]
}
}
},
watch: {
less: {
files: [
'src/less/*.less'
],
tasks: ['combine:less', 'less', 'cssmin']
},
js: {
files: [
'src/js/*.js',
'docs/docs.js'
],
tasks: ['jsbeautifier:src', 'combine:js', 'jsbeautifier:dist', 'uglify', 'jshint']
},
handlebars: {
files: [
'docs/*.hbs',
'docs/**/*.hbs',
'docs/helpers/**/*.js'
],
tasks: ['assemble']
}
},
assemble: {
options: {
assets: 'docs/assets',
helpers: ['docs/helpers/code'],
partials: ['docs/includes/**/*.hbs'],
layout: ['docs/layout.hbs'],
data: ['package.json'],
flatten: true
},
site: {
src: ['docs/pages/*.hbs'],
dest: './'
}
},
clean: {
dist: [
'dist/css/*',
'dist/js/*',
'index_new.html'
]
}
});
// Load tasks
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-jsbeautifier');
grunt.loadNpmTasks('grunt-combine');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-assemble');
grunt.loadNpmTasks('grunt-strip-code');
// Register tasks
grunt.registerTask('default', [
'clean',
'combine:less',
'less',
'cssmin',
'jsbeautifier:src',
'combine:js',
'jsbeautifier:dist',
'strip_code',
'uglify',
'assemble',
'jshint'
]);
grunt.registerTask('dev', [
'watch'
]);
};

View File

@@ -2,12 +2,12 @@
Simple and customizable colorpicker component for Twitter Bootstrap.
[![Build Status](https://api.travis-ci.org/farbelous/bootstrap-colorpicker.svg?branch=v2)](https://travis-ci.org/farbelous/bootstrap-colorpicker)
[![Build Status](https://api.travis-ci.org/farbelous/bootstrap-colorpicker.svg?branch=v2.x)](https://travis-ci.org/farbelous/bootstrap-colorpicker)
## Installation
For downloading the source code, you have many choices:
- Downloading the [latest v2 source code ZIP file](https://github.com/farbelous/bootstrap-colorpicker/archive/v2.zip)
- Downloading the [latest v2.x source code ZIP file](https://github.com/farbelous/bootstrap-colorpicker/archive/v2.x.zip)
- Cloning the source code: `git clone https://github.com/farbelous/bootstrap-colorpicker.git`
- Installing via NPM: `npm install bootstrap-colorpicker`
- Installing via Yarn: `yarn add bootstrap-colorpicker`
@@ -18,7 +18,7 @@ For downloading the source code, you have many choices:
- [Documentation and demos](https://farbelous.github.io/bootstrap-colorpicker/v2/)
## Contributing and reporting issues
If you want to contribute to the source code or report issues and suggestions, please read the [CONTRIBUTING.md](CONTRIBUTING.md) guidelines first. Some steps are mandatory in order to accept a Pull Request.
If you want to contribute to the source code or report issues and suggestions, please read the [CONTRIBUTING.md](.github/CONTRIBUTING.md) guidelines first. Some steps are mandatory in order to accept a Pull Request.
## Credits
Originally written by [Stefan Petre](http://www.eyecon.ro/)

View File

@@ -1,20 +0,0 @@
{
"name": "bootstrap-colorpicker",
"main": [
"dist/css/bootstrap-colorpicker.css",
"dist/js/bootstrap-colorpicker.js"
],
"dependencies": {
"jquery": ">=1.10"
},
"ignore": [
"\\.*",
"/index.html",
"/package.json",
"/composer.json",
"/Gruntfile.js",
"/.travis.yml",
"/travis.sh",
"/server.js"
]
}

View File

@@ -0,0 +1,15 @@
{
"name": "itsjavi/bootstrap-colorpicker",
"description": "Fancy and customizable colorpicker plugin for Twitter Bootstrap",
"license": "Apache License Version 2.0",
"authors": [
{
"name": "Javier Aguilar",
"homepage": "https://itsjavi.com"
}
],
"minimum-stability": "dev",
"require": {
"components/jquery" : ">=1.10"
}
}

View File

@@ -861,7 +861,7 @@
'keyup.colorpicker': $.proxy(this.keyup, this)
});
this.input.on({
'change.colorpicker': $.proxy(this.change, this)
'input.colorpicker': $.proxy(this.change, this)
});
if (this.component === false) {
this.element.on({
@@ -1248,7 +1248,24 @@
return false;
},
change: function(e) {
this.keyup(e);
this.color = this.createColor(this.input.val());
// Change format dynamically
// Only occurs if user choose the dynamic format by
// setting option format to false
if (this.color.origFormat && this.options.format === false) {
this.format = this.color.origFormat;
}
if (this.getValue(false) !== false) {
this.updateData();
this.updateComponent();
this.updatePicker();
}
this.element.trigger({
type: 'changeColor',
color: this.color,
value: this.input.val()
});
},
keyup: function(e) {
if ((e.keyCode === 38)) {
@@ -1261,20 +1278,8 @@
this.color.value.a = Math.round((this.color.value.a - 0.01) * 100) / 100;
}
this.update(true);
} else {
this.color = this.createColor(this.input.val());
// Change format dynamically
// Only occurs if user choose the dynamic format by
// setting option format to false
if (this.color.origFormat && this.options.format === false) {
this.format = this.color.origFormat;
}
if (this.getValue(false) !== false) {
this.updateData();
this.updateComponent();
this.updatePicker();
}
}
this.element.trigger({
type: 'changeColor',
color: this.color,

File diff suppressed because one or more lines are too long

View File

@@ -18,7 +18,7 @@
<link href="dist/css/bootstrap-colorpicker.min.css" rel="stylesheet">
<link href="docs/assets/main.css" rel="stylesheet">
<script src="//code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="dist/js/bootstrap-colorpicker.js"></script>
</head>

View File

@@ -0,0 +1,794 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Colorpicker for Twitter Bootstrap</title>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-85082661-5"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date()); gtag('config', 'UA-85082661-5');
</script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="//cdnjs.cloudflare.com/ajax/libs/octicons/3.5.0/octicons.min.css" rel="stylesheet">
<link href="dist/css/bootstrap-colorpicker.min.css" rel="stylesheet">
<link href="docs/assets/main.css" rel="stylesheet">
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="dist/js/bootstrap-colorpicker.js"></script>
</head>
<body>
<div class="container">
<div class="page-header">
<h1><i class="glyphicon glyphicon-tint"></i> Bootstrap Colorpicker 2.5.2
<small>for Twitter Bootstrap</small>
</h1>
</div>
<div class="row">
<article class="col-md-12">
<p>
Fancy and customizable colorpicker plugin for Twitter Bootstrap.
Originally written by <a
href="https://twitter.com/stefanpetre/" target="_blank">Stefan Petre</a> and maintained by
<a href="https://itsjavi.com/" target="_blank">Javi Aguilar</a> and the Github community.
</p>
<p class="alert alert-warning">
<b>NOTE</b> That this is an older version of the library documentation, please check
the project <a href="https://github.com/farbelous/bootstrap-colorpicker/blob/master/README.md">README</a>
to find the documentation for the newer and latest versions.
</p>
</article>
</div>
<div class="row">
<article class="col-md-12">
<hr>
<div class="social">
<a href="https://github.com/farbelous/bootstrap-colorpicker/" target="_blank"
class="btn btn-default btn-sm"><span class="octicon octicon-mark-github"></span>
Source code
</a>
<a href="https://github.com/farbelous/bootstrap-colorpicker/releases" target="_blank"
class="btn btn-default btn-sm">
Latest Releases
</a>
<a href="https://github.com/farbelous/bootstrap-colorpicker/archive/2.5.2.zip" target="_blank"
class="btn btn-success btn-sm"><i class="glyphicon glyphicon-download-alt"></i>
Download v2.5.2
</a>
</div>
<hr>
</article>
</div>
<div class="row">
<div class="col-md-7">
<h2>Documentation</h2>
<hr>
<p>Call the colopicker via javascript:</p>
<pre class="well">$('.sample-selector').colorpicker({ /*options...*/ });</pre>
<h3>Options</h3>
<p>
You can set colorpicker options either as a plugin parameter or data-* attributes
</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width: 100px;">Name</th>
<th style="width: 50px;">Type</th>
<th style="width: 100px;">Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>format</td>
<td>string</td>
<td>false</td>
<td>If not false, forces the color format to be hex, rgb or rgba, otherwise the format is
automatically detected.
</td>
</tr>
<tr>
<td>color</td>
<td>string</td>
<td>false</td>
<td>If not false, sets the color to this value.</td>
</tr>
<tr>
<td>container</td>
<td>string or jQuery Element</td>
<td>false</td>
<td>If not false, the picker will be contained inside this element, otherwise it will be
appended to the document body.
</td>
</tr>
<tr>
<td>component</td>
<td>string or jQuery Element</td>
<td>'.add-on, .input-group-addon'</td>
<td>Children selector for the component or element that trigger the colorpicker and which
background color will change (needs an inner &lt;i&gt; element).
</td>
</tr>
<tr>
<td>input</td>
<td>string or jQuery Element</td>
<td>'input'</td>
<td>Children selector for the input that will store the picker selected value.</td>
</tr>
<tr>
<td>hexNumberSignPrefix</td>
<td>boolean</td>
<td>true</td>
<td>If true, put a '&num;' (number sign) before hex strings.
</td>
</tr>
<tr>
<td>horizontal</td>
<td>boolean</td>
<td>false</td>
<td>If true, the hue and alpha channel bars will be rendered horizontally, above the saturation
selector.
</td>
</tr>
<tr>
<td>inline</td>
<td>boolean</td>
<td>false</td>
<td>If true, forces to show the colorpicker as an inline element.</td>
</tr>
<tr>
<td>sliders</td>
<td>object</td>
<td><abbr title='please, read the source code to see this value'>[...]</abbr></td>
<td>Vertical sliders configuration (read source code if you really need to tweak this).</td>
</tr>
<tr>
<td>slidersHorz</td>
<td>object</td>
<td><abbr title='please, read the source code to see this value'>[...]</abbr></td>
<td>Horizontal sliders configuration (read source code if you really need to tweak this).</td>
</tr>
<tr>
<td>template</td>
<td>string</td>
<td><abbr title='please, read the source code to see this value'>[...]</abbr></td>
<td>Customizes the default colorpicker HTML template.</td>
</tr>
<tr>
<td>align</td>
<td>string</td>
<td>'right'</td>
<td>By default, the colorpicker is aligned to the right of the input. If you need to switch it
to the left, set align to 'left'.
</td>
</tr>
<tr>
<td>customClass</td>
<td>string</td>
<td>null</td>
<td>Adds this class to the colorpicker widget.</td>
</tr>
<tr>
<td>colorSelectors</td>
<td>object</td>
<td>null</td>
<td>List of pre selected colors (hex format). If you choose one of these colors, the alias is returned instead of the hex
code.
</td>
</tr>
<tr>
<td>fallbackColor</td>
<td>string</td>
<td>null</td>
<td>
Fallback color string that will be applied when the color failed to be parsed.
If <var>null</var>, it will keep the current color if any.
</td>
</tr>
<tr>
<td>fallbackFormat</td>
<td>string</td>
<td>hex</td>
<td>
Fallback color format (e.g. when not specified or for alias mode, when selecting non aliased colors)
</td>
</tr>
</tbody>
</table>
<h3>jQuery API Methods</h3>
<p>General usage methods</p>
<h4>.colorpicker(options)</h4>
<p>Initializes an colorpicker.</p>
<h4>.colorpicker('getValue', defaultValue)</h4>
<p>Gets the value from the input or the data attribute (if has no input), otherwise returns the default
value, which defaults to #000000 if not specified.</p>
<h4>.colorpicker('setValue', value)</h4>
<p>Set a new value for the color picker (also updates everything). Triggers 'changeColor' event.</p>
<h4>.colorpicker('show')</h4>
<p>Show the color picker</p>
<h4>.colorpicker('hide')</h4>
<p>Hide the color picker</p>
<h4>.colorpicker('reposition')</h4>
<p>Updates the color picker's position relative to the element</p>
<h4>.colorpicker('update')</h4>
<p>Refreshes the widget colors (this is done automatically)</p>
<h4>.colorpicker('enable')</h4>
<p>Enable the color picker.</p>
<h4>.colorpicker('disable')</h4>
<p>Disable the color picker.</p>
<h4>.colorpicker('destroy')</h4>
<p>Destroys the colorpicker widget and unbind all .colorpicker events from the element and component</p>
<h4>.data('colorpicker')</h4>
<p>Access to the colorpicker API directly</p>
<h4>.data('colorpicker').color</h4>
<p>Access to the colorpicker Color object information</p>
<hr>
<h3>Color object methods</h3>
<p>Each triggered events have a color object (avaliable through event.color, see the example at the
bottom) used internally by the picker. This object has several useful methods. These are the more
commonly used:</p>
<h4>.setColor(value)</h4>
<p>Set a new color. The value is parsed and tries to do a quess on the format.</p>
<h4>.setHue(value)</h4>
<p>Set the HUE with a value between 0 and 1.</p>
<h4>.setSaturation(value)</h4>
<p>Set the saturation with a value between 0 and 1.</p>
<h4>.setBrightness(value)</h4>
<p>Set the brightness with a value between 0 and 1.</p>
<h4>.setAlpha(value)</h4>
<p>Set the transparency with a value between 0 and 1.</p>
<h4>.toRGB()</h4>
<p>Returns a hash with red, green, blue and alpha.</p>
<h4>.toHex()</h4>
<p>Returns a string with HEX format for the current color.</p>
<h4>.toHSL()</h4>
<p>Returns a hash with HSLA values.</p>
<hr>
<h3>Events</h3>
<p>The colorpicker plugin exposes some events</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width: 150px;">Event</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>create</td>
<td>This event fires immediately when the color picker is created.</td>
</tr>
<tr>
<td>showPicker</td>
<td>This event fires immediately when the color picker is displayed.</td>
</tr>
<tr>
<td>hidePicker</td>
<td>This event is fired immediately when the color picker is hidden.</td>
</tr>
<tr>
<td>changeColor</td>
<td>This event is fired when the color is changed.</td>
</tr>
<tr>
<td>disable</td>
<td>This event is fired immediately when the color picker is disabled, except if it was
initialized as disabled.
</td>
</tr>
<tr>
<td>enable</td>
<td>This event is fired immediately when the color picker is enabled, except upon
initialization.
</td>
</tr>
<tr>
<td>destroy</td>
<td>This event fires immediately when the color picker is destroyed.</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-5">
<h2>Examples</h2>
<hr>
<div class="example">
<div class="example-title">Simple input field</div>
<div class="example-content well">
<div class="example-content-widget">
<input id="cp1" type="text" class="form-control" value="#5367ce"/>
<script>
$(function () {
$('#cp1').colorpicker();
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;input id=&quot;cp1&quot; type=&quot;text&quot; class=&quot;form-control&quot; value=&quot;#5367ce&quot; &#x2F;&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp1&#39;).colorpicker();
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">As a component</div>
<div class="example-content well">
<div class="example-content-widget">
<div id="cp2" class="input-group colorpicker-component">
<input type="text" value="#00AABB" class="form-control"/>
<span class="input-group-addon"><i></i></span>
</div>
<script>
$(function () {
$('#cp2').colorpicker();
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;div id=&quot;cp2&quot; class=&quot;input-group colorpicker-component&quot;&gt;
&lt;input type=&quot;text&quot; value=&quot;#00AABB&quot; class=&quot;form-control&quot; &#x2F;&gt;
&lt;span class=&quot;input-group-addon&quot;&gt;&lt;i&gt;&lt;&#x2F;i&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp2&#39;).colorpicker();
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">With custom options</div>
<div class="example-description">Sample overriding the initial color and format</div>
<div class="example-content well">
<div class="example-content-widget">
<div id="cp3" class="input-group colorpicker-component">
<input type="text" value="#00AABB" class="form-control"/>
<span class="input-group-addon"><i></i></span>
</div>
<script>
$(function () {
$('#cp3').colorpicker({
color: '#AA3399',
format: 'rgb'
});
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;div id=&quot;cp3&quot; class=&quot;input-group colorpicker-component&quot;&gt;
&lt;input type=&quot;text&quot; value=&quot;#00AABB&quot; class=&quot;form-control&quot; &#x2F;&gt;
&lt;span class=&quot;input-group-addon&quot;&gt;&lt;i&gt;&lt;&#x2F;i&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp3&#39;).colorpicker({
color: &#39;#AA3399&#39;,
format: &#39;rgb&#39;
});
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">Working with events</div>
<div class="example-content well">
<div class="example-content-widget">
<a href="#" class="btn btn-default" id="cp4">Change background color</a>
<script>
$(function () {
$('#cp4').colorpicker().on('changeColor', function (e) {
$('body')[0].style.backgroundColor = e.color.toString('rgba');
});
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;a href=&quot;#&quot; class=&quot;btn btn-default&quot; id=&quot;cp4&quot;&gt;Change background color&lt;&#x2F;a&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp4&#39;).colorpicker().on(&#39;changeColor&#39;, function(e) {
$(&#39;body&#39;)[0].style.backgroundColor = e.color.toString(
&#39;rgba&#39;);
});
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">Transparent color support</div>
<div class="example-content well">
<div class="example-content-widget">
<input type="text" class="form-control" id="cp5"/>
<script>
$(function () {
$('#cp5').colorpicker({
color: "transparent",
format: "hex"
});
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;cp5&quot; &#x2F;&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp5&#39;).colorpicker({
color: &quot;transparent&quot;,
format: &quot;hex&quot;
});
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">Horizontal mode</div>
<div class="example-content well">
<div class="example-content-widget">
<input type="text" class="form-control" id="cp6" />
<script>
$(function () {
$('#cp6').colorpicker({
color: "#88cc33",
horizontal: true
});
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;input type=&quot;text&quot; class=&quot;form-control&quot; id=&quot;cp6&quot; &#x2F;&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp6&#39;).colorpicker({
color: &quot;#88cc33&quot;,
horizontal: true
});
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">Inline mode</div>
<div class="example-content well">
<div class="example-content-widget">
<div id="cp7" class="inl-bl"></div>
<style>
.inl-bl {
display: inline-block;
}
</style>
<script>
$(function () {
$('#cp7').colorpicker({
color:'#ffaa00',
container: true,
inline:true
});
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;div id=&quot;cp7&quot; class=&quot;inl-bl&quot;&gt;&lt;&#x2F;div&gt;
&lt;style&gt;
.inl-bl {
display: inline-block;
}
&lt;&#x2F;style&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp7&#39;).colorpicker({
color: &#39;#ffaa00&#39;,
container: true,
inline: true
});
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">Aliased color palette</div>
<div class="example-content well">
<div class="example-content-widget">
<div id="cp8" data-format="alias" class="input-group colorpicker-component">
<input type="text" value="primary" class="form-control"/>
<span class="input-group-addon"><i></i></span>
</div>
<script>
$(function () {
$('#cp8').colorpicker({
colorSelectors: {
'black': '#000000',
'white': '#ffffff',
'red': '#FF0000',
'default': '#777777',
'primary': '#337ab7',
'success': '#5cb85c',
'info': '#5bc0de',
'warning': '#f0ad4e',
'danger': '#d9534f'
}
});
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;div id=&quot;cp8&quot; data-format=&quot;alias&quot; class=&quot;input-group colorpicker-component&quot;&gt;
&lt;input type=&quot;text&quot; value=&quot;primary&quot; class=&quot;form-control&quot; &#x2F;&gt;
&lt;span class=&quot;input-group-addon&quot;&gt;&lt;i&gt;&lt;&#x2F;i&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp8&#39;).colorpicker({
colorSelectors: {
&#39;black&#39;: &#39;#000000&#39;,
&#39;white&#39;: &#39;#ffffff&#39;,
&#39;red&#39;: &#39;#FF0000&#39;,
&#39;default&#39;: &#39;#777777&#39;,
&#39;primary&#39;: &#39;#337ab7&#39;,
&#39;success&#39;: &#39;#5cb85c&#39;,
&#39;info&#39;: &#39;#5bc0de&#39;,
&#39;warning&#39;: &#39;#f0ad4e&#39;,
&#39;danger&#39;: &#39;#d9534f&#39;
}
});
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">Customized widget size</div>
<div class="example-description">Also showing the support of HTML color names</div>
<div class="example-content well">
<div class="example-content-widget">
<input id="cp9" type="text" class="form-control" value="pink"/>
<style>
.colorpicker-2x .colorpicker-saturation {
width: 200px;
height: 200px;
}
.colorpicker-2x .colorpicker-hue,
.colorpicker-2x .colorpicker-alpha {
width: 30px;
height: 200px;
}
.colorpicker-2x .colorpicker-color,
.colorpicker-2x .colorpicker-color div {
height: 30px;
}
</style>
<script>
$(function () {
$('#cp9').colorpicker({
customClass: 'colorpicker-2x',
sliders: {
saturation: {
maxLeft: 200,
maxTop: 200
},
hue: {
maxTop: 200
},
alpha: {
maxTop: 200
}
}
});
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;input id=&quot;cp9&quot; type=&quot;text&quot; class=&quot;form-control&quot; value=&quot;pink&quot; &#x2F;&gt;
&lt;style&gt;
.colorpicker-2x .colorpicker-saturation {
width: 200px;
height: 200px;
}
.colorpicker-2x .colorpicker-hue,
.colorpicker-2x .colorpicker-alpha {
width: 30px;
height: 200px;
}
.colorpicker-2x .colorpicker-color,
.colorpicker-2x .colorpicker-color div {
height: 30px;
}
&lt;&#x2F;style&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp9&#39;).colorpicker({
customClass: &#39;colorpicker-2x&#39;,
sliders: {
saturation: {
maxLeft: 200,
maxTop: 200
},
hue: {
maxTop: 200
},
alpha: {
maxTop: 200
}
}
});
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">Disabled / enabled status</div>
<div class="example-content well">
<div class="example-content-widget">
<div id="cp10" class="input-group colorpicker-component">
<input disabled type="text" value="" class="form-control"/>
<span class="input-group-addon"><i></i></span>
</div>
<br>
<p>
<a class="btn btn-sm btn-default enable-button" href="#">Enable</a>
<a class="btn btn-sm btn-default disable-button" href="#">Disable</a>
</p>
<script>
$(function () {
$(".disable-button").click(function(e) {
e.preventDefault();
$("#cp10").colorpicker('disable');
});
$(".enable-button").click(function(e) {
e.preventDefault();
$("#cp10").colorpicker('enable');
});
$('#cp10').colorpicker();
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;div id=&quot;cp10&quot; class=&quot;input-group colorpicker-component&quot;&gt;
&lt;input disabled type=&quot;text&quot; value=&quot;&quot; class=&quot;form-control&quot; &#x2F;&gt;
&lt;span class=&quot;input-group-addon&quot;&gt;&lt;i&gt;&lt;&#x2F;i&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;br&gt;
&lt;p&gt;
&lt;a class=&quot;btn btn-sm btn-default enable-button&quot; href=&quot;#&quot;&gt;Enable&lt;&#x2F;a&gt;
&lt;a class=&quot;btn btn-sm btn-default disable-button&quot; href=&quot;#&quot;&gt;Disable&lt;&#x2F;a&gt;
&lt;&#x2F;p&gt;
&lt;script&gt;
$(function() {
$(&quot;.disable-button&quot;).click(function(e) {
e.preventDefault();
$(&quot;#cp10&quot;).colorpicker(&#39;disable&#39;);
});
$(&quot;.enable-button&quot;).click(function(e) {
e.preventDefault();
$(&quot;#cp10&quot;).colorpicker(&#39;enable&#39;);
});
$(&#39;#cp10&#39;).colorpicker();
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<div class="example">
<div class="example-title">Inside a modal</div>
<div class="example-content well">
<div class="example-content-widget">
<button class="btn btn-primary btn-md" data-toggle="modal" data-target="#myModal">
Show modal
</button>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div id="cp11" class="input-group colorpicker-component">
<input type="text" value="" class="form-control"/>
<span class="input-group-addon"><i></i></span>
</div>
</div>
</div>
</div>
</div>
<script>
$(function () {
$('#cp11').colorpicker();
});
</script>
</div>
<button type="button" class="btn btn-link btn-xs example-code-toggle" data-toggle="button">View source</button>
<div class="example-code">&lt;button class=&quot;btn btn-primary btn-md&quot; data-toggle=&quot;modal&quot; data-target=&quot;#myModal&quot;&gt;
Show modal
&lt;&#x2F;button&gt;
&lt;div class=&quot;modal fade&quot; id=&quot;myModal&quot; tabindex=&quot;-1&quot; role=&quot;dialog&quot; aria-labelledby=&quot;myModalLabel&quot;
aria-hidden=&quot;true&quot;&gt;
&lt;div class=&quot;modal-dialog&quot;&gt;
&lt;div class=&quot;modal-content&quot;&gt;
&lt;div class=&quot;modal-body&quot;&gt;
&lt;div id=&quot;cp11&quot; class=&quot;input-group colorpicker-component&quot;&gt;
&lt;input type=&quot;text&quot; value=&quot;&quot; class=&quot;form-control&quot; &#x2F;&gt;
&lt;span class=&quot;input-group-addon&quot;&gt;&lt;i&gt;&lt;&#x2F;i&gt;&lt;&#x2F;span&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;
&lt;script&gt;
$(function() {
$(&#39;#cp11&#39;).colorpicker();
});
&lt;&#x2F;script&gt;</div> </div>
</div>
<script>
$(function () {
$('.example-code-toggle').on('click', function () {
$(this).parent().find('.example-code').toggle();
});
});
</script>
</div>
</div>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
{
"name": "bootstrap-colorpicker",
"version": "2.5.3",
"description": "Fancy and customizable colorpicker plugin for Twitter Bootstrap",
"main": "./dist/js/bootstrap-colorpicker.js",
"homepage": "https://itsjavi.com/bootstrap-colorpicker/",
"repository": {
"type": "git",
"url": "https://github.com/itsjavi/bootstrap-colorpicker.git"
},
"bugs": {
"url": "https://github.com/itsjavi/bootstrap-colorpicker/issues"
},
"keywords": [
"bootstrap",
"colorpicker"
],
"author": "Javier Aguilar",
"license": "Apache-2.0",
"licenses": [
{
"type": "Apache-2.0",
"url": "http://opensource.org/licenses/Apache-2.0"
}
],
"dependencies": {
"jquery": ">=1.10"
},
"scripts": {
"test": "jasmine"
},
"devDependencies": {
"grunt": "~0.4.5",
"grunt-assemble": "~0.4.0",
"grunt-combine": "~0.8.3",
"grunt-contrib-clean": "~1.0.0",
"grunt-contrib-cssmin": "~1.0.1",
"grunt-contrib-jshint": "~1.0.0",
"grunt-contrib-less": "~1.2.0",
"grunt-contrib-uglify": "~1.0.0",
"grunt-contrib-watch": "~1.0.0",
"grunt-jsbeautifier": "~0.2.10",
"grunt-strip-code": "^1.0.6",
"jasmine": "^2.6.0",
"jquery": ">=1.10",
"jsdom": "^10.1.0"
}
}

View File

@@ -102,7 +102,7 @@ var Colorpicker = function(element, options) {
'keyup.colorpicker': $.proxy(this.keyup, this)
});
this.input.on({
'change.colorpicker': $.proxy(this.change, this)
'input.colorpicker': $.proxy(this.change, this)
});
if (this.component === false) {
this.element.on({
@@ -489,7 +489,24 @@ Colorpicker.prototype = {
return false;
},
change: function(e) {
this.keyup(e);
this.color = this.createColor(this.input.val());
// Change format dynamically
// Only occurs if user choose the dynamic format by
// setting option format to false
if (this.color.origFormat && this.options.format === false) {
this.format = this.color.origFormat;
}
if (this.getValue(false) !== false) {
this.updateData();
this.updateComponent();
this.updatePicker();
}
this.element.trigger({
type: 'changeColor',
color: this.color,
value: this.input.val()
});
},
keyup: function(e) {
if ((e.keyCode === 38)) {
@@ -502,20 +519,8 @@ Colorpicker.prototype = {
this.color.value.a = Math.round((this.color.value.a - 0.01) * 100) / 100;
}
this.update(true);
} else {
this.color = this.createColor(this.input.val());
// Change format dynamically
// Only occurs if user choose the dynamic format by
// setting option format to false
if (this.color.origFormat && this.options.format === false) {
this.format = this.color.origFormat;
}
if (this.getValue(false) !== false) {
this.updateData();
this.updateComponent();
this.updatePicker();
}
}
this.element.trigger({
type: 'changeColor',
color: this.color,

File diff suppressed because it is too large Load Diff

View File

@@ -10,14 +10,14 @@
},
"ignore": [],
"homepage": "https://github.com/eternicode/bootstrap-datepicker",
"version": "1.8.0",
"_release": "1.8.0",
"version": "1.9.0",
"_release": "1.9.0",
"_resolution": {
"type": "version",
"tag": "v1.8.0",
"commit": "0d32bc5d91da11d9a3587537c3c36ce7ee815c94"
"tag": "v1.9.0",
"commit": "fb8776d65825068b9f914ab37d6fd847c951f488"
},
"_source": "https://github.com/eternicode/bootstrap-datepicker.git",
"_target": "^1.7.0",
"_target": "^1.7",
"_originalSource": "bootstrap-datepicker"
}

View File

@@ -1,4 +1,4 @@
# EditorConfig is awesome: http://EditorConfig.org
# EditorConfig is awesome: https://editorconfig.org
root = true

View File

@@ -1,10 +0,0 @@
docs/_build
*-dist.zip
# OS or Editor folders
.DS_Store
.idea
# Folders to ignore
bower_components
node_modules

View File

@@ -1,9 +1,7 @@
sudo: false
language: node_js
node_js:
- "6"
- "12"
before_script:
- npm install -g grunt-cli

View File

@@ -1,6 +1,24 @@
Changelog
=========
1.9.0
-----
## Features
* Added clearDates for clears range (#2114)
## Bugfix
* Hide today button when before start or after end date (#2474)
* Fix navigation buttons states (#2277)
* Fix updateNavArrows bug (#2230)
## Locales
### Bugfix
* Added monthsTitle to Latvian locale (#2255)
* Rename en-CA locale file to match the rest of the files (#2217)
* Fix cs locale date format (#2275)
* Added translation for months (fixing the default 'en' locale) (#2271)
1.7.1
-----
@@ -351,7 +369,7 @@ Locale changes:
* De-duplicated Ukrainian files from `uk` and `ua` to just `ua`
Repository changes:
* Documentation has been moved from the base `README.md` file to the `docs/` folder, and been re-written to use sphinx docs. The docs are now viewable online at http://bootstrap-datepicker.readthedocs.org/. The [gh-pages](http://eternicode.github.io/bootstrap-datepicker/) branch has been reduced to the sandbox demo.
* Documentation has been moved from the base `README.md` file to the `docs/` folder, and been re-written to use sphinx docs. The docs are now viewable online at https://bootstrap-datepicker.readthedocs.org/. The [gh-pages](https://uxsolutions.github.io/bootstrap-datepicker/) branch has been reduced to the sandbox demo.
* Changed the js file header to point at repo/demo/docs urls instead of eyecon.ro
* The css files are now the output of the standalone build scripts instead of `build/build.less` etc.
* `composer.json` now supports component-installer
@@ -465,7 +483,7 @@ Small optimizations release
v1.0.1
------
* Support for [Bower](http://twitter.github.com/bower/)
* Support for [Bower](https://bower.io/)
* Component pickers are now aligned under the input, not the add-on element.
* Japanese locale now has "today" and "format".
* "remove" method removes `.data().date` if the datepicker is on a non-input.

View File

@@ -67,8 +67,5 @@ members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org), version 1.4,
available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct.html>

View File

@@ -2,14 +2,14 @@
## Support requests
The issue tracker is not the place for support requests. If you get stuck with bootstrap-datepicker, it's very likely that the fine folks at [StackOverflow](http://stackoverflow.com/) will be able to help you; simply describe the problem you're having and provide them a link to the repo (so they know what code you're using). Another option is to post to the [bootstrap-datepicker google group](https://groups.google.com/group/bootstrap-datepicker).
The issue tracker is not the place for support requests. If you get stuck with bootstrap-datepicker, it's very likely that the fine folks at [StackOverflow](https://stackoverflow.com/) will be able to help you; simply describe the problem you're having and provide them a link to the repo (so they know what code you're using). Another option is to post to the [bootstrap-datepicker google group](https://groups.google.com/group/bootstrap-datepicker).
## Issues
If you've found a bug in bootstrap-datepicker, we want to know about it! However, please keep the following in mind:
* This is not the bootstrap-datepicker from [eyecon.ro](http://www.eyecon.ro/bootstrap-datepicker/). Stefan provided the initial code for bootstrap-datepicker, but this repo is divergent from his codebase. Please make sure you're using either the latest tagged version or the latest master from https://github.com/uxsolutions/bootstrap-datepicker/.
* A working example of the bug you've found is *much* easier to work with than a description alone. If possible, please provide a link to a demonstration of the bug, perhaps using http://jsfiddle.net/ .
* This is not the bootstrap-datepicker from [eyecon.ro](https://www.eyecon.ro/bootstrap-datepicker/). Stefan provided the initial code for bootstrap-datepicker, but this repo is divergent from his codebase. Please make sure you're using either the latest tagged version or the latest master from https://github.com/uxsolutions/bootstrap-datepicker/.
* A working example of the bug you've found is *much* easier to work with than a description alone. If possible, please provide a link to a demonstration of the bug, perhaps using https://jsfiddle.net/ .
* CDN-backed assets can be found at http://bsdp-assets.blackcherry.us/ . These should be used *only* for building test cases, as they may be removed or changed at any time.
* Finally, it's possible someone else has already reported the same bug you have. Please search the issue tracker for similar issues before posting your own. Thanks!

View File

@@ -1,5 +1,5 @@
/*!
* Datepicker for Bootstrap v1.8.0 (https://github.com/uxsolutions/bootstrap-datepicker)
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

View File

@@ -1,5 +1,5 @@
/*!
* Datepicker for Bootstrap v1.8.0 (https://github.com/uxsolutions/bootstrap-datepicker)
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

View File

@@ -1,5 +1,5 @@
/*!
* Datepicker for Bootstrap v1.8.0 (https://github.com/uxsolutions/bootstrap-datepicker)
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

View File

@@ -1,5 +1,5 @@
/*!
* Datepicker for Bootstrap v1.8.0 (https://github.com/uxsolutions/bootstrap-datepicker)
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

View File

@@ -1,5 +1,5 @@
/*!
* Datepicker for Bootstrap v1.8.0 (https://github.com/uxsolutions/bootstrap-datepicker)
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

View File

@@ -1,5 +1,5 @@
/*!
* Datepicker for Bootstrap v1.8.0 (https://github.com/uxsolutions/bootstrap-datepicker)
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

View File

@@ -1,5 +1,5 @@
/*!
* Datepicker for Bootstrap v1.8.0 (https://github.com/uxsolutions/bootstrap-datepicker)
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

View File

@@ -1,5 +1,5 @@
/*!
* Datepicker for Bootstrap v1.8.0 (https://github.com/uxsolutions/bootstrap-datepicker)
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/

View File

@@ -1,5 +1,5 @@
/*!
* Datepicker for Bootstrap v1.8.0 (https://github.com/uxsolutions/bootstrap-datepicker)
* Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker)
*
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
*/
@@ -89,6 +89,10 @@
var Datepicker = function(element, options){
$.data(element, 'datepicker', this);
this._events = [];
this._secondaryEvents = [];
this._process_options(options);
this.dates = new DateArray();
@@ -98,7 +102,7 @@
this.element = $(element);
this.isInput = this.element.is('input');
this.inputField = this.isInput ? this.element : this.element.find('input');
this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .btn') : false;
this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .input-group-append, .input-group-prepend, .btn') : false;
if (this.component && this.component.length === 0)
this.component = false;
this.isInline = !this.component && this.element.is('div');
@@ -308,8 +312,6 @@
o.defaultViewDate = UTCToday();
}
},
_events: [],
_secondaryEvents: [],
_applyEvents: function(evs){
for (var i=0, el, ch, ev; i < evs.length; i++){
el = evs[i][0];
@@ -465,7 +467,7 @@
},
show: function(){
if (this.inputField.prop('disabled') || (this.inputField.prop('readonly') && this.o.enableOnReadonly === false))
if (this.inputField.is(':disabled') || (this.inputField.prop('readonly') && this.o.enableOnReadonly === false))
return;
if (!this.isInline)
this.picker.appendTo(this.o.container);
@@ -962,7 +964,9 @@
endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
todaytxt = dates[this.o.language].today || dates['en'].today || '',
cleartxt = dates[this.o.language].clear || dates['en'].clear || '',
titleFormat = dates[this.o.language].titleFormat || dates['en'].titleFormat,
titleFormat = dates[this.o.language].titleFormat || dates['en'].titleFormat,
todayDate = UTCToday(),
titleBtnVisible = (this.o.todayBtn === true || this.o.todayBtn === 'linked') && todayDate >= this.o.startDate && todayDate <= this.o.endDate && !this.weekOfDateIsDisabled(todayDate),
tooltip,
before;
if (isNaN(year) || isNaN(month))
@@ -971,7 +975,7 @@
.text(DPGlobal.formatDate(d, titleFormat, this.o.language));
this.picker.find('tfoot .today')
.text(todaytxt)
.css('display', this.o.todayBtn === true || this.o.todayBtn === 'linked' ? 'table-cell' : 'none');
.css('display', titleBtnVisible ? 'table-cell' : 'none');
this.picker.find('tfoot .clear')
.text(cleartxt)
.css('display', this.o.clearBtn === true ? 'table-cell' : 'none');
@@ -1151,12 +1155,12 @@
factor *= 10;
/* falls through */
case 1:
prevIsDisabled = Math.floor(year / factor) * factor < startYear;
prevIsDisabled = Math.floor(year / factor) * factor <= startYear;
nextIsDisabled = Math.floor(year / factor) * factor + factor > endYear;
break;
case 0:
prevIsDisabled = year <= startYear && month < startMonth;
nextIsDisabled = year >= endYear && month > endMonth;
prevIsDisabled = year <= startYear && month <= startMonth;
nextIsDisabled = year >= endYear && month >= endMonth;
break;
}
@@ -2003,7 +2007,7 @@
/* DATEPICKER VERSION
* =================== */
$.fn.datepicker.version = '1.8.0';
$.fn.datepicker.version = '1.9.0';
$.fn.datepicker.deprecated = function(msg){
var console = window.console;

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
!function(a){a.fn.datepicker.dates.az={days:["Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə"],daysShort:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],daysMin:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],months:["Yanvar","Fevral","Mart","Aprel","May","İyun","İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],monthsShort:["Yan","Fev","Mar","Apr","May","İyun","İyul","Avq","Sen","Okt","Noy","Dek"],today:"Bu gün",weekStart:1}}(jQuery);
!function(a){a.fn.datepicker.dates.az={days:["Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə"],daysShort:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],daysMin:["B.","B.e","Ç.a","Ç.","C.a","C.","Ş."],months:["Yanvar","Fevral","Mart","Aprel","May","İyun","İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr"],monthsShort:["Yan","Fev","Mar","Apr","May","İyun","İyul","Avq","Sen","Okt","Noy","Dek"],today:"Bu gün",weekStart:1,clear:"Təmizlə",monthsTitle:"Aylar"}}(jQuery);

View File

@@ -0,0 +1 @@
!function(a){a.fn.datepicker.dates.bm={days:["Kari","Ntɛnɛn","Tarata","Araba","Alamisa","Juma","Sibiri"],daysShort:["Kar","Ntɛ","Tar","Ara","Ala","Jum","Sib"],daysMin:["Ka","Nt","Ta","Ar","Al","Ju","Si"],months:["Zanwuyekalo","Fewuruyekalo","Marisikalo","Awirilikalo","Mɛkalo","Zuwɛnkalo","Zuluyekalo","Utikalo","Sɛtanburukalo","ɔkutɔburukalo","Nowanburukalo","Desanburukalo"],monthsShort:["Zan","Few","Mar","Awi","Mɛ","Zuw","Zul","Uti","Sɛt","ɔku","Now","Des"],today:"Bi",monthsTitle:"Kalo",clear:"Ka jɔsi",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);

Some files were not shown because too many files have changed in this diff Show More