### Run Example Content Command (Shell) Source: https://github.com/wttech/acm/blob/main/README.md Executes a command to generate example content for the ACM project. ```shell sh taskw develop:content:example ``` -------------------------------- ### Complete Script Example with Documentation Source: https://context7.com/wttech/acm/llms.txt Demonstrates a comprehensive script incorporating YAML frontmatter for metadata, Markdown documentation, and Mermaid diagrams, alongside Groovy code for execution. ```APIDOC ## Complete Script Example with Documentation ### Description Scripts support YAML frontmatter for metadata and Markdown documentation with Mermaid diagrams. ### Method Not Applicable (Script Execution) ### Endpoint Not Applicable (Script Execution) ### Parameters N/A ### Request Example N/A ### Response N/A **Example Script:** ```groovy /* --- version: '1.0' author: john.doe@acme.com schedule: Every day at 2 AM category: maintenance tags: ['cleanup', 'performance', 'automated'] --- Cleans up temporary files and expired content from the repository. This script performs the following operations: 1. Removes files from `/tmp` older than 7 days 2. Deletes expired campaign pages 3. Cleans up orphaned DAM assets ```mermaid graph TD A[Start Cleanup] --> B[Clean /tmp] B --> C[Remove Expired Pages] C --> D[Clean Orphaned Assets] D --> E[Generate Report] E --> F[Send Notification] ``` */ import java.time.LocalDateTime import java.time.temporal.ChronoUnit def scheduleRun() { return schedules.cron("0 0 2 * * ?") // 2 AM daily } boolean canRun() { return conditions.changed() && conditions.isInstanceAuthor() } void doRun() { log.info "Starting daily cleanup job" def stats = [tmpFiles: 0, expiredPages: 0, orphanedAssets: 0] def cutoffDate = LocalDateTime.now().minus(7, ChronoUnit.DAYS) // Clean temporary files repo.query("/tmp", "nt:file").each { file -> context.checkAborted() def created = file.property("jcr:created", LocalDateTime) if (created && created.isBefore(cutoffDate)) { file.delete() stats.tmpFiles++ } } // Generate summary output outputs.text("summary") { label = "Cleanup Summary" value = """ ## Daily Cleanup Results | Category | Items Removed | |----------|---------------| | Temp Files | ${stats.tmpFiles} | | Expired Pages | ${stats.expiredPages} | | Orphaned Assets | ${stats.orphanedAssets} | **Executed at:** ${new Date()} """.stripIndent().trim() } // Notify on completion notifier.sendMessage("Daily Cleanup", "Removed ${stats.tmpFiles + stats.expiredPages + stats.orphanedAssets} items") log.info "Cleanup completed: ${stats}" } ``` ``` -------------------------------- ### Minimal Groovy Script Example Source: https://github.com/wttech/acm/blob/main/README.md A basic Groovy script demonstrating the `canRun()` and `doRun()` methods. The `canRun()` method determines execution, while `doRun()` contains the script's core logic. This script prints 'Hello World!' to the console. ```groovy boolean canRun() { return conditions.always() } void doRun() { println "Hello World!" } ``` -------------------------------- ### Integrate ESLint Plugin React for Recommended Rules Source: https://github.com/wttech/acm/blob/main/ui.frontend/README.md This code demonstrates how to integrate the `eslint-plugin-react` into your ESLint configuration. It sets the React version, adds the plugin, and enables its recommended rules, including those for JSX runtime. ```javascript // eslint.config.js import react from 'eslint-plugin-react'; export default tseslint.config({ // Set the react version settings: { react: { version: '18.3' } }, plugins: { // Add the react plugin react, }, rules: { // other rules... // Enable its recommended rules ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, }, }); ``` -------------------------------- ### ACL Service for User and Group Management (Groovy) Source: https://github.com/wttech/acm/blob/main/README.md An example of using the ACL service to create users, groups, assign permissions, and manage group memberships. Operations are idempotent, ensuring safe re-execution. Logging provides detailed insights into actions taken. ```groovy def scheduleRun() { return schedules.cron("0 10 * ? * * *") // every hour at minute 10 } boolean canRun() { return conditions.always() } void doRun() { log.info "ACL setup started" def acmeService = acl.createUser { id = "acme.service"; systemUser(); skipIfExists() }.tap { // purge() allow { path = "/content"; permissions = ["jcr:read", "jcr:write"] } } def johnDoe = acl.createUser { id = "john.doe"; fullName = "John Doe"; password = "ilovekittens"; skipIfExists() }.tap { // purge() allow("/content", ["jcr:read"]) } acl.createGroup { id = "test.group" }.tap { // removeAllMembers() addMember(acmeService) addMember(johnDoe) } log.info "ACL setup done" } ``` -------------------------------- ### ACM Script: Complete Example with Metadata and Mermaid (Groovy) Source: https://context7.com/wttech/acm/llms.txt This comprehensive Groovy script for ACM includes YAML frontmatter for metadata (version, author, schedule, category, tags) and Markdown documentation with a Mermaid diagram. The `doRun` method performs cleanup tasks, updates outputs, and sends notifications. ```groovy /* --- version: '1.0' author: john.doe@acme.com schedule: Every day at 2 AM category: maintenance tags: ['cleanup', 'performance', 'automated'] --- Cleans up temporary files and expired content from the repository. This script performs the following operations: 1. Removes files from `/tmp` older than 7 days 2. Deletes expired campaign pages 3. Cleans up orphaned DAM assets ```mermaid graph TD A[Start Cleanup] --> B[Clean /tmp] B --> C[Remove Expired Pages] C --> D[Clean Orphaned Assets] D --> E[Generate Report] E --> F[Send Notification] ``` */ import java.time.LocalDateTime import java.time.temporal.ChronoUnit def scheduleRun() { return schedules.cron("0 0 2 * * ?") // 2 AM daily } boolean canRun() { return conditions.changed() && conditions.isInstanceAuthor() } void doRun() { log.info "Starting daily cleanup job" def stats = [tmpFiles: 0, expiredPages: 0, orphanedAssets: 0] def cutoffDate = LocalDateTime.now().minus(7, ChronoUnit.DAYS) // Clean temporary files repo.query("/tmp", "nt:file").each { file -> context.checkAborted() def created = file.property("jcr:created", LocalDateTime) if (created && created.isBefore(cutoffDate)) { file.delete() stats.tmpFiles++ } } // Generate summary output outputs.text("summary") { label = "Cleanup Summary" value = """ ## Daily Cleanup Results | Category | Items Removed | |----------|---------------| | Temp Files | ${stats.tmpFiles} | | Expired Pages | ${stats.expiredPages} | | Orphaned Assets | ${stats.orphanedAssets} | **Executed at:** ${new Date()} """.stripIndent().trim() } // Notify on completion notifier.sendMessage("Daily Cleanup", "Removed ${stats.tmpFiles + stats.expiredPages + stats.orphanedAssets} items") log.info "Cleanup completed: ${stats}" } ``` -------------------------------- ### Groovy Script with Conditions and Repository Manipulation Source: https://github.com/wttech/acm/blob/main/README.md An example of a Groovy script that uses the `conditions.once()` method to ensure execution only happens once. The `doRun()` method iterates through pages in the repository, removing a specific property. It utilizes `out.info` and `out.success` for logging. ```groovy boolean canRun() { return conditions.once() } void doRun() { out.info "Removing deprecated properties from pages..." repo.get("/content/acme").query("n.[sling:resourceType=acme/component/page]").each { page -> page.removeProperty("deprecatedProperty") } out.success "Removed deprecated properties successfully." } ``` -------------------------------- ### Configure ESLint for Type-Aware Linting in React/TypeScript Source: https://github.com/wttech/acm/blob/main/ui.frontend/README.md This snippet shows how to configure ESLint for type-aware linting in a React and TypeScript project. It involves setting `parserOptions` with project configuration and updating the base ESLint configuration to include type-checked rules. ```javascript export default tseslint.config({ languageOptions: { // other options... parserOptions: { project: ['./tsconfig.node.json', './tsconfig.app.json'], tsconfigRootDir: import.meta.dirname, }, }, }); ``` -------------------------------- ### Run Frontend Production Build Command (Shell) Source: https://github.com/wttech/acm/blob/main/README.md Executes a command to build the frontend component of the ACM project in production mode. ```shell sh taskw develop:frontend ``` -------------------------------- ### Simple Console Output in Groovy Source: https://github.com/wttech/acm/blob/main/README.md Demonstrates using `println` and `printf` for basic console output in ACM scripts. This method is suitable for quick debugging or generating straightforward text without timestamps or log levels. ```groovy void doRun() { println "Simple message without timestamp" printf "Formatted: %s = %d\n", "count", 42 } ``` -------------------------------- ### Run All-in-one Development Command (Shell) Source: https://github.com/wttech/acm/blob/main/README.md Executes an all-in-one command for the ACM project, performing incremental building and deployment for both backend and frontend distributions. ```shell sh taskw develop:all ``` -------------------------------- ### Run Frontend Development Build Command (Shell) Source: https://github.com/wttech/acm/blob/main/README.md Executes a command to build the frontend component of the ACM project in development mode, enabling live reloading. ```shell sh taskw develop:frontend:dev ``` -------------------------------- ### ACM Extension Script: Prepare and Complete Execution (Groovy) Source: https://context7.com/wttech/acm/llms.txt This Groovy script demonstrates how to hook into the ACM script execution lifecycle. The `prepareRun` method allows adding custom variables and configurations before execution, while `completeRun` handles post-execution logic, including error logging and sending notifications. ```groovy // Place at: /conf/acm/settings/script/extension/myproject/main.groovy import dev.vml.es.acm.core.code.ExecutionContext import dev.vml.es.acm.core.code.Execution // Called before script execution - add custom variables void prepareRun(ExecutionContext executionContext) { // Add custom facade available as 'myapp' in all scripts executionContext.variable("myapp", new MyAppFacade()) // Add configuration executionContext.variable("appConfig", [ apiUrl: "https://api.example.com", maxRetries: 3 ]) } // Called after script execution - handle results void completeRun(Execution execution) { def status = execution.status.name() def scriptId = execution.executable.id def duration = execution.duration if (status == 'FAILED') { log.error "Script '${scriptId}' failed after ${duration}ms" // Send notification to Slack/Teams/Email notifier.sendMessageTo("alerts", "Script Failed", "Script ${scriptId} failed. Check logs for details.") } else if (status == 'SUCCEEDED') { log.info "Script '${scriptId}' completed in ${duration}ms" } } // Custom facade class class MyAppFacade { def getCurrentTimestamp() { return new Date().format("yyyy-MM-dd'T'HH:mm:ss") } def fetchData(String endpoint) { // Custom HTTP client logic return [status: "ok", data: []] } } ``` -------------------------------- ### Logging in ACM Scripts (Groovy) Source: https://github.com/wttech/acm/blob/main/README.md Demonstrates how to use log.error(), log.warn(), log.info(), log.debug(), and log.trace() for writing messages to both the console and AEM logs. This is essential for production scripts requiring persistent log records. ```groovy void doRun() { log.info "Doing regular stuff" try { // ... risky logic log.info "Doing risky stuff ended" } catch (Exception e) { log.error "Doing risky stuff failed: ${e.message}", e } } ``` -------------------------------- ### Create Extension Scripts in Groovy for ACM Source: https://github.com/wttech/acm/blob/main/README.md Allows adding custom code bindings or hooking into the execution process by creating extension Groovy scripts. These scripts are placed in a specific path within the configuration. The `prepareRun` method can inject variables, and `completeRun` can handle post-execution logic, like error notifications. ```groovy import dev.vml.es.acm.core.code.ExecutionContext import dev.vml.es.acm.core.code.Execution void prepareRun(ExecutionContext executionContext) { executionContext.variable("acme", new AcmeFacade()) } void completeRun(Execution execution) { if (execution.status.name() == 'FAILED') { log.error "Something nasty happened with '${execution.executable.id}'!" // TODO send notification on Slack, MS Teams, etc using HTTP client / WebAPI } } class AcmeFacade { def now() { return new Date() } } ``` -------------------------------- ### Run Backend Only Development Command (Shell) Source: https://github.com/wttech/acm/blob/main/README.md Executes a command to build and deploy only the backend (core) component of the ACM project. ```shell sh taskw develop:core ``` -------------------------------- ### Generate Text Outputs (Markdown, JSON) in Groovy Source: https://github.com/wttech/acm/blob/main/README.md Illustrates generating text-based outputs, such as markdown summaries or JSON configurations, using the `outputs.text` method. This is useful for providing users with quick access to generated information or configurations. The `language` property can be set for syntax highlighting. ```groovy outputs.text("summary") { label = "Summary" value = """ - Total Users: ${totalUsers} - Active Users: ${activeUsers} - Inactive Users: ${inactiveUsers} - Generated At: ${new Date()} """.stripIndent().trim() } outputs.text("configJson") { label = "Configuration" value = ''' { "setting1": true, "setting2": "value", "setting3": 10 } '''.stripIndent().trim() language = "json" } ``` -------------------------------- ### Document Groovy Scripts with YAML Frontmatter and Mermaid Source: https://github.com/wttech/acm/blob/main/README.md Adds metadata and documentation to Groovy scripts using block comments with YAML frontmatter. Supports Markdown and Mermaid diagrams for enhanced readability and visualization. The comment must be at the top of the file or after import/package statements, followed by a blank line. ```groovy /* --- version: '1.0' author: john.doe@acme.com schedule: Every hour at 10 minutes past the hour category: security tags: ['content', 'migration', 'security'] --- Creates content author groups for each tenant-country-language combination. The groups are named in the format: `{tenant}-{country}-{language}-content-authors`. Each group is granted read, write, and replicate permissions on the corresponding content and DAM paths. \```mermaid --- config: look: handDrawn theme: base --- graph LR A[Scan Tenants] --> B[Find Countries] B --> C[Find Languages] C --> D[Create Author Groups] D --> E[Grant Permissions] \``` */ boolean canRun() { return conditions.changed() } void doRun() { // implementation } ``` -------------------------------- ### Generate CSV File Output in Groovy Source: https://github.com/wttech/acm/blob/main/README.md Shows how to generate a CSV file as an output from an ACM script using the `outputs` service. The script defines the file's label, description, and download name, then writes data to the output stream. Multiple output files can be generated per script. ```groovy boolean canRun() { return conditions.always() } void doRun() { log.info "Users report generation started" def report = outputs.file("report") { label = "Report" description = "Users report generated as CSV file" downloadName = "report.csv" } def users = [ [name: "John", surname: "Doe", birth: "1991"], [name: "Jane", surname: "Doe", birth: "1995"], [name: "Jack", surname: "Smith", birth: "1988"] ] for (def user : users) { report.out.println("${user.name},${user.surname},${user.birth}") } log.info "Users report generation ended successfully" } ``` -------------------------------- ### Repository Operations with Dry-Run Support (Groovy) Source: https://context7.com/wttech/acm/llms.txt Demonstrates various JCR repository operations using the repo service API in Groovy. Includes CRUD operations, file handling, queries, and transaction management with dry-run support. The `dryRun` method allows previewing changes before committing. ```groovy void describeRun() { inputs.bool("dryRun") { value = true; switcher(); description = "Preview without saving" } } boolean canRun() { return conditions.always() } void doRun() { // Wrap operations in dry-run mode repo.dryRun(inputs.value("dryRun")) { // Get resource (returns RepoResource wrapper) def resource = repo.get("/content/mysite/en") // Check existence if (resource.exists()) { log.info "Resource exists at: ${resource.path}" } // Ensure folder structure exists def dataFolder = repo.get("/tmp/acm/data").ensureFolder() def orderedFolder = repo.get("/tmp/acm/ordered").ensureOrderedFolder() // Create/update resource with properties def newNode = dataFolder.child("item-1").save([ "jcr:primaryType": "nt:unstructured", "title": "My Item", "count": 42, "enabled": true ]) // Read properties def title = newNode.property("title", String) def count = newNode.property("count", 0) def props = newNode.properties() // Update single property newNode.saveProperty("status", "active") // Update property with transformer newNode.updateProperty("count") { current -> (current ?: 0) + 1 } // Delete property newNode.deleteProperty("temporary") // Copy and move resources def copy = newNode.copy("/tmp/acm/backup/item-1") def moved = repo.get("/tmp/acm/old").move("/tmp/acm/new") // Rename resource newNode.rename("item-renamed") // Delete resource repo.get("/tmp/acm/obsolete").delete() // Navigate tree def parent = newNode.parent() def children = dataFolder.children() def descendants = dataFolder.descendants() def siblings = newNode.siblings() // Save file with content def configFile = dataFolder.child("config.json").saveFile("application/json") { formatter.json.write(output, [setting: "value"]) } // Read file content def fileContent = configFile.readFileAsString() def fileStream = configFile.readFileAsStream() // Query resources with JCR-SQL2 repo.query("/content/mysite", "cq:Page", "n.[jcr:title] LIKE '%news%'").each { log.info "Found page: ${it.path}" } // Raw SQL2 query repo.queryRaw("SELECT * FROM [nt:base] WHERE ISDESCENDANTNODE('/content/dam')").each { context.checkAborted() // Support graceful abortion log.info "Processing: ${it.path}" } } // Manual transaction control repo.autoCommit(false) try { repo.get("/content/test").save(["prop": "value"]) repo.commit() } catch (Exception e) { repo.revert() throw e } finally { repo.autoCommit(true) } } ``` -------------------------------- ### Repository Service for JCR Operations (Groovy) Source: https://github.com/wttech/acm/blob/main/README.md Leverages the repository service to perform JCR operations like reading, writing, and deleting nodes. It abstracts complexities of dry-run and auto-commit behaviors, offering concise and reliable repository programming. ```groovy void describeRun() { inputs.bool("dryRun") { value = true; switcher(); description = "Do not commit changes to the repository" } inputs.bool("clean") { value = true; switcher(); description = "Finally delete all created resources" } } void doRun() { repo.dryRun(inputs.value("dryRun")) { log.info "Creating a folder structure in the temporary directory of the repository." def dataFolder = repo.get("/tmp/acm/demo/data").ensureFolder() for (int i = 0; i < 5; i++) { def child = dataFolder.child("child-${i+1}").save(["foo": "bar"]) child.updateProperty("foo") { v -> v.toUpperCase() } } log.info "Folder '${dataFolder.path}' has now ${dataFolder.descendants().count()} descendant(s)." log.info "Creating a post in the temporary directory of the repository." def postFolder = repo.get("/tmp/acm/demo/posts").ensureFolder() def post = postFolder.child("hello-world.yml").saveFile("application/x-yaml") { output -> formatter.yaml.write(output, [ title: "Hello World", description: "This is a sample post.", tags: ["sample", "post"] ]) } log.info "Post '${post.path}' has been created at ${post.property("jcr:created", java.time.LocalDateTime)}" if (inputs.value("clean")) { dataFolder.delete() postFolder.delete() } } } ``` -------------------------------- ### Define Script Inputs in Groovy Source: https://github.com/wttech/acm/blob/main/README.md Demonstrates how to define string inputs for an ACM script using the `inputs` service. These inputs can be set when the script is executed and accessed within the `doRun` method. Built-in input types include string, boolean, number, date, and file. ```groovy void describeRun() { inputs.string("name") { value = "John" } inputs.string("surname") { value = "Doe" } } boolean canRun() { return conditions.always() } void doRun() { println "Hello ${inputs.value('name')} ${inputs.value('surname')}" } ``` -------------------------------- ### Check Last Release Version (Shell) Source: https://github.com/wttech/acm/blob/main/README.md Runs a command to check the latest release version of the ACM project. ```shell sh taskw release ``` -------------------------------- ### Configure Feature Permissions with Groups Source: https://github.com/wttech/acm/blob/main/README.md This snippet demonstrates how to define feature permissions by creating user groups and assigning specific access rights (ACLs) to them. It includes configurations for 'acm-admins' and 'acm-script-users' groups, specifying read access to different ACM feature paths. ```INI service.ranking=I"100" scripts=[" set ACL for everyone deny jcr:read on /apps/cq/core/content/nav/tools/acm deny jcr:read on /apps/acm end create group acm-admins set ACL for acm-admins allow jcr:read on /apps/cq/core/content/nav/tools/acm allow jcr:read on /apps/acm end create group acm-script-users set ACL for acm-script-users allow jcr:read on /apps/cq/core/content/nav/tools/acm allow jcr:read on /apps/acm/gui allow jcr:read on /apps/acm/api allow jcr:read on /apps/acm/feature/script/list allow jcr:read on /apps/acm/feature/script/view allow jcr:read on /apps/acm/feature/execution/view allow jcr:read on /conf/acm/settings/script end"] ``` -------------------------------- ### Console Output and Logging Mechanisms (Groovy) Source: https://context7.com/wttech/acm/llms.txt Illustrates the different output mechanisms provided by ACM in Groovy. This includes simple `println`, timestamped console output using `out.*` for the ACM UI, and persistent logging via `log.*` which writes to both the console and AEM logs. ```groovy void doRun() { // Simple output (no timestamp, no log level) println "Starting process..." printf "Processing %d items in %s mode%n", 100, "batch" // Timestamped console output (visible in ACM UI only) out.info "Operation started" out.debug "Debug details: processing batch 1" out.warn "Resource /content/old is deprecated" out.success "Batch 1 completed successfully" out.error "Failed to process item-42" // Logged output (written to both ACM console AND AEM error.log) log.trace "Entering method processItems" log.debug "Processing configuration: ${config}" log.info "Migration started for /content/mysite" log.warn "Performance threshold exceeded: 5000ms" try { riskyOperation() } catch (Exception e) { log.error "Migration failed", e // Includes stack trace in logs throw e } log.info "Migration completed successfully" } ``` -------------------------------- ### Configure Slack Notification (INI) Source: https://github.com/wttech/acm/blob/main/README.md Configures the Slack notification service for the ACM project. This INI file enables Slack notifications, sets a unique ID, provides a webhook URL, and specifies a timeout in milliseconds. ```ini enabled=B"true" id="acm" webhookUrl="https://hooks.slack.com/services/XXXXXXXXX/YYYYYYYYYYY/ZZZZZZZZZZZZZZZZZZZZZZZZ" timeoutMillis=I"5000" ``` -------------------------------- ### Timestamped Console Output in Groovy Source: https://github.com/wttech/acm/blob/main/README.md Explains how to use `out.error()`, `out.warn()`, `out.success()`, `out.info()`, and `out.debug()` for timestamped and level-specific console messages. These messages appear in the execution console and are not persisted to AEM logs. ```groovy void doRun() { out.error "Failed to process resource: ${resource.path}" out.warn "Resource ${resource.path} is missing required property" out.success "Resource ${resource.path} processed successfully" out.info "Processing started" } ``` -------------------------------- ### AEM ACM Script Scheduling API (Groovy) Source: https://context7.com/wttech/acm/llms.txt The `schedules` service in AEM Content Manager (ACM) defines when automatic scripts should trigger. Scripts can be configured to run on boot, on a cron schedule, or be disabled entirely. The `scheduleRun()` method returns the schedule configuration. ```groovy // Run on every instance boot def scheduleRun() { return schedules.boot() } // Run every hour at minute 10 def scheduleRun() { return schedules.cron("0 10 * ? * * *") } // Run every day at midnight def scheduleRun() { return schedules.cron("0 0 0 * * ?") } // Run every Monday at 9 AM def scheduleRun() { return schedules.cron("0 0 9 ? * MON") } // Disable scheduled execution def scheduleRun() { return schedules.none() } boolean canRun() { return conditions.always() } void doRun() { log.info "Scheduled task executed" } ``` -------------------------------- ### Manage JCR Users, Groups, and Permissions with ACL Service API (Groovy) Source: https://context7.com/wttech/acm/llms.txt This Groovy script demonstrates how to use the ACL Service API to manage JCR users, groups, and permissions. It covers creating users and groups, setting permissions, adding/removing members, and deleting authorizables. The operations are idempotent and logged. ```groovy boolean canRun() { return conditions.changed() } void doRun() { log.info "ACL setup started" // Create a system user (service account) def serviceUser = acl.createUser { id = "myapp.service" systemUser() skipIfExists() } serviceUser.allow { path = "/content/myapp"; permissions = ["jcr:read", "jcr:write"] } // Create a regular user with password def regularUser = acl.createUser { id = "john.doe" password = "securePassword123" fullName = "John Doe" skipIfExists() } regularUser.allow("/content/myapp", ["jcr:read"]) // Create a group and add members def editorsGroup = acl.createGroup { id = "myapp-editors" } editorsGroup.tap { addMember(serviceUser) addMember(regularUser) allow { path = "/content/myapp"; permissions = ["jcr:read", "rep:write"] } allow { path = "/content/dam/myapp"; permissions = ["jcr:read", "rep:write", "crx:replicate"] } deny { path = "/content/myapp/secure"; permissions = ["jcr:all"] } } // Get existing user/group def existingUser = acl.getUser("admin") def existingGroup = acl.getGroup("administrators") // Add user to multiple groups acl.addToGroup("john.doe", "content-authors") acl.addToGroup(regularUser, editorsGroup) // Remove user from group acl.removeFromGroup("john.doe", "myapp-editors") // Set user property acl.setProperty("john.doe", "profile/email", "john@example.com") // Change password acl.setPassword("john.doe", "newPassword456") // Delete user/group acl.deleteUser("temporary.user") acl.deleteGroup("deprecated-group") // Purge all permissions for an authorizable acl.purge("old.user") // Clear permissions at specific path acl.clear("john.doe", "/content/legacy", true) log.info "ACL setup completed" } ``` -------------------------------- ### Release New Version (Shell) Source: https://github.com/wttech/acm/blob/main/README.md Executes a command to release a new version of the ACM project, requiring the new version number as an argument. ```shell sh taskw release -- ``` -------------------------------- ### Repository Service API Source: https://context7.com/wttech/acm/llms.txt The repo service offers a fluent API for JCR repository operations. It supports CRUD operations, queries, file handling, and transaction management, including a dry-run mode for previewing changes without persistence. ```APIDOC ## Repository (Repo) Service API ### Description Provides a fluent API for JCR repository operations including CRUD, queries, file handling, and transaction management with dry-run support. ### Method Not applicable (Groovy DSL) ### Endpoint Not applicable (Groovy DSL) ### Parameters N/A ### Request Example ```groovy // Example of using dry-run mode repo.dryRun(inputs.value("dryRun")) { // Repository operations here } // Example of getting a resource def resource = repo.get("/content/mysite/en") // Example of creating a node with properties def newNode = dataFolder.child("item-1").save([ "jcr:primaryType": "nt:unstructured", "title": "My Item", "count": 42, "enabled": true ]) // Example of querying resources repo.query("/content/mysite", "cq:Page", "n.[jcr:title] LIKE '%news%'" ).each { log.info "Found page: ${it.path}" } ``` ### Response #### Success Response (200) N/A (Groovy DSL execution) #### Response Example N/A ``` -------------------------------- ### Console Output and Logging API Source: https://context7.com/wttech/acm/llms.txt ACM provides three distinct mechanisms for outputting information: standard `println` for basic output, `out.*` for timestamped console messages visible only in the ACM UI, and `log.*` for persistent logging to both the console and AEM's error.log. ```APIDOC ## Console Output and Logging API ### Description ACM provides three output mechanisms: simple `println`, timestamped `out.*` for console display, and `log.*` for persistent AEM logs. ### Method Not applicable (Groovy DSL) ### Endpoint Not applicable (Groovy DSL) ### Parameters N/A ### Request Example ```groovy // Simple output println "Starting process..." // Timestamped console output out.info "Operation started" out.warn "Resource /content/old is deprecated" // Logged output (console and AEM error.log) log.info "Migration started for /content/mysite" log.error "Migration failed", e // Includes stack trace ``` ### Response #### Success Response (200) N/A (Groovy DSL execution) #### Response Example N/A ``` -------------------------------- ### Generate Script Outputs with Groovy Source: https://context7.com/wttech/acm/llms.txt The 'outputs' service in Groovy enables the generation of downloadable files and text-based outputs from script execution. These outputs are persisted and accessible via the execution history. Supported output types include CSV, JSON files, and formatted text with optional syntax highlighting and markdown support. ```groovy boolean canRun() { return conditions.always() } void doRun() { // Generate CSV file output def csvReport = outputs.file("usersReport") { label = "Users Report" description = "Export of all users" downloadName = "users-export.csv" } csvReport.out.println("Name,Email,Role") csvReport.out.println("John Doe,john@example.com,Admin") csvReport.out.println("Jane Smith,jane@example.com,Editor") // Generate JSON file output def jsonExport = outputs.file("jsonExport") { label = "JSON Export" downloadName = "data.json" } jsonExport.out.println('{"users": [{"name": "John"}, {"name": "Jane"}]}') // Text output with markdown (displayed in UI) outputs.text("summary") { label = "Execution Summary" value = """ ## Results - **Processed**: 150 items - **Skipped**: 5 items - **Errors**: 0 [View full report](/content/dam/reports/latest.pdf) """.stripIndent().trim() } // Text output with syntax highlighting outputs.text("generatedConfig") { label = "Generated Configuration" language = "json" value = '''{ "enabled": true, "maxRetries": 3, "timeout": 30000 }'''.stripIndent().trim() } out.success "Report generation completed" } ``` -------------------------------- ### Script Scheduling API Source: https://context7.com/wttech/acm/llms.txt The schedules service defines when automatic scripts should trigger, offering options for running on boot, at specific cron intervals, or disabling scheduled execution. ```APIDOC ## Script Scheduling API ### Description This API allows defining the schedule for automatic script execution. Scripts can be configured to run on instance boot, at specific times defined by cron expressions, or to be disabled entirely. ### Methods - `schedules.boot()`: Run on every instance boot. - `schedules.cron(String cronExpression)`: Run according to the provided cron expression. - `schedules.none()`: Disable scheduled execution. ### Example Usage ```groovy // Run on every instance boot def scheduleRun() { return schedules.boot() } // Run every hour at minute 10 def scheduleRun() { return schedules.cron("0 10 * ? * * *") } // Run every day at midnight def scheduleRun() { return schedules.cron("0 0 0 * * ?") } // Run every Monday at 9 AM def scheduleRun() { return schedules.cron("0 0 9 ? * MON") } // Disable scheduled execution def scheduleRun() { return schedules.none() } boolean canRun() { return conditions.always() // Example condition to always allow execution when scheduled } void doRun() { log.info "Scheduled task executed" } ``` ``` -------------------------------- ### Embed ACM Package in Maven FileVault Configuration Source: https://github.com/wttech/acm/blob/main/README.md This snippet demonstrates how to configure the filevault-package-maven-plugin to embed the ACM 'all' package into your AEM project's vendor packages. This is crucial for AEMaaCS deployments. ```xml dev.vml.es acm.all zip /apps/${appId}-vendor-packages/application/install ``` -------------------------------- ### Send Notifications via Groovy Script Source: https://github.com/wttech/acm/blob/main/README.md Demonstrates how to send messages using the ACM notifier service within a Groovy script. It shows how to send a message to a specific notifier or the default one. ```groovy notifier.sendMessageTo("acme", "ACME Project Notifications", "An important event occurred.") notifier.sendMessage("ACME Project Notifications", "Let's start the day with a coffee!") // uses the 'default' notifier ``` -------------------------------- ### Define Script Inputs with Groovy Source: https://context7.com/wttech/acm/llms.txt The 'inputs' service in Groovy allows defining various user input types for manual scripts. It supports strings, numbers, booleans, dates, files, selections, and more, with options for validation, default values, and grouping. Inputs can be configured with labels, descriptions, and specific UI elements like password fields, checkboxes, or toggles. ```groovy void describeRun() { // String input with default value inputs.string("userName") { value = "John Doe" } // Password input (masked) inputs.string("apiKey") { label = "API Key"; password() } // Boolean checkbox inputs.bool("dryRun") { value = true; checkbox(); description = "Preview changes without saving" } // Boolean toggle switch inputs.bool("enabled") { value = false; switcher() } // Integer with min/max inputs.integerNumber("batchSize") { min = 1; max = 1000; value = 100 } // Decimal number inputs.decimalNumber("threshold") { min = 0.0d; max = 1.0d; value = 0.5d } // Dropdown selection inputs.select("environment") { options = ["Development": "dev", "Staging": "stage", "Production": "prod"] value = "dev" } // Multi-select inputs.multiSelect("regions") { options = ["US", "EU", "APAC"] value = ["US"] } // Date and time inputs inputs.date("startDate") { value = "2024-01-01"; group = "Schedule" } inputs.time("executionTime") { value = "09:00"; group = "Schedule" } inputs.dateTime("deadline") { value = "2024-12-31T23:59:59"; group = "Schedule" } // Path picker (JCR path) inputs.path("contentRoot") { rootPathExclusive = "/content"; value = "/content/mysite" } // File upload inputs.file("importFile") { label = "Import CSV" } inputs.multiFile("attachments") { max = 5; optional() } // Text area with syntax highlighting inputs.text("jsonConfig") { language = "json"; value = '{"key": "value"}' } // Color picker inputs.color("brandColor") { value = "#ff5500" } // Key-value pairs inputs.map("headers") { value = ["Content-Type": "application/json"] } } boolean canRun() { return conditions.always() } void doRun() { def userName = inputs.value("userName") def dryRun = inputs.value("dryRun") def batchSize = inputs.value("batchSize") out.info "Processing for user: ${userName}, batch size: ${batchSize}, dry run: ${dryRun}" } ``` -------------------------------- ### Support Graceful Abortion in Groovy Scripts Source: https://github.com/wttech/acm/blob/main/README.md Enables users to stop long-running scripts without leaving the repository in an inconsistent state. Uses `context.checkAborted()` for safe points or `context.isAborted()` for manual control within loops. ```groovy void doRun() { repo.queryRaw("SELECT * FROM [nt:base] WHERE ISDESCENDANTNODE('/content/acme/us/en')").forEach { resource -> // Safe point context.checkAborted() // Process resource // TODO resource.save() etc. } } ``` ```groovy void doRun() { def assets = repo.queryRaw("SELECT * FROM [dam:Asset] WHERE ISDESCENDANTNODE('/content/dam')").iterator() for (asset in assets) { if (context.isAborted()) { // Do clean when aborted break } } // Still remember to propagate abort status context.checkAborted() } ``` -------------------------------- ### Create ACM Snippet (YAML) Source: https://github.com/wttech/acm/blob/main/README.md Defines a custom code snippet for the ACM project using YAML format. It includes a group, name, the code content with placeholders, and documentation that can include Markdown and HTML. ```yaml group: Acme name: acme_hello content: | println "Hello ${1:message} in ACME project!" } documentation: | Prints a greeting message in the ACME project. ``` -------------------------------- ### Extension Scripts API Source: https://context7.com/wttech/acm/llms.txt Extension scripts allow custom logic to be injected into the ACM execution lifecycle, enabling actions like adding bindings, setup/teardown, and handling script completion notifications. ```APIDOC ## Extension Scripts API ### Description Extension scripts hook into the execution lifecycle to add custom bindings, perform setup/teardown, or handle notifications on script completion. ### Method Not Applicable (Script Execution) ### Endpoint Not Applicable (Script Execution) ### Parameters N/A ### Request Example N/A ### Response N/A **Example Script (`/conf/acm/settings/script/extension/myproject/main.groovy`):** ```groovy import dev.vml.es.acm.core.code.ExecutionContext import dev.vml.es.acm.core.code.Execution // Called before script execution - add custom variables void prepareRun(ExecutionContext executionContext) { // Add custom facade available as 'myapp' in all scripts executionContext.variable("myapp", new MyAppFacade()) // Add configuration executionContext.variable("appConfig", [ apiUrl: "https://api.example.com", maxRetries: 3 ]) } // Called after script execution - handle results void completeRun(Execution execution) { def status = execution.status.name() def scriptId = execution.executable.id def duration = execution.duration if (status == 'FAILED') { log.error "Script '${scriptId}' failed after ${duration}ms" // Send notification to Slack/Teams/Email notifier.sendMessageTo("alerts", "Script Failed", "Script ${scriptId} failed. Check logs for details.") } else if (status == 'SUCCEEDED') { log.info "Script '${scriptId}' completed in ${duration}ms" } } // Custom facade class class MyAppFacade { def getCurrentTimestamp() { return new Date().format("yyyy-MM-dd'T'HH:mm:ss") } def fetchData(String endpoint) { // Custom HTTP client logic return [status: "ok", data: []] } } ``` ``` -------------------------------- ### Run Project Test Script (Bash) Source: https://github.com/wttech/acm/blob/main/test/project/README.MD This bash script executes the project testing task. It is run from the root of the repository. ```bash sh taskw test:project ``` -------------------------------- ### Configure AEM Cloud Manager Repository (.env file) Source: https://github.com/wttech/acm/blob/main/test/project/README.MD This snippet shows how to configure the AEM Cloud Manager repository URL in the .env file. It requires replacing placeholder values with actual Cloud Manager credentials and repository details. ```env AEM_CM_URL=https://:@git.cloudmanager.adobe.com/// ``` -------------------------------- ### AEM ACM Script Execution Conditions API (Groovy) Source: https://context7.com/wttech/acm/llms.txt The `conditions` service in AEM Content Manager (ACM) determines when automatic scripts should execute. It supports one-time execution, content-change detection, instance state monitoring, and environment-specific conditions. The `canRun()` method returns a boolean indicating whether the script should proceed. ```groovy // Run script only once (never retry on failure) boolean canRun() { return conditions.once() } // Run when script content changes OR when instance changes after a failure (recommended for production) boolean canRun() { return conditions.changed() } // Run only on author instances boolean canRun() { return conditions.isInstanceAuthor() } // Run only on AEM Cloud Service boolean canRun() { return conditions.isInstanceCloud() } // Retry up to 3 times on failure boolean canRun() { return conditions.retry(3) } // Run if at least 1 hour has passed since last execution boolean canRun() { return conditions.passed(java.time.Duration.ofHours(1)) } void doRun() { out.info "Script executed successfully" } ``` -------------------------------- ### Set API Permissions for User Source: https://github.com/wttech/acm/blob/main/README.md This configuration sets API permissions for a specific user group, 'acm-automation-user'. It grants read access to the ACM API, feature, and script settings paths, which is necessary for code execution. ```INI set ACL for acm-automation-user allow jcr:read on /apps/acm/api allow jcr:read on /apps/acm/feature allow jcr:read on /conf/acm/settings/script end ```