Gästebuch Block

Anwendungen für Webseiten. Künstliche Intelligenz verwenden.
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Ja - auf jeden Fall, ich verweise als Download auf Deine Seite:

https://www.mobirise-tutorials.com/AI-B ... tbuch.html
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

Tommy halt dich fest :D

jetzt mache ich dieses Gästebuch auch noch im Bento Style und in einer Schlichten Version, so hat der User ebi Import der Erweiterung 3 Blöcke zur Auswahl ;)

Super Idee :cool:
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

Tommy !!!!

Die KI hat geschlampt :D

Im Java Script die Smileys vergessen klickbar zu mache :crying:

Hier das Java Script für Dich

Code: Alles auswählen

 <script>
    (function() {
        var inBuilder = (typeof MbrsiteApp !== 'undefined') || (window.self !== window.top && /mobirise/i.test(document.referrer));
        if (inBuilder) return;

        var apiPath = 'guestbook-api.php';
        var feedContainer = document.getElementById('js-guestbook-feed');
        var paginationContainer = document.getElementById('js-gb-pagination');
        var form = document.getElementById('js-guestbook-form');
        var statusDiv = document.getElementById('gb-status');
        var msgField = document.getElementById('gb-message');
        
        var currentSection = document.getElementById('js-guestbook-feed') ? document.getElementById('js-guestbook-feed').closest('section') : null;
        
        var allEntries = [];
        var currentPage = 1;
        var itemsPerPage = parseInt('{{perPage}}') || 5;
        
        var isAdmin = false;
        var enteredPassword = ''; 

        if (currentSection) {
            var titleEl = currentSection.querySelector('.js-gb-main-title');
            if (titleEl) {
                titleEl.addEventListener('dblclick', function(e) {
                    e.preventDefault();
                    var pwCheck = prompt('Bitte Admin-Passwort eingeben:');
                    if (pwCheck !== null && pwCheck.trim() !== '') {
                        fetch(apiPath, {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json' },
                            body: JSON.stringify({ action: 'checkLogin', password: pwCheck.trim() })
                        })
                        .then(response => response.json())
                        .then(data => {
                            if (data.success) {
                                isAdmin = true;
                                enteredPassword = pwCheck.trim();
                                alert('Login erfolgreich! Ungeprüfte Beiträge zeigen jetzt das Freischaltfeld. 🔓');
                                loadEntries(); 
                            } else {
                                alert(data.error);
                            }
                        });
                    }
                });
            }

            // 🔥 FIX: Hier ist das Bindungs-Skript für deine Emoji-Bar!
            var emojiButtons = currentSection.querySelectorAll('.emoji-bar .emoji-btn');
            emojiButtons.forEach(function(btn) {
                btn.addEventListener('click', function(e) {
                    e.preventDefault();
                    var chosenEmoji = btn.textContent.replace('Anzeigen', '').trim(); // Bereinigt eventuelle Rückstände
                    if (msgField) {
                        var start = msgField.selectionStart;
                        var end = msgField.selectionEnd;
                        var text = msgField.value;
                        msgField.value = text.substring(0, start) + chosenEmoji + text.substring(end);
                        msgField.focus();
                        msgField.selectionStart = msgField.selectionEnd = start + chosenEmoji.length;
                    }
                });
            });
        }

        function renderFeed() {
            if (!feedContainer) return;
            if (!allEntries || allEntries.length === 0) {
                feedContainer.innerHTML = '<div class="text-center py-4 text-fallback">Noch keine freigeschalteten Einträge vorhanden.</div>';
                if (paginationContainer) paginationContainer.innerHTML = '';
                return;
            }

            var startIndex = (currentPage - 1) * itemsPerPage;
            var endIndex = startIndex + itemsPerPage;
            var pageEntries = allEntries.slice(startIndex, endIndex);

            var html = '';
            pageEntries.forEach(function(entry) {
                var date = new Date(entry.created_at.replace(/-/g, "/"));
                var formattedDate = date.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) + ' - ' + date.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }) + ' Uhr';
                
                var styleOverride = (entry.status == 0) ? 'style="border: 1px solid #eab308 !important; background: rgba(234, 179, 8, 0.08) !important;"' : '';
                var titleAddon = (entry.status == 0) ? ' <span style="color:#eab308;font-size:0.75rem;">(Wartet auf Freischaltung)</span>' : '';

                html += '<div class="guestbook-entry-card mb-3 glass-card" data-id="' + entry.id + '" ' + styleOverride + '>';
                html += '  <div class="entry-header">';
                html += '    <strong class="entry-author">' + entry.name + titleAddon + '</strong>';
                html += '    <div class="d-flex align-items-center gap-3">';
                html += '      <span class="entry-date">' + formattedDate + '</span>';
                
                if (isAdmin) {
                    html += '   <button class="btn-edit-entry" title="Eintrag bearbeiten" style="background:none; border:none; cursor:pointer; font-size:1.1rem; padding:0; line-height:1;">✏️</button>';
                    html += '   <button class="btn-delete-entry" title="Eintrag löschen" style="background:none; border:none; cursor:pointer; font-size:1.1rem; padding:0; line-height:1;">🗑️</button>';
                }
                
                html += '    </div>';
                html += '  </div>';
                html += '  <p class="entry-text js-entry-text-target">' + entry.message.replace(/\n/g, '<br>') + '</p>';
                
                if (isAdmin && entry.status == 0) {
                    html += '  <div class="mt-3 text-end js-approve-wrapper">';
                    html += '    <button class="btn btn-sm btn-success btn-approve-action" style="padding: 6px 14px; font-size: 0.85rem; font-weight: bold; border-radius: 6px; border: none; background-color: #22c55e !important; color: #fff !important;">';
                    html += '       ✅ Eintrag freischalten';
                    html += '    </button>';
                    html += '  </div>';
                }
                
                html += '</div>';
            });
            feedContainer.innerHTML = html;

            if (isAdmin) {
                feedContainer.querySelectorAll('.btn-approve-action').forEach(function(btn) {
                    btn.addEventListener('click', function(e) {
                        e.stopPropagation();
                        var card = btn.closest('.guestbook-entry-card');
                        var id = card.getAttribute('data-id');
                        approveEntry(id, card);
                    });
                });

                feedContainer.querySelectorAll('.btn-delete-entry').forEach(function(btn) {
                    btn.addEventListener('click', function(e) {
                        e.stopPropagation();
                        var card = btn.closest('.guestbook-entry-card');
                        var id = card.getAttribute('data-id');
                        if (confirm('Möchtest du diesen Eintrag wirklich unwiderruflich löschen?')) {
                            deleteEntry(id, card);
                        }
                    });
                });

                feedContainer.querySelectorAll('.btn-edit-entry').forEach(function(btn) {
                    btn.addEventListener('click', function(e) {
                        e.stopPropagation();
                        var card = btn.closest('.guestbook-entry-card');
                        var id = card.getAttribute('data-id');
                        var textP = card.querySelector('.js-entry-text-target');
                        
                        if (card.classList.contains('is-editing')) return;
                        card.classList.add('is-editing');
                        
                        var origEntry = allEntries.find(item => item.id == id);
                        var currentRawText = origEntry ? origEntry.message : textP.innerText;
                        
                        textP.innerHTML = '<textarea class="form-control mb-2 js-edit-textarea" rows="4" style="color:#000 !important; background:#fff !important;">' + currentRawText + '</textarea>' +
                                          '<button class="btn btn-sm btn-success js-save-edit-btn" style="padding:4px 10px; font-size:0.8rem;">💾 Speichern</button> ' +
                                          '<button class="btn btn-sm btn-danger js-cancel-edit-btn" style="padding:4px 10px; font-size:0.8rem;">Abbrechen</button>';
                        
                        card.querySelector('.js-save-edit-btn').addEventListener('click', function() {
                            var newText = card.querySelector('.js-edit-textarea').value;
                            updateEntryText(id, newText, card);
                        });

                        card.querySelector('.js-cancel-edit-btn').addEventListener('click', function() {
                            renderFeed();
                        });
                    });
                });
            }

            renderPaginationControls();
        }

        function approveEntry(id, card) {
            fetch(apiPath, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ action: 'approve', id: id, password: enteredPassword })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    var entry = allEntries.find(item => item.id == id);
                    if (entry) entry.status = 1;
                    renderFeed(); 
                } else {
                    alert(data.error);
                }
            })
            .catch(err => alert('Fehler bei der Freischaltung.'));
        }

        function updateEntryText(id, newText, card) {
            fetch(apiPath, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ action: 'update', id: id, message: newText, password: enteredPassword })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    var entry = allEntries.find(item => item.id == id);
                    if (entry) entry.message = newText;
                    renderFeed();
                } else {
                    alert(data.error);
                }
            })
            .catch(err => alert('Fehler beim Speichern.'));
        }

        function renderPaginationControls() {
            if (!paginationContainer) return;
            var totalPages = Math.ceil(allEntries.length / itemsPerPage);
            if (totalPages <= 1) {
                paginationContainer.innerHTML = '';
                return;
            }

            var html = '';
            html += '<button class="pag-btn" data-page="' + (currentPage - 1) + '"' + (currentPage === 1 ? ' disabled' : '') + '>«</button>';
            for (var i = 1; i <= totalPages; i++) {
                html += '<button class="pag-btn' + (i === currentPage ? ' active' : '') + '" data-page="' + i + '">' + i + '</button>';
            }
            html += '<button class="pag-btn" data-page="' + (currentPage + 1) + '"' + (currentPage === totalPages ? ' disabled' : '') + '>»</button>';

            paginationContainer.innerHTML = html;

            paginationContainer.querySelectorAll('.pag-btn').forEach(function(btn) {
                btn.addEventListener('click', function() {
                    var targetPage = parseInt(btn.getAttribute('data-page'));
                    if (targetPage >= 1 && targetPage <= totalPages) {
                        currentPage = targetPage;
                        renderFeed();
                        feedContainer.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                    }
                });
            });
        }

        function loadEntries() {
            var url = apiPath + (isAdmin ? '?adminCheck=' + encodeURIComponent(enteredPassword) : '');
            fetch(url)
                .then(response => response.json())
                .then(data => {
                    if (!data.success) {
                        feedContainer.innerHTML = '<div class="alert alert-danger">' + data.error + '</div>';
                        return;
                    }
                    allEntries = data.entries || [];
                    renderFeed();
                })
                .catch(err => {
                    if(feedContainer) {
                        feedContainer.innerHTML = '<div class="text-center py-4 text-fallback">Warte auf ersten Eintrag...</div>';
                    }
                });
        }

        function deleteEntry(id, card) {
            fetch(apiPath, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ action: 'delete', id: id, password: enteredPassword })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    card.style.transition = 'all 0.3s ease';
                    card.style.opacity = '0';
                    card.style.transform = 'scale(0.9)';
                    setTimeout(function() {
                        allEntries = allEntries.filter(entry => entry.id != id);
                        var totalPages = Math.ceil(allEntries.length / itemsPerPage);
                        if (currentPage > totalPages && currentPage > 1) {
                            currentPage = totalPages;
                        }
                        renderFeed();
                    }, 300);
                } else {
                    alert(data.error);
                }
            });
        }

        if (form) {
            form.addEventListener('submit', function(e) {
                e.preventDefault();
                var nameVal = document.getElementById('gb-name').value;
                var msgVal = msgField.value;

                statusDiv.style.display = 'block';
                statusDiv.className = 'mt-3 small text-fallback';
                statusDiv.textContent = 'Eintrag wird zur Prüfung gesendet... ⏳';

                fetch(apiPath, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ name: nameVal, message: msgVal })
                })
                .then(response => response.json())
                .then(data => {
                    if (data.success) {
                        statusDiv.textContent = 'Vielen Dank! Dein Eintrag wird geprüft und bald freigeschaltet. 🎉';
                        form.reset();
                        currentPage = 1;
                        loadEntries();
                        setTimeout(function() { statusDiv.style.display = 'none'; }, 5000);
                    } else {
                        statusDiv.textContent = data.error;
                    }
                })
                .catch(err => {
                    statusDiv.textContent = 'Fehler beim Senden.';
                });
            });
        }

        loadEntries();
    })();
    </script>
