Grammar change in the license declaration.
[citadel.git] / webcit-ng / static / js / view_forum.js
index 130f695f2d6e3f64434a2d72be9e692a9e5f9e0c..e5884d400b32ee133b757e459778ac0c1bfe88b1 100644 (file)
@@ -1,14 +1,9 @@
-// Copyright (c) 2016-2022 by the citadel.org team
+// This module handles the view for "forum" (message board) rooms.
 //
-// This program is open source software.  It runs great on the
-// Linux operating system (and probably elsewhere).  You can use,
-// copy, and run it under the terms of the GNU General Public
-// License version 3.  Richard Stallman is an asshole communist.
+// Copyright (c) 2016-2023 by the citadel.org team
 //
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
+// This program is open source software.  Use, duplication, or
+// disclosure is subject to the GNU General Public License v3.
 
 
 // Forum view (flat)
@@ -50,10 +45,10 @@ function forum_readmessages(target_div_name, gt_msg, lt_msg) {
                                }
                                target_div.innerHTML +=
                                        "<div id=\"" + new_old_div_name + "\">" +
-                                       "<div class=\"ctdl-forum-nav\">" +
-                                       "<a href=\"javascript:forum_readmessages('" + new_old_div_name + "', 0, " + newlt + ");\">" +
+                                       "<div class=\"ctdl-forum-nav\" " +
+                                       "onclick=\"javascript:forum_readmessages('" + new_old_div_name + "', 0, " + newlt + ");\">" +
                                        "<i class=\"fa fa-arrow-circle-up\"></i>&nbsp;&nbsp;" +
-                                       _("Older posts") + "&nbsp;&nbsp;<i class=\"fa fa-arrow-circle-up\"></a></div></div></a></div></div>" ;
+                                       _("Older posts") + "&nbsp;&nbsp;<i class=\"fa fa-arrow-circle-up\"></div></div></a></div></div>" ;
                        }
 
                        // The messages will go here.
@@ -70,10 +65,11 @@ function forum_readmessages(target_div_name, gt_msg, lt_msg) {
                                }
                                target_div.innerHTML +=
                                        "<div id=\"" + new_new_div_name + "\">" +
-                                       "<div class=\"ctdl-forum-nav\">" +
-                                       "<a href=\"javascript:forum_readmessages('" + new_new_div_name + "', " + newgt + ", 9999999999);\">" +
+                                       "<div id=\"ctdl-newmsg-here\"></div>" +
+                                       "<div class=\"ctdl-forum-nav\" " +
+                                       "onClick=\"javascript:forum_readmessages('" + new_new_div_name + "', " + newgt + ", 9999999999);\">" +
                                        "<i class=\"fa fa-arrow-circle-down\"></i>&nbsp;&nbsp;" +
-                                       _("Newer posts") + "&nbsp;&nbsp;<i class=\"fa fa-arrow-circle-down\"></a></div></div>" ;
+                                       _("Newer posts") + "&nbsp;&nbsp;<i class=\"fa fa-arrow-circle-down\"></div></div>" ;
                        }
 
                        // Now figure out where to scroll to after rendering.
@@ -99,6 +95,17 @@ function forum_readmessages(target_div_name, gt_msg, lt_msg) {
                }
        }
        fetch_msg_list();
+
+       // make the nav buttons appear (post a new message, skip this room, goto next room)
+
+       document.getElementById("ctdl-newmsg-button").innerHTML = `<i class="fa fa-edit"></i>&nbsp;` + _("Post message");
+       document.getElementById("ctdl-newmsg-button").style.display = "block";
+
+       document.getElementById("ctdl-skip-button").innerHTML = _("Skip this room") + `&nbsp;<i class="fa fa-arrow-alt-circle-right"></i>`;
+       document.getElementById("ctdl-skip-button").style.display = "block";
+
+       document.getElementById("ctdl-goto-button").innerHTML = _("Goto next room") + `&nbsp;<i class="fa fa-arrow-circle-right"></i>`;
+       document.getElementById("ctdl-goto-button").style.display = "block";
 }
 
 
@@ -113,7 +120,6 @@ function forum_render_messages(message_numbers, msgs_div_name, scroll_to) {
                        .then(response => response.json())
                        .catch((error) => {
                                response => null;
-                               console.error('Error: ', error);
                        })
                ;
        }
@@ -144,6 +150,12 @@ function forum_render_messages(message_numbers, msgs_div_name, scroll_to) {
        }
 
        fetch_msg_list();
