diff --git a/assets/css/style.css b/assets/css/style.css index 2a4c2a7..58b3805 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -1,18 +1,760 @@ -html { scroll-behavior: smooth; } -*{ box-sizing: border-box; margin:0; padding:0;} +/* ========================================================================== + SYSTEM BLOCKNOTES - MAIN CSS (VANILLA CSS WITH HSL THEME VARIABLES) + ========================================================================== */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Fira+Code:wght@400;500&display=swap'); -.main-nav{ - display:flex; - width:100%; - gap: 1rem; +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background-color: #0b0f19; + color: #f1f5f9; + min-height: 100vh; + display: flex; + flex-direction: column; + line-height: 1.6; +} + +a { + color: var(--secCol14, #38bdf8); + text-decoration: none; + transition: all 0.2s ease; +} + +a:hover { + color: var(--secCol18, #bae6fd); +} + +/* --- HEADER & NAVIGATION --- */ +.navbar-header { + background: rgba(15, 23, 42, 0.85); + backdrop-filter: blur(12px); + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + position: sticky; + top: 0; + z-index: 100; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); +} + +.nav-container { + max-width: 1240px; + margin: 0 auto; + padding: 0.85rem 1.5rem; + display: flex; align-items: center; - justify-content: flex-start; - list-style-type: none; - padding:1rem; + justify-content: space-between; +} + +.brand-logo { + display: flex; + align-items: center; + gap: 0.6rem; + font-weight: 700; + font-size: 1.35rem; + color: #ffffff; + letter-spacing: -0.02em; +} + +.brand-logo .logo-icon { + font-size: 1.6rem; +} + +.main-nav { + display: flex; + align-items: center; + gap: 0.75rem; + list-style: none; } .main-nav a { + color: #94a3b8; + font-weight: 500; + font-size: 0.95rem; + padding: 0.5rem 0.9rem; + border-radius: 8px; + transition: all 0.2s ease; +} + +.main-nav a:hover, +.main-nav a.active { + color: #ffffff; + background: rgba(255, 255, 255, 0.08); +} + +.admin-link { + color: var(--thiCol15, #c084fc) !important; + background: rgba(192, 132, 252, 0.1) !important; + border: 1px solid rgba(192, 132, 252, 0.25); +} + +.admin-link:hover, +.admin-link.active { + background: rgba(192, 132, 252, 0.25) !important; + color: #ffffff !important; +} + +.user-badge { + display: inline-flex; + align-items: center; + font-size: 0.82rem; + font-weight: 600; + padding: 0.35rem 0.75rem; + border-radius: 20px; + background: rgba(255, 255, 255, 0.06); + border: 1px solid rgba(255, 255, 255, 0.1); + color: #e2e8f0; +} + +.badge-admin { + background: rgba(234, 179, 8, 0.15); + border-color: rgba(234, 179, 8, 0.3); + color: #fde047; +} + +.nav-logout-btn { + color: #ef4444 !important; + background: rgba(239, 68, 68, 0.08) !important; +} + +.nav-logout-btn:hover { + background: rgba(239, 68, 68, 0.2) !important; + color: #fca5a5 !important; +} + +/* --- MAIN WRAPPER & FOOTER --- */ +.main-wrapper { + max-width: 1240px; + width: 100%; + margin: 0 auto; + padding: 2rem 1.5rem; + flex: 1; +} + +.main-footer { + border-top: 1px solid rgba(255, 255, 255, 0.08); + background: #090d16; + padding: 1.5rem 0; + margin-top: auto; + color: #64748b; + font-size: 0.85rem; + text-align: center; +} + +.footer-container { + max-width: 1240px; + margin: 0 auto; + padding: 0 1.5rem; + display: flex; + flex-direction: column; + gap: 0.3rem; +} + +/* --- PAGE HEADERS & CARDS --- */ +.page-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 2rem; + flex-wrap: wrap; + gap: 1rem; +} + +.page-title-group h1 { + font-size: 1.85rem; + font-weight: 700; + color: #ffffff; + margin-bottom: 0.3rem; +} + +.page-title-group p { + color: #94a3b8; + font-size: 0.95rem; +} + +.card { + background: #131b2e; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 14px; + padding: 1.5rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); +} + +/* --- BUTTONS --- */ +.btn { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.65rem 1.2rem; + border-radius: 8px; + font-size: 0.9rem; + font-weight: 600; + border: none; + cursor: pointer; + transition: all 0.2s ease; text-decoration: none; } +.btn-sm { + padding: 0.4rem 0.75rem; + font-size: 0.82rem; + border-radius: 6px; +} + +.btn-primary { + background: var(--priCol10, #22c55e); + color: #042f13; +} + +.btn-primary:hover { + background: var(--priCol12, #4ade80); + color: #042f13; + box-shadow: 0 0 15px rgba(34, 197, 94, 0.4); +} + +.btn-secondary { + background: var(--secCol8, #0284c7); + color: #ffffff; +} + +.btn-secondary:hover { + background: var(--secCol10, #0ea5e9); + color: #ffffff; + box-shadow: 0 0 15px rgba(14, 165, 233, 0.4); +} + +.btn-dark { + background: rgba(255, 255, 255, 0.08); + color: #e2e8f0; + border: 1px solid rgba(255, 255, 255, 0.12); +} + +.btn-dark:hover { + background: rgba(255, 255, 255, 0.15); + color: #ffffff; +} + +.btn-danger { + background: rgba(239, 68, 68, 0.15); + color: #f87171; + border: 1px solid rgba(239, 68, 68, 0.3); +} + +.btn-danger:hover { + background: #ef4444; + color: #ffffff; +} + +/* --- BLOCKNOTE GRID & CARDS --- */ +.blocknotes-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + gap: 1.5rem; +} + +.blocknote-card { + background: #131b2e; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 14px; + padding: 1.5rem; + display: flex; + flex-direction: column; + gap: 1rem; + transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; + position: relative; +} + +.blocknote-card:hover { + transform: translateY(-4px); + border-color: rgba(255, 255, 255, 0.2); + box-shadow: 0 12px 35px rgba(0, 0, 0, 0.35); +} + +.blocknote-card-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 0.75rem; +} + +.blocknote-title { + font-size: 1.25rem; + font-weight: 700; + color: #ffffff; + line-height: 1.3; +} + +.blocknote-owner-tag { + font-size: 0.75rem; + font-weight: 600; + padding: 0.25rem 0.6rem; + border-radius: 20px; + background: rgba(14, 165, 233, 0.15); + color: #7dd3fc; + border: 1px solid rgba(14, 165, 233, 0.3); + white-space: nowrap; +} + +.blocknote-card-body { + font-size: 0.9rem; + color: #94a3b8; + flex: 1; +} + +.blocknote-meta { + display: flex; + align-items: center; + gap: 1rem; + font-size: 0.8rem; + color: #64748b; +} + +.blocknote-card-actions { + display: flex; + align-items: center; + gap: 0.5rem; + padding-top: 1rem; + border-top: 1px solid rgba(255, 255, 255, 0.06); +} + +/* --- VIEW BLOCKNOTE DETAILED NOTE BLOCKS --- */ +.notes-container { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.note-block { + background: #131b2e; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 16px; + overflow: hidden; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.25); + transition: all 0.25s ease; +} + +.note-header { + background: rgba(255, 255, 255, 0.03); + padding: 1.1rem 1.5rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + cursor: pointer; + user-select: none; + transition: background 0.2s ease; +} + +.note-header:hover { + background: rgba(255, 255, 255, 0.06); +} + +.note-title-wrapper { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.collapse-icon { + font-size: 0.85rem; + color: var(--secCol14, #38bdf8); + transition: transform 0.25s ease; + display: inline-block; +} + +.note-block.collapsed .collapse-icon { + transform: rotate(0deg); +} + +.note-block:not(.collapsed) .collapse-icon { + transform: rotate(90deg); +} + +.subnote-count-badge { + font-size: 0.78rem; + font-weight: 600; + padding: 0.2rem 0.6rem; + border-radius: 12px; + background: rgba(255, 255, 255, 0.07); + color: #94a3b8; + border: 1px solid rgba(255, 255, 255, 0.1); +} + +.note-block.collapsed .note-header { + border-bottom: none; +} + +.note-block.collapsed .note-body { + display: none; +} + +.note-description-box { + padding: 1rem 1.5rem 0.5rem 1.5rem; + font-size: 0.92rem; + color: #94a3b8; + border-bottom: 1px dashed rgba(255, 255, 255, 0.08); +} + +.note-title-area h3 { + font-size: 1.25rem; + font-weight: 700; + color: #ffffff; + margin-bottom: 0; +} + +.note-actions { + display: flex; + align-items: center; + gap: 0.5rem; + flex-shrink: 0; +} + +.subnotes-list { + padding: 1.5rem; + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.subnote-card { + background: #1a243a; + border: 1px solid rgba(255, 255, 255, 0.07); + border-radius: 12px; + padding: 1.25rem; + position: relative; +} + +.subnote-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.85rem; +} + +.subnote-title-group { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.subnote-title { + font-size: 1.05rem; + font-weight: 600; + color: #f1f5f9; +} + +.type-badge { + font-size: 0.72rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 0.2rem 0.5rem; + border-radius: 4px; +} + +.type-badge.text { + background: rgba(148, 163, 184, 0.15); + color: #cbd5e1; +} + +.type-badge.command { + background: rgba(234, 179, 8, 0.15); + color: #fde047; + border: 1px solid rgba(234, 179, 8, 0.3); +} + +.subnote-content-text { + font-size: 0.95rem; + color: #cbd5e1; + white-space: pre-wrap; + background: rgba(0, 0, 0, 0.2); + padding: 0.85rem 1rem; + border-radius: 8px; + border-left: 3px solid var(--secCol10, #0ea5e9); +} + +/* --- COMMAND BLOCK WITH COPY BUTTON --- */ +.command-block-wrapper { + position: relative; + background: #050811; + border: 1px solid rgba(234, 179, 8, 0.35); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5), inset 0 0 10px rgba(234, 179, 8, 0.03); + border-radius: 10px; + overflow: hidden; + margin-top: 0.75rem; +} + +.command-block-header { + display: flex; + align-items: center; + justify-content: space-between; + background: rgba(234, 179, 8, 0.12); + padding: 0.45rem 0.9rem; + border-bottom: 1px solid rgba(234, 179, 8, 0.2); + font-size: 0.8rem; + font-family: 'Fira Code', 'Monaco', 'Consolas', monospace; + color: #fde047; + font-weight: 600; +} + +.command-badge-title { + display: inline-flex; + align-items: center; + gap: 0.4rem; + font-size: 0.82rem; + letter-spacing: 0.3px; +} + +.copy-btn { + background: rgba(255, 255, 255, 0.1); + color: #ffffff; + border: 1px solid rgba(255, 255, 255, 0.25); + padding: 0.3rem 0.75rem; + border-radius: 6px; + font-size: 0.78rem; + font-weight: 600; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 0.35rem; + transition: all 0.2s ease; +} + +.copy-btn:hover { + background: #eab308; + color: #000000; + border-color: #eab308; + box-shadow: 0 0 10px rgba(234, 179, 8, 0.4); +} + +.copy-btn.copied { + background: #22c55e !important; + color: #042f13 !important; + border-color: #22c55e !important; +} + +.command-content-code { + font-family: 'Fira Code', 'Cascadia Code', 'Monaco', 'Consolas', monospace; + font-size: 0.88rem; + line-height: 1.5; + color: #4ade80; + background: #03060d; + padding: 0.85rem 1rem; + overflow-x: auto; + overflow-y: auto; + max-height: 320px; + white-space: pre; + margin: 0; + border-top: 1px solid rgba(0, 0, 0, 0.6); +} + +.command-content-code code { + font-family: inherit; + color: inherit; +} + +/* --- FORMS & INPUTS --- */ +.form-group { + margin-bottom: 1.25rem; +} + +.form-group label { + display: block; + font-size: 0.88rem; + font-weight: 600; + color: #cbd5e1; + margin-bottom: 0.4rem; +} + +.form-control { + width: 100%; + padding: 0.75rem 1rem; + border-radius: 8px; + background: #090d16; + border: 1px solid rgba(255, 255, 255, 0.12); + color: #ffffff; + font-size: 0.95rem; + font-family: inherit; + transition: border-color 0.2s ease, box-shadow 0.2s ease; +} + +.form-control:focus { + outline: none; + border-color: var(--secCol10, #0ea5e9); + box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.2); +} + +textarea.form-control { + resize: vertical; + min-height: 100px; +} + +select.form-control { + cursor: pointer; +} + +/* --- MODAL --- */ +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: rgba(0, 0, 0, 0.75); + backdrop-filter: blur(6px); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + opacity: 0; + visibility: hidden; + transition: all 0.25s ease; +} + +.modal-backdrop.active { + opacity: 1; + visibility: visible; +} + +.modal-card { + background: #131b2e; + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 16px; + width: 100%; + max-width: 540px; + padding: 1.75rem; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5); + transform: translateY(20px); + transition: transform 0.25s ease; +} + +.modal-backdrop.active .modal-card { + transform: translateY(0); +} + +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1.25rem; + padding-bottom: 0.75rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); +} + +.modal-header h3 { + font-size: 1.25rem; + font-weight: 700; + color: #ffffff; +} + +.modal-close-btn { + background: transparent; + border: none; + color: #94a3b8; + font-size: 1.5rem; + cursor: pointer; + line-height: 1; +} + +.modal-close-btn:hover { + color: #ffffff; +} + +/* --- ALERTS & NOTIFICATIONS --- */ +.alert { + padding: 0.9rem 1.2rem; + border-radius: 10px; + margin-bottom: 1.5rem; + font-size: 0.92rem; + font-weight: 500; + display: flex; + align-items: center; + gap: 0.6rem; +} + +.alert-success { + background: rgba(34, 197, 94, 0.15); + border: 1px solid rgba(34, 197, 94, 0.3); + color: #4ade80; +} + +.alert-danger { + background: rgba(239, 68, 68, 0.15); + border: 1px solid rgba(239, 68, 68, 0.3); + color: #f87171; +} + +/* --- TABLES (ADMIN) --- */ +.table-responsive { + overflow-x: auto; +} + +.custom-table { + width: 100%; + border-collapse: collapse; + text-align: left; + font-size: 0.9rem; +} + +.custom-table th { + background: rgba(255, 255, 255, 0.04); + color: #cbd5e1; + font-weight: 600; + padding: 0.85rem 1rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); +} + +.custom-table td { + padding: 0.85rem 1rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); + color: #e2e8f0; +} + +.custom-table tr:hover td { + background: rgba(255, 255, 255, 0.02); +} + +/* --- COLOR PICKER PREVIEW --- */ +.color-preview-box { + height: 60px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + color: #0f172a; + margin-top: 0.5rem; +} + +/* Empty state */ +.empty-state { + text-align: center; + padding: 3rem 1rem; + background: #131b2e; + border: 1px border-dash rgba(255, 255, 255, 0.1); + border-radius: 16px; +} + +.empty-state-icon { + font-size: 3rem; + margin-bottom: 1rem; + opacity: 0.7; +} + +.empty-state h3 { + font-size: 1.3rem; + font-weight: 700; + color: #ffffff; + margin-bottom: 0.5rem; +} + +.empty-state p { + color: #94a3b8; + max-width: 400px; + margin: 0 auto 1.5rem auto; +} diff --git a/assets/js/main.js b/assets/js/main.js index 5f8d470..048d4fc 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -1,9 +1,211 @@ -/* @ default js*/ -function yearNow(textlocation) { - //This is a function to substitutes a value into the current year. - //Usually good for the footer. - var data = new Date(); - var year; - year = data.getFullYear(); - document.querySelector(textlocation).replaceWith(year); +/** + * Blocknotes Application Main JavaScript (Vanilla JS) + */ + +document.addEventListener("DOMContentLoaded", function() { + + // --- 1. MODALS OPEN & CLOSE HANDLERS --- + document.querySelectorAll('[data-modal-target]').forEach(function(trigger) { + trigger.addEventListener('click', function(e) { + e.preventDefault(); + const modalId = this.getAttribute('data-modal-target'); + const modal = document.getElementById(modalId); + if (modal) { + modal.classList.add('active'); + } + }); + }); + + document.querySelectorAll('.modal-close-btn, .modal-close-trigger').forEach(function(closeBtn) { + closeBtn.addEventListener('click', function() { + const modal = this.closest('.modal-backdrop'); + if (modal) { + modal.classList.remove('active'); + } + }); + }); + + // Close modal when clicking on backdrop outside modal-card + document.querySelectorAll('.modal-backdrop').forEach(function(backdrop) { + backdrop.addEventListener('click', function(e) { + if (e.target === this) { + this.classList.remove('active'); + } + }); + }); + + // --- 2. LIVE HSL THEME COLOR PREVIEW (PROFILE PAGE) --- + const priInput = document.getElementById('theme_pri'); + const secInput = document.getElementById('theme_sec'); + const thiInput = document.getElementById('theme_thi'); + + if (priInput && secInput && thiInput) { + function updateLiveTheme() { + const priVal = priInput.value; + const secVal = secInput.value; + const thiVal = thiInput.value; + + document.documentElement.style.setProperty('--pri', priVal); + document.documentElement.style.setProperty('--sec', secVal); + document.documentElement.style.setProperty('--thi', thiVal); + + const priValDisp = document.getElementById('pri_val'); + const secValDisp = document.getElementById('sec_val'); + const thiValDisp = document.getElementById('thi_val'); + if (priValDisp) priValDisp.textContent = priVal + 'ยฐ'; + if (secValDisp) secValDisp.textContent = secVal + 'ยฐ'; + if (thiValDisp) thiValDisp.textContent = thiVal + 'ยฐ'; + } + + priInput.addEventListener('input', updateLiveTheme); + secInput.addEventListener('input', updateLiveTheme); + thiInput.addEventListener('input', updateLiveTheme); + } +}); + +/** + * Copy text to clipboard with fallback and localized feedback + */ +function copyToClipboard(btnElement, textToCopy) { + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(textToCopy).then(() => { + handleCopySuccess(btnElement); + }).catch(() => { + fallbackCopyText(btnElement, textToCopy); + }); + } else { + fallbackCopyText(btnElement, textToCopy); + } +} + +function fallbackCopyText(btnElement, textToCopy) { + let textArea = document.createElement("textarea"); + textArea.value = textToCopy; + textArea.style.position = "fixed"; + textArea.style.left = "-999999px"; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + try { + document.execCommand('copy'); + handleCopySuccess(btnElement); + } catch (err) { + console.error('Fallback copy failed', err); + } + document.body.removeChild(textArea); +} + +function handleCopySuccess(btnElement) { + if (!btnElement) return; + const originalHTML = btnElement.innerHTML; + const copiedLabel = btnElement.getAttribute('data-copied-text') || 'โœ“ Copiato!'; + btnElement.innerHTML = copiedLabel; + btnElement.classList.add('copied'); + setTimeout(() => { + btnElement.innerHTML = originalHTML; + btnElement.classList.remove('copied'); + }, 2000); +} + +/** + * Toggle collapse state for a specific note block + */ +function toggleNoteCollapse(noteId) { + const noteBlock = document.getElementById('note-block-' + noteId); + if (noteBlock) { + noteBlock.classList.toggle('collapsed'); + } +} + +/** + * Expand all note blocks on page + */ +function expandAllNotes() { + document.querySelectorAll('.note-block').forEach(function(nb) { + nb.classList.remove('collapsed'); + }); +} + +/** + * Collapse all note blocks on page + */ +function collapseAllNotes() { + document.querySelectorAll('.note-block').forEach(function(nb) { + nb.classList.add('collapsed'); + }); +} + +/** + * Filter notes and subnotes within the current blocknote in real-time + */ +function filterCurrentBlocknoteNotes(query) { + const term = query.toLowerCase().trim(); + const clearBtn = document.getElementById('clear-search-btn'); + const noResults = document.getElementById('no-search-results'); + const noteBlocks = document.querySelectorAll('.note-block'); + + if (clearBtn) { + clearBtn.style.display = term ? 'inline-flex' : 'none'; + } + + let totalMatches = 0; + + noteBlocks.forEach(function(noteBlock) { + if (!term) { + // Reset search + noteBlock.style.display = ''; + noteBlock.classList.add('collapsed'); + noteBlock.querySelectorAll('.subnote-card').forEach(function(sub) { + sub.style.display = ''; + }); + totalMatches++; + return; + } + + const noteTitle = (noteBlock.querySelector('.note-title-wrapper h3')?.innerText || '').toLowerCase(); + const noteDesc = (noteBlock.querySelector('.note-description-box p')?.innerText || '').toLowerCase(); + const noteTitleMatch = noteTitle.includes(term) || noteDesc.includes(term); + + let subnoteMatchCount = 0; + const subnoteCards = noteBlock.querySelectorAll('.subnote-card'); + + subnoteCards.forEach(function(subCard) { + const subTitle = (subCard.querySelector('.subnote-title')?.innerText || '').toLowerCase(); + const subContent = (subCard.querySelector('.subnote-content-text, .command-code')?.innerText || '').toLowerCase(); + + if (noteTitleMatch || subTitle.includes(term) || subContent.includes(term)) { + subCard.style.display = ''; + subnoteMatchCount++; + } else { + subCard.style.display = 'none'; + } + }); + + if (noteTitleMatch || subnoteMatchCount > 0) { + noteBlock.style.display = ''; + noteBlock.classList.remove('collapsed'); // Auto expand on match + totalMatches++; + } else { + noteBlock.style.display = 'none'; + } + }); + + if (noResults) { + if (term && totalMatches === 0) { + noResults.style.display = 'block'; + } else { + noResults.style.display = 'none'; + } + } +} + +/** + * Clear search input and restore view + */ +function clearNoteSearch() { + const input = document.getElementById('note-search-input'); + if (input) { + input.value = ''; + filterCurrentBlocknoteNotes(''); + } } \ No newline at end of file diff --git a/blocknotes/bn_6a8589d13417a.json b/blocknotes/bn_6a8589d13417a.json new file mode 100644 index 0000000..df89546 --- /dev/null +++ b/blocknotes/bn_6a8589d13417a.json @@ -0,0 +1,41 @@ +{ + "id": "bn_6a8589d13417a", + "title": "Comandi Nginx e Server", + "username": "admin", + "created_at": "2026-08-19 10:47:45", + "updated_at": "2026-08-19 10:54:03", + "notes": [ + { + "id": "note_6a8589d1343d6", + "title": "Configurazione Nginx", + "description": "Comandi principali per riavviare e testare Nginx", + "subnotes": [ + { + "id": "sub_6a8589d1344ba", + "title": "Riavvia Nginx", + "type": "command", + "content": "sudo systemctl restart nginx" + }, + { + "id": "sub_6a8589d1344f5", + "title": "Note di Sicurezza", + "type": "text", + "content": "Assicurarsi che la porta 80 e 443 siano aperte sul firewall UFW." + } + ] + }, + { + "id": "note_6a858a699ba2c", + "title": "Test2", + "description": "Questa e' una nota di prova .", + "subnotes": [ + { + "id": "sub_6a858aede4049", + "title": "Prova 2", + "type": "command", + "content": "Questo e' un comando!" + } + ] + } + ] +} \ No newline at end of file diff --git a/blocknotes/bn_positive_internet.json b/blocknotes/bn_positive_internet.json new file mode 100644 index 0000000..d0246ac --- /dev/null +++ b/blocknotes/bn_positive_internet.json @@ -0,0 +1,444 @@ +{ + "id": "bn_positive_internet", + "title": "Positive Internet", + "username": "admin", + "created_at": "2026-08-19 10:56:45", + "updated_at": "2026-08-19 10:56:45", + "notes": [ + { + "id": "note_1_6a858bed6abbd", + "title": "Login Egguburt", + "description": "Login details", + "subnotes": [ + { + "id": "sub_1_6a858bed6abbf", + "title": "Eggburt", + "type": "command", + "content": "I want to Admin Positive Servers!" + }, + { + "id": "sub_2_6a858bed6abc0", + "title": "Timpani", + "type": "command", + "content": "I want to administer Timpani." + }, + { + "id": "sub_3_6a858bed6abc1", + "title": "## How to connect in Timpani ##", + "type": "command", + "content": "From Eggburt: ssh timpani@timpops\r\nPassword: 99flak3\r\ntimpss 'servername'" + } + ] + }, + { + "id": "note_2_6a858bed6abcc", + "title": "Varnish", + "description": "Varnish collection of commands", + "subnotes": [ + { + "id": "sub_1_6a858bed6abcd", + "title": "Varnish Domain Clean Cache", + "type": "command", + "content": "varnishadm \"ban req.http.host ~ DOMAIN\"" + }, + { + "id": "sub_2_6a858bed6abce", + "title": "Check the varnish syntax command:", + "type": "command", + "content": "varnishd -C -f \/etc\/varnish\/default.vcl" + } + ] + }, + { + "id": "note_3_6a858bed6abcf", + "title": "Find Spam IP", + "description": "findspam commands", + "subnotes": [ + { + "id": "sub_1_6a858bed6abd0", + "title": "Spam in fw-a", + "type": "command", + "content": "tail -n3000 \/var\/log\/nginx\/access.log | awk '{print $1}' | sort -nr | uniq -c | sort -nr | head -n15" + }, + { + "id": "sub_2_6a858bed6abd1", + "title": "Spam in Home Dir", + "type": "command", + "content": "tail -n3000 \/home\/*\/logs\/*access*log | awk '{print $1}' | sort -nr | uniq -c | sort -nr | head -n15" + }, + { + "id": "sub_3_6a858bed6abd2", + "title": "Spam in App (apache2)", + "type": "command", + "content": "tail -n3000 \/var\/log\/apache2\/access.log | awk '{print $1}' | sort -nr | uniq -c | sort -nr | head -n15" + }, + { + "id": "sub_4_6a858bed6abd3", + "title": "Check all kind of logs in nginx", + "type": "command", + "content": "tail -n3000 \/var\/log\/nginx\/*access*log* | awk '{print $1}' | sort -nr | uniq -c | sort -nr | head -n15" + } + ] + }, + { + "id": "note_4_6a858bed6abd4", + "title": "Clean Disks Space", + "description": "Cleaning Diskspaces for each kind of VMs", + "subnotes": [ + { + "id": "sub_1_6a858bed6abd5", + "title": "## Usually for App VM ##", + "type": "command", + "content": "apt clean; journalctl --vacuum-size=8M; logrotate -vf \/etc\/logrotate.d\/rsyslog; logrotate -vf \/etc\/logrotate.d\/apache2; logrotate -vf \/etc\/logrotate.d\/nginx; logrotate -vf \/etc\/logrotate.d\/varnish;" + }, + { + "id": "sub_2_6a858bed6abd6", + "title": "## Usually for firewall VM ##", + "type": "command", + "content": "apt clean; journalctl --vacuum-size=8M; logrotate -vf \/etc\/logrotate.d\/rsyslog; logrotate -vf \/etc\/logrotate.d\/nginx; logrotate -vf \/etc\/logrotate.d\/varnish;" + }, + { + "id": "sub_3_6a858bed6abd7", + "title": "## PHP ##", + "type": "command", + "content": "logrotate -vf \/etc\/logrotate.d\/php5.6-fpm; logrotate -vf \/etc\/logrotate.d\/php7.3-fpm; logrotate -vf \/etc\/logrotate.d\/php7.4-fpm; logrotate -vf \/etc\/logrotate.d\/php8.1-fpm; logrotate -vf \/etc\/logrotate.d\/php8.2-fpm;" + }, + { + "id": "sub_4_6a858bed6abd8", + "title": "## ONLY BASIC ##", + "type": "command", + "content": "apt clean; journalctl --vacuum-size=8M; logrotate -vf \/etc\/logrotate.d\/rsyslog;" + }, + { + "id": "sub_5_6a858bed6abd9", + "title": "VM + only apache2", + "type": "command", + "content": "apt clean; journalctl --vacuum-size=8M; logrotate -vf \/etc\/logrotate.d\/rsyslog; logrotate -vf \/etc\/logrotate.d\/apache2;" + } + ] + }, + { + "id": "note_5_6a858bed6abda", + "title": "Add findspam Script", + "description": "This is the findspam from the repository.", + "subnotes": [ + { + "id": "sub_1_6a858bed6abdb", + "title": "Command:", + "type": "command", + "content": "apt install -y jq; echo \"\" && echo \"Checking the reachability of the server...\"; ping -c 2 -W 2 scripts.svm.picl.co.uk > \/dev\/null 2>&1 && echo \"Server reachable. Proceeding with script update.\" || { echo \"Server unreachable. Update canceled.\"; exit 1; }; for i in updatebspamscripts findspam blockspamip spamwhat nobanip unblockspams; do echo \"Updating script $i...\"; curl -s -o \/usr\/local\/sbin\/$i https:\/\/scripts.svm.picl.co.uk\/scripts\/$i.sh && chmod 700 \/usr\/local\/sbin\/$i && echo \"Script '$i' updated.\" || echo \"Error updating script '$i'.\"; done" + } + ] + }, + { + "id": "note_6_6a858bed6abdc", + "title": "Create New Database", + "description": "Command to create new database and database use", + "subnotes": [ + { + "id": "sub_1_6a858bed6abdd", + "title": "## Create Databases ##", + "type": "command", + "content": "CREATE DATABASE unixuser;" + }, + { + "id": "sub_2_6a858bed6abde", + "title": "# Create User For DB #", + "type": "command", + "content": "grant all privileges on `unixuser`.* to `unixuser`@'%' identified by 'password database';" + } + ] + }, + { + "id": "note_7_6a858bed6abdf", + "title": "Rsync From Server to the other", + "description": "Command to rsync between the servers", + "subnotes": [ + { + "id": "sub_1_6a858bed6abe0", + "title": "## Rsync Command ##", + "type": "command", + "content": "rsync -avzP \/home\/user\/public_html\/ user@10.10.10.17:\/home\/user\/public_html\/" + } + ] + }, + { + "id": "note_8_6a858bed6abe1", + "title": "WP-CLI Commands", + "description": "Usefully commands for wp-cli. Note that you need to change \"from-website\" and \"to-website\" with the correct domain.", + "subnotes": [ + { + "id": "sub_1_6a858bed6abe2", + "title": "## Export DB ##", + "type": "command", + "content": "wp db export" + }, + { + "id": "sub_2_6a858bed6abe3", + "title": "## Change Links for wordpress ##", + "type": "command", + "content": "wp search-replace 'from-website.Com' 'to-website.com' --all-tables" + }, + { + "id": "sub_3_6a858bed6abe4", + "title": "Add New User (change the '###*' strings with the correct informations)", + "type": "command", + "content": "wp user create ###USERNAME ###EMAIL@DOMAIN --role=administrator --user_pass=\"###PASSWORD\"" + }, + { + "id": "sub_4_6a858bed6abe5", + "title": "Install WP-CLI (Use this as root user)", + "type": "command", + "content": "curl -O https:\/\/raw.githubusercontent.com\/wp-cli\/builds\/gh-pages\/phar\/wp-cli.phar; chmod +x wp-cli.phar; sudo mv wp-cli.phar \/usr\/local\/bin\/wp" + }, + { + "id": "sub_5_6a858bed6abe6", + "title": "Install varnish-cache plugin:", + "type": "command", + "content": "wp plugin install varnish-http-purge\r\nwp plugin activate varnish-http-purge" + }, + { + "id": "sub_6_6a858bed6abe7", + "title": "Configuration to add in wp-config.php for the cache plugin", + "type": "command", + "content": "define('VHP_VARNISH_IP','varnish-vip:6081');" + }, + { + "id": "sub_7_6a858bed6abe8", + "title": "(Run as Root) Checksums verify WP CLI on all \/home folders", + "type": "command", + "content": "for u in \/home\/*; do user=$(basename \"$u\"); for d in httpdocs public_html; do if [ -d \"$u\/$d\" ] && [ -f \"$u\/$d\/wp-config.php\" ]; then echo \"=== $user ($d) ===\"; sudo -u \"$user\" -H wp --path=\"$u\/$d\" core verify-checksums; echo; fi; done; done" + }, + { + "id": "sub_8_6a858bed6abe9", + "title": "verify checksum for a single website", + "type": "command", + "content": "wp core verify-checksums" + }, + { + "id": "sub_9_6a858bed6abea", + "title": "restore corefiles", + "type": "command", + "content": "wp core download --version=$(wp core version) --force" + }, + { + "id": "sub_10_6a858bed6abeb", + "title": "Create posops user", + "type": "command", + "content": "wp user create posops good@positive-internet.com --role=administrator --user_pass='P3dsfj7nYLaMxxXk'" + }, + { + "id": "sub_11_6a858bed6abec", + "title": "Cambiare la password di un utente", + "type": "command", + "content": "wp user update posops --user_pass='P3dsfj7nYLaMxxXk'" + } + ] + }, + { + "id": "note_9_6a858bed6abed", + "title": "Find Killig Process Error", + "description": "Command to check what has been killed in the VMs \/ Machine", + "subnotes": [ + { + "id": "sub_1_6a858bed6abee", + "title": "## How to check errors ##", + "type": "command", + "content": "grep -E '(reaper|oom)' \/var\/log\/syslog" + } + ] + }, + { + "id": "note_10_6a858bed6abef", + "title": "Generate SSH Key", + "description": "Command to generate ssh key", + "subnotes": [ + { + "id": "sub_1_6a858bed6abf0", + "title": "## ED25519 ##", + "type": "command", + "content": "ssh-keygen -t ed25519" + }, + { + "id": "sub_2_6a858bed6abf1", + "title": "## RSA ##", + "type": "command", + "content": "ssh-keygen -t rsa -b 4096" + } + ] + }, + { + "id": "note_11_6a858bed6abf2", + "title": "User Basic Auth", + "description": "How to create \/ add user for the basicauth.", + "subnotes": [ + { + "id": "sub_1_6a858bed6abf3", + "title": "## CREATE A FILE \/ USER (cancel existing one) ##", + "type": "command", + "content": "htpasswd -c \/etc\/nginx\/.htpasswd-name nuovo_utente" + }, + { + "id": "sub_2_6a858bed6abf4", + "title": "## ADD EXTRA USER ##", + "type": "command", + "content": "htpasswd \/etc\/nginx\/.htpasswd-name username" + }, + { + "id": "sub_3_6a858bed6abf5", + "title": "Block to add", + "type": "command", + "content": "auth_basic \"Restricted Access\";\r\n auth_basic_user_file \/etc\/nginx\/.htpasswd;" + }, + { + "id": "sub_4_6a858bed6abf6", + "title": "Ownership and privileges", + "type": "command", + "content": "htpassloc=\"etc\/nginx\/.htpasswd\";\r\nchown www-data:www-data $htpassloc;\r\nchmod 644 $htpassloc" + } + ] + }, + { + "id": "note_12_6a858bed6abf7", + "title": "Check DB Size", + "description": "Command to check the dimension of the databases; (needs to be specified the databases names)", + "subnotes": [ + { + "id": "sub_1_6a858bed6abf8", + "title": "## CHECK DB SIZE ##", + "type": "command", + "content": "SELECT table_schema AS \"Database\", ROUND(SUM(data_length + index_length) \/ 1024 \/ 1024, 2) AS \"Dimension (MB)\" FROM information_schema.tables WHERE table_schema IN (\"dbname1 \", \"dbname2\", \"dbname3\", \"dbname4\") GROUP BY table_schema;" + } + ] + }, + { + "id": "note_13_6a858bed6abf9", + "title": "Remove Files and keep the newest one", + "description": "Change the variable keep in base to how much newest file you need to keep!", + "subnotes": [ + { + "id": "sub_1_6a858bed6abfa", + "title": "## REMOVE OLDEST FILES CODE ##", + "type": "command", + "content": "count=0; keep=4; echo \"### FILE THAT WILL BE REMOVED: ###\"; echo \"-------------------------------\"; for i in $(ls -t); do if [ $count -lt $keep ]; then echo \"Keeping: $i\"; else echo \"Removing $i\"; fi; ((count++)); done; echo \"-------------------------------\"; echo \"Do you wish to continue?\"; echo \"y - Yes\"; echo \"default - no\"; read check; check=${check,,}; if [ \"$check\" == \"y\" ]; then echo \"#### REMOVING FILE ####\"; echo \"-------------------------------\"; count=0; for i in $(ls -t); do if [ $count -lt $keep ]; then echo \"Keeping: $i\"; else echo \"Removed $i\"; rm \"$i\"; fi; ((count++)); done; echo \"-------------------------------\"; else echo \"Not Removed\"; fi\r\n" + } + ] + }, + { + "id": "note_14_6a858bed6abfb", + "title": "Add Diskspace Command", + "description": "After increased the diskspace of the hard disk on proxmox, we can run this command to add the 100% of the diskspace to the root partition.", + "subnotes": [ + { + "id": "sub_1_6a858bed6abfc", + "title": "Command:", + "type": "command", + "content": "parted \/dev\/vda resizepart 1 100%; pvresize \/dev\/vda1; lvextend -l +100%FREE \/dev\/vgmain\/root; resize2fs \/dev\/vgmain\/root" + } + ] + }, + { + "id": "note_15_6a858bed6abfd", + "title": "New Certboot Certificate", + "description": "Install new certboot Certificate", + "subnotes": [ + { + "id": "sub_1_6a858bed6abfe", + "title": "Command:", + "type": "command", + "content": "certbot certonly --webroot -w \/var\/www\/html -d ###DOMAIN" + } + ] + }, + { + "id": "note_16_6a858bed6abff", + "title": "Extract pfx key", + "description": "Use this command to extract the key and the pem files from a .pfx file.\r\nChange the variables before to run the command.", + "subnotes": [ + { + "id": "sub_1_6a858bed6ac00", + "title": "Command:", + "type": "command", + "content": "PFX=\"FILE_NAME\";\r\nPASS=\"PASSWORD_PFX\";\r\nopenssl pkcs12 -in \"$PFX\" -passin pass:\"$PASS\" -nocerts -nodes -out server.key && \\\r\nopenssl pkcs12 -in \"$PFX\" -passin pass:\"$PASS\" -nokeys | \\\r\nsed -n '\/BEGIN CERTIFICATE\/,\/END CERTIFICATE\/p' > fullchain.pem\r\n" + } + ] + }, + { + "id": "note_17_6a858bed6ac01", + "title": "check if the ssh-key is valid", + "description": "With this command we can check if a ssh key is valid or not", + "subnotes": [ + { + "id": "sub_1_6a858bed6ac02", + "title": "Command:", + "type": "command", + "content": "FILENAME=\"path\/to\/the\/file\";\r\nssh-keygen -l -f $FILENAME;\r\n" + } + ] + }, + { + "id": "note_18_6a858bed6ac03", + "title": "checking valid rules for studiorep-fw", + "description": "This command is checking which of the following rules has been accepted for the php-rules", + "subnotes": [ + { + "id": "sub_1_6a858bed6ac04", + "title": "Command:", + "type": "command", + "content": "php-fpm8.3 -tt 2>&1 | grep -A50 \"\\[POOL_NAME\\]\"" + } + ] + }, + { + "id": "note_19_6a858bed6ac05", + "title": "Add new VM to spa", + "description": "Use this to add the new element to the spa command. Specify the Public IP, the port and the hostname", + "subnotes": [ + { + "id": "sub_1_6a858bed6ac06", + "title": "Command:", + "type": "command", + "content": "sudoedit \/home\/posops\/ansible\/hosts" + } + ] + }, + { + "id": "note_20_6a858bed6ac07", + "title": "PHP-FPM Scripts", + "description": "Script Collection for PHP-FPM", + "subnotes": [ + { + "id": "sub_1_6a858bed6ac08", + "title": "Restarting all php-fpm existing", + "type": "command", + "content": "for i in \/etc\/php\/*; do version=$(basename \"$i\"); service=\"php${version}-fpm\"; if systemctl list-units --type=service --all | grep -q \"$service\"; then echo \"[ $service ]\"; systemctl restart \"$service\"; systemctl status \"$service\" | grep Active; fi; done" + }, + { + "id": "sub_2_6a858bed6ac09", + "title": "Check the php-version for the vhost", + "type": "command", + "content": "VERSION=8.3; for i in *; do socket=$(grep -oP 'SetHandler\\s+\"proxy:unix:\\K[^|\"]+' \"$i\"); if [ -n \"$socket\" ]; then domain=$(basename \"$socket\" .sock); match=$(grep -iRl \"$domain\" \/etc\/php\/*\/fpm\/pool.d\/); if [ -n \"$match\" ]; then phpver=$(echo \"$match\" | sed -n 's|\/etc\/php\/\\([^\/]*\\)\/fpm\/.*|\\1|p' | sort -u); if echo \"$phpver\" | grep -q \"^$VERSION$\"; then echo \"[ $domain ]\"; echo \"php-fpm $phpver\"; fi; fi; fi; done" + }, + { + "id": "sub_3_6a858bed6ac0a", + "title": "Check the php-version for the vhost", + "type": "command", + "content": "exver=\"7.4\" instver=\"8.4\" ; for i in $(dpkg -l *php* | awk '{print $2}' | grep $exver); do apt install php$instver${i:6} -y; done" + } + ] + }, + { + "id": "note_21_6a858bed6ac0b", + "title": "ownership to 755 folder and 644 files", + "description": "Changing the ownership from 755 to the folders and 644 to the files (sub-directories included) with find + xarg in the current director.", + "subnotes": [ + { + "id": "sub_1_6a858bed6ac0c", + "title": "Command", + "type": "command", + "content": "find . -type d -print0 | xargs -0 chmod 755;\r\nfind . -type f -print0 | xargs -0 chmod 644" + } + ] + } + ] +} \ No newline at end of file diff --git a/config/constants.php b/config/constants.php index bc09841..3b3594a 100644 --- a/config/constants.php +++ b/config/constants.php @@ -54,8 +54,7 @@ $full_path = $const_before . $const_value; if (!is_dir($full_path) && !is_file($full_path)) { - debugStatusMes("The folder/file declared into '$const_name' constant is missing or not accessible. Please check it.", "High"); - die; + @mkdir($full_path, 0777, true); } if (!defined($const_name)) { diff --git a/config/db.php b/config/db.php index 219ad75..9666807 100644 --- a/config/db.php +++ b/config/db.php @@ -1,21 +1,104 @@ connect_error) { + header("Location: first_start.php"); + exit; + } + $test_conn->close(); + } catch (Exception $ex) { + header("Location: first_start.php"); + exit; } -} catch (mysqli_sql_exception $e) { - // Access Deny - include(PAGES_FOLD . "db-error.php"); - debugStatusMes("Database connection failed.", "High"); - +} - die(); +try { + // Create Connection to MySQL host first (in case DB doesn't exist yet) + $conn = new mysqli(DB_HOST, DB_USER, DB_PASS); + + // Create Database if not exists + $db_name_escaped = str_replace('`', '``', DB_NAME); + $conn->query("CREATE DATABASE IF NOT EXISTS `$db_name_escaped` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"); + $conn->select_db(DB_NAME); + + // Ensure BLOCKNOTES_FOLD directory exists + if (!is_dir(BLOCKNOTES_FOLD)) { + @mkdir(BLOCKNOTES_FOLD, 0777, true); + } + + // Auto-create users table if missing (with ENUM containing 'owner') + $sql_table = "CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + role ENUM('owner', 'admin', 'user') NOT NULL DEFAULT 'user', + theme_pri INT DEFAULT 98, + theme_sec INT DEFAULT 207, + theme_thi INT DEFAULT 280, + language VARCHAR(5) NOT NULL DEFAULT 'it', + default_blocknote VARCHAR(100) NOT NULL DEFAULT '', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"; + $conn->query($sql_table); + + // Auto-migrate role column ENUM if missing 'owner' + $conn->query("ALTER TABLE users MODIFY COLUMN role ENUM('owner', 'admin', 'user') NOT NULL DEFAULT 'user'"); + + // Auto-migrate columns if missing in existing table + $check_lang = $conn->query("SHOW COLUMNS FROM users LIKE 'language'"); + if ($check_lang->num_rows === 0) { + $conn->query("ALTER TABLE users ADD COLUMN language VARCHAR(5) NOT NULL DEFAULT 'it'"); + } + + $check_def_bn = $conn->query("SHOW COLUMNS FROM users LIKE 'default_blocknote'"); + if ($check_def_bn->num_rows === 0) { + $conn->query("ALTER TABLE users ADD COLUMN default_blocknote VARCHAR(100) NOT NULL DEFAULT ''"); + } + + // Auto-create site_settings table + $sql_settings = "CREATE TABLE IF NOT EXISTS site_settings ( + setting_key VARCHAR(50) PRIMARY KEY, + setting_value TEXT + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"; + $conn->query($sql_settings); + + // Seed default site settings if missing + $conn->query("INSERT IGNORE INTO site_settings (setting_key, setting_value) VALUES ('site_title', 'Blocknotes')"); + $conn->query("INSERT IGNORE INTO site_settings (setting_key, setting_value) VALUES ('site_favicon', 'assets/img/favicon.png')"); + + // Seed default owner account if table is empty or ensure admin user is owner if no owner exists + $res = $conn->query("SELECT COUNT(*) AS total FROM users"); + $row = $res->fetch_assoc(); + if ((int)$row['total'] === 0) { + $default_pass = password_hash("admin123", PASSWORD_DEFAULT); + $admin_user = "admin"; + $stmt = $conn->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, 'owner')"); + $stmt->bind_param("ss", $admin_user, $default_pass); + $stmt->execute(); + $stmt->close(); + } else { + // Check if an owner exists, if not, promote the first user to owner + $check_owner = $conn->query("SELECT COUNT(*) AS total_owners FROM users WHERE role = 'owner'"); + $owner_row = $check_owner->fetch_assoc(); + if ((int)$owner_row['total_owners'] === 0) { + $conn->query("UPDATE users SET role = 'owner' ORDER BY id ASC LIMIT 1"); + } + } + +} catch (mysqli_sql_exception $e) { + if (file_exists($first_start_path) && basename($_SERVER['SCRIPT_FILENAME'] ?? '') !== 'first_start.php') { + header("Location: first_start.php"); + exit; + } + include(PAGES_FOLD . "db-error.php"); + debugStatusMes("Database setup error: " . $e->getMessage(), "High"); + die(); } ?> \ No newline at end of file diff --git a/config/functions_init.php b/config/functions_init.php index 3b0a610..8eb752d 100644 --- a/config/functions_init.php +++ b/config/functions_init.php @@ -22,7 +22,7 @@ function get_current_page() { $page = $_GET["page"] ?? "home"; $page = strtolower(trim($page)); - $page = preg_replace("/[^a-z0-9\-]/", "", $page); + $page = preg_replace("/[^a-z0-9\-_]/", "", $page); if ($page === "") { $page = "home"; diff --git a/config/init.php b/config/init.php index 47e238b..5714e8f 100644 --- a/config/init.php +++ b/config/init.php @@ -5,12 +5,14 @@ switch($env){ /*------ DON'T TOUCH THIS PART ------ */ case "generic": - define('WEB_ROOT', $_SERVER['DOCUMENT_ROOT']); - define('DOMAIN', $_SERVER['HTTP_HOST']); + $doc_root = !empty($_SERVER['DOCUMENT_ROOT']) ? $_SERVER['DOCUMENT_ROOT'] : dirname(__DIR__); + define('WEB_ROOT', $doc_root); + $host = !empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; + define('DOMAIN', $host); $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://'; - define('FULL_DOMAIN', $protocol . $_SERVER['HTTP_HOST']); - define("ROOT_URL", "http://" . DOMAIN ); - define("ROOT_DIR", WEB_ROOT . "/"); + define('FULL_DOMAIN', $protocol . DOMAIN); + define("ROOT_URL", $protocol . DOMAIN ); + define("ROOT_DIR", rtrim(WEB_ROOT, '/') . "/"); break; /* ----------------------------------- */ default: diff --git a/config/settings.php b/config/settings.php index 5a4a6da..2ddd809 100644 --- a/config/settings.php +++ b/config/settings.php @@ -1,15 +1,15 @@ connect_error) { + $msg_error = "Connessione MySQL fallita: " . $conn->connect_error; + } else { + // Create database + $db_name_escaped = str_replace('`', '``', $db_name); + if (!$conn->query("CREATE DATABASE IF NOT EXISTS `$db_name_escaped` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci")) { + $msg_error = "Impossibile creare il database '$db_name': " . $conn->error; + } else { + $conn->select_db($db_name); + + // Create users table + $sql_users = "CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(50) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + role ENUM('owner', 'admin', 'user') NOT NULL DEFAULT 'user', + theme_pri INT DEFAULT 98, + theme_sec INT DEFAULT 207, + theme_thi INT DEFAULT 280, + language VARCHAR(5) NOT NULL DEFAULT 'it', + default_blocknote VARCHAR(100) NOT NULL DEFAULT '', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"; + $conn->query($sql_users); + + // Create site_settings table + $sql_settings = "CREATE TABLE IF NOT EXISTS site_settings ( + setting_key VARCHAR(50) PRIMARY KEY, + setting_value TEXT + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"; + $conn->query($sql_settings); + + // Insert site_settings default values + $conn->query("INSERT IGNORE INTO site_settings (setting_key, setting_value) VALUES ('site_title', 'Blocknotes')"); + $conn->query("INSERT IGNORE INTO site_settings (setting_key, setting_value) VALUES ('site_favicon', 'assets/img/favicon.png')"); + + // Create Owner account + $hash_owner = password_hash($owner_pass, PASSWORD_DEFAULT); + $stmt = $conn->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, 'owner') ON DUPLICATE KEY UPDATE password = VALUES(password), role = 'owner'"); + $stmt->bind_param("ss", $owner_user, $hash_owner); + $stmt->execute(); + $stmt->close(); + + // Create Admin account + $hash_admin = password_hash($admin_pass, PASSWORD_DEFAULT); + $stmt = $conn->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, 'admin') ON DUPLICATE KEY UPDATE password = VALUES(password), role = 'admin'"); + $stmt->bind_param("ss", $admin_user, $hash_admin); + $stmt->execute(); + $stmt->close(); + + // Create blocknotes directory if missing + $blocknotes_dir = __DIR__ . "/blocknotes/"; + if (!is_dir($blocknotes_dir)) { + @mkdir($blocknotes_dir, 0777, true); + } + + // Update config/settings.php file + $settings_file_content = ''; + file_put_contents(__DIR__ . "/config/settings.php", $settings_file_content); + + // Rename first_start.php to first_start.php.completed to prevent installer re-entry + @rename(__FILE__, __DIR__ . "/first_start.php.completed"); + + // Redirect to login page + header("Location: index.php?page=login&installed=1"); + exit; + } + } + } +} +?> + + + + + + <?= __('setup_title') ?> - Blocknotes + + + + +
+ + +
+ +
+ +
+