Im Zip gleich auch drin
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Alles klar, mache ich morgen früh ...
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

Mein Bento Style GB ist auch fertig :D

https://www.niederastroth.de/bentogb/

Obs gefällt ? ich weiß es nicht, aber ist halt Bento Style :D Und ich hab es geschafft :prost: Das war mir wichtig ;)
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Moin Volker,

danke nochmals für das schöne Gästebuch :)

Bei mir funktionieren nun auch wieder die Smileys:

↗️ https://www.mobirise-tutorials.com/AI-B ... tbuch.html

Ich habe mir jetzt eine eigene Erweiterung als .mbrext nochmals neu erstellt, denn bei Deiner fehlen jetzt die Bilder und daher kann man diese unter den Mobirise Erweiterungen nicht mehr ausmachen, man sieht nur eine leere Kachel.

Weiterhin ist die Vorlage für die SMTP Mail bei Dir mangelhaft, so weiß niemand, was er da eintragen soll, wenn Du alles leer lässt und keine Kommentare schreibst. Bei mir sieht das jetzt so aus:

Code: Alles auswählen

// 🔒 1. DEIN ADMIN-PASSWORT FÜR BROWSER-LOGIN (DOPPELKLICK)
$apiPassword = 'DeinSicheresPasswort'; 

// 📧 2. SMTP & MAIL EMPFÄNGER CONFIGURATION
$adminEmail  = 'mail@DeineDomain.de'; 
$websiteUrl  = 'https://www.DeineDomain.de/eventuelles-Projekt-Verzeichnis/'; // Wichtig für den Freischaltlink!

