add orig to comment and edit request #13
This commit is contained in:
+11
-6
@@ -148,17 +148,18 @@ func (s *Rest) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
rest.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info")
|
||||
return
|
||||
}
|
||||
log.Printf("[DEBUG] create comment %+v", comment)
|
||||
|
||||
comment.PrepareUntrusted() // clean all fields user not supposed to set
|
||||
comment.User = user
|
||||
comment.User.IP = strings.Split(r.RemoteAddr, ":")[0]
|
||||
|
||||
comment.Orig = comment.Text // original comment text, prior to md render
|
||||
if err = s.DataService.ValidateComment(&comment); err != nil {
|
||||
rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "invalid comment")
|
||||
return
|
||||
}
|
||||
|
||||
comment.Text = string(blackfriday.Run([]byte(comment.Text), blackfriday.WithExtensions(mdExt)))
|
||||
log.Printf("[DEBUG] create comment %+v", comment)
|
||||
|
||||
// check if user blocked
|
||||
if s.adminService.checkBlocked(comment.Locator.SiteID, comment.User) {
|
||||
@@ -190,6 +191,7 @@ func (s *Rest) previewCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
comment.User = user
|
||||
comment.Orig = comment.Text
|
||||
if err = s.DataService.ValidateComment(&comment); err != nil {
|
||||
rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "invalid comment")
|
||||
return
|
||||
@@ -223,9 +225,6 @@ func (s *Rest) updateCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
|
||||
id := chi.URLParam(r, "id")
|
||||
|
||||
// render markdown
|
||||
edit.Text = string(blackfriday.Run([]byte(edit.Text), blackfriday.WithNoExtensions()))
|
||||
|
||||
log.Printf("[DEBUG] update comment %s, %+v", id, edit)
|
||||
|
||||
var currComment store.Comment
|
||||
@@ -239,7 +238,13 @@ func (s *Rest) updateCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
res, err := s.DataService.EditComment(locator, id, edit.Text, store.Edit{Summary: edit.Summary})
|
||||
editReq := store.EditRequest{
|
||||
Text: string(blackfriday.Run([]byte(edit.Text), blackfriday.WithExtensions(mdExt))), // render markdown
|
||||
Orig: edit.Text,
|
||||
Summary: edit.Summary,
|
||||
}
|
||||
|
||||
res, err := s.DataService.EditComment(locator, id, editReq)
|
||||
if err != nil {
|
||||
rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't update comment")
|
||||
return
|
||||
|
||||
@@ -128,8 +128,8 @@ func TestServer_CreateAndGet(t *testing.T) {
|
||||
// create comment
|
||||
r := strings.NewReader(`{"text": "**test** *123* http://radio-t.com", "locator":{"url": "https://radio-t.com/blah1", "site": "radio-t"}}`)
|
||||
resp, err := http.Post(fmt.Sprintf("http://dev:password@127.0.0.1:%d/api/v1/comment", port), "application/json", r)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, http.StatusCreated, resp.StatusCode)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, http.StatusCreated, resp.StatusCode)
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
assert.Nil(t, err)
|
||||
c := JSON{}
|
||||
@@ -145,6 +145,7 @@ func TestServer_CreateAndGet(t *testing.T) {
|
||||
err = json.Unmarshal([]byte(res), &comment)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, `<p><strong>test</strong> <em>123</em> <a href="http://radio-t.com" rel="nofollow">http://radio-t.com</a></p>`+"\n", comment.Text)
|
||||
assert.Equal(t, "**test** *123* http://radio-t.com", comment.Orig)
|
||||
assert.Equal(t, store.User{Name: "developer one", ID: "dev",
|
||||
Picture: "/api/v1/avatar/remark.image", Admin: true, Blocked: false, IP: "ea64bfc178468d943ca5b836e2e700c335404973"},
|
||||
comment.User)
|
||||
@@ -214,6 +215,7 @@ func TestServer_Update(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, id, c2.ID)
|
||||
assert.Equal(t, "<p>updated text</p>\n", c2.Text)
|
||||
assert.Equal(t, "updated text", c2.Orig)
|
||||
assert.Equal(t, "my edit", c2.Edit.Summary)
|
||||
assert.True(t, time.Since(c2.Edit.Timestamp) < 1*time.Second)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ type Comment struct {
|
||||
ID string `json:"id"`
|
||||
ParentID string `json:"pid"`
|
||||
Text string `json:"text"`
|
||||
Orig string `json:"orig,omitempty"`
|
||||
User User `json:"user"`
|
||||
Locator Locator `json:"locator"`
|
||||
Score int `json:"score"`
|
||||
@@ -64,7 +65,7 @@ type BlockedUser struct {
|
||||
Timestamp time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// PrepareUntrusted preprocess comment received from untrusted source by clearing all
|
||||
// PrepareUntrusted pre-processes a comment received from untrusted source by clearing all
|
||||
// autogen fields and reset everything users not supposed to provide
|
||||
func (c *Comment) PrepareUntrusted() {
|
||||
c.ID = "" // don't allow user to define ID, force auto-gen
|
||||
@@ -79,6 +80,7 @@ func (c *Comment) PrepareUntrusted() {
|
||||
// SetDeleted clears comment info, reset to deleted state
|
||||
func (c *Comment) SetDeleted() {
|
||||
c.Text = ""
|
||||
c.Orig = ""
|
||||
c.Score = 0
|
||||
c.Votes = map[string]bool{}
|
||||
c.Edit = nil
|
||||
@@ -90,15 +92,13 @@ func (c *Comment) Sanitize() {
|
||||
p := bluemonday.UGCPolicy()
|
||||
p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code")
|
||||
c.Text = p.Sanitize(c.Text)
|
||||
c.Orig = p.Sanitize(c.Orig)
|
||||
c.User.ID = template.HTMLEscapeString(c.User.ID)
|
||||
c.User.Name = template.HTMLEscapeString(c.User.Name)
|
||||
c.User.Picture = p.Sanitize(c.User.Picture)
|
||||
|
||||
// c.Text = strings.Replace(c.Text, "\n", "", -1)
|
||||
// c.Text = strings.Replace(c.Text, "\t", "", -1)
|
||||
}
|
||||
|
||||
// hashIP replace sensitive fields with hmac
|
||||
// hashIP replace IP field with hashed hmac
|
||||
func (u *User) hashIP(secret string) {
|
||||
|
||||
hashVal := func(val string) string {
|
||||
@@ -117,7 +117,7 @@ func (u *User) hashIP(secret string) {
|
||||
}
|
||||
|
||||
// EncodeID hashes id to sha1. The function intentionally left outside of User struct because in some cases
|
||||
// we need hashing for parts of id, in some others hasing for non-User values.
|
||||
// we need hashing for parts of id, in some others hashing for non-User values.
|
||||
func EncodeID(id string) string {
|
||||
h := sha1.New()
|
||||
if _, err := h.Write([]byte(id)); err != nil {
|
||||
|
||||
+16
-7
@@ -88,8 +88,14 @@ func (s *Service) Vote(locator Locator, commentID string, userID string, val boo
|
||||
return comment, s.Put(locator, comment)
|
||||
}
|
||||
|
||||
type EditRequest struct {
|
||||
Text string
|
||||
Orig string
|
||||
Summary string
|
||||
}
|
||||
|
||||
// EditComment to edit text and update Edit info
|
||||
func (s *Service) EditComment(locator Locator, commentID string, text string, edit Edit) (comment Comment, err error) {
|
||||
func (s *Service) EditComment(locator Locator, commentID string, req EditRequest) (comment Comment, err error) {
|
||||
comment, err = s.Get(locator, commentID)
|
||||
if err != nil {
|
||||
return comment, err
|
||||
@@ -104,9 +110,12 @@ func (s *Service) EditComment(locator Locator, commentID string, text string, ed
|
||||
return comment, errors.Errorf("too late to edit %s", commentID)
|
||||
}
|
||||
|
||||
comment.Text = text
|
||||
comment.Edit = &edit
|
||||
comment.Edit.Timestamp = time.Now()
|
||||
comment.Text = req.Text
|
||||
comment.Orig = req.Orig
|
||||
comment.Edit = &Edit{
|
||||
Timestamp: time.Now(),
|
||||
Summary: req.Summary,
|
||||
}
|
||||
|
||||
comment.Sanitize()
|
||||
err = s.Put(locator, comment)
|
||||
@@ -130,11 +139,11 @@ func (s *Service) ValidateComment(c *Comment) error {
|
||||
if s.MaxCommentSize <= 0 {
|
||||
maxSize = defaultCommentMaxSize
|
||||
}
|
||||
if c.Text == "" {
|
||||
if c.Orig == "" {
|
||||
return errors.New("empty comment text")
|
||||
}
|
||||
if len([]rune(c.Text)) > maxSize {
|
||||
return errors.Errorf("comment text exceeded max allowed size %d (%d)", maxSize, len([]rune(c.Text)))
|
||||
if len([]rune(c.Orig)) > maxSize {
|
||||
return errors.Errorf("comment text exceeded max allowed size %d (%d)", maxSize, len([]rune(c.Orig)))
|
||||
}
|
||||
if c.User.ID == "" || c.User.Name == "" {
|
||||
return errors.Errorf("empty user info")
|
||||
|
||||
@@ -139,17 +139,20 @@ func TestService_EditComment(t *testing.T) {
|
||||
assert.Equal(t, 2, len(res))
|
||||
assert.Nil(t, res[0].Edit)
|
||||
|
||||
comment, err := b.EditComment(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "xxx", Edit{Summary: "my edit"})
|
||||
comment, err := b.EditComment(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID,
|
||||
EditRequest{Orig: "yyy", Text: "xxx", Summary: "my edit"})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "my edit", comment.Edit.Summary)
|
||||
assert.Equal(t, "xxx", comment.Text)
|
||||
assert.Equal(t, "yyy", comment.Orig)
|
||||
|
||||
c, err := b.Get(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "my edit", c.Edit.Summary)
|
||||
assert.Equal(t, "xxx", c.Text)
|
||||
|
||||
_, err = b.EditComment(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "xxx", Edit{Summary: "my edit"})
|
||||
_, err = b.EditComment(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID,
|
||||
EditRequest{Orig: "yyy", Text: "xxx", Summary: "my edit"})
|
||||
assert.NotNil(t, err, "allow edit once")
|
||||
}
|
||||
|
||||
@@ -178,8 +181,8 @@ func TestService_EditCommentDurationFailed(t *testing.T) {
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
_, err = b.EditComment(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "xxx",
|
||||
Edit{Summary: "my edit"})
|
||||
_, err = b.EditComment(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID,
|
||||
EditRequest{Orig: "yyy", Text: "xxx", Summary: "my edit"})
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
@@ -193,9 +196,9 @@ func TestService_ValidateComment(t *testing.T) {
|
||||
err error
|
||||
}{
|
||||
{inp: Comment{}, err: errors.New("empty comment text")},
|
||||
{inp: Comment{Text: "something blah", User: User{ID: "myid", Name: "name"}}, err: nil},
|
||||
{inp: Comment{Text: "something blah", User: User{ID: "myid"}}, err: errors.New("empty user info")},
|
||||
{inp: Comment{Text: longText, User: User{ID: "myid", Name: "name"}}, err: errors.New("comment text exceeded max allowed size 2000 (4000)")},
|
||||
{inp: Comment{Orig: "something blah", User: User{ID: "myid", Name: "name"}}, err: nil},
|
||||
{inp: Comment{Orig: "something blah", User: User{ID: "myid"}}, err: errors.New("empty user info")},
|
||||
{inp: Comment{Orig: longText, User: User{ID: "myid", Name: "name"}}, err: errors.New("comment text exceeded max allowed size 2000 (4000)")},
|
||||
}
|
||||
|
||||
for n, tt := range tbl {
|
||||
|
||||
Reference in New Issue
Block a user