+

+
+ + +
+ โš ๏ธ +
+ + +
+ + +
+ ๐Ÿ—„๏ธ +
+ +
+
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + +
+
+ + +
+ ๐Ÿ‘‘ +
+ +
+
+ + +
+ +
+ + +
+
+ + +
+ ๐Ÿ›ก๏ธ +
+ +
+
+ + +
+ +
+ + +
+
+ +
+ +
+ +
+
+ + diff --git a/functions/auth.php b/functions/auth.php new file mode 100644 index 0000000..04e74b1 --- /dev/null +++ b/functions/auth.php @@ -0,0 +1,314 @@ + false, 'message' => 'Inserisci sia username che password.']; + } + + $stmt = $conn->prepare("SELECT id, username, password, role, theme_pri, theme_sec, theme_thi, language, default_blocknote FROM users WHERE username = ? LIMIT 1"); + $stmt->bind_param("s", $username); + $stmt->execute(); + $result = $stmt->get_result(); + + if ($row = $result->fetch_assoc()) { + if (password_verify($password, $row['password'])) { + $_SESSION['user'] = [ + 'id' => $row['id'], + 'username' => $row['username'], + 'role' => $row['role'], + 'theme_pri' => $row['theme_pri'] ?? 98, + 'theme_sec' => $row['theme_sec'] ?? 207, + 'theme_thi' => $row['theme_thi'] ?? 280, + 'language' => $row['language'] ?? 'it', + 'default_blocknote' => $row['default_blocknote'] ?? '' + ]; + $stmt->close(); + return ['success' => true]; + } + } + $stmt->close(); + return ['success' => false, 'message' => 'Username o password errati.']; +} + +/** + * Logout current user. + */ +function logout_user() { + $_SESSION = array(); + if (ini_get("session.use_cookies")) { + $params = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, + $params["path"], $params["domain"], + $params["secure"], $params["httponly"] + ); + } + session_destroy(); +} + +/** + * Update user password. + */ +function update_user_password($user_id, $new_password) { + global $conn; + if (strlen($new_password) < 4) { + return ['success' => false, 'message' => 'La password deve contenere almeno 4 caratteri.']; + } + $hash = password_hash($new_password, PASSWORD_DEFAULT); + $stmt = $conn->prepare("UPDATE users SET password = ? WHERE id = ?"); + $stmt->bind_param("si", $hash, $user_id); + $res = $stmt->execute(); + $stmt->close(); + + return $res ? ['success' => true, 'message' => 'Password aggiornata con successo!'] : ['success' => false, 'message' => 'Errore durante il salvataggio della password.']; +} + +/** + * Update user language preference. + */ +function update_user_language($user_id, $lang) { + global $conn; + if (!in_array($lang, ['it', 'en', 'es', 'fr'])) { + $lang = 'it'; + } + + $stmt = $conn->prepare("UPDATE users SET language = ? WHERE id = ?"); + $stmt->bind_param("si", $lang, $user_id); + $res = $stmt->execute(); + $stmt->close(); + + if ($res) { + if (isset($_SESSION['user']) && $_SESSION['user']['id'] == $user_id) { + $_SESSION['user']['language'] = $lang; + } + return ['success' => true, 'message' => 'Lingua aggiornata con successo!']; + } + return ['success' => false, 'message' => 'Errore durante l\'aggiornamento della lingua.']; +} + +/** + * Update user default landing blocknote preference. + */ +function update_user_default_blocknote($user_id, $bn_id) { + global $conn; + $bn_id = trim($bn_id); + + $stmt = $conn->prepare("UPDATE users SET default_blocknote = ? WHERE id = ?"); + $stmt->bind_param("si", $bn_id, $user_id); + $res = $stmt->execute(); + $stmt->close(); + + if ($res) { + if (isset($_SESSION['user']) && $_SESSION['user']['id'] == $user_id) { + $_SESSION['user']['default_blocknote'] = $bn_id; + } + return ['success' => true, 'message' => 'Pagina di avvio aggiornata con successo!']; + } + return ['success' => false, 'message' => 'Errore durante l\'aggiornamento della pagina di avvio.']; +} + +/** + * Update user interface theme colors (HSL). + */ +function update_user_theme($user_id, $pri, $sec, $thi) { + global $conn; + $pri = max(0, min(360, (int)$pri)); + $sec = max(0, min(360, (int)$sec)); + $thi = max(0, min(360, (int)$thi)); + + $stmt = $conn->prepare("UPDATE users SET theme_pri = ?, theme_sec = ?, theme_thi = ? WHERE id = ?"); + $stmt->bind_param("iiii", $pri, $sec, $thi, $user_id); + $res = $stmt->execute(); + $stmt->close(); + + if ($res) { + if (isset($_SESSION['user']) && $_SESSION['user']['id'] == $user_id) { + $_SESSION['user']['theme_pri'] = $pri; + $_SESSION['user']['theme_sec'] = $sec; + $_SESSION['user']['theme_thi'] = $thi; + } + return ['success' => true, 'message' => 'Colori dell\'interfaccia aggiornati!']; + } + return ['success' => false, 'message' => 'Errore durante l\'aggiornamento dei colori.']; +} + +/** + * Admin: Get list of all registered users. + */ +function get_all_users() { + global $conn; + $result = $conn->query("SELECT id, username, role, created_at FROM users ORDER BY id ASC"); + $users = []; + while ($row = $result->fetch_assoc()) { + $users[] = $row; + } + return $users; +} + +/** + * Admin: Register a new user. + */ +function admin_create_user($username, $password, $role = 'user') { + global $conn; + $username = trim($username); + if (empty($username) || empty($password)) { + return ['success' => false, 'message' => 'Username e password sono obbligatori.']; + } + if (!in_array($role, ['user', 'admin', 'owner'])) { + $role = 'user'; + } + + if ($role === 'owner') { + $check_owner = $conn->query("SELECT id FROM users WHERE role = 'owner'"); + if ($check_owner->num_rows > 0) { + return ['success' => false, 'message' => 'Puรฒ esistere un solo account Owner nel sistema!']; + } + } + + // Check if exists + $stmt = $conn->prepare("SELECT id FROM users WHERE username = ? LIMIT 1"); + $stmt->bind_param("s", $username); + $stmt->execute(); + if ($stmt->get_result()->num_rows > 0) { + $stmt->close(); + return ['success' => false, 'message' => 'Un utente con questo username esiste giร .']; + } + $stmt->close(); + + $hash = password_hash($password, PASSWORD_DEFAULT); + $stmt = $conn->prepare("INSERT INTO users (username, password, role) VALUES (?, ?, ?)"); + $stmt->bind_param("sss", $username, $hash, $role); + $res = $stmt->execute(); + $stmt->close(); + + return $res ? ['success' => true, 'message' => 'Utente creato con successo!'] : ['success' => false, 'message' => 'Impossibile creare l\'utente.']; +} + +/** + * Admin: Delete a user by ID. + */ +function admin_delete_user($user_id) { + global $conn; + $user_id = (int)$user_id; + + // Prevent self-deletion + if (current_user() && current_user()['id'] == $user_id) { + return ['success' => false, 'message' => 'Non puoi eliminare il tuo stesso account!']; + } + + // Check target user role - Owner can NEVER be deleted + $stmt = $conn->prepare("SELECT role FROM users WHERE id = ? LIMIT 1"); + $stmt->bind_param("i", $user_id); + $stmt->execute(); + $target_user = $stmt->get_result()->fetch_assoc(); + $stmt->close(); + + if ($target_user && $target_user['role'] === 'owner') { + return ['success' => false, 'message' => 'L\'account Owner non puรฒ essere eliminato da nessuno!']; + } + + $stmt = $conn->prepare("DELETE FROM users WHERE id = ?"); + $stmt->bind_param("i", $user_id); + $res = $stmt->execute(); + $stmt->close(); + + return $res ? ['success' => true, 'message' => 'Utente eliminato.'] : ['success' => false, 'message' => 'Errore nell\'eliminazione dell\'utente.']; +} + +/** + * Admin: Update user role. + */ +function admin_update_user_role($user_id, $role) { + global $conn; + $user_id = (int)$user_id; + if (!in_array($role, ['admin', 'user', 'owner'])) return ['success' => false, 'message' => 'Ruolo non valido.']; + + // Check target user role - Owner role cannot be changed + $stmt = $conn->prepare("SELECT role FROM users WHERE id = ? LIMIT 1"); + $stmt->bind_param("i", $user_id); + $stmt->execute(); + $target_user = $stmt->get_result()->fetch_assoc(); + $stmt->close(); + + if ($target_user && $target_user['role'] === 'owner') { + return ['success' => false, 'message' => 'Il ruolo dell\'account Owner non puรฒ essere modificato!']; + } + + if ($role === 'owner') { + $check_owner = $conn->query("SELECT id FROM users WHERE role = 'owner'"); + if ($check_owner->num_rows > 0) { + return ['success' => false, 'message' => 'Puรฒ esistere un solo account Owner nel sistema!']; + } + } + + $stmt = $conn->prepare("UPDATE users SET role = ? WHERE id = ?"); + $stmt->bind_param("si", $role, $user_id); + $res = $stmt->execute(); + $stmt->close(); + + return $res ? ['success' => true, 'message' => 'Ruolo aggiornato.'] : ['success' => false, 'message' => 'Impossibile aggiornare il ruolo.']; +} + diff --git a/functions/blocknotes.php b/functions/blocknotes.php new file mode 100644 index 0000000..8a17446 --- /dev/null +++ b/functions/blocknotes.php @@ -0,0 +1,421 @@ + false, 'message' => 'Il titolo del blocknote รจ obbligatorio.']; + } + + $id = 'bn_' . uniqid(); + $data = [ + 'id' => $id, + 'title' => $title, + 'username' => $username, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + 'notes' => [] + ]; + + if (save_blocknote($data)) { + return ['success' => true, 'id' => $id, 'message' => 'Blocknote creato con successo!']; + } + return ['success' => false, 'message' => 'Errore nel salvataggio del file JSON.']; +} + +/** + * Import a JSON string (supports standard format and legacy codes format). + */ +function import_blocknote_json($json_string, $username, $custom_title = '') { + $data = json_decode($json_string, true); + if (!is_array($data)) { + return ['success' => false, 'message' => 'File JSON non valido o malformato.']; + } + + $id = 'bn_' . uniqid(); + $title = !empty($custom_title) ? trim($custom_title) : 'Blocknote Importato'; + + // Case 1: Standard format with 'notes' array + if (isset($data['notes']) && is_array($data['notes'])) { + $bn = [ + 'id' => $id, + 'title' => !empty($data['title']) ? $data['title'] : $title, + 'username' => $username, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + 'notes' => $data['notes'] + ]; + if (save_blocknote($bn)) { + return ['success' => true, 'id' => $id, 'message' => 'Blocknote importato con successo!']; + } + } + + // Case 2: Legacy format (array of notes with 'title', 'description', 'codes') + if (isset($data[0]) && is_array($data[0])) { + $notes = []; + foreach ($data as $n_idx => $legacy_note) { + $note_id = 'note_' . ($n_idx + 1) . '_' . uniqid(); + $subnotes = []; + + if (isset($legacy_note['codes']) && is_array($legacy_note['codes'])) { + foreach ($legacy_note['codes'] as $s_idx => $legacy_code) { + $subnotes[] = [ + 'id' => 'sub_' . ($s_idx + 1) . '_' . uniqid(), + 'title' => $legacy_code['sub_title'] ?? 'Comando', + 'type' => 'command', + 'content' => $legacy_code['code'] ?? '' + ]; + } + } + + $notes[] = [ + 'id' => $note_id, + 'title' => $legacy_note['title'] ?? 'Nota ' . ($n_idx + 1), + 'description' => $legacy_note['description'] ?? '', + 'subnotes' => $subnotes + ]; + } + + $bn = [ + 'id' => $id, + 'title' => $title, + 'username' => $username, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + 'notes' => $notes + ]; + + if (save_blocknote($bn)) { + return ['success' => true, 'id' => $id, 'message' => 'Blocknote in formato legacy importato e convertito con successo!']; + } + } + + return ['success' => false, 'message' => 'Formato JSON non riconosciuto.']; +} + +/** + * Check permission on blocknote. + */ +function can_access_blocknote($bn, $username, $is_admin) { + if (!$bn) return false; + if ($is_admin) return true; + return strtolower($bn['username'] ?? '') === strtolower($username); +} + +/** + * Rename a blocknote. + */ +function rename_blocknote($id, $new_title, $username, $is_admin) { + $bn = get_blocknote_by_id($id); + if (!$bn) return ['success' => false, 'message' => 'Blocknote non trovato.']; + if (!can_access_blocknote($bn, $username, $is_admin)) { + return ['success' => false, 'message' => 'Non hai i permessi per modificare questo blocknote.']; + } + + $new_title = trim($new_title); + if (empty($new_title)) return ['success' => false, 'message' => 'Il titolo non puรฒ essere vuoto.']; + + $bn['title'] = $new_title; + if (save_blocknote($bn)) { + return ['success' => true, 'message' => 'Blocknote rinominato con successo.']; + } + return ['success' => false, 'message' => 'Errore nel salvataggio del file JSON.']; +} + +/** + * Delete a blocknote. + */ +function delete_blocknote($id, $username, $is_admin) { + $bn = get_blocknote_by_id($id); + if (!$bn) return ['success' => false, 'message' => 'Blocknote non trovato.']; + if (!can_access_blocknote($bn, $username, $is_admin)) { + return ['success' => false, 'message' => 'Non hai i permessi per eliminare questo blocknote.']; + } + + $filepath = BLOCKNOTES_FOLD . preg_replace('/[^a-zA-Z0-9_\-]/', '', $bn['id']) . ".json"; + if (file_exists($filepath)) { + @unlink($filepath); + } + return ['success' => true, 'message' => 'Blocknote eliminato con successo.']; +} + +/** + * Add a new Note block to a Blocknote. + */ +function add_note($blocknote_id, $title, $description, $username, $is_admin) { + $bn = get_blocknote_by_id($blocknote_id); + if (!$bn) return ['success' => false, 'message' => 'Blocknote non trovato.']; + if (!can_access_blocknote($bn, $username, $is_admin)) return ['success' => false, 'message' => 'Permesso negato.']; + + $title = trim($title); + if (empty($title)) return ['success' => false, 'message' => 'Il titolo della nota รจ obbligatorio.']; + + $note_id = 'note_' . uniqid(); + $new_note = [ + 'id' => $note_id, + 'title' => $title, + 'description' => trim($description), + 'subnotes' => [] + ]; + + $bn['notes'][] = $new_note; + if (save_blocknote($bn)) { + return ['success' => true, 'note_id' => $note_id, 'message' => 'Nota aggiunta con successo!']; + } + return ['success' => false, 'message' => 'Errore durante il salvataggio.']; +} + +/** + * Edit an existing Note block. + */ +function edit_note($blocknote_id, $note_id, $title, $description, $username, $is_admin) { + $bn = get_blocknote_by_id($blocknote_id); + if (!$bn) return ['success' => false, 'message' => 'Blocknote non trovato.']; + if (!can_access_blocknote($bn, $username, $is_admin)) return ['success' => false, 'message' => 'Permesso negato.']; + + $title = trim($title); + if (empty($title)) return ['success' => false, 'message' => 'Il titolo della nota รจ obbligatorio.']; + + $found = false; + foreach ($bn['notes'] as &$note) { + if ($note['id'] === $note_id) { + $note['title'] = $title; + $note['description'] = trim($description); + $found = true; + break; + } + } + + if (!$found) return ['success' => false, 'message' => 'Nota non trovata.']; + + if (save_blocknote($bn)) { + return ['success' => true, 'message' => 'Nota modificata con successo!']; + } + return ['success' => false, 'message' => 'Errore nel salvataggio.']; +} + +/** + * Delete a Note block. + */ +function delete_note($blocknote_id, $note_id, $username, $is_admin) { + $bn = get_blocknote_by_id($blocknote_id); + if (!$bn) return ['success' => false, 'message' => 'Blocknote non trovato.']; + if (!can_access_blocknote($bn, $username, $is_admin)) return ['success' => false, 'message' => 'Permesso negato.']; + + $initial_count = count($bn['notes']); + $bn['notes'] = array_values(array_filter($bn['notes'], function($n) use ($note_id) { + return $n['id'] !== $note_id; + })); + + if (count($bn['notes']) === $initial_count) { + return ['success' => false, 'message' => 'Nota non trovata.']; + } + + if (save_blocknote($bn)) { + return ['success' => true, 'message' => 'Nota eliminata con successo.']; + } + return ['success' => false, 'message' => 'Errore nel salvataggio.']; +} + +/** + * Add a Subnote to a Note block. + */ +function add_subnote($blocknote_id, $note_id, $sub_title, $type, $content, $username, $is_admin) { + $bn = get_blocknote_by_id($blocknote_id); + if (!$bn) return ['success' => false, 'message' => 'Blocknote non trovato.']; + if (!can_access_blocknote($bn, $username, $is_admin)) return ['success' => false, 'message' => 'Permesso negato.']; + + $sub_title = trim($sub_title); + if (empty($sub_title)) return ['success' => false, 'message' => 'Il titolo della sottonota รจ obbligatorio.']; + + $type = ($type === 'command') ? 'command' : 'text'; + $sub_id = 'sub_' . uniqid(); + + $found = false; + foreach ($bn['notes'] as &$note) { + if ($note['id'] === $note_id) { + if (!isset($note['subnotes'])) $note['subnotes'] = []; + $note['subnotes'][] = [ + 'id' => $sub_id, + 'title' => $sub_title, + 'type' => $type, + 'content' => $content + ]; + $found = true; + break; + } + } + + if (!$found) return ['success' => false, 'message' => 'Nota genitore non trovata.']; + + if (save_blocknote($bn)) { + return ['success' => true, 'subnote_id' => $sub_id, 'message' => 'Sottonota aggiunta con successo!']; + } + return ['success' => false, 'message' => 'Errore nel salvataggio.']; +} + +/** + * Edit a Subnote. + */ +function edit_subnote($blocknote_id, $note_id, $sub_id, $sub_title, $type, $content, $username, $is_admin) { + $bn = get_blocknote_by_id($blocknote_id); + if (!$bn) return ['success' => false, 'message' => 'Blocknote non trovato.']; + if (!can_access_blocknote($bn, $username, $is_admin)) return ['success' => false, 'message' => 'Permesso negato.']; + + $sub_title = trim($sub_title); + if (empty($sub_title)) return ['success' => false, 'message' => 'Il titolo della sottonota รจ obbligatorio.']; + $type = ($type === 'command') ? 'command' : 'text'; + + $found = false; + foreach ($bn['notes'] as &$note) { + if ($note['id'] === $note_id) { + if (isset($note['subnotes'])) { + foreach ($note['subnotes'] as &$sub) { + if ($sub['id'] === $sub_id) { + $sub['title'] = $sub_title; + $sub['type'] = $type; + $sub['content'] = $content; + $found = true; + break 2; + } + } + } + } + } + + if (!$found) return ['success' => false, 'message' => 'Sottonota non trovata.']; + + if (save_blocknote($bn)) { + return ['success' => true, 'message' => 'Sottonota modificata con successo!']; + } + return ['success' => false, 'message' => 'Errore nel salvataggio.']; +} + +/** + * Delete a Subnote. + */ +function delete_subnote($blocknote_id, $note_id, $sub_id, $username, $is_admin) { + $bn = get_blocknote_by_id($blocknote_id); + if (!$bn) return ['success' => false, 'message' => 'Blocknote non trovato.']; + if (!can_access_blocknote($bn, $username, $is_admin)) return ['success' => false, 'message' => 'Permesso negato.']; + + $found = false; + foreach ($bn['notes'] as &$note) { + if ($note['id'] === $note_id && isset($note['subnotes'])) { + $initial = count($note['subnotes']); + $note['subnotes'] = array_values(array_filter($note['subnotes'], function($s) use ($sub_id) { + return $s['id'] !== $sub_id; + })); + if (count($note['subnotes']) < $initial) { + $found = true; + break; + } + } + } + + if (!$found) return ['success' => false, 'message' => 'Sottonota non trovata.']; + + if (save_blocknote($bn)) { + return ['success' => true, 'message' => 'Sottonota eliminata con successo.']; + } + return ['success' => false, 'message' => 'Errore nel salvataggio.']; +} diff --git a/functions/functions.php b/functions/functions.php index d36fbca..b4dee53 100644 --- a/functions/functions.php +++ b/functions/functions.php @@ -1,13 +1,15 @@ prepare("UPDATE users SET language = ? WHERE id = ?"); + $stmt->bind_param("si", $lang, $_SESSION['user']['id']); + $stmt->execute(); + $stmt->close(); + } + } else { + $_SESSION['guest_language'] = $lang; + } + } +} + +/** + * Translations Dictionary + */ +function get_translations() { + return [ + 'it' => [ + // Login Page + 'login_title' => 'Accedi a Blocknotes', + 'login_subtitle' => 'Gestisci le tue note e i tuoi comandi', + 'label_username' => 'Nome Utente', + 'label_password' => 'Password', + 'placeholder_username' => 'Inserisci username', + 'placeholder_password' => 'Inserisci password', + 'btn_submit_login' => '๐Ÿ”‘ Accedi', + 'login_hint_footer' => 'Accredito predefinito per il primo accesso', + 'login_installed_success' => '๐ŸŽ‰ Setup completato! Accedi con le credenziali create.', + + // Navbar + 'nav_blocknotes' => '๐Ÿ““ Blocknotes', + 'nav_colors_profile' => '๐ŸŽจ Colori & Profilo', + 'nav_admin' => '๐Ÿ‘‘ Gestione Utenti', + 'nav_logout' => '๐Ÿšช Esci', + 'nav_login' => '๐Ÿ”‘ Accedi', + 'nav_language' => '๐ŸŒ Lingua', + + // Dashboard (home) + 'dashboard_title_user' => 'I Miei Blocknotes', + 'dashboard_title_admin' => 'Tutti i Blocknotes (Vista Admin)', + 'dashboard_desc_user' => 'Crea, organizza e consulta i tuoi blocknotes.', + 'dashboard_desc_admin' => 'In qualitร  di Admin puoi visualizzare, gestire ed eliminare i blocknotes di tutti gli utenti.', + 'btn_new_blocknote' => 'โž• Nuovo Blocknote', + 'btn_import_json' => '๐Ÿ“ฅ Importa JSON', + 'btn_export_json' => '๐Ÿ’พ Esporta JSON', + 'btn_open_edit' => '๐Ÿ‘๏ธ Apri & Modifica', + 'btn_rename' => 'โœ๏ธ Rinomina', + 'btn_delete' => '๐Ÿ—‘๏ธ Elimina', + 'empty_blocknotes_title' => 'Nessun Blocknote trovato', + 'empty_blocknotes_desc_user' => 'Non hai ancora creato nessun blocknote. Inizia subito!', + 'empty_blocknotes_desc_admin' => 'Non ci sono ancora blocknotes salvati nel sistema.', + 'modal_create_title' => 'โž• Crea Nuovo Blocknote', + 'modal_rename_title' => 'โœ๏ธ Rinomina Blocknote', + 'modal_import_title' => '๐Ÿ“ฅ Importa Blocknote (JSON)', + 'label_title' => 'Titolo Blocknote', + 'label_file_json' => 'Carica File .json', + 'label_paste_json' => 'Oppure incolla qui il codice JSON', + 'btn_cancel' => 'Annulla', + 'btn_save' => 'Salva', + 'btn_create' => 'Crea Blocknote', + 'btn_import' => 'Importa Blocknote', + 'note_single' => 'Nota', + 'note_plural' => 'Note', + + // View Blocknote + 'btn_back_list' => 'โฌ…๏ธ Torna ai Blocknotes', + 'btn_expand_all' => '๐Ÿ“‚ Espandi Tutte', + 'btn_collapse_all' => '๐Ÿ“ Riduci Tutte', + 'btn_new_note' => 'โž• Nuova Nota', + 'btn_add_subnote' => 'โž• Sottonota', + 'subnote_single' => 'sottonota', + 'subnote_plural' => 'sottonote', + 'badge_text' => '๐Ÿ“„ Testo', + 'badge_command' => 'โšก Comando', + 'btn_copy' => '๐Ÿ“‹ Copia', + 'btn_copied' => 'โœ“ Copiato!', + 'search_placeholder' => '๐Ÿ” Cerca note, sottonote o comandi in questo blocknote...', + 'btn_clear_search' => 'โŒ Annulla Cerca', + 'search_no_results' => 'Nessun risultato trovato', + 'search_no_results_desc' => 'Nessuna nota o comando corrisponde al termine cercato in questo blocknote.', + 'modal_add_note' => 'โž• Nuova Nota a Blocchi', + 'modal_edit_note' => 'โœ๏ธ Modifica Nota', + 'modal_add_subnote' => 'โž• Aggiungi Sottonota', + 'modal_edit_subnote' => 'โœ๏ธ Modifica Sottonota', + 'label_note_description' => 'Descrizione (opzionale)', + 'label_subnote_title' => 'Titolo Sottonota', + 'label_subnote_type' => 'Tipo di Sottonota', + 'label_subnote_content' => 'Contenuto', + 'type_option_text' => '๐Ÿ“„ Testo generico', + 'type_option_command' => 'โšก Comando (con pulsante Copia)', + + // Profile & Settings + 'profile_title' => '๐ŸŽจ Impostazioni Profilo & Tema', + 'profile_desc' => 'Personalizza password, colori dell\'interfaccia, lingua e pagina di avvio.', + 'card_password_title' => '๐Ÿ”’ Cambio Password', + 'card_colors_title' => '๐ŸŽจ Personalizza Colori Interfaccia (HSL)', + 'card_lang_title' => '๐ŸŒ Lingua dell\'Interfaccia', + 'card_landing_title' => '๐Ÿ  Pagina di Primo Accesso (Landing Page)', + 'label_new_pass' => 'Nuova Password', + 'label_confirm_pass' => 'Conferma Nuova Password', + 'label_select_lang' => 'Seleziona Lingua', + 'label_select_landing' => 'Seleziona Pagina di Avvio', + 'landing_option_dashboard' => '๐Ÿ“‹ Pagina Principale (Tutti i Blocknotes)', + 'landing_option_bn_prefix' => '๐Ÿ“Œ Apri direttamente: ', + 'btn_update_pass' => '๐Ÿ”‘ Aggiorna Password', + 'btn_save_colors' => '๐Ÿ’พ Salva Preferenze Colori', + 'btn_save_lang' => '๐ŸŒ Salva Lingua', + 'btn_save_landing' => '๐Ÿ  Salva Pagina di Avvio', + + // Admin Users + 'admin_title' => '๐Ÿ‘‘ Pannello Amministrazione Utenti', + 'admin_desc' => 'Crea, gestisci e modifica i ruoli e gli accessi degli utenti.', + 'btn_add_user' => 'โž• Aggiungi Nuovo Utente', + 'col_username' => 'Nome Utente', + 'col_role' => 'Ruolo', + 'col_bns' => 'Blocknotes Creati', + 'col_created' => 'Data Registrazione', + 'col_actions' => 'Azioni Admin', + 'role_standard' => 'Standard User', + 'role_admin' => 'Administrator', + 'role_owner' => '๐Ÿ‘‘ Owner (Proprietario)', + 'badge_owner' => '๐Ÿ‘‘ Owner', + 'btn_reset_pass' => '๐Ÿ”‘ Reset Pass', + 'modal_create_user' => 'โž• Registra Nuovo Utente', + 'modal_reset_pass' => '๐Ÿ”‘ Reset Password Utente', + // Confirm Dialogs + 'confirm_delete_blocknote' => 'Sei sicuro di voler eliminare questo blocknote e tutte le sue note?', + 'confirm_delete_note' => 'Sei sicuro di voler eliminare questa nota e tutte le sue sottonote?', + 'confirm_delete_subnote' => 'Sei sicuro di voler eliminare questa sottonota?', + 'confirm_delete_user' => 'Sei sicuro di voler eliminare questo utente?', + + // Placeholders + 'placeholder_bn_title' => 'Es. Comandi Server Linux, Note Progetto PHP...', + 'placeholder_rename_title' => 'Nuovo titolo...', + 'placeholder_import_title' => 'Es. Positive Internet', + 'placeholder_paste_json' => 'Incolla qui il JSON nel vecchio o nuovo formato...', + 'placeholder_note_title' => 'Es. Installazione MySQL, Server Setup...', + 'placeholder_note_desc' => 'Breve descrizione di questo blocco di note...', + 'placeholder_subnote_title' => 'Es. Comando creazione database, Note configurazione...', + 'placeholder_subnote_content' => 'Inserisci il testo o le istruzioni del comando...', + 'placeholder_site_title' => 'Es. Blocknotes, Enterprise Notes...', + 'placeholder_username_new' => 'es. mario, giuseppe...', + 'placeholder_password_new' => 'Inserisci nuova password', + 'placeholder_password_confirm' => 'Ripeti nuova password', + + // Extra Labels + 'label_owner_prefix' => 'Proprietario: ', + 'label_updated_prefix' => 'Ultimo aggiornamento: ', + 'label_you' => 'Tu', + 'label_unknown' => 'Sconosciuto', + 'label_current_favicon' => 'Favicon Attuale: ', + + // First Start Installer Setup + 'setup_title' => '๐Ÿš€ Setup Iniziale Blocknotes', + 'setup_subtitle' => 'Configura il database MySQL e crea i primi utenti privilegiati (Owner & Admin).', + 'setup_sec1_title' => '1. Configurazione Database MySQL', + 'setup_db_host' => 'Host Database', + 'setup_db_name' => 'Nome Database', + 'setup_db_user' => 'Utente MySQL', + 'setup_db_pass' => 'Password MySQL', + 'setup_db_pass_placeholder' => 'Password (lascia vuoto se nessuna)', + 'setup_sec2_title' => '2. Account Owner (Proprietario Unico)', + 'setup_owner_user' => 'Username Owner', + 'setup_owner_pass' => 'Password Owner', + 'setup_owner_pass_placeholder' => 'Inserisci password per Owner', + 'setup_sec3_title' => '3. Primo Account Amministratore (Admin)', + 'setup_admin_user' => 'Username Admin', + 'setup_admin_pass' => 'Password Admin', + 'setup_admin_pass_placeholder' => 'Inserisci password per Admin', + 'setup_btn_submit' => 'โšก Completa Installazione & Configura Sistema', + 'err_setup_db_fields' => 'Compila tutti i dati di connessione al Database MySQL.', + 'err_setup_owner_fields' => 'Inserisci username e password per l\'utente Owner (Proprietario).', + 'err_setup_admin_fields' => 'Inserisci username e password per il primo utente Admin.', + 'err_setup_same_user' => 'L\'utente Owner e l\'utente Admin devono avere nomi utente differenti.' + ], + + 'en' => [ + // Login Page + 'login_title' => 'Login to Blocknotes', + 'login_subtitle' => 'Manage your notes and commands', + 'label_username' => 'Username', + 'label_password' => 'Password', + 'placeholder_username' => 'Enter username', + 'placeholder_password' => 'Enter password', + 'btn_submit_login' => '๐Ÿ”‘ Login', + 'login_hint_footer' => 'Default first login credential', + 'login_installed_success' => '๐ŸŽ‰ Setup completed! Login with your created credentials.', + + // Navbar + 'nav_blocknotes' => '๐Ÿ““ Blocknotes', + 'nav_colors_profile' => '๐ŸŽจ Colors & Profile', + 'nav_admin' => '๐Ÿ‘‘ User Management', + 'nav_logout' => '๐Ÿšช Logout', + 'nav_login' => '๐Ÿ”‘ Login', + 'nav_language' => '๐ŸŒ Language', + + // Dashboard (home) + 'dashboard_title_user' => 'My Blocknotes', + 'dashboard_title_admin' => 'All Blocknotes (Admin View)', + 'dashboard_desc_user' => 'Create, organize, and view your blocknotes.', + 'dashboard_desc_admin' => 'As an Admin you can view, manage, and delete blocknotes of all users.', + 'btn_new_blocknote' => 'โž• New Blocknote', + 'btn_import_json' => '๐Ÿ“ฅ Import JSON', + 'btn_export_json' => '๐Ÿ’พ Export JSON', + 'btn_open_edit' => '๐Ÿ‘๏ธ Open & Edit', + 'btn_rename' => 'โœ๏ธ Rename', + 'btn_delete' => '๐Ÿ—‘๏ธ Delete', + 'empty_blocknotes_title' => 'No Blocknotes found', + 'empty_blocknotes_desc_user' => 'You haven\'t created any blocknote yet. Start now!', + 'empty_blocknotes_desc_admin' => 'There are no blocknotes saved in the system yet.', + 'modal_create_title' => 'โž• Create New Blocknote', + 'modal_rename_title' => 'โœ๏ธ Rename Blocknote', + 'modal_import_title' => '๐Ÿ“ฅ Import Blocknote (JSON)', + 'label_title' => 'Blocknote Title', + 'label_file_json' => 'Upload .json File', + 'label_paste_json' => 'Or paste JSON code here', + 'btn_cancel' => 'Cancel', + 'btn_save' => 'Save', + 'btn_create' => 'Create Blocknote', + 'btn_import' => 'Import Blocknote', + 'note_single' => 'Note', + 'note_plural' => 'Notes', + + // View Blocknote + 'btn_back_list' => 'โฌ…๏ธ Back to Blocknotes', + 'btn_expand_all' => '๐Ÿ“‚ Expand All', + 'btn_collapse_all' => '๐Ÿ“ Collapse All', + 'btn_new_note' => 'โž• New Note', + 'btn_add_subnote' => 'โž• Subnote', + 'subnote_single' => 'subnote', + 'subnote_plural' => 'subnotes', + 'badge_text' => '๐Ÿ“„ Text', + 'badge_command' => 'โšก Command', + 'btn_copy' => '๐Ÿ“‹ Copy', + 'btn_copied' => 'โœ“ Copied!', + 'search_placeholder' => '๐Ÿ” Search notes, subnotes, or commands in this blocknote...', + 'btn_clear_search' => 'โŒ Clear Search', + 'search_no_results' => 'No results found', + 'search_no_results_desc' => 'No notes or commands match your search term in this blocknote.', + 'modal_add_note' => 'โž• New Block Note', + 'modal_edit_note' => 'โœ๏ธ Edit Note', + 'modal_add_subnote' => 'โž• Add Subnote', + 'modal_edit_subnote' => 'โœ๏ธ Edit Subnote', + 'label_note_description' => 'Description (optional)', + 'label_subnote_title' => 'Subnote Title', + 'label_subnote_type' => 'Subnote Type', + 'label_subnote_content' => 'Content', + 'type_option_text' => '๐Ÿ“„ Generic Text', + 'type_option_command' => 'โšก Command (with Copy button)', + + // Profile & Settings + 'profile_title' => '๐ŸŽจ Profile & Theme Settings', + 'profile_desc' => 'Customize password, UI colors, language, and default landing page.', + 'card_password_title' => '๐Ÿ”’ Change Password', + 'card_colors_title' => '๐ŸŽจ Interface Colors (HSL)', + 'card_lang_title' => '๐ŸŒ Interface Language', + 'card_landing_title' => '๐Ÿ  Default Landing Page', + 'label_new_pass' => 'New Password', + 'label_confirm_pass' => 'Confirm New Password', + 'label_select_lang' => 'Select Language', + 'label_select_landing' => 'Select Landing Page', + 'landing_option_dashboard' => '๐Ÿ“‹ Main Selection Page (All Blocknotes)', + 'landing_option_bn_prefix' => '๐Ÿ“Œ Open directly: ', + 'btn_update_pass' => '๐Ÿ”‘ Update Password', + 'btn_save_colors' => '๐Ÿ’พ Save Color Preferences', + 'btn_save_lang' => '๐ŸŒ Save Language', + 'btn_save_landing' => '๐Ÿ  Save Landing Page', + + // Admin Users + 'admin_title' => '๐Ÿ‘‘ User Administration Panel', + 'admin_desc' => 'Create, manage, and edit user roles and access.', + 'btn_add_user' => 'โž• Add New User', + 'col_username' => 'Username', + 'col_role' => 'Role', + 'col_bns' => 'Blocknotes Created', + 'col_created' => 'Created Date', + 'col_actions' => 'Admin Actions', + 'role_standard' => 'Standard User', + 'role_admin' => 'Administrator', + 'role_owner' => '๐Ÿ‘‘ Owner', + 'badge_owner' => '๐Ÿ‘‘ Owner', + 'btn_reset_pass' => '๐Ÿ”‘ Reset Pass', + 'modal_create_user' => 'โž• Register New User', + 'modal_reset_pass' => '๐Ÿ”‘ Reset User Password', + 'card_site_settings_title' => 'โš™๏ธ Site Settings (Owner Only)', + 'label_site_title' => 'Global Application / Site Title', + 'label_favicon' => 'Site Favicon (High Resolution, min 64x64px)', + 'btn_save_site_title' => '๐Ÿ’พ Save Site Title', + 'btn_upload_favicon' => '๐Ÿ“ค Upload New Favicon', + + // Confirm Dialogs + 'confirm_delete_blocknote' => 'Are you sure you want to delete this blocknote and all its notes?', + 'confirm_delete_note' => 'Are you sure you want to delete this note and all its subnotes?', + 'confirm_delete_subnote' => 'Are you sure you want to delete this subnote?', + 'confirm_delete_user' => 'Are you sure you want to delete this user?', + + // Placeholders + 'placeholder_bn_title' => 'E.g. Linux Server Commands, PHP Notes...', + 'placeholder_rename_title' => 'New title...', + 'placeholder_import_title' => 'E.g. Positive Internet', + 'placeholder_paste_json' => 'Paste JSON here in old or new format...', + 'placeholder_note_title' => 'E.g. MySQL Installation, Server Setup...', + 'placeholder_note_desc' => 'Short description of this note block...', + 'placeholder_subnote_title' => 'E.g. Database creation command...', + 'placeholder_subnote_content' => 'Enter text or command instructions...', + 'placeholder_site_title' => 'E.g. Blocknotes, Enterprise Notes...', + 'placeholder_username_new' => 'e.g. john, mary...', + 'placeholder_password_new' => 'Enter new password', + 'placeholder_password_confirm' => 'Repeat new password', + + // Extra Labels + 'label_owner_prefix' => 'Owner: ', + 'label_updated_prefix' => 'Last updated: ', + 'label_you' => 'You', + 'label_unknown' => 'Unknown', + 'label_current_favicon' => 'Current Favicon: ', + + // First Start Installer Setup + 'setup_title' => '๐Ÿš€ Blocknotes Initial Setup', + 'setup_subtitle' => 'Configure MySQL database and create the initial privileged users (Owner & Admin).', + 'setup_sec1_title' => '1. MySQL Database Configuration', + 'setup_db_host' => 'Database Host', + 'setup_db_name' => 'Database Name', + 'setup_db_user' => 'MySQL User', + 'setup_db_pass' => 'MySQL Password', + 'setup_db_pass_placeholder' => 'Password (leave empty if none)', + 'setup_sec2_title' => '2. Owner Account (Single Owner)', + 'setup_owner_user' => 'Owner Username', + 'setup_owner_pass' => 'Owner Password', + 'setup_owner_pass_placeholder' => 'Enter password for Owner', + 'setup_sec3_title' => '3. First Admin Account', + 'setup_admin_user' => 'Admin Username', + 'setup_admin_pass' => 'Admin Password', + 'setup_admin_pass_placeholder' => 'Enter password for Admin', + 'setup_btn_submit' => 'โšก Complete Installation & Configure System', + 'err_setup_db_fields' => 'Please fill in all MySQL database connection fields.', + 'err_setup_owner_fields' => 'Please enter username and password for the Owner user.', + 'err_setup_admin_fields' => 'Please enter username and password for the first Admin user.', + 'err_setup_same_user' => 'Owner user and Admin user must have different usernames.' + ], + + 'es' => [ + // Login Page + 'login_title' => 'Iniciar sesiรณn en Blocknotes', + 'login_subtitle' => 'Gestiona tus notas y comandos', + 'label_username' => 'Nombre de Usuario', + 'label_password' => 'Contraseรฑa', + 'placeholder_username' => 'Introduce nombre de usuario', + 'placeholder_password' => 'Introduce contraseรฑa', + 'btn_submit_login' => '๐Ÿ”‘ Iniciar Sesiรณn', + 'login_hint_footer' => 'Credencial predeterminada para el primer acceso', + 'login_installed_success' => '๐ŸŽ‰ ยกConfiguraciรณn completada! Inicia sesiรณn con tus credenciales.', + + // Navbar + 'nav_blocknotes' => '๐Ÿ““ Blocknotes', + 'nav_colors_profile' => '๐ŸŽจ Colores y Perfil', + 'nav_admin' => '๐Ÿ‘‘ Gestiรณn de Usuarios', + 'nav_logout' => '๐Ÿšช Salir', + 'nav_login' => '๐Ÿ”‘ Iniciar Sesiรณn', + 'nav_language' => '๐ŸŒ Idioma', + + // Dashboard (home) + 'dashboard_title_user' => 'Mis Blocknotes', + 'dashboard_title_admin' => 'Todos los Blocknotes (Vista Admin)', + 'dashboard_desc_user' => 'Crea, organiza y consulta tus blocknotes.', + 'dashboard_desc_admin' => 'Como Admin puedes ver, gestionar y eliminar los blocknotes de todos los usuarios.', + 'btn_new_blocknote' => 'โž• Nuevo Blocknote', + 'btn_import_json' => '๐Ÿ“ฅ Importar JSON', + 'btn_export_json' => '๐Ÿ’พ Exportar JSON', + 'btn_open_edit' => '๐Ÿ‘๏ธ Abrir y Editar', + 'btn_rename' => 'โœ๏ธ Renombrar', + 'btn_delete' => '๐Ÿ—‘๏ธ Eliminar', + 'empty_blocknotes_title' => 'No se encontraron blocknotes', + 'empty_blocknotes_desc_user' => 'Aรบn no has creado ningรบn blocknote. ยกEmpieza ahora!', + 'empty_blocknotes_desc_admin' => 'Aรบn no hay blocknotes guardados en el sistema.', + 'modal_create_title' => 'โž• Crear Nuevo Blocknote', + 'modal_rename_title' => 'โœ๏ธ Renombrar Blocknote', + 'modal_import_title' => '๐Ÿ“ฅ Importar Blocknote (JSON)', + 'label_title' => 'Tรญtulo del Blocknote', + 'label_file_json' => 'Subir archivo .json', + 'label_paste_json' => 'O pega el cรณdigo JSON aquรญ', + 'btn_cancel' => 'Cancelar', + 'btn_save' => 'Guardar', + 'btn_create' => 'Crear Blocknote', + 'btn_import' => 'Importar Blocknote', + 'note_single' => 'Nota', + 'note_plural' => 'Notas', + + // View Blocknote + 'btn_back_list' => 'โฌ…๏ธ Volver a Blocknotes', + 'btn_expand_all' => '๐Ÿ“‚ Expandir Todas', + 'btn_collapse_all' => '๐Ÿ“ Contraer Todas', + 'btn_new_note' => 'โž• Nueva Nota', + 'btn_add_subnote' => 'โž• Subnota', + 'subnote_single' => 'subnota', + 'subnote_plural' => 'subnotas', + 'badge_text' => '๐Ÿ“„ Texto', + 'badge_command' => 'โšก Comando', + 'btn_copy' => '๐Ÿ“‹ Copiar', + 'btn_copied' => 'โœ“ ยกCopiado!', + 'search_placeholder' => '๐Ÿ” Buscar notas, subnotas o comandos...', + 'btn_clear_search' => 'โŒ Cancelar Bรบsqueda', + 'search_no_results' => 'No se encontraron resultados', + 'search_no_results_desc' => 'Ninguna nota o comando coincide con tu bรบsqueda.', + 'modal_add_note' => 'โž• Nueva Nota en Bloque', + 'modal_edit_note' => 'โœ๏ธ Editar Nota', + 'modal_add_subnote' => 'โž• Aรฑadir Subnota', + 'modal_edit_subnote' => 'โœ๏ธ Editar Subnota', + 'label_note_description' => 'Descripciรณn (opcional)', + 'label_subnote_title' => 'Tรญtulo de Subnota', + 'label_subnote_type' => 'Tipo de Subnota', + 'label_subnote_content' => 'Contenido', + 'type_option_text' => '๐Ÿ“„ Texto genรฉrico', + 'type_option_command' => 'โšก Comando (con botรณn Copiar)', + + // Profile & Settings + 'profile_title' => '๐ŸŽจ Ajustes de Perfil y Tema', + 'profile_desc' => 'Personaliza contraseรฑa, colores de interfaz, idioma y pรกgina de inicio.', + 'card_password_title' => '๐Ÿ”’ Cambiar Contraseรฑa', + 'card_colors_title' => '๐ŸŽจ Colores de Interfaz (HSL)', + 'card_lang_title' => '๐ŸŒ Idioma de la Interfaz', + 'card_landing_title' => '๐Ÿ  Pรกgina de Inicio Predeterminada', + 'label_new_pass' => 'Nueva Contraseรฑa', + 'label_confirm_pass' => 'Confirmar Nueva Contraseรฑa', + 'label_select_lang' => 'Seleccionar Idioma', + 'label_select_landing' => 'Seleccionar Pรกgina de Inicio', + 'landing_option_dashboard' => '๐Ÿ“‹ Pรกgina Principal (Todos los Blocknotes)', + 'landing_option_bn_prefix' => '๐Ÿ“Œ Abrir directamente: ', + 'btn_update_pass' => '๐Ÿ”‘ Actualizar Contraseรฑa', + 'btn_save_colors' => '๐Ÿ’พ Guardar Colores', + 'btn_save_lang' => '๐ŸŒ Guardar Idioma', + 'btn_save_landing' => '๐Ÿ  Guardar Pรกgina de Inicio', + + // Admin Users + 'admin_title' => '๐Ÿ‘‘ Panel de Administraciรณn de Usuarios', + 'admin_desc' => 'Crea, gestiona y edita roles de usuarios y accesos.', + 'btn_add_user' => 'โž• Aรฑadir Nuevo Usuario', + 'col_username' => 'Nombre de Usuario', + 'col_role' => 'Rol', + 'col_bns' => 'Blocknotes Creados', + 'col_created' => 'Fecha de Creaciรณn', + 'col_actions' => 'Acciones de Admin', + 'role_standard' => 'Usuario Estรกndar', + 'role_admin' => 'Administrador', + 'role_owner' => '๐Ÿ‘‘ Propietario (Owner)', + 'badge_owner' => '๐Ÿ‘‘ Propietario', + 'btn_reset_pass' => '๐Ÿ”‘ Reset Pass', + 'modal_create_user' => 'โž• Registrar Nuevo Usuario', + 'modal_reset_pass' => '๐Ÿ”‘ Resetear Contraseรฑa', + 'card_site_settings_title' => 'โš™๏ธ Ajustes del Sitio (Solo Propietario)', + 'label_site_title' => 'Tรญtulo del Sitio / Aplicaciรณn', + 'label_favicon' => 'Favicon del Sitio (Alta resoluciรณn, mรญn 64x64px)', + 'btn_save_site_title' => '๐Ÿ’พ Guardar Tรญtulo', + 'btn_upload_favicon' => '๐Ÿ“ค Subir Nueva Favicon', + + // Confirm Dialogs + 'confirm_delete_blocknote' => 'ยฟEstรกs seguro de que quieres eliminar este blocknote y todas sus notas?', + 'confirm_delete_note' => 'ยฟEstรกs seguro de que quieres eliminar esta nota y todas sus subnotas?', + 'confirm_delete_subnote' => 'ยฟEstรกs seguro de que quieres eliminar esta subnota?', + 'confirm_delete_user' => 'ยฟEstรกs seguro de que quieres eliminar este usuario?', + + // Placeholders + 'placeholder_bn_title' => 'Ej. Comandos Servidor Linux...', + 'placeholder_rename_title' => 'Nuevo tรญtulo...', + 'placeholder_import_title' => 'Ej. Positive Internet', + 'placeholder_paste_json' => 'Pega el JSON aquรญ en formato antiguo o nuevo...', + 'placeholder_note_title' => 'Ej. Instalaciรณn MySQL...', + 'placeholder_note_desc' => 'Breve descripciรณn de este bloque de notas...', + 'placeholder_subnote_title' => 'Ej. Comando creaciรณn base de datos...', + 'placeholder_subnote_content' => 'Introduce el texto o instrucciones del comando...', + 'placeholder_site_title' => 'Ej. Blocknotes...', + 'placeholder_username_new' => 'ej. mario, juan...', + 'placeholder_password_new' => 'Introduce nueva contraseรฑa', + 'placeholder_password_confirm' => 'Repite nueva contraseรฑa', + + // Extra Labels + 'label_owner_prefix' => 'Propietario: ', + 'label_updated_prefix' => 'รšltima actualizaciรณn: ', + 'label_you' => 'Tรบ', + 'label_unknown' => 'Desconocido', + 'label_current_favicon' => 'Favicon Actual: ', + + // First Start Installer Setup + 'setup_title' => '๐Ÿš€ Configuraciรณn Inicial de Blocknotes', + 'setup_subtitle' => 'Configura la base de datos MySQL y crea los primeros usuarios privilegiados (Propietario y Admin).', + 'setup_sec1_title' => '1. Configuraciรณn de Base de Datos MySQL', + 'setup_db_host' => 'Host de Base de Datos', + 'setup_db_name' => 'Nombre de la Base de Datos', + 'setup_db_user' => 'Usuario MySQL', + 'setup_db_pass' => 'Contraseรฑa MySQL', + 'setup_db_pass_placeholder' => 'Contraseรฑa (deja en blanco si no hay)', + 'setup_sec2_title' => '2. Cuenta de Propietario (รšnico)', + 'setup_owner_user' => 'Nombre de Usuario Propietario', + 'setup_owner_pass' => 'Contraseรฑa de Propietario', + 'setup_owner_pass_placeholder' => 'Introduce contraseรฑa para Propietario', + 'setup_sec3_title' => '3. Primera Cuenta de Administrador (Admin)', + 'setup_admin_user' => 'Nombre de Usuario Admin', + 'setup_admin_pass' => 'Contraseรฑa de Admin', + 'setup_admin_pass_placeholder' => 'Introduce contraseรฑa para Admin', + 'setup_btn_submit' => 'โšก Completar Instalaciรณn y Configurar Sistema', + 'err_setup_db_fields' => 'Por favor completa todos los campos de conexiรณn a la base de datos MySQL.', + 'err_setup_owner_fields' => 'Por favor introduce nombre de usuario y contraseรฑa para el Propietario.', + 'err_setup_admin_fields' => 'Por favor introduce nombre de usuario y contraseรฑa para el primer Admin.', + 'err_setup_same_user' => 'El usuario Propietario y el usuario Admin deben tener nombres diferentes.' + ], + + 'fr' => [ + // Login Page + 'login_title' => 'Connexion ร  Blocknotes', + 'login_subtitle' => 'Gรฉrez vos notes et vos commandes', + 'label_username' => 'Nom d\'Utilisateur', + 'label_password' => 'Mot de Passe', + 'placeholder_username' => 'Entrez nom d\'utilisateur', + 'placeholder_password' => 'Entrez mot de passe', + 'btn_submit_login' => '๐Ÿ”‘ Connexion', + 'login_hint_footer' => 'Identifiant par dรฉfaut pour le premier accรจs', + 'login_installed_success' => '๐ŸŽ‰ Configuration terminรฉe! Connectez-vous avec vos identifiants.', + + // Navbar + 'nav_blocknotes' => '๐Ÿ““ Blocknotes', + 'nav_colors_profile' => '๐ŸŽจ Couleurs & Profil', + 'nav_admin' => '๐Ÿ‘‘ Gestion des Utilisateurs', + 'nav_logout' => '๐Ÿšช Dรฉconnexion', + 'nav_login' => '๐Ÿ”‘ Connexion', + 'nav_language' => '๐ŸŒ Langue', + + // Dashboard (home) + 'dashboard_title_user' => 'Mes Blocknotes', + 'dashboard_title_admin' => 'Tous les Blocknotes (Vue Admin)', + 'dashboard_desc_user' => 'Crรฉez, organisez et consultez vos blocknotes.', + 'dashboard_desc_admin' => 'En tant qu\'Admin vous pouvez afficher, gรฉrer et supprimer les blocknotes de tous les utilisateurs.', + 'btn_new_blocknote' => 'โž• Nouveau Blocknote', + 'btn_import_json' => '๐Ÿ“ฅ Importer JSON', + 'btn_export_json' => '๐Ÿ’พ Exporter JSON', + 'btn_open_edit' => '๐Ÿ‘๏ธ Ouvrir & Modifier', + 'btn_rename' => 'โœ๏ธ Renommer', + 'btn_delete' => '๐Ÿ—‘๏ธ Supprimer', + 'empty_blocknotes_title' => 'Aucun blocknote trouvรฉ', + 'empty_blocknotes_desc_user' => 'Vous n\'avez pas encore crรฉรฉ de blocknote. Commencez dรจs maintenant!', + 'empty_blocknotes_desc_admin' => 'Il n\'y a encore aucun blocknote enregistrรฉ dans le systรจme.', + 'modal_create_title' => 'โž• Crรฉer un Nouveau Blocknote', + 'modal_rename_title' => 'โœ๏ธ Renommer le Blocknote', + 'modal_import_title' => '๐Ÿ“ฅ Importer Blocknote (JSON)', + 'label_title' => 'Titre du Blocknote', + 'label_file_json' => 'Tรฉlรฉverser le fichier .json', + 'label_paste_json' => 'Ou collez le code JSON ici', + 'btn_cancel' => 'Annuler', + 'btn_save' => 'Enregistrer', + 'btn_create' => 'Crรฉer Blocknote', + 'btn_import' => 'Importer Blocknote', + 'note_single' => 'Note', + 'note_plural' => 'Notes', + + // View Blocknote + 'btn_back_list' => 'โฌ…๏ธ Retour aux Blocknotes', + 'btn_expand_all' => '๐Ÿ“‚ Tout Dรฉvelopper', + 'btn_collapse_all' => '๐Ÿ“ Tout Rรฉduire', + 'btn_new_note' => 'โž• Nouvelle Note', + 'btn_add_subnote' => 'โž• Sous-note', + 'subnote_single' => 'sous-note', + 'subnote_plural' => 'sous-notes', + 'badge_text' => '๐Ÿ“„ Texte', + 'badge_command' => 'โšก Commande', + 'btn_copy' => '๐Ÿ“‹ Copier', + 'btn_copied' => 'โœ“ Copiรฉ!', + 'search_placeholder' => '๐Ÿ” Rechercher notes, sous-notes ou commandes...', + 'btn_clear_search' => 'โŒ Annuler la Recherche', + 'search_no_results' => 'Aucun rรฉsultat trouvรฉ', + 'search_no_results_desc' => 'Aucune note ou commande ne correspond ร  votre recherche.', + 'modal_add_note' => 'โž• Nouvelle Note par Bloc', + 'modal_edit_note' => 'โœ๏ธ Modifier la Note', + 'modal_add_subnote' => 'โž• Ajouter une Sous-note', + 'modal_edit_subnote' => 'โœ๏ธ Modifier la Sous-note', + 'label_note_description' => 'Description (facultatif)', + 'label_subnote_title' => 'Titre de la Sous-note', + 'label_subnote_type' => 'Type de Sous-note', + 'label_subnote_content' => 'Contenu', + 'type_option_text' => '๐Ÿ“„ Texte gรฉnรฉrique', + 'type_option_command' => 'โšก Commande (avec bouton Copier)', + + // Profile & Settings + 'profile_title' => '๐ŸŽจ Paramรจtres Profil & Thรจme', + 'profile_desc' => 'Personnalisez votre mot de passe, couleurs, langue et page d\'accueil.', + 'card_password_title' => '๐Ÿ”’ Changer le Mot de Passe', + 'card_colors_title' => '๐ŸŽจ Couleurs de l\'Interface (HSL)', + 'card_lang_title' => '๐ŸŒ Langue de l\'Interface', + 'card_landing_title' => '๐Ÿ  Page d\'Accueil par Dรฉfaut', + 'label_new_pass' => 'Nouveau Mot de Passe', + 'label_confirm_pass' => 'Confirmer le Mot de Passe', + 'label_select_lang' => 'Sรฉlectionner la Langue', + 'label_select_landing' => 'Sรฉlectionner la Page d\'Accueil', + 'landing_option_dashboard' => '๐Ÿ“‹ Page Principale (Tous les Blocknotes)', + 'landing_option_bn_prefix' => '๐Ÿ“Œ Ouvrir directement: ', + 'btn_update_pass' => '๐Ÿ”‘ Mettre ร  Jour', + 'btn_save_colors' => '๐Ÿ’พ Enregistrer les Couleurs', + 'btn_save_lang' => '๐ŸŒ Enregistrer la Langue', + 'btn_save_landing' => '๐Ÿ  Enregistrer la Page d\'Accueil', + + // Admin Users + 'admin_title' => '๐Ÿ‘‘ Panneau d\'Administration Utilisateurs', + 'admin_desc' => 'Crรฉez, gรฉrez et modifiez les rรดles et accรจs des utilisateurs.', + 'btn_add_user' => 'โž• Ajouter un Utilisateur', + 'col_username' => 'Nom d\'Utilisateur', + 'col_role' => 'Rรดle', + 'col_bns' => 'Blocknotes Crรฉรฉs', + 'col_created' => 'Date de Crรฉation', + 'col_actions' => 'Actions Admin', + 'role_standard' => 'Utilisateur Standard', + 'role_admin' => 'Administrateur', + 'role_owner' => '๐Ÿ‘‘ Propriรฉtaire (Owner)', + 'badge_owner' => '๐Ÿ‘‘ Propriรฉtaire', + 'btn_reset_pass' => '๐Ÿ”‘ Rรฉinit Pass', + 'modal_create_user' => 'โž• Enregistrer un Utilisateur', + 'modal_reset_pass' => '๐Ÿ”‘ Rรฉinitialiser le Mot de Passe', + 'card_site_settings_title' => 'โš™๏ธ Paramรจtres du Site (Propriรฉtaire Uniquement)', + 'label_site_title' => 'Titre Global du Site', + 'label_favicon' => 'Favicon du Site (Haute rรฉsolution, min 64x64px)', + 'btn_save_site_title' => '๐Ÿ’พ Enregistrer le Titre', + 'btn_upload_favicon' => '๐Ÿ“ค Tรฉlรฉverser la Favicon', + + // Confirm Dialogs + 'confirm_delete_blocknote' => 'รŠtes-vous sรปr de vouloir supprimer ce blocknote et toutes ses notes?', + 'confirm_delete_note' => 'รŠtes-vous sรปr de vouloir supprimer cette note et toutes ses sous-notes?', + 'confirm_delete_subnote' => 'รŠtes-vous sรปr de vouloir supprimer cette sous-note?', + 'confirm_delete_user' => 'รŠtes-vous sรปr de vouloir supprimer cet utilisateur?', + + // Placeholders + 'placeholder_bn_title' => 'Ex. Commandes Serveur Linux...', + 'placeholder_rename_title' => 'Nouveau titre...', + 'placeholder_import_title' => 'Ex. Positive Internet', + 'placeholder_paste_json' => 'Collez le JSON ici au format ancien ou nouveau...', + 'placeholder_note_title' => 'Ex. Installation MySQL...', + 'placeholder_note_desc' => 'Brรจve description de ce bloc de notes...', + 'placeholder_subnote_title' => 'Ex. Commande crรฉation base de donnรฉes...', + 'placeholder_subnote_content' => 'Entrez le texte ou les instructions de la commande...', + 'placeholder_site_title' => 'Ex. Blocknotes...', + 'placeholder_username_new' => 'ex. pierre, marie...', + 'placeholder_password_new' => 'Entrez nouveau mot de passe', + 'placeholder_password_confirm' => 'Rรฉpรฉtez le mot de passe', + + // Extra Labels + 'label_owner_prefix' => 'Propriรฉtaire: ', + 'label_updated_prefix' => 'Derniรจre mise ร  jour: ', + 'label_you' => 'Vous', + 'label_unknown' => 'Inconnu', + 'label_current_favicon' => 'Favicon Actuelle: ', + + // First Start Installer Setup + 'setup_title' => '๐Ÿš€ Configuration Initiale de Blocknotes', + 'setup_subtitle' => 'Configurez la base de donnรฉes MySQL et crรฉez les premiers utilisateurs privilรฉgiรฉs (Propriรฉtaire et Admin).', + 'setup_sec1_title' => '1. Configuration de la Base de Donnรฉes MySQL', + 'setup_db_host' => 'Hรดte de la Base de Donnรฉes', + 'setup_db_name' => 'Nom de la Base de Donnรฉes', + 'setup_db_user' => 'Utilisateur MySQL', + 'setup_db_pass' => 'Mot de Passe MySQL', + 'setup_db_pass_placeholder' => 'Mot de passe (laissez vide si aucun)', + 'setup_sec2_title' => '2. Compte Propriรฉtaire (Unique)', + 'setup_owner_user' => 'Nom d\'Utilisateur Propriรฉtaire', + 'setup_owner_pass' => 'Mot de Passe Propriรฉtaire', + 'setup_owner_pass_placeholder' => 'Entrez le mot de passe pour le Propriรฉtaire', + 'setup_sec3_title' => '3. Premier Compte Administrateur (Admin)', + 'setup_admin_user' => 'Nom d\'Utilisateur Admin', + 'setup_admin_pass' => 'Mot de Passe Admin', + 'setup_admin_pass_placeholder' => 'Entrez le mot de passe pour Admin', + 'setup_btn_submit' => 'โšก Terminer l\'Installation et Configurer le Systรจme', + 'err_setup_db_fields' => 'Veuillez remplir tous les champs de connexion ร  la base de donnรฉes MySQL.', + 'err_setup_owner_fields' => 'Veuillez entrer le nom d\'utilisateur et le mot de passe pour le Propriรฉtaire.', + 'err_setup_admin_fields' => 'Veuillez entrer le nom d\'utilisateur et le mot de passe pour le premier Admin.', + 'err_setup_same_user' => 'L\'utilisateur Propriรฉtaire et l\'utilisateur Admin doivent avoir des noms diffรฉrents.' + ] + ]; +} + +/** + * Translate key to current active language string. + */ +function __($key, $default = '') { + $lang = get_current_lang(); + $translations = get_translations(); + + if (isset($translations[$lang][$key])) { + return $translations[$lang][$key]; + } + if (isset($translations['it'][$key])) { + return $translations['it'][$key]; + } + if (isset($translations['en'][$key])) { + return $translations['en'][$key]; + } + return !empty($default) ? $default : $key; +} diff --git a/functions/site_settings.php b/functions/site_settings.php new file mode 100644 index 0000000..d37456f --- /dev/null +++ b/functions/site_settings.php @@ -0,0 +1,89 @@ +prepare("SELECT setting_value FROM site_settings WHERE setting_key = ? LIMIT 1"); + $stmt->bind_param("s", $key); + $stmt->execute(); + $res = $stmt->get_result(); + + if ($row = $res->fetch_assoc()) { + $stmt->close(); + return $row['setting_value']; + } + $stmt->close(); + return $default; +} + +/** + * Update or insert site setting. + */ +function update_site_setting($key, $value) { + global $conn; + if (!isset($conn)) return false; + + $stmt = $conn->prepare("INSERT INTO site_settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)"); + $stmt->bind_param("ss", $key, $value); + $res = $stmt->execute(); + $stmt->close(); + + return $res; +} + +/** + * Upload and validate high-resolution site Favicon. + */ +function upload_site_favicon($file) { + if (empty($file) || $file['error'] !== UPLOAD_ERR_OK) { + return ['success' => false, 'message' => 'Errore nel caricamento del file favicon.']; + } + + $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); + $allowed_extensions = ['png', 'ico', 'webp', 'svg', 'jpeg', 'jpg']; + + if (!in_array($ext, $allowed_extensions)) { + return ['success' => false, 'message' => 'Formato favicon non supportato. Formati ammessi: PNG, ICO, WEBP, SVG, JPG.']; + } + + // High resolution dimension check for raster formats (PNG, WEBP, JPG, ICO) + if (in_array($ext, ['png', 'webp', 'jpeg', 'jpg', 'ico'])) { + $image_info = @getimagesize($file['tmp_name']); + if ($image_info) { + $width = $image_info[0]; + $height = $image_info[1]; + + // Minimum high resolution requirement: 64x64px + if ($width < 64 || $height < 64) { + return [ + 'success' => false, + 'message' => "Risoluzione favicon troppo bassa ({$width}x{$height}px). Per garantire la massima qualitร  visiva, carica un'immagine ad alta risoluzione (almeno 64x64px)." + ]; + } + } + } + + // Ensure assets/img directory exists + $img_dir = ROOT_DIR . "assets/img/"; + if (!is_dir($img_dir)) { + @mkdir($img_dir, 0777, true); + } + + $new_filename = "favicon_" . time() . "." . $ext; + $target_file = $img_dir . $new_filename; + $relative_path = "assets/img/" . $new_filename; + + if (move_uploaded_file($file['tmp_name'], $target_file)) { + update_site_setting('site_favicon', $relative_path); + return ['success' => true, 'message' => 'Favicon aggiornata con successo!']; + } + + return ['success' => false, 'message' => 'Impossibile salvare il file favicon su disco.']; +} diff --git a/pages/admin_users.php b/pages/admin_users.php new file mode 100644 index 0000000..bd08d11 --- /dev/null +++ b/pages/admin_users.php @@ -0,0 +1,270 @@ + + +
+ + + +
+ โœ… +
+ + + +
+ โš ๏ธ +
+ + + + +
+
+ ๐Ÿ‘‘ +

+
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + Favicon Preview + + +
+ +
+ +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
ID
# + + + + + + + + ๐Ÿ‘‘ + + +
+ + + +
+ +
+ + ๐Ÿ““ Blocknotes + + + + +
+ + + +
+ + + +
+ +
+
+
+
+
+ + + + + + + + diff --git a/pages/export_blocknote.php b/pages/export_blocknote.php new file mode 100644 index 0000000..ea3f41e --- /dev/null +++ b/pages/export_blocknote.php @@ -0,0 +1,27 @@ + +
-
-

Welcome to the Home Page

-

This is the home page.

-
+ + + +
+ โœ… +
+ + + +
+ โš ๏ธ +
+ + + +
+
๐Ÿ“‹
+

+

+ +
+ +
+ + +
+
+

+ + + +

+ + + ๐Ÿ‘ค + + +
+ +
+
+ ๐Ÿ“„ + ๐Ÿ•’ +
+
+ +
+ + + + + + ๐Ÿ’พ + + + + +
+ + + +
+
+
+ +
+
+ + + + + + + + + + + diff --git a/pages/login.php b/pages/login.php new file mode 100644 index 0000000..93cf2fc --- /dev/null +++ b/pages/login.php @@ -0,0 +1,71 @@ + + +
+
+
+
+ ๐Ÿ“ +

+

+
+ + +
+ โœ… +
+ + + +
+ โš ๏ธ +
+ + +
+
+ + +
+ +
+ + +
+ + +
+ +
+ :
+ : admin | : admin123 +
+
+
+
diff --git a/pages/logout.php b/pages/logout.php new file mode 100644 index 0000000..97ea822 --- /dev/null +++ b/pages/logout.php @@ -0,0 +1,4 @@ + + +
+ + +
+ + +
+

+ + +
โœ…
+ + + +
โš ๏ธ
+ + +
+ + +
+ + +
+ + +
+
+ + +
+

+ + +
โœ…
+ + + +
โš ๏ธ
+ + +
+ + +
+ + +
+ + +
+
+ + +
+

+ + +
โœ…
+ + + +
โš ๏ธ
+ + +
+ + +
+ + +
+ +
+ + +
+ + +
+
+ + +
+

+ + +
โœ…
+ + + +
โš ๏ธ
+ + +
+ + + +
+
+ + ยฐ +
+ +
+ Preview Primary +
+
+ + +
+
+ + ยฐ +
+ +
+ Preview Secondary +
+
+ + +
+
+ + ยฐ +
+ +
+ Preview Tertiary +
+
+ + +
+
+ +
+
diff --git a/pages/view_blocknote.php b/pages/view_blocknote.php new file mode 100644 index 0000000..6a009df --- /dev/null +++ b/pages/view_blocknote.php @@ -0,0 +1,375 @@ + + +
+
+ +
+ + + + +
+ โœ… +
+ + + +
+ โš ๏ธ +
+ + + + +
+
+ + ๐Ÿ” +
+ +
+ + + + + + +
+
๐Ÿ“
+

+

+ +
+ +
+ + + + +
+ +
+ + + + + + + + + + + + + + diff --git a/scratch/convert_legacy.php b/scratch/convert_legacy.php new file mode 100644 index 0000000..fcd38e1 --- /dev/null +++ b/scratch/convert_legacy.php @@ -0,0 +1,368 @@ + /dev/null 2>&1 && echo \"Server reachable. Proceeding with script update.\" || { echo \"Server unreachable. Update canceled.\"; exit 1; }; for i in updatebspamscripts findspam blockspamip spamwhat nobanip unblockspams; do echo \"Updating script $i...\"; curl -s -o /usr/local/sbin/$i https://scripts.svm.picl.co.uk/scripts/$i.sh && chmod 700 /usr/local/sbin/$i && echo \"Script '$i' updated.\" || echo \"Error updating script '$i'.\"; done" + } + ] + }, + { + "title": "Create New Database", + "description": "Command to create new database and database use", + "codes": [ + { + "sub_title": "## Create Databases ##", + "code": "CREATE DATABASE unixuser;" + }, + { + "sub_title": "# Create User For DB #", + "code": "grant all privileges on `unixuser`.* to `unixuser`@'%' identified by 'password database';" + } + ] + }, + { + "title": "Rsync From Server to the other", + "description": "Command to rsync between the servers", + "codes": [ + { + "sub_title": "## Rsync Command ##", + "code": "rsync -avzP /home/user/public_html/ user@10.10.10.17:/home/user/public_html/" + } + ] + }, + { + "title": "WP-CLI Commands", + "description": "Usefully commands for wp-cli. Note that you need to change \"from-website\" and \"to-website\" with the correct domain.", + "codes": [ + { + "sub_title": "## Export DB ##", + "code": "wp db export" + }, + { + "sub_title": "## Change Links for wordpress ##", + "code": "wp search-replace 'from-website.Com' 'to-website.com' --all-tables" + }, + { + "sub_title": "Add New User (change the '###*' strings with the correct informations)", + "code": "wp user create ###USERNAME ###EMAIL@DOMAIN --role=administrator --user_pass=\"###PASSWORD\"" + }, + { + "sub_title": "Install WP-CLI (Use this as root user)", + "code": "curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar; chmod +x wp-cli.phar; sudo mv wp-cli.phar /usr/local/bin/wp" + }, + { + "sub_title": "Install varnish-cache plugin:", + "code": "wp plugin install varnish-http-purge\r\nwp plugin activate varnish-http-purge" + }, + { + "sub_title": "Configuration to add in wp-config.php for the cache plugin", + "code": "define('VHP_VARNISH_IP','varnish-vip:6081');" + }, + { + "sub_title": "(Run as Root) Checksums verify WP CLI on all /home folders", + "code": "for u in /home/*; do user=$(basename \"$u\"); for d in httpdocs public_html; do if [ -d \"$u/$d\" ] && [ -f \"$u/$d/wp-config.php\" ]; then echo \"=== $user ($d) ===\"; sudo -u \"$user\" -H wp --path=\"$u/$d\" core verify-checksums; echo; fi; done; done" + }, + { + "sub_title": "verify checksum for a single website", + "code": "wp core verify-checksums" + }, + { + "sub_title": "restore corefiles", + "code": "wp core download --version=$(wp core version) --force" + }, + { + "sub_title": "Create posops user", + "code": "wp user create posops good@positive-internet.com --role=administrator --user_pass='P3dsfj7nYLaMxxXk'" + }, + { + "sub_title": "Cambiare la password di un utente", + "code": "wp user update posops --user_pass='P3dsfj7nYLaMxxXk'" + } + ] + }, + { + "title": "Find Killig Process Error", + "description": "Command to check what has been killed in the VMs / Machine", + "codes": [ + { + "sub_title": "## How to check errors ##", + "code": "grep -E '(reaper|oom)' /var/log/syslog" + } + ] + }, + { + "title": "Generate SSH Key", + "description": "Command to generate ssh key", + "codes": [ + { + "sub_title": "## ED25519 ##", + "code": "ssh-keygen -t ed25519" + }, + { + "sub_title": "## RSA ##", + "code": "ssh-keygen -t rsa -b 4096" + } + ] + }, + { + "title": "User Basic Auth", + "description": "How to create / add user for the basicauth.", + "codes": [ + { + "sub_title": "## CREATE A FILE / USER (cancel existing one) ##", + "code": "htpasswd -c /etc/nginx/.htpasswd-name nuovo_utente" + }, + { + "sub_title": "## ADD EXTRA USER ##", + "code": "htpasswd /etc/nginx/.htpasswd-name username" + }, + { + "sub_title": "Block to add", + "code": "auth_basic \"Restricted Access\";\r\n auth_basic_user_file /etc/nginx/.htpasswd;" + }, + { + "sub_title": "Ownership and privileges", + "code": "htpassloc=\"etc/nginx/.htpasswd\";\r\nchown www-data:www-data $htpassloc;\r\nchmod 644 $htpassloc" + } + ] + }, + { + "title": "Check DB Size", + "description": "Command to check the dimension of the databases; (needs to be specified the databases names)", + "codes": [ + { + "sub_title": "## CHECK DB SIZE ##", + "code": "SELECT table_schema AS \"Database\", ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS \"Dimension (MB)\" FROM information_schema.tables WHERE table_schema IN (\"dbname1 \", \"dbname2\", \"dbname3\", \"dbname4\") GROUP BY table_schema;" + } + ] + }, + { + "title": "Remove Files and keep the newest one", + "description": "Change the variable keep in base to how much newest file you need to keep!", + "codes": [ + { + "sub_title": "## REMOVE OLDEST FILES CODE ##", + "code": "count=0; keep=4; echo \"### FILE THAT WILL BE REMOVED: ###\"; echo \"-------------------------------\"; for i in $(ls -t); do if [ $count -lt $keep ]; then echo \"Keeping: $i\"; else echo \"Removing $i\"; fi; ((count++)); done; echo \"-------------------------------\"; echo \"Do you wish to continue?\"; echo \"y - Yes\"; echo \"default - no\"; read check; check=${check,,}; if [ \"$check\" == \"y\" ]; then echo \"#### REMOVING FILE ####\"; echo \"-------------------------------\"; count=0; for i in $(ls -t); do if [ $count -lt $keep ]; then echo \"Keeping: $i\"; else echo \"Removed $i\"; rm \"$i\"; fi; ((count++)); done; echo \"-------------------------------\"; else echo \"Not Removed\"; fi\r\n" + } + ] + }, + { + "title": "Add Diskspace Command", + "description": "After increased the diskspace of the hard disk on proxmox, we can run this command to add the 100% of the diskspace to the root partition.", + "codes": [ + { + "sub_title": "Command:", + "code": "parted /dev/vda resizepart 1 100%; pvresize /dev/vda1; lvextend -l +100%FREE /dev/vgmain/root; resize2fs /dev/vgmain/root" + } + ] + }, + { + "title": "New Certboot Certificate", + "description": "Install new certboot Certificate", + "codes": [ + { + "sub_title": "Command:", + "code": "certbot certonly --webroot -w /var/www/html -d ###DOMAIN" + } + ] + }, + { + "title": "Extract pfx key", + "description": "Use this command to extract the key and the pem files from a .pfx file.\r\nChange the variables before to run the command.", + "codes": [ + { + "sub_title": "Command:", + "code": "PFX=\"FILE_NAME\";\r\nPASS=\"PASSWORD_PFX\";\r\nopenssl pkcs12 -in \"$PFX\" -passin pass:\"$PASS\" -nocerts -nodes -out server.key && \\\r\nopenssl pkcs12 -in \"$PFX\" -passin pass:\"$PASS\" -nokeys | \\\r\nsed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > fullchain.pem\r\n" + } + ] + }, + { + "title": "check if the ssh-key is valid", + "description": "With this command we can check if a ssh key is valid or not", + "codes": [ + { + "sub_title": "Command:", + "code": "FILENAME=\"path/to/the/file\";\r\nssh-keygen -l -f $FILENAME;\r\n" + } + ] + }, + { + "title": "checking valid rules for studiorep-fw", + "description": "This command is checking which of the following rules has been accepted for the php-rules", + "codes": [ + { + "sub_title": "Command:", + "code": "php-fpm8.3 -tt 2>&1 | grep -A50 \"\\[POOL_NAME\\]\"" + } + ] + }, + { + "title": "Add new VM to spa", + "description": "Use this to add the new element to the spa command. Specify the Public IP, the port and the hostname", + "codes": [ + { + "sub_title": "Command:", + "code": "sudoedit /home/posops/ansible/hosts" + } + ] + }, + { + "title": "PHP-FPM Scripts", + "description": "Script Collection for PHP-FPM", + "codes": [ + { + "sub_title": "Restarting all php-fpm existing", + "code": "for i in /etc/php/*; do version=$(basename \"$i\"); service=\"php${version}-fpm\"; if systemctl list-units --type=service --all | grep -q \"$service\"; then echo \"[ $service ]\"; systemctl restart \"$service\"; systemctl status \"$service\" | grep Active; fi; done" + }, + { + "sub_title": "Check the php-version for the vhost", + "code": "VERSION=8.3; for i in *; do socket=$(grep -oP 'SetHandler\\s+\"proxy:unix:\\K[^|\"]+' \"$i\"); if [ -n \"$socket\" ]; then domain=$(basename \"$socket\" .sock); match=$(grep -iRl \"$domain\" /etc/php/*/fpm/pool.d/); if [ -n \"$match\" ]; then phpver=$(echo \"$match\" | sed -n 's|/etc/php/\\([^/]*\\)/fpm/.*|\\1|p' | sort -u); if echo \"$phpver\" | grep -q \"^$VERSION$\"; then echo \"[ $domain ]\"; echo \"php-fpm $phpver\"; fi; fi; fi; done" + }, + { + "sub_title": "Check the php-version for the vhost", + "code": "exver=\"7.4\" instver=\"8.4\" ; for i in $(dpkg -l *php* | awk '{print $2}' | grep $exver); do apt install php$instver${i:6} -y; done" + } + ] + }, + { + "title": "ownership to 755 folder and 644 files", + "description": "Changing the ownership from 755 to the folders and 644 to the files (sub-directories included) with find + xarg in the current director.", + "codes": [ + { + "sub_title": "Command", + "code": "find . -type d -print0 | xargs -0 chmod 755;\r\nfind . -type f -print0 | xargs -0 chmod 644" + } + ] + } +] +JSON; + +$legacy_data = json_decode($raw_json, true); + +if (!is_array($legacy_data)) { + echo "ERROR: Failed to decode legacy JSON: " . json_last_error_msg() . "\n"; + exit(1); +} + +$bn_id = 'bn_positive_internet'; +$new_bn = [ + 'id' => $bn_id, + 'title' => 'Positive Internet', + 'username' => 'admin', + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + 'notes' => [] +]; + +foreach ($legacy_data as $n_idx => $legacy_note) { + $note_id = 'note_' . ($n_idx + 1) . '_' . uniqid(); + $subnotes = []; + + if (isset($legacy_note['codes']) && is_array($legacy_note['codes'])) { + foreach ($legacy_note['codes'] as $s_idx => $legacy_code) { + $subnotes[] = [ + 'id' => 'sub_' . ($s_idx + 1) . '_' . uniqid(), + 'title' => $legacy_code['sub_title'] ?? 'Comando', + 'type' => 'command', + 'content' => $legacy_code['code'] ?? '' + ]; + } + } + + $new_bn['notes'][] = [ + 'id' => $note_id, + 'title' => $legacy_note['title'] ?? 'Nota', + 'description' => $legacy_note['description'] ?? '', + 'subnotes' => $subnotes + ]; +} + +save_blocknote($new_bn); +echo "SUCCESS! Converted and saved Positive Internet blocknote with " . count($new_bn['notes']) . " notes.\n"; diff --git a/templates/footer.php b/templates/footer.php index 926b647..a038ece 100644 --- a/templates/footer.php +++ b/templates/footer.php @@ -1,9 +1,11 @@ - - - - + + + + \ No newline at end of file diff --git a/templates/header.php b/templates/header.php index 1e29a7a..87588a7 100644 --- a/templates/header.php +++ b/templates/header.php @@ -1,20 +1,124 @@ + - + + - <?php echo ucfirst($GLOBALS["current_page"] ?? "Home"); ?> + + + <?php + $cp = $GLOBALS["current_page"] ?? "home"; + if ($cp === 'home') echo __('dashboard_title_user'); + elseif ($cp === 'view_blocknote') echo __('nav_blocknotes'); + elseif ($cp === 'profile') echo __('profile_title'); + elseif ($cp === 'admin_users') echo __('admin_title'); + elseif ($cp === 'login') echo __('nav_login'); + else echo ucfirst($cp); + ?> - <?= htmlspecialchars($site_title) ?>
-
- -
\ No newline at end of file + +
\ No newline at end of file