diff --git a/CHANGELOG.md b/CHANGELOG.md index e39f8ff..851cede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # 更新日志 +## v3.3.3 `anqicms` `2024-03-13` + +- [优化] API接口文档列表和文档详情增加支持待审文档显示 +- [优化] 如果原JPEG图片的质量低于预设值,则使用原质量值 +- [修复] 修复伪静态规则使用多级分类时候内存暴涨的问题 +- [修复] 修复伪静态规则对时间支持不全面的问题 +- [修复] 修复后台蜘蛛统计存在标记错误的问题 +- [修复] 后台界面调整 + ## v3.3.2 `anqicms` `2024-03-10` - [新增] 增加 Google Indexing diff --git a/config/constant.go b/config/constant.go index 5fba316..f0848b7 100644 --- a/config/constant.go +++ b/config/constant.go @@ -1,6 +1,6 @@ package config -const Version = "3.3.2" +const Version = "3.3.3" const ( StatusOK = 0 diff --git a/controller/manageController/pluginSendmail.go b/controller/manageController/pluginSendmail.go index 67292ba..b6b9b60 100644 --- a/controller/manageController/pluginSendmail.go +++ b/controller/manageController/pluginSendmail.go @@ -5,6 +5,7 @@ import ( "github.com/kataras/iris/v12" "kandaoni.com/anqicms/config" "kandaoni.com/anqicms/provider" + "kandaoni.com/anqicms/request" ) func PluginSendmailList(ctx iris.Context) { @@ -36,6 +37,37 @@ func PluginSendmailTest(ctx iris.Context) { }) return } + var req request.PluginTestSendmailRequest + if err := ctx.ReadJSON(&req); err != nil { + ctx.JSON(iris.Map{ + "code": config.StatusFailed, + "msg": err.Error(), + }) + return + } + + if req.Recipient != "" { + if req.Subject == "" || req.Message == "" { + ctx.JSON(iris.Map{ + "code": config.StatusFailed, + "msg": "请填写回复标题和内容", + }) + return + } + err := currentSite.SendMail(req.Subject, req.Message, req.Recipient) + if err != nil { + ctx.JSON(iris.Map{ + "code": config.StatusFailed, + "msg": err.Error(), + }) + return + } + ctx.JSON(iris.Map{ + "code": config.StatusOK, + "msg": "邮件发送成功", + }) + return + } subject := "测试邮件" content := "这是一封测试邮件。收到邮件表示配置正常" diff --git a/library/image.go b/library/image.go index cc6c488..d570af0 100644 --- a/library/image.go +++ b/library/image.go @@ -1,11 +1,14 @@ package library import ( + "bytes" "encoding/hex" + "errors" "github.com/disintegration/imaging" "image" "image/color" "image/draw" + "io" "strings" ) @@ -62,3 +65,221 @@ func HEXToRGB(h string) color.Color { } return color.RGBA{R: bs[0], G: bs[1], B: bs[2], A: 255} } + +// Errors +var ( + ErrInvalidJPEG = errors.New("Invalid JPEG header") + ErrWrongTable = errors.New("ERROR: Wrong size for quantization table") + ErrShortSegment = errors.New("short segment length") + ErrShortDQT = errors.New("DQT section too short") +) + +const ( + //from /usr/lib/go/src/image/jpeg/reader.go + dqtMarker = 0xdb // Define Quantization Table. +) + +// for the DQT marker -- start -- +// Sample quantization tables from JPEG spec --- only needed for +// guesstimate of quality factor. Note these are in zigzag order. + +var stdLuminanceQuantTbl = [64]int{ + 16, 11, 12, 14, 12, 10, 16, 14, + 13, 14, 18, 17, 16, 19, 24, 40, + 26, 24, 22, 22, 24, 49, 35, 37, + 29, 40, 58, 51, 61, 60, 57, 51, + 56, 55, 64, 72, 92, 78, 64, 68, + 87, 69, 55, 56, 80, 109, 81, 87, + 95, 98, 103, 104, 103, 62, 77, 113, + 121, 112, 100, 120, 92, 101, 103, 99, +} + +var stdChrominanceQuantTbl = [64]int{ + 17, 18, 18, 24, 21, 24, 47, 26, + 26, 47, 99, 66, 56, 66, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, +} + +var deftabs = [2][64]int{ + stdLuminanceQuantTbl, stdChrominanceQuantTbl, +} + +// for the DQT marker -- end -- + +// Qualitier ... +type Qualitier interface { + Quality() int +} + +type jpegReader struct { + rs io.ReadSeeker + q int +} + +// NewQualityWithBytes ... +func NewQualityWithBytes(buf []byte) (qr Qualitier, err error) { + return NewQuality(bytes.NewReader(buf)) +} + +// NewQuality ... +func NewQuality(rs io.ReadSeeker) (qr Qualitier, err error) { + jr := &jpegReader{rs: rs} + _, err = jr.rs.Seek(0, 0) + if err != nil { + return + } + + var ( + sign = make([]byte, 2) + ) + _, err = jr.rs.Read(sign) + if err != nil { + return + } + if sign[0] != 0xff && sign[1] != 0xd8 { + err = ErrInvalidJPEG + return + } + + jr.q, err = jr.readQuality() + if err != nil { + return + } + qr = jr + return +} + +func (jr *jpegReader) readQuality() (q int, err error) { + for { + mark := jr.readMarker() + if mark == 0 { + err = ErrInvalidJPEG + return + } + var ( + length, tableindex int + sign = make([]byte, 2) + ) + _, err = jr.rs.Read(sign) + if err != nil { + return + } + + length = int(sign[0])<<8 + int(sign[1]) - 2 + if length < 0 { + err = ErrShortSegment + return + } + + if (mark & 0xff) != dqtMarker { // not a quantization table + _, err = jr.rs.Seek(int64(length), 1) + if err != nil { + return + } + continue + } + + if length%65 != 0 { + err = ErrWrongTable + return + } + + var tabuf = make([]byte, length) + var n int + n, err = jr.rs.Read(tabuf) + if err != nil { + return + } + + allones := 1 + var cumsf, cumsf2 float64 + buf := tabuf[0:n] + + var reftable [64]int + + a := 0 + for a < n { + tableindex = int(tabuf[a] & 0x0f) + a++ + //precision: (c>>4) ? 16 : 8 + + if tableindex < 2 { + reftable = deftabs[tableindex] + } + // Read in the table, compute statistics relative to reference table + if a+64 > n { + err = ErrShortDQT + return + } + for coefindex := 0; coefindex < 64 && a < n; coefindex++ { + var val int + + if tableindex>>4 != 0 { + temp := int(buf[a]) + a++ + temp *= 256 + val = int(buf[a]) + temp + a++ + } else { + val = int(buf[a]) + a++ + } + + // scaling factor in percent + x := 100.0 * float64(val) / float64(reftable[coefindex]) + cumsf += x + cumsf2 += x * x + // separate check for all-ones table (Q 100) + if val != 1 { + allones = 0 + } + } + + if 0 != len(reftable) { // terse output includes quality + var qual float64 + cumsf /= 64.0 // mean scale factor + cumsf2 /= 64.0 + if allones == 1 { // special case for all-ones table + qual = 100.0 + } else if cumsf <= 100.0 { + qual = (200.0 - cumsf) / 2.0 + } else { + qual = 5000.0 / cumsf + } + + if tableindex == 0 { + q = (int)(qual + 0.5) + return + } + } + } + + } +} + +func (jr *jpegReader) readMarker() int { + var ( + mark = make([]byte, 2) + err error + ) + +ReadAgain: + _, err = jr.rs.Read(mark) + if err != nil { + return 0 + } + if mark[0] != 0xff || mark[1] == 0xff || mark[1] == 0x00 { + goto ReadAgain + } + + return int(mark[0])<<8 + int(mark[1]) +} + +func (jr *jpegReader) Quality() int { + return jr.q +} diff --git a/provider/attachment.go b/provider/attachment.go index 2764bd3..6aee5fe 100644 --- a/provider/attachment.go +++ b/provider/attachment.go @@ -38,6 +38,9 @@ func (w *Website) AttachmentUpload(file multipart.File, info *multipart.FileHead if fileExt == ".jpeg" { fileExt = ".jpg" } + if fileExt == ".ico" || fileExt == ".bmp" { + fileExt = ".png" + } if fileExt == "." { fileExt = "" } @@ -151,6 +154,10 @@ func (w *Website) AttachmentUpload(file multipart.File, info *multipart.FileHead if imgType == "jpeg" { imgType = "jpg" } + oriImgType := imgType + if imgType == "ico" || imgType == "bmp" { + imgType = "png" + } //只允许上传jpg,jpeg,gif,png,webp if imgType != "jpg" && imgType != "jpeg" && imgType != "gif" && imgType != "png" && imgType != "webp" { return nil, errors.New(fmt.Sprintf("%s: %s。", w.Lang("不支持的图片格式"), imgType)) @@ -175,6 +182,15 @@ func (w *Website) AttachmentUpload(file multipart.File, info *multipart.FileHead // 默认质量是90 quality = webp.DefaulQuality } + if oriImgType == "jpg" { + j, err2 := library.NewQuality(file) + file.Seek(0, 0) + if err2 == nil { + if j.Quality() < quality { + quality = j.Quality() + } + } + } if w.Content.ResizeImage == 1 && width > resizeWidth && imgType != "gif" { img = library.Resize(img, resizeWidth, 0) diff --git a/request/plugin.go b/request/plugin.go index caed291..c404bc8 100644 --- a/request/plugin.go +++ b/request/plugin.go @@ -122,3 +122,9 @@ type PluginHtmlCachePushRequest struct { All bool `json:"all"` Paths []string `json:"paths"` } + +type PluginTestSendmailRequest struct { + Recipient string `json:"recipient"` + Subject string `json:"subject"` + Message string `json:"message"` +}