snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
This commit is contained in:
270
server/public/pluginapi/experimental/flow/step.go
Normal file
270
server/public/pluginapi/experimental/flow/step.go
Normal file
@@ -0,0 +1,270 @@
|
||||
package flow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
)
|
||||
|
||||
type Color string
|
||||
|
||||
const (
|
||||
ColorDefault Color = "default"
|
||||
ColorPrimary Color = "primary"
|
||||
ColorSuccess Color = "success"
|
||||
ColorGood Color = "good"
|
||||
ColorWarning Color = "warning"
|
||||
ColorDanger Color = "danger"
|
||||
)
|
||||
|
||||
type Step struct {
|
||||
name Name
|
||||
template *model.MessageAttachment
|
||||
forwardTo Name
|
||||
autoForward bool
|
||||
terminal bool
|
||||
onRender func(f *Flow)
|
||||
buttons []Button
|
||||
}
|
||||
|
||||
type Button struct {
|
||||
Name string
|
||||
Disabled bool
|
||||
Color Color
|
||||
|
||||
// OnClick is called when the button is clicked. It returns the next step's
|
||||
// name and the state updates to apply.
|
||||
//
|
||||
// If Dialog is also specified, OnClick is executed first.
|
||||
OnClick func(f *Flow) (Name, State, error)
|
||||
|
||||
// Dialog is the interactive dialog to display if the button is clicked
|
||||
// (OnClick is executed first). OnDialogSubmit must be provided.
|
||||
Dialog *model.Dialog
|
||||
|
||||
// Function that is called when the dialog box is submitted. It can return a
|
||||
// general error, or field-specific errors. On success it returns the name
|
||||
// of the next step, and the state updates to apply.
|
||||
OnDialogSubmit func(f *Flow, submitted map[string]any) (Name, State, map[string]string, error)
|
||||
}
|
||||
|
||||
func NewStep(name Name) Step {
|
||||
return Step{
|
||||
name: name,
|
||||
template: &model.MessageAttachment{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s Step) WithButton(buttons ...Button) Step {
|
||||
s.buttons = append(s.buttons, buttons...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) Terminal() Step {
|
||||
s.terminal = true
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) OnRender(f func(*Flow)) Step {
|
||||
s.onRender = f
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) Next(name Name) Step {
|
||||
s.forwardTo = name
|
||||
s.autoForward = true
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) WithImage(imageURL string) Step {
|
||||
if u, err := url.Parse(imageURL); err == nil {
|
||||
if u.Host != "" && (u.Scheme == "http" || u.Scheme == "https") {
|
||||
s.template.ImageURL = imageURL
|
||||
} else {
|
||||
s.template.ImageURL = u.Path
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) WithColor(color Color) Step {
|
||||
s.template.Color = string(color)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) WithPretext(text string) Step {
|
||||
s.template.Pretext = text
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) WithField(title, value string) Step {
|
||||
s.template.Fields = append(s.template.Fields, &model.MessageAttachmentField{
|
||||
Title: title,
|
||||
Value: value,
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) WithTitle(text string) Step {
|
||||
s.template.Title = text
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) WithText(text string) Step {
|
||||
s.template.Text = text
|
||||
return s
|
||||
}
|
||||
|
||||
func (s Step) do(f *Flow) (*model.Post, bool, error) {
|
||||
if s.onRender != nil {
|
||||
s.onRender(f)
|
||||
}
|
||||
|
||||
return s.render(f, false, 0)
|
||||
}
|
||||
|
||||
func (s Step) done(f *Flow, selectedButton int) (*model.Post, error) {
|
||||
post, _, err := s.render(f, true, selectedButton)
|
||||
return post, err
|
||||
}
|
||||
|
||||
func (s Step) render(f *Flow, done bool, selectedButton int) (*model.Post, bool, error) {
|
||||
sa := f.processAttachment(s.template)
|
||||
post := model.Post{}
|
||||
model.ParseMessageAttachment(&post, []*model.MessageAttachment{sa})
|
||||
|
||||
if s.terminal {
|
||||
// Nothing else to do, do not display buttons on terminal posts.
|
||||
return &post, true, nil
|
||||
}
|
||||
|
||||
buttons := processButtons(s.buttons, f.state.AppState)
|
||||
|
||||
attachments, ok := post.GetProp(model.PostPropsAttachments).([]*model.MessageAttachment)
|
||||
if !ok || len(attachments) != 1 {
|
||||
return nil, false, errors.New("expected 1 message attachment")
|
||||
}
|
||||
var actions []*model.PostAction
|
||||
if done {
|
||||
if selectedButton > 0 {
|
||||
action := renderButton(buttons[selectedButton-1], s.name, selectedButton, f.state.AppState)
|
||||
action.Disabled = true
|
||||
actions = append(actions, action)
|
||||
}
|
||||
} else {
|
||||
for i, b := range buttons {
|
||||
actions = append(actions, renderButton(b, s.name, i+1, f.state.AppState))
|
||||
}
|
||||
}
|
||||
attachments[0].Actions = actions
|
||||
return &post, false, nil
|
||||
}
|
||||
|
||||
func (f *Flow) processAttachment(attachment *model.MessageAttachment) *model.MessageAttachment {
|
||||
if attachment == nil {
|
||||
return &model.MessageAttachment{Text: "ERROR"}
|
||||
}
|
||||
a := *attachment
|
||||
a.Pretext = formatState(attachment.Pretext, f.state.AppState)
|
||||
a.Title = formatState(attachment.Title, f.state.AppState)
|
||||
a.Text = formatState(attachment.Text, f.state.AppState)
|
||||
|
||||
for _, field := range a.Fields {
|
||||
field.Title = formatState(field.Title, f.state.AppState)
|
||||
v := field.Value.(string)
|
||||
if v != "" {
|
||||
field.Value = formatState(v, f.state.AppState)
|
||||
}
|
||||
}
|
||||
|
||||
a.Fallback = fmt.Sprintf("%s: %s", a.Title, a.Text)
|
||||
|
||||
if attachment.ImageURL != "" {
|
||||
if u, err := url.Parse(attachment.ImageURL); err == nil {
|
||||
if u.Host != "" && (u.Scheme == "http" || u.Scheme == "https") {
|
||||
a.ImageURL = attachment.ImageURL
|
||||
} else {
|
||||
a.ImageURL = fmt.Sprintf("%s/plugins/%s/%s", f.siteURL, f.pluginID, strings.TrimPrefix(attachment.ImageURL, "/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &a
|
||||
}
|
||||
|
||||
func processButtons(in []Button, state State) []Button {
|
||||
var out []Button
|
||||
for _, b := range in {
|
||||
button := b
|
||||
button.Name = formatState(b.Name, state)
|
||||
out = append(out, button)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func processDialog(in *model.Dialog, state State) model.Dialog {
|
||||
d := *in
|
||||
d.Title = formatState(d.Title, state)
|
||||
d.IntroductionText = formatState(d.IntroductionText, state)
|
||||
d.SubmitLabel = formatState(d.SubmitLabel, state)
|
||||
for i := range d.Elements {
|
||||
d.Elements[i].DisplayName = formatState(d.Elements[i].DisplayName, state)
|
||||
d.Elements[i].Name = formatState(d.Elements[i].Name, state)
|
||||
d.Elements[i].Default = formatState(d.Elements[i].Default, state)
|
||||
d.Elements[i].Placeholder = formatState(d.Elements[i].Placeholder, state)
|
||||
d.Elements[i].HelpText = formatState(d.Elements[i].HelpText, state)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func renderButton(b Button, stepName Name, i int, state State) *model.PostAction {
|
||||
return &model.PostAction{
|
||||
Type: model.PostActionTypeButton,
|
||||
Name: formatState(b.Name, state),
|
||||
Disabled: b.Disabled,
|
||||
Style: string(b.Color),
|
||||
Integration: &model.PostActionIntegration{
|
||||
Context: map[string]any{
|
||||
contextStepKey: string(stepName),
|
||||
contextButtonKey: strconv.Itoa(i),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buttonContext(request *model.PostActionIntegrationRequest) (Name, int, error) {
|
||||
fromString, ok := request.Context[contextStepKey].(string)
|
||||
if !ok {
|
||||
return "", 0, errors.New("missing step name")
|
||||
}
|
||||
fromName := Name(fromString)
|
||||
|
||||
buttonStr, ok := request.Context[contextButtonKey].(string)
|
||||
if !ok {
|
||||
return "", 0, errors.New("missing button id")
|
||||
}
|
||||
buttonIndex, err := strconv.Atoi(buttonStr)
|
||||
if err != nil {
|
||||
return "", 0, errors.Wrap(err, "invalid button number")
|
||||
}
|
||||
|
||||
return fromName, buttonIndex, nil
|
||||
}
|
||||
|
||||
func dialogContext(request *model.SubmitDialogRequest) (Name, int, error) {
|
||||
data := strings.Split(request.State, ",")
|
||||
if len(data) != 2 {
|
||||
return "", 0, errors.New("invalid request")
|
||||
}
|
||||
fromName := Name(data[0])
|
||||
buttonIndex, err := strconv.Atoi(data[1])
|
||||
if err != nil {
|
||||
return "", 0, errors.Wrap(err, "malformed button number")
|
||||
}
|
||||
return fromName, buttonIndex, nil
|
||||
}
|
||||
Reference in New Issue
Block a user