Fix 3.9.3 (#861)

* fix: 记录转换预览时不显示明细导入

* 3.9.3

* msg {NOW}

* be:手动备份成功后清除警告

* email

* be isSelf

* style
This commit is contained in:
REBUILD 企业管理系统 2025-02-07 11:20:03 +08:00 committed by GitHub
parent 45d0985553
commit f0af100849
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 130 additions and 106 deletions

View file

@ -10,7 +10,7 @@
</parent>
<groupId>com.rebuild</groupId>
<artifactId>rebuild</artifactId>
<version>3.9.2</version>
<version>3.9.3</version>
<name>rebuild</name>
<description>Building your business-systems freely!</description>
<url>https://getrebuild.com/</url>

View file

@ -74,11 +74,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
/**
* Rebuild Version
*/
public static final String VER = "3.9.2";
public static final String VER = "3.9.3";
/**
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
*/
public static final int BUILD = 3090207;
public static final int BUILD = 3090308;
static {
// Driver for DB

View file

@ -65,7 +65,7 @@ public class UserHelper {
try {
return Application.getUserStore().getUser(userId).isAdmin();
} catch (NoMemberFoundException ex) {
log.error("No User found : " + userId);
log.error("No User found : {}", userId);
}
return false;
}
@ -100,7 +100,7 @@ public class UserHelper {
}
} catch (NoMemberFoundException ex) {
log.error("No bizz found : " + bizzId);
log.error("No bizz found : {}", bizzId);
}
return false;
}
@ -460,16 +460,21 @@ public class UserHelper {
* @return
*/
public static boolean isSelf(ID user, ID otherUserOrAnyRecordId) {
ID createdBy = otherUserOrAnyRecordId;
if (otherUserOrAnyRecordId.getEntityCode() != EntityHelper.User) {
createdBy = getCreatedBy(otherUserOrAnyRecordId);
if (createdBy == null) return false;
try {
ID createdBy = otherUserOrAnyRecordId;
if (otherUserOrAnyRecordId.getEntityCode() != EntityHelper.User) {
createdBy = getCreatedBy(otherUserOrAnyRecordId);
if (createdBy == null) return false;
}
if (createdBy.equals(user)) return true;
// 所有管理员被视为同一用户
return isAdmin(createdBy) && isAdmin(user);
} catch (Exception ex) {
log.warn("Check isSelf error : {}, {}", user, otherUserOrAnyRecordId, ex);
return false;
}
if (createdBy.equals(user)) return true;
// 所有管理员被视为同一用户
return isAdmin(createdBy) && isAdmin(user);
}
private static ID getCreatedBy(ID anyRecordId) {

View file

@ -21,14 +21,18 @@ import com.rebuild.core.configuration.general.FormsBuilderContextHolder;
import com.rebuild.core.configuration.general.TransformManager;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
import com.rebuild.core.service.general.GeneralEntityService;
import com.rebuild.core.service.query.QueryHelper;
import com.rebuild.core.support.CommonsLog;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Zixin
@ -175,6 +179,12 @@ public class RecordTransfomer39 extends RecordTransfomer37 {
}
((JSONObject) formModel).put(GeneralEntityService.HAS_DETAILS, formModelDetailsMap);
// 明细导入
if (targetEntity.getDetailEntity() != null) {
((JSONObject) formModel).put("detailImports", buildDetailImports39(targetEntity));
}
return formModel;
}
@ -190,21 +200,6 @@ public class RecordTransfomer39 extends RecordTransfomer37 {
transTargetRecord.removeValue(EntityHelper.OwningUser);
transTargetRecord.removeValue(EntityHelper.OwningDept);
}
// if (isPreview) {
// Record existsRecordSnap = Application.getQueryFactory().recordNoFilter(targetExistsRecordId);
// for (Field field : existsRecordSnap.getEntity().getFields()) {
// EasyField easyField = EasyMetaFactory.valueOf(field);
// if (MetadataHelper.isCommonsField(field) || easyField.getDisplayType() == DisplayType.SERIES) {
// if (field.getType() == FieldType.PRIMARY) continue;
//
// String fieldName = field.getName();
// if (EntityHelper.AutoId.equals(fieldName) || EntityHelper.QuickCode.equals(fieldName)) continue;
//
// transTargetRecord.setObjectValue(fieldName, existsRecordSnap.getObjectValue(fieldName));
// }
// }
// }
}
// 获取主记录如果是明细的话
@ -215,4 +210,44 @@ public class RecordTransfomer39 extends RecordTransfomer37 {
}
return null;
}
// ~~
/**
* @param mainEntity
* @return
*/
public static List<Object> buildDetailImports39(Entity mainEntity) {
List<Object> alist = new ArrayList<>();
for (Entity de : mainEntity.getDetialEntities()) {
List<ConfigBean> confImports = TransformManager.instance.getDetailImports(de.getName());
if (!confImports.isEmpty()) {
for (ConfigBean c : confImports) {
JSONObject trans = (JSONObject) EasyMetaFactory.valueOf(c.getString("source")).toJSON();
trans.put("transid", c.getID("id"));
trans.put("transName", c.getString("name"));
int ifAuto = ((JSONObject) c.getJSON("config")).getIntValue("importsMode2Auto");
if (ifAuto > 0) {
JSONArray importsFilter = ((JSONObject) c.getJSON("config")).getJSONArray("importsFilter");
Set<String> autoFields = new HashSet<>();
for (Object o : importsFilter) {
String name = ((JSONArray) o).getString(0);
autoFields.add(name.split("\\.")[1]);
}
if (!autoFields.isEmpty()) {
trans.put("auto", ifAuto);
trans.put("autoFields", autoFields);
}
}
trans.put("detailName", de.getName());
alist.add(trans);
}
}
}
return alist;
}
}

