mirror of
https://github.com/getrebuild/rebuild.git
synced 2025-09-26 16:45:52 +08:00
Merge branch 'master' into develop
This commit is contained in:
commit
112470a4ea
33 changed files with 370 additions and 189 deletions
|
@ -4,7 +4,7 @@ RUN apk add ttf-dejavu
|
|||
RUN apk add wget
|
||||
|
||||
RUN mkdir -p /app/rebuild/.rebuild/
|
||||
ADD https://www.qn-cdn.getrebuild.com/pub/deploy/rebuild-boot-4.0.0-beta3.jar /app/rebuild/rebuild-boot.jar
|
||||
ADD ./rebuild-boot-4.0.0.jar /app/rebuild/rebuild-boot.jar
|
||||
ADD https://www.qn-cdn.getrebuild.com/pub/deploy/SourceHanSansK-Regular.ttf /app/rebuild/.rebuild/
|
||||
|
||||
EXPOSE 18080
|
||||
|
|
|
@ -4,11 +4,6 @@ services:
|
|||
environment:
|
||||
MYSQL_ROOT_HOST: "%"
|
||||
MYSQL_ROOT_PASSWORD: "rebuildP4wd"
|
||||
MYSQL_DATABASE: rebuild40
|
||||
command: sh -c "
|
||||
curl -sSL -o /docker-entrypoint-initdb.d/db-init.sql https://www.qn-cdn.getrebuild.com/pub/deploy/db-init_4.0.sql &&
|
||||
exec docker-entrypoint.sh mysqld
|
||||
"
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
healthcheck:
|
||||
|
@ -29,7 +24,7 @@ services:
|
|||
- >
|
||||
mkdir -p /app/rebuild/.rebuild &&
|
||||
wget -O /app/rebuild/.rebuild/.rebuild https://www.qn-cdn.getrebuild.com/pub/deploy/.rebuild &&
|
||||
java -Duser.timezone=Asia/Shanghai -DDataDirectory=/app/rebuild/.rebuild -jar /app/rebuild/rebuild-boot.jar
|
||||
java -Dinitialize=yes -Duser.timezone=Asia/Shanghai -DDataDirectory=/app/rebuild/.rebuild -jar /app/rebuild/rebuild-boot.jar
|
||||
ports:
|
||||
- "18080:18080"
|
||||
volumes:
|
||||
|
|
16
.deploy/rebuild.conf
Normal file
16
.deploy/rebuild.conf
Normal file
|
@ -0,0 +1,16 @@
|
|||
# ConfigurationItem
|
||||
MobileUrl=
|
||||
RbStoreUrl=
|
||||
SecurityEnhanced=
|
||||
TrustedAllUrl=
|
||||
LibreofficeBin=
|
||||
OnlyofficeServer=
|
||||
OnlyofficeJwt=
|
||||
MysqldumpBin=
|
||||
UnsafeImgAccess=
|
||||
# CommandArgs
|
||||
_SmsDistributor=
|
||||
_EmailDistributor=
|
||||
_UseFrontJSAnywhere=
|
||||
_TriggerMaxDepth=
|
||||
_TriggerLessLog=
|
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
|||
</parent>
|
||||
<groupId>com.rebuild</groupId>
|
||||
<artifactId>rebuild</artifactId>
|
||||
<version>4.0.0</version>
|
||||
<version>4.0.2</version>
|
||||
<name>rebuild</name>
|
||||
<description>Building your business-systems freely!</description>
|
||||
<url>https://getrebuild.com/</url>
|
||||
|
|
|
@ -37,6 +37,7 @@ import com.rebuild.core.support.License;
|
|||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.core.support.setup.DatabaseFixer;
|
||||
import com.rebuild.core.support.setup.DockerInstaller;
|
||||
import com.rebuild.core.support.setup.Installer;
|
||||
import com.rebuild.core.support.setup.UpgradeDatabase;
|
||||
import com.rebuild.utils.JSONable;
|
||||
|
@ -74,11 +75,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
/**
|
||||
* Rebuild Version
|
||||
*/
|
||||
public static final String VER = "4.0.0";
|
||||
public static final String VER = "4.0.2";
|
||||
/**
|
||||
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
|
||||
*/
|
||||
public static final int BUILD = 4000006;
|
||||
public static final int BUILD = 4000208;
|
||||
|
||||
static {
|
||||
// Driver for DB
|
||||
|
@ -133,17 +134,25 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
final Timer timer = new Timer("Boot-Timer");
|
||||
|
||||
try {
|
||||
// v4.0.2 for Docker
|
||||
DockerInstaller di = new DockerInstaller();
|
||||
final boolean isNeedInitialize = di.isNeedInitialize();
|
||||
if (isNeedInitialize) {
|
||||
log.info("Initializing REBUILD for Docker container ...");
|
||||
di.install();
|
||||
}
|
||||
|
||||
if (Installer.isInstalled()) {
|
||||
started = init();
|
||||
|
||||
if (started) {
|
||||
final long time2 = System.currentTimeMillis() - time;
|
||||
final long timeCost = System.currentTimeMillis() - time;
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
String localUrl = BootApplication.getLocalUrl(null);
|
||||
String banner = RebuildBanner.formatSimple(
|
||||
"REBUILD (" + VER + ") started successfully in " + time2 + " ms.",
|
||||
"REBUILD (" + VER + ") started successfully in " + timeCost + " ms.",
|
||||
" License : " + License.queryAuthority().values(),
|
||||
"Access URLs : ",
|
||||
" Local : " + localUrl,
|
||||
|
@ -162,6 +171,8 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
}
|
||||
}
|
||||
}, 999);
|
||||
|
||||
if (isNeedInitialize) di.installAfter();
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -215,7 +226,7 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
// 版本升级会清除缓存
|
||||
int lastBuild = ObjectUtils.toInt(RebuildConfiguration.get(ConfigurationItem.AppBuild, true), 0);
|
||||
// MINOR
|
||||
if (lastBuild / 100000 != BUILD / 100000) {
|
||||
if (lastBuild > 0 && lastBuild / 100000 != BUILD / 100000) {
|
||||
log.warn("Clean up the cache when upgrading : {} from {}", BUILD, lastBuild);
|
||||
Installer.clearAllCache();
|
||||
RebuildConfiguration.set(ConfigurationItem.AppBuild, BUILD);
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.rebuild.core.configuration.ConfigManager;
|
|||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -47,7 +48,7 @@ public class ClassificationManager implements ConfigManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取全名称(包括父级,用 . 分割)
|
||||
* 获取全名称(包括父级)
|
||||
*
|
||||
* @param itemId
|
||||
* @return
|
||||
|
@ -61,39 +62,20 @@ public class ClassificationManager implements ConfigManager {
|
|||
* @param itemId
|
||||
* @return
|
||||
*/
|
||||
public String getColor(ID itemId) {
|
||||
Item item = getItem(itemId);
|
||||
return item == null ? null : item.Color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param itemId
|
||||
* @return
|
||||
*/
|
||||
public String getCode(ID itemId) {
|
||||
Item item = getItem(itemId);
|
||||
return item == null ? null : item.Code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param itemId
|
||||
* @return
|
||||
*/
|
||||
private Item getItem(ID itemId) {
|
||||
final String ckey = "ClassificationITEM38-" + itemId;
|
||||
public Item getItem(ID itemId) {
|
||||
final String ckey = "ClassificationITEM40-" + itemId;
|
||||
Item ditem = (Item) Application.getCommonsCache().getx(ckey);
|
||||
if (ditem != null) {
|
||||
return DELETED_ITEM.equals(ditem.Name) ? null : ditem;
|
||||
}
|
||||
|
||||
Object[] o = Application.createQueryNoFilter(
|
||||
"select name,fullName,code,color from ClassificationData where itemId = ?")
|
||||
"select name,fullName,code,color,isHide from ClassificationData where itemId = ?")
|
||||
.setParameter(1, itemId)
|
||||
.unique();
|
||||
if (o != null) ditem = new Item((String) o[0], (String) o[1], (String) o[2], (String) o[3]);
|
||||
|
||||
if (o != null) ditem = new Item((String) o[0], (String) o[1], (String) o[2], (String) o[3], ObjectUtils.toBool(o[4]));
|
||||
// 可能已删除
|
||||
if (ditem == null) ditem = new Item(DELETED_ITEM, null, null, null);
|
||||
if (ditem == null) ditem = new Item(DELETED_ITEM, null, null, null, true);
|
||||
|
||||
Application.getCommonsCache().putx(ckey, ditem);
|
||||
return DELETED_ITEM.equals(ditem.Name) ? null : ditem;
|
||||
|
@ -188,18 +170,21 @@ public class ClassificationManager implements ConfigManager {
|
|||
}
|
||||
|
||||
// Bean
|
||||
static class Item implements Serializable {
|
||||
@Getter
|
||||
public static class Item implements Serializable {
|
||||
private static final long serialVersionUID = -1903227875771376652L;
|
||||
Item(String name, String fullName, String code, String color) {
|
||||
Item(String name, String fullName, String code, String color, boolean isHide) {
|
||||
this.Name = name;
|
||||
this.FullName = fullName;
|
||||
this.Code = code;
|
||||
this.Color = color;
|
||||
this.Hide = isHide;
|
||||
}
|
||||
|
||||
final String Name;
|
||||
final String FullName;
|
||||
final String Code;
|
||||
final String Color;
|
||||
final boolean Hide;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ public class EasyClassification extends EasyReference {
|
|||
JSONObject map = (JSONObject) super.wrapValue(value);
|
||||
if (map != null) {
|
||||
map.remove("entity");
|
||||
String color = ClassificationManager.instance.getColor((ID) value);
|
||||
if (color != null) map.put("color", color);
|
||||
ClassificationManager.Item item = ClassificationManager.instance.getItem((ID) value);
|
||||
if (item != null && item.getColor() != null) map.put("color", item.getColor());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
|
|
@ -22,11 +22,6 @@ import java.util.Objects;
|
|||
*/
|
||||
public class Axis {
|
||||
|
||||
/**
|
||||
* -- GETTER --
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Getter
|
||||
private Field field;
|
||||
private FormatSort sort;
|
||||
|
|
|
@ -9,6 +9,7 @@ package com.rebuild.core.service.general;
|
|||
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.configuration.general.ClassificationManager;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.privileges.UserFilters;
|
||||
|
@ -90,9 +91,12 @@ public class RecentlyUsedHelper {
|
|||
|
||||
// 是否符合条件
|
||||
if (checkFilter != null) {
|
||||
if (!QueryHelper.isMatchFilter(raw, checkFilter)) {
|
||||
continue;
|
||||
}
|
||||
if (!QueryHelper.isMatchFilter(raw, checkFilter)) continue;
|
||||
}
|
||||
// fix:4.0.2
|
||||
if (entityCode == EntityHelper.ClassificationData) {
|
||||
ClassificationManager.Item item = ClassificationManager.instance.getItem(raw);
|
||||
if (item == null || item.isHide()) continue;
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -163,13 +163,13 @@ public enum ConfigurationItem {
|
|||
|| SN.name().equals(name);
|
||||
}
|
||||
|
||||
private Object defaultVal;
|
||||
private Object defaultValue;
|
||||
|
||||
ConfigurationItem() {
|
||||
}
|
||||
|
||||
ConfigurationItem(Object defaultVal) {
|
||||
this.defaultVal = defaultVal;
|
||||
this.defaultValue = defaultVal;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,9 +178,9 @@ public enum ConfigurationItem {
|
|||
* @return
|
||||
*/
|
||||
public Object getDefaultValue() {
|
||||
if (defaultVal != null && defaultVal instanceof ConfigurationItem) {
|
||||
return ((ConfigurationItem) defaultVal).getDefaultValue();
|
||||
if (defaultValue != null && defaultValue instanceof ConfigurationItem) {
|
||||
return ((ConfigurationItem) defaultValue).getDefaultValue();
|
||||
}
|
||||
return defaultVal;
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -252,10 +252,11 @@ public class DataListWrapper {
|
|||
value = colorValue;
|
||||
|
||||
} else if (easyField.getDisplayType() == DisplayType.CLASSIFICATION) {
|
||||
String color = ClassificationManager.instance.getColor((ID) originValue);
|
||||
if (StringUtils.isNotBlank(color)) {
|
||||
ClassificationManager.Item item = ClassificationManager.instance.getItem((ID) originValue);
|
||||
if (item != null && StringUtils.isNotBlank(item.getColor())) {
|
||||
value = JSONUtils.toJSONObject(
|
||||
new String[]{ "text", "color" }, new Object[]{ value, color });
|
||||
new String[]{ "text", "color" },
|
||||
new Object[]{ value, item.getColor() });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*!
|
||||
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||
|
||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
package com.rebuild.core.support.setup;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.utils.OshiUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
||||
/**
|
||||
* Docker 安装
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 2025/4/10
|
||||
*/
|
||||
@Slf4j
|
||||
public class DockerInstaller extends Installer {
|
||||
|
||||
public DockerInstaller() {
|
||||
super(buildInstallProps());
|
||||
}
|
||||
|
||||
// FAKE
|
||||
static JSONObject buildInstallProps() {
|
||||
JSONObject installProps = new JSONObject();
|
||||
JSONObject databaseProps = JSONUtils.toJSONObject(
|
||||
new String[]{"dbName", "dbHost", "dbPort", "dbUser", "dbPassword"},
|
||||
new String[]{"rebuild40", "mysql", "3306", "root", "rebuildP4wd"});
|
||||
if (Application.devMode()) {
|
||||
databaseProps.put("dbHost", "localhost");
|
||||
databaseProps.put("dbUser", "rebuild");
|
||||
databaseProps.put("dbPassword", "rebuild");
|
||||
}
|
||||
installProps.put("databaseProps", databaseProps);
|
||||
return installProps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void install() throws Exception {
|
||||
this.installDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public void installAfter() {
|
||||
try {
|
||||
this.installClassificationAsync();
|
||||
} catch (Exception ex) {
|
||||
log.error("Error installing classification data", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public boolean isNeedInitialize() {
|
||||
if (!OshiUtils.isDockerEnv()) return false;
|
||||
if (!BooleanUtils.toBoolean(System.getProperty("initialize"))) return false;
|
||||
|
||||
if (Installer.isInstalled()) {
|
||||
return !isRbDatabase();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
147
src/main/java/com/rebuild/utils/OnlyOfficeUtils.java
Normal file
147
src/main/java/com/rebuild/utils/OnlyOfficeUtils.java
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*!
|
||||
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||
|
||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
package com.rebuild.utils;
|
||||
|
||||
import cn.devezhao.commons.CodecUtils;
|
||||
import cn.devezhao.commons.EncryptUtils;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.jwt.JWT;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.api.user.AuthTokenManager;
|
||||
import com.rebuild.core.RebuildException;
|
||||
import com.rebuild.core.support.ConfigurationItem;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.integration.QiniuCloud;
|
||||
import com.rebuild.web.admin.ConfigurationController;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.rebuild.core.support.ConfigurationItem.OnlyofficeJwt;
|
||||
import static com.rebuild.core.support.ConfigurationItem.OnlyofficeServer;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* @author devezhao
|
||||
* @since 2025/2/26
|
||||
*/
|
||||
public class OnlyOfficeUtils {
|
||||
|
||||
/**
|
||||
* OnlyOffice PDF
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
* @throws PdfConverterException
|
||||
*/
|
||||
public static Path convertPdf(Path path) throws IOException {
|
||||
final String ooServer = getOoServer();
|
||||
final String ooJwt = RebuildConfiguration.get(OnlyofficeJwt);
|
||||
|
||||
String filename = path.getFileName().toString();
|
||||
String filenameWithoutExt = filename.substring(0, filename.lastIndexOf("."));
|
||||
JSONObject document = new JSONObject(true);
|
||||
document.put("async", false);
|
||||
document.put("key", "key-" + EncryptUtils.toMD5Hex(filename));
|
||||
document.put("fileType", FileUtil.getSuffix(filename));
|
||||
document.put("outputType", "pdf");
|
||||
document.put("title", filenameWithoutExt);
|
||||
|
||||
String fileUrl = String.format("/filex/download/%s?_csrfToken=%s&temp=yes",
|
||||
filename, AuthTokenManager.generateCsrfToken(90));
|
||||
fileUrl = RebuildConfiguration.getHomeUrl(fileUrl);
|
||||
document.put("url", fileUrl);
|
||||
|
||||
// Token
|
||||
String tokenIfNeed = StringUtils.isBlank(ooJwt) ? null : JWT.create()
|
||||
.addPayloads(document)
|
||||
.setKey(ooJwt.getBytes(UTF_8))
|
||||
.sign();
|
||||
|
||||
Map<String, String> reqHeaders = new HashMap<>();
|
||||
reqHeaders.put("Content-Type", "application/json");
|
||||
if (tokenIfNeed != null) {
|
||||
reqHeaders.put("Authorization", "Bearer " + tokenIfNeed);
|
||||
}
|
||||
|
||||
String res = OkHttpUtils.post(ooServer + "/converter", document, reqHeaders);
|
||||
JSONObject resJson = JSON.parseObject(res);
|
||||
|
||||
String resFileUrl = resJson == null ? null : resJson.getString("fileUrl");
|
||||
if (resFileUrl != null) {
|
||||
File dest = new File(
|
||||
RebuildConfiguration.getFileOfTemp(null), filenameWithoutExt + ".pdf");
|
||||
OkHttpUtils.readBinary(resFileUrl, dest, null);
|
||||
if (dest.exists()) return dest.toPath();
|
||||
}
|
||||
|
||||
throw new RebuildException("Convert PDF fails (oo-ds) : " + resJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filepath
|
||||
* @return
|
||||
*/
|
||||
public static Object[] buildPreviewParams(String filepath) {
|
||||
final String ooJwt = RebuildConfiguration.get(OnlyofficeJwt);
|
||||
|
||||
final String filepathDecode = CodecUtils.urlDecode(filepath);
|
||||
String[] fs = filepathDecode.split("/");
|
||||
String filename = fs[fs.length - 1].split("\\?")[0];
|
||||
|
||||
JSONObject document = new JSONObject(true);
|
||||
document.put("fileType", FileUtil.getSuffix(filename));
|
||||
document.put("key", "key-" + EncryptUtils.toMD5Hex(filename));
|
||||
document.put("title", QiniuCloud.parseFileName(filename));
|
||||
// 外部地址
|
||||
if (CommonsUtils.isExternalUrl(filepath)) {
|
||||
document.put("url", filepath);
|
||||
} else {
|
||||
String fileUrl = String.format("/filex/download/%s?_csrfToken=%s",
|
||||
filepath,
|
||||
AuthTokenManager.generateCsrfToken(90));
|
||||
fileUrl = RebuildConfiguration.getHomeUrl(fileUrl);
|
||||
document.put("url", fileUrl);
|
||||
}
|
||||
|
||||
// Token
|
||||
String tokenIfNeed = StringUtils.isBlank(ooJwt) ? null : JWT.create()
|
||||
.setPayload("document", document)
|
||||
.setKey(ooJwt.getBytes(UTF_8))
|
||||
.sign();
|
||||
|
||||
return new Object[]{document, tokenIfNeed};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static String getOoServer() {
|
||||
String ooServer = RebuildConfiguration.get(OnlyofficeServer);
|
||||
Assert.notNull(ooServer, "[OnlyofficeServer] is not set");
|
||||
|
||||
if (ooServer.endsWith("/")) ooServer = ooServer.substring(0, ooServer.length() - 1);
|
||||
return ooServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static boolean isUseOoPreview() {
|
||||
if (RebuildConfiguration.get(OnlyofficeServer) == null) return false;
|
||||
return StringUtils.contains(
|
||||
RebuildConfiguration.get(ConfigurationItem.PortalOfficePreviewUrl),
|
||||
ConfigurationController.OO_PREVIEW_URL);
|
||||
}
|
||||
}
|
|
@ -7,11 +7,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.utils;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.jwt.JWT;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.rebuild.api.user.AuthTokenManager;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.support.ConfigurationItem;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
|
@ -26,12 +21,8 @@ import org.jsoup.nodes.Element;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.rebuild.core.support.ConfigurationItem.OnlyofficeJwt;
|
||||
import static com.rebuild.core.support.ConfigurationItem.OnlyofficeServer;
|
||||
|
||||
/**
|
||||
|
@ -53,12 +44,12 @@ public class PdfConverter {
|
|||
* @throws PdfConverterException
|
||||
*/
|
||||
public static Path convert(Path path, String type) throws PdfConverterException {
|
||||
// v4.0
|
||||
if (TYPE_PDF.equalsIgnoreCase(type) && RebuildConfiguration.get(OnlyofficeServer) != null) {
|
||||
return ooConvertPdf(path);
|
||||
}
|
||||
|
||||
try {
|
||||
// v4.0
|
||||
if (TYPE_PDF.equalsIgnoreCase(type) && RebuildConfiguration.get(OnlyofficeServer) != null) {
|
||||
return OnlyOfficeUtils.convertPdf(path);
|
||||
}
|
||||
|
||||
return convert(path, type, true);
|
||||
} catch (IOException e) {
|
||||
throw new PdfConverterException(e);
|
||||
|
@ -165,47 +156,5 @@ public class PdfConverter {
|
|||
FileUtils.writeStringToFile(sourceHtml, template.html(), "UTF-8");
|
||||
}
|
||||
|
||||
/**
|
||||
* OnlyOffice PDF
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
* @throws PdfConverterException
|
||||
*/
|
||||
public static Path ooConvertPdf(Path path) throws PdfConverterException {
|
||||
final String ooServer = RebuildConfiguration.get(OnlyofficeServer);
|
||||
final String ooJwt = RebuildConfiguration.get(OnlyofficeJwt);
|
||||
|
||||
String filename = path.getFileName().toString();
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
document.put("key", "key-" + filename.hashCode());
|
||||
document.put("filetype", FileUtil.getSuffix(filename));
|
||||
document.put("outputtype", "pdf");
|
||||
document.put("async", "false");
|
||||
document.put("title", filename);
|
||||
|
||||
String fileUrl = String.format("/filex/download/%s?_csrfToken=%s&temp=yes",
|
||||
filename, AuthTokenManager.generateCsrfToken(90));
|
||||
fileUrl = RebuildConfiguration.getHomeUrl(fileUrl);
|
||||
document.put("url", fileUrl);
|
||||
|
||||
// Token
|
||||
String token = JWT.create()
|
||||
.setPayload("document", document)
|
||||
.setExpiresAt(CalendarUtils.add(15, Calendar.MINUTE))
|
||||
.setKey(ooJwt.getBytes())
|
||||
.sign();
|
||||
|
||||
Map<String, String> httpHeaders = new HashMap<>();
|
||||
httpHeaders.put("Content-Type", "application/json");
|
||||
httpHeaders.put("Authorization", "Bearer " + token);
|
||||
|
||||
String res;
|
||||
try {
|
||||
res = OkHttpUtils.post(ooServer + "/converter", JSON.toJSON(document), httpHeaders);
|
||||
} catch (IOException e) {
|
||||
throw new PdfConverterException(e);
|
||||
}
|
||||
throw new UnsupportedOperationException("TODO:" + res);
|
||||
}
|
||||
}
|
|
@ -276,7 +276,8 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
|| requestUri.startsWith("/rbmob/env")
|
||||
|| requestUri.startsWith("/h5app-download")
|
||||
|| requestUri.startsWith("/apiman/")
|
||||
|| requestUri.startsWith("/commons/frontjs/use-frontjs");
|
||||
|| requestUri.startsWith("/commons/frontjs/use-frontjs")
|
||||
|| requestUri.startsWith("/commons/file-preview");
|
||||
}
|
||||
|
||||
private boolean isHtmlRequest(String requestUri, HttpServletRequest request) {
|
||||
|
|
|
@ -71,6 +71,8 @@ public class ConfigurationController extends BaseController {
|
|||
public static final String ETAG_DIMGLOGOTIME = "dimgLogoTime";
|
||||
public static final String ETAG_DIMGBGIMGTIME = "dimgBgimgTime";
|
||||
|
||||
public static final String OO_PREVIEW_URL = "/commons/file-preview?src=";
|
||||
|
||||
@GetMapping("systems")
|
||||
public ModelAndView pageSystems() {
|
||||
ModelAndView mv = createModelAndView("/admin/system-cfg");
|
||||
|
@ -101,8 +103,10 @@ public class ConfigurationController extends BaseController {
|
|||
}
|
||||
|
||||
String dPortalOfficePreviewUrl = defaultIfBlank(data, ConfigurationItem.PortalOfficePreviewUrl);
|
||||
if (StringUtils.isNotBlank(dPortalOfficePreviewUrl) && !RegexUtils.isUrl(dPortalOfficePreviewUrl)) {
|
||||
return RespBody.errorl("无效文档预览服务地址");
|
||||
if (StringUtils.isNotBlank(dPortalOfficePreviewUrl)) {
|
||||
boolean valid = RegexUtils.isUrl(dPortalOfficePreviewUrl)
|
||||
|| dPortalOfficePreviewUrl.contains(OO_PREVIEW_URL);
|
||||
if (!valid) return RespBody.errorl("无效文档预览服务地址");
|
||||
}
|
||||
|
||||
// 验证数字参数
|
||||
|
|
|
@ -7,15 +7,11 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
|
||||
package com.rebuild.web.commons;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.commons.CodecUtils;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.jwt.JWT;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.rebuild.api.user.AuthTokenManager;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.core.support.integration.QiniuCloud;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.utils.AppUtils;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.utils.OnlyOfficeUtils;
|
||||
import com.rebuild.web.BaseController;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
@ -23,11 +19,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.rebuild.core.support.ConfigurationItem.OnlyofficeJwt;
|
||||
import static com.rebuild.core.support.ConfigurationItem.OnlyofficeServer;
|
||||
|
||||
/**
|
||||
|
@ -41,42 +33,24 @@ import static com.rebuild.core.support.ConfigurationItem.OnlyofficeServer;
|
|||
@Controller
|
||||
public class FilePreviewer extends BaseController {
|
||||
|
||||
@GetMapping("/filex/preview/**")
|
||||
@GetMapping("/commons/file-preview")
|
||||
public ModelAndView ooPreview(HttpServletRequest request) {
|
||||
final String ooServer = RebuildConfiguration.get(OnlyofficeServer);
|
||||
final String ooJwt = RebuildConfiguration.get(OnlyofficeJwt);
|
||||
|
||||
final String filepathRaw = request.getRequestURI().split("/filex/preview/")[1];
|
||||
final String filepath = CodecUtils.urlDecode(filepathRaw);
|
||||
String[] fs = filepath.split("/");
|
||||
String filename = fs[fs.length - 1];
|
||||
|
||||
Map<String, Object> document = new HashMap<>();
|
||||
document.put("fileType", FileUtil.getSuffix(filename));
|
||||
document.put("key", "key-" + filename.hashCode());
|
||||
document.put("title", QiniuCloud.parseFileName(filename));
|
||||
// 外部地址
|
||||
if (CommonsUtils.isExternalUrl(filepath)) {
|
||||
document.put("url", filepath);
|
||||
} else {
|
||||
String fileUrl = String.format("/filex/download/%s?_csrfToken=%s",
|
||||
filepathRaw,
|
||||
AuthTokenManager.generateCsrfToken(90));
|
||||
fileUrl = RebuildConfiguration.getHomeUrl(fileUrl);
|
||||
document.put("url", fileUrl);
|
||||
}
|
||||
|
||||
// Token
|
||||
String token = JWT.create()
|
||||
.setPayload("document", document)
|
||||
.setExpiresAt(CalendarUtils.add(15, Calendar.MINUTE))
|
||||
.setKey(ooJwt.getBytes())
|
||||
.sign();
|
||||
String src = getParameterNotNull(request, "src");
|
||||
Object[] ps = OnlyOfficeUtils.buildPreviewParams(src);
|
||||
|
||||
ModelAndView mv = createModelAndView("/common/oo-preview");
|
||||
mv.getModel().put(OnlyofficeServer.name(), ooServer);
|
||||
mv.getModel().put("_DocumentConfig", JSON.toJSON(document));
|
||||
mv.getModel().put("_Token", token);
|
||||
mv.getModel().put(OnlyofficeServer.name(), OnlyOfficeUtils.getOoServer());
|
||||
mv.getModel().put("_DocumentConfig", ps[0]);
|
||||
mv.getModel().put("_Token", ps[1]);
|
||||
|
||||
String[] user = new String[]{"REBUILD", "REBUILD"};
|
||||
ID userid = AppUtils.getRequestUser(request);
|
||||
if (userid != null) {
|
||||
user = new String[]{userid.toString(), UserHelper.getName(userid)};
|
||||
}
|
||||
mv.getModel().put("_User",
|
||||
JSONUtils.toJSONObject(new String[]{"id", "name"}, user));
|
||||
|
||||
return mv;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,10 +87,10 @@ public class RecentlyUsedSearchController extends BaseController {
|
|||
label = FieldValueHelper.NO_LABEL_PREFIX + id.toLiteral().toUpperCase();
|
||||
}
|
||||
|
||||
String code = isClazz ? ClassificationManager.instance.getCode(id) : null;
|
||||
ClassificationManager.Item item = isClazz ? ClassificationManager.instance.getItem(id) : null;
|
||||
data.add(JSONUtils.toJSONObject(
|
||||
new String[] { "id", "text", "code" },
|
||||
new Object[] { id, label, code }));
|
||||
new Object[] { id, label, item == null ? null : item.getCode() }));
|
||||
}
|
||||
|
||||
if (useGroupName != null) {
|
||||
|
|
|
@ -38,6 +38,7 @@ import com.rebuild.core.support.i18n.Language;
|
|||
import com.rebuild.utils.AppUtils;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.utils.OnlyOfficeUtils;
|
||||
import com.rebuild.utils.PdfConverter;
|
||||
import com.rebuild.utils.RbAssert;
|
||||
import com.rebuild.web.BaseController;
|
||||
|
@ -175,7 +176,10 @@ public class ReportsController extends BaseController {
|
|||
|
||||
} else {
|
||||
// 直接预览
|
||||
boolean forcePreview = isHtml || isPdf || getBoolParameter(request, "preview");
|
||||
boolean forcePreview = isHtml || getBoolParameter(request, "preview");
|
||||
if (!forcePreview) {
|
||||
if (isPdf && !OnlyOfficeUtils.isUseOoPreview()) forcePreview = true;
|
||||
}
|
||||
FileDownloader.downloadTempFile(response, output, fileName, forcePreview);
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<th:block th:if="${commercial > 1}">
|
||||
<script th:src="@{/assets/js/general/rb-easyaction.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/general/rb-sop.js}" type="text/babel"></script>
|
||||
</th:block>
|
||||
<script th:src="@{/assets/js/general/rb-forms.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/general/rb-forms.append.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/general/rb-forms.protable.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/general/rb-datalist.common.js}" type="text/babel"></script>
|
||||
<th:block if="${commercial > 1}">
|
||||
<script th:src="@{/assets/js/general/rb-easyaction.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/general/rb-sop.js}" type="text/babel"></script>
|
||||
</th:block>
|
||||
<script th:src="@{/assets/js/general/rb-approval.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/general/rb-advfilter.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/general/rb-assignshare.js}" type="text/babel"></script>
|
||||
|
|
|
@ -347,8 +347,8 @@ td.task-user > .avatar img {
|
|||
|
||||
.ps-toolbar .J_view > .btn > .icon,
|
||||
.ps-toolbar .J_sorts > .btn > .icon {
|
||||
font-size: 16px;
|
||||
top: 10px;
|
||||
font-size: 17px;
|
||||
top: 11.5px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
|
|
@ -177,7 +177,8 @@ $(document).ready(() => {
|
|||
setTimeout(() => {
|
||||
RbGritter.create(
|
||||
<RF>
|
||||
<p>{$L('REBUILD 已成功更新至 %s 版本', rb.ver)}</p>
|
||||
{$L('REBUILD 已成功更新至 %s 版本', rb.ver)}
|
||||
<br />
|
||||
<a href={`https://getrebuild.com/docs/dev/changelog?ver=${rb.ver}`} target="_blank" className="text-white link">
|
||||
{$L('查看详情')}
|
||||
</a>
|
||||
|
|
|
@ -131,7 +131,7 @@ class FeedsList extends React.Component {
|
|||
|
||||
<a>{item.createdBy[1]}</a>
|
||||
<p className="text-muted fs-12 m-0">
|
||||
<DateShow date={item.createdOn} />
|
||||
<DateShow date={item.createdOn} showOrigin />
|
||||
{item.createdOn !== item.modifiedOn && (
|
||||
<span className="text-warning ml-1" title={`${$L('修改于')} ${item.modifiedOn}`}>
|
||||
({$L('已修改')})
|
||||
|
|
|
@ -198,8 +198,11 @@ class RbPreview extends React.Component {
|
|||
if (this._isDoc(fileName)) {
|
||||
const isPdfType = fileName.toLowerCase().endsWith('.pdf')
|
||||
const setPreviewUrl = function (url, fullUrl) {
|
||||
let previewUrl = (rb._officePreviewUrl || 'https://view.officeapps.live.com/op/embed.aspx?src=') + $encode(url)
|
||||
if (isPdfType) {
|
||||
let previewUrl = rb._officePreviewUrl || 'https://view.officeapps.live.com/op/embed.aspx?src='
|
||||
if (previewUrl.includes('/commons/file-preview?src=')) previewUrl = rb.baseUrl + previewUrl
|
||||
previewUrl += $encode(url)
|
||||
|
||||
if (isPdfType && !previewUrl.includes('/commons/file-preview?src=')) {
|
||||
if ($.browser.mobile) {
|
||||
previewUrl = `${rb.baseUrl}/assets/lib/pdfjs/web/viewer.html?src=${$encode(url)}`
|
||||
} else {
|
||||
|
|
|
@ -2119,7 +2119,10 @@ class RbFormReference extends RbFormElement {
|
|||
// 新建记录时触发回填
|
||||
const props = this.props
|
||||
if (this._isNew && props.value && props.value.id) {
|
||||
setTimeout(() => this.triggerAutoFillin(props.value.id), 200)
|
||||
// fix: 4.0.2 #IC0GPI 复制时无需回填
|
||||
if (props._disableAutoFillin !== true) {
|
||||
setTimeout(() => this.triggerAutoFillin(props.value.id), 200)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ class ProTable extends React.Component {
|
|||
this._addLine(model, index)
|
||||
}
|
||||
|
||||
_addLine(model, index) {
|
||||
_addLine(model, index, _disableAutoFillin) {
|
||||
// 明细未配置或出错
|
||||
if (!model) {
|
||||
if (this.state.hasError) RbHighbar.create(this.state.hasError)
|
||||
|
@ -296,7 +296,7 @@ class ProTable extends React.Component {
|
|||
const FORM = (
|
||||
<InlineForm entity={entityName} id={model.id} rawModel={model} $$$parent={this} $$$main={this.props.$$$main} key={lineKey} ref={ref} _componentDidUpdate={() => this._componentDidUpdate()}>
|
||||
{model.elements.map((item) => {
|
||||
return detectElement({ ...item, colspan: 4 })
|
||||
return detectElement({ ...item, colspan: 4, _disableAutoFillin: _disableAutoFillin === true })
|
||||
})}
|
||||
</InlineForm>
|
||||
)
|
||||
|
@ -320,7 +320,7 @@ class ProTable extends React.Component {
|
|||
|
||||
// force New
|
||||
delete data.metadata.id
|
||||
this._formdataRebuild(data, (res) => this._addLine(res.data, index))
|
||||
this._formdataRebuild(data, (res) => this._addLine(res.data, index, true))
|
||||
}
|
||||
|
||||
removeLine(lineKey) {
|
||||
|
|
|
@ -470,7 +470,6 @@ class SelectReport extends React.Component {
|
|||
<div>
|
||||
<ul className="list-unstyled">
|
||||
{(this.state.reports || []).map((item) => {
|
||||
rb._officePreviewUrl = 111
|
||||
const reportUrl = `${rb.baseUrl}/app/${this.props.entity}/report/export?report=${item.id}&record=${this.props.id}`
|
||||
const showPdf = (item.outputType || '').includes('pdf')
|
||||
const showHtml = item.outputType !== 'html5' && (item.outputType || '').includes('html')
|
||||
|
|
|
@ -747,7 +747,7 @@ const RbViewPage = {
|
|||
$('.J_sharingList').parent().remove()
|
||||
}
|
||||
} else if (k === 'createdOn' || k === 'modifiedOn') {
|
||||
renderRbcomp(<DateShow date={v} />, $el[0])
|
||||
renderRbcomp(<DateShow date={v} showOrigin />, $el)
|
||||
} else {
|
||||
$(`<span>${v}</span>`).appendTo($el.empty())
|
||||
}
|
||||
|
@ -933,6 +933,7 @@ const RbViewPage = {
|
|||
// 记录转换
|
||||
initTransform(config) {
|
||||
config.forEach((item) => {
|
||||
if ($isSysMask(item.transName)) return // v4.0.2
|
||||
const $item = $(`<a class="dropdown-item"><i class="icon zmdi zmdi-${item.icon}"></i>${item.transName || item.entityLabel}</a>`)
|
||||
$item.on('click', () => renderRbcomp(<DlgTransform {...item} sourceRecord={this.__id} />))
|
||||
$('.J_transform .dropdown-divider').before($item)
|
||||
|
|
|
@ -824,8 +824,20 @@ const UserShow = function (props) {
|
|||
}
|
||||
|
||||
// ~~ 日期显示
|
||||
const DateShow = function ({ date, title }) {
|
||||
return date ? <span title={title || date}>{$fromNow(date)}</span> : null
|
||||
class DateShow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {}
|
||||
}
|
||||
|
||||
render() {
|
||||
const date = this.props.date
|
||||
return date ? (
|
||||
<span title={this.props.title || date} onClick={() => this.setState({ _showOrigin: !this.state._showOrigin })}>
|
||||
{this.props.showOrigin && this.state._showOrigin ? date : $fromNow(date)}
|
||||
</span>
|
||||
) : null
|
||||
}
|
||||
}
|
||||
|
||||
// ~~ 记录选择器
|
||||
|
|
|
@ -1203,7 +1203,7 @@ var $isFullUrl = function (url) {
|
|||
|
||||
// Mask prefix `SYS `
|
||||
var $isSysMask = function (label) {
|
||||
return label && (label.startsWith('SYS ') || label.contains('.SYS ')) && location.href.indexOf('/admin/') === -1
|
||||
return label && (label.startsWith('SYS ') || label.contains('.SYS ') || label.contains('#SYS')) && location.href.indexOf('/admin/') === -1
|
||||
}
|
||||
|
||||
// 颜色
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
function setZoom(z) {
|
||||
zoom += z
|
||||
if (zoom < 20) zoom = 20
|
||||
if (zoom > 500) zoom = 500
|
||||
if (zoom > 1000) zoom = 1000
|
||||
|
||||
$('.zoom > span').text(zoom + '%')
|
||||
$node.css('transform', `scale(${zoom / 100})`)
|
||||
|
|
|
@ -17,14 +17,18 @@
|
|||
<div id="viewer" style="width: 100%; height: 100%"></div>
|
||||
<script th:src="${OnlyofficeServer + '/web-apps/apps/api/documents/api.js'}"></script>
|
||||
<script>
|
||||
// https://api.onlyoffice.com/docs/docs-api/usage-api/config/
|
||||
new DocsAPI.DocEditor('viewer', {
|
||||
editorConfig: {
|
||||
mode: 'view',
|
||||
lang: 'zh',
|
||||
user: { id: '001-0000000000000000', name: 'REBUILD' },
|
||||
toolbar: false,
|
||||
menu: false,
|
||||
user: [(${_User})],
|
||||
},
|
||||
token: '[[${_Token}]]',
|
||||
document: [(${_DocumentConfig})],
|
||||
token: '[[${_Token ?:""}]]',
|
||||
type: 'embedded',
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="plan-boxes" data-fullcontent="115"></div>
|
||||
<div id="plan-boxes" data-fullcontent="116"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Add table
Reference in a new issue