// 📧 SMTP KONFIGURATION - Leer lassen, wenn du normales PHP-Mail nutzen willst!
//    oder jede Zeile mit zwei // deaktivieren, wenn normales PHP-Mail verwendet wird.
define('SMTP_HOST', 'smtp.ionos.de');                  // z.B. smtp.ionos.de
define('SMTP_PORT', 465);                              // 465 (SSL) oder 587 (TLS)
define('SMTP_CRYPT', 'ssl');                           // 'ssl', 'tls' oder ''
define('SMTP_USER', 'mail@DeineDomain.de');            // Deine SMTP-Mailadresse
define('SMTP_PASS', '**SMTP-Passwort**');              // Dein echtes Mail-Passwort (leer lassen für PHP-Mail)

Das solltest Du schnell nochmals neu erstellen - prüfst Du denn die Dateien, die Du erstellst, nicht :eek:

Grundsätzlich solltest Du auch darüber nachdenken, nicht immer dasselbe Bild für jede Anwendung zu verwenden. So sind die Anwendungen nur schwer zu finden. Das Prinzip von Mobirise ist doch, jeder Erweiterung eine aussagekräftige Beschreibung und ein passendes Bild zuzuordnen.



Das "Bento Gästebuch" ist jetzt für mich persönlich etwas zu wild - aber warum nicht - ist mal etwas anderes :tu:
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

