Files
team/server/public/model/product_notices.go
zeekayandHanzo Dev 05e7bfa24b call the product Hanzo Team, and paint it in one ink
Renames the product across user-facing text -- 1475 lines, i18n and the
defaultMessage strings that must match it -- while leaving the names that
are code rather than prose: upstream copyright, Go import paths, the
@mattermost npm scope, i18n keys, Go template fields, and legacy theme
identifiers like AuthorType.Mattermost and the 'Mattermost' key that maps
an old theme to a new one. The rule is a word boundary on identifier
characters; MattermostEntry is a symbol, "Mattermost" in a sentence is
the product.

The theme goes monochrome. Hanzo and Hanzo Dark are the two built-ins and
Hanzo is the default, with the CSS custom properties that carry brand
colour outside the theme object -- sidebar, buttons, links, titles --
moved to black. Status colours stay: online, away, do-not-disturb and
error encode state, not brand, and flattening them to grey would cost
meaning to buy consistency.

The sign-in header drops the TEAM EDITION badge. It advertised which
licence tier you were on, and there are no tiers here. Booting without a
licence is now logged at debug rather than warned about, because it is
this server's normal condition rather than a fault.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 22:28:09 -07:00

225 lines
8.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"encoding/json"
"io"
"github.com/pkg/errors"
)
type ProductNotices []ProductNotice
func (r *ProductNotices) Marshal() ([]byte, error) {
return json.Marshal(r)
}
func UnmarshalProductNotices(data []byte) (ProductNotices, error) {
var r ProductNotices
err := json.Unmarshal(data, &r)
return r, err
}
// List of product notices. Order is important and is used to resolve priorities.
// Each notice will only be show if conditions are met.
type ProductNotice struct {
Conditions Conditions `json:"conditions"`
ID string `json:"id"` // Unique identifier for this notice. Can be a running number. Used for storing 'viewed'; state on the server.
LocalizedMessages map[string]NoticeMessageInternal `json:"localizedMessages"` // Notice message data, organized by locale.; Example:; "localizedMessages": {; "en": { "title": "English", description: "English description"},; "frFR": { "title": "Frances", description: "French description"}; }
Repeatable *bool `json:"repeatable,omitempty"` // Configurable flag if the notice should reappear after it’s seen and dismissed
}
func (n *ProductNotice) SysAdminOnly() bool {
return n.Conditions.Audience != nil && *n.Conditions.Audience == NoticeAudienceSysadmin
}
func (n *ProductNotice) TeamAdminOnly() bool {
return n.Conditions.Audience != nil && *n.Conditions.Audience == NoticeAudienceTeamAdmin
}
type Conditions struct {
Audience *NoticeAudience `json:"audience,omitempty"`
ClientType *NoticeClientType `json:"clientType,omitempty"` // Only show the notice on specific clients. Defaults to 'all'
DesktopVersion []string `json:"desktopVersion,omitempty"` // What desktop client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
DisplayDate *string `json:"displayDate,omitempty"` // When to display the notice.; Examples:; "2020-03-01T00:00:00Z" - show on specified date; ">= 2020-03-01T00:00:00Z" - show after specified date; "< 2020-03-01T00:00:00Z" - show before the specified date; "> 2020-03-01T00:00:00Z <= 2020-04-01T00:00:00Z" - show only between the specified dates
InstanceType *NoticeInstanceType `json:"instanceType,omitempty"`
MobileVersion []string `json:"mobileVersion,omitempty"` // What mobile client versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
NumberOfPosts *int64 `json:"numberOfPosts,omitempty"` // Only show the notice when server has more than specified number of posts
NumberOfUsers *int64 `json:"numberOfUsers,omitempty"` // Only show the notice when server has more than specified number of users
ServerConfig map[string]any `json:"serverConfig,omitempty"` // Map of mattermost server config paths and their values. Notice will be displayed only if; the values match the target server config; Example: serverConfig: { "PluginSettings.Enable": true, "GuestAccountsSettings.Enable":; false }
ServerVersion []string `json:"serverVersion,omitempty"` // What server versions does this notice apply to.; Format: semver ranges (https://devhints.io/semver); Example: [">=1.2.3 < ~2.4.x"]; Example: ["<v5.19", "v5.20-v5.22"]
Sku *NoticeSKU `json:"sku,omitempty"`
UserConfig map[string]any `json:"userConfig,omitempty"` // Map of user's settings and their values. Notice will be displayed only if the values; match the viewing users' config; Example: userConfig: { "new_sidebar.disabled": true }
DeprecatingDependency *ExternalDependency `json:"deprecating_dependency,omitempty"` // External dependency which is going to be deprecated
}
type NoticeMessageInternal struct {
Action *NoticeAction `json:"action,omitempty"` // Optional action to perform on action button click. (defaults to closing the notice)
ActionParam *string `json:"actionParam,omitempty"` // Optional action parameter.; Example: {"action": "url", actionParam: "/console/some-page"}
ActionText *string `json:"actionText,omitempty"` // Optional override for the action button text (defaults to OK)
Description string `json:"description"` // Notice content. Use {{Hanzo Team}} instead of plain text to support white-labeling. Text; supports Markdown.
Image *string `json:"image,omitempty"`
Title string `json:"title"` // Notice title. Use {{Hanzo Team}} instead of plain text to support white-labeling. Text; supports Markdown.
}
type NoticeMessages []NoticeMessage
type NoticeMessage struct {
NoticeMessageInternal
ID string `json:"id"`
SysAdminOnly bool `json:"sysAdminOnly"`
TeamAdminOnly bool `json:"teamAdminOnly"`
}
func (r *NoticeMessages) Marshal() ([]byte, error) {
return json.Marshal(r)
}
func UnmarshalProductNoticeMessages(data io.Reader) (NoticeMessages, error) {
var r NoticeMessages
err := json.NewDecoder(data).Decode(&r)
return r, err
}
// User role, i.e. who will see the notice. Defaults to "all"
type NoticeAudience string
//go:fix inline
func NewNoticeAudience(s NoticeAudience) *NoticeAudience {
return new(s)
}
func (a *NoticeAudience) Matches(sysAdmin bool, teamAdmin bool) bool {
switch *a {
case NoticeAudienceAll:
return true
case NoticeAudienceMember:
return !sysAdmin && !teamAdmin
case NoticeAudienceSysadmin:
return sysAdmin
case NoticeAudienceTeamAdmin:
return teamAdmin
}
return false
}
const (
NoticeAudienceAll NoticeAudience = "all"
NoticeAudienceMember NoticeAudience = "member"
NoticeAudienceSysadmin NoticeAudience = "sysadmin"
NoticeAudienceTeamAdmin NoticeAudience = "teamadmin"
)
// Only show the notice on specific clients. Defaults to 'all'
//
// Client type. Defaults to "all"
type NoticeClientType string
//go:fix inline
func NewNoticeClientType(s NoticeClientType) *NoticeClientType { return new(s) }
func (c *NoticeClientType) Matches(other NoticeClientType) bool {
switch *c {
case NoticeClientTypeAll:
return true
case NoticeClientTypeMobile:
return other == NoticeClientTypeMobileIos || other == NoticeClientTypeMobileAndroid
default:
return *c == other
}
}
const (
NoticeClientTypeAll NoticeClientType = "all"
NoticeClientTypeDesktop NoticeClientType = "desktop"
NoticeClientTypeMobile NoticeClientType = "mobile"
NoticeClientTypeMobileAndroid NoticeClientType = "mobile-android"
NoticeClientTypeMobileIos NoticeClientType = "mobile-ios"
NoticeClientTypeWeb NoticeClientType = "web"
)
func NoticeClientTypeFromString(s string) (NoticeClientType, error) {
switch s {
case "web":
return NoticeClientTypeWeb, nil
case "mobile-ios":
return NoticeClientTypeMobileIos, nil
case "mobile-android":
return NoticeClientTypeMobileAndroid, nil
case "desktop":
return NoticeClientTypeDesktop, nil
}
return NoticeClientTypeAll, errors.New("Invalid client type supplied")
}
// Instance type. Defaults to "both"
type NoticeInstanceType string
//go:fix inline
func NewNoticeInstanceType(n NoticeInstanceType) *NoticeInstanceType { return new(n) }
func (t *NoticeInstanceType) Matches(isCloud bool) bool {
if *t == NoticeInstanceTypeBoth {
return true
}
if *t == NoticeInstanceTypeCloud && !isCloud {
return false
}
if *t == NoticeInstanceTypeOnPrem && isCloud {
return false
}
return true
}
const (
NoticeInstanceTypeBoth NoticeInstanceType = "both"
NoticeInstanceTypeCloud NoticeInstanceType = "cloud"
NoticeInstanceTypeOnPrem NoticeInstanceType = "onprem"
)
// SKU. Defaults to "all"
type NoticeSKU string
//go:fix inline
func NewNoticeSKU(s NoticeSKU) *NoticeSKU { return new(s) }
func (c *NoticeSKU) Matches(s string) bool {
switch *c {
case NoticeSKUAll:
return true
case NoticeSKUE0, NoticeSKUTeam:
return s == ""
default:
return s == string(*c)
}
}
const (
NoticeSKUE0 NoticeSKU = "e0"
NoticeSKUE10 NoticeSKU = "e10"
NoticeSKUE20 NoticeSKU = "e20"
NoticeSKUAll NoticeSKU = "all"
NoticeSKUTeam NoticeSKU = "team"
)
// Optional action to perform on action button click. (defaults to closing the notice)
//
// Possible actions to execute on button press
type NoticeAction string
const (
URL NoticeAction = "url"
)
// Definition of the table keeping the 'viewed' state of each in-product notice per user
type ProductNoticeViewState struct {
UserId string
NoticeId string
Viewed int32
Timestamp int64
}
type ExternalDependency struct {
Name string `json:"name"`
MinimumVersion string `json:"minimum_version"`
}