diff --git a/TODO.txt b/TODO.txt index 702bfcf3f..99dfffbdb 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,7 +1,14 @@ # List of minor Todos +?? Default Translation Configuration in AEM? + https://wcm.io/caconfig/editor/usage.html translation config drop down +Generated with ... in conf pages +http://localhost:4502/editor.html/content/dam/wknd/en/magazine/arctic-surfing/aloha-spirits-in-northern-norway doesnt add page text or component text!!! + +- default icon for ai.composum site +!!! Dictation in Chat repeat in history bar. !! Empty prompt text !! http://localhost:5502/editor.html/content/xxx/com/en/about-us/going-forward.html "Explore the world..." RTE PDF and markdown as possible datatypes for the content creation dialog @@ -13,6 +20,8 @@ Why are the teasers in https://author-p43852-e197429.adobeaemcloud.com/editor.html/content/gfps/com/en/products-solutions/systems/primofit.html not included into the excerpt. +? Add history in content creation dialog, current component, last text, ... + ---------------- - Marker on page whenever a translation is in progress diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AITranslatePropertyWrapper.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AITranslatePropertyWrapper.java index 0a0e19289..262757507 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AITranslatePropertyWrapper.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AITranslatePropertyWrapper.java @@ -11,7 +11,7 @@ import org.apache.sling.api.resource.ModifiableValueMap; import org.apache.sling.api.resource.ValueMap; -class AITranslatePropertyWrapper { +public class AITranslatePropertyWrapper { /** * PageContent only property: saves the additional instructions the page was translated with. @@ -62,6 +62,12 @@ class AITranslatePropertyWrapper { */ public static final String AI_MANUAL_CHANGE_SUFFIX = "_manualChange"; + /** + * Attribute that is set on jcr:content of a page when the translation of a page failed, to make it easy to find such pages. Not set by {@link AITranslatePropertyWrapper}, but since all property names are defined here... + * Is set to the time at which the error occurred, to make it easy to find in the logs. + */ + public static final String AI_TRANSLATION_ERRORMARKER = "ai_translationError"; + private final ModifiableValueMap targetValueMap; private final String propertyName; private final ValueMap sourceValueMap; diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImpl.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImpl.java index 714d237c0..5a377e8ad 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImpl.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoPageTranslateServiceImpl.java @@ -1,5 +1,6 @@ package com.composum.ai.aem.core.impl.autotranslate; +import static com.composum.ai.aem.core.impl.autotranslate.AITranslatePropertyWrapper.AI_TRANSLATION_ERRORMARKER; import static com.composum.ai.backend.base.service.chat.impl.GPTTranslationServiceImpl.LASTID; import static com.composum.ai.backend.base.service.chat.impl.GPTTranslationServiceImpl.MULTITRANSLATION_SEPARATOR_END; import static com.composum.ai.backend.base.service.chat.impl.GPTTranslationServiceImpl.MULTITRANSLATION_SEPARATOR_START; @@ -131,9 +132,23 @@ public Stats translateLiveCopy(@Nonnull Resource resource, if (translationParameters.rules != null) { allRules.addAll(translationParameters.rules); } + if (autoTranslateCaConfig.rules() != null) { allRules.addAll(Arrays.asList(autoTranslateCaConfig.rules())); } + if (autoTranslateCaConfig.temperature() != null && !autoTranslateCaConfig.temperature().trim().isEmpty()) { + try { + double temperature = Double.parseDouble(autoTranslateCaConfig.temperature()); + configuration = GPTConfiguration.ofTemperature(temperature).merge(configuration); + } catch (NumberFormatException e) { + LOG.error("Invalid temperature value {} for path {}", autoTranslateCaConfig.temperature(), resource.getPath()); + } + } + if (autoTranslateCaConfig.preferHighIntelligenceModel()) { + configuration = GPTConfiguration.HIGH_INTELLIGENCE.merge(configuration); + } else if (autoTranslateCaConfig.preferStandardModel()) { + configuration = GPTConfiguration.STANDARD_INTELLIGENCE.merge(configuration); + } // collect translation rules that apply List allTranslateableProperties = new ArrayList<>(); @@ -364,9 +379,6 @@ protected String remapPaths(String translatedValue, String blueprintPath, String Pattern pattern = Pattern.compile("href=\"" + Pattern.quote(blueprintPath) + "(/[^\"]*)\""); String result = pattern.matcher(translatedValue).replaceAll("href=\"" + livecopyPath + "$1\""); - if (translatedValue.contains("href")) { // FIXME(hps,24/10/03) no checkin - LOG.trace("Remapping paths from {} to {} in {}", blueprintPath, livecopyPath, translatedValue); - } return result; } @@ -392,6 +404,7 @@ protected void markAsAiTranslated(Resource resource, LiveRelationship liveRelati liveRelationshipManager.cancelPropertyRelationship(resource.getResourceResolver(), liveRelationship, targetWrapper.allGeneralKeys(), false); } + valueMap.put(AI_TRANSLATION_ERRORMARKER, Boolean.FALSE); // reset error marker if there was one. } /** @@ -581,10 +594,6 @@ protected boolean collectPropertiesToTranslate( stats.translateableProperties++; AITranslatePropertyWrapper targetWrapper = new AITranslatePropertyWrapper(sourceValueMap, targetValueMap, key); - if (StringUtils.contains(targetWrapper.getOriginalCopy(), "href")) { // FIXME(hps,24/10/03) no checkin - LOG.trace("Skipping {} in {} because it contains href", key, resource.getPath()); - } - // we will translate except if the property is cancelled and we don't want to touch cancelled properties, // or if we have a current translation. boolean isCancelled = isCancelled(resource, key, relationship); @@ -621,6 +630,10 @@ protected boolean collectPropertiesToTranslate( propertyToTranslate.propertyName = key; propertyToTranslate.isAlreadyCorrectlyTranslated = isAlreadyCorrectlyTranslated; propertiesToTranslate.add(propertyToTranslate); + + if (targetWrapper.getOriginal().contains("THROWUPRIGHTNOW49e43jwsdsg")) { + throw new IllegalStateException("THROWUPRIGHTNOW49e43jwsdsg requested for " + sourceResource.getPath()); + } } for (Resource child : resource.getChildren()) { if (!PATTERN_IGNORED_SUBNODE_NAMES.matcher(child.getName()).matches()) { diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateCaConfig.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateCaConfig.java index 02f7096f1..96ae1fd7d 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateCaConfig.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateCaConfig.java @@ -9,8 +9,8 @@ // is also added to Sling-ContextAware-Configuration-Classes bnd header in pom.xml public @interface AutoTranslateCaConfig { - @Property(label = "Additional Instructions", order = 1, - description = "Additional instructions for the automatic translation.") + @Property(label = "Additional Instructions (Deprecated)", order = 1, + description = "Additional instructions for the automatic translation. Deprecated, please use 'Rules for additional Instructions' instead - if you do not give a path regex nor a content pattern the instructions will be used everywhere.") String additionalInstructions(); @Property(label = "Rules for additional Instructions", order = 2, @@ -57,4 +57,16 @@ }) String includeExistingTranslationsInRetranslation(); + @Property(label = "Optional Comment (for documentation, not used by AI)", order = 7, + description = "An optional comment about the configuration, for documentation purposes (not used by the translation).", + property = { + "widgetType=textarea", + "textareaRows=2" + }) + String comment(); + + @Property(label = "Temperature", order = 8, + description = "Optional temperature setting that determines variability and creativity as a floating point between 0.0 and 1.0") + String temperature(); + } diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateListModel.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateListModel.java index 44b1c5798..f23faeee6 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateListModel.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateListModel.java @@ -12,6 +12,7 @@ import org.apache.sling.api.resource.LoginException; import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.injectorspecific.OSGiService; import org.apache.sling.models.annotations.injectorspecific.Self; @@ -19,7 +20,10 @@ import org.slf4j.LoggerFactory; import com.composum.ai.backend.slingbase.AIConfigurationService; +import com.day.cq.wcm.api.Page; +import com.day.cq.wcm.api.PageManager; import com.day.cq.wcm.api.WCMException; +import com.day.cq.wcm.msm.api.LiveRelationshipManager; @Model(adaptables = SlingHttpServletRequest.class) public class AutoTranslateListModel { @@ -35,6 +39,9 @@ public class AutoTranslateListModel { @OSGiService private AutoTranslateConfigService autoTranslateConfigService; + @OSGiService + private LiveRelationshipManager liveRelationshipManager; + @Self private SlingHttpServletRequest request; @@ -55,7 +62,7 @@ public boolean inProgress() { return runs.stream().filter(run -> run.isInProgress()).findAny().isPresent(); } - public AutoTranslateService.TranslationRun createRun() throws LoginException, PersistenceException { + public AutoTranslateService.TranslationRun createRun() throws LoginException, PersistenceException, WCMException { if (run == null) { String path = request.getParameter("path"); if (path == null || path.isEmpty()) { @@ -64,6 +71,7 @@ public AutoTranslateService.TranslationRun createRun() throws LoginException, Pe path = path.replaceAll("_jcr_content", "jcr:content").replaceAll("\\.html$", "").trim(); boolean recursive = request.getParameter("recursive") != null; boolean changed = request.getParameter("translateWhenChanged") != null; + boolean copyOriginalPage = request.getParameter("copyOriginalPage") != null; String additionalInstructions = request.getParameter("additionalInstructions"); boolean debugaddinstructions = request.getParameter("debugaddinstructions") != null; if (debugaddinstructions) { @@ -93,11 +101,48 @@ public AutoTranslateService.TranslationRun createRun() throws LoginException, Pe parms.translateWhenChanged = changed; parms.additionalInstructions = additionalInstructions; parms.breakInheritance = breakInheritance; + if (copyOriginalPage) { + copyOriginalPage(request, path); + } run = autoTranslateService.startTranslation(request.getResourceResolver(), path, parms); } return run; } + /** + * If parameter copyOriginalPage is set, we create a copy of the original page with this suffix + * before doing the translation. + */ + public static final String SUFFIX_TRANSLATECOPY = "_aitranslate_bak"; + + /** + * Make a copy of the original page for comparison purposes. + */ + protected void copyOriginalPage(SlingHttpServletRequest request, String path) throws WCMException, PersistenceException { + ResourceResolver resolver = request.getResourceResolver(); + PageManager pageManager = resolver.adaptTo(PageManager.class); + Page originalPage = pageManager.getContainingPage(path); + path = originalPage.getPath(); + if (originalPage != null) { + String newPath = path + SUFFIX_TRANSLATECOPY; + if (resolver.getResource(newPath) != null) { + resolver.delete(resolver.getResource(newPath)); + } + Page copy = pageManager.copy(originalPage, newPath, null, true, true, false); + if (copy != null) { + liveRelationshipManager.endRelationship(copy.getContentResource(), true); + liveRelationshipManager.detach(copy.getContentResource(), true); // end doesn't seem to work + LOG.info("Created copy of {} at {}", originalPage.getPath(), newPath); + resolver.commit(); + } else { + LOG.error("Failed to create copy of {} at {}", originalPage.getPath(), newPath); + throw new IllegalArgumentException("Failed to create copy of " + originalPage.getPageTitle() + " at " + newPath); + } + } else { + throw new IllegalArgumentException("No page exists at " + path); + } + } + public String rollback() throws WCMException, PersistenceException { String path = request.getParameter("path"); try { diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateRuleConfig.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateRuleConfig.java index 32c965ed6..19204fba5 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateRuleConfig.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateRuleConfig.java @@ -10,7 +10,7 @@ public @interface AutoTranslateRuleConfig { @Property(label = "Path Regex", order = 1, - description = "A regular expression matching the absolute path to the page. " + + description = "A regular expression matching the absolute path to the page, incl. jcr:content. " + "E.g. .*/home/products/.* will match all pages under .../home/products/. If empty every page will match" + "if the content pattern condition is met.") String pathRegex(); @@ -30,4 +30,12 @@ }) String additionalInstructions(); + @Property(label = "Optional Comment (for documentation, not used by AI)", order = 4, + description = "An optional comment for the rule, for documentation purposes (not used by the translation).", + property = { + "widgetType=textarea", + "textareaRows=2" + }) + String comment(); + } diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateService.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateService.java index fe08813ef..74628c235 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateService.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateService.java @@ -159,6 +159,8 @@ abstract class TranslationPage { Pattern.compile("\\.(png|jpg|jpeg|gif|svg|mp3|mov|mp4)(/|$)", Pattern.CASE_INSENSITIVE); public String pagePath; + public String translateCopyPagePath; + public String status; public AutoPageTranslateService.Stats stats; @@ -172,6 +174,17 @@ public String editorUrl() { return "/editor.html" + pagePath + ".html"; } } + + /** If a translate copy is present, this would open a diff view. */ + public String diffToCopyUrl() { + if (startsWith(pagePath, "/content/dam") || translateCopyPagePath == null) { + return null; + } else { + return "/mnt/overlay/wcm/core/content/sites/diffresources.html" + pagePath + + "?item=" + translateCopyPagePath + "&sideBySide"; + } + } + } } diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateServiceImpl.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateServiceImpl.java index 2eb0fa4ed..7e8bd8dc0 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateServiceImpl.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/AutoTranslateServiceImpl.java @@ -1,6 +1,10 @@ package com.composum.ai.aem.core.impl.autotranslate; +import static com.composum.ai.aem.core.impl.autotranslate.AITranslatePropertyWrapper.AI_TRANSLATION_ERRORMARKER; +import static org.apache.commons.lang3.StringUtils.startsWith; + import java.util.ArrayList; +import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.List; @@ -10,6 +14,7 @@ import javax.annotation.Nonnull; import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.ModifiableValueMap; import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; @@ -131,9 +136,10 @@ public TranslationRun startTranslation( TranslationRunImpl run = new TranslationRunImpl(); run.id = "" + Math.abs(System.nanoTime()); run.rootPath = path; - run.translationParameters = translationParameters; + run.translationParameters = translationParameters.clone(); + run.translationParameters.autoSave = true; // otherwise it'll be just rolled back run.translatedPages = resources.stream() - .map(r -> new TranslationPageImpl(r.getPath())) + .map(r -> new TranslationPageImpl(r)) .collect(Collectors.toList()); run.waituntil = System.currentTimeMillis() + 1000; // when triggered during live copy creation. run.status = TranslationStatus.QUEUED; @@ -144,7 +150,7 @@ public TranslationRun startTranslation( } protected List collectPages(Resource root, int maxDepth) { - if (maxDepth < 0) { + if (maxDepth < 0 || root.getName().endsWith(AutoTranslateListModel.SUFFIX_TRANSLATECOPY)) { return Collections.emptyList(); } if (root.getPath().contains("/jcr:content")) { @@ -233,10 +239,21 @@ public void execute(ResourceResolver callResourceResolver) { resourceResolver.revert(); resourceResolver.refresh(); Resource resource = resourceResolver.getResource(page.resourcePath); - if (resource != null) { - AutoPageTranslateService.Stats stats = pageTranslateService.translateLiveCopy(resource, translationParameters); - page.stats = stats; - page.status = stats.hasChanges() ? "done" : "unchanged"; + try { + if (resource != null) { + AutoPageTranslateService.Stats stats = pageTranslateService.translateLiveCopy(resource, translationParameters); + page.stats = stats; + page.status = stats.hasChanges() ? "done" : "unchanged"; + } + } catch (GPTException.GPTUserNotificationException e) { + throw e; + } catch (Exception e) { + resourceResolver.revert(); + resourceResolver.refresh(); + // mark translation as failed. + resource.adaptTo(ModifiableValueMap.class).put(AI_TRANSLATION_ERRORMARKER, Calendar.getInstance()); + resourceResolver.commit(); + throw e; } } catch (GPTException.GPTUserNotificationException e) { page.status = "cancelled - user notification"; @@ -273,11 +290,15 @@ public void execute(ResourceResolver callResourceResolver) { public static class TranslationPageImpl extends TranslationPage { String resourcePath; - public TranslationPageImpl(String resourcePath) { - this.resourcePath = resourcePath; + public TranslationPageImpl(Resource resource) { + this.resourcePath = resource.getPath(); pagePath = ResourceUtil.getParent(resourcePath); // remove jcr:content this.status = "queued"; + Resource translateCopyResource = resource.getParent().getParent() + .getChild(resource.getParent().getName() + AutoTranslateListModel.SUFFIX_TRANSLATECOPY); + translateCopyPagePath = translateCopyResource != null ? translateCopyResource.getPath() : null; } + } } diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/rollout/AutoTranslateLiveActionImpl.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/rollout/AutoTranslateLiveActionImpl.java index b09b1e30a..81cd85ffd 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/rollout/AutoTranslateLiveActionImpl.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/rollout/AutoTranslateLiveActionImpl.java @@ -1,10 +1,16 @@ package com.composum.ai.aem.core.impl.autotranslate.rollout; +import static com.composum.ai.aem.core.impl.autotranslate.AITranslatePropertyWrapper.AI_TRANSLATION_ERRORMARKER; + +import java.util.Calendar; + import javax.jcr.Node; import javax.jcr.RepositoryException; +import org.apache.sling.api.resource.ModifiableValueMap; import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ValueMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,6 +18,7 @@ import com.composum.ai.aem.core.impl.autotranslate.AutoPageTranslateService; import com.composum.ai.aem.core.impl.autotranslate.AutoTranslateService; import com.composum.ai.backend.slingbase.AIConfigurationService; +import com.day.cq.wcm.api.WCMException; import com.day.cq.wcm.msm.api.LiveAction; import com.day.cq.wcm.msm.api.LiveRelationship; import com.day.cq.wcm.msm.commons.BaseAction; @@ -58,7 +65,7 @@ protected boolean handles(Resource source, Resource target, LiveRelationship rel } @Override - protected void doExecute(Resource source, Resource target, LiveRelationship liveRelationship, boolean autoSave) { + protected void doExecute(Resource source, Resource target, LiveRelationship liveRelationship, boolean autoSave) throws WCMException { String id = (Math.abs(Math.random()) + "").substring(2, 8); LOG.info(">>>{} doExecute({}, {}, {})", id, liveRelationship.getSourcePath(), liveRelationship.getTargetPath(), autoSave); AutoTranslateService.TranslationParameters parms = new AutoTranslateService.TranslationParameters(); @@ -80,13 +87,28 @@ protected void doExecute(Resource source, Resource target, LiveRelationship live } else { autoPageTranslateService.translateLiveCopy(target, parms); } -// } catch (PersistenceException | RuntimeException | LoginException e) { -// throw new WCMException("Error translating " + source.getPath() + "\n" + e, e); - } catch (Exception e) { // rather log exception for now since a demo is coming... + } catch (Exception e) { + // Throwing an exception here will abort a whole recursive rollout, which is a problem. + // So we rather mark failures (which should be infrequent) so that they are easy to find. LOG.error("Error translating " + source.getPath(), e); + markAsError(target); } finally { LOG.info("<<<{} doExecute({}, {}, {})", id, liveRelationship.getSourcePath(), liveRelationship.getTargetPath(), autoSave); } } + /** Make it easy to find pages with errors by marking page with AI_TRANSLATION_ERRORMARKER. */ + protected static void markAsError(Resource target) { + Calendar date = Calendar.getInstance(); + try (ResourceResolver nestedResolver = target.getResourceResolver().clone(null)) { + // use a copy of the resolver since that somehow doesn't work with the original resolver + Resource targetNested = nestedResolver.getResource(target.getPath()); + targetNested.adaptTo(ModifiableValueMap.class).put(AI_TRANSLATION_ERRORMARKER, date); + nestedResolver.commit(); + target.getResourceResolver().refresh(); + } catch (Exception e) { + LOG.warn("Error marking " + target.getPath() + " as error", e); + } + } + } diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/workflow/AutoTranslateWorkflowProcess.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/workflow/AutoTranslateWorkflowProcess.java index a51a3133b..075e87b0a 100644 --- a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/workflow/AutoTranslateWorkflowProcess.java +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/workflow/AutoTranslateWorkflowProcess.java @@ -23,6 +23,7 @@ import com.adobe.granite.workflow.metadata.MetaDataMap; import com.composum.ai.aem.core.impl.autotranslate.AutoPageTranslateService; import com.composum.ai.aem.core.impl.autotranslate.AutoTranslateConfigService; +import com.composum.ai.aem.core.impl.autotranslate.AutoTranslateListModel; import com.composum.ai.aem.core.impl.autotranslate.AutoTranslateService.TranslationParameters; import com.day.cq.wcm.api.WCMException; import com.google.gson.Gson; @@ -146,7 +147,8 @@ protected void translate(@Nonnull Resource resource, TranslationParameters parms while (childIterator.hasNext()) { Resource child = childIterator.next(); // skip jcr:content node since that has been translated already - if (!child.getPath().contains("/jcr:content")) { + if (!child.getPath().contains("/jcr:content") && + !child.getName().endsWith(AutoTranslateListModel.SUFFIX_TRANSLATECOPY)) { translate(child, parms, depth + 1); } } diff --git a/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/workflow/PageTemplatingWorkflowProcess.java b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/workflow/PageTemplatingWorkflowProcess.java new file mode 100644 index 000000000..d3db15cb2 --- /dev/null +++ b/aem/core/src/main/java/com/composum/ai/aem/core/impl/autotranslate/workflow/PageTemplatingWorkflowProcess.java @@ -0,0 +1,91 @@ +package com.composum.ai.aem.core.impl.autotranslate.workflow; + +import static com.adobe.granite.workflow.PayloadMap.TYPE_JCR_PATH; + +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.adobe.granite.workflow.WorkflowException; +import com.adobe.granite.workflow.WorkflowSession; +import com.adobe.granite.workflow.exec.WorkItem; +import com.adobe.granite.workflow.exec.WorkflowData; +import com.adobe.granite.workflow.exec.WorkflowProcess; +import com.adobe.granite.workflow.metadata.MetaDataMap; +import com.adobe.granite.workflow.model.ValidationException; +import com.composum.ai.backend.slingbase.experimential.AITemplatingService; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonSyntaxException; + +/** + * Triggers a call of the {@link AITemplatingService} on the current page. + * If parameter "reset" is given, it does a resetToPrompts instead. + * As process arguments it can be given a json {"reset":true} to reset the page to prompts. + * The URL as source needs to be in the page somewhere. + * + * @see "https://ai.composum.com/aiPageTemplating.html" + */ +public class PageTemplatingWorkflowProcess implements WorkflowProcess { + + private static final Logger LOG = LoggerFactory.getLogger(PageTemplatingWorkflowProcess.class); + + @Reference + protected AITemplatingService aiTemplatingService; + + protected final Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + + @Override + public void execute(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap metaDataMap) throws WorkflowException { + String path = null; + try (ResourceResolver resourceResolver = workflowSession.adaptTo(ResourceResolver.class)) { + WorkflowData workflowData = workItem.getWorkflowData(); + if (workflowData.getPayloadType().equals(TYPE_JCR_PATH)) { + path = workflowData.getPayload().toString(); + if (path == null || !path.startsWith("/content/")) { + LOG.error("Workflow started with wrong payload path {}", path); + throw new IllegalArgumentException("Workflow started with wrong payload path: " + path); + } + Resource resource = resourceResolver.getResource(path); + if (resource != null) { + if (isReset(workItem, metaDataMap)) { + aiTemplatingService.resetToPrompts(resource); + } else { + aiTemplatingService.replacePromptsInResource(resource, null, null, null); + } + } else { + LOG.error("Autotranslate workflow started with wrong payload path - no resource found: {}", path); + } + } else { + LOG.error("Autotranslate workflow started with wrong payload type: {}", workflowData.getPayloadType()); + } + + } catch (Exception e) { + LOG.error("Failed to process page templating for {}", path, e); + throw new WorkflowException("Failed to process page templating for " + path, e); + } + } + + + protected boolean isReset(WorkItem workItem, MetaDataMap metaDataMap) throws WorkflowException { + Object payload = workItem.getWorkflowData().getPayload(); + String processArguments = metaDataMap.get("PROCESS_ARGS", String.class); // e.g. {"reset":true} + LOG.info("TriggerRollout workflow receiver {} , args {}", payload, processArguments); + if (StringUtils.isNotBlank(processArguments)) { + try { + Map parameters = gson.fromJson(processArguments, Map.class); + return parameters.containsKey("reset") && (Boolean) parameters.get("reset"); + } catch (JsonSyntaxException | ClassCastException | IllegalArgumentException e) { + LOG.error("Failed to parse process arguments: {} , ", processArguments, e); + throw new ValidationException("Failed to parse process arguments " + processArguments, e); + } + } + return false; + } + +} diff --git a/aem/ui.apps/src/main/content/jcr_root/apps/composum-ai/components/autotranslate-experiments/list/.content.xml b/aem/ui.apps/src/main/content/jcr_root/apps/composum-ai/components/autotranslate-experiments/list/.content.xml index a059532c3..367fc1a00 100644 --- a/aem/ui.apps/src/main/content/jcr_root/apps/composum-ai/components/autotranslate-experiments/list/.content.xml +++ b/aem/ui.apps/src/main/content/jcr_root/apps/composum-ai/components/autotranslate-experiments/list/.content.xml @@ -2,4 +2,4 @@ + sling:resourceType="composum-ai/components/autotranslate-experiments/list"/> diff --git a/aem/ui.apps/src/main/content/jcr_root/apps/composum-ai/components/autotranslate-experiments/list/list.html b/aem/ui.apps/src/main/content/jcr_root/apps/composum-ai/components/autotranslate-experiments/list/list.html index e108437d4..379e55962 100644 --- a/aem/ui.apps/src/main/content/jcr_root/apps/composum-ai/components/autotranslate-experiments/list/list.html +++ b/aem/ui.apps/src/main/content/jcr_root/apps/composum-ai/components/autotranslate-experiments/list/list.html @@ -87,6 +87,11 @@ +
+ + + +