Moin Tommy,

Du bist echt der Knaller :D

JA hab die Bilder vergessen - ist aber auch ein bisschen Stress gewesen - immer einen Tommy im Nacken der immer alles will :D

Ich habe jetzt alles ordentlich im Zip. Auch das Bento Style ist nun als Block da verfügbar ;)

Klar prüfe ich alle meine Scripte und Blöcke immer - durch Dich :D Wofür hab ich dich denn ? :D :D :D

https://www.niederastroth.de/gaestebuch.html
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Schöne Gästebuchseite --- NUR --- ohne Download-Button :anmachen:
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

Das ist doch auch mein richtiges GB für meine Seite.

Der Download kommt gleich auch noch wie bei allen meiner Blöcke als separate Seite ;)
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

Hier die offizielle Download Seite :

https://www.niederastroth.de/gaestebuch_block.html

Hab mal bei Dir abgeschrieben :D
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Sehr schön - alles fertig - dann können wir ja ½ Stunde Pause machen :D
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Du musst den Text an einer Stelle etwas anpassen:

Original:

Die meinem Download beiliegende .mbrext-Datei gastbuch.mbrext

Sollte auf Deiner Seite heißen:

Die meinem Download beiliegende .mbrext-Datei gaestebuch.mbrext
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

Ha ha