View file

@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.core.support.general;
import cn.devezhao.commons.CalendarUtils;
import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
@ -51,14 +52,14 @@ public class ContentWithFieldVars {
* @return
*/
public static String replaceWithRecord(String content, ID recordId) {
if (StringUtils.isBlank(content) || recordId == null) {
return content;
}
if (StringUtils.isBlank(content) || recordId == null) return content;
Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
String pkName = entity.getPrimaryField().getName();
// 主键占位符
final Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
final String pkName = entity.getPrimaryField().getName();
// 固定占位符
content = content.replace("{ID}", String.format("{%s}", pkName));
content = content.replace("{NOW}", CalendarUtils.getUTCDateFormat().format(CalendarUtils.now()));
Set<String> fieldVars = new HashSet<>();
for (String field : matchsVars(content)) {
@ -84,12 +85,12 @@ public class ContentWithFieldVars {
public static String replaceWithRecord(String content, Record record) {
if (StringUtils.isBlank(content) || record == null) return content;
// 主键占位符
content = content.replace("{ID}",
String.format("{%s}", record.getEntity().getPrimaryField().getName()));
final Entity entity = record.getEntity();
// 固定占位符
content = content.replace("{ID}", String.format("{%s}", entity.getPrimaryField().getName()));
content = content.replace("{NOW}", CalendarUtils.getUTCDateFormat().format(CalendarUtils.now()));
Map<String, String> fieldVars = new HashMap<>();
for (String field : matchsVars(content)) {
if (MetadataHelper.getLastJoinField(entity, field) != null) {

View file

@ -39,6 +39,7 @@ import org.springframework.util.Assert;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@ -194,7 +195,7 @@ public class SMSender {
String base64;
try {
byte[] bs = FileUtils.readFileToByteArray(a);
base64 = java.util.Base64.getEncoder().encodeToString(bs);
base64 = Base64.getEncoder().encodeToString(bs);
} catch (IOException ex) {
continue;
}
@ -280,10 +281,22 @@ public class SMSender {
if (Application.devMode()) MT_CACHE = null;
if (MT_CACHE != null) return MT_CACHE.clone();
String content = CommonsUtils.getStringOfRes("i18n/email.zh_CN.html");
Assert.notNull(content, "Cannot load template of email");
Document html = Jsoup.parse(content);
String content = null;
// v3.9.3 从数据目录
File file = RebuildConfiguration.getFileOfData("email.zh_CN.html");
if (file.exists()) {
try {
content = FileUtils.readFileToString(file, AppUtils.UTF8);
} catch (IOException ex) {
log.warn("Cannot read file of email template : {}", file, ex);
}
}
if (content == null) {
content = CommonsUtils.getStringOfRes("i18n/email.zh_CN.html");
}
Assert.notNull(content, "Cannot read email template");
Document html = Jsoup.parse(content);
MT_CACHE = html.body();
return MT_CACHE.clone();
}

View file

@ -99,7 +99,7 @@ public class DatabaseBackup {
log.warn("Cannot zip backup : {}", zip);
}
log.info("Backup succeeded : {} ({})", dest, FileUtils.byteCountToDisplaySize(dest.length()));
log.info("Backup database succeeded : {} ({})", dest, FileUtils.byteCountToDisplaySize(dest.length()));
// 恢复
// https://stackoverflow.com/questions/16735344/how-to-ignore-certain-mysql-tables-when-importing-a-database

View file

@ -25,10 +25,6 @@ import java.io.IOException;
@Slf4j
public class DatafileBackup extends DatabaseBackup {
/**
* @return
* @throws IOException
*/
@Override
public File backup(File backups) throws IOException {
File rbdata = RebuildConfiguration.getFileOfData("");
@ -41,7 +37,7 @@ public class DatafileBackup extends DatabaseBackup {
return !("_backups".equals(name) || "_log".equals(name) || "temp".equals(name) || "rebuild.pid".equals(name));
});
log.info("Backup succeeded : {} ({})", destZip, FileUtils.byteCountToDisplaySize(destZip.length()));
log.info("Backup files succeeded : {} ({})", destZip, FileUtils.byteCountToDisplaySize(destZip.length()));
return destZip;
}

View file

@ -117,8 +117,8 @@ public abstract class BaseController extends Controller {
* @param name
* @return
*/
protected Integer getIntParameter(HttpServletRequest request, String name) {
return getIntParameter(request, name, null);
protected int getIntParameter(HttpServletRequest request, String name) {
return getIntParameter(request, name, 0);
}
/**
@ -127,7 +127,7 @@ public abstract class BaseController extends Controller {
* @param defaultValue
* @return
*/
protected Integer getIntParameter(HttpServletRequest request, String name, Integer defaultValue) {
protected int getIntParameter(HttpServletRequest request, String name, int defaultValue) {
String v = request.getParameter(name);
if (StringUtils.isBlank(v)) return defaultValue;

View file

@ -24,6 +24,7 @@ import com.rebuild.core.support.ConfigurationItem;
import com.rebuild.core.support.DataDesensitized;
import com.rebuild.core.support.License;
import com.rebuild.core.support.RebuildConfiguration;
import com.rebuild.core.support.SysbaseHeartbeat;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.core.support.integration.QiniuCloud;
import com.rebuild.core.support.integration.SMSender;
@ -145,6 +146,7 @@ public class ConfigurationController extends BaseController {
if (type == 1 || type == 3) {
try {
dbFile = "_backups/" + new DatabaseBackup().backup(backups).getName();
SysbaseHeartbeat.setItem(SysbaseHeartbeat.DatabaseBackupFail, null);
} catch (Exception e) {
dbFile = "ERR:" + e.getMessage();
log.error("Executing [DatabaseBackup] fails", e);
@ -153,6 +155,7 @@ public class ConfigurationController extends BaseController {
if (type == 2 || type == 3) {
try {
fileFile = "_backups/" + new DatafileBackup().backup(backups).getName();
SysbaseHeartbeat.setItem(SysbaseHeartbeat.DataFileBackupFail, null);
} catch (Exception e) {
fileFile = "ERR:" + e.getMessage();
log.error("Executing [DataFileBackup] fails", e);

View file

@ -88,7 +88,7 @@ public class ClassificationController extends BaseController {
item = EntityHelper.forUpdate(itemId, user);
} else if (dataId != null) {
ID parent = getIdParameter(request, "parent");
int level = getIntParameter(request, "level", 0);
int level = getIntParameter(request, "level");
item = EntityHelper.forNew(EntityHelper.ClassificationData, user);
item.setID("dataId", dataId);

View file

@ -54,7 +54,7 @@ public class BarCodeGeneratorController extends BaseController {
@GetMapping({"/commons/barcode/render-qr", "/commons/barcode/render"})
public void render(HttpServletRequest request, HttpServletResponse response) throws IOException {
String content = getParameter(request, "t", "UNSET");
int w = getIntParameter(request, "w", 0);
int w = getIntParameter(request, "w");
BufferedImage bi;
if (request.getRequestURI().endsWith("render-qr")) {

View file

@ -182,7 +182,7 @@ public class FileDownloader extends BaseController {
public void readRawText(HttpServletRequest request, HttpServletResponse response) throws IOException {
String filepath = getParameterNotNull(request, "url");
final String charset = getParameter(request, "charset", AppUtils.UTF8);
final int cut = getIntParameter(request, "cut"); // MB
final Integer cut = getIntParameter(request, "cut"); // MB
if (CommonsUtils.isExternalUrl(filepath)) {
// v3.7 禁外部地址

View file

@ -68,7 +68,7 @@ public class MetadataGetting extends BaseController {
public JSON fields(HttpServletRequest request) {
Entity entity = MetadataHelper.getEntity(getParameterNotNull(request, "entity"));
// 返回引用实体的字段层级
int appendRefFields = getIntParameter(request, "deep", 0);
int appendRefFields = getIntParameter(request, "deep");
// 根据不同的 referer 返回不同的字段列表
// 返回 ID 主键字段

View file

@ -44,7 +44,7 @@ public class AnnouncementController extends BaseController {
@GetMapping("/commons/announcements")
public RespBody announcementList(HttpServletRequest request) {
final ID user = AppUtils.getRequestUser(request);
int fromWhere = getIntParameter(request, "from", 0);
int fromWhere = getIntParameter(request, "from");
if (fromWhere == 0) {
// 1=动态页 2=首页 4=登录页

View file

@ -80,7 +80,7 @@ public class FeedsListController extends BaseController {
sqlWhere = "(1=1)";
}
int type = getIntParameter(request, "type", 0);
int type = getIntParameter(request, "type");
if (type == 1) {
sqlWhere += String.format(" and exists (select feedsId from FeedsMention where ^feedsId = feedsId and user = '%s')", user);
} else if (type == 2) {

View file

@ -72,7 +72,7 @@ public class GeneralListController extends EntityController {
final EasyEntity easyEntity = EasyMetaFactory.valueOf(listEntity);
int listMode = ObjectUtils.toInt(easyEntity.getExtraAttr(EasyEntityConfigProps.ADVLIST_MODE), 1);
int listModeForce = getIntParameter(request, "mode", 0);
int listModeForce = getIntParameter(request, "mode");
if (listModeForce >= 1 && listModeForce <= 3) listMode = listModeForce;
String listPage = listEntity.getMainEntity() != null ? "/general/detail-list" : "/general/record-list";
if (listMode == 2) listPage = "/general/record-list2"; // Mode2

View file

@ -24,8 +24,8 @@ import com.rebuild.core.configuration.general.TransformManager;
import com.rebuild.core.configuration.general.ViewAddonsManager;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
import com.rebuild.core.privileges.UserHelper;
import com.rebuild.core.service.general.transform.RecordTransfomer39;
import com.rebuild.core.service.query.QueryHelper;
import com.rebuild.core.support.ConfigurationItem;
import com.rebuild.core.support.RebuildConfiguration;
@ -43,13 +43,10 @@ import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* 表单/视图
@ -149,41 +146,9 @@ public class GeneralModelController extends EntityController {
FormsBuilder.instance.setFormInitialValue(modelEntity, model, (JSONObject) initialVal);
}
// v3.1 明细导入配置
// v3.4 FIXME ND只有第一个实体支持转换
// v3.7 ND
// 明细导入
if (modelEntity.getDetailEntity() != null) {
List<Object> alist = new ArrayList<>();
for (Entity de : modelEntity.getDetialEntities()) {
List<ConfigBean> confImports = TransformManager.instance.getDetailImports(de.getName());
if (!confImports.isEmpty()) {
for (ConfigBean c : confImports) {
JSONObject trans = (JSONObject) EasyMetaFactory.valueOf(c.getString("source")).toJSON();
trans.put("transid", c.getID("id"));
trans.put("transName", c.getString("name"));
int ifAuto = ((JSONObject) c.getJSON("config")).getIntValue("importsMode2Auto");
if (ifAuto > 0) {
JSONArray importsFilter = ((JSONObject) c.getJSON("config")).getJSONArray("importsFilter");
Set<String> autoFields = new HashSet<>();
for (Object o : importsFilter) {
String name = ((JSONArray) o).getString(0);
autoFields.add(name.split("\\.")[1]);
}
if (!autoFields.isEmpty()) {
trans.put("auto", ifAuto);
trans.put("autoFields", autoFields);
}
}
trans.put("detailName", de.getName());
alist.add(trans);
}
}
((JSONObject) model).put("detailImports", alist);
}
((JSONObject) model).put("detailImports", RecordTransfomer39.buildDetailImports39(modelEntity));
}
return model;

View file

@ -101,7 +101,7 @@ public class NotificationController extends BaseController {
final ID user = getRequestUser(request);
int pn = getIntParameter(request, "pageNo", 1);
int ps = getIntParameter(request, "pageSize", 40);
int type = getIntParameter(request, "type", 0);
int type = getIntParameter(request, "type");
boolean preview = getBoolParameter(request, "preview");
String sql = "select fromUser,message,createdOn,unread,messageId,relatedRecord,type from Notification" +

View file

@ -198,7 +198,7 @@ public class UserSettingsController extends BaseController {
@PostMapping("/cancel-external-user")
public RespBody cancelExternalUser(HttpServletRequest request) {
int appType = getIntParameter(request, "type", 0);
int appType = getIntParameter(request, "type");
// 1=Dingtalk, 2=Wxwork, 3=Feishu
String appId = appType == 1
? RebuildConfiguration.get(ConfigurationItem.DingtalkCorpid)

View file

@ -156,9 +156,8 @@ public class SignUpController extends BaseController {
public RespBody checkoutName(HttpServletRequest request) {
String fullName = getParameterNotNull(request, "fullName");
//noinspection UnnecessaryUnicodeEscape
fullName = fullName.replaceAll("[^a-zA-Z0-9\u4e00-\u9fa5]", "");
String loginName = HanLP.convertToPinyinString(fullName, "", false);
loginName = loginName.replaceAll("[^a-zA-Z0-9]", "");
if (loginName.length() > 20) {
loginName = loginName.substring(0, 20);
}

View file

@ -46,6 +46,9 @@
font-style: italic;
color: #999;
}
.modal.rbalert .modal-content {
margin: 13px auto;
}
</style>
</head>
<body class="dialog">

View file

@ -173,7 +173,9 @@ class ShowStyles2 extends ShowStyles {
// init
if (this.props.color) {
$cs.find(`a[data-color="${this.props.color}"]`).trigger('click')
$('.rbcolors >input').val(this.props.color)
$('.rbcolors>input').val(this.props.color)
} else {
$('.rbcolors>a:eq(0)').trigger('click')
}
}

View file

@ -55,7 +55,7 @@ $(document).ready(() => {
}
const id = $('.J_text').attr('attr-id')
const color = $('.rbcolors >a>i').parent().data('color') || _color || ''
const color = $('.rbcolors>a>i').parent().data('color') || _color || ''
let exists = null
$('.J_config .dd3-content, .unset-list .dd-handle>span').each(function () {
@ -95,7 +95,7 @@ $(document).ready(() => {
// Reset
$('.J_text').val('').removeAttr('data-key')
$('.J_confirm').text($L('添加'))
$('.rbcolors >a>i').remove()
$('.rbcolors>a>i').remove()
$('.J_config').parent().scrollTop(9999)
_color = null
@ -176,8 +176,10 @@ render_item_after = function (item, data) {
data[3] = item.attr('data-color')
if (data[3]) {
$(`.rbcolors >a[data-color="${data[3]}"]`).trigger('click')
$('.rbcolors >input').val(data[3])
$(`.rbcolors>a[data-color="${data[3]}"]`).trigger('click')
$('.rbcolors>input').val(data[3])
} else {
$('.rbcolors>a>i').remove()
}
})