2.0 - WebUI builder ("Cielight" merge) #9
@@ -24,6 +24,8 @@ footer:
 | 
				
			|||||||
# Build parameters
 | 
					# Build parameters
 | 
				
			||||||
build:
 | 
					build:
 | 
				
			||||||
  theme: modern # choose a theme in config/theme folder
 | 
					  theme: modern # choose a theme in config/theme folder
 | 
				
			||||||
 | 
					  convert_images: true # true to enable image conversion
 | 
				
			||||||
 | 
					  resize_images: true # true to enable image resizing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Change this by your legals
 | 
					# Change this by your legals
 | 
				
			||||||
legals:
 | 
					legals:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import logging
 | 
					import logging
 | 
				
			||||||
 | 
					import yaml
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from flask import Flask, jsonify, request, send_from_directory, render_template
 | 
					from flask import Flask, jsonify, request, send_from_directory, render_template
 | 
				
			||||||
from src.py.builder.gallery_builder import (
 | 
					from src.py.builder.gallery_builder import (
 | 
				
			||||||
@@ -18,6 +19,8 @@ app = Flask(
 | 
				
			|||||||
    static_url_path=""
 | 
					    static_url_path=""
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SITE_YAML = Path(__file__).resolve().parents[3] / "config" / "site.yaml"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# --- Photos directory (configurable) ---
 | 
					# --- Photos directory (configurable) ---
 | 
				
			||||||
PHOTOS_DIR = Path(__file__).resolve().parents[3] / "config" / "photos"
 | 
					PHOTOS_DIR = Path(__file__).resolve().parents[3] / "config" / "photos"
 | 
				
			||||||
app.config["PHOTOS_DIR"] = PHOTOS_DIR
 | 
					app.config["PHOTOS_DIR"] = PHOTOS_DIR
 | 
				
			||||||
@@ -133,6 +136,25 @@ def photos(section, filename):
 | 
				
			|||||||
    """Serve uploaded photos from disk."""
 | 
					    """Serve uploaded photos from disk."""
 | 
				
			||||||
    return send_from_directory(PHOTOS_DIR / section, filename)
 | 
					    return send_from_directory(PHOTOS_DIR / section, filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route("/site-info")
 | 
				
			||||||
 | 
					def site_info():
 | 
				
			||||||
 | 
					    return render_template("site-info/index.html")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route("/api/site-info", methods=["GET"])
 | 
				
			||||||
 | 
					def get_site_info():
 | 
				
			||||||
 | 
					    with open(SITE_YAML, "r") as f:
 | 
				
			||||||
 | 
					        data = yaml.safe_load(f)
 | 
				
			||||||
 | 
					    return jsonify(data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@app.route("/api/site-info", methods=["POST"])
 | 
				
			||||||
 | 
					def update_site_info():
 | 
				
			||||||
 | 
					    data = request.json
 | 
				
			||||||
 | 
					    with open(SITE_YAML, "w") as f:
 | 
				
			||||||
 | 
					        yaml.safe_dump(data, f, sort_keys=False, allow_unicode=True)
 | 
				
			||||||
 | 
					    return jsonify({"status": "ok"})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# --- Run server ---
 | 
					# --- Run server ---
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    logging.info("Starting WebUI at http://127.0.0.1:5000")
 | 
					    logging.info("Starting WebUI at http://127.0.0.1:5000")
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										197
									
								
								src/webui/js/site-info.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								src/webui/js/site-info.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
				
			|||||||
 | 
					document.addEventListener("DOMContentLoaded", () => {
 | 
				
			||||||
 | 
					  const form = document.getElementById("site-info-form");
 | 
				
			||||||
 | 
					  const status = document.getElementById("site-info-status");
 | 
				
			||||||
 | 
					  const menuList = document.getElementById("menu-items-list");
 | 
				
			||||||
 | 
					  const addMenuBtn = document.getElementById("add-menu-item");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let menuItems = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function renderMenuItems() {
 | 
				
			||||||
 | 
					    menuList.innerHTML = "";
 | 
				
			||||||
 | 
					    menuItems.forEach((item, idx) => {
 | 
				
			||||||
 | 
					      const div = document.createElement("div");
 | 
				
			||||||
 | 
					      div.style.display = "flex";
 | 
				
			||||||
 | 
					      div.style.gap = "8px";
 | 
				
			||||||
 | 
					      div.style.marginBottom = "6px";
 | 
				
			||||||
 | 
					      div.innerHTML = `
 | 
				
			||||||
 | 
					        <input type="text" placeholder="Label" value="${item.label || ""}" style="flex:1;" data-idx="${idx}" data-type="label">
 | 
				
			||||||
 | 
					        <input type="text" placeholder="URL" value="${item.href || ""}" style="flex:2;" data-idx="${idx}" data-type="href">
 | 
				
			||||||
 | 
					        <button type="button" class="remove-menu-item" data-idx="${idx}">🗑</button>
 | 
				
			||||||
 | 
					      `;
 | 
				
			||||||
 | 
					      menuList.appendChild(div);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function updateMenuItemsFromInputs() {
 | 
				
			||||||
 | 
					    const inputs = menuList.querySelectorAll("input");
 | 
				
			||||||
 | 
					    const items = [];
 | 
				
			||||||
 | 
					    for (let i = 0; i < inputs.length; i += 2) {
 | 
				
			||||||
 | 
					      const label = inputs[i].value.trim();
 | 
				
			||||||
 | 
					      const href = inputs[i + 1].value.trim();
 | 
				
			||||||
 | 
					      if (label || href) items.push({ label, href });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    menuItems = items;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const ipList = document.getElementById("ip-list");
 | 
				
			||||||
 | 
					  const addIpBtn = document.getElementById("add-ip-paragraph");
 | 
				
			||||||
 | 
					  let ipParagraphs = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function renderIpParagraphs() {
 | 
				
			||||||
 | 
					    ipList.innerHTML = "";
 | 
				
			||||||
 | 
					    ipParagraphs.forEach((item, idx) => {
 | 
				
			||||||
 | 
					      const div = document.createElement("div");
 | 
				
			||||||
 | 
					      div.style.display = "flex";
 | 
				
			||||||
 | 
					      div.style.gap = "8px";
 | 
				
			||||||
 | 
					      div.style.marginBottom = "6px";
 | 
				
			||||||
 | 
					      div.innerHTML = `
 | 
				
			||||||
 | 
					        <input type="text" placeholder="Paragraph" value="${item.paragraph || ""}" style="flex:1;" data-idx="${idx}">
 | 
				
			||||||
 | 
					        <button type="button" class="remove-ip-paragraph" data-idx="${idx}">🗑</button>
 | 
				
			||||||
 | 
					      `;
 | 
				
			||||||
 | 
					      ipList.appendChild(div);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function updateIpParagraphsFromInputs() {
 | 
				
			||||||
 | 
					    const inputs = ipList.querySelectorAll("input");
 | 
				
			||||||
 | 
					    ipParagraphs = Array.from(inputs).map(input => ({
 | 
				
			||||||
 | 
					      paragraph: input.value.trim()
 | 
				
			||||||
 | 
					    })).filter(item => item.paragraph !== "");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // --- Build checkboxes ---
 | 
				
			||||||
 | 
					  const convertImagesCheckbox = document.getElementById("convert-images-checkbox");
 | 
				
			||||||
 | 
					  const resizeImagesCheckbox = document.getElementById("resize-images-checkbox");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Load config
 | 
				
			||||||
 | 
					  if (form) {
 | 
				
			||||||
 | 
					    fetch("/api/site-info")
 | 
				
			||||||
 | 
					      .then(res => res.json())
 | 
				
			||||||
 | 
					      .then(data => {
 | 
				
			||||||
 | 
					        ipParagraphs = Array.isArray(data.legals?.intellectual_property)
 | 
				
			||||||
 | 
					          ? data.legals.intellectual_property
 | 
				
			||||||
 | 
					          : [];
 | 
				
			||||||
 | 
					        renderIpParagraphs();
 | 
				
			||||||
 | 
					        menuItems = Array.isArray(data.menu?.items) ? data.menu.items : [];
 | 
				
			||||||
 | 
					        renderMenuItems();
 | 
				
			||||||
 | 
					        form.elements["info.title"].value = data.info?.title || "";
 | 
				
			||||||
 | 
					        form.elements["info.subtitle"].value = data.info?.subtitle || "";
 | 
				
			||||||
 | 
					        form.elements["info.description"].value = data.info?.description || "";
 | 
				
			||||||
 | 
					        form.elements["info.canonical"].value = data.info?.canonical || "";
 | 
				
			||||||
 | 
					        form.elements["info.keywords"].value = Array.isArray(data.info?.keywords) ? data.info.keywords.join(", ") : (data.info?.keywords || "");
 | 
				
			||||||
 | 
					        form.elements["info.author"].value = data.info?.author || "";
 | 
				
			||||||
 | 
					        form.elements["social.instagram_url"].value = data.social?.instagram_url || "";
 | 
				
			||||||
 | 
					        form.elements["social.thumbnail"].value = data.social?.thumbnail || "";
 | 
				
			||||||
 | 
					        form.elements["footer.copyright"].value = data.footer?.copyright || "";
 | 
				
			||||||
 | 
					        form.elements["footer.legal_label"].value = data.footer?.legal_label || "";
 | 
				
			||||||
 | 
					        form.elements["build.theme"].value = data.build?.theme || "";
 | 
				
			||||||
 | 
					        form.elements["legals.hoster_name"].value = data.legals?.hoster_name || "";
 | 
				
			||||||
 | 
					        form.elements["legals.hoster_adress"].value = data.legals?.hoster_adress || "";
 | 
				
			||||||
 | 
					        form.elements["legals.hoster_contact"].value = data.legals?.hoster_contact || "";
 | 
				
			||||||
 | 
					        // --- Build checkboxes ---
 | 
				
			||||||
 | 
					        if (convertImagesCheckbox) {
 | 
				
			||||||
 | 
					          convertImagesCheckbox.checked = !!data.build?.convert_images;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (resizeImagesCheckbox) {
 | 
				
			||||||
 | 
					          resizeImagesCheckbox.checked = !!data.build?.resize_images;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Add menu item
 | 
				
			||||||
 | 
					  if (addMenuBtn) {
 | 
				
			||||||
 | 
					    addMenuBtn.addEventListener("click", () => {
 | 
				
			||||||
 | 
					      menuItems.push({ label: "", href: "" });
 | 
				
			||||||
 | 
					      renderMenuItems();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Remove menu item
 | 
				
			||||||
 | 
					  menuList.addEventListener("click", (e) => {
 | 
				
			||||||
 | 
					    if (e.target.classList.contains("remove-menu-item")) {
 | 
				
			||||||
 | 
					      const idx = parseInt(e.target.getAttribute("data-idx"));
 | 
				
			||||||
 | 
					      menuItems.splice(idx, 1);
 | 
				
			||||||
 | 
					      renderMenuItems();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Update menuItems on input change
 | 
				
			||||||
 | 
					  menuList.addEventListener("input", () => {
 | 
				
			||||||
 | 
					    updateMenuItemsFromInputs();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Add paragraph
 | 
				
			||||||
 | 
					  if (addIpBtn) {
 | 
				
			||||||
 | 
					    addIpBtn.addEventListener("click", () => {
 | 
				
			||||||
 | 
					      ipParagraphs.push({ paragraph: "" });
 | 
				
			||||||
 | 
					      renderIpParagraphs();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Remove paragraph
 | 
				
			||||||
 | 
					  ipList.addEventListener("click", (e) => {
 | 
				
			||||||
 | 
					    if (e.target.classList.contains("remove-ip-paragraph")) {
 | 
				
			||||||
 | 
					      const idx = parseInt(e.target.getAttribute("data-idx"));
 | 
				
			||||||
 | 
					      ipParagraphs.splice(idx, 1);
 | 
				
			||||||
 | 
					      renderIpParagraphs();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Update ipParagraphs on input change
 | 
				
			||||||
 | 
					  ipList.addEventListener("input", () => {
 | 
				
			||||||
 | 
					    updateIpParagraphsFromInputs();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Save config
 | 
				
			||||||
 | 
					  if (form) {
 | 
				
			||||||
 | 
					    form.addEventListener("submit", async (e) => {
 | 
				
			||||||
 | 
					      e.preventDefault();
 | 
				
			||||||
 | 
					      updateMenuItemsFromInputs();
 | 
				
			||||||
 | 
					      updateIpParagraphsFromInputs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // --- Build object with checkboxes ---
 | 
				
			||||||
 | 
					      const build = {
 | 
				
			||||||
 | 
					        theme: form.elements["build.theme"].value,
 | 
				
			||||||
 | 
					        convert_images: !!(convertImagesCheckbox && convertImagesCheckbox.checked),
 | 
				
			||||||
 | 
					        resize_images: !!(resizeImagesCheckbox && resizeImagesCheckbox.checked)
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const payload = {
 | 
				
			||||||
 | 
					        info: {
 | 
				
			||||||
 | 
					          title: form.elements["info.title"].value,
 | 
				
			||||||
 | 
					          subtitle: form.elements["info.subtitle"].value,
 | 
				
			||||||
 | 
					          description: form.elements["info.description"].value,
 | 
				
			||||||
 | 
					          canonical: form.elements["info.canonical"].value,
 | 
				
			||||||
 | 
					          keywords: form.elements["info.keywords"].value.split(",").map(i => i.trim()).filter(Boolean),
 | 
				
			||||||
 | 
					          author: form.elements["info.author"].value
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        social: {
 | 
				
			||||||
 | 
					          instagram_url: form.elements["social.instagram_url"].value,
 | 
				
			||||||
 | 
					          thumbnail: form.elements["social.thumbnail"].value
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        menu: {
 | 
				
			||||||
 | 
					          items: menuItems
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        footer: {
 | 
				
			||||||
 | 
					          copyright: form.elements["footer.copyright"].value,
 | 
				
			||||||
 | 
					          legal_label: form.elements["footer.legal_label"].value
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        build,
 | 
				
			||||||
 | 
					        legals: {
 | 
				
			||||||
 | 
					          hoster_name: form.elements["legals.hoster_name"].value,
 | 
				
			||||||
 | 
					          hoster_adress: form.elements["legals.hoster_adress"].value,
 | 
				
			||||||
 | 
					          hoster_contact: form.elements["legals.hoster_contact"].value,
 | 
				
			||||||
 | 
					          intellectual_property: ipParagraphs
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      const res = await fetch("/api/site-info", {
 | 
				
			||||||
 | 
					        method: "POST",
 | 
				
			||||||
 | 
					        headers: { "Content-Type": "application/json" },
 | 
				
			||||||
 | 
					        body: JSON.stringify(payload)
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      const result = await res.json();
 | 
				
			||||||
 | 
					      status.textContent = result.status === "ok" ? "✅ Saved!" : "❌ Error saving";
 | 
				
			||||||
 | 
					      setTimeout(() => status.textContent = "", 2000);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										109
									
								
								src/webui/site-info/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/webui/site-info/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					  <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
				
			||||||
 | 
					  <meta charset="UTF-8">
 | 
				
			||||||
 | 
					  <title>Lumeex</title>
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					  <!-- Top bar -->	
 | 
				
			||||||
 | 
					  <div class="nav-bar">
 | 
				
			||||||
 | 
					    <div class="content-inner nav">
 | 
				
			||||||
 | 
					      <div class="nav-cta">
 | 
				
			||||||
 | 
					        <div class="arrow">→</div>
 | 
				
			||||||
 | 
					        <a class="button" href="#" target="_blank">
 | 
				
			||||||
 | 
					          <span id="step">🚀 Build !<i class="fa-solid fa-envelope"></i></span>
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <input type="checkbox" id="nav-check">
 | 
				
			||||||
 | 
					      <div class="nav-header">
 | 
				
			||||||
 | 
					        <div class="nav-title">
 | 
				
			||||||
 | 
					          <img src="{{ url_for('static', filename='img/logo.svg') }}">
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="nav-btn">
 | 
				
			||||||
 | 
					        <label for="nav-check">
 | 
				
			||||||
 | 
					          <span></span>
 | 
				
			||||||
 | 
					          <span></span>
 | 
				
			||||||
 | 
					          <span></span>
 | 
				
			||||||
 | 
					        </label>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="nav-links">
 | 
				
			||||||
 | 
					        <ul class="nav-list">
 | 
				
			||||||
 | 
					          <li class="nav-item appear2"><a href="#">Site info</a>
 | 
				
			||||||
 | 
					          <li class="nav-item appear2"><a href="#">Theme info</a>
 | 
				
			||||||
 | 
					          <li class="nav-item appear2"><a href="#">Gallery</a>
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <!-- Toast container for notifications -->
 | 
				
			||||||
 | 
					  <div class="content-inner">
 | 
				
			||||||
 | 
					    <div id="toast-container"></div>
 | 
				
			||||||
 | 
					    <h1>Edit Site Info</h1>
 | 
				
			||||||
 | 
					    <form id="site-info-form">
 | 
				
			||||||
 | 
					      <fieldset>
 | 
				
			||||||
 | 
					        <legend>Info</legend>
 | 
				
			||||||
 | 
					        <label>Title: <input type="text" name="info.title"></label><br>
 | 
				
			||||||
 | 
					        <label>Subtitle: <input type="text" name="info.subtitle"></label><br>
 | 
				
			||||||
 | 
					        <label>Description: <textarea name="info.description"></textarea></label><br>
 | 
				
			||||||
 | 
					        <label>Canonical URL: <input type="text" name="info.canonical"></label><br>
 | 
				
			||||||
 | 
					        <label>Keywords (comma separated): <input type="text" name="info.keywords"></label><br>
 | 
				
			||||||
 | 
					        <label>Author: <input type="text" name="info.author"></label><br>
 | 
				
			||||||
 | 
					      </fieldset>
 | 
				
			||||||
 | 
					      <fieldset>
 | 
				
			||||||
 | 
					        <legend>Social</legend>
 | 
				
			||||||
 | 
					        <label>Instagram URL: <input type="text" name="social.instagram_url"></label><br>
 | 
				
			||||||
 | 
					        <label>Thumbnail: <input type="text" name="social.thumbnail"></label><br>
 | 
				
			||||||
 | 
					      </fieldset>
 | 
				
			||||||
 | 
					      <fieldset>
 | 
				
			||||||
 | 
					        <legend>Menu</legend>
 | 
				
			||||||
 | 
					        <div id="menu-items-list"></div>
 | 
				
			||||||
 | 
					        <button type="button" id="add-menu-item">+ Add menu item</button>
 | 
				
			||||||
 | 
					      </fieldset>
 | 
				
			||||||
 | 
					      <fieldset>
 | 
				
			||||||
 | 
					        <legend>Footer</legend>
 | 
				
			||||||
 | 
					        <label>Copyright: <input type="text" name="footer.copyright"></label><br>
 | 
				
			||||||
 | 
					        <label>Legal Label: <input type="text" name="footer.legal_label"></label><br>
 | 
				
			||||||
 | 
					      </fieldset>
 | 
				
			||||||
 | 
					      <fieldset>
 | 
				
			||||||
 | 
					        <legend>Build</legend>
 | 
				
			||||||
 | 
					        <label>Theme: <input type="text" name="build.theme"></label><br>
 | 
				
			||||||
 | 
					        <label>
 | 
				
			||||||
 | 
					            <input type="checkbox" name="build.convert_images" id="convert-images-checkbox">
 | 
				
			||||||
 | 
					            Convert images
 | 
				
			||||||
 | 
					        </label><br>
 | 
				
			||||||
 | 
					        <label>
 | 
				
			||||||
 | 
					            <input type="checkbox" name="build.resize_images" id="resize-images-checkbox">
 | 
				
			||||||
 | 
					            Resize images
 | 
				
			||||||
 | 
					        </label><br>
 | 
				
			||||||
 | 
					    </fieldset>
 | 
				
			||||||
 | 
					      <fieldset>
 | 
				
			||||||
 | 
					        <legend>Legals</legend>
 | 
				
			||||||
 | 
					        <label>Hoster Name: <input type="text" name="legals.hoster_name"></label><br>
 | 
				
			||||||
 | 
					        <label>Hoster Address: <input type="text" name="legals.hoster_adress"></label><br>
 | 
				
			||||||
 | 
					        <label>Hoster Contact: <input type="text" name="legals.hoster_contact"></label><br>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					            <label>Intellectual Property:</label>
 | 
				
			||||||
 | 
					            <div id="ip-list"></div>
 | 
				
			||||||
 | 
					            <button type="button" id="add-ip-paragraph">+ Add paragraph</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </fieldset>
 | 
				
			||||||
 | 
					      <button type="submit">Save</button>
 | 
				
			||||||
 | 
					    </form>
 | 
				
			||||||
 | 
					    <div id="site-info-status"></div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <div id="delete-modal" class="modal" style="display:none;">
 | 
				
			||||||
 | 
					    <div class="modal-content">
 | 
				
			||||||
 | 
					      <span id="delete-modal-close" class="modal-close">×</span>
 | 
				
			||||||
 | 
					      <h3>Confirm Deletion</h3>
 | 
				
			||||||
 | 
					      <p id="delete-modal-text">Are you sure you want to delete this image?</p>
 | 
				
			||||||
 | 
					      <div class="modal-actions">
 | 
				
			||||||
 | 
					        <button id="delete-modal-confirm" class="modal-btn danger">Delete</button>
 | 
				
			||||||
 | 
					        <button id="delete-modal-cancel" class="modal-btn">Cancel</button>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  <script src="{{ url_for('static', filename='js/site-info.js') }}"></script>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user