+
+       // Make a note of the highest message number we saw, so we can mark it when we "Goto next room"
+       // (Compared to the text client, this is actually more like <A>bandon than <G>oto)
+       if ((num_msgs > 0) && (message_numbers[num_msgs-1] > last_seen)) {
+               last_seen = message_numbers[num_msgs-1];
+       }
 }
 
 
@@ -162,19 +174,14 @@ function forum_render_one(msg, existing_div) {
 
        try {
                outmsg =
-                 "<div class=\"ctdl-msg-wrapper\">"                            // begin message wrapper
-               + "<div class=\"ctdl-avatar\">"                                 // begin avatar
-               + "<img src=\"/ctdl/u/" + msg.from + "/userpic\" width=\"32\" "
-               + "onerror=\"this.parentNode.innerHTML='&lt;i class=&quot;fa fa-user-circle fa-2x&quot;&gt;&lt;/i&gt; '\">"
-               + "</div>"                                                      // end avatar
-               + "<div class=\"ctdl-msg-content\">"                            // begin content
+                 "<div class=\"ctdl-fmsg-wrapper\">"                           // begin message wrapper
+               + render_userpic(msg.from)                                      // user avatar
+               + "<div class=\"ctdl-fmsg-content\">"                           // begin content
                + "<div class=\"ctdl-msg-header\">"                             // begin header
                + "<span class=\"ctdl-msg-header-info\">"                       // begin header info on left side
-               + "<span class=\"ctdl-username\"><a href=\"#\">"                // FIXME link to user profile
-               + msg.from
-               + "</a></span>"                                                 // end username
+               + render_msg_author(msg, views.VIEW_BBS)                        // author
                + "<span class=\"ctdl-msgdate\">"
-               + convertTimestamp(msg.time)
+               + string_timestamp(msg.time,0)
                + "</span>"                                                     // end msgdate
                + "</span>"                                                     // end header info on left side
                + "<span class=\"ctdl-msg-header-buttons\">"                    // begin buttons on right side
@@ -189,21 +196,26 @@ function forum_render_one(msg, existing_div) {
                + "<a href=\"javascript:open_reply_box('"+mdiv+"',true,'"+msg.wefw+"','"+msg.msgn+"');\">"
                + "<i class=\"fa fa-comment\"></i> " 
                + _("ReplyQuoted")
-               + "</a></span>"
+               + "</a></span>";
        
-               + "<span class=\"ctdl-msg-button\"><a href=\"#\">"              // Delete , show only with permission FIXME
-               + "<i class=\"fa fa-trash\"></i> " 
-               + _("Delete")
-               + "</a></span>"
+               if (can_delete_messages) {
+                       outmsg +=
+                       "<span class=\"ctdl-msg-button\">"
+                       + "<a href=\"javascript:forum_delete_message('"+mdiv+"','"+msg.msgnum+"');\">"
+                       + "<i class=\"fa fa-trash\"></i> " 
+                       + _("Delete")
+                       + "</a></span>";
+               }
        
-               + "</span>";                                                    // end buttons on right side
+               outmsg +=
+                 "</span>";                                                    // end buttons on right side
                if (msg.subj) {
                        outmsg +=
                        "<br><span class=\"ctdl-msgsubject\">" + msg.subj + "</span>";
                }
                outmsg +=
-                 "</div><br>"                                                  // end header
-               + "<div class=\"ctdl-msg-body\">"                               // begin body
+                 "</div>"                                                      // end header
+               + "<div class=\"ctdl-msg-body\" id=\"" + mdiv + "_body\">"      // begin body
                + msg.text
                + "</div>"                                                      // end body
                + "</div>"                                                      // end content
@@ -211,7 +223,7 @@ function forum_render_one(msg, existing_div) {
                ;
        }
        catch(err) {
-               outmsg = "<div class=\"ctdl-msg-wrapper\">" + err.message + "</div>";
+               outmsg = "<div class=\"ctdl-fmsg-wrapper\">" + err.message + "</div>";
        }
 
        div.innerHTML = outmsg;
@@ -238,6 +250,24 @@ function compose_references(references, msgid) {
        return refs;
 }
 
+// Delete a message.
+// We don't bother checking for permission because the button only appears if we have permission,
+// and even if someone hacks the client, the server will deny any unauthorized deletes.
+function forum_delete_message(message_div, message_number) {
+       if (confirm(_("Delete this message?")) == true) {
+               async_forum_delete_message = async() => {
+                       response = await fetch(
+                               "/ctdl/r/" + escapeHTMLURI(current_room) + "/" + message_number,
+                               { method: "DELETE" }
+                       );
+                       if (response.ok) {              // If the server accepted the delete, blank out the message div.
+                               document.getElementById(message_div).outerHTML = "";
+                       }
+               }
+               async_forum_delete_message();
+       }
+}
+
 
 // Open a reply box directly below a specific message
 function open_reply_box(parent_div, is_quoted, references, msgid) {
@@ -248,19 +278,19 @@ function open_reply_box(parent_div, is_quoted, references, msgid) {
        document.getElementById(parent_div).append(new_div);
 
        replybox =
-         "<div class=\"ctdl-msg-wrapper ctdl-msg-reply\">"             // begin message wrapper
+         "<div class=\"ctdl-fmsg-wrapper ctdl-msg-reply\">"            // begin message wrapper
        + "<div class=\"ctdl-avatar\">"                                 // begin avatar
-       + "<img src=\"/ctdl/u/" + "FIXME my name" + "/userpic\" width=\"32\" "
+       + "<img src=\"/ctdl/u/" + current_user + "/userpic\" width=\"32\" "
        + "onerror=\"this.parentNode.innerHTML='&lt;i class=&quot;fa fa-user-circle fa-2x&quot;&gt;&lt;/i&gt; '\">"
        + "</div>"                                                      // end avatar
-       + "<div class=\"ctdl-msg-content\">"                            // begin content
+       + "<div class=\"ctdl-fmsg-content\">"                           // begin content
        + "<div class=\"ctdl-msg-header\">"                             // begin header
        + "<span class=\"ctdl-msg-header-info\">"                       // begin header info on left side
        + "<span class=\"ctdl-username\">"
        + current_user                                                  // user = me !
        + "</span>"
        + "<span class=\"ctdl-msgdate\">"
-       + convertTimestamp(Date.now() / 1000)                           // the current date/time (temporary for display)
+       + string_timestamp((Date.now() / 1000),0)                       // the current date/time (temporary for display)
        + "</span>"
        + "</span>"                                                     // end header info on left side
        + "<span class=\"ctdl-msg-header-buttons\">"                    // begin buttons on right side
@@ -299,9 +329,17 @@ function open_reply_box(parent_div, is_quoted, references, msgid) {
        + "<span id=\"ctdl-replyreferences\" style=\"display:none\">"   // hidden field for references
        + compose_references(references,msgid) + "</span>"
                                                                        // begin body
-       + "<div class=\"ctdl-msg-body\" id=\"ctdl-editor-body\" style=\"padding:5px;\" contenteditable=\"true\">"
-       + "\n"                                                          // empty initial content
-       + "</div>"                                                      // end body
+       + "<div class=\"ctdl-msg-body ctdl-forum-editor-body\" id=\"ctdl-editor-body\" contenteditable=\"true\">"
+       + "\n";                                                         // empty initial content
+
+       if (is_quoted) {
+               replybox += "<br><blockquote>"
+                       + document.getElementById(parent_div+"_body").innerHTML
+                       + "</blockquote>";
+       }
+
+       replybox +=
+         "</div>"                                                      // end body
 
        + "<div class=\"ctdl-msg-header\">"                             // begin footer
        + "<span class=\"ctdl-msg-header-info\">"                       // begin footer info on left side
@@ -326,25 +364,22 @@ function open_reply_box(parent_div, is_quoted, references, msgid) {
        + "</div>"                                                      // end content
        + "</div>"                                                      // end wrapper
 
-       + "<div id=\"forum_url_entry_box\" class=\"w3-modal\">"         // begin URL entry modal
-       + "     <div class=\"w3-modal-content w3-animate-top w3-card-4\">"
-       + "             <header class=\"w3-container w3-blue\"> "
-       + "                     <p><span>URL:</span></p>"
-       + "             </header>"
-       + "             <div class=\"w3-container w3-blue\">"
-       + "                     <input id=\"forum_txtFormatUrl\" placeholder=\"http://\" style=\"width:100%\">"
-       + "             </div>"
-       + "             <footer class=\"w3-container w3-blue\">"
-       + "                     <p><span class=\"ctdl-msg-button\"><a href=\"javascript:forum_close_urlbox(true);\">"
-       + "                             <i class=\"fa fa-check\" style=\"color:green\"></i> "
-       +                               _("Save")
-       +                       "</a></span>"
-       + "                     <span class=\"ctdl-msg-button\"><a href=\"javascript:forum_close_urlbox(false);\">"
-       + "                             <i class=\"fa fa-trash\" style=\"color:red\"></i> "
-       +                               _("Cancel")
-       +                       "</a></span></p>"
-       + "             </footer>"
-       + "             </div>"
+       + "<div id=\"forum_url_entry_box\" class=\"ctdl-modal ctdl-forum-urlmodal\">"           // begin URL entry modal
+       + "     <div class=\"ctdl-modal-header\">" 
+       + "             <p>URL:</p>"
+       + "     </div>"
+       + "     <div class=\"ctdl-modal-main\">"
+       + "             <input id=\"ctdl-forum-urlbox\" placeholder=\"http://\" style=\"width:100%\">"
+       + "     </div>"
+       + "     <div class=\"ctdl-modal-footer\">"
+       + "             <p><span class=\"ctdl-msg-button\"><a href=\"javascript:forum_close_urlbox(true);\">"
+       + "                     <i class=\"fa fa-check\" style=\"color:green\"></i> "
+       +                       _("Save")
+       +               "</a></span>"
+       + "             <span class=\"ctdl-msg-button\"><a href=\"javascript:forum_close_urlbox(false);\">"
+       + "                     <i class=\"fa fa-trash\" style=\"color:red\"></i> "
+       +                       _("Cancel")
+       +               "</a></span></p>"
        + "     </div>"
        + "     <input id=\"forum_selection_start\" style=\"display:none\"></input>"    // hidden fields
        + "     <input id=\"forum_selection_end\" style=\"display:none\"></input>"      // to store selection range
@@ -380,21 +415,11 @@ function forum_save_message(editor_div_name) {
                + "/dummy_name_for_new_message"
                + "?wefw=" + wefw
                + "&subj=" + subj
-       boundary = randomString();
-       body_text =
-               "--" + boundary + "\r\n"
-               + "Content-type: text/html\r\n"
-               + "Content-transfer-encoding: quoted-printable\r\n"
-               + "\r\n"
-               + quoted_printable_encode(
-                       "<html><body>" + document.getElementById("ctdl-editor-body").innerHTML + "</body></html>"
-               ) + "\r\n"
-               + "--" + boundary + "--\r\n"
-       ;
+       body_text = "<html><body>" + document.getElementById("ctdl-editor-body").innerHTML + "</body></html>\r\n";
 
        var request = new XMLHttpRequest();
        request.open("PUT", url, true);
-       request.setRequestHeader("Content-type", "multipart/mixed; boundary=\"" + boundary + "\"");
+       request.setRequestHeader("Content-type", "text/html");
        request.onreadystatechange = function() {
                if (request.readyState == 4) {
                        document.body.style.cursor = "default";
@@ -452,12 +477,25 @@ function forum_close_urlbox(do_save) {
                var start_replace = document.getElementById("forum_selection_start").value;     // use saved selection range
                var end_replace = document.getElementById("forum_selection_end").value;
                new_text = tag.innerHTML.substring(0, start_replace)
-                       + "<a href=\"" + document.getElementById("forum_txtFormatUrl").value + "\">"
+                       + "<a href=\"" + document.getElementById("ctdl-forum-urlbox").value + "\">"
                        + tag.innerHTML.substring(start_replace, end_replace)
                        + "</a>"
                        + tag.innerHTML.substring(end_replace);
                tag.innerHTML = new_text;
        }
-       document.getElementById("forum_txtFormatUrl").value = "";                               // clear url box for next time
+       document.getElementById("ctdl-forum-urlbox").value = "";                                // clear url box for next time
        document.getElementById("forum_url_entry_box").style.display = "none";
 }
+
+
+// User has clicked the "Post message" button.  This is roughly the same as "reply" except there is no parent message.
+function forum_entmsg() {
+       open_reply_box("ctdl-newmsg-here", false, "", "");
+}
+
+
+// RENDERER FOR THIS VIEW
+function view_render_forums() {
+       document.getElementById("ctdl-main").innerHTML = `<div id="ctdl-mrp" class="ctdl-forum-reading-pane"></div>`;
+       forum_readmessages("ctdl-mrp", 0, 9999999999);
+}