svn commit: r1848745 - in /ofbiz/ofbiz-framework/trunk: docs/asciidoc/developer-manual.adoc framework/minilang/docs/ framework/minilang/docs/asciidoc/ framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1848745 - in /ofbiz/ofbiz-framework/trunk: docs/asciidoc/developer-manual.adoc framework/minilang/docs/ framework/minilang/docs/asciidoc/ framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc

jleroux@apache.org
Author: jleroux
Date: Wed Dec 12 11:41:25 2018
New Revision: 1848745

URL: http://svn.apache.org/viewvc?rev=1848745&view=rev
Log:
Improved: Convert Minilang to Groovy Guide into asciidoc
(OFBIZ-10300)

Convert Dennis Balkirs Minilang to Groovy Guide into AsciiDoc.

Thanks: Benjamin Jugl

Added:
    ofbiz/ofbiz-framework/trunk/framework/minilang/docs/
    ofbiz/ofbiz-framework/trunk/framework/minilang/docs/asciidoc/
    ofbiz/ofbiz-framework/trunk/framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc   (with props)
Modified:
    ofbiz/ofbiz-framework/trunk/docs/asciidoc/developer-manual.adoc

Modified: ofbiz/ofbiz-framework/trunk/docs/asciidoc/developer-manual.adoc
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/docs/asciidoc/developer-manual.adoc?rev=1848745&r1=1848744&r2=1848745&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/docs/asciidoc/developer-manual.adoc (original)
+++ ofbiz/ofbiz-framework/trunk/docs/asciidoc/developer-manual.adoc Wed Dec 12 11:41:25 2018
@@ -272,3 +272,5 @@ include::../../framework/webapp/src/docs
 include::../../framework/security/src/docs/asciidoc/security.adoc[leveloffset=+1]
 
 == Appendices
+
+include::../../framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc[leveloffset=+1]

