Skip to content

Commit

Permalink
Improve --options (#4)
Browse files Browse the repository at this point in the history
* Disable configuration in System Console
* Pinned posts are not deleted by default
* Added flag `--delete-pinned-posts`
* Added flag `--confirm`
* Updated README.md
  • Loading branch information
nathanaelhoun authored Aug 9, 2020
1 parent 8b95049 commit 364aa15
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 61 deletions.
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ linters-settings:
gofmt:
simplify: true
goimports:
local-prefixes: github.com/nathanaelhoun/mattermost-plugin-postmanager
local-prefixes: github.com/nathanaelhoun/mattermost-plugin-clear
golint:
min-confidence: 0
govet:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
GO ?= $(shell command -v go 2> /dev/null)
NPM ?= $(shell command -v npm 2> /dev/null)
CURL ?= $(shell command -v curl 2> /dev/null)
DEBUG ?=1
DEBUG ?=
MANIFEST_FILE ?= plugin.json
GOPATH ?= $(shell go env GOPATH)
GO_TEST_FLAGS ?= -race
Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

[![CircleCI branch](https://img.shields.io/circleci/project/github/nathanaelhoun/mattermost-plugin-clear/master.svg)](https://circleci.com/gh/nathanaelhoun/mattermost-plugin-clear)

This [mattermost](https://mattermost.org) plugin allow to delete post with a command.
This [Mattermost](https://mattermost.org) plugin allow to delete posts with a /command.

![Plugin screenshot](./screenshot.png)

**Supported Mattermost Server Versions: 5.2+** (command autocomplete with Mattermost 5.24+)
**Supported Mattermost Server Versions: 5.12+** (command autocomplete with Mattermost 5.24+)

## Features

#### Manage posts with commands

`/clear [number-of-post]` Delete the last `[number-of-post]` posts in the current channel

Available options :
_incoming feature_
### Available options :
* `--delete-pinned-posts` Also delete pinned post (disabled by default)
* `--confirm` Do not show confirmation dialog

## Installation

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ module github.com/mattermost/mattermost-plugin-starter-template
go 1.12

require (
github.com/mattermost/mattermost-server/v5 v5.24.0-rc3
github.com/mattermost/mattermost-server/v5 v5.24.1
github.com/pkg/errors v0.9.1
)
6 changes: 3 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ github.com/Masterminds/glide v0.13.2/go.mod h1:STyF5vcenH/rUqTEv+/hBXlSTo7KYwg2o
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/squirrel v1.2.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA=
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PaulARoy/azurestoragecache v0.0.0-20170906084534-3c249a3ba788/go.mod h1:lY1dZd8HBzJ10eqKERHn3CU59tfhzcAVb2c0ZhIWSOk=
github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
Expand Down Expand Up @@ -279,8 +280,8 @@ github.com/mattermost/gorp v2.0.1-0.20190301154413-3b31e9a39d05+incompatible/go.
github.com/mattermost/gosaml2 v0.3.2/go.mod h1:Z429EIOiEi9kbq6yHoApfzlcXpa6dzRDc6pO+Vy2Ksk=
github.com/mattermost/ldap v0.0.0-20191128190019-9f62ba4b8d4d h1:2DV7VIlEv6J5R5o6tUcb3ZMKJYeeZuWZL7Rv1m23TgQ=
github.com/mattermost/ldap v0.0.0-20191128190019-9f62ba4b8d4d/go.mod h1:HLbgMEI5K131jpxGazJ97AxfPDt31osq36YS1oxFQPQ=
github.com/mattermost/mattermost-server/v5 v5.24.0-rc3 h1:+fQNocygVkOnh0LV+x97qWxKd/Ibydk8ju2PKx1PSXk=
github.com/mattermost/mattermost-server/v5 v5.24.0-rc3/go.mod h1:wmIfEohk+D0xbFFfJPNvvUYaxeqUNKQcPZejT73m9YM=
github.com/mattermost/mattermost-server/v5 v5.24.1 h1:dkEDqjLTtgqlQTc03kEi7N2IgBT9cZmzLs6XPylpMUo=
github.com/mattermost/mattermost-server/v5 v5.24.1/go.mod h1:TVkOfVyk4wGw8j5J2IX3PDCP5R7j20IEP4FAezDK8Wk=
github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0/go.mod h1:nV5bfVpT//+B1RPD2JvRnxbkLmJEYXmRaaVl15fsXjs=
github.com/mattermost/viper v1.0.4/go.mod h1:uc5hKG9lv4/KRwPOt2c1omOyirS/UnuA2TytiZQSFHM=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
Expand Down Expand Up @@ -312,7 +313,6 @@ github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mkraft/gziphandler v1.1.2-0.20200509175700-73dc64f3ad90/go.mod h1:gG8WEPb2aI5MHdmHv83au7bk3molRSZiAjdxYrEMJdQ=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
Expand Down
7 changes: 1 addition & 6 deletions plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "com.github.nathanaelhoun.plugin-clear",
"name": "Clear",
"description": "Delete multiple posts with a command.",
"description": "Delete multiple posts with a /command.",
"version": "0.2.0",
"min_server_version": "5.12.0",
"server": {
Expand All @@ -10,10 +10,5 @@
"darwin-amd64": "server/dist/plugin-darwin-amd64",
"windows-amd64": "server/dist/plugin-windows-amd64.exe"
}
},
"settings_schema": {
"header": "",
"footer": "* To report an issue, make a suggestion or a contribution, [check the repository](https://github.com/nathanaelhoun/mattermost-plugin-clear).",
"settings": []
}
}
Binary file modified screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
112 changes: 91 additions & 21 deletions server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,86 @@ import (
const (
commandTrigger = "clear"

commandHelpText = "**Delete posts with commands.**\n" +
"`/clear [number-of-post]` Delete the last `[number-of-post]` posts in the current channel" +
"" +
"Available options :" +
" _incoming feature_"
optionDeletePinnedPost = "delete-pinned-posts"
optionNoConfirmDialog = "confirm"

commandHelpText = "## Delete posts with /" + commandTrigger + "\n" +
"`/clear [number-of-post]` Delete the last `[number-of-post]` posts in the current channel\n" +
"\n" +
"### Available options :\n" +
" * `--" + optionDeletePinnedPost + "` Also delete pinned post (disabled by default)\n" +
" * `--" + optionNoConfirmDialog + "` Do not show confirmation dialog\n"
)

func (p *Plugin) getCommand() *model.Command {
return &model.Command{
Trigger: commandTrigger,
AutoComplete: true,
AutoCompleteDesc: "Delete posts",
AutoCompleteHint: "[--option / number-of-posts]",
AutoCompleteHint: "[number-of-posts]",
AutocompleteData: getAutocompleteData(),
}
}

func getAutocompleteData() *model.AutocompleteData {
command := model.NewAutocompleteData(commandTrigger, "[--option / number-of-posts]", "Delete posts in the current channel")
command := model.NewAutocompleteData(commandTrigger, "[number-of-posts]", "Delete posts in the current channel")

command.AddTextArgument("Delete the last [number-of-post] posts in this channel.", "[number-of-post]", "[0-9]+")
command.AddTextArgument("Delete the last [number-of-post] posts in this channel", "[number-of-post]", "[0-9]+")
command.AddNamedTextArgument(optionDeletePinnedPost, "Also delete pinned posts (disabled by default)", "true", "", false)
command.AddNamedTextArgument(optionNoConfirmDialog, "Do not show confirmation dialog", "true", "", false)

return command
}

func parseArguments(args *model.CommandArgs) ([]string, map[string]bool, string) {
parameters := []string{}
options := make(map[string]bool)

nextIsNamedTextArgumentValue := false
namedTextArgumentName := ""

for position, arg := range strings.Fields(args.Command) {
if position == 0 {
continue // skip '/commandTrigger'
}

if nextIsNamedTextArgumentValue {
// NamedTextArgument should only be "true" or "false" in this plugin
switch arg {
case "false":
delete(options, namedTextArgumentName)
case "true":
break
default:
return nil, nil, fmt.Sprintf("Invalid value for argument `--%s`, must be `true` or `false`.", namedTextArgumentName)
}

nextIsNamedTextArgumentValue = false
namedTextArgumentName = ""
continue
}

if strings.HasPrefix(arg, "--") {
optionName := arg[2:]
options[optionName] = true
nextIsNamedTextArgumentValue = true
namedTextArgumentName = optionName
continue
}

parameters = append(parameters, arg)
}

if nextIsNamedTextArgumentValue {
return nil, nil, fmt.Sprintf("Invalid value for argument `--%s`, must be `true` or `false`.", namedTextArgumentName)
}

return parameters, options, ""
}

func (p *Plugin) verifyCommandDelete(parameters []string, args *model.CommandArgs) (int, *model.AppError) {
if len(parameters) < 1 {
p.sendEphemeralPost(args, "Please precise the [number-of-post] you want to delete.")
p.sendEphemeralPost(args, "Please precise the [number-of-post] you want to delete")
return 0, nil
}

Expand All @@ -56,22 +108,22 @@ func (p *Plugin) verifyCommandDelete(parameters []string, args *model.CommandArg

currentChannel, appErr := p.API.GetChannel(args.ChannelId)
if appErr != nil {
// stop the command because if numPostToDelete > currentChannel.TotalMsgCount, the plugin crashes
p.sendEphemeralPost(args, "Error when deleting posts.")
p.sendEphemeralPost(args, "Error when deleting posts")
return 0, &model.AppError{
Message: "Unable to get channel statistics",
DetailedError: appErr.DetailedError,
}
}
if currentChannel.TotalMsgCount < numPostToDelete64 {
p.sendEphemeralPost(args, "Cannot delete more posts that there is in this channel.")
// stop the command because if numPostToDelete > currentChannel.TotalMsgCount, the plugin crashes
p.sendEphemeralPost(args, "Cannot delete more posts that there is in this channel")
return 0, nil
}

return int(numPostToDelete64), nil
}

func (p *Plugin) askConfirmCommandDelete(numPostToDelete int, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
func (p *Plugin) askConfirmCommandDelete(numPostToDelete int, args *model.CommandArgs, deletePinnedPosts bool) (*model.CommandResponse, *model.AppError) {
serverConfig := p.API.GetConfig()

dialog := &model.OpenDialogRequest{
Expand All @@ -83,6 +135,16 @@ func (p *Plugin) askConfirmCommandDelete(numPostToDelete int, args *model.Comman
SubmitLabel: "Confirm",
NotifyOnCancel: false,
State: strconv.Itoa(numPostToDelete),
Elements: []model.DialogElement{
{
Type: "bool",
Name: "deletePinnedPosts",
DisplayName: "Delete pinned posts ?",
HelpText: "Pinned posts are keept by default",
Default: strconv.FormatBool(deletePinnedPosts),
Optional: true,
},
},
},
}

Expand All @@ -97,19 +159,27 @@ func (p *Plugin) askConfirmCommandDelete(numPostToDelete int, args *model.Comman
}

func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
split := strings.Fields(args.Command)
parameters, options, argumentError := parseArguments(args)
if argumentError != "" {
p.sendEphemeralPost(args, argumentError)
return &model.CommandResponse{}, nil
}

if len(split) <= 1 {
if len(parameters) < 1 {
p.sendEphemeralPost(args, commandHelpText)
return &model.CommandResponse{}, nil
}

parameters := split[1:]

numPostToDelete, err := p.verifyCommandDelete(parameters, args)
if err != nil || numPostToDelete == 0 {
return &model.CommandResponse{}, err
numPostToDelete, appErr := p.verifyCommandDelete(parameters, args)
if appErr != nil || numPostToDelete == 0 {
return &model.CommandResponse{}, appErr
}

return p.askConfirmCommandDelete(numPostToDelete, args)
deletePinnedPost := options[optionDeletePinnedPost]

if options[optionNoConfirmDialog] {
appErr := p.deleteLastPostsInChannel(numPostToDelete, args.ChannelId, args.UserId, deletePinnedPost)
return &model.CommandResponse{}, appErr
}
return p.askConfirmCommandDelete(numPostToDelete, args, deletePinnedPost)
}
9 changes: 7 additions & 2 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,19 @@ func (p *Plugin) handleDeletion(w http.ResponseWriter, r *http.Request) {

numPostToDelete, err := strconv.Atoi(request.State)
if err != nil {
p.API.LogError("Failed to convert string to int. Bad request.", "err", err.Error())
p.API.LogError("Failed to convert string to int. Bad request", "err", err.Error())
w.WriteHeader(http.StatusBadRequest)
return
}

w.WriteHeader(http.StatusOK)

if err := p.deleteLastPosts(numPostToDelete, request.ChannelId, request.UserId); err != nil {
deletePinnedPost := false
if request.Submission["deletePinnedPosts"] == true {
deletePinnedPost = true
}

if err := p.deleteLastPostsInChannel(numPostToDelete, request.ChannelId, request.UserId, deletePinnedPost); err != nil {
p.API.LogError("Failed to delete posts", "err", err.Error())
return
}
Expand Down
5 changes: 0 additions & 5 deletions server/manifest.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 25 additions & 15 deletions server/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (p *Plugin) sendEphemeralPost(args *model.CommandArgs, message string) *mod
)
}

func (p *Plugin) deleteLastPosts(numPostToDelete int, channelID string, userID string) *model.AppError {
func (p *Plugin) deleteLastPostsInChannel(numPostToDelete int, channelID string, userID string, deletePinnedPosts bool) *model.AppError {
postList, err := p.API.GetPostsForChannel(channelID, 0, numPostToDelete)
if err != nil {
p.API.LogError(
Expand All @@ -39,26 +39,32 @@ func (p *Plugin) deleteLastPosts(numPostToDelete int, channelID string, userID s

isError := false
isErrorNotAdmin := false
isErrorPinnedPost := false
numDeletedPost := 0
hasAdminRights := hasAdminRights(p, userID)
for _, postID := range postList.Order {
if !hasAdminRights {
post, err := p.API.GetPost(postID)
if err != nil {
isError = true
p.API.LogError(
"Unable to get post "+postID+" informations.",
"err", err.Error(),
)
continue // process next post
}
post, err := p.API.GetPost(postID)
if err != nil {
isError = true
p.API.LogError(
"Unable to get post "+postID+" informations",
"err", err.Error(),
)
continue // process next post
}

if !hasAdminRights {
if post.UserId != userID {
isErrorNotAdmin = true
continue // process next post
}
}

if post.IsPinned && !deletePinnedPosts {
isErrorPinnedPost = true
continue // process next post
}

if err := p.API.DeletePost(postID); err != nil {
isError = true
p.API.LogError(
Expand All @@ -74,23 +80,27 @@ func (p *Plugin) deleteLastPosts(numPostToDelete int, channelID string, userID s
strResponse := ""

if isError {
strResponse += "An error has occurred, some post could not be deleted.\n"
strResponse += "An error has occurred, some post could not be deleted\n"
}

if isErrorNotAdmin {
strResponse += "Some posts have not been deleted because they were not yours.\n"
strResponse += "Some posts have not been deleted because they were not yours\n"
}

if isErrorPinnedPost {
strResponse += "Some posts have not been deleted because they were pinned in the channel\n"
}

if numDeletedPost > 0 {
plural := ""
if numDeletedPost > 1 {
plural = "s"
}
strResponse += fmt.Sprintf("Successfully deleted %d post%s.", numDeletedPost, plural)
strResponse += fmt.Sprintf("Successfully deleted %d post%s", numDeletedPost, plural)
}

if strResponse == "" {
strResponse = "There are no posts in this channel."
strResponse = "There are no posts in this channel"
}

p.sendEphemeralPost(&model.CommandArgs{
Expand Down

0 comments on commit 364aa15

Please sign in to comment.