mattermost-community-enterp.../public/model/channel_bookmark.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

337 lines
10 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"net/http"
"strings"
"unicode/utf8"
)
type ChannelBookmarkType string
const (
ChannelBookmarkLink ChannelBookmarkType = "link"
ChannelBookmarkFile ChannelBookmarkType = "file"
BookmarkFileOwner = "bookmark"
MaxBookmarksPerChannel = 50
DisplayNameMaxRunes = 64
LinkMaxRunes = 1024
)
type ChannelBookmark struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"`
ChannelId string `json:"channel_id"`
OwnerId string `json:"owner_id"`
FileId string `json:"file_id"`
DisplayName string `json:"display_name"`
SortOrder int64 `json:"sort_order"`
LinkUrl string `json:"link_url,omitempty"`
ImageUrl string `json:"image_url,omitempty"`
Emoji string `json:"emoji,omitempty"`
Type ChannelBookmarkType `json:"type"`
OriginalId string `json:"original_id,omitempty"`
ParentId string `json:"parent_id,omitempty"`
}
func (o *ChannelBookmark) Auditable() map[string]any {
return map[string]any{
"id": o.Id,
"create_at": o.CreateAt,
"update_at": o.UpdateAt,
"delete_at": o.DeleteAt,
"channel_id": o.ChannelId,
"owner_id": o.OwnerId,
"file_id": o.FileId,
"type": o.Type,
"original_id": o.OriginalId,
"parent_id": o.ParentId,
}
}
// Clone returns a shallow copy of the channel bookmark.
func (o *ChannelBookmark) Clone() *ChannelBookmark {
bCopy := *o
return &bCopy
}
// SetOriginal generates a new bookmark copying the data of the
// receiver bookmark, resets its timestamps and main ID, updates its
// OriginalId and sets the owner to the ID passed as a parameter
func (o *ChannelBookmark) SetOriginal(newOwnerId string) *ChannelBookmark {
bCopy := *o
bCopy.Id = ""
bCopy.CreateAt = 0
bCopy.DeleteAt = 0
bCopy.UpdateAt = 0
bCopy.OriginalId = o.Id
bCopy.OwnerId = newOwnerId
return &bCopy
}
func (o *ChannelBookmark) IsValid() *AppError {
if !IsValidId(o.Id) {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.id.app_error", nil, "", http.StatusBadRequest)
}
if o.CreateAt == 0 {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if o.UpdateAt == 0 {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if !IsValidId(o.ChannelId) {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.channel_id.app_error", nil, "", http.StatusBadRequest)
}
if !IsValidId(o.OwnerId) {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.owner_id.app_error", nil, "", http.StatusBadRequest)
}
if o.DisplayName == "" || utf8.RuneCountInString(o.DisplayName) > DisplayNameMaxRunes {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.display_name.app_error", nil, "", http.StatusBadRequest)
}
if !(o.Type == ChannelBookmarkFile || o.Type == ChannelBookmarkLink) {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.type.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if o.Type == ChannelBookmarkLink && o.FileId != "" {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.file_id.missing_or_invalid.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if o.Type == ChannelBookmarkFile && o.LinkUrl != "" {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.link_url.missing_or_invalid.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if o.Type == ChannelBookmarkLink && (o.LinkUrl == "" || !IsValidHTTPURL(o.LinkUrl) || utf8.RuneCountInString(o.LinkUrl) > LinkMaxRunes) {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.link_url.missing_or_invalid.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if o.Type == ChannelBookmarkLink && o.ImageUrl != "" && (!IsValidHTTPURL(o.ImageUrl) || utf8.RuneCountInString(o.ImageUrl) > LinkMaxRunes) {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.image_url.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if o.Type == ChannelBookmarkFile && (o.FileId == "" || !IsValidId(o.FileId)) {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.file_id.missing_or_invalid.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if o.ImageUrl != "" && o.FileId != "" {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.link_file.app_error", nil, "id="+o.Id, http.StatusBadRequest)
}
if o.OriginalId != "" && !IsValidId(o.OriginalId) {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.original_id.app_error", nil, "", http.StatusBadRequest)
}
if o.ParentId != "" && !IsValidId(o.ParentId) {
return NewAppError("ChannelBookmark.IsValid", "model.channel_bookmark.is_valid.parent_id.app_error", nil, "", http.StatusBadRequest)
}
return nil
}
func (o *ChannelBookmark) PreSave() {
if o.Id == "" {
o.Id = NewId()
}
o.DisplayName = SanitizeUnicode(o.DisplayName)
o.Emoji = strings.Trim(o.Emoji, ":")
if o.CreateAt == 0 {
o.CreateAt = GetMillis()
}
o.UpdateAt = o.CreateAt
}
func (o *ChannelBookmark) PreUpdate() {
o.UpdateAt = GetMillis()
o.DisplayName = SanitizeUnicode(o.DisplayName)
o.Emoji = strings.Trim(o.Emoji, ":")
}
func (o *ChannelBookmark) ToBookmarkWithFileInfo(f *FileInfo) *ChannelBookmarkWithFileInfo {
bwf := ChannelBookmarkWithFileInfo{
ChannelBookmark: &ChannelBookmark{
Id: o.Id,
CreateAt: o.CreateAt,
UpdateAt: o.UpdateAt,
DeleteAt: o.DeleteAt,
ChannelId: o.ChannelId,
OwnerId: o.OwnerId,
FileId: o.FileId,
DisplayName: o.DisplayName,
SortOrder: o.SortOrder,
LinkUrl: o.LinkUrl,
ImageUrl: o.ImageUrl,
Emoji: strings.Trim(o.Emoji, ":"),
Type: o.Type,
OriginalId: o.OriginalId,
ParentId: o.ParentId,
},
}
if f != nil && f.Id != "" {
bwf.FileInfo = f
}
return &bwf
}
type ChannelBookmarkPatch struct {
FileId *string `json:"file_id"`
DisplayName *string `json:"display_name"`
SortOrder *int64 `json:"sort_order"`
LinkUrl *string `json:"link_url,omitempty"`
ImageUrl *string `json:"image_url,omitempty"`
Emoji *string `json:"emoji,omitempty"`
}
func (o *ChannelBookmarkPatch) Auditable() map[string]any {
return map[string]any{
"file_id": o.FileId,
}
}
func (o *ChannelBookmark) Patch(patch *ChannelBookmarkPatch) {
if patch.FileId != nil {
o.FileId = *patch.FileId
}
if patch.DisplayName != nil {
o.DisplayName = *patch.DisplayName
}
if patch.SortOrder != nil {
o.SortOrder = *patch.SortOrder
}
if patch.LinkUrl != nil {
o.LinkUrl = *patch.LinkUrl
}
if patch.ImageUrl != nil {
o.ImageUrl = *patch.ImageUrl
}
if patch.Emoji != nil {
o.Emoji = *patch.Emoji
}
}
type ChannelBookmarkWithFileInfo struct {
*ChannelBookmark
FileInfo *FileInfo `json:"file,omitempty"`
}
func (o *ChannelBookmarkWithFileInfo) Auditable() map[string]any {
a := o.ChannelBookmark.Auditable()
if o.FileInfo != nil {
a["file"] = o.FileInfo.Auditable()
}
return a
}
// Clone returns a shallow copy of the channel bookmark with file info.
func (o *ChannelBookmarkWithFileInfo) Clone() *ChannelBookmarkWithFileInfo {
bCopy := *o
return &bCopy
}
type ChannelWithBookmarks struct {
*Channel
Bookmarks []*ChannelBookmarkWithFileInfo `json:"bookmarks,omitempty"`
}
type ChannelWithTeamDataAndBookmarks struct {
*ChannelWithTeamData
Bookmarks []*ChannelBookmarkWithFileInfo `json:"bookmarks,omitempty"`
}
type UpdateChannelBookmarkResponse struct {
Updated *ChannelBookmarkWithFileInfo `json:"updated,omitempty"`
Deleted *ChannelBookmarkWithFileInfo `json:"deleted,omitempty"`
}
func (o *UpdateChannelBookmarkResponse) Auditable() map[string]any {
a := map[string]any{}
if o.Updated != nil {
a["updated"] = o.Updated.Auditable()
}
if o.Deleted != nil {
a["updated"] = o.Deleted.Auditable()
}
return a
}
type ChannelBookmarkAndFileInfo struct {
Id string
CreateAt int64
UpdateAt int64
DeleteAt int64
ChannelId string
OwnerId string
FileInfoId string
DisplayName string
SortOrder int64
LinkUrl string
ImageUrl string
Emoji string
Type ChannelBookmarkType
OriginalId string
ParentId string
FileId string
FileName string
Extension string
Size int64
MimeType string
Width int
Height int
HasPreviewImage bool
MiniPreview *[]byte
}
func (o *ChannelBookmarkAndFileInfo) ToChannelBookmarkWithFileInfo() *ChannelBookmarkWithFileInfo {
bwf := &ChannelBookmarkWithFileInfo{
ChannelBookmark: &ChannelBookmark{
Id: o.Id,
CreateAt: o.CreateAt,
UpdateAt: o.UpdateAt,
DeleteAt: o.DeleteAt,
ChannelId: o.ChannelId,
OwnerId: o.OwnerId,
FileId: o.FileInfoId,
DisplayName: o.DisplayName,
SortOrder: o.SortOrder,
LinkUrl: o.LinkUrl,
ImageUrl: o.ImageUrl,
Emoji: o.Emoji,
Type: o.Type,
OriginalId: o.OriginalId,
ParentId: o.ParentId,
},
}
if o.FileInfoId != "" && o.FileId != "" {
miniPreview := o.MiniPreview
if len(*miniPreview) == 0 {
miniPreview = nil
}
bwf.FileInfo = &FileInfo{
Id: o.FileId,
Name: o.FileName,
Extension: o.Extension,
Size: o.Size,
MimeType: o.MimeType,
Width: o.Width,
Height: o.Height,
HasPreviewImage: o.HasPreviewImage,
MiniPreview: miniPreview,
}
}
return bwf
}