Compare commits

..
108 Commits
Author SHA1 Message Date
Namonay f7cfd94c7e fix: docker skill issue 2026-05-27 13:19:23 +00:00
Namonay 6bca320f92 add: case insensitive 2026-05-27 11:47:41 +00:00
Namonay 6fbc63cbf4 a 2026-03-26 13:40:55 +00:00
Namonay 12e9b3552e fix: remove millisecs 2026-03-26 13:29:18 +00:00
Namonay 20fac5368d add: frontend 2026-03-26 13:26:58 +00:00
Namonay 307199258b fix: triple typo 2026-03-26 12:53:36 +00:00
Namonay 4e2aea1f2e fix: double typo 💀 2026-03-26 11:00:45 +00:00
Namonay 8bbe624f8a fix: typo... 2026-03-26 10:52:32 +00:00
Namonay 3e00258cfe add: new formating 2026-03-26 10:19:16 +00:00
Namonay f8edc84579 i got my swag 2026-03-04 15:39:21 +00:00
Namonay 946244bda9 a! 2026-03-04 15:36:44 +00:00
Namonay 86b9729fdc a 2026-03-04 15:35:04 +00:00
Namonay 2364221780 a 2026-03-04 15:33:39 +00:00
Namonay 552a45c1a8 a 2026-03-04 15:32:45 +00:00
Namonay 06a311f8c0 a 2026-03-04 15:31:48 +00:00
Namonay aee0b6399a a 2026-03-04 15:30:42 +00:00
Namonay 8dfd683880 a 2026-03-04 15:17:18 +00:00
Namonay 0c75eb7d3c a 2026-03-04 15:14:33 +00:00
Namonay 948af50461 a 2026-03-04 15:11:03 +00:00
Namonay 20a5627444 a? 2026-03-04 15:06:19 +00:00
Namonay 2bc3a0916c a 2026-03-03 13:56:31 +00:00
Namonay a2d91691a0 remove notif 2026-03-03 13:55:03 +00:00
Namonay 665df23108 aaa 2026-03-02 14:36:56 +00:00
Namonay 1c6903b3c0 add: timer & reboot 2026-03-02 14:35:19 +00:00
Namonay 94184aea03 a 2026-03-02 12:31:49 +00:00
Namonay 5975ea48f7 i got my swag back 2026-03-02 12:24:03 +00:00
Namonay 7a23612ee5 they nerfed my swag 2026-03-02 12:06:45 +00:00
Namonay aafe082406 a 2026-03-02 12:00:43 +00:00
Namonay 9536bb9157 z 2026-03-02 09:34:56 +00:00
Namonay 20aa3d6246 a 2026-03-02 09:32:52 +00:00
Namonay 9d62d44bc4 d 2026-03-02 09:32:26 +00:00
Namonay 19b72a8f5c aa 2026-03-02 09:28:37 +00:00
Namonay 3835ae16b3 & 2026-03-02 09:11:51 +00:00
Namonay 17957e03ee b 2026-03-02 08:44:06 +00:00
Namonay a421151d13 a 2026-03-02 08:42:41 +00:00
Namonay 504d237405 swag 2026-02-25 15:53:41 +00:00
Namonay 6696bcc4e7 a 2026-02-25 15:36:18 +00:00
Namonay 32a1458218 a 2026-02-25 15:33:42 +00:00
Namonay 4697086f86 a 2026-02-25 15:32:36 +00:00
Namonay c4b9e5f360 w 2026-02-25 15:14:30 +00:00
Namonay 0a9934b889 b 2026-02-25 15:14:07 +00:00
Namonay 8ad1650079 a 2026-02-25 15:13:51 +00:00
Namonay 6e50013a84 swag 2026-02-25 15:03:45 +00:00
Namonay dba9a7690d a 2026-02-25 14:59:35 +00:00
Namonay 319d237e5d omg 2026-02-25 14:58:11 +00:00
Namonay af6515e55f a 2026-02-25 14:44:54 +00:00
Namonay 1b83098fd0 swag 2026-02-25 14:39:11 +00:00
Namonay ec11a3d4c4 a 2026-02-25 14:28:49 +00:00
Namonay 024b829562 a 2026-02-25 14:26:55 +00:00
Namonay d918540252 a 2026-02-25 14:26:20 +00:00
Namonay 9022704e5c a 2026-02-25 14:25:46 +00:00
Namonay 4046d25aa7 a 2026-02-25 14:07:04 +00:00
Namonay aac2ec7ce2 a 2026-02-25 14:06:32 +00:00
Namonay a714814ad0 a 2026-02-25 14:05:45 +00:00
Namonay 47555303e2 a 2026-02-25 14:04:18 +00:00
Namonay 9c83537d65 a 2026-02-25 14:03:39 +00:00
Namonay b7d47b130d a 2026-02-25 13:59:30 +00:00
Namonay baa8c1392d a 2026-02-25 13:58:29 +00:00
Namonay b18012713e a 2026-02-25 13:58:11 +00:00
Namonay b714a6c4e6 a 2026-02-25 13:57:08 +00:00
Namonay 7188ad7c75 a 2026-02-25 13:55:06 +00:00
Namonay 68314ba7af a 2026-02-25 13:54:30 +00:00
Namonay f1e591ab5b a 2026-02-25 13:50:50 +00:00
Namonay ee5d1b2758 a 2026-02-25 13:48:10 +00:00
Namonay 7f2d0ec2f6 a 2026-02-25 13:47:10 +00:00
Namonay 2b22eaf0e9 slop 2026-02-25 13:46:14 +00:00
Namonay 19d646aa40 a 2026-02-25 13:43:50 +00:00
Namonay acd57b90a8 update 2026-02-25 13:42:03 +00:00
Namonay d63d9cdb78 swag 2026-02-25 13:32:06 +00:00
Namonay 2ca97cba47 a 2026-02-25 12:19:29 +00:00
Namonay 5ee126d0ec a 2026-02-25 11:51:28 +00:00
Namonay 0d69fcc296 a 2026-02-25 11:20:20 +00:00
Namonay 46c99f1ae0 a 2026-02-25 11:18:10 +00:00
Namonay 1801e6d054 a 2026-02-25 11:10:52 +00:00
Namonay ae17970d8c a 2026-02-25 11:05:31 +00:00
Namonay 30fef37d45 a 2026-02-25 11:03:59 +00:00
Namonay 76e90450f8 aa 2026-02-25 11:03:40 +00:00
Namonay 845a1c8494 a 2026-02-25 11:03:16 +00:00
Namonay 4104ee47c4 a 2026-02-25 10:40:55 +00:00
Namonay 597900d7b6 a 2026-02-25 09:02:32 +00:00
Namonay 942a3adaf6 a 2026-02-25 09:00:23 +00:00
Namonay 96960c17a1 a 2026-02-24 14:57:24 +00:00
Namonay 6f7fc56e32 a 2026-02-24 14:55:11 +00:00
Namonay 1cfa2668b2 a 2026-02-24 14:53:20 +00:00
Namonay f524ed4e6a aaa 2026-02-24 14:27:28 +00:00
Namonay 245f524c38 typo 2026-02-24 14:24:37 +00:00
Namonay 76f857ca0a add: time 2026-02-24 14:23:56 +00:00
Namonay 6aeea9dedb add: contenttype 2026-02-24 13:06:48 +00:00
Namonay dd6ca2d75b add: json 2026-02-24 13:04:50 +00:00
Namonay d9796b0b87 typo 2026-02-24 12:50:25 +00:00
Namonay 9958763f6f add: client 2026-02-24 12:47:07 +00:00
Namonay a71f3f3681 add: edit 2026-02-20 15:07:45 +00:00
Namonay 9f8a9eebfe a 2026-02-20 15:03:22 +00:00
Namonay a1fc7a4b27 a 2026-02-20 14:55:12 +00:00
Namonay 0e8b558747 a 2026-02-20 14:54:16 +00:00
Namonay b6d60e7210 a 2026-02-20 14:52:55 +00:00
Namonay bf31f647e7 a 2026-02-20 14:50:50 +00:00
Namonay 121cc91ad1 a 2026-02-20 14:36:54 +00:00
Namonay ac2b23c459 a 2026-02-20 14:35:03 +00:00
Namonay 10675cffbc boolean bruh 2026-02-20 14:33:18 +00:00
Namonay 32dee47628 a 2026-02-20 14:19:27 +00:00
Namonay c08f81bd03 a 2026-02-20 13:40:04 +00:00
Namonay 5063d78198 a 2026-02-20 13:37:30 +00:00
Namonay a12366867d add: whitelist 2026-02-20 13:36:02 +00:00
Namonay 614dea44b4 add a 2026-02-20 10:58:52 +00:00
Namonay 305b269093 add: db 2026-02-20 10:55:55 +00:00
Namonay 612ee5c740 a 2026-02-20 09:48:06 +00:00
Namonay 64c2269c0a add: lookup 2026-02-20 09:44:08 +00:00
5 changed files with 405 additions and 57 deletions
+88
View File
@@ -0,0 +1,88 @@
import sys
sys.path = [p for p in sys.path if 'Python27' not in p] # uglyfix
from urllib import request
import json
import socket
from datetime import datetime
import subprocess
import time
DEFAULT_SHUTDOWN = "21:00"
LAST_VALUE = 0
machine_name = socket.gethostname()
url = "http://192.168.50.27:5000/lookup"
payload = {
"name": machine_name,
}
def get_data():
data = json.dumps(payload).encode("utf-8")
req = request.Request(
url,
headers={"Content-Type": "application/json"},
data=data,
method="POST"
)
try:
with request.urlopen(req, timeout=10) as response:
data = response.read()
json_data = json.loads(data)
print(json_data)
return json_data
except:
return None
def to_seconds(t):
return t.hour * 3600 + t.minute * 60 + t.second
def set_shutdown(time, now):
global LAST_VALUE
if (time - now) < 0 or time == LAST_VALUE:
return
LAST_VALUE = time
print("[BS Shutdown] shutting down in " + str(time - now) + " seconds")
subprocess.run(["shutdown", "-r", "-f", "-t", str(time - now)])
def remove_shutdown():
print("[BS Shutdown] Removed shutdown")
subprocess.run(["shutdown", "-a"])
def update():
global LAST_VALUE
data = get_data()
if data is not None and data["noshutdown"] == 1:
if (LAST_VALUE != 0):
remove_shutdown() # In case
LAST_VALUE = 0
return
if data is None:
time = to_seconds(datetime.strptime(DEFAULT_SHUTDOWN, "%H:%M").time())
else:
time = to_seconds(datetime.strptime(data["time"], "%H:%M").time())
now = to_seconds(datetime.now().time())
if (time - now) < 0:
return
if (time == LAST_VALUE):
return
if (LAST_VALUE != 0):
remove_shutdown()
print("[BS Shutdown] New value")
if (time - now) < 1800:
set_shutdown(time, now)
if __name__ == '__main__':
while True:
update()
time.sleep(900)
+3
View File
@@ -4,5 +4,8 @@ services:
build: ./src
volumes:
- "./src/data:/data"
- "/etc/timezone:/etc/timezone:ro"
- "/etc/localtime:/etc/localtime:ro"
ports:
- "5000:5000"
+126 -10
View File
@@ -1,23 +1,139 @@
from flask import Flask, render_template, request, redirect, url_for
from flask import Flask, render_template, request, redirect, url_for, jsonify
import sqlite3
import re
from datetime import datetime
# -- INIT --
app = Flask(__name__)
pattern = re.compile(r"^(?:[01]\d|2[0-3]):[0-5]\d$") # Regex "XX:XX"
items = []
# -- GLOBAL VAR --
DB_PATH = "data.db"
DEFAULT_TIME = "21:00"
## -- INIT DB --
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
time TEXT,
noshutdown INTEGER DEFAULT 0
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS val (
default_time TEXT NOT NULL DEFAULT '21:00'
)
""")
cursor.execute("SELECT COUNT(*) FROM val")
count = cursor.fetchone()[0]
if count == 0:
cursor.execute("INSERT INTO val (default_time) VALUES (?)", ('21:00',))
conn.commit()
conn.close()
# -- END --
def get_db_connection():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
# --- MAIN ---
@app.route("/")
def mainpage():
return render_template('index.html', title='BG Shutdown', items=items)
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT name, time, noshutdown, last_connection FROM items ORDER BY name")
items = cursor.fetchall()
cursor.execute("SELECT default_time FROM val")
default_time_row = cursor.fetchone()
default_time = default_time_row[0] if default_time_row else DEFAULT_TIME
conn.close()
return render_template('index.html', title='BS Shutdown', items=items, default_time=default_time)
@app.route('/submit', methods =['POST'])
def submit():
value = request.form.get("query")
value = request.form.get("query").upper()
time_value = request.form.get("time")
print(value)
items.append({"name" : value, "time" : time_value})
noshutdown = 1 if request.form.get("noshutdown") == "on" else 0
if not value:
return redirect(url_for("mainpage"))
if noshutdown == 0 and not time_value:
return redirect(url_for("mainpage"))
if noshutdown == 1:
time_value = 'None'
if time_value != 'None':
if bool(pattern.match(time_value)) is not True:
return redirect(url_for("mainpage"))
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('SELECT 1 FROM items WHERE name = ?', (value,))
item = cursor.fetchone()
if item:
cursor.execute("UPDATE items SET time = ?, noshutdown = ? WHERE name = ?", (time_value, noshutdown, value))
else:
cursor.execute("INSERT INTO items (name, time, noshutdown) VALUES (?, ?, ?)", (value, time_value, noshutdown))
conn.commit()
conn.close()
return redirect(url_for("mainpage"))
@app.route("/submit-default", methods=["POST"])
def submit_default():
time_value = request.form.get("default-time")
conn = get_db_connection()
cursor = conn.cursor()
if bool(pattern.match(time_value)) is not True:
return redirect(url_for("mainpage"))
cursor.execute("UPDATE val SET default_time = ?", (time_value,))
conn.commit()
conn.close()
return redirect(url_for("mainpage"))
@app.route("/delete", methods=["POST"])
def delete():
value = request.form.get("item_name")
time_value = request.form.get("item_time")
if {"name": value, "time": time_value} in items:
items.remove({"name": value, "time": time_value})
print("Deleting:", value)
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute('SELECT 1 FROM items WHERE name = ?', (value,))
item = cursor.fetchone()
if not item:
conn.close()
return redirect(url_for("mainpage"))
cursor.execute(
"DELETE FROM items WHERE name = ?",
(value,)
)
conn.commit()
conn.close()
return redirect(url_for("mainpage"))
# --- END MAIN ---
# --- API ---
@app.route("/lookup", methods=["POST"])
def lookup():
data = request.get_json()
if not data or "name" not in data:
return jsonify({"error": "Not found"}), 404
name = data["name"].upper()
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
"SELECT name, time, noshutdown FROM items WHERE name = ?",
(name,)
)
item = cursor.fetchone()
cursor.execute("SELECT default_time FROM val")
default_time_row = cursor.fetchone()
default_time = default_time_row[0] if default_time_row else DEFAULT_TIME
if item:
cursor.execute("UPDATE items SET last_connection = ? WHERE name = ?", (datetime.now().replace(microsecond=0), item["name"]))
conn.commit()
conn.close()
if item:
return jsonify({"name": item["name"], "noshutdown": item["noshutdown"], "time": item["time"]}), 200
else:
return jsonify({"noshutdown": 0, "time": default_time}), 200
# --- END API ---
+6 -3
View File
@@ -35,14 +35,17 @@
<header>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{{ url_for('mainpage') }}">BG Shutdown</a>
<a class="navbar-brand" href="{{ url_for('mainpage') }}">BS Shutdown</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link {% if title=='BG Shutdown' %}active{% endif %}" href="{{ url_for('mainpage') }}">Accueil</a>
<a class="nav-link {% if title=='BS Shutdown' %}active{% endif %}" href="{{ url_for('mainpage') }}">Accueil</a>
</li>
<li class="nav-item">
<a class="nav-link {% if title=='BG Shutdown' %}active{% endif %}" href="{{ url_for('mainpage') }}">Groupes</a>
</li>
</ul>
</div>
@@ -50,7 +53,7 @@
</nav>
</header>
<main class="container my-5">
<main class="container my-5 flex-fill">
{% block content %}{% endblock %}
</main>
+181 -43
View File
@@ -1,67 +1,205 @@
{% extends "base.html" %}
{% block content %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<div class="container py-4">
<!-- Formulaire réduit avec sélection d'heure -->
<div class="card bg-dark bg-opacity-75 p-3 mb-4">
<form method="post" action="/submit" class="row g-2 align-items-center justify-content-start">
<div class="row g-2 align-items-center">
<!-- Champ texte -->
<div class="col-md-4">
<input
type="text"
name="query"
class="form-control bg-dark text-white"
placeholder="Enter text">
</div>
<div class="col-10">
<form method="post" action="/submit" class="row g-2 align-items-center">
<div class="col-md-2">
<input
type="text"
name="query"
class="form-control bg-dark text-white"
placeholder="Nom de la machine">
</div>
<div class="col-md-2">
<select name="time" class="form-select bg-dark text-white" id="time-select">
{% for hour in range(0, 24) %}
<option value="{{ '%02d:00'|format(hour) }}">{{ '%02d:00'|format(hour) }}</option>
<option value="{{ '%02d:30'|format(hour) }}">{{ '%02d:30'|format(hour) }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-2">
<div class="form-check text-white">
<input class="form-check-input" type="checkbox" name="noshutdown" id="noshutdown">
<label class="form-check-label" for="noshutdown">Whitelist shutdown</label>
</div>
</div>
<div class="col-md-1">
<button type="submit" class="btn btn-sm btn-primary w-100">
<i class="bi bi-plus-lg"> Add</i>
</button>
</div>
</form>
</div>
<!-- Sélecteur d'heure -->
<div class="col-md-2">
<select name="time" class="form-select bg-dark text-white">
<div class="col-auto gap-1 ms-auto">
<form method="post" action="/submit-default" class="d-flex align-items-center gap-1">
<select name="default-time" class="form-select bg-dark text-white flex-grow-1" id="default-time-select">
{% for hour in range(0, 24) %}
<option value="{{ '%02d:00'|format(hour) }}">{{ '%02d:00'|format(hour) }}</option>
<option value="{{ '%02d:30'|format(hour) }}">{{ '%02d:30'|format(hour) }}</option>
{% set h1 = '%02d:00'|format(hour) %}
{% set h2 = '%02d:30'|format(hour) %}
<option value="{{ h1 }}"
{% if h1 == default_time %}selected{% endif %}>
{{ h1 }}
</option>
<option value="{{ h2 }}"
{% if h2 == default_time %}selected{% endif %}>
{{ h2 }}
</option>
{% endfor %}
</select>
</div>
<!-- Bouton submit -->
<div class="col-auto">
<button type="submit" class="btn btn-secondary">
Submit
<button type="submit" class="btn btn-sm btn-primary w-100">
<i class="bi bi-save"> Save</i>
</button>
</div>
</form>
</div>
</form>
</div>
</div>
<div class="card bg-dark bg-opacity-75 p-3">
<h5 class="text-white mb-3">Liste des éléments</h5>
<div class="row fw-bold text-white mb-2">
<div class="col-4">Nom</div>
<div class="col-3">Dernier ping</div>
<div class="col-2">Whitelist</div>
<div class="col-2">Time</div>
<div class="col-1 text-end">Actions</div>
</div>
<!-- Tableau / Liste -->
<div class="card bg-dark bg-opacity-75 p-3">
<h5 class="text-white mb-3">Liste des éléments</h5>
<div class="table-items">
{% for item in items %}
<div class="d-flex justify-content-between align-items-center py-2">
<!-- Nom -->
<span class="text-white">{{ item.name }}</span>
<!-- Heure -->
<span class="text-white">{{ item.time }}</span>
<!-- Bouton supprimer -->
<form method="post" action="/delete" class="m-0 p-0">
<div class="table-items">
{% for item in items %}
<div class="row align-items-center py-2">
<div class="col-4 text-white">
{{ item.name }}
</div>
<div class="col-3 text-white">
{% if item.last_connection %}
{{ item.last_connection }}
{% endif %}
</div>
<div class="col-2">
{% if item.noshutdown == 1 %}
<span class="text-success">OUI</span>
{% else %}
<span class="text-danger">NON</span>
{% endif %}
</div>
<div class="col-2 text-white">
{% if item.noshutdown == 0 %}
{{ item.time }}
{% endif %}
</div>
<div class="col-1 text-end d-flex justify-content-end gap-1">
<button
type="button"
class="btn btn-sm btn-warning edit-btn"
data-name="{{ item.name }}"
data-noshutdown="{{ item.noshutdown }}"
data-time="{{ item.time }}"
data-bs-toggle="modal"
data-bs-target="#editModal">
<i class="bi bi-pencil"></i>
</button>
<form method="post" action="/delete" class="m-0">
<input type="hidden" name="item_name" value="{{ item.name }}">
<input type="hidden" name="item_time" value="{{ item.time }}">
<button type="submit" class="btn btn-sm btn-danger">&times;</button>
<button type="submit" class="btn btn-sm btn-danger">
<i class="bi bi-x-lg"></i>
</button>
</form>
</div>
{% if not loop.last %}
<hr class="border-secondary my-1">
{% endif %}
{% endfor %}
</div>
{% if not loop.last %}
<hr class="border-secondary my-1">
{% endif %}
{% endfor %}
<div class="modal fade" id="editModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark text-white">
<form method="post" action="/submit">
<div class="modal-header">
<h5 class="modal-title">Modifier l'élément</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Nom</label>
<input type="text" class="form-control" name="query" id="editName" readonly>
</div>
<div class="mb-3">
<label class="form-label">No Shutdown</label>
<input class="form-check-input" type="checkbox" name="noshutdown" id="editNoshutdown">
</div>
<div class="mb-3">
<label class="form-label">Time</label>
<select class="form-select" name="time" id="editTime">
{% for hour in range(0, 24) %}
{% set h1 = '%02d:00'|format(hour) %}
{% set h2 = '%02d:30'|format(hour) %}
<option value="{{ h1 }}">{{ h1 }}</option>
<option value="{{ h2 }}">{{ h2 }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">Enregistrer</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
const tickCheckbox = document.getElementById('noshutdown'); // script pr desactiver la selection de l'heure si whitelist
const timeSelect = document.getElementById('time-select');
timeSelect.disabled = tickCheckbox.checked;
tickCheckbox.addEventListener('change', function() {
timeSelect.disabled = this.checked;
});
document.querySelectorAll('.edit-btn').forEach(button => {
button.addEventListener('click', function () {
const name = this.dataset.name;
const noshutdown = this.dataset.noshutdown;
const time = this.dataset.time;
document.getElementById('editName').value = name;
document.getElementById('editNoshutdown').checked = (noshutdown == 1);
document.getElementById('editTime').value = time;
const timeSelect = document.getElementById('editTime');
const noshutdownSelect = document.getElementById('editNoshutdown');
timeSelect.disabled = noshutdownSelect.checked;
noshutdownSelect.addEventListener('change', function () {
timeSelect.disabled = this.checked;
});
});
});
</script>
{% endblock %}