api, frontend: add comment editing
This commit is contained in:
parent
ce19cb8842
commit
cc1dfee017
66
api/comment_edit.go
Normal file
66
api/comment_edit.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func commentEdit(commentHex string, markdown string) (string, error) {
|
||||||
|
if commentHex == "" {
|
||||||
|
return "", errorMissingField
|
||||||
|
}
|
||||||
|
|
||||||
|
html := markdownToHtml(markdown)
|
||||||
|
|
||||||
|
statement := `
|
||||||
|
UPDATE comments
|
||||||
|
SET markdown = $2, html = $3
|
||||||
|
WHERE commentHex=$1;
|
||||||
|
`
|
||||||
|
_, err := db.Exec(statement, commentHex, markdown, html)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// TODO: make sure this is the error is actually non-existant commentHex
|
||||||
|
return "", errorNoSuchComment
|
||||||
|
}
|
||||||
|
|
||||||
|
return html, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func commentEditHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
type request struct {
|
||||||
|
CommenterToken *string `json:"commenterToken"`
|
||||||
|
CommentHex *string `json:"commentHex"`
|
||||||
|
Markdown *string `json:"markdown"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var x request
|
||||||
|
if err := bodyUnmarshal(r, &x); err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := commenterGetByCommenterToken(*x.CommenterToken)
|
||||||
|
if err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cm, err := commentGetByCommentHex(*x.CommentHex)
|
||||||
|
if err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if cm.CommenterHex != c.CommenterHex {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": errorNotAuthorised.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
html, err := commentEdit(*x.CommentHex, *x.Markdown)
|
||||||
|
if err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyMarshal(w, response{"success": true, "html": html})
|
||||||
|
}
|
@ -51,6 +51,7 @@ func apiRouterInit(router *mux.Router) error {
|
|||||||
router.HandleFunc("/api/oauth/sso/callback", ssoCallbackHandler).Methods("GET")
|
router.HandleFunc("/api/oauth/sso/callback", ssoCallbackHandler).Methods("GET")
|
||||||
|
|
||||||
router.HandleFunc("/api/comment/new", commentNewHandler).Methods("POST")
|
router.HandleFunc("/api/comment/new", commentNewHandler).Methods("POST")
|
||||||
|
router.HandleFunc("/api/comment/edit", commentEditHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/comment/list", commentListHandler).Methods("POST")
|
router.HandleFunc("/api/comment/list", commentListHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/comment/count", commentCountHandler).Methods("POST")
|
router.HandleFunc("/api/comment/count", commentCountHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/comment/vote", commentVoteHandler).Methods("POST")
|
router.HandleFunc("/api/comment/vote", commentVoteHandler).Methods("POST")
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
var autoInit;
|
var autoInit;
|
||||||
var isAuthenticated = false;
|
var isAuthenticated = false;
|
||||||
var comments = [];
|
var comments = [];
|
||||||
|
var commentsMap = {};
|
||||||
var commenters = {};
|
var commenters = {};
|
||||||
var requireIdentification = true;
|
var requireIdentification = true;
|
||||||
var isModerator = false;
|
var isModerator = false;
|
||||||
@ -76,6 +77,7 @@
|
|||||||
var isLocked = false;
|
var isLocked = false;
|
||||||
var stickyCommentHex = "none";
|
var stickyCommentHex = "none";
|
||||||
var shownReply = {};
|
var shownReply = {};
|
||||||
|
var shownEdit = {};
|
||||||
var configuredOauths = {};
|
var configuredOauths = {};
|
||||||
var popupBoxType = "login";
|
var popupBoxType = "login";
|
||||||
var oauthButtonsShown = false;
|
var oauthButtonsShown = false;
|
||||||
@ -499,7 +501,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function textareaCreate(id) {
|
function textareaCreate(id, edit) {
|
||||||
var textareaSuperContainer = create("div");
|
var textareaSuperContainer = create("div");
|
||||||
var textareaContainer = create("div");
|
var textareaContainer = create("div");
|
||||||
var textarea = create("textarea");
|
var textarea = create("textarea");
|
||||||
@ -529,11 +531,19 @@
|
|||||||
attrSet(anonymousCheckboxLabel, "for", ID_ANONYMOUS_CHECKBOX + id);
|
attrSet(anonymousCheckboxLabel, "for", ID_ANONYMOUS_CHECKBOX + id);
|
||||||
|
|
||||||
anonymousCheckboxLabel.innerText = "Comment anonymously";
|
anonymousCheckboxLabel.innerText = "Comment anonymously";
|
||||||
submitButton.innerText = "Add Comment";
|
if (edit === true) {
|
||||||
|
submitButton.innerText = "Save Changes";
|
||||||
|
} else {
|
||||||
|
submitButton.innerText = "Add Comment";
|
||||||
|
}
|
||||||
markdownButton.innerHTML = "<b>M ↓</b> Markdown";
|
markdownButton.innerHTML = "<b>M ↓</b> Markdown";
|
||||||
|
|
||||||
textarea.oninput = autoExpander(textarea);
|
textarea.oninput = autoExpander(textarea);
|
||||||
onclick(submitButton, submitAccountDecide, id);
|
if (edit === true) {
|
||||||
|
onclick(submitButton, commentEdit, id);
|
||||||
|
} else {
|
||||||
|
onclick(submitButton, submitAccountDecide, id);
|
||||||
|
}
|
||||||
onclick(markdownButton, markdownHelpShow, id);
|
onclick(markdownButton, markdownHelpShow, id);
|
||||||
|
|
||||||
append(textareaContainer, textarea);
|
append(textareaContainer, textarea);
|
||||||
@ -541,7 +551,7 @@
|
|||||||
append(anonymousCheckboxContainer, anonymousCheckbox);
|
append(anonymousCheckboxContainer, anonymousCheckbox);
|
||||||
append(anonymousCheckboxContainer, anonymousCheckboxLabel);
|
append(anonymousCheckboxContainer, anonymousCheckboxLabel);
|
||||||
append(textareaSuperContainer, submitButton);
|
append(textareaSuperContainer, submitButton);
|
||||||
if (!requireIdentification) {
|
if (!requireIdentification && edit !== true) {
|
||||||
append(textareaSuperContainer, anonymousCheckboxContainer);
|
append(textareaSuperContainer, anonymousCheckboxContainer);
|
||||||
}
|
}
|
||||||
append(textareaSuperContainer, markdownButton);
|
append(textareaSuperContainer, markdownButton);
|
||||||
@ -911,6 +921,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onclick(edit, global.editShow, comment.commentHex);
|
||||||
onclick(collapse, global.commentCollapse, comment.commentHex);
|
onclick(collapse, global.commentCollapse, comment.commentHex);
|
||||||
onclick(approve, global.commentApprove, comment.commentHex);
|
onclick(approve, global.commentApprove, comment.commentHex);
|
||||||
onclick(remove, global.commentDelete, comment.commentHex);
|
onclick(remove, global.commentDelete, comment.commentHex);
|
||||||
@ -933,11 +944,14 @@
|
|||||||
|
|
||||||
append(options, collapse);
|
append(options, collapse);
|
||||||
|
|
||||||
// append(options, edit); // uncomment when implemented
|
|
||||||
append(options, downvote);
|
append(options, downvote);
|
||||||
append(options, upvote);
|
append(options, upvote);
|
||||||
|
|
||||||
append(options, reply);
|
if (comment.commenterHex === selfHex) {
|
||||||
|
append(options, edit);
|
||||||
|
} else {
|
||||||
|
append(options, reply);
|
||||||
|
}
|
||||||
|
|
||||||
if (isModerator && parentHex === "root") {
|
if (isModerator && parentHex === "root") {
|
||||||
append(options, sticky);
|
append(options, sticky);
|
||||||
@ -1109,6 +1123,106 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function commentEdit(id) {
|
||||||
|
var textarea = $(ID_TEXTAREA + id);
|
||||||
|
|
||||||
|
var markdown = textarea.value;
|
||||||
|
|
||||||
|
if (markdown === "") {
|
||||||
|
classAdd(textarea, "red-border");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
classRemove(textarea, "red-border");
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = {
|
||||||
|
"commenterToken": commenterTokenGet(),
|
||||||
|
"commentHex": id,
|
||||||
|
"markdown": markdown,
|
||||||
|
};
|
||||||
|
|
||||||
|
post(origin + "/api/comment/edit", json, function(resp) {
|
||||||
|
if (!resp.success) {
|
||||||
|
errorShow(resp.message);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
errorHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
commentsMap[id].markdown = markdown;
|
||||||
|
commentsMap[id].html = resp.html;
|
||||||
|
|
||||||
|
var editButton = $(ID_EDIT + id);
|
||||||
|
var textarea = $(ID_SUPER_CONTAINER + id);
|
||||||
|
|
||||||
|
textarea.innerHTML = commentsMap[id].html;
|
||||||
|
textarea.id = ID_TEXT + id;
|
||||||
|
delete shownEdit[id];
|
||||||
|
|
||||||
|
classAdd(editButton, "option-edit");
|
||||||
|
classRemove(editButton, "option-cancel");
|
||||||
|
|
||||||
|
editButton.title = "Edit comment";
|
||||||
|
|
||||||
|
editButton = removeAllEventListeners(editButton);
|
||||||
|
onclick(editButton, global.editShow, id)
|
||||||
|
|
||||||
|
var message = "";
|
||||||
|
if (resp.state === "unapproved") {
|
||||||
|
message = "Your comment is under moderation.";
|
||||||
|
} else if (resp.state === "flagged") {
|
||||||
|
message = "Your comment was flagged as spam and is under moderation.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message !== "") {
|
||||||
|
prepend($(ID_SUPER_CONTAINER + id), messageCreate(message));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
global.editShow = function(id) {
|
||||||
|
if (id in shownEdit && shownEdit[id]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = $(ID_TEXT + id);
|
||||||
|
shownEdit[id] = true;
|
||||||
|
text.replaceWith(textareaCreate(id, true));
|
||||||
|
|
||||||
|
var textarea = $(ID_TEXTAREA + id);
|
||||||
|
textarea.innerText = commentsMap[id].markdown;
|
||||||
|
|
||||||
|
var editButton = $(ID_EDIT + id);
|
||||||
|
|
||||||
|
classRemove(editButton, "option-edit");
|
||||||
|
classAdd(editButton, "option-cancel");
|
||||||
|
|
||||||
|
editButton.title = "Cancel edit";
|
||||||
|
|
||||||
|
editButton = removeAllEventListeners(editButton);
|
||||||
|
onclick(editButton, global.editCollapse, id);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
global.editCollapse = function(id) {
|
||||||
|
var editButton = $(ID_EDIT + id);
|
||||||
|
var textarea = $(ID_SUPER_CONTAINER + id);
|
||||||
|
|
||||||
|
textarea.innerHTML = commentsMap[id].html;
|
||||||
|
textarea.id = ID_TEXT + id;
|
||||||
|
delete shownEdit[id];
|
||||||
|
|
||||||
|
classAdd(editButton, "option-edit");
|
||||||
|
classRemove(editButton, "option-cancel");
|
||||||
|
|
||||||
|
editButton.title = "Edit comment";
|
||||||
|
|
||||||
|
editButton = removeAllEventListeners(editButton);
|
||||||
|
onclick(editButton, global.editShow, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
global.replyShow = function(id) {
|
global.replyShow = function(id) {
|
||||||
if (id in shownReply && shownReply[id]) {
|
if (id in shownReply && shownReply[id]) {
|
||||||
return;
|
return;
|
||||||
@ -1135,7 +1249,7 @@
|
|||||||
var el = $(ID_SUPER_CONTAINER + id);
|
var el = $(ID_SUPER_CONTAINER + id);
|
||||||
|
|
||||||
el.remove();
|
el.remove();
|
||||||
shownReply[id] = false;
|
delete shownReply[id];
|
||||||
|
|
||||||
classAdd(replyButton, "option-reply");
|
classAdd(replyButton, "option-reply");
|
||||||
classRemove(replyButton, "option-cancel");
|
classRemove(replyButton, "option-cancel");
|
||||||
@ -1198,6 +1312,10 @@
|
|||||||
comment.creationDate = new Date(comment.creationDate);
|
comment.creationDate = new Date(comment.creationDate);
|
||||||
|
|
||||||
parentMap[parentHex].push(comment);
|
parentMap[parentHex].push(comment);
|
||||||
|
commentsMap[comment.commentHex] = {
|
||||||
|
"html": comment.html,
|
||||||
|
"markdown": comment.markdown,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
var cards = commentsRecurse(parentMap, "root");
|
var cards = commentsRecurse(parentMap, "root");
|
||||||
|
@ -67,6 +67,14 @@
|
|||||||
background: $indigo-6;
|
background: $indigo-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.commento-option-edit {
|
||||||
|
height: 14px;
|
||||||
|
width: 14px;
|
||||||
|
@include mask-image('data:image/svg+xml;utf8,<?xml version="1.0" encoding="UTF-8"?><svg enable-background="new 0 0 528.899 528.899" version="1.1" viewBox="0 0 528.899 528.899" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m328.88 89.125l107.59 107.59-272.34 272.34-107.53-107.59 272.28-272.34zm189.23-25.948l-47.981-47.981c-18.543-18.543-48.653-18.543-67.259 0l-45.961 45.961 107.59 107.59 53.611-53.611c14.382-14.383 14.382-37.577 0-51.959zm-517.81 449.51c-1.958 8.812 5.998 16.708 14.811 14.565l119.89-29.069-107.53-107.59-27.173 122.09z"/></svg>');
|
||||||
|
margin: 12px 6px 12px 6px;
|
||||||
|
background: $gray-5;
|
||||||
|
}
|
||||||
|
|
||||||
.commento-option-remove {
|
.commento-option-remove {
|
||||||
height: 14px;
|
height: 14px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
|
Loading…
Reference in New Issue
Block a user