github.com/decred/politeia@v1.4.0/politeiawww/cmd/pictl/cmdcommentedit.go (about)

     1  // Copyright (c) 2021 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"encoding/hex"
     9  	"encoding/json"
    10  	"strconv"
    11  
    12  	cmv1 "github.com/decred/politeia/politeiawww/api/comments/v1"
    13  	piv1 "github.com/decred/politeia/politeiawww/api/pi/v1"
    14  	pclient "github.com/decred/politeia/politeiawww/client"
    15  	"github.com/decred/politeia/politeiawww/cmd/shared"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // cmdCommentEdit edits a new comment.
    20  type cmdCommentEdit struct {
    21  	Args struct {
    22  		Token     string `positional-arg-name:"token" required:"true"`
    23  		CommentID uint32 `positional-arg-name:"commentid" required:"true"`
    24  		Comment   string `positional-arg-name:"comment" required:"true"`
    25  		ParentID  uint32 `positional-arg-name:"parentid"`
    26  	} `positional-args:"true"`
    27  
    28  	// Unvetted is used to comment on an unvetted record. If this flag
    29  	// is not used the command assumes the record is vetted.
    30  	Unvetted bool `long:"unvetted" optional:"true"`
    31  
    32  	// UpdateTitle is used to post a new author update.
    33  	UpdateTitle string `long:"updatetitle" optional:"true"`
    34  }
    35  
    36  // Execute executes the cmdCommentEdit command.
    37  //
    38  // This function satisfies the go-flags Commander interface.
    39  func (c *cmdCommentEdit) Execute(args []string) error {
    40  	// Unpack args
    41  	var (
    42  		token     = c.Args.Token
    43  		commentID = c.Args.CommentID
    44  		comment   = c.Args.Comment
    45  		parentID  = c.Args.ParentID
    46  	)
    47  
    48  	// Check for user identity. A user identity is required to sign
    49  	// the comment.
    50  	if cfg.Identity == nil {
    51  		return shared.ErrUserIdentityNotFound
    52  	}
    53  
    54  	// Setup client
    55  	opts := pclient.Opts{
    56  		HTTPSCert:  cfg.HTTPSCert,
    57  		Cookies:    cfg.Cookies,
    58  		HeaderCSRF: cfg.CSRF,
    59  		Verbose:    cfg.Verbose,
    60  		RawJSON:    cfg.RawJSON,
    61  	}
    62  	pc, err := pclient.New(cfg.Host, opts)
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	// Setup state
    68  	var state cmv1.RecordStateT
    69  	switch {
    70  	case c.Unvetted:
    71  		state = cmv1.RecordStateUnvetted
    72  	default:
    73  		state = cmv1.RecordStateVetted
    74  	}
    75  
    76  	// Prepare extra data if it's a new author update
    77  	var (
    78  		extraData,
    79  		extraDataHint string
    80  	)
    81  	if c.UpdateTitle != "" {
    82  		extraDataHint = piv1.ProposalUpdateHint
    83  		pum := piv1.ProposalUpdateMetadata{
    84  			Title: c.UpdateTitle,
    85  		}
    86  		b, err := json.Marshal(pum)
    87  		if err != nil {
    88  			return err
    89  		}
    90  		extraData = string(b)
    91  	}
    92  
    93  	// Get user ID of logged in user
    94  	lr, err := client.Me()
    95  	if err != nil {
    96  		if err.Error() == "401" {
    97  			return errors.Errorf("no logged in user found")
    98  		}
    99  		return err
   100  	}
   101  	userID := lr.UserID
   102  
   103  	// Setup request
   104  	msg := strconv.FormatUint(uint64(state), 10) + token +
   105  		strconv.FormatUint(uint64(parentID), 10) +
   106  		strconv.FormatUint(uint64(commentID), 10) +
   107  		comment + extraData + extraDataHint
   108  	sig := cfg.Identity.SignMessage([]byte(msg))
   109  	e := cmv1.Edit{
   110  		UserID:        userID,
   111  		State:         state,
   112  		Token:         token,
   113  		ParentID:      parentID,
   114  		CommentID:     commentID,
   115  		Comment:       comment,
   116  		Signature:     hex.EncodeToString(sig[:]),
   117  		PublicKey:     cfg.Identity.Public.String(),
   118  		ExtraDataHint: extraDataHint,
   119  		ExtraData:     extraData,
   120  	}
   121  
   122  	// Send request
   123  	er, err := pc.CommentEdit(e)
   124  	if err != nil {
   125  		return err
   126  	}
   127  
   128  	// Verify receipt
   129  	vr, err := client.Version()
   130  	if err != nil {
   131  		return err
   132  	}
   133  	err = pclient.CommentEditVerify(er.Comment, vr.PubKey)
   134  	if err != nil {
   135  		return err
   136  	}
   137  
   138  	// Print receipt
   139  	printComment(er.Comment)
   140  
   141  	return nil
   142  }
   143  
   144  // commentEditHelpMsg is printed to stdout by the help command.
   145  const commentEditHelpMsg = `commentedit "token" commentid "comment" parentid
   146  
   147  Edit a comment. Requires the user to be logged in.
   148  
   149  This command assumes the record is a vetted record.
   150  
   151  If the record is unvetted, the --unvetted flag must be used. Commenting on
   152  unvetted records requires admin priviledges.
   153  
   154  Proposal's author may edit an author update using the --updatetitle flag.
   155  
   156  Arguments:
   157  1. token     (string, required)  Proposal censorship token.
   158  2. commentid (string, required)  Comment ID. 
   159  3. comment   (string, required)  Comment text.
   160  4. parentid  (uint32, optional)  ID of parent commment. Including a parent ID
   161                                   indicates that the comment is a reply.
   162  
   163  Flags:
   164    --unvetted    (bool, optional)   Record is unvetted.
   165    --updatetitle (string, optional) Authour update title.
   166  `