mirror of https://github.com/cloudreve/Cloudreve
				
				
				
			feat(fs): custom properties for files (#2407)
							parent
							
								
									b13490357b
								
							
						
					
					
						commit
						3cda4d1ef7
					
				
							
								
								
									
										2
									
								
								assets
								
								
								
								
							
							
								
								
								
								
								
								
							
						
						
									
										2
									
								
								assets
								
								
								
								
							| 
						 | 
				
			
			@ -1 +1 @@
 | 
			
		|||
Subproject commit e9b91c4e03654d5968f8a676a13fc4badf530b5d
 | 
			
		||||
Subproject commit ada49fd21d159563b21d29d7d3499a45c5ab1503
 | 
			
		||||
| 
						 | 
				
			
			@ -59,11 +59,17 @@ type (
 | 
			
		|||
		StoragePolicyID int
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MetadataFilter struct {
 | 
			
		||||
		Key   string
 | 
			
		||||
		Value string
 | 
			
		||||
		Exact bool
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SearchFileParameters struct {
 | 
			
		||||
		Name []string
 | 
			
		||||
		// NameOperatorOr is true if the name should match any of the given names, false if all of them
 | 
			
		||||
		NameOperatorOr bool
 | 
			
		||||
		Metadata       map[string]string
 | 
			
		||||
		Metadata       []MetadataFilter
 | 
			
		||||
		Type           *types.FileType
 | 
			
		||||
		UseFullText    bool
 | 
			
		||||
		CaseFolding    bool
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,10 @@ import (
 | 
			
		|||
	"github.com/samber/lo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	metadataExactMatchPrefix = "!exact:"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (f *fileClient) searchQuery(q *ent.FileQuery, args *SearchFileParameters, parents []*ent.File, ownerId int) *ent.FileQuery {
 | 
			
		||||
	if len(parents) == 1 && parents[0] == nil {
 | 
			
		||||
		q = q.Where(file.OwnerID(ownerId))
 | 
			
		||||
| 
						 | 
				
			
			@ -69,13 +73,17 @@ func (f *fileClient) searchQuery(q *ent.FileQuery, args *SearchFileParameters, p
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if len(args.Metadata) > 0 {
 | 
			
		||||
		metaPredicates := lo.MapToSlice(args.Metadata, func(name string, value string) predicate.Metadata {
 | 
			
		||||
			nameEq := metadata.NameEQ(value)
 | 
			
		||||
			if name == "" {
 | 
			
		||||
		metaPredicates := lo.Map(args.Metadata, func(item MetadataFilter, index int) predicate.Metadata {
 | 
			
		||||
			if item.Exact {
 | 
			
		||||
				return metadata.And(metadata.NameEQ(item.Key), metadata.ValueEQ(item.Value))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			nameEq := metadata.NameEQ(item.Key)
 | 
			
		||||
			if item.Value == "" {
 | 
			
		||||
				return nameEq
 | 
			
		||||
			} else {
 | 
			
		||||
				valueContain := metadata.ValueContainsFold(value)
 | 
			
		||||
				return metadata.And(metadata.NameEQ(name), valueContain)
 | 
			
		||||
				valueContain := metadata.ValueContainsFold(item.Value)
 | 
			
		||||
				return metadata.And(nameEq, valueContain)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		metaPredicates = append(metaPredicates, metadata.IsPublic(true))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -324,6 +324,22 @@ var (
 | 
			
		|||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defaultFileProps = []types.CustomProps{
 | 
			
		||||
		{
 | 
			
		||||
			ID:   "description",
 | 
			
		||||
			Type: types.CustomPropsTypeText,
 | 
			
		||||
			Name: "fileManager.description",
 | 
			
		||||
			Icon: "fluent:slide-text-24-filled",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			ID:   "rating",
 | 
			
		||||
			Type: types.CustomPropsTypeRating,
 | 
			
		||||
			Name: "fileManager.rating",
 | 
			
		||||
			Icon: "fluent:data-bar-vertical-star-24-filled",
 | 
			
		||||
			Max:  5,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var DefaultSettings = map[string]string{
 | 
			
		||||
| 
						 | 
				
			
			@ -516,4 +532,10 @@ func init() {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	DefaultSettings["file_viewers"] = string(viewers)
 | 
			
		||||
 | 
			
		||||
	customProps, err := json.Marshal(defaultFileProps)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	DefaultSettings["custom_props"] = string(customProps)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -173,7 +173,8 @@ type (
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	ColumTypeProps struct {
 | 
			
		||||
		MetadataKey string `json:"metadata_key,omitempty" binding:"max=255"`
 | 
			
		||||
		MetadataKey   string `json:"metadata_key,omitempty" binding:"max=255"`
 | 
			
		||||
		CustomPropsID string `json:"custom_props_id,omitempty" binding:"max=255"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ShareProps struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -278,26 +279,51 @@ const (
 | 
			
		|||
	ViewerTypeCustom  = "custom"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Viewer struct {
 | 
			
		||||
	ID          string                             `json:"id"`
 | 
			
		||||
	Type        ViewerType                         `json:"type"`
 | 
			
		||||
	DisplayName string                             `json:"display_name"`
 | 
			
		||||
	Exts        []string                           `json:"exts"`
 | 
			
		||||
	Url         string                             `json:"url,omitempty"`
 | 
			
		||||
	Icon        string                             `json:"icon,omitempty"`
 | 
			
		||||
	WopiActions map[string]map[ViewerAction]string `json:"wopi_actions,omitempty"`
 | 
			
		||||
	Props       map[string]string                  `json:"props,omitempty"`
 | 
			
		||||
	MaxSize     int64                              `json:"max_size,omitempty"`
 | 
			
		||||
	Disabled    bool                               `json:"disabled,omitempty"`
 | 
			
		||||
	Templates   []NewFileTemplate                  `json:"templates,omitempty"`
 | 
			
		||||
	Platform    string                             `json:"platform,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
type (
 | 
			
		||||
	Viewer struct {
 | 
			
		||||
		ID          string                             `json:"id"`
 | 
			
		||||
		Type        ViewerType                         `json:"type"`
 | 
			
		||||
		DisplayName string                             `json:"display_name"`
 | 
			
		||||
		Exts        []string                           `json:"exts"`
 | 
			
		||||
		Url         string                             `json:"url,omitempty"`
 | 
			
		||||
		Icon        string                             `json:"icon,omitempty"`
 | 
			
		||||
		WopiActions map[string]map[ViewerAction]string `json:"wopi_actions,omitempty"`
 | 
			
		||||
		Props       map[string]string                  `json:"props,omitempty"`
 | 
			
		||||
		MaxSize     int64                              `json:"max_size,omitempty"`
 | 
			
		||||
		Disabled    bool                               `json:"disabled,omitempty"`
 | 
			
		||||
		Templates   []NewFileTemplate                  `json:"templates,omitempty"`
 | 
			
		||||
		Platform    string                             `json:"platform,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
	ViewerGroup struct {
 | 
			
		||||
		Viewers []Viewer `json:"viewers"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
type ViewerGroup struct {
 | 
			
		||||
	Viewers []Viewer `json:"viewers"`
 | 
			
		||||
}
 | 
			
		||||
	NewFileTemplate struct {
 | 
			
		||||
		Ext         string `json:"ext"`
 | 
			
		||||
		DisplayName string `json:"display_name"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type NewFileTemplate struct {
 | 
			
		||||
	Ext         string `json:"ext"`
 | 
			
		||||
	DisplayName string `json:"display_name"`
 | 
			
		||||
}
 | 
			
		||||
type (
 | 
			
		||||
	CustomPropsType string
 | 
			
		||||
	CustomProps     struct {
 | 
			
		||||
		ID      string          `json:"id"`
 | 
			
		||||
		Name    string          `json:"name"`
 | 
			
		||||
		Type    CustomPropsType `json:"type"`
 | 
			
		||||
		Max     int             `json:"max,omitempty"`
 | 
			
		||||
		Min     int             `json:"min,omitempty"`
 | 
			
		||||
		Default string          `json:"default,omitempty"`
 | 
			
		||||
		Options []string        `json:"options,omitempty"`
 | 
			
		||||
		Icon    string          `json:"icon,omitempty"`
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	CustomPropsTypeText        = "text"
 | 
			
		||||
	CustomPropsTypeNumber      = "number"
 | 
			
		||||
	CustomPropsTypeBoolean     = "boolean"
 | 
			
		||||
	CustomPropsTypeSelect      = "select"
 | 
			
		||||
	CustomPropsTypeMultiSelect = "multi_select"
 | 
			
		||||
	CustomPropsTypeLink        = "link"
 | 
			
		||||
	CustomPropsTypeRating      = "rating"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@ const (
 | 
			
		|||
	QuerySearchNameOpOr       = "name_op_or"
 | 
			
		||||
	QuerySearchUseOr          = "use_or"
 | 
			
		||||
	QuerySearchMetadataPrefix = "meta_"
 | 
			
		||||
	QuerySearchMetadataExact  = "exact_meta_"
 | 
			
		||||
	QuerySearchCaseFolding    = "case_folding"
 | 
			
		||||
	QuerySearchType           = "type"
 | 
			
		||||
	QuerySearchTypeCategory   = "category"
 | 
			
		||||
| 
						 | 
				
			
			@ -218,7 +219,7 @@ func (u *URI) FileSystem() constants.FileSystemType {
 | 
			
		|||
func (u *URI) SearchParameters() *inventory.SearchFileParameters {
 | 
			
		||||
	q := u.U.Query()
 | 
			
		||||
	res := &inventory.SearchFileParameters{
 | 
			
		||||
		Metadata: make(map[string]string),
 | 
			
		||||
		Metadata: make([]inventory.MetadataFilter, 0),
 | 
			
		||||
	}
 | 
			
		||||
	withSearch := false
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -252,7 +253,18 @@ func (u *URI) SearchParameters() *inventory.SearchFileParameters {
 | 
			
		|||
 | 
			
		||||
	for k, v := range q {
 | 
			
		||||
		if strings.HasPrefix(k, QuerySearchMetadataPrefix) {
 | 
			
		||||
			res.Metadata[strings.TrimPrefix(k, QuerySearchMetadataPrefix)] = v[0]
 | 
			
		||||
			res.Metadata = append(res.Metadata, inventory.MetadataFilter{
 | 
			
		||||
				Key:   strings.TrimPrefix(k, QuerySearchMetadataPrefix),
 | 
			
		||||
				Value: v[0],
 | 
			
		||||
				Exact: false,
 | 
			
		||||
			})
 | 
			
		||||
			withSearch = true
 | 
			
		||||
		} else if strings.HasPrefix(k, QuerySearchMetadataExact) {
 | 
			
		||||
			res.Metadata = append(res.Metadata, inventory.MetadataFilter{
 | 
			
		||||
				Key:   strings.TrimPrefix(k, QuerySearchMetadataExact),
 | 
			
		||||
				Value: v[0],
 | 
			
		||||
				Exact: true,
 | 
			
		||||
			})
 | 
			
		||||
			withSearch = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,14 +5,18 @@ import (
 | 
			
		|||
	"crypto/sha1"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudreve/Cloudreve/v4/application/constants"
 | 
			
		||||
	"github.com/cloudreve/Cloudreve/v4/application/dependency"
 | 
			
		||||
	"github.com/cloudreve/Cloudreve/v4/inventory/types"
 | 
			
		||||
	"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs"
 | 
			
		||||
	"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs/dbfs"
 | 
			
		||||
	"github.com/cloudreve/Cloudreve/v4/pkg/hashid"
 | 
			
		||||
	"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
 | 
			
		||||
	"github.com/go-playground/validator/v10"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"github.com/samber/lo"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
| 
						 | 
				
			
			@ -20,13 +24,14 @@ type (
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	wildcardMetadataKey      = "*"
 | 
			
		||||
	customizeMetadataSuffix  = "customize"
 | 
			
		||||
	tagMetadataSuffix        = "tag"
 | 
			
		||||
	iconColorMetadataKey     = customizeMetadataSuffix + ":icon_color"
 | 
			
		||||
	emojiIconMetadataKey     = customizeMetadataSuffix + ":emoji"
 | 
			
		||||
	shareOwnerMetadataKey    = dbfs.MetadataSysPrefix + "shared_owner"
 | 
			
		||||
	shareRedirectMetadataKey = dbfs.MetadataSysPrefix + "shared_redirect"
 | 
			
		||||
	wildcardMetadataKey       = "*"
 | 
			
		||||
	customizeMetadataSuffix   = "customize"
 | 
			
		||||
	tagMetadataSuffix         = "tag"
 | 
			
		||||
	customPropsMetadataSuffix = "props"
 | 
			
		||||
	iconColorMetadataKey      = customizeMetadataSuffix + ":icon_color"
 | 
			
		||||
	emojiIconMetadataKey      = customizeMetadataSuffix + ":emoji"
 | 
			
		||||
	shareOwnerMetadataKey     = dbfs.MetadataSysPrefix + "shared_owner"
 | 
			
		||||
	shareRedirectMetadataKey  = dbfs.MetadataSysPrefix + "shared_redirect"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
| 
						 | 
				
			
			@ -131,6 +136,126 @@ var (
 | 
			
		|||
				return nil
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		customPropsMetadataSuffix: {
 | 
			
		||||
			wildcardMetadataKey: func(ctx context.Context, m *manager, patch *fs.MetadataPatch) error {
 | 
			
		||||
				if patch.Remove {
 | 
			
		||||
					return nil
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				customProps := m.settings.CustomProps(ctx)
 | 
			
		||||
				propId := strings.TrimPrefix(patch.Key, customPropsMetadataSuffix+":")
 | 
			
		||||
				for _, prop := range customProps {
 | 
			
		||||
					if prop.ID == propId {
 | 
			
		||||
						switch prop.Type {
 | 
			
		||||
						case types.CustomPropsTypeText:
 | 
			
		||||
							if prop.Min > 0 && prop.Min > len(patch.Value) {
 | 
			
		||||
								return fmt.Errorf("value is too short")
 | 
			
		||||
							}
 | 
			
		||||
							if prop.Max > 0 && prop.Max < len(patch.Value) {
 | 
			
		||||
								return fmt.Errorf("value is too long")
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							return nil
 | 
			
		||||
						case types.CustomPropsTypeRating:
 | 
			
		||||
							if patch.Value == "" {
 | 
			
		||||
								return nil
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							// validate the value is a number
 | 
			
		||||
							rating, err := strconv.Atoi(patch.Value)
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								return fmt.Errorf("value is not a number")
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if prop.Max < rating {
 | 
			
		||||
								return fmt.Errorf("value is too large")
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							return nil
 | 
			
		||||
 | 
			
		||||
						case types.CustomPropsTypeNumber:
 | 
			
		||||
							if patch.Value == "" {
 | 
			
		||||
								return nil
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							value, err := strconv.Atoi(patch.Value)
 | 
			
		||||
							if err != nil {
 | 
			
		||||
								return fmt.Errorf("value is not a number")
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if prop.Min > value {
 | 
			
		||||
								return fmt.Errorf("value is too small")
 | 
			
		||||
							}
 | 
			
		||||
							if prop.Max > 0 && prop.Max < value {
 | 
			
		||||
								return fmt.Errorf("value is too large")
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							return nil
 | 
			
		||||
 | 
			
		||||
						case types.CustomPropsTypeBoolean:
 | 
			
		||||
							if patch.Value == "" {
 | 
			
		||||
								return nil
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if patch.Value != "true" && patch.Value != "false" {
 | 
			
		||||
								return fmt.Errorf("value is not a boolean")
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							return nil
 | 
			
		||||
						case types.CustomPropsTypeSelect:
 | 
			
		||||
							if patch.Value == "" {
 | 
			
		||||
								return nil
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							for _, option := range prop.Options {
 | 
			
		||||
								if option == patch.Value {
 | 
			
		||||
									return nil
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							return fmt.Errorf("invalid option")
 | 
			
		||||
						case types.CustomPropsTypeMultiSelect:
 | 
			
		||||
							if patch.Value == "" {
 | 
			
		||||
								return nil
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							var values []string
 | 
			
		||||
							if err := json.Unmarshal([]byte(patch.Value), &values); err != nil {
 | 
			
		||||
								return fmt.Errorf("invalid multi select value: %w", err)
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							// make sure all values are in the options
 | 
			
		||||
							for _, value := range values {
 | 
			
		||||
								if !lo.Contains(prop.Options, value) {
 | 
			
		||||
									return fmt.Errorf("invalid option")
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							return nil
 | 
			
		||||
 | 
			
		||||
						case types.CustomPropsTypeLink:
 | 
			
		||||
							if patch.Value == "" {
 | 
			
		||||
								return nil
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if prop.Min > 0 && len(patch.Value) < prop.Min {
 | 
			
		||||
								return fmt.Errorf("value is too small")
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							if prop.Max > 0 && len(patch.Value) > prop.Max {
 | 
			
		||||
								return fmt.Errorf("value is too large")
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							return nil
 | 
			
		||||
						default:
 | 
			
		||||
							return nil
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return fmt.Errorf("unkown custom props")
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -196,6 +196,8 @@ type (
 | 
			
		|||
		LibRawThumbExts(ctx context.Context) []string
 | 
			
		||||
		// LibRawThumbPath returns the path of libraw executable.
 | 
			
		||||
		LibRawThumbPath(ctx context.Context) string
 | 
			
		||||
		// CustomProps returns the custom props settings.
 | 
			
		||||
		CustomProps(ctx context.Context) []types.CustomProps
 | 
			
		||||
	}
 | 
			
		||||
	UseFirstSiteUrlCtxKey = struct{}
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -223,6 +225,15 @@ type (
 | 
			
		|||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s *settingProvider) CustomProps(ctx context.Context) []types.CustomProps {
 | 
			
		||||
	raw := s.getString(ctx, "custom_props", "[]")
 | 
			
		||||
	var props []types.CustomProps
 | 
			
		||||
	if err := json.Unmarshal([]byte(raw), &props); err != nil {
 | 
			
		||||
		return []types.CustomProps{}
 | 
			
		||||
	}
 | 
			
		||||
	return props
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *settingProvider) License(ctx context.Context) string {
 | 
			
		||||
	return s.getString(ctx, "license", "")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,7 @@ type SiteConfig struct {
 | 
			
		|||
	MaxBatchSize      int                       `json:"max_batch_size,omitempty"`
 | 
			
		||||
	ThumbnailWidth    int                       `json:"thumbnail_width,omitempty"`
 | 
			
		||||
	ThumbnailHeight   int                       `json:"thumbnail_height,omitempty"`
 | 
			
		||||
	CustomProps       []types.CustomProps       `json:"custom_props,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// App settings
 | 
			
		||||
	AppPromotion bool `json:"app_promotion,omitempty"`
 | 
			
		||||
| 
						 | 
				
			
			@ -87,6 +88,7 @@ func (s *GetSettingService) GetSiteConfig(c *gin.Context) (*SiteConfig, error) {
 | 
			
		|||
		explorerSettings := settings.ExplorerFrontendSettings(c)
 | 
			
		||||
		mapSettings := settings.MapSetting(c)
 | 
			
		||||
		fileViewers := settings.FileViewers(c)
 | 
			
		||||
		customProps := settings.CustomProps(c)
 | 
			
		||||
		maxBatchSize := settings.MaxBatchedFile(c)
 | 
			
		||||
		w, h := settings.ThumbSize(c)
 | 
			
		||||
		for i := range fileViewers {
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +104,7 @@ func (s *GetSettingService) GetSiteConfig(c *gin.Context) (*SiteConfig, error) {
 | 
			
		|||
			GoogleMapTileType: mapSettings.GoogleTileType,
 | 
			
		||||
			ThumbnailWidth:    w,
 | 
			
		||||
			ThumbnailHeight:   h,
 | 
			
		||||
			CustomProps:       customProps,
 | 
			
		||||
		}, nil
 | 
			
		||||
	case "emojis":
 | 
			
		||||
		emojis := settings.EmojiPresets(c)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue