diff --git a/platform-annotation-processors-test/pom.xml b/platform-annotation-processors-test/pom.xml
index f656769286a..025b96c7d5e 100644
--- a/platform-annotation-processors-test/pom.xml
+++ b/platform-annotation-processors-test/pom.xml
@@ -4,7 +4,7 @@
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
platform-annotation-processors-test
diff --git a/platform-annotation-processors/pom.xml b/platform-annotation-processors/pom.xml
index ccfbe15215f..e7eb9101730 100644
--- a/platform-annotation-processors/pom.xml
+++ b/platform-annotation-processors/pom.xml
@@ -4,7 +4,7 @@
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
platform-annotation-processors
diff --git a/platform-annotations/pom.xml b/platform-annotations/pom.xml
index 21c5c02523f..1339d9c3b71 100644
--- a/platform-annotations/pom.xml
+++ b/platform-annotations/pom.xml
@@ -3,7 +3,7 @@
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
platform-annotations
diff --git a/platform-benchmark/pom.xml b/platform-benchmark/pom.xml
index d6c448f540d..90bc14064b3 100644
--- a/platform-benchmark/pom.xml
+++ b/platform-benchmark/pom.xml
@@ -6,7 +6,7 @@
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
Trident Genesis Platform Microbenckmarks
diff --git a/platform-benchmark/src/main/java/ua/com/fielden/companion/BenchmarkIocModule.java b/platform-benchmark/src/main/java/ua/com/fielden/companion/BenchmarkIocModule.java
index 23fac98897e..de2ea58db99 100644
--- a/platform-benchmark/src/main/java/ua/com/fielden/companion/BenchmarkIocModule.java
+++ b/platform-benchmark/src/main/java/ua/com/fielden/companion/BenchmarkIocModule.java
@@ -19,9 +19,9 @@
import ua.com.fielden.platform.serialisation.api.impl.IdOnlyProxiedEntityTypeCacheForTests;
import ua.com.fielden.platform.web.annotations.AppUri;
+import java.time.Duration;
import java.util.List;
import java.util.Properties;
-import java.util.concurrent.TimeUnit;
import static java.lang.String.format;
@@ -59,7 +59,7 @@ protected void configure() {
@Provides
@Singleton
@SessionCache Cache provideSessionCache() {
- return CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.MINUTES).build();
+ return CacheBuilder.newBuilder().expireAfterWrite(Duration.ofMinutes(2)).build();
}
}
diff --git a/platform-benchmark/src/main/java/ua/com/fielden/eql/BenchmarkIocModule.java b/platform-benchmark/src/main/java/ua/com/fielden/eql/BenchmarkIocModule.java
index a6b5d01c134..e7825c097e1 100644
--- a/platform-benchmark/src/main/java/ua/com/fielden/eql/BenchmarkIocModule.java
+++ b/platform-benchmark/src/main/java/ua/com/fielden/eql/BenchmarkIocModule.java
@@ -19,9 +19,9 @@
import ua.com.fielden.platform.serialisation.api.impl.IdOnlyProxiedEntityTypeCacheForTests;
import ua.com.fielden.platform.web.annotations.AppUri;
+import java.time.Duration;
import java.util.List;
import java.util.Properties;
-import java.util.concurrent.TimeUnit;
import static java.lang.String.format;
@@ -59,7 +59,7 @@ protected void configure() {
@Provides
@Singleton
@SessionCache Cache provideSessionCache() {
- return CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.MINUTES).build();
+ return CacheBuilder.newBuilder().expireAfterWrite(Duration.ofMinutes(2)).build();
}
}
diff --git a/platform-dao/pom.xml b/platform-dao/pom.xml
index 36acd584fc8..ddb9e8af1c8 100644
--- a/platform-dao/pom.xml
+++ b/platform-dao/pom.xml
@@ -3,7 +3,7 @@
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
platform-dao
diff --git a/platform-dao/src/main/java/ua/com/fielden/platform/eql/dbschema/HibernateToJdbcSqlTypeCorrespondence.java b/platform-dao/src/main/java/ua/com/fielden/platform/eql/dbschema/HibernateToJdbcSqlTypeCorrespondence.java
index 2a6a51578df..342835b88e3 100644
--- a/platform-dao/src/main/java/ua/com/fielden/platform/eql/dbschema/HibernateToJdbcSqlTypeCorrespondence.java
+++ b/platform-dao/src/main/java/ua/com/fielden/platform/eql/dbschema/HibernateToJdbcSqlTypeCorrespondence.java
@@ -10,8 +10,6 @@
import java.sql.Types;
import java.util.Arrays;
import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import static org.apache.commons.lang3.StringUtils.substringBefore;
import static ua.com.fielden.platform.utils.Pair.pair;
@@ -40,11 +38,8 @@ public final class HibernateToJdbcSqlTypeCorrespondence {
*/
// funnily enough Hibernate itself performs such conversion in certain Dialect implementations
public static String genericSqlTypeName(final int sqlType, final Dialect dialect) {
- final var map = GENERIC_TYPE_NAMES.computeIfAbsent(dialect, $ -> new ConcurrentHashMap<>());
- return map.computeIfAbsent(sqlType, $ -> substringBefore(dialect.getTypeName(sqlType), '('));
+ return substringBefore(dialect.getTypeName(sqlType), '(');
}
- // where
- private static final Map> GENERIC_TYPE_NAMES = new ConcurrentHashMap<>(1); // expect at most 1 dialect
/**
* Infers the name of a Hibernate type to use in a {@code CAST} SQL expression.
diff --git a/platform-dao/src/main/java/ua/com/fielden/platform/sample/domain/TgEntityWithTimeZoneDatesDao.java b/platform-dao/src/main/java/ua/com/fielden/platform/sample/domain/TgEntityWithTimeZoneDatesDao.java
index f0d84e3c7ce..07dd2c74099 100644
--- a/platform-dao/src/main/java/ua/com/fielden/platform/sample/domain/TgEntityWithTimeZoneDatesDao.java
+++ b/platform-dao/src/main/java/ua/com/fielden/platform/sample/domain/TgEntityWithTimeZoneDatesDao.java
@@ -22,7 +22,14 @@ public TgEntityWithTimeZoneDatesDao(final IFilter filter) {
@Override
protected IFetchProvider createFetchProvider() {
- return super.createFetchProvider().with("key", "dateProp", "datePropUtc");
+ return super.createFetchProvider().with(
+ "key",
+ "dateProp",
+ "datePropUtc",
+ "datePropDependent",
+ "dateOnlyProp",
+ "dateOnlyPropUtc"
+ );
}
}
\ No newline at end of file
diff --git a/platform-dao/src/test/java/ua/com/fielden/platform/test/ioc/PlatformTestServerIocModule.java b/platform-dao/src/test/java/ua/com/fielden/platform/test/ioc/PlatformTestServerIocModule.java
index f51f658075b..1cb61e0d236 100644
--- a/platform-dao/src/test/java/ua/com/fielden/platform/test/ioc/PlatformTestServerIocModule.java
+++ b/platform-dao/src/test/java/ua/com/fielden/platform/test/ioc/PlatformTestServerIocModule.java
@@ -38,9 +38,9 @@
import ua.com.fielden.platform.utils.IUniversalConstants;
import ua.com.fielden.platform.web.annotations.AppUri;
+import java.time.Duration;
import java.util.List;
import java.util.Properties;
-import java.util.concurrent.TimeUnit;
import static java.lang.String.format;
@@ -230,7 +230,7 @@ protected void bindDomainCompanionObjects(final List
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
platform-db-evolution
diff --git a/platform-doc/claude/entity-model/quick-reference.md b/platform-doc/claude/entity-model/quick-reference.md
index 7322141029c..34fe5ce7c83 100644
--- a/platform-doc/claude/entity-model/quick-reference.md
+++ b/platform-doc/claude/entity-model/quick-reference.md
@@ -96,6 +96,13 @@ protected static final EntityResultQueryModel model_ = select(Source.class)
Use `models_` (plural `List`), inner `enum`, `@SupportsEntityExistsValidation`.
See `entity-model/reference.md` § *Synthetic Grouping Property Entities*.
+## Fixed Entity Instances
+
+Protect specific records of a user-maintained *persistent* entity that business logic references by key.
+An inner `enum Fixed` lists the protected keys and is the single source of truth; the rows are created by migration/population, not by the enum.
+Four parts: (1) the `Fixed` enum with `matches` / `fromValue` / `isOneOf`; (2) a key+desc `@BeforeChange` validator — **fail** on key rename, **warn** on desc change, guarded by `isPersisted() && Fixed.isOneOf(entity)`; (3) an `IRenderingCustomiser` that italicises fixed rows; (4) a `batchDelete` override rejecting fixed rows.
+See `entity-model/reference.md` § *Fixed Entity Instances*.
+
## Metamodel References
Always use metamodel references instead of string literals:
diff --git a/platform-doc/claude/entity-model/reference.md b/platform-doc/claude/entity-model/reference.md
index 9c2edbfa130..5e259c04e1e 100644
--- a/platform-doc/claude/entity-model/reference.md
+++ b/platform-doc/claude/entity-model/reference.md
@@ -667,6 +667,204 @@ The distinguishing line is *what kind of data they present*, not the field shape
Grouping property entities pair naturally with **generative entities** below — the latter typically host them as `@CritOnly(SINGLE)` selection criteria that drive the data generator.
+## Fixed Entity Instances
+
+The **fixed entity instances** pattern protects a subset of records in an otherwise user-maintained reference (lookup) entity.
+The table supports full CRUD through its Entity Centre and Master, but a few specific records are referenced by key from business logic, so they must not be renamed or deleted — and their descriptions must not silently drift.
+The protected records are declared as an inner `enum` (named `Fixed`), which is the single source of truth for their keys.
+
+The enum **identifies** fixed records; it does not create them.
+The records themselves are inserted by the usual means — a data-migration retriever, a release script, or test data population.
+Each fixed record's key must equal the key value its enum member declares.
+This is the value that the conventional enum method `matches` compares against.
+It could be the enum member's name or one of its fields (e.g., `HIGH_PRIORITY("High Priority")`
+carries the key `"High Priority"`.).
+Unlike a *synthetic grouping property entity*, which synthesises its records from the enum at query time, a fixed-instance entity is an ordinary persistent entity whose records live in the database; the enum merely flags which of them are protected.
+
+The code blocks below are reproduced verbatim from a representative implementation; the pattern itself is independent of any particular entity.
+
+### The `Fixed` enum — conventional methods
+
+```java
+public enum Fixed {
+ NONE("NONE", "No Meter");
+
+ public final String unit;
+ public final String desc;
+
+ Fixed(final String unit, final String desc) {
+ this.unit = unit;
+ this.desc = desc;
+ }
+
+ public boolean matches(final String unit) {
+ return this.unit.equalsIgnoreCase(unit);
+ }
+
+ public boolean matches(final MeasurementUnit unit) {
+ return unit != null && matches(unit.getUnit());
+ }
+
+ public static Fixed fromValue(final MeasurementUnit unit) {
+ return unit != null ? fromValue(unit.getUnit()) : null;
+ }
+
+ public static Fixed fromValue(final String unit) {
+ for (final var fixed : Fixed.values()) {
+ if (fixed.matches(unit)) {
+ return fixed;
+ }
+ }
+ return null;
+ }
+
+ public static boolean isOneOf(final MeasurementUnit unit) {
+ return fromValue(unit) != null;
+ }
+
+ public static boolean isOneOf(final String unit) {
+ return fromValue(unit) != null;
+ }
+}
+```
+
+The contract every `Fixed` enum provides:
+- A field per key-defining property — at minimum the natural key (`unit`), usually the description too.
+- `matches(String)` — case-insensitive comparison against the key.
+ The `String` overload is the primitive; the entity overload is a null-safe convenience.
+- `fromValue(...)` — resolve the enum constant from a key or entity, returning `null` when none matches (paired `String` and entity overloads).
+- `isOneOf(...)` — membership test built on `fromValue`.
+
+Business logic consumes these directly — e.g. `Fixed.NONE.matches(unit)` to special-case a particular record, or `Fixed.isOneOf(unit)` to ask "is this a protected record?".
+
+### Validation of key and desc
+
+A single validator guards both the key and the description, attached to each via `@BeforeChange(@Handler(...))`:
+
+```java
+@IsProperty
+@MapTo
+@CompositeKeyMember(1)
+@BeforeChange(@Handler(MeasurementUnitKeyAndDescValidator.class))
+private String unit;
+
+@IsProperty
+@MapTo
+@BeforeChange(@Handler(MeasurementUnitKeyAndDescValidator.class))
+private String desc;
+```
+
+```java
+public class MeasurementUnitKeyAndDescValidator implements IBeforeChangeEventHandler {
+
+ public static final String
+ ERR_FIXED_MEASUREMENT_UNIT = MeasurementUnit.ENTITY_TITLE + " [%s] is used in the business logic and cannot be modified.",
+ WARN_FIXED_MEASUREMENT_UNIT = MeasurementUnit.ENTITY_TITLE + " [%s] is used in the business logic — please make sure that overall meaning remains the same if the description is changed.";
+
+ @Override
+ public Result handle(final MetaProperty property, final String newValue, final Set mutatorAnnotations) {
+ final MeasurementUnit unit = property.getEntity();
+ if (unit.isPersisted() && Fixed.isOneOf(unit)) {
+ if (property.getName().contentEquals(MeasurementUnit_.unit()) && fromValue(unit) != fromValue(newValue)) {
+ return failuref(ERR_FIXED_MEASUREMENT_UNIT, unit.getUnit());
+ }
+ else if (property.getName().contentEquals(MeasurementUnit_.desc()) && !EntityUtils.equalsEx(property.getValue(), newValue)) {
+ return warningf(WARN_FIXED_MEASUREMENT_UNIT, unit.getUnit());
+ }
+ }
+ return successful();
+ }
+
+}
+```
+
+The rules encoded here:
+- **Guard on `isPersisted() && Fixed.isOneOf(entity)`.**
+ Restrictions apply only to a persisted record that *is* one of the fixed instances.
+ New entities are unrestricted — which is what lets migration/population create the fixed records in the first place — and non-fixed records are never touched.
+- **Key change is a hard failure.**
+ Renaming a fixed record to a different identity (`fromValue(current) != fromValue(newValue)`) returns `failuref(...)`, blocking the assignment.
+ Comparing fixed *identities* rather than raw strings means a case-only or no-op re-set of the same key is allowed.
+- **Description change is a warning, not a failure.**
+ A genuine description change produces a warning — the edit is permitted but the user is warned that the record carries business meaning.
+
+**Deactivation (activatable reference entities).**
+When the entity extends `ActivatableAbstractEntity`, protect the `active` flag too: add a separate validator on `active` that *warns* when a fixed record is deactivated, chained after `ActivePropertyValidator`.
+
+### Rendering in the Entity Centre
+
+Fixed records are rendered italic and in the "required" colour so users can distinguish protected records from their own.
+Implement an `IRenderingCustomiser` whose `getCustomRenderingFor` returns value styles for the key column (keyed by `""`, which stands for "this") when the entity is a fixed record, and register it on the centre via `.setRenderingCustomiser(...)`.
+
+```java
+private static class MeasurementUnitRenderingCustomiser implements IRenderingCustomiser> {
+
+ @Override
+ public Optional> getCustomRenderingFor(final AbstractEntity> entity) {
+ return RenderingCustomiserUtils.getCustomRenderingForFixedValue((MeasurementUnit) entity, MeasurementUnit.Fixed::isOneOf);
+ }
+
+}
+```
+
+The customiser delegates to a small shared helper that holds the two non-obvious details — the italic + required-colour styling, and a workaround for a platform bug that unwraps an empty `Optional` (so it returns an empty map, never `Optional.empty()`):
+
+```java
+public static > Optional> getCustomRenderingForFixedValue(final T entity, final Predicate isFixed) {
+ if (isFixed.test(entity)) {
+ final Map valueStyles = new HashMap<>();
+ valueStyles.put("font-style", "italic");
+ valueStyles.put("color", REQUIRED_PROPERTY_COLOUR);
+
+ final Map> keyStyles = new HashMap<>();
+ keyStyles.put("valueStyles", valueStyles);
+
+ final Map styles = mapOf(t2("" /* stands for "this" */, keyStyles));
+
+ return Optional.of(styles);
+ }
+
+ return Optional.of(Map.of());
+}
+```
+
+Pass the membership predicate as a method reference (`Fixed::isOneOf`); `REQUIRED_PROPERTY_COLOUR` is `#03A9F4`.
+
+### Deletion rules
+
+Block deletion of fixed records in the DAO by overriding `batchDelete(Collection)` (see *Canonical `batchDelete` Pattern*):
+
+```java
+public static final String ERR_FIXED_UNIT_CANNOT_BE_DELETED = MeasurementUnit.ENTITY_TITLE + " [%s] is used in the business logic and cannot be deleted.";
+
+@Override
+@SessionRequired
+@Authorise(MeasurementUnit_CanDelete_Token.class)
+public int batchDelete(final Collection entitiesIds) {
+ validateDeletion(entitiesIds).ifFailure(Result::throwRuntime);
+
+ return defaultBatchDelete(entitiesIds);
+}
+
+private Result validateDeletion(final Collection entitiesIds) {
+ final var query = select(MeasurementUnit.class).where()
+ .prop(MeasurementUnit_.id()).in().values(entitiesIds)
+ .and().prop(MeasurementUnit_.unit()).in().values(Arrays.stream(MeasurementUnit.Fixed.values()).map(it -> it.unit).toList())
+ .model();
+ final var maybeUnit = first(co(MeasurementUnit.class).getFirstEntities(from(query).model(), 1));
+ if (maybeUnit.isPresent()) {
+ return failuref(ERR_FIXED_UNIT_CANNOT_BE_DELETED, maybeUnit.get());
+ }
+
+ return successful();
+}
+```
+
+- The check is a single DB query intersecting the requested IDs with the set of fixed keys (`Fixed.values()` mapped to keys), asking for at most one hit (`getFirstEntities(..., 1)`).
+ This avoids loading every requested entity and short-circuits on the first protected record.
+- This key comparison runs in the database, so its case sensitivity follows the column's collation — unlike method `matches`, which may be case-insensitive via `equalsIgnoreCase`. Store fixed keys in their canonical case so the two sites cannot diverge.
+- Only override the `Collection` overload; leave `batchDelete(List)` delegating to `defaultBatchDelete`.
+
## Generative Entities
A **generative entity** is a persistent entity whose rows are computed ad hoc at Entity Centre `run` phase rather than maintained by normal CRUD.
diff --git a/platform-eql-grammar/pom.xml b/platform-eql-grammar/pom.xml
index 07c6d737e20..e011d8b6438 100644
--- a/platform-eql-grammar/pom.xml
+++ b/platform-eql-grammar/pom.xml
@@ -6,7 +6,7 @@
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
platform-eql-grammar
diff --git a/platform-pojo-bl/pom.xml b/platform-pojo-bl/pom.xml
index 21ef87a1a25..9bc256d7d58 100644
--- a/platform-pojo-bl/pom.xml
+++ b/platform-pojo-bl/pom.xml
@@ -3,7 +3,7 @@
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
platform-pojo-bl
@@ -16,10 +16,10 @@
- 2.21.1
- 2.21
- 33.5.0-jre
- 5.4.1
+ 2.22.1
+ 2.22
+ 33.6.0-jre
+ 5.5.1
2.7.3
7.0.0
9.9.1
@@ -268,7 +268,7 @@
commons-io
commons-io
- 2.20.0
+ 2.21.0
org.commonmark
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/CriteriaGenerator.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/CriteriaGenerator.java
index b953600a19c..16656fd0d49 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/CriteriaGenerator.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/CriteriaGenerator.java
@@ -215,15 +215,10 @@ private static List generateCriteriaProperties(final Class extend
return generatedProperties;
}
- /**
- * Copies date-related property annotations.
- *
- * @param managedType
- * @param propertyName
- * @return
- */
+ /// Copies date-related property annotations.
+ ///
private static List copyDateAnnotations(final Class> managedType, final String propertyName) {
- return of(DateOnly.class, TimeOnly.class, PersistentType.class)
+ return of(DateOnly.class, TimeOnly.class, PersistentType.class, DependentTimeZoneMode.class)
.map(annotationType -> getPropertyAnnotationOptionally(annotationType, managedType, propertyName))
.flatMap(annotation -> annotation.isPresent() ? of(annotation.get()) : empty())
.map(annotation -> {
@@ -231,8 +226,10 @@ private static List copyDateAnnotations(final Class> managedType,
return newDateOnlyAnnotation();
} else if (annotation instanceof TimeOnly) {
return newTimeOnlyAnnotation();
- } else {
+ } else if (annotation instanceof PersistentType) {
return newUtcAnnotation();
+ } else {
+ return newDependentTimeZoneModeAnnotation();
}
})
.collect(toList());
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/TypeDiffSerialiser.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/TypeDiffSerialiser.java
index 7f8b5e73e6f..d97cbd14e06 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/TypeDiffSerialiser.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/criteria/generator/impl/TypeDiffSerialiser.java
@@ -34,7 +34,7 @@ private TypeDiffSerialiser() {
*/
public byte[] serialise(final Map diff) {
try {
- return writeValueAsBytes(diff); // default encoding is Charsets.UTF_8
+ return writeValueAsBytes(diff); // default encoding is StandardCharsets.UTF_8
} catch (final JsonProcessingException ex) {
throw new SerialisationException("Error during type diff serialisation.", ex);
}
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/DependentTimeZoneMode.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/DependentTimeZoneMode.java
new file mode 100644
index 00000000000..adf8d56d108
--- /dev/null
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/DependentTimeZoneMode.java
@@ -0,0 +1,35 @@
+package ua.com.fielden.platform.entity.annotation;
+
+import ua.com.fielden.platform.utils.IDates;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Date;
+
+/// An annotation for properties of type [java.util.Date] to enforce dependent time-zone mode in Web UI logic.
+///
+/// Please note, that appropriate [IDates#now()] and [IDates#zoned(Date)] method variations are required in server-side logic.
+/// I.e. it may not be sufficient to annotate the properties with this annotation.
+///
+/// ```java
+///static DateTime now(final IDates dates) {
+/// return new DateTime(getTimeZone(dates));
+///}
+///static DateTime zoned(final Date date, fina IDates dates) {
+/// return new DateTime(date, getTimeZone(dates));
+///}
+///private static DateTimeZone getTimeZone(final IDates dates) {
+/// if (dates.requestTimeZone().isEmpty()) {
+/// throw failure(ERR_TIME_ZONE_IS_MISSING);
+/// }
+/// return dates.requestTimeZone().get();
+///}
+/// ```
+///
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD })
+public @interface DependentTimeZoneMode {
+
+}
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/factory/DateAnnotations.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/factory/DateAnnotations.java
index 5aa37d51693..4396a9a2ea0 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/factory/DateAnnotations.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/entity/annotation/factory/DateAnnotations.java
@@ -1,25 +1,19 @@
package ua.com.fielden.platform.entity.annotation.factory;
-import java.lang.annotation.Annotation;
-
import ua.com.fielden.platform.entity.annotation.DateOnly;
+import ua.com.fielden.platform.entity.annotation.DependentTimeZoneMode;
import ua.com.fielden.platform.entity.annotation.PersistentType;
import ua.com.fielden.platform.entity.annotation.TimeOnly;
import ua.com.fielden.platform.types.markers.IUtcDateTimeType;
-/**
- * Factory for date annotations.
- *
- * @author TG Team
- *
- */
+import java.lang.annotation.Annotation;
+
+/// Factory for date annotations.
+///
public class DateAnnotations {
- /**
- * Instantiates {@link DateOnly} annotation.
- *
- * @return
- */
+ /// Instantiates [DateOnly] annotation.
+ ///
public static DateOnly newDateOnlyAnnotation() {
return new DateOnly() {
@Override
@@ -29,11 +23,8 @@ public Class extends Annotation> annotationType() {
};
}
- /**
- * Instantiates {@link TimeOnly} annotation.
- *
- * @return
- */
+ /// Instantiates [TimeOnly] annotation.
+ ///
public static TimeOnly newTimeOnlyAnnotation() {
return new TimeOnly() {
@Override
@@ -43,11 +34,8 @@ public Class extends Annotation> annotationType() {
};
}
- /**
- * Instantiates marker annotation for UTC date properties.
- *
- * @return
- */
+ /// Instantiates marker annotation for UTC date properties.
+ ///
public static PersistentType newUtcAnnotation() {
return new PersistentType() {
@Override
@@ -66,5 +54,16 @@ public Class userType() {
}
};
}
-
+
+ /// Instantiates [DependentTimeZoneMode] annotation.
+ ///
+ public static DependentTimeZoneMode newDependentTimeZoneModeAnnotation() {
+ return new DependentTimeZoneMode() {
+ @Override
+ public Class extends Annotation> annotationType() {
+ return DependentTimeZoneMode.class;
+ }
+ };
+ }
+
}
\ No newline at end of file
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/sample/domain/TgEntityWithTimeZoneDates.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/sample/domain/TgEntityWithTimeZoneDates.java
index b9e1a2c7245..e2bfed8fe7f 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/sample/domain/TgEntityWithTimeZoneDates.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/sample/domain/TgEntityWithTimeZoneDates.java
@@ -4,6 +4,8 @@
import ua.com.fielden.platform.entity.AbstractPersistentEntity;
import ua.com.fielden.platform.entity.annotation.CompanionObject;
+import ua.com.fielden.platform.entity.annotation.DateOnly;
+import ua.com.fielden.platform.entity.annotation.DependentTimeZoneMode;
import ua.com.fielden.platform.entity.annotation.IsProperty;
import ua.com.fielden.platform.entity.annotation.KeyTitle;
import ua.com.fielden.platform.entity.annotation.KeyType;
@@ -41,6 +43,35 @@ public class TgEntityWithTimeZoneDates extends AbstractPersistentEntity
@AfterChange(UtcDatesToLocalDefiner.class)
private Date datePropUtc;
+ @IsProperty
+ @MapTo
+ @Title("Date Prop Dependent")
+ @DependentTimeZoneMode
+ private Date datePropDependent;
+
+ @IsProperty
+ @MapTo
+ @Title("Date Only Prop")
+ @DateOnly
+ private Date dateOnlyProp;
+
+ @IsProperty
+ @MapTo
+ @Title("Date Only Prop UTC")
+ @PersistentType(userType = IUtcDateTimeType.class)
+ @DateOnly
+ private Date dateOnlyPropUtc;
+
+ @Observable
+ public TgEntityWithTimeZoneDates setDatePropDependent(final Date datePropDependent) {
+ this.datePropDependent = datePropDependent;
+ return this;
+ }
+
+ public Date getDatePropDependent() {
+ return datePropDependent;
+ }
+
@Observable
public TgEntityWithTimeZoneDates setDatePropUtc(final Date datePropUtc) {
this.datePropUtc = datePropUtc;
@@ -60,4 +91,24 @@ public TgEntityWithTimeZoneDates setDateProp(final Date dateProp) {
public Date getDateProp() {
return dateProp;
}
+
+ @Observable
+ public TgEntityWithTimeZoneDates setDateOnlyProp(final Date dateOnlyProp) {
+ this.dateOnlyProp = dateOnlyProp;
+ return this;
+ }
+
+ public Date getDateOnlyProp() {
+ return dateOnlyProp;
+ }
+
+ @Observable
+ public TgEntityWithTimeZoneDates setDateOnlyPropUtc(final Date dateOnlyPropUtc) {
+ this.dateOnlyPropUtc = dateOnlyPropUtc;
+ return this;
+ }
+
+ public Date getDateOnlyPropUtc() {
+ return dateOnlyPropUtc;
+ }
}
\ No newline at end of file
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/api/impl/TgJackson.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/api/impl/TgJackson.java
index 616413ee75f..1cf13350c88 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/api/impl/TgJackson.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/api/impl/TgJackson.java
@@ -5,7 +5,6 @@
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.type.TypeFactory;
-import com.google.common.base.Charsets;
import jakarta.inject.Inject;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.Logger;
@@ -34,6 +33,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -225,10 +225,10 @@ public byte[] serialise(final Object obj) {
}
try {
- // logger.debug("Serialised pretty JSON = |" + new String(writerWithDefaultPrettyPrinter().writeValueAsBytes(obj), Charsets.UTF_8) + "|.");
+ // logger.debug("Serialised pretty JSON = |" + new String(writerWithDefaultPrettyPrinter().writeValueAsBytes(obj), StandardCharsets.UTF_8) + "|.");
EntitySerialiser.getContext().reset();
- final byte[] bytes = writeValueAsBytes(obj); // default encoding is Charsets.UTF_8
- logger.debug("Serialised JSON = |" + new String(bytes, Charsets.UTF_8) + "|.");
+ final byte[] bytes = writeValueAsBytes(obj); // default encoding is StandardCharsets.UTF_8
+ logger.debug("Serialised JSON = |" + new String(bytes, StandardCharsets.UTF_8) + "|.");
return bytes;
} catch (final JsonProcessingException e) {
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/DefaultValueContract.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/DefaultValueContract.java
index edb5be8b8b1..2bbe8c7d794 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/DefaultValueContract.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/DefaultValueContract.java
@@ -1,19 +1,5 @@
package ua.com.fielden.platform.serialisation.jackson;
-import static java.lang.Boolean.FALSE;
-import static java.util.Optional.empty;
-import static java.util.Optional.of;
-import static ua.com.fielden.platform.entity.annotation.IsProperty.DEFAULT_DISPLAY_AS;
-import static ua.com.fielden.platform.entity.annotation.IsProperty.DEFAULT_LENGTH;
-import static ua.com.fielden.platform.entity.annotation.IsProperty.DEFAULT_PRECISION;
-import static ua.com.fielden.platform.entity.annotation.IsProperty.DEFAULT_SCALE;
-import static ua.com.fielden.platform.entity.annotation.IsProperty.DEFAULT_TRAILING_ZEROS;
-import static ua.com.fielden.platform.reflection.TitlesDescsGetter.getDefaultEntityTitleAndDesc;
-import static ua.com.fielden.platform.types.tuples.T2.t2;
-import static ua.com.fielden.platform.utils.EntityUtils.equalsEx;
-
-import java.util.Optional;
-
import ua.com.fielden.platform.entity.AbstractEntity;
import ua.com.fielden.platform.entity.annotation.DateOnly;
import ua.com.fielden.platform.entity.annotation.PersistentType;
@@ -26,15 +12,24 @@
import ua.com.fielden.platform.types.markers.IUtcDateTimeType;
import ua.com.fielden.platform.types.tuples.T2;
-/**
- * A set of utilities to determine if the value of some property or meta-info is default. It is used internally for Jackson entity serialiser to significantly reduce the amount of
- * the information to be serialised.
- *
- * @author TG Team
- *
- */
+import java.util.Optional;
+
+import static java.lang.Boolean.FALSE;
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
+import static ua.com.fielden.platform.entity.annotation.IsProperty.*;
+import static ua.com.fielden.platform.reflection.AnnotationReflector.isPropertyAnnotationPresent;
+import static ua.com.fielden.platform.reflection.TitlesDescsGetter.getDefaultEntityTitleAndDesc;
+import static ua.com.fielden.platform.types.tuples.T2.t2;
+import static ua.com.fielden.platform.utils.EntityUtils.equalsEx;
+
+/// A set of utilities to determine if the value of some property or meta-info is default.
+/// It is used internally for Jackson entity serialiser to significantly reduce the amount of the information to be serialised.
+///
public class DefaultValueContract {
private static final String UTC = "UTC";
+ public static final String DATE_ONLY = "DATE";
+ public static final String TIME_ONLY = "TIME";
private DefaultValueContract() {
}
@@ -134,18 +129,14 @@ public static boolean isUtc(final Class> entityType, final String propertyName
return UTC.equals(getTimeZone(entityType, propertyName));
}
- /**
- * Returns the value that indicates what portion of date property to display.
- *
- * @param entityType
- * @param propertyName
- * @return
- */
+ /// Returns the value that indicates which portion of date property to display.
+ ///
public static String getTimePortionToDisplay(final Class> entityType, final String propertyName) {
- if (AnnotationReflector.isPropertyAnnotationPresent(DateOnly.class, entityType, propertyName)) {
- return "DATE";
- } else if (AnnotationReflector.isPropertyAnnotationPresent(TimeOnly.class, entityType, propertyName)) {
- return "TIME";
+ if (isPropertyAnnotationPresent(DateOnly.class, entityType, propertyName)) {
+ return DATE_ONLY;
+ }
+ else if (isPropertyAnnotationPresent(TimeOnly.class, entityType, propertyName)) {
+ return TIME_ONLY;
}
return null;
}
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntitySerialiser.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntitySerialiser.java
index bc8ea007c92..c785258a104 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntitySerialiser.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntitySerialiser.java
@@ -199,12 +199,16 @@ private static > EntityType createEntityTypeInfo(fin
if (!isIgnoreDefault(ignore)) {
entityTypeProp.set_ignore(ignore);
}
- if (isPropertyAnnotationPresent(DateOnly.class, type, name)) {
+ final var timePortion = getTimePortionToDisplay(type, name);
+ if (DATE_ONLY.equals(timePortion)) {
entityTypeProp.set_date(TRUE);
}
- if (isPropertyAnnotationPresent(TimeOnly.class, type, name)) {
+ else if (TIME_ONLY.equals(timePortion)) {
entityTypeProp.set_time(TRUE);
}
+ if (isPropertyAnnotationPresent(DependentTimeZoneMode.class, type, name)) {
+ entityTypeProp.set_dependentTimeZoneMode(TRUE);
+ }
final IsProperty isPropertyAnnotation = AnnotationReflector.getPropertyAnnotation(IsProperty.class, type, name);
if (isPropertyAnnotation != null) {
final Long length = Long.valueOf(isPropertyAnnotation.length());
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntityTypeProp.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntityTypeProp.java
index 9e882f50900..74f9dbf5c33 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntityTypeProp.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/EntityTypeProp.java
@@ -58,6 +58,10 @@ public class EntityTypeProp extends AbstractEntity {
@Title(value = "TimeZone", desc = "TimeZone of date-typed property.")
private String _timeZone;
+ @IsProperty
+ @Title(value = "Dependent Time-zone Mode", desc = "Dependent Time-zone Mode of date-typed property.")
+ private Boolean _dependentTimeZoneMode;
+
@IsProperty
@Title(value = "Is Date Only?", desc = "Should display only date portion?")
private Boolean _date;
@@ -156,6 +160,16 @@ public Boolean get_date() {
return _date;
}
+ @Observable
+ public EntityTypeProp set_dependentTimeZoneMode(final Boolean _dependentTimeZoneMode) {
+ this._dependentTimeZoneMode = _dependentTimeZoneMode;
+ return this;
+ }
+
+ public Boolean get_dependentTimeZoneMode() {
+ return _dependentTimeZoneMode;
+ }
+
@Observable
public EntityTypeProp set_timeZone(final String _timeZone) {
this._timeZone = _timeZone;
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/TgSimpleDeserialisers.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/TgSimpleDeserialisers.java
index 0a4dad2571b..383b1c62ff3 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/TgSimpleDeserialisers.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/TgSimpleDeserialisers.java
@@ -4,8 +4,8 @@
import static org.apache.logging.log4j.LogManager.getLogger;
import static ua.com.fielden.platform.reflection.PropertyTypeDeterminator.stripIfNeeded;
+import java.time.Duration;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Logger;
@@ -27,7 +27,7 @@ public class TgSimpleDeserialisers extends SimpleDeserializers {
private static final Logger LOGGER = getLogger(TgSimpleDeserialisers.class);
private final transient TgJacksonModule module;
- private final transient Cache,JsonDeserializer>> genClassDeserialisers = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).initialCapacity(1000).build();
+ private final transient Cache,JsonDeserializer>> genClassDeserialisers = CacheBuilder.newBuilder().expireAfterAccess(Duration.ofSeconds(10)).initialCapacity(1000).build();
public TgSimpleDeserialisers(final TgJacksonModule module) {
this.module = module;
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/TgSimpleSerialisers.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/TgSimpleSerialisers.java
index d2d3c31791a..bc8a12cc94d 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/TgSimpleSerialisers.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/serialisation/jackson/TgSimpleSerialisers.java
@@ -4,8 +4,8 @@
import static org.apache.logging.log4j.LogManager.getLogger;
import static ua.com.fielden.platform.reflection.PropertyTypeDeterminator.stripIfNeeded;
+import java.time.Duration;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.Logger;
@@ -26,7 +26,7 @@ public class TgSimpleSerialisers extends SimpleSerializers {
private static final Logger LOGGER = getLogger(TgSimpleSerialisers.class);
private final transient TgJacksonModule module;
- public final transient Cache, JsonSerializer>> genClassSerialisers = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).initialCapacity(1000).build();
+ public final transient Cache, JsonSerializer>> genClassSerialisers = CacheBuilder.newBuilder().expireAfterAccess(Duration.ofSeconds(10)).initialCapacity(1000).build();
public TgSimpleSerialisers(final TgJacksonModule module) {
this.module = module;
diff --git a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/EntityUtils.java b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/EntityUtils.java
index b8b6c0a1db2..9a05fb751a2 100644
--- a/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/EntityUtils.java
+++ b/platform-pojo-bl/src/main/java/ua/com/fielden/platform/utils/EntityUtils.java
@@ -37,9 +37,9 @@
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.NumberFormat;
+import java.time.Duration;
import java.util.*;
import java.util.Optional;
-import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -67,9 +67,9 @@
public class EntityUtils {
private static final Logger logger = getLogger();
- private static final Cache, Boolean> persistentTypes = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).initialCapacity(512).build();
- private static final Cache, Boolean> syntheticTypes = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).initialCapacity(512).build();
- private static final Cache, Boolean> entityCriteriaTypes = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).initialCapacity(512).build();
+ private static final Cache, Boolean> persistentTypes = CacheBuilder.newBuilder().expireAfterAccess(Duration.ofSeconds(10)).initialCapacity(512).build();
+ private static final Cache, Boolean> syntheticTypes = CacheBuilder.newBuilder().expireAfterAccess(Duration.ofSeconds(10)).initialCapacity(512).build();
+ private static final Cache, Boolean> entityCriteriaTypes = CacheBuilder.newBuilder().expireAfterAccess(Duration.ofSeconds(10)).initialCapacity(512).build();
public static final String ERR_PERSISTENT_NATURE_OF_ENTITY_TYPE = "Could not determine persistent nature of entity type [%s].";
diff --git a/platform-web-resources/pom.xml b/platform-web-resources/pom.xml
index 9c528ce0836..15f0064e7fe 100644
--- a/platform-web-resources/pom.xml
+++ b/platform-web-resources/pom.xml
@@ -5,7 +5,7 @@
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
platform-web-resources
diff --git a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/ioc/WebResourceLoader.java b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/ioc/WebResourceLoader.java
index fbb8bb2554e..9e73c4d9870 100644
--- a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/ioc/WebResourceLoader.java
+++ b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/ioc/WebResourceLoader.java
@@ -28,8 +28,8 @@
import java.io.InputStream;
import java.util.*;
-import static com.google.common.base.Charsets.UTF_8;
import static java.lang.String.format;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.sort;
import static java.util.Optional.empty;
import static java.util.Optional.ofNullable;
diff --git a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/FileResource.java b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/FileResource.java
index 350ef0282f4..c5d9a728316 100644
--- a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/FileResource.java
+++ b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/FileResource.java
@@ -19,7 +19,7 @@
import java.util.Set;
import java.util.function.Supplier;
-import static com.google.common.base.Charsets.UTF_8;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static org.restlet.data.MediaType.*;
diff --git a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/GraphiQLResource.java b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/GraphiQLResource.java
index 76e1283ca4e..df96fd8dd56 100644
--- a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/GraphiQLResource.java
+++ b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/GraphiQLResource.java
@@ -1,6 +1,6 @@
package ua.com.fielden.platform.web.resources.webui;
-import static com.google.common.base.Charsets.UTF_8;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.restlet.data.MediaType.TEXT_HTML;
import static ua.com.fielden.platform.web.resources.RestServerUtil.encodedRepresentation;
diff --git a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/MasterTestsComponentResource.java b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/MasterTestsComponentResource.java
index f70997d57f8..7bbde18de50 100644
--- a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/MasterTestsComponentResource.java
+++ b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/MasterTestsComponentResource.java
@@ -12,7 +12,7 @@
import org.restlet.representation.Representation;
import org.restlet.resource.Get;
-import com.google.common.base.Charsets;
+import java.nio.charset.StandardCharsets;
import ua.com.fielden.platform.entity.AbstractEntity;
import ua.com.fielden.platform.utils.IDates;
@@ -51,6 +51,6 @@ public MasterTestsComponentResource(
@Get
@Override
public Representation get() {
- return new EncodeRepresentation(Encoding.GZIP, new InputRepresentation(new ByteArrayInputStream(master.render().toString().getBytes(Charsets.UTF_8)), MediaType.TEXT_HTML));
+ return new EncodeRepresentation(Encoding.GZIP, new InputRepresentation(new ByteArrayInputStream(master.render().toString().getBytes(StandardCharsets.UTF_8)), MediaType.TEXT_HTML));
}
}
\ No newline at end of file
diff --git a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/ServiceWorkerResource.java b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/ServiceWorkerResource.java
index f2fc4c1ab12..ac23f748c8f 100644
--- a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/ServiceWorkerResource.java
+++ b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/resources/webui/ServiceWorkerResource.java
@@ -1,6 +1,6 @@
package ua.com.fielden.platform.web.resources.webui;
-import static com.google.common.base.Charsets.UTF_8;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.restlet.data.MediaType.TEXT_JAVASCRIPT;
import static ua.com.fielden.platform.web.resources.RestServerUtil.encodedRepresentation;
diff --git a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/test/server/TgTestApplicationServerIocModule.java b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/test/server/TgTestApplicationServerIocModule.java
index f50bddb016f..1aa0dc9e988 100644
--- a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/test/server/TgTestApplicationServerIocModule.java
+++ b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/test/server/TgTestApplicationServerIocModule.java
@@ -22,9 +22,9 @@
import ua.com.fielden.platform.web.annotations.AppUri;
import ua.com.fielden.platform.web.interfaces.IUserPreferencesProvider;
+import java.time.Duration;
import java.util.List;
import java.util.Properties;
-import java.util.concurrent.TimeUnit;
import static java.lang.String.format;
@@ -70,7 +70,7 @@ protected void configure() {
@Singleton
@SessionCache Cache provideSessionCache(final @UntrustedDeviceSessionDuration int untrustedDeviceSessionDurationMins) {
return CacheBuilder.newBuilder()
- .expireAfterWrite(untrustedDeviceSessionDurationMins / 2, TimeUnit.MINUTES)
+ .expireAfterWrite(Duration.ofMinutes(untrustedDeviceSessionDurationMins / 2))
.build();
}
diff --git a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/test/server/config/TgEntityWithTimeZoneDatesWebUiConfig.java b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/test/server/config/TgEntityWithTimeZoneDatesWebUiConfig.java
index f1815034424..d0dc9ec1033 100644
--- a/platform-web-resources/src/main/java/ua/com/fielden/platform/web/test/server/config/TgEntityWithTimeZoneDatesWebUiConfig.java
+++ b/platform-web-resources/src/main/java/ua/com/fielden/platform/web/test/server/config/TgEntityWithTimeZoneDatesWebUiConfig.java
@@ -68,7 +68,8 @@ private EntityCentre createCentre(final Injector inje
.withSummary("total_count_", "COUNT(SELF)", "Count:The total number of matching TgEntityWithTimeZoneDates.")
.withAction(standardEditAction).also()
.addProp("dateProp").minWidth(100).also()
- .addProp("datePropUtc").minWidth(100)
+ .addProp("datePropUtc").minWidth(100).also()
+ .addProp("datePropDependent").minWidth(100)
.addPrimaryAction(standardEditAction)
.build();
@@ -76,11 +77,14 @@ private EntityCentre createCentre(final Injector inje
return entityCentre;
}
private EntityMaster createMaster(final Injector injector) {
- final String layout = LayoutComposer.mkGridForMaster(640, 1, 2);
+ final String layout = LayoutComposer.mkGridForMaster(960, 1, 5);
final IMaster masterConfig = new SimpleMasterBuilder().forEntity(TgEntityWithTimeZoneDates.class)
.addProp("dateProp").asDateTimePicker().also()
.addProp("datePropUtc").asDateTimePicker().also()
+ .addProp("datePropDependent").asDateTimePicker().also()
+ .addProp("dateOnlyProp").asDatePicker().also()
+ .addProp("dateOnlyPropUtc").asDatePicker().also()
.addAction(MasterActions.REFRESH).shortDesc("Cancel").longDesc("Cancel action")
.addAction(MasterActions.SAVE)
.setActionBarLayoutFor(Device.DESKTOP, Optional.empty(), LayoutComposer.mkActionLayoutForMaster())
diff --git a/platform-web-ui/pom.xml b/platform-web-ui/pom.xml
index a74a9cc78c2..6dd2a5f53ed 100644
--- a/platform-web-ui/pom.xml
+++ b/platform-web-ui/pom.xml
@@ -3,7 +3,7 @@
fielden
platform-parent
- 2.4.4-SNAPSHOT
+ 2.4.5-SNAPSHOT
platform-web-ui
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/svg/combining/IronIconsetUtility.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/svg/combining/IronIconsetUtility.java
index f5cd6f9b177..2840ddb0bb2 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/svg/combining/IronIconsetUtility.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/svg/combining/IronIconsetUtility.java
@@ -9,6 +9,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.DirectoryIteratorException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -18,8 +19,6 @@
import java.util.List;
import java.util.Set;
-import com.google.common.base.Charsets;
-
public class IronIconsetUtility {
public static final String FILE_BEGIN_TEMPLATE = "import '/resources/polymer/@polymer/iron-icon/iron-icon.js';%n" +
"import '/resources/polymer/@polymer/iron-iconset-svg/iron-iconset-svg.js';%n" +
@@ -39,7 +38,7 @@ public IronIconsetUtility(final String iconsetId, final int svgWidth, final Stri
public void createSvgIconset(final String outputFile) throws IOException {
try (OutputStream outputStream = new FileOutputStream(outputFile)) {
- outputStream.write(joinFilesContent().getBytes(Charsets.UTF_8));
+ outputStream.write(joinFilesContent().getBytes(StandardCharsets.UTF_8));
}
}
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/CentreDiffSerialiser.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/CentreDiffSerialiser.java
index 9ac4af743ca..80fa8c2b79c 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/CentreDiffSerialiser.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/CentreDiffSerialiser.java
@@ -35,8 +35,8 @@ private CentreDiffSerialiser() {
*/
public byte[] serialise(final Map diff) {
try {
- // logger.error("Serialised pretty JSON = |" + new String(writerWithDefaultPrettyPrinter().writeValueAsBytes(diff), Charsets.UTF_8) + "|.");
- final byte[] bytes = writeValueAsBytes(diff); // default encoding is Charsets.UTF_8
+ // logger.error("Serialised pretty JSON = |" + new String(writerWithDefaultPrettyPrinter().writeValueAsBytes(diff), StandardCharsets.UTF_8) + "|.");
+ final byte[] bytes = writeValueAsBytes(diff); // default encoding is StandardCharsets.UTF_8
return bytes;
} catch (final JsonProcessingException ex) {
throw new SerialisationException("Error during centre diff serialisation.", ex);
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateCriterionWidget.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateCriterionWidget.java
index 3e0bdb6174c..d7caad20f26 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateCriterionWidget.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateCriterionWidget.java
@@ -1,41 +1,29 @@
package ua.com.fielden.platform.web.centre.api.crit.impl;
-import java.util.Map;
-
import ua.com.fielden.platform.entity.AbstractEntity;
-import ua.com.fielden.platform.serialisation.jackson.DefaultValueContract;
import ua.com.fielden.platform.web.view.master.api.widgets.datetimepicker.impl.DateTimePickerWidget;
-/**
- * An implementation for date double-editor criterion.
- *
- * @author TG Team
- *
- */
+import java.util.Map;
+
+/// An implementation for date double-editor criterion.
+///
public class DateCriterionWidget extends AbstractRangeCriterionWidget {
- /**
- * Creates an instance of {@link DateCriterionWidget} for specified entity type and property name.
- *
- * @param criteriaType
- * @param propertyName
- */
+ /// Creates an instance of [DateCriterionWidget] for specified entity type and property name.
+ ///
public DateCriterionWidget(final Class extends AbstractEntity>> root, final Class> managedType, final String propertyName) {
super(root, "centre/criterion/multi/range/tg-date-range-criterion", propertyName,
- new DateTimePickerWidget(
- AbstractCriterionWidget.generateTitleDesc(root, managedType, propertyName).getKey(),
- AbstractCriterionWidget.generateNames(root, managedType, propertyName).getKey(),
- false,
- DefaultValueContract.getTimeZone(managedType, propertyName),
- DefaultValueContract.getTimePortionToDisplay(managedType, propertyName)
- ),
- new DateTimePickerWidget(
- AbstractCriterionWidget.generateTitleDesc(root, managedType, propertyName).getValue(),
- AbstractCriterionWidget.generateNames(root, managedType, propertyName).getValue(),
- true,
- DefaultValueContract.getTimeZone(managedType, propertyName),
- DefaultValueContract.getTimePortionToDisplay(managedType, propertyName)
- ));
+ new DateTimePickerWidget(
+ generateTitleDesc(root, managedType, propertyName).getKey(),
+ generateNames(root, managedType, propertyName).getKey(),
+ false
+ ),
+ new DateTimePickerWidget(
+ generateTitleDesc(root, managedType, propertyName).getValue(),
+ generateNames(root, managedType, propertyName).getValue(),
+ true
+ )
+ );
}
@Override
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateSingleCriterionWidget.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateSingleCriterionWidget.java
index 7a88e0a974d..30c62fe4892 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateSingleCriterionWidget.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/crit/impl/DateSingleCriterionWidget.java
@@ -1,31 +1,19 @@
package ua.com.fielden.platform.web.centre.api.crit.impl;
import ua.com.fielden.platform.entity.AbstractEntity;
-import ua.com.fielden.platform.serialisation.jackson.DefaultValueContract;
import ua.com.fielden.platform.web.view.master.api.widgets.datetimepicker.impl.DateTimePickerWidget;
-/**
- * An implementation for date single-editor criterion.
- *
- * @author TG Team
- *
- */
+/// An implementation for date single-editor criterion.
+///
public class DateSingleCriterionWidget extends AbstractSingleCriterionWidget {
- /**
- * Creates an instance of {@link DateSingleCriterionWidget} for specified entity type and property name.
- *
- * @param criteriaType
- * @param propertyName
- */
+ /// Creates an instance of [DateSingleCriterionWidget] for specified entity type and property name.
+ ///
public DateSingleCriterionWidget(final Class extends AbstractEntity>> root, final Class> managedType, final String propertyName) {
- super(root, propertyName,
- new DateTimePickerWidget(
- AbstractCriterionWidget.generateSingleTitleDesc(root, managedType, propertyName),
- AbstractCriterionWidget.generateSingleName(root, managedType, propertyName),
- false,
- DefaultValueContract.getTimeZone(managedType, propertyName),
- DefaultValueContract.getTimePortionToDisplay(managedType, propertyName)
- ));
+ super(root, propertyName, new DateTimePickerWidget(
+ generateSingleTitleDesc(root, managedType, propertyName),
+ generateSingleName(root, managedType, propertyName),
+ false
+ ));
}
}
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/impl/ResultSetBuilder.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/impl/ResultSetBuilder.java
index f46e88c4f37..2a3641afc4c 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/impl/ResultSetBuilder.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/centre/api/impl/ResultSetBuilder.java
@@ -7,7 +7,6 @@
import ua.com.fielden.platform.entity.fetch.IFetchProvider;
import ua.com.fielden.platform.reflection.PropertyTypeDeterminator;
import ua.com.fielden.platform.reflection.TitlesDescsGetter;
-import ua.com.fielden.platform.serialisation.jackson.DefaultValueContract;
import ua.com.fielden.platform.types.Colour;
import ua.com.fielden.platform.types.Hyperlink;
import ua.com.fielden.platform.types.Money;
@@ -161,9 +160,7 @@ private Optional createWidget(final String propName) {
} else if (isBoolean(propertyType)) {
return of(new CheckboxWidget(pair("", TitlesDescsGetter.getTitleAndDesc(propName, root).getValue()), propName));
} else if (isDate(propertyType)) {
- return of(new DateTimePickerWidget(pair("", TitlesDescsGetter.getTitleAndDesc(propName, root).getValue()), propName, false,
- DefaultValueContract.getTimeZone(root, propName),
- DefaultValueContract.getTimePortionToDisplay(root, propName)));
+ return of(new DateTimePickerWidget(pair("", TitlesDescsGetter.getTitleAndDesc(propName, root).getValue()), propName, false));
} else if (isCollectional(propertyType)) {
return of(new CollectionalRepresentorWidget(pair("", TitlesDescsGetter.getTitleAndDesc(propName, root).getValue()),propName));
}
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/menu/iconset/SvgButtonsIconset.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/menu/iconset/SvgButtonsIconset.java
index 175ca07a000..063fa223c72 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/menu/iconset/SvgButtonsIconset.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/menu/iconset/SvgButtonsIconset.java
@@ -5,8 +5,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-
-import com.google.common.base.Charsets;
+import java.nio.charset.StandardCharsets;
import ua.com.fielden.platform.svg.combining.IronIconsetUtility;
@@ -19,7 +18,7 @@ public static void main(final String[] args) throws IOException {
"";
for (int index = 1; index <= 9; index++) {
try (OutputStream outputStream = new FileOutputStream(format("src/main/resources/images/collapse-expand/number%s.svg", index))) {
- outputStream.write(numberSvgTemplate.replace('@', (index + "").charAt(0)).getBytes(Charsets.UTF_8));
+ outputStream.write(numberSvgTemplate.replace('@', (index + "").charAt(0)).getBytes(StandardCharsets.UTF_8));
}
}
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/helpers/impl/WidgetSelector.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/helpers/impl/WidgetSelector.java
index 8044b2caa04..d17450a0057 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/helpers/impl/WidgetSelector.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/helpers/impl/WidgetSelector.java
@@ -5,8 +5,6 @@
import ua.com.fielden.platform.entity.annotation.DateOnly;
import ua.com.fielden.platform.entity.annotation.TimeOnly;
import ua.com.fielden.platform.reflection.PropertyTypeDeterminator;
-import ua.com.fielden.platform.reflection.TitlesDescsGetter;
-import ua.com.fielden.platform.serialisation.jackson.DefaultValueContract;
import ua.com.fielden.platform.web.view.master.api.helpers.IWidgetSelector;
import ua.com.fielden.platform.web.view.master.api.impl.SimpleMasterBuilder;
import ua.com.fielden.platform.web.view.master.api.widgets.*;
@@ -29,7 +27,8 @@
import java.util.Optional;
import static java.lang.String.format;
-import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.getTimePortionToDisplay;
+import static ua.com.fielden.platform.reflection.TitlesDescsGetter.getTitleAndDesc;
+import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.*;
public class WidgetSelector> implements IWidgetSelector {
@@ -79,15 +78,15 @@ public static > EntityAutocompletionWidget createAut
final var declaredPropType = StringUtils.isEmpty(propertyName) ? entityType : PropertyTypeDeterminator.determinePropertyType(entityType, propertyName);
return optPropType.map(propType -> {
if (String.class.isAssignableFrom(declaredPropType)) {
- return new EntityAutocompletionWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, entityType), propertyName, propType, true);
+ return new EntityAutocompletionWidget(getTitleAndDesc(propertyName, entityType), propertyName, propType, true);
} else if (propType.equals(declaredPropType)) {
- return new EntityAutocompletionWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, entityType), propertyName, propType, false);
+ return new EntityAutocompletionWidget(getTitleAndDesc(propertyName, entityType), propertyName, propType, false);
} else {
throw new EntityMasterConfigurationException(format(ERR_INVALID_AUTOCOMPLETER_TYPE, propType.getTypeName(), entityType.getSimpleName(), propertyName, declaredPropType.getTypeName()));
}
}).orElseGet(() -> {
if (AbstractEntity.class.isAssignableFrom(declaredPropType)) {
- return new EntityAutocompletionWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, entityType), propertyName, (Class extends AbstractEntity>>) declaredPropType, false);
+ return new EntityAutocompletionWidget(getTitleAndDesc(propertyName, entityType), propertyName, (Class extends AbstractEntity>>) declaredPropType, false);
} else {
throw new EntityMasterConfigurationException(format(ERR_INVALID_PROPERTY_FOR_AUTOCOMPLETION, entityType.getSimpleName(), propertyName, declaredPropType.getTypeName()));
}
@@ -96,31 +95,31 @@ public static > EntityAutocompletionWidget createAut
@Override
public ISinglelineTextConfig asSinglelineText() {
- widget = new SinglelineTextWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
+ widget = new SinglelineTextWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
return new SinglelineTextConfig<>((SinglelineTextWidget) widget, smBuilder);
}
@Override
public ICollectionalRepresentorConfig asCollectionalRepresentor() {
- widget = new CollectionalRepresentorWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
+ widget = new CollectionalRepresentorWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
return new CollectionalRepresentorConfig<>((CollectionalRepresentorWidget) widget, smBuilder);
}
@Override
public ICollectionalEditorConfig asCollectionalEditor() {
- widget = new CollectionalEditorWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
+ widget = new CollectionalEditorWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
return new CollectionalEditorConfig<>((CollectionalEditorWidget) widget, smBuilder);
}
@Override
public IMultilineTextConfig asMultilineText() {
- widget = new MultilineTextWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), smBuilder.getEntityType(), propertyName);
+ widget = new MultilineTextWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), smBuilder.getEntityType(), propertyName);
return new MultilineTextConfig<>((MultilineTextWidget) widget, smBuilder);
}
@Override
public IRichTextConfig asRichText() {
- widget = new RichTextWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), smBuilder.getEntityType(), propertyName);
+ widget = new RichTextWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), smBuilder.getEntityType(), propertyName);
return new RichTextConfig<>((RichTextWidget)widget, smBuilder);
}
@@ -138,13 +137,7 @@ public IFileConfig asFile() {
public IDateTimePickerConfig asDateTimePicker() {
final String timePortion = getTimePortionToDisplay(smBuilder.getEntityType(), propertyName);
if (timePortion == null) {
- widget = new DateTimePickerWidget(
- TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()),
- propertyName,
- false,
- DefaultValueContract.getTimeZone(smBuilder.getEntityType(), propertyName),
- null
- );
+ widget = new DateTimePickerWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName, false);
return new DateTimePickerConfig<>((DateTimePickerWidget) widget, smBuilder);
}
throw new EntityMasterConfigurationException(format(ERR_INVALID_DATEPICKER_CHOICE,
@@ -153,15 +146,8 @@ public IDateTimePickerConfig asDateTimePicker() {
@Override
public IDatePickerConfig asDatePicker() {
- final String DATE_ONLY = "DATE";
if (DATE_ONLY.equals(getTimePortionToDisplay(smBuilder.getEntityType(), propertyName))) {
- widget = new DateTimePickerWidget(
- TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()),
- propertyName,
- false,
- DefaultValueContract.getTimeZone(smBuilder.getEntityType(), propertyName),
- DATE_ONLY
- );
+ widget = new DateTimePickerWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName, false);
return new DatePickerConfig<>((DateTimePickerWidget) widget, smBuilder);
}
throw new EntityMasterConfigurationException(format(ERR_INVALID_DATEPICKER_CHOICE, propertyName, smBuilder.getEntityType().getSimpleName(), DateOnly.class.getSimpleName()));
@@ -169,15 +155,8 @@ public IDatePickerConfig asDatePicker() {
@Override
public ITimePickerConfig asTimePicker() {
- final String TIME_ONLY = "TIME";
if (TIME_ONLY.equals(getTimePortionToDisplay(smBuilder.getEntityType(), propertyName))) {
- widget = new DateTimePickerWidget(
- TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()),
- propertyName,
- false,
- DefaultValueContract.getTimeZone(smBuilder.getEntityType(), propertyName),
- TIME_ONLY
- );
+ widget = new DateTimePickerWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName, false);
return new TimePickerConfig<>((DateTimePickerWidget) widget, smBuilder);
}
throw new EntityMasterConfigurationException(format(ERR_INVALID_DATEPICKER_CHOICE, propertyName, smBuilder.getEntityType().getSimpleName(), TimeOnly.class.getSimpleName()));
@@ -185,13 +164,13 @@ public ITimePickerConfig asTimePicker() {
@Override
public IDecimalConfig asDecimal() {
- widget = new DecimalWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
+ widget = new DecimalWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
return new DecimalConfig<>((DecimalWidget) widget, smBuilder);
}
@Override
public ISpinnerConfig asSpinner() {
- widget = new SpinnerWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
+ widget = new SpinnerWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
return new SpinnerConfig<>((SpinnerWidget) widget, smBuilder);
}
@@ -202,13 +181,13 @@ public ISpinnerConfig asInteger() {
@Override
public IMoneyConfig asMoney() {
- widget = new MoneyWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
+ widget = new MoneyWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
return new MoneyConfig<>((MoneyWidget) widget, smBuilder);
}
@Override
public ICheckboxConfig asCheckbox() {
- widget = new CheckboxWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
+ widget = new CheckboxWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
return new CheckboxConfig<>((CheckboxWidget) widget, smBuilder);
}
@@ -224,13 +203,13 @@ public IEmailConfig asEmail() {
@Override
public IColourConfig asColour() {
- widget = new ColourWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
+ widget = new ColourWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
return new ColourConfig<>((ColourWidget) widget, smBuilder);
}
@Override
public IHyperlinkConfig asHyperlink() {
- widget = new HyperlinkWidget(TitlesDescsGetter.getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
+ widget = new HyperlinkWidget(getTitleAndDesc(propertyName, smBuilder.getEntityType()), propertyName);
return new HyperlinkConfig<>((HyperlinkWidget) widget, smBuilder);
}
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java
index b4c405a9912..12122a2770c 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/api/widgets/datetimepicker/impl/DateTimePickerWidget.java
@@ -1,44 +1,26 @@
package ua.com.fielden.platform.web.view.master.api.widgets.datetimepicker.impl;
-import java.util.Map;
-
import ua.com.fielden.platform.utils.Pair;
import ua.com.fielden.platform.web.view.master.api.widgets.impl.AbstractWidget;
-/**
- * The implementation for web date-time picker widgets.
- *
- * @author TG Team
- *
- */
+import java.util.Map;
+
+/// The implementation for Web UI date-time picker widgets.
+///
public class DateTimePickerWidget extends AbstractWidget {
private final boolean timePortionToBecomeEndOfDay;
- private final String timeZone;
- private final String datePortion;
- /**
- * Creates an instance of {@link DateTimePickerWidget} for specified entity type and property name.
- *
- * @param titleDesc
- * @param propertyName
- */
- public DateTimePickerWidget(final Pair titleDesc, final String propertyName, final boolean timePortionToBecomeEndOfDay, final String timeZone, final String datePortion) {
+ /// Creates an instance of [DateTimePickerWidget] for specified entity type and property name.
+ ///
+ public DateTimePickerWidget(final Pair titleDesc, final String propertyName, final boolean timePortionToBecomeEndOfDay) {
super("editors/tg-datetime-picker", titleDesc, propertyName);
this.timePortionToBecomeEndOfDay = timePortionToBecomeEndOfDay;
- this.timeZone = timeZone;
- this.datePortion = datePortion;
}
@Override
protected Map createCustomAttributes() {
final Map customAttr = super.createCustomAttributes();
customAttr.put("time-portion-to-become-end-of-day", timePortionToBecomeEndOfDay);
- if (timeZone != null) {
- customAttr.put("time-zone", timeZone);
- }
- if (datePortion != null) {
- customAttr.put("date-portion", datePortion);
- }
return customAttr;
}
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/chart/decker/api/impl/ChartDeckerMaster.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/chart/decker/api/impl/ChartDeckerMaster.java
index ca901e70895..c58a9b5e03c 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/chart/decker/api/impl/ChartDeckerMaster.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/chart/decker/api/impl/ChartDeckerMaster.java
@@ -21,11 +21,8 @@
import java.util.function.Function;
import java.util.stream.Stream;
-import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.join;
-import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.getTimePortionToDisplay;
-import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.getTimeZone;
import static ua.com.fielden.platform.web.centre.EntityCentre.IMPORTS;
import static ua.com.fielden.platform.web.centre.api.resultset.impl.FunctionalActionKind.PRIMARY_RESULT_SET;
import static ua.com.fielden.platform.web.view.master.EntityMaster.ENTITY_TYPE;
@@ -119,9 +116,9 @@ private String readyCallback(final IChartDeckerConfig deckerConfig) {
+ " },\n"
+ " lines: " + generateListOfValues(deck.getLines(), l -> generateLine(l)) + ",\n"
+ " dataPropertyNames: {\n"
- + " groupKeyProp: " + generateValueAccessor(deck.getEntityType(), deck.getPropertyType(), deck.getGroupKeyProp()) + ",\n"
+ + " groupKeyProp: " + generateValueAccessor(deck.getPropertyType(), deck.getGroupKeyProp()) + ",\n"
+ " groupDescProp: '" + deck.getGroupDescProperty() + "',\n"
- + " valueProps: " + generateListOfValues(deck.getSeries(), s -> generateValueAccessor(s.getEntityType(), s.getPropertyType(), s.getPropertyName())) + "\n"
+ + " valueProps: " + generateListOfValues(deck.getSeries(), s -> generateValueAccessor(s.getPropertyType(), s.getPropertyName())) + "\n"
+ " },\n"
+ " colours: " + generateListOfValues(deck.getSeries(), s -> "'" + s.getColour().getColourValue() + "'") + ",\n"
+ " barColour: (d, i) => self.barOptions[" + deckIndex + "].colours[i],\n"
@@ -129,8 +126,8 @@ private String readyCallback(final IChartDeckerConfig deckerConfig) {
+ " propertyTypes: " + generateListOfValues(deck.getSeries(), s -> "'" + s.getPropertyType().getSimpleName() + "'") + ",\n"
+ " barLabel: (d, i) => this._labelFormatter(d, i, self.barOptions[" + deckIndex + "].propertyNames, self.barOptions[" + deckIndex + "].propertyTypes, self.barOptions[" + deckIndex + "].mode),\n"
+ " tooltip: (d, i) => this._tooltip(d, "
- + generateValueAccessor(deck.getEntityType(), deck.getPropertyType(), deck.getGroupKeyProp()) + ", "
- + generateValueAccessor(deck.getEntityType(), String.class, deck.getGroupDescProperty()) + ", "
+ + generateValueAccessor(deck.getPropertyType(), deck.getGroupKeyProp()) + ", "
+ + generateValueAccessor(String.class, deck.getGroupDescProperty()) + ", "
+ "self.barOptions[" + deckIndex + "].propertyNames[i], "
+ "self.barOptions[" + deckIndex + "].propertyTypes[i], "
+ "self.legendItems[" + deckIndex + "][i].title, " + deckIndex + ", i),\n"
@@ -147,7 +144,7 @@ private String generateLineLegendItem(final ChartLine line) {
}
private String generateLine(final ChartLine line) {
- return "{property: " + generateValueAccessor(line.getEntityType(), line.getPropertyType(), line.getProperty()) + ", title: '" + line.getTitle() + "', colour: '" + line.getColour().getColourValue() + "'}";
+ return "{property: " + generateValueAccessor(line.getPropertyType(), line.getProperty()) + ", title: '" + line.getTitle() + "', colour: '" + line.getColour().getColourValue() + "'}";
}
private String generateListOfValues(final List series, final Function func) {
@@ -167,14 +164,11 @@ private String generateLegendItem(final ChartSeries series) {
return "{title: '" + (isEmpty(series.getTitle()) ? "" : series.getTitle()) + "', colour: '" + series.getColour().getColourValue() + "'}";
}
- private String generateValueAccessor(final Class> deckType, final Class> propertyType, final String aggregationProperty) {
+ private String generateValueAccessor(final Class> propertyType, final String aggregationProperty) {
if (Money.class.isAssignableFrom(propertyType)) {
return "this._moneyPropAccessor('" + aggregationProperty + "')";
} else if (EntityUtils.isDate(propertyType)) {
- final Optional timeZone = ofNullable(getTimeZone(deckType, aggregationProperty));
- final Optional timePortion = ofNullable(getTimePortionToDisplay(deckType, aggregationProperty));
- final String typeSpec = "Date:" + timeZone.orElse(":") + timePortion.orElse("");
- return "this._datePropAccessor('" + aggregationProperty + "', '" + typeSpec + "')";
+ return "this._datePropAccessor('" + aggregationProperty + "')";
}
return "'" + aggregationProperty + "'";
}
diff --git a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/scatterplot/api/implementation/ScatterPlotMaster.java b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/scatterplot/api/implementation/ScatterPlotMaster.java
index b928407155f..5ab5730d51b 100644
--- a/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/scatterplot/api/implementation/ScatterPlotMaster.java
+++ b/platform-web-ui/src/main/java/ua/com/fielden/platform/web/view/master/scatterplot/api/implementation/ScatterPlotMaster.java
@@ -24,9 +24,8 @@
import static java.util.Optional.*;
import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.join;
-import static ua.com.fielden.platform.reflection.TitlesDescsGetter.*;
-import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.getTimePortionToDisplay;
-import static ua.com.fielden.platform.serialisation.jackson.DefaultValueContract.getTimeZone;
+import static ua.com.fielden.platform.reflection.TitlesDescsGetter.getEntityTitleAndDesc;
+import static ua.com.fielden.platform.reflection.TitlesDescsGetter.getTitleAndDesc;
import static ua.com.fielden.platform.web.centre.EntityCentre.IMPORTS;
import static ua.com.fielden.platform.web.centre.api.resultset.impl.FunctionalActionKind.PRIMARY_RESULT_SET;
import static ua.com.fielden.platform.web.view.master.EntityMaster.ENTITY_TYPE;
@@ -154,10 +153,7 @@ private String generateValueAccessor(Class extends AbstractEntity>> entityTy
if (Money.class.isAssignableFrom(propertyType)) {
return "this._moneyPropAccessor('" + propertyName + "')";
} else if (EntityUtils.isDate(propertyType)) {
- final Optional timeZone = ofNullable(getTimeZone(entityType, propertyName));
- final Optional timePortion = ofNullable(getTimePortionToDisplay(entityType, propertyName));
- final String typeSpec = "Date:" + timeZone.orElse(":") + timePortion.orElse("");
- return "this._datePropAccessor('" + propertyName + "', '" + typeSpec + "')";
+ return "this._datePropAccessor('" + propertyName + "')";
}
return "'" + propertyName + "'";
}
diff --git a/platform-web-ui/src/main/resources/package-lock.json b/platform-web-ui/src/main/resources/package-lock.json
index 19934ee3a7f..2507518388a 100644
--- a/platform-web-ui/src/main/resources/package-lock.json
+++ b/platform-web-ui/src/main/resources/package-lock.json
@@ -86,73 +86,73 @@
"optional": true
},
"node_modules/@fullcalendar/core": {
- "version": "6.1.20",
- "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.20.tgz",
- "integrity": "sha512-1cukXLlePFiJ8YKXn/4tMKsy0etxYLCkXk8nUCFi11nRONF2Ba2CD5b21/ovtOO2tL6afTJfwmc1ed3HG7eB1g==",
+ "version": "6.1.21",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.21.tgz",
+ "integrity": "sha512-t3u/+sqh3Iq7TWtUnVLcGDUE6OWZh0UD3c04bI/l7lSLAgAKr3kngBmhHiQD1QXpwC8ZN5iNqG7a7gOVixhSKQ==",
"license": "MIT",
"dependencies": {
"preact": "~10.12.1"
}
},
"node_modules/@fullcalendar/daygrid": {
- "version": "6.1.20",
- "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.20.tgz",
- "integrity": "sha512-AO9vqhkLP77EesmJzuU+IGXgxNulsA8mgQHynclJ8U70vSwAVnbcLG9qftiTAFSlZjiY/NvhE7sflve6cJelyQ==",
+ "version": "6.1.21",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.21.tgz",
+ "integrity": "sha512-QYb1y40RGYLlOxKpYWg8O+7njEnKnFG8Tt7qjnubJGR35s1phQg67E+81y2TyAbbm59p2JFOCXGDk9t6KDujIA==",
"license": "MIT",
"peerDependencies": {
- "@fullcalendar/core": "~6.1.20"
+ "@fullcalendar/core": "~6.1.21"
}
},
"node_modules/@fullcalendar/interaction": {
- "version": "6.1.20",
- "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.20.tgz",
- "integrity": "sha512-p6txmc5txL0bMiPaJxe2ip6o0T384TyoD2KGdsU6UjZ5yoBlaY+dg7kxfnYKpYMzEJLG58n+URrHr2PgNL2fyA==",
+ "version": "6.1.21",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/interaction/-/interaction-6.1.21.tgz",
+ "integrity": "sha512-WPYpqtljDWmU0Xm2cOtFrLlocgxv7cgkOppj34Q6OUUat8a6Cnd6kYo2JR+irP223PE5lBYHFNp1qh7SIpJc0w==",
"license": "MIT",
"peerDependencies": {
- "@fullcalendar/core": "~6.1.20"
+ "@fullcalendar/core": "~6.1.21"
}
},
"node_modules/@fullcalendar/list": {
- "version": "6.1.20",
- "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.20.tgz",
- "integrity": "sha512-7Hzkbb7uuSqrXwTyD0Ld/7SwWNxPD6SlU548vtkIpH55rZ4qquwtwYdMPgorHos5OynHA4OUrZNcH51CjrCf2g==",
+ "version": "6.1.21",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/list/-/list-6.1.21.tgz",
+ "integrity": "sha512-2rpIhs5pJmV7jyk4oX4bckNqurt6iHcsweE3FDYDdNpmRukPrARnyQYcaVFNVw2bnBFeR/jQW/St2MlauxF3GQ==",
"license": "MIT",
"peerDependencies": {
- "@fullcalendar/core": "~6.1.20"
+ "@fullcalendar/core": "~6.1.21"
}
},
"node_modules/@fullcalendar/moment-timezone": {
- "version": "6.1.20",
- "resolved": "https://registry.npmjs.org/@fullcalendar/moment-timezone/-/moment-timezone-6.1.20.tgz",
- "integrity": "sha512-fGk3bQU4hf0rgw3Zd/PH6Ok0Db+s9/nsuALj3IG8GYFqInwLsHZI0Qc+ljN8jv9LrLS5sOBBOZHWDg2ncx1inw==",
+ "version": "6.1.21",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/moment-timezone/-/moment-timezone-6.1.21.tgz",
+ "integrity": "sha512-1H5voLR6PGiIf+JM6TCWq8oMTMOOqs+Am8lo/3GzHnBPo81RN72h87zm50YgGq9HjIsMdNs7RgdMVAUcXByerQ==",
"license": "MIT",
"peerDependencies": {
- "@fullcalendar/core": "~6.1.20",
+ "@fullcalendar/core": "~6.1.21",
"moment-timezone": "^0.5.40"
}
},
"node_modules/@fullcalendar/multimonth": {
- "version": "6.1.20",
- "resolved": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.20.tgz",
- "integrity": "sha512-rMMiPBA71lUJ1DV/0ckPtN4/G4LozkkDKoG7/CbmTYqFJiMRskM/1WpilhtRn4iUdNe03V5K7ofFQRs0wo4ZtQ==",
+ "version": "6.1.21",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/multimonth/-/multimonth-6.1.21.tgz",
+ "integrity": "sha512-/5IZDDcdRzgO/h7V3RBEhzhrza5TIQ2T025IjA2Dfq3xbvxSnMAl2CMcy7VrSHsKaY1eWvxQXg/NZZm7hLmDYA==",
"license": "MIT",
"dependencies": {
- "@fullcalendar/daygrid": "~6.1.20"
+ "@fullcalendar/daygrid": "~6.1.21"
},
"peerDependencies": {
- "@fullcalendar/core": "~6.1.20"
+ "@fullcalendar/core": "~6.1.21"
}
},
"node_modules/@fullcalendar/timegrid": {
- "version": "6.1.20",
- "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.20.tgz",
- "integrity": "sha512-4H+/MWbz3ntA50lrPif+7TsvMeX3R1GSYjiLULz0+zEJ7/Yfd9pupZmAwUs/PBpA6aAcFmeRr0laWfcz1a9V1A==",
+ "version": "6.1.21",
+ "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.21.tgz",
+ "integrity": "sha512-2DnShx/jallGmb8QCkr6pAOu/zuPhJrP7+uTrAtSnbqsX7GF3lTxqSeNGkTQwsgF5g/ia8udhQ+JNYaE+TN1cQ==",
"license": "MIT",
"dependencies": {
- "@fullcalendar/daygrid": "~6.1.20"
+ "@fullcalendar/daygrid": "~6.1.21"
},
"peerDependencies": {
- "@fullcalendar/core": "~6.1.20"
+ "@fullcalendar/core": "~6.1.21"
}
},
"node_modules/@google-web-components/google-chart": {
@@ -1130,9 +1130,9 @@
}
},
"node_modules/dompurify": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.3.tgz",
- "integrity": "sha512-VVwJidIJcp1hpg2OMXML3ZVRPYSZiq4aX7qBh83BSIpOaRDqI+qxhXjjIWnpzkOXhmp0L81lnoME1mnCc9H48A==",
+ "version": "3.4.11",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz",
+ "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
@@ -1224,17 +1224,17 @@
}
},
"node_modules/fullcalendar": {
- "version": "6.1.20",
- "resolved": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-6.1.20.tgz",
- "integrity": "sha512-7lz2P+0YdA86fBTwfr/ducajrM3zUw5o6uoiiYk9xMqcoTf003xBh3mpzW5om9slfAMwGgCBh0KsiYcU1JY1eQ==",
+ "version": "6.1.21",
+ "resolved": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-6.1.21.tgz",
+ "integrity": "sha512-yGZhflQmfCJKmmOlwJcQ0MPNAYABieaiJp9AaRgHBMRIUxNA+VFa6mkIbVNpjdSZIy9AY2HwteTt58tJ+3ktuQ==",
"license": "MIT",
"dependencies": {
- "@fullcalendar/core": "~6.1.20",
- "@fullcalendar/daygrid": "~6.1.20",
- "@fullcalendar/interaction": "~6.1.20",
- "@fullcalendar/list": "~6.1.20",
- "@fullcalendar/multimonth": "~6.1.20",
- "@fullcalendar/timegrid": "~6.1.20"
+ "@fullcalendar/core": "~6.1.21",
+ "@fullcalendar/daygrid": "~6.1.21",
+ "@fullcalendar/interaction": "~6.1.21",
+ "@fullcalendar/list": "~6.1.21",
+ "@fullcalendar/multimonth": "~6.1.21",
+ "@fullcalendar/timegrid": "~6.1.21"
}
},
"node_modules/get-caller-file": {
@@ -1384,10 +1384,20 @@
}
},
"node_modules/js-yaml": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
- "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
- "dev": true,
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
+ "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
+ ],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -1444,9 +1454,9 @@
"license": "MIT"
},
"node_modules/lru-cache": {
- "version": "11.3.6",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz",
- "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==",
+ "version": "11.5.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
+ "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
@@ -1523,9 +1533,9 @@
"license": "MIT"
},
"node_modules/mocha/node_modules/brace-expansion": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
- "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
+ "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1725,9 +1735,9 @@
}
},
"node_modules/prosemirror-model": {
- "version": "1.25.6",
- "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.6.tgz",
- "integrity": "sha512-RIm+e9BiqAaJ1mRECv3vR3C+VG8ELoTTI+47tVudGi82yLnFOx3G/p/iSPK1HmHQdKhkkrJ68NJqxh7S+FBVmQ==",
+ "version": "1.25.9",
+ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.9.tgz",
+ "integrity": "sha512-pRTklkDDMMRopyoAcrr9wV/8g/RYgrLHBuJAb5hlEuYZRdm5yqmPjWId83fpBwPpSFqEdja0H7Dfd7z1X/npcA==",
"license": "MIT",
"dependencies": {
"orderedmap": "^2.0.0"
@@ -1754,12 +1764,12 @@
}
},
"node_modules/prosemirror-view": {
- "version": "1.41.8",
- "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.8.tgz",
- "integrity": "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==",
+ "version": "1.41.9",
+ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.9.tgz",
+ "integrity": "sha512-clTunTX+eaLbr87L1V1QPheRlEQJyTlL3gXe9x3jQIk3rL0RVWxviDGz8tFaydwIVm+hKhYCyr+R/zBtWr9s6A==",
"license": "MIT",
"dependencies": {
- "prosemirror-model": "^1.20.0",
+ "prosemirror-model": "^1.25.8",
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.1.0"
}
@@ -2008,9 +2018,9 @@
}
},
"node_modules/yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "version": "16.2.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz",
+ "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==",
"dev": true,
"license": "MIT",
"dependencies": {
diff --git a/platform-web-ui/src/main/resources/package.json b/platform-web-ui/src/main/resources/package.json
index bd9a18805dd..f6edfbd50b2 100644
--- a/platform-web-ui/src/main/resources/package.json
+++ b/platform-web-ui/src/main/resources/package.json
@@ -79,7 +79,7 @@
"//": "Do NOT nest overrides under a package that appears as $ elsewhere (npm/cli#5914 + #5730). Hoist such sub-rules to the root instead.",
"overrides": {
"@toast-ui/editor": {
- "dompurify": "^3.4.3",
+ "dompurify": "^3.4.11",
"@types/trusted-types": "../_EXCLUDED_"
},
"wct-browser-legacy": {
diff --git a/platform-web-ui/src/main/resources/polymer/dompurify/dist/purify.es.mjs.js b/platform-web-ui/src/main/resources/polymer/dompurify/dist/purify.es.mjs.js
index fa15f56486f..3c6d3d19922 100644
--- a/platform-web-ui/src/main/resources/polymer/dompurify/dist/purify.es.mjs.js
+++ b/platform-web-ui/src/main/resources/polymer/dompurify/dist/purify.es.mjs.js
@@ -1,4 +1,4 @@
-/*! @license DOMPurify 3.4.3 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.3/LICENSE */
+/*! @license DOMPurify 3.4.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.11/LICENSE */
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
@@ -310,7 +310,7 @@ const mathMl$1 = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mgly
const mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);
const text = freeze(['#text']);
-const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
+const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
@@ -327,16 +327,31 @@ const ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205
);
const DOCTYPE_NAME = seal(/^html$/i);
const CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
+// Markup-significant character probes used by _sanitizeElements.
+// Shared module-level instances are safe despite the sticky /g flags:
+// unapply() resets lastIndex for RegExp receivers before every call.
+const ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
+const COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
+const FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
+const SELF_CLOSING_TAG = seal(/\/>/i);
-/* eslint-disable @typescript-eslint/indent */
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
const NODE_TYPE = {
element: 1,
+ attribute: 2,
text: 3,
+ cdataSection: 4,
+ entityReference: 5,
// Deprecated
- progressingInstruction: 7,
+ entityNode: 6,
+ // Deprecated
+ processingInstruction: 7,
comment: 8,
- document: 9};
+ document: 9,
+ documentType: 10,
+ documentFragment: 11,
+ notation: 12 // Deprecated
+};
const getGlobal = function getGlobal() {
return typeof window === 'undefined' ? null : window;
};
@@ -391,10 +406,25 @@ const _createHooksMap = function _createHooksMap() {
uponSanitizeShadowNode: []
};
};
+/**
+ * Resolve a set-valued configuration option: a fresh set built from
+ * cfg[key] when it is an own array property (seeded with a clone of
+ * options.base when given, case-normalized via options.transform),
+ * the fallback set otherwise.
+ *
+ * @param cfg the cloned, prototype-free configuration object
+ * @param key the configuration property to read
+ * @param fallback the set to use when the option is absent or not an array
+ * @param options transform and optional base set to merge into
+ * @returns the resolved set
+ */
+const _resolveSetOption = function _resolveSetOption(cfg, key, fallback, options) {
+ return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options.base ? clone(options.base) : {}, cfg[key], options.transform) : fallback;
+};
function createDOMPurify() {
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
const DOMPurify = root => createDOMPurify(root);
- DOMPurify.version = '3.4.3';
+ DOMPurify.version = '3.4.11';
DOMPurify.removed = [];
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
// Not running in a browser, provide a factory function
@@ -405,15 +435,15 @@ function createDOMPurify() {
let document = window.document;
const originalDocument = document;
const currentScript = originalDocument.currentScript;
- const DocumentFragment = window.DocumentFragment,
- HTMLTemplateElement = window.HTMLTemplateElement,
+ window.DocumentFragment;
+ const HTMLTemplateElement = window.HTMLTemplateElement,
Node = window.Node,
Element = window.Element,
NodeFilter = window.NodeFilter,
- _window$NamedNodeMap = window.NamedNodeMap,
- NamedNodeMap = _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
- HTMLFormElement = window.HTMLFormElement,
- DOMParser = window.DOMParser,
+ _window$NamedNodeMap = window.NamedNodeMap;
+ _window$NamedNodeMap === void 0 ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap;
+ window.HTMLFormElement;
+ const DOMParser = window.DOMParser,
trustedTypes = window.trustedTypes;
const ElementPrototype = Element.prototype;
const cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
@@ -421,6 +451,10 @@ function createDOMPurify() {
const getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
const getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
const getParentNode = lookupGetter(ElementPrototype, 'parentNode');
+ const getShadowRoot = lookupGetter(ElementPrototype, 'shadowRoot');
+ const getAttributes = lookupGetter(ElementPrototype, 'attributes');
+ const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
+ const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;
// As per issue #47, the web-components registry is inherited by a
// new document created via createHTMLDocument. As per the spec
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
@@ -435,6 +469,54 @@ function createDOMPurify() {
}
let trustedTypesPolicy;
let emptyHTML = '';
+ // The instance's own internal Trusted Types policy. Unlike a caller-supplied
+ // `TRUSTED_TYPES_POLICY`, this is created at most once — Trusted Types throws
+ // on duplicate policy names — and is the only policy allowed to persist
+ // across configurations and survive `clearConfig()`.
+ let defaultTrustedTypesPolicy;
+ let defaultTrustedTypesPolicyResolved = false;
+ // Tracks whether we are already inside a call to the configured Trusted Types
+ // policy (`createHTML` or `createScriptURL`). If a supplied policy callback
+ // itself calls `DOMPurify.sanitize` (the cause of #1422), `sanitize` would
+ // re-enter the policy and recurse until the stack overflows. We detect that
+ // re-entry and throw a clear, actionable error instead. The guard is shared
+ // across both callbacks, because either one re-entering `sanitize` triggers
+ // the same unbounded recursion.
+ let IN_TRUSTED_TYPES_POLICY = 0;
+ const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy() {
+ if (IN_TRUSTED_TYPES_POLICY > 0) {
+ throw typeErrorCreate('A configured TRUSTED_TYPES_POLICY callback (createHTML or ' + 'createScriptURL) must not call DOMPurify.sanitize, as that causes ' + 'infinite recursion. Do not pass a policy whose callbacks wrap ' + 'DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted ' + 'Types" section of the README.');
+ }
+ };
+ const _createTrustedHTML = function _createTrustedHTML(html) {
+ _assertNotInTrustedTypesPolicy();
+ IN_TRUSTED_TYPES_POLICY++;
+ try {
+ return trustedTypesPolicy.createHTML(html);
+ } finally {
+ IN_TRUSTED_TYPES_POLICY--;
+ }
+ };
+ const _createTrustedScriptURL = function _createTrustedScriptURL(scriptUrl) {
+ _assertNotInTrustedTypesPolicy();
+ IN_TRUSTED_TYPES_POLICY++;
+ try {
+ return trustedTypesPolicy.createScriptURL(scriptUrl);
+ } finally {
+ IN_TRUSTED_TYPES_POLICY--;
+ }
+ };
+ // Lazily resolve (and cache) the instance's internal default policy.
+ // Resolution is attempted at most once: a successful `createPolicy` cannot be
+ // repeated (Trusted Types throws on duplicate names), and a failed or
+ // unsupported attempt must not be retried on every parse.
+ const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy() {
+ if (!defaultTrustedTypesPolicyResolved) {
+ defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
+ defaultTrustedTypesPolicyResolved = true;
+ }
+ return defaultTrustedTypesPolicy;
+ };
const _document = document,
implementation = _document.implementation,
createNodeIterator = _document.createNodeIterator,
@@ -531,6 +613,13 @@ function createDOMPurify() {
let WHOLE_DOCUMENT = false;
/* Track whether config is already set on this instance of DOMPurify. */
let SET_CONFIG = false;
+ /* Pristine allowlist bindings captured at setConfig() time. On the
+ * persistent-config path sanitize() restores the sets from these before
+ * the per-walk hook clone-guard, so a hook's in-call widening cannot
+ * carry across calls. Null until setConfig() is called; reset by
+ * clearConfig(). */
+ let SET_CONFIG_ALLOWED_TAGS = null;
+ let SET_CONFIG_ALLOWED_ATTR = null;
/* Decide if all elements (e.g. style, script) must be children of
* document.body. By default, browsers might move them to document.head */
let FORCE_BODY = false;
@@ -573,7 +662,17 @@ function createDOMPurify() {
let USE_PROFILES = {};
/* Tags to ignore content of when KEEP_CONTENT is true */
let FORBID_CONTENTS = null;
- const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
+ const DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script',
+ // mirrors the selected 's subtree, cloned by
+ // the UA (customizable ) — including any on* handlers — and the
+ // engine re-mirrors synchronously whenever a removal changes which
+ // option/selectedcontent is current, even inside DOMPurify's inert
+ // DOMParser document. Hoisting its children on removal re-inserts a fresh
+ // mirror target ahead of the walk, which the engine refills, looping
+ // forever (DoS) and amplifying output. Dropping its content on removal
+ // (rather than hoisting) breaks that cascade; the content is a duplicate
+ // of the option, which is sanitized on its own. See campaign-3 F1/F6.
+ 'selectedcontent', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);
/* Tags that are safe for data: URIs */
let DATA_URI_TAGS = null;
const DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);
@@ -589,8 +688,10 @@ function createDOMPurify() {
/* Allowed XHTML+XML namespaces */
let ALLOWED_NAMESPACES = null;
const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
- let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
- let HTML_INTEGRATION_POINTS = addToSet({}, ['annotation-xml']);
+ const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze(['mi', 'mo', 'mn', 'ms', 'mtext']);
+ let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
+ const DEFAULT_HTML_INTEGRATION_POINTS = freeze(['annotation-xml']);
+ let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
// Certain elements are allowed in both SVG and HTML
// namespace. We need to specify them explicitly
// so that they don't get erroneously deleted from
@@ -632,14 +733,32 @@ function createDOMPurify() {
// HTML tags and attributes are not case-sensitive, converting to lowercase. Keeping XHTML as is.
transformCaseFunc = PARSER_MEDIA_TYPE === 'application/xhtml+xml' ? stringToString : stringToLowerCase;
/* Set configuration parameters */
- ALLOWED_TAGS = objectHasOwnProperty(cfg, 'ALLOWED_TAGS') && arrayIsArray(cfg.ALLOWED_TAGS) ? addToSet({}, cfg.ALLOWED_TAGS, transformCaseFunc) : DEFAULT_ALLOWED_TAGS;
- ALLOWED_ATTR = objectHasOwnProperty(cfg, 'ALLOWED_ATTR') && arrayIsArray(cfg.ALLOWED_ATTR) ? addToSet({}, cfg.ALLOWED_ATTR, transformCaseFunc) : DEFAULT_ALLOWED_ATTR;
- ALLOWED_NAMESPACES = objectHasOwnProperty(cfg, 'ALLOWED_NAMESPACES') && arrayIsArray(cfg.ALLOWED_NAMESPACES) ? addToSet({}, cfg.ALLOWED_NAMESPACES, stringToString) : DEFAULT_ALLOWED_NAMESPACES;
- URI_SAFE_ATTRIBUTES = objectHasOwnProperty(cfg, 'ADD_URI_SAFE_ATTR') && arrayIsArray(cfg.ADD_URI_SAFE_ATTR) ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR, transformCaseFunc) : DEFAULT_URI_SAFE_ATTRIBUTES;
- DATA_URI_TAGS = objectHasOwnProperty(cfg, 'ADD_DATA_URI_TAGS') && arrayIsArray(cfg.ADD_DATA_URI_TAGS) ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS, transformCaseFunc) : DEFAULT_DATA_URI_TAGS;
- FORBID_CONTENTS = objectHasOwnProperty(cfg, 'FORBID_CONTENTS') && arrayIsArray(cfg.FORBID_CONTENTS) ? addToSet({}, cfg.FORBID_CONTENTS, transformCaseFunc) : DEFAULT_FORBID_CONTENTS;
- FORBID_TAGS = objectHasOwnProperty(cfg, 'FORBID_TAGS') && arrayIsArray(cfg.FORBID_TAGS) ? addToSet({}, cfg.FORBID_TAGS, transformCaseFunc) : clone({});
- FORBID_ATTR = objectHasOwnProperty(cfg, 'FORBID_ATTR') && arrayIsArray(cfg.FORBID_ATTR) ? addToSet({}, cfg.FORBID_ATTR, transformCaseFunc) : clone({});
+ ALLOWED_TAGS = _resolveSetOption(cfg, 'ALLOWED_TAGS', DEFAULT_ALLOWED_TAGS, {
+ transform: transformCaseFunc
+ });
+ ALLOWED_ATTR = _resolveSetOption(cfg, 'ALLOWED_ATTR', DEFAULT_ALLOWED_ATTR, {
+ transform: transformCaseFunc
+ });
+ ALLOWED_NAMESPACES = _resolveSetOption(cfg, 'ALLOWED_NAMESPACES', DEFAULT_ALLOWED_NAMESPACES, {
+ transform: stringToString
+ });
+ URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, 'ADD_URI_SAFE_ATTR', DEFAULT_URI_SAFE_ATTRIBUTES, {
+ transform: transformCaseFunc,
+ base: DEFAULT_URI_SAFE_ATTRIBUTES
+ });
+ DATA_URI_TAGS = _resolveSetOption(cfg, 'ADD_DATA_URI_TAGS', DEFAULT_DATA_URI_TAGS, {
+ transform: transformCaseFunc,
+ base: DEFAULT_DATA_URI_TAGS
+ });
+ FORBID_CONTENTS = _resolveSetOption(cfg, 'FORBID_CONTENTS', DEFAULT_FORBID_CONTENTS, {
+ transform: transformCaseFunc
+ });
+ FORBID_TAGS = _resolveSetOption(cfg, 'FORBID_TAGS', clone({}), {
+ transform: transformCaseFunc
+ });
+ FORBID_ATTR = _resolveSetOption(cfg, 'FORBID_ATTR', clone({}), {
+ transform: transformCaseFunc
+ });
USE_PROFILES = objectHasOwnProperty(cfg, 'USE_PROFILES') ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === 'object' ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
@@ -658,8 +777,8 @@ function createDOMPurify() {
IN_PLACE = cfg.IN_PLACE || false; // Default false
IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI; // Default regexp
NAMESPACE = typeof cfg.NAMESPACE === 'string' ? cfg.NAMESPACE : HTML_NAMESPACE; // Default HTML namespace
- MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); // Default built-in map
- HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, ['annotation-xml']); // Default built-in map
+ MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'MATHML_TEXT_INTEGRATION_POINTS') && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === 'object' ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS); // Default built-in map
+ HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, 'HTML_INTEGRATION_POINTS') && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === 'object' ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS); // Default built-in map
const customElementHandling = objectHasOwnProperty(cfg, 'CUSTOM_ELEMENT_HANDLING') && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === 'object' ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
CUSTOM_ELEMENT_HANDLING = create(null);
if (objectHasOwnProperty(customElementHandling, 'tagNameCheck') && isRegexOrFunction(customElementHandling.tagNameCheck)) {
@@ -671,6 +790,7 @@ function createDOMPurify() {
if (objectHasOwnProperty(customElementHandling, 'allowCustomizedBuiltInElements') && typeof customElementHandling.allowCustomizedBuiltInElements === 'boolean') {
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements; // Default undefined
}
+ seal(CUSTOM_ELEMENT_HANDLING);
if (SAFE_FOR_TEMPLATES) {
ALLOW_DATA_ATTR = false;
}
@@ -754,6 +874,13 @@ function createDOMPurify() {
addToSet(ALLOWED_TAGS, ['tbody']);
delete FORBID_TAGS.tbody;
}
+ // Re-derive the active Trusted Types policy from this configuration on
+ // every parse. The active policy must never be sticky closure state that
+ // outlives the config that set it: a caller-supplied policy left in place
+ // after `clearConfig()` — or after a later call that supplied none, or
+ // `TRUSTED_TYPES_POLICY: null` — could sign a subsequent "default"
+ // `RETURN_TRUSTED_TYPE` result with a foreign, possibly unsafe policy.
+ // See GHSA-vxr8-fq34-vvx9.
if (cfg.TRUSTED_TYPES_POLICY) {
if (typeof cfg.TRUSTED_TYPES_POLICY.createHTML !== 'function') {
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');
@@ -761,18 +888,45 @@ function createDOMPurify() {
if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== 'function') {
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
}
- // Overwrite existing TrustedTypes policy.
+ // A caller-supplied policy applies to this configuration only.
+ const previousTrustedTypesPolicy = trustedTypesPolicy;
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
- // Sign local variables required by `sanitize`.
- emptyHTML = trustedTypesPolicy.createHTML('');
+ // Sign local variables required by `sanitize`. If the supplied policy's
+ // `createHTML` is circular (i.e. it calls `DOMPurify.sanitize`), this
+ // throws via the re-entrancy guard. Restore the previous policy first so
+ // the instance is not left in a poisoned state. See #1422.
+ try {
+ emptyHTML = _createTrustedHTML('');
+ } catch (error) {
+ trustedTypesPolicy = previousTrustedTypesPolicy;
+ throw error;
+ }
+ } else if (cfg.TRUSTED_TYPES_POLICY === null) {
+ // Explicit opt-out for this call: perform no Trusted Types signing and
+ // create nothing (so a strict `trusted-types` CSP that disallows a
+ // `dompurify` policy can still call `sanitize` from inside its own
+ // policy — see #1422). Resetting to `undefined` rather than a sticky
+ // `null` also drops any previously retained caller policy, so it cannot
+ // resurface on a later call, while still allowing the next config-less
+ // call to restore the internal default policy. See GHSA-vxr8-fq34-vvx9.
+ trustedTypesPolicy = undefined;
+ emptyHTML = '';
} else {
- // Uninitialized policy, attempt to initialize the internal dompurify policy.
+ // No policy supplied: keep the currently active policy if one is set — a
+ // previously supplied policy is intentionally sticky across config-less
+ // calls — otherwise fall back to the instance's own internal policy,
+ // created at most once. (A policy supplied for a *single* call still
+ // lingers by design; what must not linger is a policy whose configuration
+ // has been torn down via `clearConfig()`, which restores the default.)
if (trustedTypesPolicy === undefined) {
- trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
+ trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
}
- // If creating the internal policy succeeded sign internal variables.
- if (trustedTypesPolicy !== null && typeof emptyHTML === 'string') {
- emptyHTML = trustedTypesPolicy.createHTML('');
+ // Sign internal variables only when a policy is active. A falsy policy
+ // (Trusted Types unsupported, creation failed, or an explicit opt-out)
+ // leaves `emptyHTML` as a plain string, so we never call `.createHTML` on
+ // a non-policy and throw. See #1422.
+ if (trustedTypesPolicy && typeof emptyHTML === 'string') {
+ emptyHTML = _createTrustedHTML('');
}
}
// Prevent further manipulation of configuration.
@@ -787,6 +941,77 @@ function createDOMPurify() {
* correctly. */
const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
+ /**
+ * Namespace rules for an element in the SVG namespace.
+ *
+ * @param tagName the element's lowercase tag name
+ * @param parent the (possibly simulated) parent node
+ * @param parentTagName the parent's lowercase tag name
+ * @returns true if a spec-compliant parser could produce this element
+ */
+ const _checkSvgNamespace = function _checkSvgNamespace(tagName, parent, parentTagName) {
+ // The only way to switch from HTML namespace to SVG
+ // is via . If it happens via any other tag, then
+ // it should be killed.
+ if (parent.namespaceURI === HTML_NAMESPACE) {
+ return tagName === 'svg';
+ }
+ // The only way to switch from MathML to SVG is via
+ // if the parent is either or a MathML
+ // text integration point.
+ if (parent.namespaceURI === MATHML_NAMESPACE) {
+ return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
+ }
+ // We only allow elements that are defined in SVG
+ // spec. All others are disallowed in SVG namespace.
+ return Boolean(ALL_SVG_TAGS[tagName]);
+ };
+ /**
+ * Namespace rules for an element in the MathML namespace.
+ *
+ * @param tagName the element's lowercase tag name
+ * @param parent the (possibly simulated) parent node
+ * @param parentTagName the parent's lowercase tag name
+ * @returns true if a spec-compliant parser could produce this element
+ */
+ const _checkMathMlNamespace = function _checkMathMlNamespace(tagName, parent, parentTagName) {
+ // The only way to switch from HTML namespace to MathML
+ // is via . If it happens via any other tag, then
+ // it should be killed.
+ if (parent.namespaceURI === HTML_NAMESPACE) {
+ return tagName === 'math';
+ }
+ // The only way to switch from SVG to MathML is via
+ // and HTML integration points
+ if (parent.namespaceURI === SVG_NAMESPACE) {
+ return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
+ }
+ // We only allow elements that are defined in MathML
+ // spec. All others are disallowed in MathML namespace.
+ return Boolean(ALL_MATHML_TAGS[tagName]);
+ };
+ /**
+ * Namespace rules for an element in the HTML namespace.
+ *
+ * @param tagName the element's lowercase tag name
+ * @param parent the (possibly simulated) parent node
+ * @param parentTagName the parent's lowercase tag name
+ * @returns true if a spec-compliant parser could produce this element
+ */
+ const _checkHtmlNamespace = function _checkHtmlNamespace(tagName, parent, parentTagName) {
+ // The only way to switch from SVG to HTML is via
+ // HTML integration points, and from MathML to HTML
+ // is via MathML text integration points
+ if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
+ return false;
+ }
+ if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
+ return false;
+ }
+ // We disallow tags that are specific for MathML
+ // or SVG and should never appear in HTML namespace
+ return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
+ };
/**
* @param element a DOM element whose namespace is being checked
* @returns Return false if the element has a
@@ -809,51 +1034,13 @@ function createDOMPurify() {
return false;
}
if (element.namespaceURI === SVG_NAMESPACE) {
- // The only way to switch from HTML namespace to SVG
- // is via . If it happens via any other tag, then
- // it should be killed.
- if (parent.namespaceURI === HTML_NAMESPACE) {
- return tagName === 'svg';
- }
- // The only way to switch from MathML to SVG is via`
- // svg if parent is either or MathML
- // text integration points.
- if (parent.namespaceURI === MATHML_NAMESPACE) {
- return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
- }
- // We only allow elements that are defined in SVG
- // spec. All others are disallowed in SVG namespace.
- return Boolean(ALL_SVG_TAGS[tagName]);
+ return _checkSvgNamespace(tagName, parent, parentTagName);
}
if (element.namespaceURI === MATHML_NAMESPACE) {
- // The only way to switch from HTML namespace to MathML
- // is via . If it happens via any other tag, then
- // it should be killed.
- if (parent.namespaceURI === HTML_NAMESPACE) {
- return tagName === 'math';
- }
- // The only way to switch from SVG to MathML is via
- // and HTML integration points
- if (parent.namespaceURI === SVG_NAMESPACE) {
- return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
- }
- // We only allow elements that are defined in MathML
- // spec. All others are disallowed in MathML namespace.
- return Boolean(ALL_MATHML_TAGS[tagName]);
+ return _checkMathMlNamespace(tagName, parent, parentTagName);
}
if (element.namespaceURI === HTML_NAMESPACE) {
- // The only way to switch from SVG to HTML is via
- // HTML integration points, and from MathML to HTML
- // is via MathML text integration points
- if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
- return false;
- }
- if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
- return false;
- }
- // We disallow tags that are specific for MathML
- // or SVG and should never appear in HTML namespace
- return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
+ return _checkHtmlNamespace(tagName, parent, parentTagName);
}
// For XHTML and XML documents that support custom namespaces
if (PARSER_MEDIA_TYPE === 'application/xhtml+xml' && ALLOWED_NAMESPACES[element.namespaceURI]) {
@@ -878,7 +1065,74 @@ function createDOMPurify() {
// eslint-disable-next-line unicorn/prefer-dom-node-remove
getParentNode(node).removeChild(node);
} catch (_) {
+ /* The normal detach failed — this is reached for a parentless node
+ (getParentNode() is null, so .removeChild throws). Element.prototype
+ .remove() is itself a spec no-op on a parentless node, so a recorded
+ "removal" would otherwise hand the caller back an intact,
+ payload-bearing node (e.g. a detached IN_PLACE root the mXSS canary or
+ the style-with-element-child rule decided to kill). Fail closed by
+ throwing — exactly as a clobbered root does at the IN_PLACE entry —
+ rather than trying to "neutralize" the node via its own methods.
+ Neutralizing would mean calling getAttributeNames()/removeAttribute()
+ on the node, both of which a