Added: ofbiz/ofbiz-framework/trunk/framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc?rev=1848745&view=auto
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc (added)
+++ ofbiz/ofbiz-framework/trunk/framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc Wed Dec 12 11:41:25 2018
@@ -0,0 +1,858 @@
+////
+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.
+////
+= From Mini Language to Groovy
+:author: Dennis Balkir
+
+This is a small guide for everybody involved in converting the Mini Language into Groovy.
+
+NOTE: *Why is this important?* +
+This tutorial is directly linked to the efforts of converting all scripts in Mini Language to newer Groovy Scripts.
+All of this is done, because Groovy is much more readable and easier to review, more up to date and many other reasons, which can be found here: https://lists.apache.org/thread.html/253b41060a295b8ab68bc78763cc129fc74b712cf776f8716022097f@%3Cdev.ofbiz.apache.org%3E[Proposal for deprecating Mini Language] +
+ +
+To contribute, or just be up to date with the current process, you can look at the existing https://issues.apache.org/jira/browse/OFBIZ-9350[JIRA issue OFBIZ-9350 - Deprecate Mini Lang]
+
+== Groovy DSL (dynamic scripting library)
+
+=== How to get Groovy support in your IDE
+*The following paragraph is for Eclipse users.*
+
+It is possible to get Groovy support in Eclipse by converting the loaded project to a Groovy Project. The project itself will work as before.
+
+To do this just follow these few steps:
+
+. Right-click on the project that has to be converted
+. Click on "Configure"
+. Click on "Convert to Groovy Project"
+
+Eclipse will automatically load the file OfbizDslDescriptorForEclipse.dsld , in which the known fields and methods used in Groovy Scripts are defined.
+
+=== Known Fields
+`*property name: 'parameters' +
+type : 'java.util.Map'*` +
+These are the parameters given to the Groovy Script, when it is called as a service. It is equivalent to `Map<String, Object>` context in the Java-Service-Definition. +
+ +
+`*property name: 'context' +
+type: 'java.util.Map'*` +
+More parameters, which are, for example, given through a screen or another Groovy Script. This is important when the script is called through an action segment of a screen. +
+ +
+`*property name: 'delegator' +
+type: 'org.apache.ofbiz.entity.Delegator'*` +
+Normal instance of the Delegator, which is used for special database access. +
+ +
+`*property name: 'dispatcher' +
+type: 'org.apache.ofbiz.service.LocalDispatcher'*` +
+Normal instance of the LocalDispatcher, which is used to call services and other service-like operations. +
+ +
+`*property name: 'security' +
+type: 'org.apache.ofbiz.security.Security'*` +
+Normal instance of the Security-Interface with which permission checks are done.
+
+== Known Methods
+`*method name: 'runService' +
+type: 'java.util.Map' +
+params: [serviceName: 'String', inputMap: 'java.util.Map']*` +
+Helping method to call services instead of dispatcher.runSync(serviceName, inputMap). Also possible: run service: serviceName, with: inputMap +
+ +
+`*method name: 'makeValue' +
+type: 'java.util.Map' +
+params: [entityName: 'String']*` +
+Helping method to make a GenericValue instead of delegator.makeValue(entityName). Creates an empty GenericValue of the specific entity. +
+ +
+`*method name: 'findOne' +
+type: 'java.util.Map' +
+params: [entityName: 'String', inputMap: 'java.util.Map']*` +
+Helping method to find one GenericValue in the database. Used instead of delegator.findOne(entityName, inputMap) +
+ +
+`*method name: 'findList' +
+type: 'java.util.List' +
+params: [entityName: 'String', inputMap: 'java.util.Map']*` +
+Helping method to find many GenericValue in the database. Used instead of delegator.findList(entityName, inputMap, null, null, null, false) +
+ +
+`*method name: 'select' +
+type: 'org.apache.ofbiz.entity.util.EntityQuery' +
+params: [entity: 'java.util.Set']*` +
+Helping method used instead of EntityQuery.use(delegator).select(...) +
+ +
+`*method name: 'select', type: 'org.apache.ofbiz.entity.util.EntityQuery', params: [entity: 'String...']*` +
+As above. +
+ +
+`*method name: 'from' +
+type: 'org.apache.ofbiz.entity.util.EntityQuery' +
+params: [entity: 'java.lang.Object']*` +
+Helping method used instead of EntityQuery.use(delegator).from(...) +
+ +
+`*method name: 'success' +
+type: 'def' +
+params: [message: 'String']*` +
+Helping method used instead of ServiceUtil.returnSuccess(message) +
+ +
+`*method name: 'failure' +
+type: 'java.util.Map' +
+params: [message: 'String']*` +
+Helping method used instead of ServiceUtil.returnFailure(message) +
+ +
+`*method name: 'error' +
+type: 'def' +
+params: [message: 'String']*` +
+Helping method used instead of ServiceUtil.returnError(message) +
+ +
+`*method name: 'logInfo' +
+type: 'void' +
+params: [message: 'String']*` +
+Helping method used instead of Debug.logInfo(message, fileName) +
+ +
+`*method name: 'logWarning' +
+type: 'void' +
+params: [message: 'String']*` +
+Helping method used instead of Debug.logWarning(message, fileName) +
+ +
+`*method name: 'logError' +
+type: 'void' +
+params: [message: 'String']*` +
+Helping method used instead of Debug.logError(message, fileName) +
+ +
+`*method name: 'logVerbose' +
+type: 'void' +
+params: [message: 'String']*` +
+Helping method used instead of Debug.logVerbose(message, fileName) +
+
+The actual definition of the methods can be found in ``/framework/service/src/main/java/org/apache/ofbiz/service/engine/GroovyBaseScript.groovy`,
+the variables `dctx`, `dispatcher` and `delegator` are set in the file `GroovyEngine.java` which can be found in the same location.
+
+== Services
+=== From MiniLang to Groovy
+
+To see additional examples and finished conversions, which may help with occurring questions, click: https://issues.apache.org/jira/browse/OFBIZ-9350[OFBIZ-9350 - Deprecate Mini Lang]
+There is a chance that a similar case has already been converted.
+
+IMPORTANT: When a simple-method ends, it will automatically at least return a success-map.
+
+All the Groovy Services have to return success at least, too.
+[source,java]
+--
+return success()
+--
+
+=== Getting started
+
+MiniLang files consist of services, which, in most cases, implement services.
+
+The get converted to Groovy like the following:
+[source,xml]
+--
+<!-- This is MiniLang -->
+<simple-method method-name="createProductCategory" short-description="Create an ProductCategory">
+   <!-- Code -->
+</simple-method>
+--
+[source,groovy]
+--
+// This is the converted Groovy equivalent
+/**
+ * Create an ProductCategory
+ */
+def createProductCategory() {
+    // Code
+}
+--
+It will be useful for future developers, and everybody who has to check something in the code, to put at least the short-description as the new Groovydoc. This will hopefully more or less explain, what the method should or shouldn't do.
+If the short-description isn't helpful enough, feel free  complete it.
+
+The structure of if and else in MiniLang is a little different than the one from Groovy or Java and can be a bit confusing when first seen, so here is an example:
+[source,xml]
+--
+<if-empty field="parameters.productCategoryId">
+    <sequenced-id sequence-name="ProductCategory" field="newEntity.productCategoryId"/>
+<else>
+    <set field="newEntity.productCategoryId" from-field="parameters.productCategoryId"/>
+    <check-id field="newEntity.productCategoryId"/>
+    <check-errors/>
+</else>
+</if-empty>
+--
+NOTE: Notice, that the else always starts before the if-tag is closed, but sometimes isn't indented as one would expect it.
+
+When navigating through bigger `if`-phrases, the navigation itself will be much easier through just clicking in the opening or closing `if`-tag; Eclipse will automatically mark the matching opening or closing `if`-tag for you.
+
+There are two possibilities to initialize a field/variable in Groovy.
+
+. To define a field/variable with its correct typing +
+`String fieldName = "value"``
+. To just "define" a field/variable. The IDE you are working with may not recognize the typing, but OFBiz can work with it: +
+`def fieldName = "value"`
+
+== Checking Fields
+|===
+h| Minilang h| Groovy
+
+a|[source,xml]
+--
+<if-empty field="fieldName"></if-empty>
+--
+a|[source,groovy]
+--
+ //checks if fieldName is existent and/or empty
+if (!fieldName) {}
+--
+
+a|[source,xml]
+--
+<if-empty field="fieldName.property"></if-empty>
+--
+a|[source,groovy]
+--
+ // fieldName has to be existent, property doesn't need to
+ // if known, that property does exist, the ? can be left out
+if (!fieldName?.property) {}
+ // CAUTION: every query like this in Groovy evaluates to a Boolean type
+ // everything that is empty or false will turn into false:
+ // null, [], [:], "", false -> false
+
+// if you want to check if the field really is empty
+if (UtilValidate.isEmpty(fieldName)) {}
+--
+
+a|[source,xml]
+--
+<if>
+    <condition>
+        <or>
+            <if-empty field="field1"/>
+            <if-empty field="field2"/>
+        </or>
+    </condition>
+    <then>
+        <!-- code in if -->
+    </then>
+    <else>
+        <!-- code in else -->
+    </else>
+</if>
+--
+a|[source,groovy]
+--
+if (!field1 \|\| !field2) {
+ // code in if
+} else {
+ // code in else
+}
+--
+
+a|[source,xml]
+--
+<if-compare-field field="product.primaryProductCategoryId" to-field="parameters.productCategoryId" operator="equals">
+    <!-- code -->
+</if-compare-field>
+--
+a|[source,groovy]
+--
+ // this will even work, if product is not existent or null
+if (UtilValidate.areEqual(product?.primaryProductCategoryId, parameters.productCategoryId)) {
+    // code
+}
+--
+
+a|[source,xml]
+--
+<if-instance-of field="parameters.categories" class="java.util.List"></if-instance-of>
+--
+a|[source,groovy]
+--
+if (parameters.categories instanceof java.util.List) {}
+--
+|===
+== Setting Fields
+|===
+h| Minilang h| Groovy
+
+a|[source,xml]
+--
+<set field="fieldName" value="value"/>
+--
+a|[source,groovy]
+--
+ // if fieldName is not initialized
+String fieldName = "value"
+ // if fieldName is initialized
+fieldName = "value"
+--
+
+a|[source,xml]
+--
+<set field="otherFieldName.property" value="value"/>
+<set field="otherFieldName.otherProperty" value="true" type="Boolean"/>
+<set field="otherFieldName.otherProperty" from-field="parameters.property/>
+--
+a|[source,groovy]
+--
+ // if otherFieldName is not yet initialized, you have to do it first
+ // MiniLang does that automatically
+Map otherFieldName = [:] // empty Map
+ // now put the values in
+otherFieldName = [
+    property: "value",
+    otherProperty: true
+]
+ // or the less efficient way
+otherFieldName.property = "value"
+otherFieldName.otherProperty = true
+
+ // it is possible to put different values in later:
+otherFieldName.property = parameters.property
+--
+
+a|[source,xml]
+--
+<set field="thisFieldName" value="${groovy: []}" type="List"/>
+--
+a|[source,groovy]
+--
+ // this is easier in Groovy
+List thisFieldName = []
+--
+
+a|[source,xml]
+--
+<property-to-field resource="CommonUiLabels" property="CommonGenericPermissionError" field="failMessage"/>
+<!-- there are different cases of this, which are not distinguished in MiniLang -->
+<property-to-field resource="general.properties" property="currency.uom.id.default" field="parameters.rateCurrencyUomId"/>
+--
+a|[source,groovy]
+--
+String failMessage = UtilProperties.getMessage("CommonUiLabels", "CommonGenericPermissionError", parameters.locale)
+ // in Groovy there can be a difference for the second case
+parameters.rateCurrencyUomId = UtilProperties.getPropertyValue('general.properties', 'currency.uom.id.default')
+--
+
+a|[source,xml]
+--
+<clear-field field="product.primaryProductCategoryId"/>
+--
+a|[source,groovy]
+--
+product.primaryProductCategoryId = null
+--
+|===
+
+== Starting Services
+|===
+h| Minilang h| Groovy
+
+a|[source,xml]
+--
+<set field="relatedCategoryContext.parentProductCategoryId"  from-field="defaultTopCategoryId"/>
+<call-service service-name="getRelatedCategories" in-map-name="relatedCategoryContext">
+    <result-to-field result-name="categories" field="resCategories"/>
+</call-service>
+--
+a|[source,groovy]
+--
+def relatedCategoryContext = [parentProductCategoryId: defaultTopCategoryId]
+def serviceResult = run service: "getRelatedCategoryies", with: relatedCategoryContext
+def resCategories = serviceResult.categories
+ // if it is not too confusing to read you can leave out the extra variable
+run service: "getRelatedCategoryies", with: [parentProductCategoryId: defaultTopCategoryId]
+--
+
+a|[source,xml]
+--
+<set-service-fields service-name="productCategoryGenericPermission" map="parameters" to-map="productCategoryGenericPermissionMap"/>
+<call-service service-name="productCategoryGenericPermission" in-map-name="productCategoryGenericPermissionMap">
+    <results-to-map map-name="genericResult"/>
+</call-service>
+--
+a|[source,groovy]
+--
+ // instead of setting the service fields from parameters, it is possible to run the service with the parameters map
+Map genericResult = run service: "productCategoryGenericPermission", with: parameters
+--
+|===
+== Preparing Service Results
+|===
+h| Minilang h| Groovy
+
+a|[source,xml]
+--
+<field-to-result field="fieldBudgetId" result-name="budgetId"/>
+--
+a|[source,groovy]
+--
+ // MiniLang knows this implicitly
+def result = success()
+result.budgetId = fieldBudgetId
+return result
+--
+|===
+== Database Communication
+|===
+h| Minilang h| Groovy
+
+a|[source,xml]
+--
+<make-value entity-name="FinAccountTrans" value-field="newEntity"/>
+<set-nonpk-fields map="parameters" value-field="newEntity"/>
+<set-pk-fields map="parameters" value-field="newEntity"/>
+--
+a|[source,groovy]
+--
+ // this is the easy way
+GenericValue newEntity = makeValue("FinAccountTrans", parameters)
+ // this is also possible
+GenericValue newEntity = makeValue("FinAccountTrans")
+newEntity.setPKFields(parameters)
+newEntity.setNonPKFields(parameters)
+--
+
+a|[source,xml]
+--
+<entity-and entity-name="BudgetStatus" list="budgetStatuses">
+    <field-map field-name="budgetId" from-field="parameters.budgetId"/>
+    <order-by field-name="-statusDate"/>
+</entity-and>
+--
+a|[source,groovy]
+--
+ // this can also be done in one line, but it can easily become unreadable
+def budgetStatuses = from("BudgetStatus")
+    .where("budgetId", paramters.budgetId)
+    .orderBy("-statusDate")
+    .queryList()
+--
+
+a|[source,xml]
+--
+<entity-one entity-name="StatusValidChange" value-field="statusValidChange">
+    <field-map field-name="statusId" from-field="budgetStatus.statusId"/>
+    <field-map field-name="statusIdTo" from-field="parameters.statusId"/>
+</entity-one>
+<!-- entity-one can be called without child elements, too -->
+<entity-one entity-name="Product" value-field="product" auto-field-map="true"/>
+--
+a|[source,groovy]
+--
+ // MiniLang has false set for useCache as the default value
+statusValidChange = findOne("StatusValidChange", [statusId: budgetStatus.statusId, statusIdTo: parameters.statusId], false)
+ // this is also possible
+statusValidChange = from("StatusValidChange")
+    .where("statusId", budgetStatus.statusId, "statusIdTo", parameters.statusId)
+    .queryOne()
+ // if there are no child elements, this can be used
+GenericValue product = from("Product").where(parameters).queryOne()
+--
+
+a|[source,xml]
+--
+<find-by-primary-key entity-name="ProductCategoryMember" map="lookupPKMap" value-field="lookedUpValue"/>
+--
+a|[source,groovy]
+--
+GenericValue lookedUpValue = findOne("ProductCategoryMember", lookupPKMap, false)
+ // this is also possible
+lookedUpValue = from("ProductCategoryRole")
+    .where(lookupPKMap)
+    .queryOne()
+--
+
+a|[source,xml]
+--
+<entity-condition entity-name="ProductCategoryContentAndInfo" list="productCategoryContentAndInfoList" filter-by-date="true" use-cache="true">
+    <condition-list combine="and">
+        <condition-expr field-name="productCategoryId" from-field="productCategoryList.productCategoryId"/>
+        <condition-expr field-name="prodCatContentTypeId" value="ALTERNATIVE_URL"/>
+    </condition-list>
+    <order-by field-name="-fromDate"/>
+</entity-condition>
+<!-- entity-condition can also be used with the "or" operator -->
+<entity-condition entity-name="ProdCatalogCategory" list="prodCatalogCategoryList" filter-by-date="true">
+    <condition-list combine="and">
+        <condition-expr field-name="productCategoryId" from-field="parameters.productCategoryId"/>
+        <condition-list combine="or">
+            <condition-expr field-name="prodCatalogCategoryTypeId" value="PCCT_VIEW_ALLW"/>
+            <condition-expr field-name="prodCatalogCategoryTypeId" value="PCCT_PURCH_ALLW"/>
+        </condition-list>
+    </condition-list>
+</entity-condition>
+a|[source,groovy]
+--
+ // the Groovy methods use the "and" and "equals" operator as default values
+List productCategoryContentAndInfoList = from("ProductCategoryContentAndInfo")
+    .where("productCategoryId", productCategoryList.productCategoryId, "prodCatContentTypeId", "ALTERNATIVE_URL")
+    .cache().orderBy("-fromDate")
+    .filterByDate()
+    .queryList()
+ // with the use of the "or" operator you have to build your condition like this
+EntityCondition condition = EntityCondition.makeCondition([
+    EntityCondition.makeCondition([
+        EntityCondition.makeCondition("prodCatalogCategoryTypeId", "PCCT_VIEW_ALLW"),
+        EntityCondition.makeCondition("prodCatalogCategoryTypeId", "PCCT_PURCH_ALLW")
+    ], EntityOperator.OR),
+    EntityCondition.makeCondition("productCategoryId", parameters.productCategoryId)
+])
+List prodCatalogCategoryList = from("ProdCatalogCategory").where(condition).filterByDate().queryList()
+--
+
+a|[source,xml]
+--
+<make-value entity-name="FinAccountTrans" value-field="newEntity"/>
+<set-nonpk-fields map="parameters" value-field="newEntity"/>
+<!-- In this case multiple fields of the GenericValue are set -->
+<make-value entity-name="ProductCategoryRollup" value-field="newLimitRollup"/>
+<set field="newLimitRollup.productCategoryId" from-field="newEntity.productCategoryId"/>
+<set field="newLimitRollup.parentProductCategoryId" from-field="productCategoryRole.productCategoryId"/>
+<set field="newLimitRollup.fromDate" from-field="nowTimestamp"/>
+a|[source,groovy]
+--
+def newEntity = makeValue("FinAccountTrans", parameters)
+ // you can set multiple fields of a GenericValue like this
+def newLimitRollup = makeValue("ProductCategoryRollup", [
+    productCategoryId: newEntity.productCategoryId,
+    parentProductCategoryId: productCategoryRole.productCategoryId,
+    fromDate: nowTimestamp
+])
+--
+
+a|[source,xml]
+--
+<set field="statusValidChange.prop" value="value"/>
+--
+a|[source,groovy]
+--
+statusValidChange.prop = "value"
+--
+
+a|[source,xml]
+--
+<create-value value-field="newEntity"/>
+--
+a|[source,groovy]
+--
+newEntity.create()
+--
+
+a|[source,xml]
+--
+<store-value value-field="newEntity"/>
+<store-list list="listToStore"/>
+--
+a|[source,groovy]
+--
+newEntity.store()
+delegator.storeAll(listToStore)
+--
+
+a|[source,xml]
+--
+<clone-value value-field="productCategoryMember" new-value-field="newProductCategoryMember"/>
+--
+a|[source,groovy]
+--
+def newProductCategoryMember = productCategoryMember.clone()
+--
+
+a|[source,xml]
+--
+<remove-value value-field="lookedUpValue"/>
+--
+a|[source,groovy]
+--
+lookedUpValue.remove()
+--
+
+a|[source,xml]
+--
+<sequenced-id sequence-name="ProductCategory" field="newEntity.productCategoryId"/>
+--
+a|[source,groovy]
+--
+newEntity.productCategoryId = delegator.getNextSeqId("ProductCategory")
+--
+
+a|[source,xml]
+--
+<check-id field="newEntity.productCategoryId"/>
+--
+a|[source,groovy]
+--
+UtilValidate.checkValidDatabaseId(newEntity.productCategoryId)
+--
+
+a|[source,xml]
+--
+<make-next-seq-id value-field="newEntity" seq-field-name="linkSeqId"/>
+--
+a|[source,groovy]
+--
+delegator.setNextSubSeqId(newEntity, "linkSeqId", 5, 1)
+ // the numbers 5 and 1 are used in the Java implementation of the MiniLang method
+ // and can also be found as the default values in the MiniLang documentation
+--
+|===
+
+== Permissions
+
+CAUTION: To also check for admin-permissions, this method has to be used: +
+`*hasEntityPermission(permission, action, userLogin)*`
+
+
+If the method is used with wildcards, it is important to [underline]#not forget the underscore#, which comes before the parameter action!
+|===
+h| Minilang h| Groovy
+
+a|[source,xml]
+--
+<check-permission permission="CATALOG" action="_CREATE">
+    <alt-permission permission="CATALOG_ROLE" action="_CREATE"/>
+    <fail-property resource="ProductUiLabels" property="ProductCatalogCreatePermissionError"/>
+</check-permission>
+<check-errors/>
+--
+a|[source,groovy]
+--
+if (!(security.hasEntityPermission("CATALOG", "_CREATE", parameters.userLogin)
+    \|\| security.hasEntityPermission("CATALOG_ROLE", "_CREATE", parameters.userLogin))) {
+    return error(UtilProperties.getMessage("ProductUiLabels", "ProductCatalogCreatePermissionError", parameters.locale))
+}
+--
+
+a|[source,xml]
+--
+<set field="hasCreatePermission" value="false" type="Boolean"/>
+<if-has-permission permission="${primaryPermission}" action="${mainAction}">
+    <set field="hasCreatePermission" value="true" type="Boolean"/>
+</if-has-permission>
+--
+a|[source,groovy]
+--
+ // this will automatically be set to false if the user doesn't have the permission
+def hasCreatePermission = security.hasEntityPermission(primaryPermission, "_${mainAction}", parameters.userLogin)
+--
+|===
+== Timestamp And System Time
+The first two simple-method are deprecated; the third method should have been used instead.
+|===
+h| Minilang h| Groovy
+
+a|[source,xml]
+--
+<now-timestamp field="nowTimestamp"/>
+--
+a|[source,groovy]
+--
+Timestamp nowTimestamp = UtilDateTime.nowTimestamp()
+--
+
+a|[source,xml]
+--
+<now-date-to-env field="nowDate"/>
+--
+a|[source,groovy]
+--
+Timestamp nowDate = UtilDateTime.nowTimestamp()
+--
+
+a|[source,xml]
+--
+<!-- this method also has the parameter "type", which is set to 'java.sql.timestamp' as default -->
+<now field="fooNow"/>
+--
+a|[source,groovy]
+--
+Timestamp fooNow = UtilDateTime.nowTimestamp()
+--
+
+a|[source,xml]
+--
+<if-compare-field field="productCategoryMember.thruDate" to-field="expireTimestamp" operator="less" type="Timestamp">
+    <!-- code -->
+</if-compare-field>
+--
+a|[source,groovy]
+--
+Timestamp thruDate = productCategoryMember.thruDate
+if (thruDate && thruDate.before(expireTimestamp)) {
+    // code
+}
+--
+|===
+
+== Logging
+
+Since all of the log methods are know to the Groovy Language, it is possible to just nearly use them as they are in MiniLang. +
+For further explanation, here are some examples:
+|===
+h| Minilang h| Groovy
+
+a|[source,xml]
+--
+<log level="verbose" message="Permission check failed, user does not have permission"/>
+--
+a|[source,groovy]
+--
+logVerbose("Permission check failed, user does not have the correct permission.")
+--
+
+a|[source,xml]
+--
+<log level="info" message="Applying feature [${productFeatureId}] of type [${productFeatureTypeId}] to product [${productId}]"/>
+--
+a|[source,groovy]
+--
+logInfo("Applying feature [${productFeatureId}] of type [${productFeatureTypeId}] to product [${productId}]")
+--
+|===
+== General
+|===
+h| Minilang h| Groovy
+
+a|[source,xml]
+--
+<call-simple-method method-name="checkCategoryRelatedPermission"/>
+<check-errors/>
+--
+a|[source,groovy]
+--
+ // simple-methods inside of classes, as long as they are not services, will be called like normal methods
+Map res = checkCategoryRelatedPermission("updateProductCategory", "UPDATE", null, null)
+if (!ServiceUtil.isSuccess(res)) {
+    return res
+}
+--
+
+a|[source,xml]
+--
+<iterate list="subCategories" entry="subCategory">
+    <!-- code -->
+</iterate>
+--
+a|[source,groovy]
+--
+for (def subCategory : subCategories) {
+    // code
+}
+// this is also possible (CAUTION: Eclipse sometimes doesn't know, that it already knows methods inside of closures)
+subCategories.each { subCategory ->
+    // code
+}
+--
+
+a|[source,xml]
+--
+<iterate-map map="parameters.productFeatureIdByType" key="productFeatureTypeId" value="productFeatureId">
+    <!-- in here something should happen with value and key -->
+</iterate-map>
+--
+a|[source,groovy]
+--
+for (Map entry : parameters.productFeatureIdByType.entrySet()) {
+    def productFeatureTypeId = entry.getKey()
+    def productFeatureId = entry.getValue()
+    // in here something should happen with value and key
+}
+--
+
+a|[source,xml]
+--
+<if>
+    <condition>
+        <not>
+            <or>
+                <if-has-permission permission="CATALOG" action="_${checkAction}"/>
+                <and>
+                    <if-has-permission permission="CATALOG_ROLE" action="_${checkAction}"/>
+                    <not><if-empty field="roleCategories"/></not>
+                </and>
+            </or>
+        </not>
+    </condition>
+    <then>
+        <!-- code -->
+    </then>
+</if>
+--
+a|[source,groovy]
+--
+if (!security.hasEntityPermission("CATALOG", "_${checkAction}", parameters.userLogin)
+    && !(security.hasEntityPermission("CATALOG_ROLE", "_${checkAction}", parameters.userLogin)
+    && roleCategories)) {
+    // code
+}
+--
+
+a|[source,xml]
+--
+<set field="validDate" from-field="parameters.validDate"/>
+<if-not-empty field="validDate">
+    <filter-list-by-date list="productCategoryMembers" valid-date="validDate"/>
+</if-not-empty>
+--
+a|[source,groovy]
+--
+def query = from("ProductCategoryMember").where("productCategoryId", parameters.productCategoryId)
+if (parameters.validDate) {
+    query.filterByDate()
+}
+List productCategoryMembers = query.queryList()
+--
+
+a|[source,xml]
+--
+<order-map-list list="productsList">
+    <order-by field-name="sequenceNum"/>
+</order-map-list>
+--
+a|[source,groovy]
+--
+productsList = EntityUtil.orderBy(productsList, ["sequenceNum"])
+--
+|===
+
+== Where to find MiniLang implementation
+If you find yourself in a position, where you don't know how to convert a certain tag from MiniLang to Groovy, you can always check the Java implementation of the MiniLang method. +
+All of the methods have an existing Java implementation and you can find all of them in this folder: `/ofbiz/trunk/framework/minilang/src/main/java/org/apache/ofbiz/minilang/method` +
+
+The interesting part of this implementation is the method `exec()`, which actually runs the MiniLang tag. +
+The tag `<remove-by-and>` for example is realized using this part of code here:
+[source,java]
+--
+@Override
+
+public boolean exec(MethodContext methodContext) throws MiniLangException {
+    @Deprecated
+    String entityName = entityNameFse.expandString(methodContext.getEnvMap());
+    if (entityName.isEmpty()) {
+        throw new MiniLangRuntimeException("Entity name not found.", this);
+    }
+    try {
+        Delegator delegator = getDelegator(methodContext);
+        delegator.removeByAnd(entityName, mapFma.get(methodContext.getEnvMap()));
+    } catch (GenericEntityException e) {
+        String errMsg = "Exception thrown while removing entities: " + e.getMessage();
+        Debug.logWarning(e, errMsg, module);
+        simpleMethod.addErrorMessage(methodContext, errMsg);
+        return false;
+    }
+    return true;
+}
+--
+In this you can find one important part of code, which is:
+[source,java]
+--
+delegator.removeByAnd(entityName, mapFma.get(methodContext.getEnvMap()));
+--
+This tells you, that, if you're trying to convert the tag `<remove-by-and>`, you can use `delegator.removeByAnd()` in Groovy.

Propchange: ofbiz/ofbiz-framework/trunk/framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/ofbiz-framework/trunk/framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/ofbiz-framework/trunk/framework/minilang/docs/asciidoc/minilang-to-groovy-manual.adoc
------------------------------------------------------------------------------
    svn:mime-type = text/plain