Improved: Convert createMissingCategoryContentAltUrlInline service from mini-lang to groovy DSL (OFBIZ-11387)

Thanks to Devanshu Vyas for the patch
diff --git a/applications/content/groovyScripts/content/ContentServices.groovy b/applications/content/groovyScripts/content/ContentServices.groovy
index 9c3ab04..9fa1c46 100644
--- a/applications/content/groovyScripts/content/ContentServices.groovy
+++ b/applications/content/groovyScripts/content/ContentServices.groovy
@@ -533,3 +533,108 @@
     }
     return result
 }
+
+/**
+ * create missing content alternative urls.
+ */
+Map createMissingContentAltUrls() {
+    List contentCreatedList = []
+    if (parameters.prodCatalogId) {
+        List productCategorieIds = []
+        // Get all categories
+        from('ProdCatalogCategory')
+                .where(prodCatalogId: parameters.prodCatalogId)
+                .getFieldList('productCategoryId')
+                .each { String productCategoryId ->
+                    productCategorieIds.addAll(createMissingCategoryContentAltUrlInline(productCategoryId))
+                }
+        // Create Content Alternative URLs for Product Category Content
+        EntityCondition condition = new EntityConditionBuilder().AND {
+            NOT_EQUAL(prodCatContentTypeId: 'ALTERNATIVE_URL')
+            IN(productCategoryId: productCategorieIds)
+        }
+        from('ProductCategoryContentAndInfo')
+                .where(condition)
+                .filterByDate()
+                .orderBy('-fromDate')
+                .getFieldList('contentId')
+                .each { String contentId ->
+                    Map serviceResult = run service: 'createContentAlternativeUrl', with: [contentId: contentId]
+                    contentCreatedList << serviceResult.contentCreated
+                }
+
+        //Create Content Alternative URLs for Product Content
+        condition = new EntityConditionBuilder().AND {
+            IN(productCategoryId: productCategorieIds)
+        }
+        List productIds = from('ProductCategoryMember')
+                .where(condition)
+                .filterByDate()
+                .orderBy('-fromDate')
+                .getFieldList('productId')
+
+        condition = new EntityConditionBuilder().AND {
+            NOT_EQUAL(productContentTypeId: 'ALTERNATIVE_URL')
+            IN(productId: productIds)
+        }
+        from('ProductContentAndInfo')
+                .where(condition)
+                .filterByDate()
+                .orderBy('-fromDate')
+                .getFieldList('contentId')
+                .each { String contentId ->
+                    Map serviceResult = run service: 'createContentAlternativeUrl', with: [contentId: contentId]
+                    contentCreatedList << serviceResult.contentCreated
+                }
+    }
+
+    //Create Content Alternative URLs for WebSite Content and children
+    if (parameters.webSiteId) {
+        from('WebSiteContent')
+                .where(webSiteId: parameters.webSiteId)
+                .getFieldList('contentId')
+                .each { String contentId ->
+                    contentCreatedList.addAll(createMissingContentAltUrlInline(contentId))
+                }
+    }
+    long totalParse = contentCreatedList.size()
+    long contentsUpdated = contentCreatedList.stream().filter {it == 'Y'}.count()
+    return success([contentsUpdated: contentsUpdated, contentsNotUpdated: totalParse - contentsUpdated])
+}
+
+/**
+ * recursive call to get all productCategoryId from children
+ * @param parentProductCategoryId
+ * @return
+ */
+List createMissingCategoryContentAltUrlInline(String parentProductCategoryId) {
+    List productCategoryIds = []
+    from('ProductCategoryRollup')
+            .where(parentProductCategoryId: parentProductCategoryId)
+            .filterByDate()
+            .getFieldList('productCategoryId')
+            .each { String productCategoryId ->
+                productCategoryIds << productCategoryId
+                productCategoryIds.addAll(createMissingCategoryContentAltUrlInline(productCategoryId))
+            }
+    return productCategoryIds
+}
+
+/**
+ * call createContentAlternativeUrl on all subContent and return the result for analyse
+ * @param contentId
+ * @return
+ */
+List createMissingContentAltUrlInline(String contentId) {
+    List contentCreatedList = []
+    from('ContentAssoc')
+            .where(contentId: contentId)
+            .filterByDate()
+            .getFieldList('contentIdTo')
+            .each { String contentIdTo ->
+                Map serviceResult = run service: 'createContentAlternativeUrl', with: [contentId: contentIdTo]
+                contentCreatedList << serviceResult.contentCreated
+                contentCreatedList.addAll(createMissingContentAltUrlInline(contentIdTo))
+            }
+    return contentCreatedList
+}
\ No newline at end of file
diff --git a/applications/content/servicedef/services_content.xml b/applications/content/servicedef/services_content.xml
index 0935031..23dbfbf 100644
--- a/applications/content/servicedef/services_content.xml
+++ b/applications/content/servicedef/services_content.xml
@@ -349,8 +349,8 @@
     </service>
 
     <!-- Alternative URLs services -->
-    <service name="createMissingContentAltUrls" engine="simple"
-                location="component://content/minilang/content/ContentServices.xml" invoke="createMissingContentAltUrls" auth="true" use-transaction="false">
+    <service name="createMissingContentAltUrls" engine="groovy" auth="true" use-transaction="false"
+                location="component://content/groovyScripts/content/ContentServices.groovy" invoke="createMissingContentAltUrls">
         <description>Create missing Content Alternative URLs</description>
         <attribute name="prodCatalogId" mode="IN" type="String" optional="true"/>
         <attribute name="webSiteId" mode="IN" type="String" optional="true"/>