blind abschreiben ist immer ein Fehler :D
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Im Gästebuch können Admins übrigens HTML-Code verwenden, sodass sich beliebige Darstellungen realisieren lassen – zum Beispiel Links, Bilder oder Videos:

↗️ https://www.mobirise-tutorials.com/AI-B ... tbuch.html


Gästebuch mit HTML Bearbeitung.jpg
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

Hi Tommy,
mit deinen Code Verbesserungen werde ich deine mbrext in mein Zip legen wenn das OK für Dich ist
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Natürlich ist das OK, das sind jetzt aber nicht Verbessererungen, sondern eher Anpassungen an meine Seite. Ich weiß zum Beispiel nicht genau, ob das mit dem neuen Anker der Pagenierung auch bei anderen dann stimmt. Teste das mal. Ansonsten habe ich das im Script kommentiert, sodass man die Höhe auch alleine anpassen kann oder den alten Zustand wieder herstellen kann.
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

oder ich bau es ins zahnrad dann ist felxibell
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Solange das Design der Eingabebox sich nicht ändert, sollte das eigentlich passen.

Ich würde das nicht flexibel machen, denn das geht schief beim Benutzen vom User.

Beim Seitenwechsel soll der Gästebuchblock ja nur halbwegs logisch beginnen, auch am Handy. Das ist jetzt der Fall.
Volker
Moderator
Moderator
Beiträge: 2518
Registriert: Sa 12. Dez 2020, 22:35

Re: Gästebuch Block

Ungelesener Beitrag von Volker »

Ich bau es trotzdem mal ein, ist doch einfacher für nutzer als das per hand im code zu ändern. Heute Abend ist das in der mbrext, dann testest du das mal und wenn ok, hauen wir das in die mbrext
Schoieber schon drin :D passe ich heute Abend an
gbschieber.PNG
PADDING: Oben (rem) — (Steuert den inneren Abstand nach oben)

PADDING: Rechts (rem) — (Steuert den inneren Abstand nach rechts)

PADDING: Unten (rem) — (Steuert den inneren Abstand nach unten)

PADDING: Links (rem) — (Steuert den inneren Abstand nach links)
gbschieber2.PNG
gbschieber2.PNG (33.42 KiB) 41 mal betrachtet
Ein kurzes Beispiel zur Funktionsweise:

Stellst du den Schieber auf -100 (Standard), springt die Seite beim Klicken auf Seite 2 so weit hoch, dass über dem Feed noch 100 Pixel Platz frei bleiben (gut, damit der Feed nicht direkt unter die klebrige Menüleiste knallt).

Stellst du ihn auf 0, springt der Browser exakt bündig an die Kante des Feeds.

Stellst du ihn auf -200, bleibt noch mehr Platz nach oben frei.

wenn schon denn schon Tommy :D
Benutzeravatar
Tommy Herrmann
Site Admin
Site Admin
Beiträge: 8564
Registriert: So 6. Dez 2020, 07:37
Wohnort: Berlin
Kontaktdaten:

Re: Gästebuch Block

Ungelesener Beitrag von Tommy Herrmann »

Du musst den Block unter einem Header-Block einbauen, nicht ganz oben.

Im Original sprang der Seitenwechsel irgendwo in die Beiträge, aber eben nicht nach oben.
Antworten

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast