This is an automated email from the ASF dual-hosted git repository.

nmalin pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new c7f1ea4d18 Improved : Improve Uel loading (OFBIZ-13372) - Adds tests 
to UEL - Added the possibility to load uel in a modular way. - separates 
current UEL into thematic files. - Switched to an object structure to allow for 
a description to be loaded with uel, that will be displayed in the uel screen.
c7f1ea4d18 is described below

commit c7f1ea4d18a0d996a3fcb720b61fc4b827619b4b
Author: cgaetan <[email protected]>
AuthorDate: Fri Mar 13 17:08:10 2026 +0100

    Improved : Improve Uel loading (OFBIZ-13372)
    - Adds tests to UEL
    - Added the possibility to load uel in a modular way.
    - separates current UEL into thematic files.
    - Switched to an object structure to allow for a description to be loaded 
with uel, that will be displayed in the uel screen.
    
    With this you can create your customized uel library dedicate to your 
plugin. Create you class that implements IUelMappingLibrary (look at 
org.apache.ofbiz.base.util.string.uel.*) and load it on your 
ofbiz-component.xml :
    
    <ofbiz-component name="myplugin" ...
        <uel-mapping name="my-custom-uel" 
class-name="org.company.my.plugin.MyCustomUel"/>
    </ofbiz-component>
---
 framework/base/ofbiz-component.xml                 |   5 +
 .../apache/ofbiz/base/test/uel/DateUelTest.groovy  | 118 ++++++++
 .../apache/ofbiz/base/test/uel/MathUelTest.groovy  |  90 ++++++
 .../apache/ofbiz/base/test/uel/MiscUelTest.groovy  |  75 +++++
 .../ofbiz/base/test/uel/StringUelTest.groovy       |  70 +++++
 .../ofbiz/base/util/string/uel/DateUel.groovy      |  83 ++++++
 .../ofbiz/base/util/string/uel/MathUel.groovy      | 128 +++++++++
 .../ofbiz/base/util/string/uel/MiscUel.groovy      |  62 +++++
 .../ofbiz/base/util/string/uel/StringUel.groovy    |  68 +++++
 .../ofbiz/base/component/ComponentConfig.java      |  42 +++
 .../ofbiz/base/util/string/IUelMappingLibrary.java |  31 +++
 .../ofbiz/base/util/string/UelFunctions.java       | 301 ++++-----------------
 .../apache/ofbiz/base/util/string/UelMapping.java  |  49 ++++
 framework/base/testdef/basetests.xml               |  18 +-
 framework/webtools/config/WebtoolsUiLabels.xml     |  20 ++
 .../webtools/util/uel/FindAvailableUel.groovy      |  37 +++
 framework/webtools/template/Main.ftl               |   1 +
 .../webapp/webtools/WEB-INF/controller.xml         |   2 +
 framework/webtools/widget/MiscScreens.xml          |  22 ++
 framework/webtools/widget/UelForms.xml             |  34 +++
 20 files changed, 1004 insertions(+), 252 deletions(-)

diff --git a/framework/base/ofbiz-component.xml 
b/framework/base/ofbiz-component.xml
index 8cde8e9ff9..5fceb1cad0 100644
--- a/framework/base/ofbiz-component.xml
+++ b/framework/base/ofbiz-component.xml
@@ -34,4 +34,9 @@ under the License.
     <!-- load the administration server -->
     <container name="admin-container" loaders="main" 
class="org.apache.ofbiz.base.container.AdminServerContainer"/>
 
+    <!--  Load base uel methods  -->
+    <uel-mapping name="date-uel" 
class-name="org.apache.ofbiz.base.util.string.uel.DateUel"/>
+    <uel-mapping name="math-uel" 
class-name="org.apache.ofbiz.base.util.string.uel.MathUel"/>
+    <uel-mapping name="string-uel" 
class-name="org.apache.ofbiz.base.util.string.uel.StringUel"/>
+    <uel-mapping name="misc-uel" 
class-name="org.apache.ofbiz.base.util.string.uel.MiscUel"/>
 </ofbiz-component>
diff --git 
a/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/DateUelTest.groovy
 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/DateUelTest.groovy
