Files
cloudpods/pkg/notify/models/mod_template.go

225 lines
6.6 KiB
Go

// Copyright 2019 Yunion
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package models
import (
"bytes"
"context"
"database/sql"
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
"strings"
ptem "text/template"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/notify/options"
"yunion.io/x/onecloud/pkg/notify/rpc/apis"
"yunion.io/x/onecloud/pkg/util/httputils"
)
type STemplateManager struct {
SStandaloneResourceBaseManager
}
var TemplateManager *STemplateManager
func init() {
TemplateManager = &STemplateManager{
SStandaloneResourceBaseManager: NewStandaloneResourceBaseManager(
STemplate{},
"notify_t_template",
"notifytemplate",
"notifytemplates",
),
}
}
const (
TEMPLATE_TYPE_TITLE = "title"
TEMPLATE_TYPE_CONTENT = "content"
TEMPLATE_TYPE_REMOTE = "remote"
CONTACTTYPE_ALL = "all"
)
type STemplate struct {
SStandaloneResourceBase
ContactType string `width:"16" nullable:"false" create:"required" update:"user" list:"user"`
Topic string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
// title | content | remote
TemplateType string `width:"10" nullable:"false" create:"required" update:"user" list:"user"`
Content string `length:"text" nullable:"false" create:"required" get:"user" list:"user" update:"user"`
}
func (tm *STemplateManager) GetEmailUrl() string {
if len(options.Options.ApiServer) > 0 && len(options.Options.VerifyEmailUrlPath) > 0 {
return httputils.JoinPath(options.Options.ApiServer, options.Options.VerifyEmailUrlPath)
}
return options.Options.VerifyEmailUrl
}
var templatePath = "/opt/yunion/share/template"
func (tm *STemplateManager) defaultTemplate() ([]STemplate, error) {
templates := make([]STemplate, 0, 4)
for _, templateType := range []string{"title", "content", "remote"} {
contactType, topic := CONTACTTYPE_ALL, ""
titleTemplatePath := fmt.Sprintf("%s/%s", templatePath, templateType)
files, err := ioutil.ReadDir(titleTemplatePath)
if err != nil {
return templates, errors.Wrapf(err, "Read Dir '%s'", titleTemplatePath)
}
for _, file := range files {
if file.IsDir() {
continue
}
spliteName := strings.Split(file.Name(), ".")
topic = spliteName[0]
if len(spliteName) > 1 {
contactType = spliteName[1]
}
fullPath := filepath.Join(titleTemplatePath, file.Name())
content, err := ioutil.ReadFile(fullPath)
if err != nil {
return templates, err
}
templates = append(templates, STemplate{
ContactType: contactType,
Topic: topic,
TemplateType: templateType,
Content: string(content),
})
}
}
return templates, nil
}
func (tm *STemplateManager) InitializeData() error {
templates, err := tm.defaultTemplate()
if err != nil {
return err
}
for _, template := range templates {
q := tm.Query().Equals("contact_type", template.ContactType).Equals("topic", template.Topic).Equals("template_type", template.TemplateType)
count, _ := q.CountWithError()
if count > 0 {
continue
}
err := tm.TableSpec().InsertOrUpdate(&template)
if err != nil {
return errors.Wrap(err, "sqlchemy.TableSpec.InsertOrUpdate")
}
}
return nil
}
// NotifyFilter will return the title and content generated by corresponding template.
// Local cache about common template will be considered in case of performance issues.
func (tm *STemplateManager) NotifyFilter(contactType, topic, msg string) (params apis.SendParams, err error) {
params.Topic = topic
templates := make([]STemplate, 0, 3)
q := tm.Query().Equals("topic", strings.ToUpper(topic)).In("contact_type", []string{CONTACTTYPE_ALL, contactType})
err = db.FetchModelObjects(tm, q, &templates)
if errors.Cause(err) == sql.ErrNoRows || len(templates) == 0 {
// no such template, return as is
params.Title = topic
params.Message = msg
return
}
if err != nil {
err = errors.Wrap(err, "db.FetchModelObjects")
return
}
for _, template := range templates {
var title, content string
switch template.TemplateType {
case TEMPLATE_TYPE_TITLE:
title, err = template.Execute(msg)
if err != nil {
return
}
params.Title = title
case TEMPLATE_TYPE_CONTENT:
content, err = template.Execute(msg)
if err != nil {
return
}
params.Message = content
case TEMPLATE_TYPE_REMOTE:
params.RemoteTemplate = template.Content
params.Message = msg
default:
err = errors.Error("no support template type")
return
}
}
return
}
func (tm *STemplate) Execute(str string) (string, error) {
tem, err := ptem.New("tmp").Parse(tm.Content)
if err != nil {
return "", errors.Wrapf(err, "Template.Parse for template %s", tm.GetId())
}
var buffer bytes.Buffer
tmpMap := make(map[string]interface{})
err = json.Unmarshal([]byte(str), &tmpMap)
if err != nil {
return "", errors.Wrap(err, "json.Unmarshal")
}
err = tem.Execute(&buffer, tmpMap)
if err != nil {
return "", errors.Wrap(err, "template,Execute")
}
return buffer.String(), nil
}
func (manager *STemplateManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential,
ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
ty, _ := data.GetString("template_type")
if ty != TEMPLATE_TYPE_TITLE && ty != TEMPLATE_TYPE_CONTENT && ty != TEMPLATE_TYPE_REMOTE {
return nil, httperrors.NewInputParameterError("no such support for tempalte type %s", ty)
}
return data, nil
}
func (self *STemplateManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
queryDict := query.(*jsonutils.JSONDict)
if queryDict.Contains("topic") {
val, _ := queryDict.GetString("topic")
q = q.Equals("topic", val)
}
if queryDict.Contains("template_type") {
val, _ := queryDict.GetString("template_type")
q = q.Equals("template_type", val)
}
if queryDict.Contains("contact_type") {
val, _ := queryDict.GetString("contact_type")
q = q.Equals("contact_type", val)
}
return q, nil
}