mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
commit
976a4d48f5
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit c7521f5df907ed9500d65dd8487b6517cfd7b21d
|
||||
Subproject commit e26090af686fd25f9071b1776b303b3e8ae9491a
|
2
pom.xml
2
pom.xml
|
@ -297,7 +297,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.devezhao</groupId>
|
||||
<artifactId>persist4j</artifactId>
|
||||
<version>1.7.6</version>
|
||||
<version>1.7.7</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba</groupId>
|
||||
|
|
|
@ -78,7 +78,7 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
|
|||
/**
|
||||
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
|
||||
*/
|
||||
public static final int BUILD = 3060001;
|
||||
public static final int BUILD = 3060002;
|
||||
|
||||
static {
|
||||
// Driver for DB
|
||||
|
|
|
@ -21,9 +21,12 @@ import com.rebuild.core.metadata.MetadataHelper;
|
|||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
@ -50,16 +53,16 @@ public class TransformManager implements ConfigManager {
|
|||
*/
|
||||
public JSONArray getTransforms(String sourceEntity, ID user) {
|
||||
JSONArray data = new JSONArray();
|
||||
for (ConfigBean c : getRawTransforms(sourceEntity)) {
|
||||
JSONObject config = (JSONObject) c.getJSON("config");
|
||||
for (ConfigBean cb : getRawTransforms(sourceEntity)) {
|
||||
JSONObject config = (JSONObject) cb.getJSON("config");
|
||||
// 过滤尚未配置或禁用的
|
||||
if (config == null || c.getBoolean("disabled")) continue;
|
||||
if (config == null || cb.getBoolean("disabled")) continue;
|
||||
|
||||
// 无字段映射
|
||||
JSONObject fieldsMapping = config.getJSONObject("fieldsMapping");
|
||||
if (fieldsMapping == null || fieldsMapping.isEmpty()) continue;
|
||||
|
||||
String target = c.getString("target");
|
||||
String target = cb.getString("target");
|
||||
Entity targetEntity = MetadataHelper.getEntity(target);
|
||||
|
||||
if (targetEntity.getMainEntity() == null) {
|
||||
|
@ -74,8 +77,8 @@ public class TransformManager implements ConfigManager {
|
|||
}
|
||||
|
||||
JSONObject item = EasyMetaFactory.toJSON(targetEntity);
|
||||
item.put("transid", c.getID("id"));
|
||||
item.put("transName", c.getString("name")); // v3.6 重启用
|
||||
item.put("transid", cb.getID("id"));
|
||||
item.put("transName", cb.getString("name"));
|
||||
item.put("previewMode", config.getIntValue("transformMode") == 2);
|
||||
data.add(item);
|
||||
}
|
||||
|
@ -104,9 +107,7 @@ public class TransformManager implements ConfigManager {
|
|||
public List<ConfigBean> getRawTransforms(String sourceEntity) {
|
||||
final String cKey = "TransformManager31-" + sourceEntity;
|
||||
Object cached = Application.getCommonsCache().getx(cKey);
|
||||
if (cached != null) {
|
||||
return (List<ConfigBean>) cached;
|
||||
}
|
||||
if (cached != null) return (List<ConfigBean>) cached;
|
||||
|
||||
Object[][] array = Application.createQueryNoFilter(
|
||||
"select belongEntity,targetEntity,configId,config,isDisabled,name from TransformConfig where belongEntity = ?")
|
||||
|
@ -131,6 +132,7 @@ public class TransformManager implements ConfigManager {
|
|||
entries.add(entry);
|
||||
}
|
||||
|
||||
sortByName(entries);
|
||||
Application.getCommonsCache().putx(cKey, entries);
|
||||
return entries;
|
||||
}
|
||||
|
@ -184,10 +186,21 @@ public class TransformManager implements ConfigManager {
|
|||
}
|
||||
}
|
||||
|
||||
sortByName(imports);
|
||||
WEAK_CACHED.put(targetEntity, imports);
|
||||
return imports;
|
||||
}
|
||||
|
||||
// v3.6 排序
|
||||
private void sortByName(List<ConfigBean> list) {
|
||||
if (list == null || list.isEmpty()) return;
|
||||
|
||||
Comparator<Object> comparator = Collator.getInstance(Locale.CHINESE);
|
||||
list.sort((o1, o2) -> comparator.compare(
|
||||
org.apache.commons.lang3.ObjectUtils.defaultIfNull(o1.getString("name"), ""),
|
||||
org.apache.commons.lang3.ObjectUtils.defaultIfNull(o2.getString("name"), "")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clean(Object cfgid) {
|
||||
final String cKey = "TransformManager31-" + getBelongEntity((ID) cfgid);
|
||||
|
|
|
@ -438,27 +438,37 @@ public class Field2Schema extends SetUser {
|
|||
meta.setString("displayType", toType.name());
|
||||
Application.getCommonsService().update(meta, false);
|
||||
|
||||
Dialect dialect = Application.getPersistManagerFactory().getDialect();
|
||||
final Table table = new Table(field.getOwnEntity(), dialect);
|
||||
StringBuilder ddl = new StringBuilder();
|
||||
table.generateFieldDDL(field, ddl);
|
||||
|
||||
String alterSql = String.format("alter table `%s` change column `%s` ",
|
||||
field.getOwnEntity().getPhysicalName(), field.getPhysicalName());
|
||||
alterSql += ddl.toString().trim();
|
||||
// 类型生效
|
||||
DynamicMetadataContextHolder.setSkipLanguageRefresh();
|
||||
MetadataHelper.getMetadataFactory().refresh();
|
||||
field = MetadataHelper.getField(field.getOwnEntity().getName(), field.getName());
|
||||
|
||||
String alterTypeSql = null;
|
||||
try {
|
||||
Application.getSqlExecutor().executeBatch(new String[]{alterSql}, DDL_TIMEOUT);
|
||||
Dialect dialect = Application.getPersistManagerFactory().getDialect();
|
||||
final Table table = new Table(field.getOwnEntity(), dialect);
|
||||
StringBuilder ddl = new StringBuilder();
|
||||
table.generateFieldDDL(field, ddl);
|
||||
|
||||
alterTypeSql = String.format("alter table `%s` change column `%s` ",
|
||||
field.getOwnEntity().getPhysicalName(), field.getPhysicalName());
|
||||
alterTypeSql += ddl.toString().trim().replace(" ", "");
|
||||
|
||||
Application.getSqlExecutor().executeBatch(new String[]{alterTypeSql}, DDL_TIMEOUT);
|
||||
log.info("Cast field type : {}", alterTypeSql);
|
||||
|
||||
} catch (Throwable ex) {
|
||||
// 还原
|
||||
meta.setString("displayType", EasyMetaFactory.getDisplayType(field).name());
|
||||
Application.getCommonsService().update(meta, false);
|
||||
|
||||
log.error("DDL ERROR : \n" + alterSql, ex);
|
||||
log.error("DDL ERROR : \n" + alterTypeSql, ex);
|
||||
throw new MetadataModificationException(ThrowableUtils.getRootCause(ex).getLocalizedMessage());
|
||||
} finally {
|
||||
MetadataHelper.getMetadataFactory().refresh();
|
||||
DynamicMetadataContextHolder.isSkipLanguageRefresh(true);
|
||||
}
|
||||
|
||||
MetadataHelper.getMetadataFactory().refresh();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -217,6 +217,7 @@ public class DataReportManager implements ConfigManager {
|
|||
else if (fileName.endsWith(".doc")) name += ".doc";
|
||||
else if (fileName.endsWith(".xlsx")) name += ".xlsx";
|
||||
else if (fileName.endsWith(".xls")) name += ".xls";
|
||||
else if (fileName.endsWith(".csv")) name += ".csv";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,8 @@ import static com.rebuild.core.service.datareport.TemplateExtractor33.NROW_PREFI
|
|||
@Slf4j
|
||||
public class EasyExcelGenerator extends SetUser {
|
||||
|
||||
final protected static String MDATA_KEY = ".";
|
||||
final protected static String REFKEY_RECORD_MAIN = ".";
|
||||
final protected static String REFKEY_LIST = REFKEY_RECORD_MAIN + "36LIST";
|
||||
|
||||
protected File templateFile;
|
||||
protected Integer writeSheetAt = null;
|
||||
|
@ -110,10 +111,11 @@ public class EasyExcelGenerator extends SetUser {
|
|||
Map<String, List<Map<String, Object>>> datas = buildData();
|
||||
if (datas.isEmpty()) throw new DefinedException(Language.L("暂无数据"));
|
||||
|
||||
// 记录模板-主记录
|
||||
Map<String, Object> main = null;
|
||||
if (datas.containsKey(MDATA_KEY)) {
|
||||
main = datas.get(MDATA_KEY).get(0);
|
||||
datas.remove(MDATA_KEY);
|
||||
if (datas.containsKey(REFKEY_RECORD_MAIN)) {
|
||||
main = datas.get(REFKEY_RECORD_MAIN).get(0);
|
||||
datas.remove(REFKEY_RECORD_MAIN);
|
||||
}
|
||||
|
||||
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
|
||||
|
@ -124,18 +126,20 @@ public class EasyExcelGenerator extends SetUser {
|
|||
.registerWriteHandler(new FormulaCellWriteHandler())
|
||||
.build();
|
||||
|
||||
// 一个列表
|
||||
if (datas.size() == 1) {
|
||||
Object o = datas.values().iterator().next();
|
||||
excelWriter.fill(o, fillConfig, writeSheet);
|
||||
int datasLen = datas.size();
|
||||
boolean useRefKeys = datasLen > 1;
|
||||
if (datasLen == 1) {
|
||||
String refKey = datas.keySet().iterator().next();
|
||||
useRefKeys = refKey.startsWith("$");
|
||||
}
|
||||
// fix: v3.6 多个列表
|
||||
else if (datas.size() > 1) {
|
||||
|
||||
// fix: v3.6 多个列表 $前缀
|
||||
if (useRefKeys) {
|
||||
for (Map.Entry<String, List<Map<String, Object>>> e : datas.entrySet()) {
|
||||
final String refKey = NROW_PREFIX2 + e.getKey().substring(1);
|
||||
final List<Map<String, Object>> refDatas = e.getValue();
|
||||
|
||||
List<Map<String, Object>> refDatasNew = new ArrayList<>();
|
||||
List<Map<String, Object>> refDatas2New = new ArrayList<>();
|
||||
for (Map<String, Object> map : refDatas) {
|
||||
Map<String, Object> mapNew = new HashMap<>();
|
||||
for (Map.Entry<String, Object> ee : map.entrySet()) {
|
||||
|
@ -143,12 +147,17 @@ public class EasyExcelGenerator extends SetUser {
|
|||
if (keyNew.startsWith("$") || keyNew.startsWith(".")) keyNew = keyNew.substring(1);
|
||||
mapNew.put(keyNew, ee.getValue());
|
||||
}
|
||||
refDatasNew.add(mapNew);
|
||||
refDatas2New.add(mapNew);
|
||||
}
|
||||
|
||||
excelWriter.fill(new FillWrapper(refKey, refDatasNew), fillConfig, writeSheet);
|
||||
excelWriter.fill(new FillWrapper(refKey, refDatas2New), fillConfig, writeSheet);
|
||||
}
|
||||
}
|
||||
// 一个列表/列表模板
|
||||
else if (datasLen == 1) {
|
||||
Object datas35 = datas.values().iterator().next();
|
||||
excelWriter.fill(datas35, fillConfig, writeSheet);
|
||||
}
|
||||
|
||||
// 主记录
|
||||
if (main != null) {
|
||||
|
@ -253,7 +262,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
Assert.notNull(record, "No record found : " + recordId);
|
||||
|
||||
Map<String, Object> d = buildData(record, varsMapOfMain);
|
||||
datas.put(MDATA_KEY, Collections.singletonList(d));
|
||||
datas.put(REFKEY_RECORD_MAIN, Collections.singletonList(d));
|
||||
}
|
||||
|
||||
// 明细
|
||||
|
@ -274,7 +283,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
detailList.add(buildData(c, varsMapOfDetail));
|
||||
phNumber++;
|
||||
}
|
||||
datas.put(MDATA_KEY + "detail", detailList);
|
||||
datas.put(REFKEY_RECORD_MAIN + "detail", detailList);
|
||||
}
|
||||
|
||||
// 审批
|
||||
|
@ -293,7 +302,7 @@ public class EasyExcelGenerator extends SetUser {
|
|||
approvalList.add(buildData(c, varsMapOfApproval));
|
||||
phNumber++;
|
||||
}
|
||||
datas.put(MDATA_KEY + "approval", approvalList);
|
||||
datas.put(REFKEY_RECORD_MAIN + "approval", approvalList);
|
||||
}
|
||||
|
||||
return datas;
|
||||
|
|
|
@ -65,7 +65,7 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
final private List<ID> recordIdMultiple;
|
||||
|
||||
private Set<String> inShapeVars;
|
||||
private Map<String, Object> mdataHolder;
|
||||
private Map<String, Object> recordMainHolder;
|
||||
|
||||
protected EasyExcelGenerator33(File templateFile, ID recordId) {
|
||||
super(templateFile, recordId);
|
||||
|
@ -98,9 +98,9 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
String refKey = null;
|
||||
if (varName.startsWith(NROW_PREFIX) || varName.startsWith(NROW_PREFIX2)) {
|
||||
if (varName.startsWith(APPROVAL_PREFIX) || varName.startsWith(APPROVAL_PREFIX2)) {
|
||||
refKey = APPROVAL_PREFIX;
|
||||
refKey = varName.startsWith(NROW_PREFIX) ? APPROVAL_PREFIX : APPROVAL_PREFIX2;
|
||||
} else if (varName.startsWith(DETAIL_PREFIX) || varName.startsWith(DETAIL_PREFIX2)) {
|
||||
refKey = DETAIL_PREFIX;
|
||||
refKey = varName.startsWith(NROW_PREFIX) ? DETAIL_PREFIX : DETAIL_PREFIX2;
|
||||
} else {
|
||||
// 在客户中导出订单(下列 AccountId 为订单中引用客户的引用字段)
|
||||
// .AccountId.SalesOrder.SalesOrderName or $AccountId$SalesOrder$SalesOrderName
|
||||
|
@ -120,7 +120,9 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
}
|
||||
|
||||
// 占位字段
|
||||
if (TemplateExtractor33.isPlaceholder(varName)) continue;
|
||||
if (TemplateExtractor33.isPlaceholder(varName)) {
|
||||
continue;
|
||||
}
|
||||
// 无效字段
|
||||
if (fieldName == null) {
|
||||
log.warn("Invalid field `{}` in template : {}", e.getKey(), templateFile);
|
||||
|
@ -155,14 +157,15 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
Assert.notNull(record, "No record found : " + recordId);
|
||||
|
||||
Map<String, Object> d = buildData(record, varsMapOfMain);
|
||||
datas.put(MDATA_KEY, Collections.singletonList(d));
|
||||
mdataHolder = d;
|
||||
datas.put(REFKEY_RECORD_MAIN, Collections.singletonList(d));
|
||||
recordMainHolder = d;
|
||||
}
|
||||
|
||||
// 相关记录(含明细、审批)
|
||||
for (Map.Entry<String, List<String>> e : fieldsOfRefs.entrySet()) {
|
||||
final String refKey = e.getKey();
|
||||
final boolean isApproval = refKey.startsWith(APPROVAL_PREFIX);
|
||||
final boolean isApproval = refKey.startsWith(APPROVAL_PREFIX) || refKey.startsWith(APPROVAL_PREFIX2);
|
||||
final boolean isDetail = refKey.startsWith(DETAIL_PREFIX) || refKey.startsWith(DETAIL_PREFIX2);
|
||||
|
||||
String querySql = baseSql;
|
||||
if (isApproval) {
|
||||
|
@ -170,12 +173,11 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
querySql = String.format(querySql, StringUtils.join(e.getValue(), ","),
|
||||
"createdOn,recordId,state,stepId", "RobotApprovalStep", "recordId");
|
||||
|
||||
} else if (refKey.startsWith(DETAIL_PREFIX)) {
|
||||
Entity de = entity.getDetailEntity();
|
||||
|
||||
} else if (isDetail) {
|
||||
String sortField = templateExtractor33.getSortField(DETAIL_PREFIX);
|
||||
querySql += " order by " + StringUtils.defaultIfBlank(sortField, "createdOn asc");
|
||||
|
||||
Entity de = entity.getDetailEntity();
|
||||
querySql = String.format(querySql, StringUtils.join(e.getValue(), ","),
|
||||
de.getPrimaryField().getName(), de.getName(), MetadataHelper.getDetailToMainField(de).getName());
|
||||
|
||||
|
@ -312,7 +314,7 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
|
||||
private File superGenerate() {
|
||||
File file = super.generate();
|
||||
if (inShapeVars.isEmpty() || mdataHolder == null) return file;
|
||||
if (inShapeVars.isEmpty() || recordMainHolder == null) return file;
|
||||
|
||||
// v3.6 提取文本框
|
||||
try (Workbook wb = WorkbookFactory.create(file)) {
|
||||
|
@ -324,7 +326,7 @@ public class EasyExcelGenerator33 extends EasyExcelGenerator {
|
|||
while (matcher.find()) {
|
||||
String varName = matcher.group(1);
|
||||
if (StringUtils.isNotBlank(varName)) {
|
||||
shapeText = shapeText.replace("{" + varName +"}", String.valueOf(mdataHolder.get(varName)));
|
||||
shapeText = shapeText.replace("{" + varName +"}", String.valueOf(recordMainHolder.get(varName)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ public class EasyExcelListGenerator extends EasyExcelGenerator {
|
|||
if (varsMap.containsKey(PH__CURRENTDATETIME)) phValues.put(PH__CURRENTDATETIME, getPhValue(PH__CURRENTDATETIME));
|
||||
|
||||
Map<String, List<Map<String, Object>>> datasMap = new HashMap<>();
|
||||
datasMap.put(MDATA_KEY, datas);
|
||||
datasMap.put(REFKEY_LIST, datas);
|
||||
return datasMap;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ public abstract class BaseFeedsService extends ObservableService {
|
|||
|
||||
@Override
|
||||
public Record create(Record record) {
|
||||
record = super.create(converContent(record));
|
||||
record = super.create(converContent4Mentions(record));
|
||||
|
||||
awareMention(record, true);
|
||||
return record;
|
||||
|
@ -52,7 +52,7 @@ public abstract class BaseFeedsService extends ObservableService {
|
|||
|
||||
@Override
|
||||
public Record update(Record record) {
|
||||
record = super.update(converContent((record)));
|
||||
record = super.update(converContent4Mentions((record)));
|
||||
|
||||
awareMention(record, false);
|
||||
return record;
|
||||
|
@ -174,7 +174,7 @@ public abstract class BaseFeedsService extends ObservableService {
|
|||
* @param record
|
||||
* @return
|
||||
*/
|
||||
private Record converContent(Record record) {
|
||||
private Record converContent4Mentions(Record record) {
|
||||
String content = record.getString("content");
|
||||
if (StringUtils.isBlank(content)) return record;
|
||||
|
||||
|
|
|
@ -14,10 +14,13 @@ import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
|
|||
import com.googlecode.aviator.lexer.token.OperatorType;
|
||||
import com.googlecode.aviator.runtime.function.system.AssertFunction;
|
||||
import com.googlecode.aviator.runtime.type.AviatorFunction;
|
||||
import com.googlecode.aviator.runtime.type.Sequence;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -138,4 +141,16 @@ public class AviatorUtils {
|
|||
public static AviatorEvaluatorInstance getInstance() {
|
||||
return AVIATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Iterator<Object> toIterator(Object value) {
|
||||
if (value instanceof Collection) return ((Collection<Object>) value).iterator();
|
||||
if (value instanceof Sequence) return ((Sequence<Object>) value).iterator();
|
||||
|
||||
throw new UnsupportedOperationException("Unsupport type : " + value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import com.googlecode.aviator.runtime.type.AviatorJavaType;
|
|||
import com.googlecode.aviator.runtime.type.AviatorNil;
|
||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||
import com.googlecode.aviator.runtime.type.AviatorString;
|
||||
import com.googlecode.aviator.runtime.type.Sequence;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.support.general.FieldValueHelper;
|
||||
|
@ -24,11 +25,12 @@ import org.apache.commons.lang3.StringUtils;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Usage: TEXT($id|$id[], [$defaultValue], [$separator], [$fieldName])
|
||||
* Usage: TEXT($id|$id[], [$defaultValue], [$separator], [$labelFieldName])
|
||||
* Return: String
|
||||
*
|
||||
* @author RB
|
||||
|
@ -78,16 +80,14 @@ public class TextFunction extends AbstractFunction {
|
|||
// 多引用 ID[]
|
||||
|
||||
Object idArray = $id;
|
||||
if ($id instanceof Collection) {
|
||||
List<ID> list = null;
|
||||
for (Object o : (Collection<?>) $id) {
|
||||
if (o instanceof ID) {
|
||||
if (list == null) list = new ArrayList<>();
|
||||
list.add((ID) o);
|
||||
}
|
||||
if (idArray instanceof Collection || idArray instanceof Sequence) {
|
||||
Iterator<?> iter = AviatorUtils.toIterator(idArray);
|
||||
List<ID> list = new ArrayList<>();
|
||||
while (iter.hasNext()) {
|
||||
Object o = iter.next();
|
||||
if (o instanceof ID) list.add((ID) o);
|
||||
}
|
||||
|
||||
if (list != null) idArray = list.toArray(new ID[0]);
|
||||
if (!list.isEmpty()) idArray = list.toArray(new ID[0]);
|
||||
}
|
||||
|
||||
if (idArray instanceof ID[]) {
|
||||
|
|
|
@ -218,7 +218,7 @@ public class AggregationEvaluator {
|
|||
Map<Object, Integer> countList = null;
|
||||
if (mode == 2 || mode == 3) {
|
||||
nvList = new LinkedHashSet<>();
|
||||
if (mode == 3) countList = new HashMap<>(); // 针对文本
|
||||
if (mode == 3) countList = new HashMap<>(); // 仅文本有效 *N
|
||||
} else {
|
||||
nvList = new ArrayList<>();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.BootEnvironmentPostProcessor;
|
||||
import com.rebuild.core.RebuildException;
|
||||
import com.rebuild.core.service.PerHourJob;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
@ -40,9 +41,7 @@ public class RebuildConfiguration extends KVStorage {
|
|||
* @return
|
||||
*/
|
||||
public static File getFileOfData(String filepath) {
|
||||
if (filepath != null && filepath.contains("../")) {
|
||||
throw new SecurityException("Attack path detected : " + filepath);
|
||||
}
|
||||
CommonsUtils.checkFilePathAttack(filepath);
|
||||
|
||||
String d = get(ConfigurationItem.DataDirectory);
|
||||
File datadir = null;
|
||||
|
@ -77,9 +76,7 @@ public class RebuildConfiguration extends KVStorage {
|
|||
* @see PerHourJob#doCleanTempFiles()
|
||||
*/
|
||||
public static File getFileOfTemp(String filepath) {
|
||||
if (filepath != null && filepath.contains("../")) {
|
||||
throw new SecurityException("Attack path detected : " + filepath);
|
||||
}
|
||||
CommonsUtils.checkFilePathAttack(filepath);
|
||||
|
||||
File temp = getFileOfData("temp");
|
||||
if (!temp.exists()) {
|
||||
|
|
|
@ -395,29 +395,29 @@ public class QiniuCloud {
|
|||
/**
|
||||
* 读取文件
|
||||
*
|
||||
* @param filePath
|
||||
* @param filepath
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws RebuildException If cannot read/download
|
||||
*/
|
||||
public static File getStorageFile(String filePath) throws IOException, RebuildException {
|
||||
File file;
|
||||
if (filePath.startsWith("http://") || filePath.startsWith("https://")) {
|
||||
String name = filePath.split("\\?")[0];
|
||||
public static File getStorageFile(String filepath) throws IOException, RebuildException {
|
||||
File file = null;
|
||||
if (filepath.startsWith("http://") || filepath.startsWith("https://")) {
|
||||
String name = filepath.split("\\?")[0];
|
||||
name = name.substring(name.lastIndexOf("/") + 1);
|
||||
file = RebuildConfiguration.getFileOfTemp("down" + System.nanoTime() + "." + name);
|
||||
OkHttpUtils.readBinary(filePath, file, null);
|
||||
OkHttpUtils.readBinary(filepath, file, null);
|
||||
|
||||
} else if (QiniuCloud.instance().available()) {
|
||||
String name = parseFileName(filePath);
|
||||
String name = parseFileName(filepath);
|
||||
file = RebuildConfiguration.getFileOfTemp("down" + System.nanoTime() + "." + name);
|
||||
instance().download(filePath, file);
|
||||
instance().download(filepath, file);
|
||||
|
||||
} else {
|
||||
file = RebuildConfiguration.getFileOfData(filePath);
|
||||
} else if (filepath.startsWith("rb/") || filepath.startsWith("/rb/")) {
|
||||
file = RebuildConfiguration.getFileOfData(filepath);
|
||||
}
|
||||
|
||||
if (!file.exists()) throw new RebuildException("Cannot read file : " + filePath);
|
||||
if (file == null || !file.exists()) throw new RebuildException("Cannot read file : " + filepath);
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -299,4 +299,15 @@ public class CommonsUtils {
|
|||
int rnd = RandomUtils.nextInt(e);
|
||||
return rnd < s ? rnd + s : rnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param filepath
|
||||
* @throws SecurityException
|
||||
*/
|
||||
public static void checkFilePathAttack(String filepath) throws SecurityException {
|
||||
if (filepath == null) return;
|
||||
if (filepath.contains("../") || filepath.contains("<") || filepath.contains(">")) {
|
||||
throw new SecurityException("Attack path detected : " + filepath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,6 +216,7 @@ public abstract class BaseController extends Controller {
|
|||
* @return
|
||||
*/
|
||||
protected ModelAndView createModelAndView(String view) {
|
||||
view = view.replaceAll("[^a-zA-Z0-9_/\\-]", "");
|
||||
return new ModelAndView(view);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import com.rebuild.core.support.RebuildConfiguration;
|
|||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.core.support.integration.QiniuCloud;
|
||||
import com.rebuild.utils.AppUtils;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.utils.MarkdownUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.catalina.connector.ClientAbortException;
|
||||
|
@ -203,10 +204,10 @@ public class RebuildWebConfigurer implements WebMvcConfigurer, ErrorViewResolver
|
|||
if (StringUtils.isBlank(errorMsg)) errorMsg = Language.L("系统繁忙,请稍后重试");
|
||||
|
||||
error.getModel().put("error_code", errorCode);
|
||||
error.getModel().put("error_msg", errorMsg);
|
||||
error.getModel().put("error_msg", CommonsUtils.escapeHtml(errorMsg));
|
||||
|
||||
if (ex != null && Application.devMode()) {
|
||||
error.getModel().put("error_stack", ThrowableUtils.extractStackTrace(ex));
|
||||
error.getModel().put("error_stack", CommonsUtils.escapeHtml(ThrowableUtils.extractStackTrace(ex)));
|
||||
}
|
||||
|
||||
return error;
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.alibaba.fastjson.JSON;
|
|||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
|
@ -87,6 +88,7 @@ public class RevisionHistoryController extends EntityController {
|
|||
|
||||
// 补充字段名称
|
||||
private void paddingFieldsName(JSONArray contents, Entity entity) {
|
||||
final int entityCode = entity.getEntityCode();
|
||||
for (Iterator<Object> iter = contents.iterator(); iter.hasNext(); ) {
|
||||
JSONObject item = (JSONObject) iter.next();
|
||||
String fieldName = item.getString("field");
|
||||
|
@ -95,8 +97,12 @@ public class RevisionHistoryController extends EntityController {
|
|||
EasyField easyField = EasyMetaFactory.valueOf(entity.getField(fieldName));
|
||||
// 排除不可查询字段
|
||||
if (!easyField.isQueryable()) {
|
||||
iter.remove();
|
||||
continue;
|
||||
if (fieldName.equalsIgnoreCase("contentMore") && entityCode == EntityHelper.Feeds) {
|
||||
// 保留
|
||||
} else {
|
||||
iter.remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fieldName = easyField.getLabel();
|
||||
|
|
|
@ -268,8 +268,8 @@ public class FileDownloader extends BaseController {
|
|||
filepath = CodecUtils.urlDecode(filepath);
|
||||
filepath = filepath.replace("\\", "/");
|
||||
|
||||
if (filepath.contains("../")
|
||||
|| filepath.startsWith("_log/") || filepath.contains("/_log/")
|
||||
CommonsUtils.checkFilePathAttack(filepath);
|
||||
if (filepath.startsWith("_log/") || filepath.contains("/_log/")
|
||||
|| filepath.startsWith("_backups/") || filepath.contains("/_backups/")) {
|
||||
throw new SecurityException("Attack path detected : " + filepath);
|
||||
}
|
||||
|
|
|
@ -427,7 +427,7 @@
|
|||
<field name="feedsId" type="primary"/>
|
||||
<field name="type" type="small-int" nullable="false" updatable="false" default-value="1" description="类型"/>
|
||||
<field name="content" type="text" nullable="false" description="内容"/>
|
||||
<field name="contentMore" type="text" description="扩展内容 (JSON Map)" queryable="false"/>
|
||||
<field name="contentMore" type="text" description="附加内容" queryable="false"/>
|
||||
<field name="images" type="string" max-length="700" description="图片" extra-attrs="{displayType:'IMAGE'}"/>
|
||||
<field name="attachments" type="string" max-length="700" description="附件" extra-attrs="{displayType:'FILE'}"/>
|
||||
<field name="relatedRecord" type="any-reference" description="相关记录" cascade="ignore"/>
|
||||
|
|
|
@ -11,15 +11,21 @@
|
|||
.mdi-monitor::before {
|
||||
margin-left: -1px;
|
||||
}
|
||||
.badge.badge-info {
|
||||
color: #555;
|
||||
}
|
||||
.badge.badge-info.excel {
|
||||
background-color: #0f9d58;
|
||||
background-color: #caead8;
|
||||
}
|
||||
.badge.badge-info.word {
|
||||
background-color: #307cf1;
|
||||
background-color: #d2e0f4;
|
||||
}
|
||||
.badge.badge-info.html5 {
|
||||
background-color: #f16529;
|
||||
background-color: #e44d26;
|
||||
background-color: #f6dbd4;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
</tbody>
|
||||
</table>
|
||||
<div th:if="${smsAccount == null}" class="alert alert-danger alert-icon mt-6 mb-6">
|
||||
<div class="icon"><span class="zmdi zmdi-close-circle-o"></span></div>
|
||||
<div class="icon"><span class="mdi mdi-message-alert-outline"></span></div>
|
||||
<div class="message">[[${bundle.L('短信服务未配置,相关功能不可用')}]]</div>
|
||||
</div>
|
||||
<div class="edit-footer">
|
||||
|
|
|
@ -107,12 +107,14 @@
|
|||
<span class="custom-control-label">[[${bundle.L('禁用详情页单字段编辑')}]] <i class="support-plat2 mdi mdi-monitor" th:title="${bundle.L('支持 PC')}"></i></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" id="enableRecordMerger" />
|
||||
<span class="custom-control-label">[[${bundle.L('启用记录合并功能')}]] <i class="support-plat2 mdi mdi-monitor" th:title="${bundle.L('支持 PC')}"></i></span>
|
||||
</label>
|
||||
</div>
|
||||
<th:block th:if="${currentEntity == mainEntity || detailEntity == null}">
|
||||
<div class="mt-2">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" id="enableRecordMerger" />
|
||||
<span class="custom-control-label">[[${bundle.L('启用记录合并功能')}]] <i class="support-plat2 mdi mdi-monitor" th:title="${bundle.L('支持 PC')}"></i></span>
|
||||
</label>
|
||||
</div>
|
||||
</th:block>
|
||||
<div class="mt-2">
|
||||
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
|
||||
<input class="custom-control-input" type="checkbox" id="repeatFieldsCheckMode" />
|
||||
|
|
|
@ -298,8 +298,8 @@ a.ui-draggable.ui-draggable-dragging {
|
|||
|
||||
.data-info ul li i.icon,
|
||||
.data-info ul.fields li a::before {
|
||||
width: 18px;
|
||||
display: inline-block;
|
||||
width: 15px;
|
||||
color: #4285f4;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,9 +79,11 @@ code {
|
|||
|
||||
.dropdown-menu {
|
||||
box-shadow: 0 3px 0.3077rem rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);
|
||||
border-width: 0;
|
||||
border-radius: 0;
|
||||
max-width: 300px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.dropdown-menu .dropdown-item,
|
||||
|
@ -3203,7 +3205,7 @@ form {
|
|||
border-radius: 2px;
|
||||
padding: 15px;
|
||||
background-color: rgba(230, 230, 230, 0.3);
|
||||
box-shadow: inset 0px 0px 10px rgba(230, 230, 230, 0.6);
|
||||
box-shadow: inset 0 0 10px rgba(230, 230, 230, 0.6);
|
||||
}
|
||||
|
||||
.form.approval-form .approval-list {
|
||||
|
@ -5483,8 +5485,8 @@ span.icon-append .icon {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.media-capture.md video,
|
||||
.media-capture.md canvas {
|
||||
.media-capture.media-capture-md video,
|
||||
.media-capture.media-capture-md canvas {
|
||||
width: 768px;
|
||||
height: 576px;
|
||||
}
|
||||
|
|
|
@ -420,19 +420,28 @@ textarea.formula-code + .fields-vars {
|
|||
color: #edebe6;
|
||||
}
|
||||
|
||||
.dropdown-item > em,
|
||||
.fields ul li.flag-DECIMAL > a::before,
|
||||
.fields ul li.flag-NUMBER > a::before,
|
||||
.fields ul li.flag-DATE > a::before,
|
||||
.fields ul li.flag-DATETIME > a::before {
|
||||
content: 'N';
|
||||
.fields ul li.flag-DATETIME > a::before,
|
||||
.fields ul li.flag-TIME > a::before {
|
||||
content: '#';
|
||||
width: 15px;
|
||||
display: inline-block;
|
||||
color: #4285f4;
|
||||
font-size: 0.9rem;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.fields ul li.flag-DECIMAL > a::before,
|
||||
.fields ul li.flag-NUMBER > a::before {
|
||||
content: 'N';
|
||||
}
|
||||
|
||||
.fields ul li.flag-DATE > a::before,
|
||||
.fields ul li.flag-DATETIME > a::before {
|
||||
.fields ul li.flag-DATETIME > a::before,
|
||||
.fields ul li.flag-TIME > a::before{
|
||||
content: 'D';
|
||||
}
|
||||
|
||||
|
|
|
@ -203,6 +203,8 @@ class RbViewModal extends React.Component {
|
|||
* @param {Boolean} subView
|
||||
*/
|
||||
static create(props, subView) {
|
||||
if (props.id) props.id = props.id.toLowerCase()
|
||||
|
||||
this.__HOLDERs = this.__HOLDERs || {}
|
||||
this.__HOLDERsStack = this.__HOLDERsStack || []
|
||||
const that = this
|
||||
|
|
|
@ -1590,7 +1590,7 @@ class RbFormImage extends RbFormElement {
|
|||
// NOOP
|
||||
} else {
|
||||
if (!this._fieldValue__input) {
|
||||
console.warn('No element `_fieldValue__input` defined')
|
||||
console.log('No element `_fieldValue__input` defined :', this.props.field)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
|||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
// css width
|
||||
const _VIDEO_WIDTH = 1000
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
@ -30,7 +29,7 @@ class MediaCapturer extends RbModal {
|
|||
<canvas ref={(c) => (this._$resImage = c)} className={this.state.recType === 'image' ? '' : 'hide'}></canvas>
|
||||
</div>
|
||||
|
||||
<div className="action" ref={(c) => (this._$btn = c)}>
|
||||
<div className={`action ${this.state.unsupportted && 'hide'}`} ref={(c) => (this._$btn = c)}>
|
||||
<input type="file" className="hide" ref={(c) => (this._$fileinput = c)} />
|
||||
<button className="btn btn-primary J_used" type="button" onClick={() => this.handleConfirm()}>
|
||||
<i className="icon mdi mdi-check" /> {$L('使用')}
|
||||
|
@ -74,26 +73,27 @@ class MediaCapturer extends RbModal {
|
|||
componentDidMount() {
|
||||
super.componentDidMount()
|
||||
|
||||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||
this.initDevice(null, $storage.get('MediaCapturerDeviceId'))
|
||||
|
||||
navigator.mediaDevices
|
||||
.enumerateDevices()
|
||||
.then((devices) => {
|
||||
const devices2 = devices.filter((device) => device.kind === 'videoinput')
|
||||
const devices3 = []
|
||||
devices2.forEach((device, idx) => {
|
||||
devices3.push([device.deviceId, device.label || idx])
|
||||
})
|
||||
this.setState({ webcamList: devices3 })
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
} else {
|
||||
this.setState({ initMsg: $L('你的浏览器不支持此功能') })
|
||||
if (!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) {
|
||||
this.setState({ initMsg: $L('你的浏览器不支持此功能'), unsupportted: true })
|
||||
return
|
||||
}
|
||||
|
||||
this.initDevice(null, $storage.get('MediaCapturerDeviceId'))
|
||||
|
||||
navigator.mediaDevices
|
||||
.enumerateDevices()
|
||||
.then((devices) => {
|
||||
const devices2 = devices.filter((device) => device.kind === 'videoinput')
|
||||
const devices3 = []
|
||||
devices2.forEach((device, idx) => {
|
||||
devices3.push([device.deviceId, device.label || idx])
|
||||
})
|
||||
this.setState({ webcamList: devices3 })
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
if (this.props.forceFile) {
|
||||
$initUploader(
|
||||
this._$fileinput,
|
||||
|
@ -129,7 +129,8 @@ class MediaCapturer extends RbModal {
|
|||
return
|
||||
}
|
||||
|
||||
const ps = { video: true }
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
|
||||
const ps = { video: true, audio: this.props.recordAudio || false }
|
||||
if (deviceId) ps.video = { deviceId: deviceId }
|
||||
navigator.mediaDevices
|
||||
.getUserMedia(ps)
|
||||
|
@ -143,8 +144,7 @@ class MediaCapturer extends RbModal {
|
|||
})
|
||||
this._mediaRecorder.addEventListener('stop', () => {
|
||||
this._capturedData = new Blob(this._blobs, { type: 'video/mp4' })
|
||||
const videoBlobURL = URL.createObjectURL(this._capturedData)
|
||||
this._$resVideo.src = videoBlobURL
|
||||
this._$resVideo.src = URL.createObjectURL(this._capturedData)
|
||||
this.setState({ captured: true, initMsg: null })
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
*/
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
const isMulti = ['MULTISELECT'].includes($urlp('type'))
|
||||
const maxOptions = 100
|
||||
const isMulti = 'MULTISELECT' === $urlp('type')
|
||||
const maxOptions = isMulti ? 32 : 200
|
||||
|
||||
$(document).ready(() => {
|
||||
const query = `entity=${$urlp('entity')}&field=${$urlp('field')}`
|
||||
|
|
|
@ -139,6 +139,9 @@ $(function () {
|
|||
typeof window.bosskeyTrigger === 'function' && window.bosskeyTrigger()
|
||||
window.__BOSSKEY = true
|
||||
}
|
||||
setTimeout(function () {
|
||||
--bosskey
|
||||
}, 50000) // clean
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -497,6 +497,9 @@ class EditorWithFieldVars extends React.Component {
|
|||
</a>
|
||||
<div className="dropdown-menu auto-scroller dropdown-menu-right" ref={(c) => (this._$fieldVars = c)}>
|
||||
{(this.state.fieldVars || []).map((item) => {
|
||||
let typeMark = 'T'
|
||||
if (['DATE', 'DATETIME', 'TIME'].includes(item.type)) typeMark = 'D'
|
||||
else if (['NUMBER', 'DECIMAL'].includes(item.type)) typeMark = 'N'
|
||||
return (
|
||||
<a
|
||||
className="dropdown-item"
|
||||
|
@ -504,6 +507,7 @@ class EditorWithFieldVars extends React.Component {
|
|||
onClick={() => {
|
||||
$(this._$content).insertAtCursor(`{${item.name}}`)
|
||||
}}>
|
||||
<em>{typeMark}</em>
|
||||
{item.label}
|
||||
</a>
|
||||
)
|
||||
|
|
|
@ -10,7 +10,7 @@ const CALC_MODES2 = {
|
|||
...FormulaAggregation.CALC_MODES,
|
||||
RBJOIN: $L('连接'),
|
||||
RBJOIN2: $L('去重连接'),
|
||||
// RBJOIN3: $L('去重连接*N'),
|
||||
RBJOIN3: $L('去重连接*N'),
|
||||
}
|
||||
|
||||
const __LAB_MATCHFIELDS = false
|
||||
|
@ -288,7 +288,7 @@ class ContentFieldAggregation extends ActionContentSpec {
|
|||
if ('RBJOIN' === cm || 'RBJOIN2' === cm || 'RBJOIN3' === cm) {
|
||||
tfAllow = this.__targetFieldsCache.filter((x) => {
|
||||
if ('NTEXT' === x.type) return true
|
||||
if ('N2NREFERENCE' === x.type) return x.ref[0] === sf.ref[0]
|
||||
if ('N2NREFERENCE' === x.type) return sf.ref && sf.ref[0] === x.ref[0]
|
||||
if ('FILE' === x.type) return true
|
||||
return false
|
||||
})
|
||||
|
|
|
@ -10,7 +10,7 @@ const CALC_MODES2 = {
|
|||
...FormulaAggregation.CALC_MODES,
|
||||
RBJOIN: $L('连接'),
|
||||
RBJOIN2: $L('去重连接'),
|
||||
// RBJOIN3: $L('去重连接*N'),
|
||||
RBJOIN3: $L('去重连接*N'),
|
||||
}
|
||||
|
||||
// ~~ 分组聚合
|
||||
|
@ -380,7 +380,7 @@ class ContentGroupAggregation extends ActionContentSpec {
|
|||
if ('RBJOIN' === cm || 'RBJOIN2' === cm || 'RBJOIN3' === cm) {
|
||||
tfAllow = this.__targetFieldsCache.filter((x) => {
|
||||
if ('NTEXT' === x.type) return true
|
||||
if ('N2NREFERENCE' === x.type) return x.ref[0] === sf.ref[0]
|
||||
if ('N2NREFERENCE' === x.type) return sf.ref && sf.ref[0] === x.ref[0]
|
||||
if ('FILE' === x.type) return true
|
||||
return false
|
||||
})
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<ul class="list-unstyled esource">
|
||||
<li>
|
||||
<span class="text-bold">
|
||||
<i class="zmdi icon" th:classappend="|zmdi-${entityIcon}|"></i>
|
||||
<i class="zmdi icon fs-16 down-1" th:classappend="|zmdi-${entityIcon}|"></i>
|
||||
[[${entityLabel}]]
|
||||
</span>
|
||||
</li>
|
||||
|
|
Loading…
Reference in a new issue