new file mode 100644
index 0000000000..10863d1574
--- /dev/null
+++ 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/DateUelTest.groovy
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.base.test.uel
+
+import org.apache.ofbiz.base.util.UtilDateTime
+import org.apache.ofbiz.base.util.string.FlexibleStringExpander
+import org.apache.ofbiz.base.util.string.UelFunctions
+import org.apache.ofbiz.service.testtools.OFBizTestCase
+
+import java.sql.Timestamp
+
+/**
+ * ./gradlew 'ofbiz -t component=base -t suitename=basetests'
+ */
+/* codenarc-disable GStringExpressionWithinString,ClosureAsLastMethodParameter 
*/
+class DateUelTest extends OFBizTestCase {
+
+    DateUelTest(String name) {
+        super(name)
+    }
+
+    void testDateUel() { // codenarc-disable JUnitTestMethodWithoutAssert
+        doUelDateTest('${date:second(now, timeZone, locale)}', { Timestamp now 
->
+            UtilDateTime.getSecond(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:minute(now, timeZone, locale)}', { Timestamp now 
->
+            UtilDateTime.getMinute(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:hour(now, timeZone, locale)}', { Timestamp now ->
+            UtilDateTime.getHour(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:dayOfMonth(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getDayOfMonth(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:dayOfWeek(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getDayOfWeek(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:dayOfYear(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getDayOfYear(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:week(now, timeZone, locale)}', { Timestamp now ->
+            UtilDateTime.getWeek(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:month(now, timeZone, locale)}', { Timestamp now 
->
+            UtilDateTime.getMonth(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:year(now, timeZone, locale)}', { Timestamp now ->
+            UtilDateTime.getYear(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:dayStart(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getDayStart(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:dayEnd(now, timeZone, locale)}', { Timestamp now 
->
+            UtilDateTime.getDayEnd(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:weekStart(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getWeekStart(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:weekEnd(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getWeekEnd(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:monthStart(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getMonthStart(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:monthEnd(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getMonthEnd(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:yearStart(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getYearStart(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:yearEnd(now, timeZone, locale)}', { Timestamp 
now ->
+            UtilDateTime.getYearEnd(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:dateStr(now, timeZone, locale)}', { Timestamp 
now ->
+            UelFunctions.dateString(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:localizedDateStr(now, timeZone, locale)}', { 
Timestamp now ->
+            UelFunctions.localizedDateString(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:localizedDateTimeStr(now, timeZone, locale)}', { 
Timestamp now ->
+            UelFunctions.localizedDateTimeString(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+        doUelDateTest('${date:timeStr(now, timeZone, locale)}', { Timestamp 
now ->
+            UelFunctions.timeString(now, TimeZone.getDefault(), 
Locale.getDefault())
+        })
+    }
+
+    void testNowTimestampUel() {
+        FlexibleStringExpander fse = 
FlexibleStringExpander.getInstance('${date:nowTimestamp()}')
+        assert (fse.expand([:]).time - UtilDateTime.nowTimestamp().time).abs() 
< 15 // less than 10 ns appart
+    }
+
+    private void doUelDateTest(String uelInput, Closure uelFunction) {
+        Timestamp now = UtilDateTime.nowTimestamp()
+        Map context = [now: now,
+                       timeZone: TimeZone.getDefault(),
+                       locale: Locale.getDefault()]
+        FlexibleStringExpander fse = 
FlexibleStringExpander.getInstance(uelInput)
+        assert fse.expand(context) == uelFunction(now)
+    }
+
+}
diff --git 
a/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/MathUelTest.groovy
 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/MathUelTest.groovy
new file mode 100644
index 0000000000..8cf9e7efd7
--- /dev/null
+++ 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/MathUelTest.groovy
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.base.test.uel
+
+import org.apache.ofbiz.base.util.string.FlexibleStringExpander
+import org.apache.ofbiz.service.testtools.OFBizTestCase
+
+/**
+ * ./gradlew 'ofbiz -t component=base -t suitename=basetests'
+ */
+/* codenarc-disable GStringExpressionWithinString,ClosureAsLastMethodParameter 
*/
+
+class MathUelTest extends OFBizTestCase {
+
+    MathUelTest(String name) { super(name) }
+
+    void testMathUel() { // codenarc-disable JUnitTestMethodWithoutAssert
+        doMathTest('${math:absDouble(a)}', [a: -0.3, b: null], { a, b -> 
Math.abs(a as double) })
+        doMathTest('${math:absFloat(a)}', [a: -0.3, b: null], { a, b -> 
Math.abs(a as float) })
+        doMathTest('${math:absInt(a)}', [a: -2, b: null], { a, b -> Math.abs(a 
as int) })
+        doMathTest('${math:absLong(a)}', [a: -5, b: null], { a, b -> 
Math.abs(a as long) })
+        doMathTest('${math:acos(a)}', [a: 0.2, b: null], { a, b -> 
Math.acos(a) })
+        doMathTest('${math:asin(a)}', [a: 0.2, b: null], { a, b -> 
Math.asin(a) })
+        doMathTest('${math:atan(a)}', [a: 0.2, b: null], { a, b -> 
Math.atan(a) })
+        doMathTest('${math:atan2(a, b)}', [a: 0.2, b: 0.2], { a, b -> 
Math.atan2(a, b) })
+        doMathTest('${math:cbrt(a)}', [a: 10, b: null], { a, b -> Math.cbrt(a) 
})
+        doMathTest('${math:ceil(a)}', [a: 20, b: null], { a, b -> Math.ceil(a) 
})
+        doMathTest('${math:cos(a)}', [a: 0.2, b: null], { a, b -> Math.cos(a) 
})
+        doMathTest('${math:cosh(a)}', [a: 0.2, b: null], { a, b -> 
Math.cosh(a) })
+        doMathTest('${math:exp(a)}', [a: 29, b: null], { a, b -> Math.exp(a) })
+        doMathTest('${math:expm1(a)}', [a: 20, b: null], { a, b -> 
Math.expm1(a) })
+        doMathTest('${math:floor(a)}', [a: 23.4, b: null], { a, b -> 
Math.floor(a) })
+        doMathTest('${math:hypot(a, b)}', [a: 29, b: 12], { a, b -> 
Math.hypot(a, b) })
+        doMathTest('${math:IEEEremainder(a, b)}', [a: 12, b: 1.3], { a, b -> 
Math.IEEEremainder(a, b) })
+        doMathTest('${math:log(a)}', [a: 20, b: null], { a, b -> Math.log(a) })
+        doMathTest('${math:log10(a)}', [a: 29, b: null], { a, b -> 
Math.log10(a) })
+        doMathTest('${math:log1p(a)}', [a: 12, b: null], { a, b -> 
Math.log1p(a) })
+        doMathTest('${math:maxDouble(a, b)}', [a: 12, b: 13], { a, b -> 
Math.max(a as double, b as double) })
+        doMathTest('${math:maxFloat(a, b)}', [a: 2.4, b: 3.9], { a, b -> 
Math.max(a as float, b as float) })
+        doMathTest('${math:maxInt(a, b)}', [a: 2.6, b: 3.7], { a, b -> 
Math.max(a as int, b as int) })
+        doMathTest('${math:maxLong(a, b)}', [a: 23, b: 32], { a, b -> 
Math.max(a as long, b as long) })
+        doMathTest('${math:minDouble(a, b)}', [a: 10, b: 20], { a, b -> 
Math.min(a as double, b as double) })
+        doMathTest('${math:minFloat(a, b)}', [a: 1.2, b: 2.5], { a, b -> 
Math.min(a as float, b as float) })
+        doMathTest('${math:minInt(a, b)}', [a: 12, b: 13], { a, b -> 
Math.min(a as int, b as int) })
+        doMathTest('${math:minLong(a, b)}', [a: 24, b: 14], { a, b -> 
Math.min(a as long, b as long) })
+        doMathTest('${math:pow(a, b)}', [a: 12, b: 13], { a, b -> Math.pow(a, 
b) })
+        doMathTest('${math:rint(a)}', [a: 29, b: null], { a, b -> Math.rint(a) 
})
+        doMathTest('${math:roundDouble(a)}', [a: 23.4, b: null], { a, b -> 
Math.round(a as double) })
+        doMathTest('${math:roundFloat(a)}', [a: 12.4, b: null], { a, b -> 
Math.round(a as float) })
+        doMathTest('${math:signumDouble(a)}', [a: 23, b: null], { a, b -> 
Math.signum(a as double) })
+        doMathTest('${math:signumFloat(a)}', [a: 23, b: null], { a, b -> 
Math.signum(a as float) })
+        doMathTest('${math:sin(a)}', [a: 12, b: null], { a, b -> Math.sin(a) })
+        doMathTest('${math:sinh(a)}', [a: 12, b: null], { a, b -> Math.sinh(a) 
})
+        doMathTest('${math:sqrt(a)}', [a: 34, b: null], { a, b -> Math.sqrt(a) 
})
+        doMathTest('${math:tan(a)}', [a: 13, b: null], { a, b -> Math.tan(a) })
+        doMathTest('${math:tanh(a)}', [a: 30, b: null], { a, b -> Math.tanh(a) 
})
+        doMathTest('${math:toDegrees(a)}', [a: 30, b: null], { a, b -> 
Math.toDegrees(a) })
+        doMathTest('${math:toRadians(a)}', [a: 12, b: null], { a, b -> 
Math.toRadians(a) })
+        doMathTest('${math:ulpDouble(a)}', [a: 12, b: null], { a, b -> 
Math.ulp(a as double) })
+        doMathTest('${math:ulpFloat(a)}', [a: 30, b: null], { a, b -> 
Math.ulp(a as float) })
+    }
+
+    void testMathRandom() {
+        FlexibleStringExpander fse = 
FlexibleStringExpander.getInstance('${math:random()}')
+        assert fse.expand([:]) instanceof Double
+        assert fse.expand([:]) != BigDecimal.ZERO
+    }
+
+    private void doMathTest(String uelInput, Map context, Closure uelFunction) 
{
+        FlexibleStringExpander fse = 
FlexibleStringExpander.getInstance(uelInput)
+        assert new BigDecimal(fse.expand(context)) == new 
BigDecimal(uelFunction(context.a, context.b))
+    }
+
+}
diff --git 
a/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/MiscUelTest.groovy
 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/MiscUelTest.groovy
new file mode 100644
index 0000000000..3bbaa04b18
--- /dev/null
+++ 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/MiscUelTest.groovy
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.base.test.uel
+
+import org.apache.ofbiz.base.util.string.FlexibleStringExpander
+import org.apache.ofbiz.base.util.string.UelFunctions
+import org.apache.ofbiz.service.testtools.OFBizTestCase
+
+/**
+ * ./gradlew 'ofbiz -t component=base -t suitename=basetests'
+ */
+/* codenarc-disable GStringExpressionWithinString,ClosureAsLastMethodParameter 
*/
+class MiscUelTest extends OFBizTestCase {
+
+    MiscUelTest(String name) { super(name) }
+
+    void testSystemUel() { // codenarc-disable JUnitTestMethodWithoutAssert
+         // todo: Both are null, but mockito doesn't do system mock..
+        doUelSystemTest('${sys:getenv}', 'foo', { String prop -> 
UelFunctions.sysGetEnv(prop) })
+        doUelSystemTest('${sys:getProperty}', 'bar', { String prop -> 
UelFunctions.sysGetProp(prop) })
+    }
+
+    void testUtilSizeUel() {
+        List foo = [1, 2, 3]
+        FlexibleStringExpander fse = 
FlexibleStringExpander.getInstance('${util:size(foo)}')
+        assert fse.expand([foo: foo]) == UelFunctions.getSize(foo)
+    }
+
+    void testDefaultLocaleAndTimzoneUel() {
+        FlexibleStringExpander fse = 
FlexibleStringExpander.getInstance('${util:defaultLocale()}')
+        assert fse.expand([foo: 'bar']) == Locale.getDefault()
+        fse = FlexibleStringExpander.getInstance('${util:defaultTimeZone()}')
+        assert fse.expand([foo: 'bar']) == TimeZone.getDefault()
+    }
+
+    void testLabelUel() {
+        String labelFile = 'CommonEntityLabels', labelKey = 
'VisualTheme.description.RAINBOWSTONE_AMBER'
+        FlexibleStringExpander fse = FlexibleStringExpander.getInstance(
+                
'${util:label(\'CommonEntityLabels\',\'VisualTheme.description.RAINBOWSTONE_AMBER\',
 locale)}')
+        assert fse.expand([locale: Locale.getDefault()]) == 
UelFunctions.label(labelFile, labelKey, Locale.getDefault())
+    }
+
+    private void doUelSystemTest(String uelInput, String param, Closure 
uelFunction) {
+        FlexibleStringExpander fse = 
FlexibleStringExpander.getInstance(uelInput)
+        assert fse.expand([:]) == uelFunction(param)
+    }
+
+//     TODO: Some day
+//    '${screen:id}', UelFunctions.class.getMethod("resolveCurrentScreenId", 
ScreenRenderer.ScreenStack.class))
+//    '${dom:readHtmlDocument}', 
UelFunctions.class.getMethod("readHtmlDocument", String.class))
+//    '${dom:readXmlDocument}', 
UelFunctions.class.getMethod("readXmlDocument", String.class))
+//    '${dom:toHtmlString}', UelFunctions.class.getMethod("toHtmlString", 
Node.class, String.class, boolean.class,
+//          int.class))
+//    '${dom:toXmlString}', UelFunctions.class.getMethod("toXmlString", 
Node.class, String.class, boolean.class,
+//          boolean.class, int.class))
+//    '${dom:writeXmlDocument}', 
UelFunctions.class.getMethod("writeXmlDocument", String.class, Node.class,
+//          String.class, boolean.class, boolean.class, int.class))
+
+}
diff --git 
a/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/StringUelTest.groovy
 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/StringUelTest.groovy
new file mode 100644
index 0000000000..ad24acce02
--- /dev/null
+++ 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/test/uel/StringUelTest.groovy
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.base.test.uel
+
+import org.apache.ofbiz.base.util.string.FlexibleStringExpander
+import org.apache.ofbiz.base.util.string.UelFunctions
+import org.apache.ofbiz.service.testtools.OFBizTestCase
+
+/**
+ * ./gradlew 'ofbiz -t component=base -t suitename=basetests'
+ */
+/* codenarc-disable GStringExpressionWithinString,ClosureAsLastMethodParameter 
*/
+
+class StringUelTest extends OFBizTestCase {
+
+    StringUelTest(String name) { super(name) }
+
+    void testUelString() { // codenarc-disable JUnitTestMethodWithoutAssert
+        doStringTest('${str:endsWith(a, b)}', [a: 'dog', b: 'og', c: null],
+                { a, b, c -> UelFunctions.endsWith(a, b) })
+        doStringTest('${str:indexOf(a, b)}', [a: 'dog', b: 'og', c: null],
+                { a, b, c -> UelFunctions.indexOf(a, b) })
+        doStringTest('${str:lastIndexOf(a, b)}', [a: 'dog', b: 'og', c: null],
+                { a, b, c -> UelFunctions.lastIndexOf(a, b) })
+        doStringTest('${str:length(a)}', [a: 'dog', b: null, c: null],
+                { a, b, c -> UelFunctions.length(a) })
+        doStringTest('${str:replace(a, b, c)}', [a: 'the dog', b: 'dog', c: 
'cat'],
+                { a, b, c -> UelFunctions.replace(a, b, c) })
+        doStringTest('${str:replaceAll(a, b, c)}', [a: 'the dog', b: 'dog', c: 
'cat'],
+                { a, b, c -> UelFunctions.replaceAll(a, b, c) })
+        doStringTest('${str:replaceFirst(a, b, c)}', [a: 'the dog', b: 'dog', 
c: 'cat'],
+                { a, b, c -> UelFunctions.replaceFirst(a, b, c) })
+        doStringTest('${str:startsWith(a, b)}', [a: 'the dog', b: 'the', c: 
null],
+                { a, b, c -> UelFunctions.startsWith(a, b) })
+        doStringTest('${str:endstring(a, b)}', [a: 'the dog', b: 3, c: null],
+                { a, b, c -> UelFunctions.endString(a, b) })
+        doStringTest('${str:substring(a, b, c)}', [a: 'the dog', b: 3, c: 7],
+                { a, b, c -> UelFunctions.subString(a, b, c) })
+        doStringTest('${str:toString(a)}', [a: 'foo', b: null, c: null],
+                { a, b, c -> UelFunctions.toString(a) })
+        doStringTest('${str:toLowerCase(a)}', [a: 'FOO', b: null, c: null],
+                { a, b, c -> UelFunctions.toLowerCase(a) })
+        doStringTest('${str:toUpperCase(a)}', [a: 'foo', b: null, c: null],
+                { a, b, c -> UelFunctions.toUpperCase(a) })
+        doStringTest('${str:trim(a)}', [a: ' foo ', b: null, c: null],
+                { a, b, c -> UelFunctions.trim(a) })
+    }
+
+    private void doStringTest(String uelInput, Map context, Closure 
uelFunction) {
+        FlexibleStringExpander fse = 
FlexibleStringExpander.getInstance(uelInput)
+        assert fse.expand(context) == uelFunction(context.a, context.b, 
context.c)
+    }
+
+}
diff --git 
a/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/DateUel.groovy
 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/DateUel.groovy
new file mode 100644
index 0000000000..52e410ad0c
--- /dev/null
+++ 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/DateUel.groovy
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.base.util.string.uel
+
+import org.apache.ofbiz.base.util.UtilDateTime
+import org.apache.ofbiz.base.util.string.IUelMappingLibrary
+import org.apache.ofbiz.base.util.string.UelFunctions
+import org.apache.ofbiz.base.util.string.UelMapping
+
+import java.sql.Timestamp
+
+/**
+ * Class for importing the Date Uel
+ */
+class DateUel implements IUelMappingLibrary {
+
+    @Override
+    List<UelMapping> getUelMappingList() {
+        return [
+                new UelMapping('date:second', 
UtilDateTime.getMethod('getSecond', Timestamp, TimeZone, Locale),
+                        'Returns the second value of Timestamp (0 - 59).'),
+                new UelMapping('date:minute', 
UtilDateTime.getMethod('getMinute', Timestamp, TimeZone, Locale),
+                        'Returns the minute value of Timestamp (0 - 59).'),
+                new UelMapping('date:hour', UtilDateTime.getMethod('getHour', 
Timestamp, TimeZone, Locale),
+                        'Returns the hour value of Timestamp (0 - 23).'),
+                new UelMapping('date:dayOfMonth', 
UtilDateTime.getMethod('getDayOfMonth', Timestamp, TimeZone, Locale),
+                        'Returns the day of month value of Timestamp (1 - 31) 
.'),
+                new UelMapping('date:dayOfWeek', 
UtilDateTime.getMethod('getDayOfWeek', Timestamp, TimeZone, Locale),
+                        'Returns the day of week value of Timestamp (Sunday = 
1, Saturday = 7).'),
+                new UelMapping('date:dayOfYear', 
UtilDateTime.getMethod('getDayOfYear', Timestamp, TimeZone, Locale),
+                        'Returns the day of year value of Timestamp.'),
+                new UelMapping('date:week', UtilDateTime.getMethod('getWeek', 
Timestamp, TimeZone, Locale),
+                        'Returns the week value of Timestamp.'),
+                new UelMapping('date:month', 
UtilDateTime.getMethod('getMonth', Timestamp, TimeZone, Locale),
+                        'Returns the month value of Timestamp (January = 0, 
December = 11).'),
+                new UelMapping('date:year', UtilDateTime.getMethod('getYear', 
Timestamp, TimeZone, Locale),
+                        'Returns the year value of Timestamp.'),
+                new UelMapping('date:dayStart', 
UtilDateTime.getMethod('getDayStart', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp set to start of day.'),
+                new UelMapping('date:dayEnd', 
UtilDateTime.getMethod('getDayEnd', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp set to end of day.'),
+                new UelMapping('date:weekStart', 
UtilDateTime.getMethod('getWeekStart', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp set to start of week.'),
+                new UelMapping('date:weekEnd', 
UtilDateTime.getMethod('getWeekEnd', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp set to end of week.'),
+                new UelMapping('date:monthStart', 
UtilDateTime.getMethod('getMonthStart', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp set to start of month.'),
+                new UelMapping('date:monthEnd', 
UtilDateTime.getMethod('getMonthEnd', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp set to end of month.'),
+                new UelMapping('date:yearStart', 
UtilDateTime.getMethod('getYearStart', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp set to start of year.'),
+                new UelMapping('date:yearEnd', 
UtilDateTime.getMethod('getYearEnd', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp set to end of year.'),
+                new UelMapping('date:dateStr', 
UelFunctions.getMethod('dateString', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp as a date String (yyyy-mm-dd) .'),
+                new UelMapping('date:localizedDateStr', 
UelFunctions.getMethod('localizedDateString', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp as a date String formatted 
according to the supplied locale.'),
+                new UelMapping('date:localizedDateTimeStr', 
UelFunctions.getMethod('localizedDateTimeString', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp as a date-time String formatted 
according to the supplied locale.'),
+                new UelMapping('date:timeStr', 
UelFunctions.getMethod('timeString', Timestamp, TimeZone, Locale),
+                        'Returns Timestamp as a time String (hh:mm) .'),
+                new UelMapping('date:nowTimestamp', 
UtilDateTime.getMethod('nowTimestamp'),
+                        'Returns Timestamp for right now.')
+        ]
+    }
+
+}
diff --git 
a/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/MathUel.groovy
 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/MathUel.groovy
new file mode 100644
index 0000000000..c0b90b3e0d
--- /dev/null
+++ 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/MathUel.groovy
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.base.util.string.uel
+
+import org.apache.ofbiz.base.util.string.IUelMappingLibrary
+import org.apache.ofbiz.base.util.string.UelMapping
+
+/**
+ * Class for importing the Math Uel
+ */
+class MathUel implements IUelMappingLibrary {
+
+    @Override
+    List<UelMapping> getUelMappingList() {
+        return [
+                new UelMapping('math:absDouble', Math.getMethod('abs', double),
+                        'Returns the absolute value of a double value'),
+                new UelMapping('math:absFloat', Math.getMethod('abs', float),
+                        'Returns the absolute value of a float value.'),
+                new UelMapping('math:absInt', Math.getMethod('abs', int),
+                        'Returns the absolute value of an int value.'),
+                new UelMapping('math:absLong', Math.getMethod('abs', long),
+                        'Returns the absolute value of a long value.'),
+                new UelMapping('math:acos', Math.getMethod('acos', double),
+                        'Returns the arc cosine of an angle, in the range of 
0.0 through pi.'),
+                new UelMapping('math:asin', Math.getMethod('asin', double),
+                        'Returns the arc sine of an angle, in the range of 
-pi/2 through pi/2.'),
+                new UelMapping('math:atan', Math.getMethod('atan', double),
+                        'Returns the arc tangent of an angle, in the range of 
-pi/2 through pi/2.'),
+                new UelMapping('math:atan2', Math.getMethod('atan2', double, 
double),
+                        'Converts rectangular coordinates (x, y) to polar (r,  
theta).'),
+                new UelMapping('math:cbrt', Math.getMethod('cbrt', double),
+                        'Returns the cube root of a double value.'),
+                new UelMapping('math:ceil', Math.getMethod('ceil', double),
+                        'Returns the smallest (closest to negative infinity) 
double value that is greater than or' +
+                                ' equal to the argument and is equal to a 
mathematical integer.'),
+                new UelMapping('math:cos', Math.getMethod('cos', double),
+                        'Returns the trigonometric cosine of an angle.'),
+                new UelMapping('math:cosh', Math.getMethod('cosh', double),
+                        'Returns the hyperbolic cosine of a double value.'),
+                new UelMapping('math:exp', Math.getMethod('exp', double),
+                        "Returns Euler's number e raised to the power of a 
double value."),
+                new UelMapping('math:expm1', Math.getMethod('expm1', double),
+                        'Returns ex -1.'),
+                new UelMapping('math:floor', Math.getMethod('floor', double),
+                        'Returns the largest (closest to positive infinity) 
double value that is less than or equal ' +
+                                'to the argument and is equal to a 
mathematical integer.'),
+                new UelMapping('math:hypot', Math.getMethod('hypot', double, 
double),
+                        'Returns sqrt(x2 +y2) without intermediate overflow or 
underflow.'),
+                new UelMapping('math:IEEEremainder', 
Math.getMethod('IEEEremainder', double, double),
+                        'Computes the remainder operation on two arguments as 
prescribed by the IEEE 754 standard.'),
+                new UelMapping('math:log', Math.getMethod('log', double),
+                        'Returns the natural logarithm (base e) of a double 
value.'),
+                new UelMapping('math:log10', Math.getMethod('log10', double),
+                        'Returns the base 10 logarithm of a double value.'),
+                new UelMapping('math:log1p', Math.getMethod('log1p', double),
+                        'Returns the natural logarithm of the sum of the 
argument and 1.'),
+                new UelMapping('math:maxDouble', Math.getMethod('max', double, 
double),
+                        'Returns the greater of two double values.'),
+                new UelMapping('math:maxFloat', Math.getMethod('max', float, 
float),
+                        'Returns the greater of two float values.'),
+                new UelMapping('math:maxInt', Math.getMethod('max', int, int),
+                        'Returns the greater of two int values.'),
+                new UelMapping('math:maxLong', Math.getMethod('max', long, 
long),
+                        'Returns the greater of two long values.'),
+                new UelMapping('math:minDouble', Math.getMethod('min', double, 
double),
+                        'Returns the smaller of two double values.'),
+                new UelMapping('math:minFloat', Math.getMethod('min', float, 
float),
+                        'Returns the smaller of two float values.'),
+                new UelMapping('math:minInt', Math.getMethod('min', int, int),
+                        'Returns the smaller of two int values.'),
+                new UelMapping('math:minLong', Math.getMethod('min', long, 
long),
+                        'Returns the smaller of two long values.'),
+                new UelMapping('math:pow', Math.getMethod('pow', double, 
double),
+                        'Returns the value of the first argument raised to the 
power of the second argument.'),
+                new UelMapping('math:random', Math.getMethod('random'),
+                        'Returns a double value with a positive sign, greater 
than or equal to 0.0 and less than 1.0.'),
+                new UelMapping('math:rint', Math.getMethod('rint', double),
+                        'Returns the double value that is closest in value to 
the argument and is equal to a' +
+                                ' mathematical integer.'),
+                new UelMapping('math:roundDouble', Math.getMethod('round', 
double),
+                        'Returns the closest long to the argument.'),
+                new UelMapping('math:roundFloat', Math.getMethod('round', 
float),
+                        'Returns the closest int to the argument.'),
+                new UelMapping('math:signumDouble', Math.getMethod('signum', 
double),
+                        'Returns the signum function of the argument; zero if 
the argument is zero, 1.0 if the ' +
+                                'argument is greater than zero, -1.0 if the 
argument is less than zero.'),
+                new UelMapping('math:signumFloat', Math.getMethod('signum', 
float),
+                        'Returns the signum function of the argument; zero if 
the argument is zero, 1.0f if the ' +
+                                'argument is greater than zero, -1.0f if the 
argument is less than zero.'),
+                new UelMapping('math:sin', Math.getMethod('sin', double),
+                        'Returns the trigonometric sine of an angle.'),
+                new UelMapping('math:sinh', Math.getMethod('sinh', double),
+                        'Returns the hyperbolic sine of a double value.'),
+                new UelMapping('math:sqrt', Math.getMethod('sqrt', double),
+                        'Returns the correctly rounded positive square root of 
a double value.'),
+                new UelMapping('math:tan', Math.getMethod('tan', double),
+                        'Returns the trigonometric tangent of an angle.'),
+                new UelMapping('math:tanh', Math.getMethod('tanh', double),
+                        'Returns the hyperbolic tangent of a double value.'),
+                new UelMapping('math:toDegrees', Math.getMethod('toDegrees', 
double),
+                        'Converts an angle measured in radians to an 
approximately equivalent angle measured in degrees.'),
+                new UelMapping('math:toRadians', Math.getMethod('toRadians', 
double),
+                        'Converts an angle measured in degrees to an 
approximately equivalent angle measured in radians.'),
+                new UelMapping('math:ulpDouble', Math.getMethod('ulp', double),
+                        'Returns the size of an ulp (units in the last place) 
of the argument.'),
+                new UelMapping('math:ulpFloat', Math.getMethod('ulp', float),
+                        'Returns the size of an ulp (units in the last place) 
of the argument.')
+        ]
+    }
+
+}
diff --git 
a/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/MiscUel.groovy
 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/MiscUel.groovy
new file mode 100644
index 0000000000..f03ef45c80
--- /dev/null
+++ 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/MiscUel.groovy
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.base.util.string.uel
+
+import org.apache.ofbiz.base.util.string.IUelMappingLibrary
+import org.apache.ofbiz.base.util.string.UelFunctions
+import org.apache.ofbiz.base.util.string.UelMapping
+import org.apache.ofbiz.widget.renderer.ScreenRenderer
+import org.w3c.dom.Node
+
+/**
+ * Class for importing the various system, util, and dom Uel
+ */
+class MiscUel implements IUelMappingLibrary {
+
+    @Override
+    List<UelMapping> getUelMappingList() {
+        return [
+                new UelMapping('sys:getenv', 
UelFunctions.getMethod('sysGetEnv', String),
+                        'Gets the value of the specified environment 
variable.'),
+                new UelMapping('sys:getProperty', 
UelFunctions.getMethod('sysGetProp', String),
+                        'Gets the system property indicated by the specified 
key.'),
+                new UelMapping('util:size', UelFunctions.getMethod('getSize', 
Object),
+                        'Returns the default Locale.'),
+                new UelMapping('util:defaultLocale', 
Locale.getMethod('getDefault'),
+                        'Returns the default TimeZone.'),
+                new UelMapping('util:defaultTimeZone', 
TimeZone.getMethod('getDefault'),
+                        'Return the label present in ressource on the given 
locale.'),
+                new UelMapping('util:label', UelFunctions.getMethod('label', 
String, String, Locale),
+                        'Returns the size of Maps,'),
+                new UelMapping('screen:id', 
UelFunctions.getMethod('resolveCurrentScreenId', ScreenRenderer.ScreenStack),
+                        'Returns the id of the current screen, Collections, 
and Strings. Invalid Object types return -1.'),
+                new UelMapping('dom:readHtmlDocument', 
UelFunctions.getMethod('readHtmlDocument', String),
+                        'Reads an HTML file and returns a org.w3c.dom.Document 
instance.'),
+                new UelMapping('dom:readXmlDocument', 
UelFunctions.getMethod('readXmlDocument', String),
+                        'Reads an XML file and returns a org.w3c.dom.Document 
instance.'),
+                new UelMapping('dom:toHtmlString', 
UelFunctions.getMethod('toHtmlString', Node, String, boolean, int),
+                        'Returns a org.w3c.dom.Node as an HTML String.'),
+                new UelMapping('dom:toXmlString', 
UelFunctions.getMethod('toXmlString', Node, String, boolean, boolean, int),
+                        'Returns a org.w3c.dom.Node as an XML String.'),
+                new UelMapping('dom:writeXmlDocument', 
UelFunctions.getMethod('writeXmlDocument', String, Node, String, boolean, 
boolean, int),
+                        'Writes a org.w3c.dom.Node to an XML file and returns 
true if successful.')
+        ]
+    }
+
+}
diff --git 
a/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/StringUel.groovy
 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/StringUel.groovy
new file mode 100644
index 0000000000..f675edb9c1
--- /dev/null
+++ 
b/framework/base/src/main/groovy/org/apache/ofbiz/base/util/string/uel/StringUel.groovy
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.base.util.string.uel
+
+import org.apache.ofbiz.base.util.string.IUelMappingLibrary
+import org.apache.ofbiz.base.util.string.UelFunctions
+import org.apache.ofbiz.base.util.string.UelMapping
+
+/**
+ * Class for importing the String Uel
+ */
+class StringUel implements IUelMappingLibrary {
+
+    @Override
+    List<UelMapping> getUelMappingList() {
+        return [
+                new UelMapping('str:endsWith', 
UelFunctions.getMethod('endsWith', String, String),
+                        'Returns true if this string ends with the specified 
suffix.'),
+                new UelMapping('str:indexOf', 
UelFunctions.getMethod('indexOf', String, String),
+                        'Returns the index within this string of the first 
occurrence of the specified substring .'),
+                new UelMapping('str:lastIndexOf', 
UelFunctions.getMethod('lastIndexOf', String, String),
+                        'Returns the index within this string of the last 
occurrence of the specified character.'),
+                new UelMapping('str:length', UelFunctions.getMethod('length', 
String),
+                        'Returns the length of this string.'),
+                new UelMapping('str:replace', 
UelFunctions.getMethod('replace', String, String, String),
+                        'Replaces each substring of this string that matches 
the literal target sequence with the' +
+                                ' specified literal replacement sequence.'),
+                new UelMapping('str:replaceAll', 
UelFunctions.getMethod('replaceAll', String, String, String),
+                        'Replaces each substring of this string that matches 
the given regular expression with the given replacement.'),
+                new UelMapping('str:replaceFirst', 
UelFunctions.getMethod('replaceFirst', String, String, String),
+                        'Replaces the first substring of this string that 
matches the given regular expression with the given replacement.'),
+                new UelMapping('str:startsWith', 
UelFunctions.getMethod('startsWith', String, String),
+                        'Returns true if this string starts with the specified 
prefix.'),
+                new UelMapping('str:endstring', 
UelFunctions.getMethod('endString', String, int),
+                        'Returns a new string that is a substring of this 
string. The substring begins with the' +
+                                ' character at the specified index and extends 
to the end of this string.'),
+                new UelMapping('str:substring', 
UelFunctions.getMethod('subString', String, int, int),
+                        'Returns a new string that is a substring of this 
string. The substring begins at the ' +
+                                'specified beginIndex and extends to the 
character at index endIndex - 1. Thus ' +
+                                'the length of the substring is 
endIndex-beginIndex.'),
+                new UelMapping('str:toString', 
UelFunctions.getMethod('toString', Object),
+                        'Converts all of the characters in this String to 
lower case using the rules of the default locale.'),
+                new UelMapping('str:toLowerCase', 
UelFunctions.getMethod('toLowerCase', String),
+                        'Converts Object to a String - bypassing 
localization.'),
+                new UelMapping('str:toUpperCase', 
UelFunctions.getMethod('toUpperCase', String),
+                        'Converts all of the characters in this String to 
upper case using the rules of the default locale.'),
+                new UelMapping('str:trim', UelFunctions.getMethod('trim', 
String),
+                        'Returns a copy of the string, with leading and 
trailing whitespace omitted.')
+        ]
+    }
+
+}
diff --git 
a/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java
 
b/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java
index 3dcb13bdbd..d611a418c3 100644
--- 
a/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java
+++ 
b/framework/base/src/main/java/org/apache/ofbiz/base/component/ComponentConfig.java
@@ -132,6 +132,16 @@ public final class ComponentConfig {
                 .collect(Collectors.toList());
     }
 
+    /**
+     * Provides the list of all the keystore information available in 
components.
+     * @return a list of keystore information
+     */
+    public static List<UelMappingInfo> getAllUelMappingInfo() {
+        return components()
+                .flatMap(cc -> cc.getUelMappings().stream())
+                .collect(Collectors.toList());
+    }
+
     /**
      * Provides the list of all the keystore information available in 
components.
      * @return a list of keystore information
@@ -270,6 +280,7 @@ public final class ComponentConfig {
     private final List<TestSuiteInfo> testSuiteInfos;
     private final List<KeystoreInfo> keystoreInfos;
     private final List<WebappInfo> webappInfos;
+    private final List<UelMappingInfo> uelMappings;
     private final List<ContainerConfig.Configuration> configurations;
 
     /**
@@ -293,6 +304,7 @@ public final class ComponentConfig {
         this.keystoreInfos = b.keystoreInfos;
         this.webappInfos = b.webappInfos;
         this.configurations = b.configurations;
+        this.uelMappings = b.uelMappings;
     }
 
     /**
@@ -311,6 +323,7 @@ public final class ComponentConfig {
         private List<TestSuiteInfo> testSuiteInfos;
         private List<KeystoreInfo> keystoreInfos;
         private List<WebappInfo> webappInfos;
+        private List<UelMappingInfo> uelMappings;
         private List<ContainerConfig.Configuration> configurations;
 
         public Builder globalName(String name) {
@@ -373,6 +386,11 @@ public final class ComponentConfig {
             return this;
         }
 
+        public Builder uelMappings(List<UelMappingInfo> uelMappings) {
+            this.uelMappings = uelMappings;
+            return this;
+        }
+
         public Builder configurations(List<ContainerConfig.Configuration> 
configurations) {
             this.configurations = configurations;
             return this;
@@ -422,6 +440,7 @@ public final class ComponentConfig {
         testSuiteInfos = collectElements(componentElement, "test-suite", 
TestSuiteInfo::new);
         keystoreInfos = collectElements(componentElement, "keystore", 
KeystoreInfo::new);
         webappInfos = collectElements(componentElement, "webapp", 
WebappInfo::new);
+        uelMappings = collectElements(componentElement, "uel-mapping", 
UelMappingInfo::new);
         resourceLoaderInfos = UtilXml.childElementList(componentElement, 
"resource-loader").stream()
                 .map(ResourceLoaderInfo::new)
                 .collect(Collectors.collectingAndThen(
@@ -523,6 +542,10 @@ public final class ComponentConfig {
         return this.keystoreInfos;
     }
 
+    private List<UelMappingInfo> getUelMappings() {
+        return this.uelMappings;
+    }
+
     public Map<String, ResourceLoaderInfo> getResourceLoaderInfos() {
         return this.resourceLoaderInfos;
     }
@@ -762,6 +785,25 @@ public final class ComponentConfig {
         }
     }
 
+    /**
+     * An object that models a component's uel methods mapping
+     * @see <code>ofbiz-component.xsd</code>
+     */
+    public static final class UelMappingInfo extends ResourceInfo {
+        private final String name;
+        private final String className;
+
+        private UelMappingInfo(ComponentConfig componentConfig, Element 
element) {
+            super(componentConfig, element);
+            this.name = element.getAttribute("name");
+            this.className = element.getAttribute("class-name");
+        }
+
+        public String getClassName() {
+            return this.className;
+        }
+    }
+
     /**
      * An object that models the <code>&lt;keystore&gt;</code> element.
      * @see <code>ofbiz-component.xsd</code>
diff --git 
a/framework/base/src/main/java/org/apache/ofbiz/base/util/string/IUelMappingLibrary.java
 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/string/IUelMappingLibrary.java
new file mode 100644
index 0000000000..ee96cdbd3c
--- /dev/null
+++ 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/string/IUelMappingLibrary.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ 
*******************************************************************************/
+package org.apache.ofbiz.base.util.string;
+
+import java.util.List;
+
+/**
+ * Interface used for loading the Uel methods.
+ * Just extend it and add an element to an ofbiz-component to add custom 
methods.
+ */
+public interface IUelMappingLibrary {
+
+    List<UelMapping> getUelMappingList();
+
+}
diff --git 
a/framework/base/src/main/java/org/apache/ofbiz/base/util/string/UelFunctions.java
 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/string/UelFunctions.java
index e6438e641c..1a6b87e8ac 100644
--- 
a/framework/base/src/main/java/org/apache/ofbiz/base/util/string/UelFunctions.java
+++ 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/string/UelFunctions.java
@@ -24,15 +24,20 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.sql.Timestamp;
 import java.text.DateFormat;
 import java.util.Collection;
-import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
 import java.util.TimeZone;
 
 import jakarta.el.FunctionMapper;
@@ -45,6 +50,7 @@ import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.stream.StreamSource;
 
+import org.apache.ofbiz.base.component.ComponentConfig;
 import org.apache.ofbiz.base.location.FlexibleLocation;
 import org.apache.ofbiz.base.util.Debug;
 import org.apache.ofbiz.base.util.FileUtil;
@@ -56,165 +62,28 @@ import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.xml.sax.SAXException;
 
+import static 
org.apache.ofbiz.base.component.ComponentConfig.getAllUelMappingInfo;
+
 /**
  * Implements Unified Expression Language functions.
- * <p>Built-in functions are divided into a number of
- * namespace prefixes:</p>
- * <table border="1">
- * <caption>Miscellaneous date/time functions</caption>
- * <tr><td colspan="2"><b><code>date:</code> contains miscellaneous date/time 
functions</b></td></tr>
- * <tr><td><code>date:second(Timestamp, TimeZone, 
Locale)</code></td><td>Returns the second value of <code>Timestamp</code> (0 - 
59).</td></tr>
- * <tr><td><code>date:minute(Timestamp, TimeZone, 
Locale)</code></td><td>Returns the minute value of <code>Timestamp</code> (0 - 
59).</td></tr>
- * <tr><td><code>date:hour(Timestamp, TimeZone, Locale)</code></td><td>Returns 
the hour value of <code>Timestamp</code> (0 - 23).</td></tr>
- * <tr><td><code>date:dayOfMonth(Timestamp, TimeZone, 
Locale)</code></td><td>Returns the day of month value of <code>Timestamp</code> 
(1 - 31)
- * .</td></tr>
- * <tr><td><code>date:dayOfWeek(Timestamp, TimeZone, 
Locale)</code></td><td>Returns the day of week value of <code>Timestamp</code> 
(Sunday = 1,
- * Saturday = 7).</td></tr>
- * <tr><td><code>date:dayOfYear(Timestamp, TimeZone, 
Locale)</code></td><td>Returns the day of year value of 
<code>Timestamp</code>.</td></tr>
- * <tr><td><code>date:week(Timestamp, TimeZone, Locale)</code></td><td>Returns 
the week value of <code>Timestamp</code>.</td></tr>
- * <tr><td><code>date:month(Timestamp, TimeZone, 
Locale)</code></td><td>Returns the month value of <code>Timestamp</code> 
(January = 0, December =
- * 11).</td></tr>
- * <tr><td><code>date:year(Timestamp, TimeZone, Locale)</code></td><td>Returns 
the year value of <code>Timestamp</code>.</td></tr>
- * <tr><td><code>date:dayStart(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> set to start of 
day.</td></tr>
- * <tr><td><code>date:dayEnd(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> set to end of 
day.</td></tr>
- * <tr><td><code>date:weekStart(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> set to start of 
week.</td></tr>
- * <tr><td><code>date:weekEnd(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> set to end of 
week.</td></tr>
- * <tr><td><code>date:monthStart(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> set to start of 
month.</td></tr>
- * <tr><td><code>date:monthEnd(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> set to end of 
month.</td></tr>
- * <tr><td><code>date:yearStart(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> set to start of 
year.</td></tr>
- * <tr><td><code>date:yearEnd(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> set to end of 
year.</td></tr>
- * <tr><td><code>date:dateStr(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> as a date 
<code>String</code> (yyyy-mm-dd)
- * .</td></tr>
- * <tr><td><code>date:localizedDateStr(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> as a date 
<code>String</code>
- * formatted according to the supplied locale.</td></tr>
- * <tr><td><code>date:dateTimeStr(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> as a date-time 
<code>String</code>
- * (yyyy-mm-dd hh:mm).</td></tr>
- * <tr><td><code>date:localizedDateTimeStr(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> as a date-time
- * <code>String</code> formatted according to the supplied locale.</td></tr>
- * <tr><td><code>date:timeStr(Timestamp, TimeZone, 
Locale)</code></td><td>Returns <code>Timestamp</code> as a time 
<code>String</code> (hh:mm)
- * .</td></tr>
- * <tr><td><code>date:nowTimestamp()</code></td><td>Returns <code>Timestamp 
</code> for right now.</td></tr>
- * <tr><td colspan="2"><b><code>math:</code> maps to 
<code>java.lang.Math</code></b></td></tr>
- * <tr><td><code>math:absDouble(double)</code></td><td>Returns the absolute 
value of a <code>double</code> value.</td></tr>
- * <tr><td><code>math:absFloat(float)</code></td><td>Returns the absolute 
value of a <code>float</code> value.</td></tr>
- * <tr><td><code>math:absInt(int)</code></td><td>Returns the absolute value of 
an <code>int</code> value.</td></tr>
- * <tr><td><code>math:absLong(long)</code></td><td>Returns the absolute value 
of a <code>long</code> value.</td></tr>
- * <tr><td><code>math:acos(double)</code></td><td>Returns the arc cosine of an 
angle, in the range of 0.0 through <i>pi</i>.</td></tr>
- * <tr><td><code>math:asin(double)</code></td><td>Returns the arc sine of an 
angle, in the range of -<i>pi</i>/2 through <i>pi</i>/2.</td></tr>
- * <tr><td><code>math:atan(double)</code></td><td>Returns the arc tangent of 
an angle, in the range of -<i>pi</i>/2 through <i>pi</i>/2.</td></tr>
- * <tr><td><code>math:atan2(double, double)</code></td><td>Converts 
rectangular coordinates (<code>x</code>,&nbsp;<code>y</code>) to polar (r,
- * &nbsp;<i>theta</i>).</td></tr>
- * <tr><td><code>math:cbrt(double)</code></td><td>Returns the cube root of a 
<code>double</code> value.</td></tr>
- * <tr><td><code>math:ceil(double)</code></td><td>Returns the smallest 
(closest to negative infinity) <code>double</code> value that is greater
- * than or equal to the argument and is equal to a mathematical 
integer.</td></tr>
- * <tr><td><code>math:cos(double)</code></td><td>Returns the trigonometric 
cosine of an angle.</td></tr>
- * <tr><td><code>math:cosh(double)</code></td><td>Returns the hyperbolic 
cosine of a <code>double</code> value.</td></tr>
- * <tr><td><code>math:exp(double)</code></td><td>Returns Euler's number 
<i>e</i> raised to the power of a <code>double</code> value.</td></tr>
- * <tr><td><code>math:expm1(double)</code></td><td>Returns 
<i>e</i><sup>x</sup>&nbsp;-1.</td></tr>
- * <tr><td><code>math:floor(double)</code></td><td>Returns the largest 
(closest to positive infinity) <code>double</code> value that is less than
- * or equal to the argument and is equal to a mathematical integer.</td></tr>
- * <tr><td><code>math:hypot(double, double)</code></td><td>Returns 
sqrt(<i>x</i><sup>2</sup>&nbsp;+<i>y</i><sup>2</sup>) without intermediate
- * overflow or underflow.</td></tr>
- * <tr><td><code>math:IEEEremainder(double, double)</code></td><td>Computes 
the remainder operation on two arguments as prescribed by the IEEE 754
- * standard.</td></tr>
- * <tr><td><code>math:log(double)</code></td><td>Returns the natural logarithm 
(base <i>e</i>) of a <code>double</code> value.</td></tr>
- * <tr><td><code>math:log10(double)</code></td><td>Returns the base 10 
logarithm of a <code>double</code> value.</td></tr>
- * <tr><td><code>math:log1p(double)</code></td><td>Returns the natural 
logarithm of the sum of the argument and 1.</td></tr>
- * <tr><td><code>math:maxDouble(double, double)</code></td><td>Returns the 
greater of two <code>double</code> values.</td></tr>
- * <tr><td><code>math:maxFloat(float, float)</code></td><td>Returns the 
greater of two <code>float</code> values.</td></tr>
- * <tr><td><code>math:maxInt(int, int)</code></td><td>Returns the greater of 
two <code>int</code> values.</td></tr>
- * <tr><td><code>math:maxLong(long, long)</code></td><td>Returns the greater 
of two <code>long</code> values.</td></tr>
- * <tr><td><code>math:minDouble(double, double)</code></td><td>Returns the 
smaller of two <code>double</code> values.</td></tr>
- * <tr><td><code>math:minFloat(float, float)</code></td><td>Returns the 
smaller of two <code>float</code> values.</td></tr>
- * <tr><td><code>math:minInt(int, int)</code></td><td>Returns the smaller of 
two <code>int</code> values.</td></tr>
- * <tr><td><code>math:minLong(long, long)</code></td><td>Returns the smaller 
of two <code>long</code> values.</td></tr>
- * <tr><td><code>math:pow(double, double)</code></td><td>Returns the value of 
the first argument raised to the power of the second argument.</td></tr>
- * <tr><td><code>math:random()</code></td><td>Returns a <code>double</code> 
value with a positive sign, greater than or equal to <code>0.0</code>
- * and less than <code>1.0</code>.</td></tr>
- * <tr><td><code>math:rint(double)</code></td><td>Returns the 
<code>double</code> value that is closest in value to the argument and is equal 
to a
- * mathematical integer.</td></tr>
- * <tr><td><code>math:roundDouble(double)</code></td><td>Returns the closest 
<code>long</code> to the argument.</td></tr>
- * <tr><td><code>math:roundFloat(float)</code></td><td>Returns the closest 
<code>int</code> to the argument.</td></tr>
- * <tr><td><code>math:signumDouble(double)</code></td><td>Returns the signum 
function of the argument; zero if the argument is zero, 1.0 if the
- * argument is greater than zero, -1.0 if the argument is less than 
zero.</td></tr>
- * <tr><td><code>math:signumFloat(float)</code></td><td>Returns the signum 
function of the argument; zero if the argument is zero, 1.0f if the
- * argument is greater than zero, -1.0f if the argument is less than 
zero.</td></tr>
- * <tr><td><code>math:sin(double)</code></td><td>Returns the trigonometric 
sine of an angle.</td></tr>
- * <tr><td><code>math:sinh(double)</code></td><td>Returns the hyperbolic sine 
of a <code>double</code> value.</td></tr>
- * <tr><td><code>math:sqrt(double)</code></td><td>Returns the correctly 
rounded positive square root of a <code>double</code> value.</td></tr>
- * <tr><td><code>math:tan(double)</code></td><td>Returns the trigonometric 
tangent of an angle.</td></tr>
- * <tr><td><code>math:tanh(double)</code></td><td>Returns the hyperbolic 
tangent of a <code>double</code> value.</td></tr>
- * <tr><td><code>math:toDegrees(double)</code></td><td>Converts an angle 
measured in radians to an approximately equivalent angle measured in
- * degrees.</td></tr>
- * <tr><td><code>math:toRadians(double)</code></td><td>Converts an angle 
measured in degrees to an approximately equivalent angle measured in
- * radians.</td></tr>
- * <tr><td><code>math:ulpDouble(double)</code></td><td>Returns the size of an 
ulp (units in the last place) of the argument.</td></tr>
- * <tr><td><code>math:ulpFloat(float)</code></td><td>Returns the size of an 
ulp (units in the last place) of the argument.</td></tr>
- * <tr><td colspan="2"><b><code>str:</code> maps to 
<code>java.lang.String</code></b></td></tr>
- * <tr><td><code>str:endsWith(String, String)</code></td><td>Returns 
<code>true</code> if this string ends with the specified suffix.</td></tr>
- * <tr><td><code>str:indexOf(String, String)</code></td><td>Returns the index 
within this string of the first occurrence of the specified substring
- * .</td></tr>
- * <tr><td><code>str:lastIndexOf(String, String)</code></td><td>Returns the 
index within this string of the last occurrence of the specified
- * character.</td></tr>
- * <tr><td><code>str:length(String)</code></td><td>Returns the length of this 
string.</td></tr>
- * <tr><td><code>str:replace(String, String, String)</code></td><td>Replaces 
each substring of this string that matches the literal target sequence
- * with the specified literal replacement sequence.</td></tr>
- * <tr><td><code>str:replaceAll(String, String, 
String)</code></td><td>Replaces each substring of this string that matches the 
given regular
- * expression with the given replacement.</td></tr>
- * <tr><td><code>str:replaceFirst(String, String, 
String)</code></td><td>Replaces the first substring of this string that matches 
the given regular
- * expression with the given replacement.</td></tr>
- * <tr><td><code>str:startsWith(String, String)</code></td><td>Returns 
<code>true</code> if this string starts with the specified prefix.</td></tr>
- * <tr><td><code>str:endstring(String, int)</code></td><td>Returns a new 
string that is a substring of this string. The substring begins with the
- * character at the specified index and extends to the end of this 
string.</td></tr>
- * <tr><td><code>str:substring(String, int, int)</code></td><td>Returns a new 
string that is a substring of this string. The substring begins at
- * the specified beginIndex and extends to the character at index endIndex - 
1. Thus the length of the substring is endIndex-beginIndex.</td></tr>
- * <tr><td><code>str:toLowerCase(String)</code></td><td>Converts all of the 
characters in this String to lower case using the rules of the default
- * locale.</td></tr>
- * <tr><td><code>str:toString(Object)</code></td><td>Converts 
<code>Object</code> to a <code>String</code> - bypassing localization.</td></tr>
- * <tr><td><code>str:toUpperCase(String)</code></td><td>Converts all of the 
characters in this String to upper case using the rules of the default
- * locale.</td></tr>
- * <tr><td><code>str:trim(String)</code></td><td>Returns a copy of the string, 
with leading and trailing whitespace omitted.</td></tr>
- * <tr><td colspan="2"><b><code>sys:</code> maps to 
<code>java.lang.System</code></b></td></tr>
- * <tr><td><code>sys:getenv(String)</code></td><td>Gets the value of the 
specified environment variable.</td></tr>
- * <tr><td><code>sys:getProperty(String)</code></td><td>Gets the system 
property indicated by the specified key.</td></tr>
- * <tr><td colspan="2"><b><code>util:</code> contains miscellaneous utility 
functions</b></td></tr>
- * <tr><td><code>util:defaultLocale()</code></td><td>Returns the default 
<code>Locale</code>.</td></tr>
- * <tr><td><code>util:defaultTimeZone()</code></td><td>Returns the default 
<code>TimeZone</code>.</td></tr>
- * <tr><td><code>util:label(String, String, Locale)</code></td><td>Return the 
label present in ressource on the given locale.</td></tr>
- * <tr><td><code>util:size(Object)</code></td><td>Returns the size of 
<code>Maps</code>,
- * <tr><td><code>screen:id(ScreenStack)</code></td><td>Returns the id of the 
current screen,
- * <code>Collections</code>, and <code>Strings</code>. Invalid 
<code>Object</code> types return -1.</td></tr>
- * <tr><td colspan="2"><b><code>dom:</code> contains 
<code>org.w3c.dom.*</code> functions</b></td></tr>
- * <tr><td><code>dom:readHtmlDocument(String)</code></td><td>Reads an HTML 
file and returns a <code>org.w3c.dom.Document</code> instance.</td></tr>
- * <tr><td><code>dom:readXmlDocument(String)</code></td><td>Reads an XML file 
and returns a <code>org.w3c.dom.Document</code> instance.</td></tr>
- * <tr><td><code>dom:toHtmlString(Node, String encoding, boolean indent, int 
indentAmount)</code></td><td>Returns a <code>org.w3c.dom.Node</code>
- * as an HTML <code>String</code>.</td></tr>
- * <tr><td><code>dom:toXmlString(Node, String encoding, boolean 
omitXmlDeclaration, boolean indent, int indentAmount)</code></td><td>Returns a
- * <code>org.w3c.dom.Node</code> as an XML <code>String</code>.</td></tr>
- * <tr><td><code>dom:writeXmlDocument(String, Node, String encoding, boolean 
omitXmlDeclaration, boolean indent, int indentAmount)
- * </code></td><td>Writes a <code>org.w3c.dom.Node</code> to an XML file and 
returns <code>true</code> if successful.</td></tr>
- * </table>
+ * <p>The uel functions mappings are defined in the 
<code>ofbiz-component</code> files. An example can
+ * be found in the base component of the framework.</p>
  */
 public class UelFunctions {
 
     protected static final Functions FUNCTION_MAPPER = new Functions();
     public static final String MODULE = UelFunctions.class.getName();
 
+    public UelFunctions() { }
+
     /**
      * Returns a <code>FunctionMapper</code> instance.
      * @return <code>FunctionMapper</code> instance
      */
-    public static FunctionMapper getFunctionMapper() {
+    public static Functions getFunctionMapper() {
         return FUNCTION_MAPPER;
     }
 
-    /**
-     * Add a function to OFBiz's built-in UEL functions.
-     */
-    public static synchronized void setFunction(String prefix, String 
localName, Method method) {
-        FUNCTION_MAPPER.functionMap.put(prefix + ":" + localName, method);
-    }
-
     public static String dateString(Timestamp stamp, TimeZone timeZone, Locale 
locale) {
         DateFormat dateFormat = 
UtilDateTime.toDateFormat(UtilDateTime.getDateFormat(), timeZone, locale);
         dateFormat.setTimeZone(timeZone);
@@ -468,119 +337,51 @@ public class UelFunctions {
         return null;
     }
 
-    protected static class Functions extends FunctionMapper {
-        private final Map<String, Method> functionMap = new HashMap<>();
+    protected static final class Functions extends FunctionMapper {
+        private Set<UelMapping> functionList = new HashSet<>();
 
         public Functions() {
-            try {
-                this.functionMap.put("date:second", 
UtilDateTime.class.getMethod("getSecond", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:minute", 
UtilDateTime.class.getMethod("getMinute", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:hour", 
UtilDateTime.class.getMethod("getHour", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:dayOfMonth", 
UtilDateTime.class.getMethod("getDayOfMonth", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:dayOfWeek", 
UtilDateTime.class.getMethod("getDayOfWeek", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:dayOfYear", 
UtilDateTime.class.getMethod("getDayOfYear", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:week", 
UtilDateTime.class.getMethod("getWeek", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:month", 
UtilDateTime.class.getMethod("getMonth", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:year", 
UtilDateTime.class.getMethod("getYear", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:dayStart", 
UtilDateTime.class.getMethod("getDayStart", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:dayEnd", 
UtilDateTime.class.getMethod("getDayEnd", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:weekStart", 
UtilDateTime.class.getMethod("getWeekStart", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:weekEnd", 
UtilDateTime.class.getMethod("getWeekEnd", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:monthStart", 
UtilDateTime.class.getMethod("getMonthStart", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:monthEnd", 
UtilDateTime.class.getMethod("getMonthEnd", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:yearStart", 
UtilDateTime.class.getMethod("getYearStart", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:yearEnd", 
UtilDateTime.class.getMethod("getYearEnd", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:dateStr", 
UelFunctions.class.getMethod("dateString", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:localizedDateStr", 
UelFunctions.class.getMethod("localizedDateString", Timestamp.class, 
TimeZone.class,
-                        Locale.class));
-                this.functionMap.put("date:localizedDateTimeStr", 
UelFunctions.class.getMethod("localizedDateTimeString", Timestamp.class,
-                        TimeZone.class, Locale.class));
-                this.functionMap.put("date:timeStr", 
UelFunctions.class.getMethod("timeString", Timestamp.class, TimeZone.class, 
Locale.class));
-                this.functionMap.put("date:nowTimestamp", 
UtilDateTime.class.getMethod("nowTimestamp"));
-                this.functionMap.put("math:absDouble", 
Math.class.getMethod("abs", double.class));
-                this.functionMap.put("math:absFloat", 
Math.class.getMethod("abs", float.class));
-                this.functionMap.put("math:absInt", 
Math.class.getMethod("abs", int.class));
-                this.functionMap.put("math:absLong", 
Math.class.getMethod("abs", long.class));
-                this.functionMap.put("math:acos", Math.class.getMethod("abs", 
double.class));
-                this.functionMap.put("math:asin", Math.class.getMethod("asin", 
double.class));
-                this.functionMap.put("math:atan", Math.class.getMethod("atan", 
double.class));
-                this.functionMap.put("math:atan2", Math.class.getMethod("max", 
double.class, double.class));
-                this.functionMap.put("math:cbrt", Math.class.getMethod("cbrt", 
double.class));
-                this.functionMap.put("math:ceil", Math.class.getMethod("ceil", 
double.class));
-                this.functionMap.put("math:cos", Math.class.getMethod("cos", 
double.class));
-                this.functionMap.put("math:cosh", Math.class.getMethod("cosh", 
double.class));
-                this.functionMap.put("math:exp", Math.class.getMethod("exp", 
double.class));
-                this.functionMap.put("math:expm1", 
Math.class.getMethod("expm1", double.class));
-                this.functionMap.put("math:floor", 
Math.class.getMethod("floor", double.class));
-                this.functionMap.put("math:hypot", 
Math.class.getMethod("hypot", double.class, double.class));
-                this.functionMap.put("math:IEEEremainder", 
Math.class.getMethod("IEEEremainder", double.class, double.class));
-                this.functionMap.put("math:log", Math.class.getMethod("log", 
double.class));
-                this.functionMap.put("math:log10", 
Math.class.getMethod("log10", double.class));
-                this.functionMap.put("math:log1p", 
Math.class.getMethod("log1p", double.class));
-                this.functionMap.put("math:maxDouble", 
Math.class.getMethod("max", double.class, double.class));
-                this.functionMap.put("math:maxFloat", 
Math.class.getMethod("max", float.class, float.class));
-                this.functionMap.put("math:maxInt", 
Math.class.getMethod("max", int.class, int.class));
-                this.functionMap.put("math:maxLong", 
Math.class.getMethod("max", long.class, long.class));
-                this.functionMap.put("math:minDouble", 
Math.class.getMethod("min", double.class, double.class));
-                this.functionMap.put("math:minFloat", 
Math.class.getMethod("min", float.class, float.class));
-                this.functionMap.put("math:minInt", 
Math.class.getMethod("min", int.class, int.class));
-                this.functionMap.put("math:minLong", 
Math.class.getMethod("min", long.class, long.class));
-                this.functionMap.put("math:pow", Math.class.getMethod("pow", 
double.class, double.class));
-                this.functionMap.put("math:random", 
Math.class.getMethod("random"));
-                this.functionMap.put("math:rint", Math.class.getMethod("rint", 
double.class));
-                this.functionMap.put("math:roundDouble", 
Math.class.getMethod("round", double.class));
-                this.functionMap.put("math:roundFloat", 
Math.class.getMethod("round", float.class));
-                this.functionMap.put("math:signumDouble", 
Math.class.getMethod("signum", double.class));
-                this.functionMap.put("math:signumFloat", 
Math.class.getMethod("signum", float.class));
-                this.functionMap.put("math:sin", Math.class.getMethod("sin", 
double.class));
-                this.functionMap.put("math:sinh", Math.class.getMethod("sinh", 
double.class));
-                this.functionMap.put("math:sqrt", Math.class.getMethod("sqrt", 
double.class));
-                this.functionMap.put("math:tan", Math.class.getMethod("tan", 
double.class));
-                this.functionMap.put("math:tanh", Math.class.getMethod("tanh", 
double.class));
-                this.functionMap.put("math:toDegrees", 
Math.class.getMethod("toDegrees", double.class));
-                this.functionMap.put("math:toRadians", 
Math.class.getMethod("toRadians", double.class));
-                this.functionMap.put("math:ulpDouble", 
Math.class.getMethod("ulp", double.class));
-                this.functionMap.put("math:ulpFloat", 
Math.class.getMethod("ulp", float.class));
-                this.functionMap.put("str:endsWith", 
UelFunctions.class.getMethod("endsWith", String.class, String.class));
-                this.functionMap.put("str:indexOf", 
UelFunctions.class.getMethod("indexOf", String.class, String.class));
-                this.functionMap.put("str:lastIndexOf", 
UelFunctions.class.getMethod("lastIndexOf", String.class, String.class));
-                this.functionMap.put("str:length", 
UelFunctions.class.getMethod("length", String.class));
-                this.functionMap.put("str:replace", 
UelFunctions.class.getMethod("replace", String.class, String.class, 
String.class));
-                this.functionMap.put("str:replaceAll", 
UelFunctions.class.getMethod("replaceAll", String.class, String.class, 
String.class));
-                this.functionMap.put("str:replaceFirst", 
UelFunctions.class.getMethod("replaceFirst", String.class, String.class, 
String.class));
-                this.functionMap.put("str:startsWith", 
UelFunctions.class.getMethod("startsWith", String.class, String.class));
-                this.functionMap.put("str:endstring", 
UelFunctions.class.getMethod("endString", String.class, int.class));
-                this.functionMap.put("str:substring", 
UelFunctions.class.getMethod("subString", String.class, int.class, int.class));
-                this.functionMap.put("str:toString", 
UelFunctions.class.getMethod("toString", Object.class));
-                this.functionMap.put("str:toLowerCase", 
UelFunctions.class.getMethod("toLowerCase", String.class));
-                this.functionMap.put("str:toUpperCase", 
UelFunctions.class.getMethod("toUpperCase", String.class));
-                this.functionMap.put("str:trim", 
UelFunctions.class.getMethod("trim", String.class));
-                this.functionMap.put("sys:getenv", 
UelFunctions.class.getMethod("sysGetEnv", String.class));
-                this.functionMap.put("sys:getProperty", 
UelFunctions.class.getMethod("sysGetProp", String.class));
-                this.functionMap.put("util:size", 
UelFunctions.class.getMethod("getSize", Object.class));
-                this.functionMap.put("util:defaultLocale", 
Locale.class.getMethod("getDefault"));
-                this.functionMap.put("util:defaultTimeZone", 
TimeZone.class.getMethod("getDefault"));
-                this.functionMap.put("util:label", 
UelFunctions.class.getMethod("label", String.class, String.class, 
Locale.class));
-                this.functionMap.put("screen:id", 
UelFunctions.class.getMethod("resolveCurrentScreenId", 
ScreenRenderer.ScreenStack.class));
-                this.functionMap.put("dom:readHtmlDocument", 
UelFunctions.class.getMethod("readHtmlDocument", String.class));
-                this.functionMap.put("dom:readXmlDocument", 
UelFunctions.class.getMethod("readXmlDocument", String.class));
-                this.functionMap.put("dom:toHtmlString", 
UelFunctions.class.getMethod("toHtmlString", Node.class, String.class, 
boolean.class,
-                        int.class));
-                this.functionMap.put("dom:toXmlString", 
UelFunctions.class.getMethod("toXmlString", Node.class, String.class, 
boolean.class,
-                        boolean.class, int.class));
-                this.functionMap.put("dom:writeXmlDocument", 
UelFunctions.class.getMethod("writeXmlDocument", String.class, Node.class,
-                        String.class, boolean.class, boolean.class, 
int.class));
-            } catch (NoSuchMethodException | NullPointerException | 
SecurityException e) {
-                Debug.logError(e, "Error while initializing 
UelFunctions.Functions instance", MODULE);
-            }
+            this.functionList = loadUelFromComponents();
             if (Debug.verboseOn()) {
-                Debug.logVerbose("UelFunctions.Functions loaded " + 
this.functionMap.size() + " functions", MODULE);
+                Debug.logVerbose("UelFunctions.Functions loaded " + 
this.functionList.size() + " functions", MODULE);
             }
         }
         /** resolve function */
         @Override
         public Method resolveFunction(String prefix, String localName) {
-            return functionMap.get(prefix + ":" + localName);
+            Optional<UelMapping> uelCandidate = functionList.stream()
+                    .filter(uelMapping -> Objects.equals(uelMapping.getKey(), 
prefix + ":" + localName))
+                    .findFirst();
+            return uelCandidate.map(UelMapping::getMethod).orElse(null);
+        }
+
+        private Set<UelMapping> loadUelFromComponents() {
+            List<ComponentConfig.UelMappingInfo> uelsMappingInfo = 
getAllUelMappingInfo();
+            Set<UelMapping> uelMappings = new HashSet<>();
+
+            uelsMappingInfo.forEach(uelMappingInfo -> {
+                String className = uelMappingInfo.getClassName();
+                IUelMappingLibrary mapping = null;
+                Class<?> lClass;
+                ClassLoader classLoader = 
Thread.currentThread().getContextClassLoader();
+                try {
+                    lClass = classLoader.loadClass(className);
+                    mapping = (IUelMappingLibrary) 
lClass.getDeclaredConstructor().newInstance();
+                } catch (ClassNotFoundException | InvocationTargetException | 
InstantiationException
+                         | IllegalAccessException | NoSuchMethodException e) {
+                    Debug.logError(e, "Error while initializing 
UelFunctions.Functions instance", MODULE);
+                }
+                if (mapping == null) {
+                    return;
+                }
+                uelMappings.addAll(mapping.getUelMappingList());
+            });
+            return uelMappings;
+        }
+
+        public Set<UelMapping> getUelFunctions() {
+            return this.functionList;
         }
     }
+
 }
diff --git 
a/framework/base/src/main/java/org/apache/ofbiz/base/util/string/UelMapping.java
 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/string/UelMapping.java
new file mode 100644
index 0000000000..d647fe76ea
--- /dev/null
+++ 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/string/UelMapping.java
@@ -0,0 +1,49 @@
+package org.apache.ofbiz.base.util.string;
+
+import java.lang.reflect.Method;
+
+/**
+ * Small Uel utility class
+ */
+public final class UelMapping {
+
+    /**
+     * The key of this Uel, often composed by a domain and a name separated by 
a column
+     */
+    private final String myKey;
+
+    /**
+     * The method that is called by the Uel
+     */
+    private final Method myMethod;
+
+    /**
+     * The description of the Uel that will be displayed in the Uel screen
+     */
+    private final String myDescription;
+
+    public UelMapping(String key, Method method) {
+        myKey = key;
+        myMethod = method;
+        myDescription = "No description";
+    }
+
+    public UelMapping(String key, Method method, String description) {
+        myKey = key;
+        myMethod = method;
+        myDescription = description;
+    }
+
+    public String getKey() {
+        return myKey;
+    }
+
+    public Method getMethod() {
+        return myMethod;
+    }
+
+    public String getDescription() {
+        return myDescription;
+    }
+
+}
diff --git a/framework/base/testdef/basetests.xml 
b/framework/base/testdef/basetests.xml
index 661302cf02..81b1638abd 100644
--- a/framework/base/testdef/basetests.xml
+++ b/framework/base/testdef/basetests.xml
@@ -18,9 +18,23 @@
   -->
 
 <test-suite suite-name="basetests"
-        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-        
xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/test-suite.xsd";>
+            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+            
xsi:noNamespaceSchemaLocation="http://ofbiz.apache.org/dtds/test-suite.xsd";>
     <test-case case-name="basesimpletests">
         <junit-test-suite class-name="org.apache.ofbiz.base.test.SimpleTests"/>
     </test-case>
+
+    <test-case case-name="uel-date-tests">
+        <junit-test-suite 
class-name="org.apache.ofbiz.base.test.uel.DateUelTest"/>
+    </test-case>
+    <test-case case-name="uel-math-tests">
+        <junit-test-suite 
class-name="org.apache.ofbiz.base.test.uel.MathUelTest"/>
+    </test-case>
+    <test-case case-name="uel-string-tests">
+        <junit-test-suite 
class-name="org.apache.ofbiz.base.test.uel.StringUelTest"/>
+    </test-case>
+    <test-case case-name="uel-misc-tests">
+        <junit-test-suite 
class-name="org.apache.ofbiz.base.test.uel.MiscUelTest"/>
+    </test-case>
+
 </test-suite>
diff --git a/framework/webtools/config/WebtoolsUiLabels.xml 
b/framework/webtools/config/WebtoolsUiLabels.xml
index 6edef76fa0..00443a0220 100644
--- a/framework/webtools/config/WebtoolsUiLabels.xml
+++ b/framework/webtools/config/WebtoolsUiLabels.xml
@@ -880,6 +880,10 @@
         <value xml:lang="zh">异步</value>
         <value xml:lang="zh-TW">非同步</value>
     </property>
+    <property key="WebtoolsAvailableUelMethods">
+        <value xml:lang="en">Available UEL Methods</value>
+        <value xml:lang="fr">Fonctions UEL disponibles</value>
+    </property>
     <property key="WebtoolsBackToCacheMaintenance">
         <value xml:lang="de">Zurück zur Cache-Wartung</value>
         <value xml:lang="en">Back to Cache Maintenance</value>
@@ -5688,6 +5692,22 @@
         <value xml:lang="zh">全部</value>
         <value xml:lang="zh-TW">全部</value>
     </property>
+    <property key="WebtoolsUelKeyString">
+        <value xml:lang="en">Uel string key</value>
+        <value xml:lang="fr">Clef de l'Uel</value>
+    </property>
+    <property key="WebtoolsUelMethodClass">
+        <value xml:lang="en">Uel Method class</value>
+        <value xml:lang="fr">Methode appelée par l'Uel</value>
+    </property>
+    <property key="WebtoolsUelMethodParams">
+        <value xml:lang="en">Uel method params</value>
+        <value xml:lang="fr">Paramètres de la methode Uel</value>
+    </property>
+    <property key="WebtoolsUelDescription">
+        <value xml:lang="en">Uel description</value>
+        <value xml:lang="fr">Description de l'Uel</value>
+    </property>
     <property key="WebtoolsUnCheckAll">
         <value xml:lang="de">Keine auswählen</value>
         <value xml:lang="en">Un-Check All</value>
diff --git 
a/framework/webtools/src/main/groovy/org/apache/ofbiz/webtools/util/uel/FindAvailableUel.groovy
 
b/framework/webtools/src/main/groovy/org/apache/ofbiz/webtools/util/uel/FindAvailableUel.groovy
new file mode 100644
index 0000000000..d64fff2e7c
--- /dev/null
+++ 
b/framework/webtools/src/main/groovy/org/apache/ofbiz/webtools/util/uel/FindAvailableUel.groovy
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+package org.apache.ofbiz.webtools.util.uel
+
+import org.apache.ofbiz.base.util.UtilMisc
+import org.apache.ofbiz.base.util.string.UelFunctions
+import org.apache.ofbiz.base.util.string.UelMapping
+
+List uelList = []
+Set<UelMapping> uelMap = UelFunctions.getFunctionMapper().getUelFunctions()
+uelMap.each { mapping ->
+    uelList << [
+            uelKeyString: mapping.key,
+            uelMethodClass: mapping.method.getDeclaringClass(),
+            uelMethodParams: 
mapping.method.getParameterTypes()*.getName().join(', '),
+            uelDescription: mapping.description ?: 'Uel has no description'
+    ]
+}
+
+context.uelList = (parameters.sortField) ? UtilMisc.sortMaps(uelList, 
[parameters.sortField]) : uelList
+
diff --git a/framework/webtools/template/Main.ftl 
b/framework/webtools/template/Main.ftl
index 4df797bc4d..e683f42b14 100644
--- a/framework/webtools/template/Main.ftl
+++ b/framework/webtools/template/Main.ftl
@@ -99,6 +99,7 @@ under the License.
         <#if security.hasPermission("PORTALPAGE_ADMIN", session)>
           <li><a 
href="<@ofbizUrl>FindGeo</@ofbizUrl>">${uiLabelMap.WebtoolsGeoManagement}</a></li>
           <li><a 
href="<@ofbizUrl>WebtoolsLayoutDemo</@ofbizUrl>">${uiLabelMap.WebtoolsLayoutDemo}</a></li>
+          <li><a 
href="<@ofbizUrl>AvailableUelList</@ofbizUrl>">${uiLabelMap.WebtoolsAvailableUelMethods}</a></li>
         </#if>
         <#if security.hasPermission("ENUM_STATUS_MAINT", session)>
           <#--
diff --git a/framework/webtools/webapp/webtools/WEB-INF/controller.xml 
b/framework/webtools/webapp/webtools/WEB-INF/controller.xml
index 375dd08768..a2499a9204 100644
--- a/framework/webtools/webapp/webtools/WEB-INF/controller.xml
+++ b/framework/webtools/webapp/webtools/WEB-INF/controller.xml
@@ -635,6 +635,7 @@ under the License.
     <request-map uri="WebtoolsLayoutDemoCsv"><security https="true" 
auth="true"/><response name="success" type="view" 
value="WebtoolsLayoutDemoCsv"/></request-map>
     <request-map uri="WebtoolsLayoutDemoXls"><security https="true" 
auth="true"/><response name="success" type="view" 
value="WebtoolsLayoutDemoXls"/></request-map>
 
+    <request-map uri="AvailableUelList"><security https="true" 
auth="true"/><response name="success" type="view" 
value="AvailableUelList"/></request-map>
     <!-- end of request mappings -->
 
     <!-- View Mappings -->
@@ -743,5 +744,6 @@ under the License.
     <view-map name="WebtoolsLayoutDemoCsv" type="screencsv" 
page="component://webtools/widget/MiscScreens.xml#WebtoolsLayoutDemoText" 
content-type="text/csv"/>
     <view-map name="WebtoolsLayoutDemoXls" type="screenxls" 
page="component://webtools/widget/MiscScreens.xml#WebtoolsLayoutDemoText" 
content-type="application/vnd.ms-excel"/>
 
+    <view-map name="AvailableUelList" type="screen" 
page="component://webtools/widget/MiscScreens.xml#AvailableUelList"/>
     <!-- end of view mappings -->
 </site-conf>
diff --git a/framework/webtools/widget/MiscScreens.xml 
b/framework/webtools/widget/MiscScreens.xml
index 48c69516c7..965eeea806 100644
--- a/framework/webtools/widget/MiscScreens.xml
+++ b/framework/webtools/widget/MiscScreens.xml
@@ -239,4 +239,26 @@ under the License.
             </widgets>
         </section>
     </screen>
+
+    <screen name="AvailableUelList">
+        <section>
+            <actions>
+                <script 
location="component://webtools/src/main/groovy/org/apache/ofbiz/webtools/util/uel/FindAvailableUel.groovy"/>
+            </actions>
+            <widgets>
+                <decorator-screen name="main-decorator" 
location="${parameters.mainDecoratorLocation}">
+                    <decorator-section name="body">
+                        <section>
+                            <widgets>
+                                <screenlet 
title="${uiLabelMap.WebtoolsAvailableUelMethods}">
+                                    <include-form name="ListUel" 
location="component://webtools/widget/UelForms.xml"/>
+                                </screenlet>
+                            </widgets>
+                        </section>
+                    </decorator-section>
+                </decorator-screen>
+            </widgets>
+        </section>
+    </screen>
+
 </screens>
diff --git a/framework/webtools/widget/UelForms.xml 
b/framework/webtools/widget/UelForms.xml
new file mode 100644
index 0000000000..e2150226ed
--- /dev/null
+++ b/framework/webtools/widget/UelForms.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<forms xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+       xmlns="http://ofbiz.apache.org/Widget-Form";
+       xsi:schemaLocation="http://ofbiz.apache.org/Widget-Form 
http://ofbiz.apache.org/dtds/widget-form.xsd";>
+
+    <grid name="ListUel" list-name="uelList" paginate-target="AvailableUelList"
+          separate-columns="true" odd-row-style="alternate-row" 
default-table-style="basic-table hover-bar"
+          header-row-style="header-row-2">
+        <field name="uelKeyString" title="${uiLabelMap.WebtoolsUelKeyString}" 
sort-field="true"><display/></field>
+        <field name="uelMethodClass" 
title="${uiLabelMap.WebtoolsUelMethodClass}" 
sort-field="true"><display/></field>
+        <field name="uelMethodParams" 
title="${uiLabelMap.WebtoolsUelMethodParams}" 
sort-field="true"><display/></field>
+        <field name="uelDescription" 
title="${uiLabelMap.WebtoolsUelDescription}" 
sort-field="true"><display/></field>
+    </grid>
+
+</forms>

Reply via email to