<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The Colorful Slate Blog]]></title><description><![CDATA[A blog focused on AEM and other Dev related stuff]]></description><link>https://aemslate.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Apr 2026 13:06:59 GMT</lastBuildDate><atom:link href="https://aemslate.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Integrating Jenkins with AEM Deployments]]></title><description><![CDATA[In this article, let's dive in and understand how Jenkins pulls the code from Git, builds it, and then pushes it to AEM, deploying the artifact.
Steps to follow
Step 1: We need to have a running AEM Author and Publish instance. You can also use local...]]></description><link>https://aemslate.com/integrating-jenkins-with-aem-deployments</link><guid isPermaLink="true">https://aemslate.com/integrating-jenkins-with-aem-deployments</guid><category><![CDATA[AEM]]></category><category><![CDATA[aem development services]]></category><category><![CDATA[ Jenkins, DevOps]]></category><category><![CDATA[jenkins pipeline]]></category><dc:creator><![CDATA[Ashish Mishra]]></dc:creator><pubDate>Wed, 20 Aug 2025 15:40:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755669415066/5aca29c4-c243-4b7d-ae50-835a7a15c259.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, let's dive in and understand how Jenkins pulls the code from Git, builds it, and then pushes it to AEM, deploying the artifact.</p>
<h3 id="heading-steps-to-follow">Steps to follow</h3>
<p><strong>Step 1:</strong> We need to have a running AEM Author and Publish instance. You can also use local author and publish instances for simplicity. If you are deploying to a server, the steps are mostly the same.</p>
<p><strong>Step 2:</strong> We need to install Jenkins using Docker. You can install Docker by following this <a target="_blank" href="https://docs.docker.com/desktop/setup/install/mac-install/"><mark>article</mark></a>.</p>
<p>Once Docker is installed, run this command in the terminal -</p>
<pre><code class="lang-bash">docker run -d -p 8080:8080 -p 50000:50000 --name jenkins jenkins/jenkins:lts
</code></pre>
<p>This command will install Jenkins using Docker on port 8080, and you can access it from your browser. If it's on your local machine, try using localhost:8080, or use serverIP:8080 if it's on a server.</p>
<p><strong>Step 3:</strong></p>
<p>When you open localhost:8080, you'll see a message like the image below, prompting you to access the admin password to continue.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755669852707/e0924561-af48-4325-92ab-14d1c66037e7.png" alt class="image--center mx-auto" /></p>
<p>To get the root password, run the following command in the terminal.</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> -it jenkins cat /var/jenkins_home/secrets/initialAdminPassword
</code></pre>
<p>It will output the admin password, use it to proceed in jenkins.</p>
<p>Next, install the suggested plugins option. It will take few minutes, and all the plugins will get installed. These include plugins like git, dark mode, credentials manager, etc.</p>
<p>It will now ask you to fill username and password. Create a user and proceed.</p>
<p><strong>Step 4:</strong></p>
<p>Once done go to the <a target="_blank" href="http://localhost:8080/manage/">http://localhost:8080/manage/</a> and click on <strong>Plugins</strong>, we have to search and select “<strong>Maven Integration plugin”</strong> from this list (refer below image)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755528346435/a5db9383-bd4a-4745-8b90-89e7a0b70750.png" alt class="image--center mx-auto" /></p>
<p><strong>Step 5:</strong></p>
<p>Once done with installation, go to <a target="_blank" href="http://localhost:8080/manage/configureTools/">http://localhost:8080/manage/configureTools/</a> and scroll below, and go to “<strong>Maven Installations</strong>”, and select the maven version from the list and save.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755528262116/b8a3c18a-4779-4b19-8f34-7436d5bcf244.png" alt class="image--center mx-auto" /></p>
<p><strong>Step 6:</strong></p>
<p>Now, we also need to securely pass our credentials to the pipeline. To do this, go to <a target="_blank" href="http://localhost:8080/manage/credentials/store/system/domain/_/">http://localhost:8080/manage/credentials/store/system/domain/_/</a> and add new credentials there. Make sure to note the ID, as we will use this ID in our pipeline syntax, allowing Jenkins to pass the credentials securely. In my case i have saved my bitbucket(git) credentials to checkout the code, and then aem credentials to upload the package. These two are required for this script execution.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755702794902/5d922860-02a4-4ffd-b9a5-1e95e14349c2.png" alt class="image--center mx-auto" /></p>
<p><strong>Step 7:</strong></p>
<p>Now, come back to main page <a target="_blank" href="http://localhost:8080/">http://localhost:8080/</a> , click on <strong>“Create Job”</strong> and then provide a name to this item <strong>“Local AEM Build and Deploy”</strong> and select “<strong>Pipeline</strong>”</p>
<blockquote>
<p><strong>Pipeline is the simplest option, but we can choose other options too, depending on the use case, such as "Multi Branch Project." For simplicity, we have selected "Pipeline" for this topic.</strong></p>
</blockquote>
<p>Now, you have several options available, and most of them are easy to understand. For this tutorial, scroll down to the end and add the script below in the pipeline script option.</p>
<p>You might need to make some changes based on your use case, such as replacing the correct Maven name you provided in the tools section in Step 5.</p>
<p>Update the <strong>AEM_HOST IP</strong> or set it to “<strong>localhost</strong>” if AEM is running locally.</p>
<p>In case jenkins is running on docker, and aem is running on mac/windows, we have to use <strong>“host.docker.internal”</strong> to access the aem machine from docker jenkins</p>
<p>Depending on the publisher status, you can comment out or remove the <strong>“Deploy to Publisher”</strong> stage from the script.</p>
<p>Update the correct Git URL of your repository in the “Checkout” step.</p>
<pre><code class="lang-bash">pipeline {
    agent any
    tools {
        maven <span class="hljs-string">'maven-3.9.11'</span>
    }
    environment {
        AEM_HOST = <span class="hljs-string">"172.104.206.123"</span>
        AUTHOR_AEM_PORT = <span class="hljs-string">"4502"</span>
        PUBLISHER_AEM_PORT = <span class="hljs-string">"4503"</span>
        AEM_PACKAGE = <span class="hljs-string">"all/target/colorful.all-1.0.0-SNAPSHOT.zip"</span>
    }

    stages {
        stage(<span class="hljs-string">'Checkout'</span>) {
            steps {
                git branch: <span class="hljs-string">'master'</span>,
                        credentialsId: <span class="hljs-string">'bitbucket-credentials'</span>,
                        url: <span class="hljs-string">'https://the-colorful-slate@bitbucket.org/the-colorful-slate/colorful-aem.git'</span>
            }
        }

        stage(<span class="hljs-string">'Build Package'</span>) {
            steps {
                sh <span class="hljs-string">""</span><span class="hljs-string">"
                mvn clean install
                "</span><span class="hljs-string">""</span>
            }
        }

        stage(<span class="hljs-string">'Archive Package'</span>) {
            steps {
                archiveArtifacts artifacts: <span class="hljs-string">"<span class="hljs-variable">${AEM_PACKAGE}</span>"</span>, fingerprint: <span class="hljs-literal">true</span>
            }
        }

        stage(<span class="hljs-string">'Deploy to AEM Author'</span>) {
            steps {
                // Inject AEM credentials securely
                withCredentials([usernamePassword(
                        credentialsId: <span class="hljs-string">'aem-credentials'</span>,
                        usernameVariable: <span class="hljs-string">'AEM_USER'</span>,
                        passwordVariable: <span class="hljs-string">'AEM_PASS'</span>
                )]) {
                    sh <span class="hljs-string">""</span><span class="hljs-string">"
                        mvn com.day.jcr.vault:content-package-maven-plugin:1.0.2:install \
                          -Dvault.file=<span class="hljs-variable">${AEM_PACKAGE}</span> \
                          -Daem.host=<span class="hljs-variable">$AEM_HOST</span> \
                          -Daem.port=<span class="hljs-variable">$AUTHOR_AEM_PORT</span> \
                          -Dvault.user=<span class="hljs-variable">$AEM_USER</span> \
                          -Dvault.password=<span class="hljs-variable">$AEM_PASS</span>
                    "</span><span class="hljs-string">""</span>
                }
            }
        }

        stage(<span class="hljs-string">'Deploy to AEM Publisher'</span>) {
            steps {
                // Inject AEM credentials securely
                withCredentials([usernamePassword(
                        credentialsId: <span class="hljs-string">'aem-credentials'</span>,
                        usernameVariable: <span class="hljs-string">'AEM_USER'</span>,
                        passwordVariable: <span class="hljs-string">'AEM_PASS'</span>
                )]) {
                    sh <span class="hljs-string">""</span><span class="hljs-string">"
                        mvn com.day.jcr.vault:content-package-maven-plugin:1.0.2:install \
                          -Dvault.file=<span class="hljs-variable">${AEM_PACKAGE}</span> \
                          -Daem.host=<span class="hljs-variable">$AEM_HOST</span> \
                          -Daem.port=<span class="hljs-variable">$PUBLISHER_AEM_PORT</span> \
                          -Dvault.user=<span class="hljs-variable">$AEM_USER</span> \
                          -Dvault.password=<span class="hljs-variable">$AEM_PASS</span>
                    "</span><span class="hljs-string">""</span>
                }
            }
        }
    }

    post {
        success {
            <span class="hljs-built_in">echo</span> <span class="hljs-string">'✅ Build &amp; Deploy successful!'</span>
        }
        failure {
            <span class="hljs-built_in">echo</span> <span class="hljs-string">'❌ Build failed!'</span>
        }
    }
}
</code></pre>
<p>Once finished, you can click on "Build Now" to start running the Jenkins pipeline. You will see a similar page once it is completed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755704041785/6630dc6d-5598-4e5b-92b0-94449ad7bd29.png" alt class="image--center mx-auto" /></p>
<p>That's it! We successfully set up Jenkins on Docker, checked out the AEM code from Git, and deployed it to running AEM instances.</p>
<p>Do comments, if you face any issue while implementing this. 🚀🚀🚀</p>
]]></content:encoded></item><item><title><![CDATA[Boosting AEM Website Performance with Smart Imaging]]></title><description><![CDATA[Page load time is one of the critical factors that significantly impacts the customer experience on your website, mobile site, or app. Customers will often abandon a site or app if a page takes too long to load.
This can have a significant impact on ...]]></description><link>https://aemslate.com/boosting-aem-website-performance-with-smart-imaging</link><guid isPermaLink="true">https://aemslate.com/boosting-aem-website-performance-with-smart-imaging</guid><category><![CDATA[AEM]]></category><category><![CDATA[#Adobe]]></category><dc:creator><![CDATA[Raushan Jaiswal]]></dc:creator><pubDate>Wed, 04 Oct 2023 07:58:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1696405582891/23ce6910-f754-42a6-b38b-645e1f35e045.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Page load time is one of the critical factors that significantly impacts the customer experience on your website, mobile site, or app. Customers will often abandon a site or app if a page takes too long to load.</p>
<p>This can have a significant impact on key business metrics such as conversion rates, time spent on a site, and lower bounce rates, as images are often a major contributor to page load time.</p>
<p>Smart Imaging in Dynamic Media Classic enhances image delivery performance by automatically optimizing image format and quality based on client browser capabilities. It reduces image sizes by <strong>30 percent</strong> or more.</p>
<p><strong>If you are using AEM Sites and have Dynamic Media available to you, you can take advantage of Smart Imaging.</strong></p>
<p>Smart Imaging technology applies Adobe Sensei AI capabilities and works with existing "image presets".</p>
<h3 id="heading-how-does-smart-imaging-operate"><strong>How does Smart Imaging Operate?</strong></h3>
<p>When an image is requested by a consumer, Smart Imaging checks the user characteristics and converts it to the appropriate image format based on the browser in use. These format conversions are done in a manner that does not degrade visual fidelity. Smart imaging automatically converts images to different formats based on browser capability in the following manner.</p>
<ul>
<li><p>Automatically convert to AVIF if the browser supports the format.</p>
</li>
<li><p>Automatically convert to WebP if AVIF conversion is not beneficial or the browser does not support AVIF.</p>
</li>
<li><p>Automatically convert to JPEG2000 if Safari does not support WebP.</p>
</li>
<li><p>Automatically convert to JPEGXR for IE 9+ or if Edge does not support WebP.</p>
</li>
</ul>
<h3 id="heading-steps-to-how-to-use-smart-imaging"><strong>Steps to How To Use Smart Imaging</strong></h3>
<p><strong>Step 1</strong>:- <strong>Enable Smart Imaging</strong></p>
<p>You need to create an Adobe support ticket to enable Smart Imaging on your account. <a target="_blank" href="https://helpx.adobe.com/enterprise/using/support-for-experience-cloud.html">More details</a> on the support ticket</p>
<p>Adobe support person will enable a domain for like as <strong>mycompany.scene7.com</strong> or images.company.com and not a generic domain, such as <a target="_blank" href="http://s7d1.scene7.com">s7d1.scene7.com</a>, <a target="_blank" href="http://s7d2.scene7.com">s7d2.scene7.com</a>, <a target="_blank" href="http://s7d13.scene7.com">s7d13.scene7.com</a></p>
<p><strong>Step 2: Verify Domain</strong></p>
<p>To find your domains, open the <a target="_blank" href="https://experienceleague.adobe.com/docs/dynamic-media-classic/using/getting-started/signing-out.html?lang=en">Dynamic Media Classic desktop application</a>, then sign in to your company account or accounts. Go to <strong>Setup</strong> &gt; <strong>Application Setup</strong> &gt; <strong>General Settings</strong>.</p>
<p>Look for the field labeled <strong>Published Server Name</strong>. If you are currently using a generic Dynamic Media Classic domain, you can request moving over to your own custom domain as part of this transition.</p>
<p><strong>Step 3: Re-Configure Dynamic Media General Settings</strong></p>
<p>To utilize the smart image domain within AEM. You have to change the Published Server Name in Dynamic Media General Setting.</p>
<p>Go to <strong>aem/start.html</strong> &gt; <strong>Tools</strong> &gt; <strong>Assets</strong> &gt;<strong>Dynamic Media General Settings</strong>\&gt; change the <strong>Publish server Name</strong> to smart image domain.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696344085889/6fd4a5ea-f51b-49a2-a280-a3ad6b23e257.png" alt class="image--center mx-auto" /></p>
<p><strong>Note:</strong> <em><mark>You</mark></em> <em><mark>need to reprocess your assets to utilize this smart image domain</mark></em></p>
<p><strong>Step 4: Validation</strong></p>
<ol>
<li><p>After your account is configured with Smart Imaging, load the Dynamic Media image URL on the browser.</p>
</li>
<li><p>Open the Chrome developer pane by going to <strong>View</strong> &gt; <strong>Developer</strong> &gt; <strong>Developer Tools</strong> in the browser. Or, choose any browser developer tool of your choice.</p>
</li>
<li><p>Ensure that the cache is disabled when developer tools are open.</p>
<ul>
<li><p>On Windows®, navigate to settings in the developer tool pane, then select the <strong>Disable cache (while devtools is open)</strong> check box.</p>
</li>
<li><p>On macOS, in the developer pane, under the <strong>Network</strong> tab, select <strong>disable cache</strong>.</p>
</li>
</ul>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1696345263846/18a29b3f-62c1-45a1-9327-fcdf44b59abd.png" alt class="image--center mx-auto" /></p>
<p>Once Smart Imaging is enabled, after you request an image, under the <strong>Response Headers</strong> you will see the Request URL and <code>-X-Adobe-Smart-Imaging</code> as seen in the above highlighted example:</p>
<p>This response header tells you the following</p>
<ul>
<li><p>Smart Imaging is working.</p>
</li>
<li><p>A positive value means that the conversion is successful. In this case, a new WebP image is returned.</p>
</li>
<li><p>A negative value means that the conversion is not successful. In such a case, the original requested image is returned (JPEG by default, if not specified).</p>
</li>
<li><p>A positive value shows the difference in bytes between the requested image and the new image. In the example above, the bytes saved is <code>62689</code> or approximately 62 KB for one image.</p>
</li>
<li><p>A negative value means that the requested image is smaller than the new image. The negative size difference is shown, but the image served is the original requested image only.</p>
</li>
</ul>
<p><strong>Key benefits of Smart Imaging?</strong></p>
<ol>
<li><p><strong>Image Optimization</strong>: Smart imaging technologies can automatically optimize images for various factors like size, format, and quality. This leads to improved website and app performance, faster loading times, and reduced bandwidth usage.</p>
</li>
<li><p><strong>Enhanced User Experience</strong>: Optimized images ensure a better user experience by reducing page load times and providing visually appealing content, which can increase user engagement and reduce bounce rates.</p>
</li>
<li><p><strong>Responsive Design</strong>: Smart imaging can adapt images to fit different screen sizes and resolutions, making websites and apps more responsive and accessible across a wide range of devices.</p>
</li>
<li><p><strong>Improved SEO</strong>: Optimized images with proper metadata and alt tags can enhance search engine optimization (SEO) efforts, leading to better visibility and rankings in search results.</p>
</li>
<li><p><strong>Bandwidth Efficiency</strong>: By reducing image sizes, smart imaging minimizes data consumption, benefiting users with limited bandwidth or on slower networks. This can be especially crucial in mobile applications.</p>
</li>
<li><p><strong>Quality Retention</strong>: Despite image compression and optimization, smart imaging technologies aim to maintain image quality, ensuring that visuals remain sharp and clear.</p>
</li>
<li><p>It Serves optimized content immediately (at runtime).</p>
</li>
</ol>
<p><strong>In summary</strong>, smart imaging plays a crucial role in optimizing digital content, improving user experiences, reducing costs, and enhancing various aspects of web and app development and management.</p>
<p>It's impressive how Smart Imaging does all the work on its own, and consumers don't need to do anything—it's effortless and smooth.</p>
<p><strong>Note:</strong> Smart Imaging requires that you use the out-of-the-box CDN (Content Delivery Network) that is bundled with Adobe Experience Manager - Dynamic Media. Any other custom CDN is not supported with this feature.</p>
<p>If you have any questions, please don't hesitate to ask! <a target="_blank" href="https://www.linkedin.com/in/raushan-jaiswal-79653392/"><strong>Linkedin</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Unlocking AEM Cloud  Power: 
Step-by-Step Guide to Creating System or Service User( AEM CaaS)]]></title><description><![CDATA[Why do we need a System or Service User?
A system user is typically utilized by the AEM backend code which has privileges to create, read, update, and delete nodes in the JCR using a session.
Developers have to close the session after the tasks are p...]]></description><link>https://aemslate.com/aem-cloud-system-service-user-guide</link><guid isPermaLink="true">https://aemslate.com/aem-cloud-system-service-user-guide</guid><category><![CDATA[AEM]]></category><category><![CDATA[#Adobe]]></category><category><![CDATA[aaemcaas]]></category><dc:creator><![CDATA[Raushan Jaiswal]]></dc:creator><pubDate>Wed, 27 Sep 2023 08:43:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1695803484230/fffb10e0-6f43-4b9a-866e-108bdd69ef08.tif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Why do we need a System or Service User?</strong></p>
<p>A system user is typically utilized by the AEM backend code which has privileges to create, read, update, and delete nodes in the JCR using a session.</p>
<p>Developers have to close the session after the tasks are performed.</p>
<p>If you forget to close the sessions that you've opened, your AEM can get overwhelmed with too many open sessions. This can cause your AEM to run out of memory and crash. In simpler terms, not closing sessions properly can lead to memory issues and system crashes in AEM.To learn more about this topic, take a look at this blog How to Close <a target="_blank" href="https://cqdump.joerghoh.de/2018/11/12/resourceresolvers-and-sessions-you-open-it-you-close-it/">ResourceResolvers, and Sessions</a></p>
<p><strong>How to create a system user or service User</strong></p>
<p>Creating a service user in Adobe Experience Manager (AEM) as a Cloud Service requires configuring the RepositoryInitializer</p>
<p><strong>What is repoinit (RepositoryInitializer configuration)?</strong></p>
<p>Repoinit is a tool for setting up a content repository in a specific way. It is like a recipe that tells the repository how to create nodes, properties, service users, and access control policies, as well as register JCR namespaces, node types, and privileges.</p>
<p>Full documentation is found <a target="_blank" href="https://sling.apache.org/documentation/bundles/repository-initialization.html">here</a>.</p>
<p>Here is a simple example of a repoinit script:</p>
<pre><code class="lang-yaml">{
    <span class="hljs-string">"create node /content/my-site"</span>
    <span class="hljs-string">"create property /content/my-site/title "</span><span class="hljs-string">My</span> <span class="hljs-string">Site"</span>
    <span class="hljs-string">"create service user mySystemUser"</span>
    <span class="hljs-string">"grant permission to mySystemUser to read and write /content/my-site"</span>
}
</code></pre>
<p>Here are the <strong>steps</strong> to create a service user in AEM Cloud:</p>
<p><strong>Steps 1:</strong> Set up the "org.apache.sling.jcr.repoinit.RepositoryInitializer" PID configuration.</p>
<p><strong>filename:</strong> <mark>apps/mysite/osgiconfig/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~mySystemUser.cfg.json</mark></p>
<pre><code class="lang-yaml">{
  <span class="hljs-attr">"scripts":</span> [
    <span class="hljs-string">"create path (sling:OrderedFolder) /content/dam/mysite"</span>,
    <span class="hljs-string">"create path (nt:unstructured) /content/dam/mysite/jcr:content"</span>,
    <span class="hljs-string">"create path (cq:Page) /content/mysite"</span>,
    <span class="hljs-string">"create path (cq:PageContent) /content/mysite/jcr:content"</span>,
    <span class="hljs-string">"create path (sling:Folder) /conf/mysite"</span>,
    <span class="hljs-string">"create service user mySystemUser with path /home/users/system/mysite"</span>,
    <span class="hljs-string">"set ACL for mySystemUser \r\n  allow jcr:all on /content/mysite\r\n  allow jcr:all on /conf/mysite\r\n  allow jcr:all on /content/dam/mysite\nend"</span>
  ]
}
</code></pre>
<p><strong>Description of Step 1:</strong></p>
<p>This script enables the creation of a system user named "mySystemUser," which will be situated within the folder structure at /home/users/system/mysite.</p>
<p>The system user, mySystemUser, will have ACL (Access Control List) privileges configured to:</p>
<ul>
<li><p>allow read, write, and replicate for all JCR resources that exist under /content/mysite</p>
</li>
<li><p>allow read, write, and replicate for all JCR resources that exist under /conf/mysite</p>
</li>
</ul>
<p><strong>Steps 2:</strong> Set up the "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended " PID</p>
<p><strong>filename:</strong></p>
<p><mark>apps/mysite/osgiconfig/config/org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~mySystemUser.cfg.json</mark></p>
<pre><code class="lang-yaml">{
  <span class="hljs-attr">"user.mapping":</span> [
    <span class="hljs-string">"mysite-core.core:mySystemUser=[mySystemUser]"</span>
  ]
}
</code></pre>
<p><strong>Deployment: -</strong></p>
<p>Make this change and deploy it locally, you will able to see the service or system user inside this folder (/home/users/system/mysite)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1695803028380/e622ca79-dc99-4315-ba39-673b62196a7b.png" alt class="image--center mx-auto" /></p>
<p><strong><mark>Note: </mark></strong> <mark>If you are not able to see system users in your local please restart your AEM.</mark></p>
<p><strong>Sample code</strong> to get a resource resolver using <em>mySystemUser</em> as the Sling sub-service</p>
<pre><code class="lang-java"><span class="hljs-keyword">final</span> Map&lt;String, Object&gt; authInfo =
        ImmutableMap.of(ResourceResolverFactory.SUBSERVICE, <span class="hljs-string">"mySystemUser"</span>);
<span class="hljs-keyword">try</span> (ResourceResolver resolver =
        resourceResolverFactory.getServiceResourceResolver(authInfo)) {

    <span class="hljs-keyword">final</span> Resource blog = resolver.getResource(<span class="hljs-string">"/content/mysite/blog"</span>);
    logger.info(<span class="hljs-string">"got {}"</span>, blog.getPath());

} <span class="hljs-keyword">catch</span> (LoginException e){
    logger.error(<span class="hljs-string">"could not open resource resolver"</span>, e);
}
</code></pre>
<p><strong>Common Errors occurs related to repoinit configurations in AEM</strong> <strong>:</strong></p>
<p>Sometimes you may find that your AEMaaCS pipelines are failing due to the <strong>repoinit</strong></p>
<p>To Troubleshoot that error please check the below points</p>
<ol>
<li><p><strong>Syntax Errors</strong>: Incorrect syntax in repoinit scripts can lead to parsing errors. Ensure that the script is properly formatted and follows the repoinit language rules.</p>
</li>
<li><p><strong>Invalid Paths</strong>: If the paths specified in repoinit scripts do not exist or are incorrect, you may encounter errors when trying to apply the configuration.</p>
</li>
<li><p><strong>Access Denied</strong>: Insufficient permissions for the user executing the repoinit script can result in errors when trying to make changes to the repository.</p>
</li>
<li><p><strong>Node Overwrite</strong>: If the repoinit script attempts to create a node that already exists without specifying whether it should be overwritten, it can cause errors.</p>
</li>
<li><p><strong>Incompatible Actions</strong>: Some actions specified in repoinit scripts may be incompatible with the existing repository state or configurations. This can lead to conflicts and errors.</p>
</li>
<li><p><strong>Timing Issues</strong>: Repoinit scripts are executed during repository startup or when triggered manually. If the script depends on certain conditions that are not met at the time of execution, errors can occur.</p>
</li>
<li><p><strong>Resource Dependencies</strong>: If the repoinit script relies on external resources (e.g., files, services) that are unavailable or misconfigured, it can result in errors.</p>
</li>
<li><p><strong>Logging and Debugging</strong>: Inadequate logging and debugging in the repoinit script can make it challenging to diagnose and troubleshoot errors.</p>
</li>
<li><p><strong>Conflict Resolution</strong>: If multiple repoinit scripts are applied, they may conflict with each other or with existing repository configurations. Resolving these conflicts is essential to prevent errors.</p>
</li>
<li><p><strong>Version Compatibility</strong>: Repoinit scripts may not be compatible with the version of AEM being used, leading to errors or unexpected behavior.</p>
</li>
</ol>
<p>To address these issues, it's crucial to thoroughly review and test your repoinit scripts, ensure that they align with your AEM environment's requirements, and monitor the logs for any error messages or warnings during script execution. Additionally, refer to the official <a target="_blank" href="https://sling.apache.org/documentation/bundles/repository-initialization.html">AEM documentation</a> and best practices for repoinit configuration to avoid common pitfalls.</p>
<p>If you have any questions, please don't hesitate to ask! <a target="_blank" href="https://www.linkedin.com/in/raushan-jaiswal-79653392/"><strong>Linkedin</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[AEM Cloud Service Log Tailing: Troubleshooting and Monitoring Tips]]></title><description><![CDATA[Tailing AEM (Adobe Experience Manager) logs in AEM Cloud Service is a common task for troubleshooting and monitoring purposes. AEM Cloud Service is a managed service provided by Adobe that runs AEM in a cloud environment, and accessing logs may diffe...]]></description><link>https://aemslate.com/aem-cloud-service-log-tailing-troubleshooting-and-monitoring-tips</link><guid isPermaLink="true">https://aemslate.com/aem-cloud-service-log-tailing-troubleshooting-and-monitoring-tips</guid><category><![CDATA[AEM]]></category><category><![CDATA[#Adobe]]></category><category><![CDATA[Logs]]></category><dc:creator><![CDATA[Raushan Jaiswal]]></dc:creator><pubDate>Tue, 26 Sep 2023 15:24:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1695744101885/f7d418e7-ac40-46dc-86b7-cfe8758f4334.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Tailing AEM (Adobe Experience Manager) logs in AEM Cloud Service is a common task for troubleshooting and monitoring purposes. AEM Cloud Service is a managed service provided by Adobe that runs AEM in a cloud environment, and accessing logs may differ slightly from an on-premises setup. Here's a general process to tail AEM logs in AEM Cloud Service:</p>
<p>So, here are the steps to go through to tail logs on an AEM as a Cloud Service environment:</p>
<p><strong>Step 1: Set up Adobe I/O CLI</strong></p>
<p>Launch the Command Prompt/Terminal and install Adobe I/O CLI. More details on <a target="_blank" href="https://github.com/adobe/aio-cli-plugin-cloudmanager">Adobe IO CLI here</a></p>
<p>Check whether Node is installed or not if it's not please install. More details on installation <a target="_blank" href="https://nodejs.org/en/download/package-manager">Node</a></p>
<pre><code class="lang-plaintext">$ npm install -g @adobe/aio-cli
</code></pre>
<p>Post Install of adobe/ aio-cli</p>
<pre><code class="lang-plaintext">$ aio info
output: - 
rj00752505@BGINCHAMLP00792 ~ % aio info

  System:
    OS: macOS 13.4.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 87.38 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Virtualization:
    Docker: Not Found
  npmGlobalPackages:
    @adobe/aio-cli: 9.3.0
</code></pre>
<p><strong>Step 2: Configure the Adobe IO Cloud Manager CLI Plugin</strong></p>
<p>Installation</p>
<pre><code class="lang-plaintext">aio plugins:install @adobe/aio-cli-plugin-cloudmanager
</code></pre>
<p>Updating</p>
<pre><code class="lang-plaintext">$ aio plugins:update
</code></pre>
<p><strong>Step 3: Authentication</strong></p>
<p>a) Browser-Based Authentication - Browser-based authentication starts by running this command.</p>
<pre><code class="lang-plaintext">aio auth:login
</code></pre>
<p>This command will open a browser window in which you will authenticate using your Adobe Identity.</p>
<p><strong><em>Note:</em></strong> <em>You can choose any type of authentication</em>***.***</p>
<p>b) <a target="_blank" href="https://github.com/adobe/aio-cli-plugin-cloudmanager#service-account-authentication">S</a>ervice Account Authentication To use a service account authentication, an integration (aka project) must be created in the <a target="_blank" href="https://console.adobe.io/">Adobe I/O Console</a> which has the Cloud Manager service.</p>
<p><em>The required type of</em> <a target="_blank" href="https://developer.adobe.com/developer-console/docs/guides/authentication/ServerToServerAuthentication/implementation/"><em>server-to-server authentication</em></a></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"client_id"</span>: <span class="hljs-string">"value from your CLI integration (String)"</span>,
  <span class="hljs-attr">"client_secret"</span>: <span class="hljs-string">"value from your CLI integration (String)"</span>,
  <span class="hljs-attr">"jwt_payload"</span>: { value from your CLI integration (JSON Object Literal) },
  <span class="hljs-attr">"token_exchange_url"</span>: <span class="hljs-string">"https://ims-na1.adobelogin.com/ims/exchange/jwt"</span>
}
</code></pre>
<p><strong>Step 4: Setting Up the Default Program and Default Environment</strong></p>
<p><strong>To Find the Env and Program id</strong> - Navigate to <a target="_blank" href="http://my.cloudmanager.adobe.com">my.cloudmanager.adobe.com</a>, you’ll see a URL after logging in like “<a target="_blank" href="https://experience.adobe.com/#/@the_cool_name_of_my_company/cloud-manager/home.html/program/%7BPROGRAM_NUMBER%7D%E2%80%9D">https://experience.adobe.com/#/@the_cool_name_of_my_company/cloud-manager/home.html/program/{PROGRAM_NUMBER}”</a></p>
<pre><code class="lang-plaintext">$ aio config:set cloudmanager_programid 34562 // setting up default program
$ aio config:set cloudmanager_environmentid 124551 //setting up default env
</code></pre>
<p><strong>Useful Command</strong></p>
<ol>
<li>To List list all the program</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-string">$</span> <span class="hljs-string">aio</span> <span class="hljs-string">cloudmanager:list-programs</span>

 <span class="hljs-string">Program</span> <span class="hljs-string">Id</span>    <span class="hljs-string">Name</span>                <span class="hljs-string">Enabled</span> 
 <span class="hljs-string">──────────</span> <span class="hljs-string">─────────────────────</span> <span class="hljs-string">───────</span>  
 <span class="hljs-number">43674</span>       <span class="hljs-string">sandbox-B2c</span>              <span class="hljs-literal">true</span>    
 <span class="hljs-number">34562</span>       <span class="hljs-string">sandbox-B2b</span>              <span class="hljs-literal">true</span>
</code></pre>
<ol>
<li>To List all the Environments</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-string">$</span>  <span class="hljs-string">aio</span> <span class="hljs-string">cloudmanager:list-environments</span> 

 <span class="hljs-string">Environment</span> <span class="hljs-string">Id</span> <span class="hljs-string">Name</span>                                  <span class="hljs-string">Type</span>  <span class="hljs-string">Description</span>       
 <span class="hljs-string">──────────────</span> <span class="hljs-string">─────────────────────────────────────</span> <span class="hljs-string">─────</span> <span class="hljs-string">─────────────────</span>
 <span class="hljs-number">124551</span>          <span class="hljs-string">demo</span>                          <span class="hljs-string">dev</span>   <span class="hljs-string">dev</span> <span class="hljs-string">environment</span> <span class="hljs-string">n</span>          
 <span class="hljs-number">9845628</span>         <span class="hljs-string">demo-prod</span>                     <span class="hljs-string">prod</span>  <span class="hljs-string">prod</span>   <span class="hljs-string">This</span> <span class="hljs-string">is</span> <span class="hljs-string">prod</span> <span class="hljs-string">env.</span>
</code></pre>
<ol>
<li>To List list all the log-options</li>
</ol>
<pre><code class="lang-yaml"><span class="hljs-string">$</span> <span class="hljs-string">aio</span> <span class="hljs-string">cloudmanager:list-available-log-options</span> <span class="hljs-number">124551</span> 
 <span class="hljs-string">Environment</span> <span class="hljs-string">Id</span> <span class="hljs-string">Service</span>            <span class="hljs-string">Name</span>          
 <span class="hljs-string">──────────────</span> <span class="hljs-string">──────────────────</span> <span class="hljs-string">─────────────</span> 
 <span class="hljs-number">124551</span>         <span class="hljs-string">preview_dispatcher</span> <span class="hljs-string">httpdaccess</span>   
 <span class="hljs-number">124551</span>         <span class="hljs-string">preview_dispatcher</span> <span class="hljs-string">httpderror</span>    
 <span class="hljs-number">124551</span>         <span class="hljs-string">preview_dispatcher</span> <span class="hljs-string">aemdispatcher</span> 
 <span class="hljs-number">124551</span>         <span class="hljs-string">author</span>             <span class="hljs-string">aemaccess</span>     
 <span class="hljs-number">124551</span>         <span class="hljs-string">author</span>             <span class="hljs-string">aemerror</span>      
 <span class="hljs-number">124551</span>         <span class="hljs-string">author</span>             <span class="hljs-string">aemrequest</span>
</code></pre>
<p>4)To Tailing the Author error log</p>
<pre><code class="lang-yaml"><span class="hljs-string">$</span>  <span class="hljs-string">aio</span> <span class="hljs-string">cloudmanager:tail-log</span> <span class="hljs-number">121351</span> <span class="hljs-string">author</span> <span class="hljs-string">aemerror</span>
<span class="hljs-number">25.09</span><span class="hljs-number">.2023</span> <span class="hljs-number">15</span><span class="hljs-string">:34:56.428</span> [<span class="hljs-string">cm-p33452-e121351-aem-author-54c7ddc56c-vf8vb</span>] <span class="hljs-string">*INFO*</span> [<span class="hljs-string">sling-oak-observation-5</span>] <span class="hljs-string">com.adobe.cq.ups.user.utils.UserCreationListener</span> <span class="hljs-string">UPS</span> <span class="hljs-string">Feature</span> <span class="hljs-string">is</span> <span class="hljs-string">disabled</span>
<span class="hljs-number">25.09</span><span class="hljs-number">.2023</span> <span class="hljs-number">15</span><span class="hljs-string">:34:56.452</span> [<span class="hljs-string">cm-p33452-121351-aem-author-54c7ddc56c-vf8vbJ</span> <span class="hljs-string">*INFO*</span> [<span class="hljs-number">188.24</span><span class="hljs-number">.112</span><span class="hljs-number">.253</span> [<span class="hljs-number">1695656096450</span>] <span class="hljs-string">GET</span> <span class="hljs-string">/mnt/overlay/granite/ui/content/shell/header/actions/pulse.data.jsonHTTP/1.11com.day.ca.wcm.core.imp1.designer.searchPathlimiter</span>
<span class="hljs-attr">Search path limiter configured with searchPathLimiterFeatureToggleOn: true and searchPathThreshold:</span> <span class="hljs-literal">true</span>
<span class="hljs-number">25.09</span><span class="hljs-number">.2023</span> <span class="hljs-number">15</span><span class="hljs-string">:34:56.469</span> [<span class="hljs-string">cm-p33452-e121351-aem-author-54c7ddc56c-vf8vb</span>] <span class="hljs-string">*INFO*</span> [<span class="hljs-number">188.24</span><span class="hljs-number">.112</span><span class="hljs-number">.253</span> [<span class="hljs-number">1695656096468</span>] <span class="hljs-string">GET</span> <span class="hljs-string">/mnt/overlay/granite/ui/content/shell/header/actions/pulse.data.jsonHTTP/1.11com.day.cq.wcm.core.imp1.designer.SearchPathLimiter</span>
<span class="hljs-attr">Search path limiter configured with searchPathLimiterFeatureToggleOn: true and searchPathThreshold:</span> <span class="hljs-string">true.</span>
</code></pre>
<p>Similarly, you can tail any logs.</p>
<p>If you have any questions, please don't hesitate to ask! <a target="_blank" href="https://www.linkedin.com/in/raushan-jaiswal-79653392/">Linkedin</a></p>
]]></content:encoded></item><item><title><![CDATA[AEM - Groovy script to add rep:policy based on Query]]></title><description><![CDATA[Recently, I came across a requirement where we need to add rep:policy to a large number of nodes. We clearly understood that it wouldn't be possible to do it via the usual AEM GUI, as it would be time-consuming work. So, we tried doing it via the Gro...]]></description><link>https://aemslate.com/aem-groovy-script-to-add-reppolicy-based-on-query</link><guid isPermaLink="true">https://aemslate.com/aem-groovy-script-to-add-reppolicy-based-on-query</guid><category><![CDATA[AEM]]></category><category><![CDATA[groovy]]></category><category><![CDATA[automation]]></category><category><![CDATA[adobe experience manager implementation]]></category><dc:creator><![CDATA[Ashish Mishra]]></dc:creator><pubDate>Mon, 25 Sep 2023 17:35:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753257617288/3b5eaa4b-9776-4acb-b1f5-2b051051ea21.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently, I came across a requirement where we need to add <strong>rep:policy</strong> to a large number of nodes. We clearly understood that it wouldn't be possible to do it via the usual AEM GUI, as it would be time-consuming work. So, we tried doing it via the Groovy script, where we first found all the nodes via xpath query and then added the allow or deny policies using the <strong>AccessControlManager.</strong></p>
<p>Groovy example below contains an XPath query that finds all the pages under <code>"/content/geeksdemo"</code> with template <code>"/conf/geeksdemo/settings/wcm/templates/page"</code> and adds a <strong>deny</strong> permission of <strong>delete</strong> to the <strong>admin-test</strong> group members.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="c5b63d250761f16dbef336f9f443af9a"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/a-blank-slate/c5b63d250761f16dbef336f9f443af9a" class="embed-card">https://gist.github.com/a-blank-slate/c5b63d250761f16dbef336f9f443af9a</a></div><p> </p>
<p>Same way, other permissions under the <a target="_blank" href="https://developer.adobe.com/experience-manager/reference-materials/spec/jsr170/javadocs/jcr-2.0/javax/jcr/security/Privilege.html"><code>Privilege</code></a><code>interface</code> can be added.</p>
<p>That's a wrap. Let me know your feedback on this. Please follow for more such tricks and tutorials.</p>
]]></content:encoded></item><item><title><![CDATA[Unlimited Free Email Address using Cloudflare and your Free Custom Domain]]></title><description><![CDATA[We all have faced this issue where we need to provide our email id for registration purposes on different websites. Sometimes we think we can avoid giving our personal or business email addresses as this can result in spam emails. 
Now, on the intern...]]></description><link>https://aemslate.com/unlimited-free-email-address-using-cloudflare-and-your-free-custom-domain</link><guid isPermaLink="true">https://aemslate.com/unlimited-free-email-address-using-cloudflare-and-your-free-custom-domain</guid><category><![CDATA[email]]></category><category><![CDATA[tricks]]></category><category><![CDATA[free]]></category><category><![CDATA[cloudflare]]></category><dc:creator><![CDATA[Ashish Mishra]]></dc:creator><pubDate>Sun, 08 May 2022 07:39:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753257477736/c84698a6-efd4-416c-b3e6-6aca061031fe.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We all have faced this issue where we need to provide our email id for registration purposes on different websites. Sometimes we think we can avoid giving our personal or business email addresses as this can result in spam emails. </p>
<p>Now, on the internet, we know there are many websites that provide free temporary email addresses. No doubt those email addresses will do your job, but there are some disadvantages to them, such as they are temporary addresses so cant be used for the long term, second you need to create those addresses beforehand to fill in the website. Now, as this trick is very well known, many sites block addresses from these websites' domain names. </p>
<p>There is another way to generate an unlimited Gmail email address by adding a (+) plus sign. for example - <strong>abc+xyz@gmail.com</strong> or <strong>abc+123@gmail.com</strong>, these all will go to <strong>abc@gmail.com</strong>. This method will work on most sites if they don't have a check on plus sign with gmail.com addresses. </p>
<p>Cloudflare Trick:
We will use the free domain from the freedom website for this tutorial, if you already have a domain name registered with Cloudflare, then go to step 3.</p>
<p><strong> Step 1:</strong>   <strong>Get Free Domain Name: </strong> Go to <a target="_blank" href="https://www.freenom.com/">freenom. com</a>, and register for an account. Once login to the website, search for a domain name that is available, then select the <strong>.tk </strong> option and checkout. This domain is free of cost and valid for 1 year. On the checkout page, you can change the duration from 3 months to 12 months and then proceed for checkout. You will get your free domain name now. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651989311235/X7AnSVa7t.gif" alt="freenom.gif" class="image--center mx-auto" /></p>
<p><strong>Step 2:</strong>  Login or create a <a target="_blank" href="https://dash.cloudflare.com/login">Cloudflare</a> account. Add your domain name to the Cloudflare dashboard. Now, Cloudflare will search for the existing domain name record for this domain and then ask you to modify it to point to Cloudflare name servers. Copy the name servers from Cloudflare, and go to freedom, domain management and replace the name servers. You need to wait for a few minutes till Cloudflare picks the new changes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651991340568/gHSypxLDL.gif" alt="cloudflare-nameservers-change.gif" /></p>
<p><strong>Step 3:</strong>  Now, go to the email section of Cloudflare, it will show some records are missing, you can click on a skip for now. Click on the Route tab and then click on Enable Email Routing. Cloudflare will show some options you just need to accept those recommendations and apply. Finally, it will give you an "Email Routing is enabled and routing emails." message. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651992112199/_0ULGkFs0.gif" alt="enable-email-record.gif" /></p>
<p><strong>Step 4:</strong>  All set to create email addresses. Go to the Routes tab and scroll to the destination email address section, add your email address where you want to receive all emails for this domain. Once you verify the destination email address </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651995048178/VwUtoDH5u.png" alt="image.png" /></p>
<p>Go to the "Catch-all address" section and enable it, click on edit and select send to option and from the next drop down select your email address which you have verified. Click Save. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1651995180255/9Ud85Evvq.png" alt="image.png" /></p>
<p>Now, you are set to use any email address from this domain. Like if you receive an email on hashnode@domainname.tk, devto@domainname.tk, or medium@domainname.tk. These all will land in your primary email address which you verified in cloudflare. </p>
<p>Now, while signing up on any website, you can add any email address on that website and the email will land on your webmail.</p>
<p>That's a wrap. Let me know your feedback on this. Please follow for more such tricks and tutorials.</p>
]]></content:encoded></item><item><title><![CDATA[Fetch Unread Emails to Slack using Slash Commands Bot]]></title><description><![CDATA[Slack is one of the most popular messaging tools. In this article, we will connect our free slack account with Gmail using Slash commands and Google Scripts to pull our unread emails. Many times we face situations where Gmail is blocked because of se...]]></description><link>https://aemslate.com/fetch-unread-emails-to-slack-using-slash-commands-bot</link><guid isPermaLink="true">https://aemslate.com/fetch-unread-emails-to-slack-using-slash-commands-bot</guid><category><![CDATA[bot]]></category><category><![CDATA[slack]]></category><category><![CDATA[gmail]]></category><category><![CDATA[bots]]></category><category><![CDATA[Script]]></category><dc:creator><![CDATA[Ashish Mishra]]></dc:creator><pubDate>Sun, 17 Apr 2022 13:29:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753256696719/0ddef9c7-ed54-4d8b-9468-f0a81a9c327d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Slack is one of the most popular messaging tools. In this article, we will connect our free slack account with Gmail using Slash commands and Google Scripts to pull our unread emails. Many times we face situations where Gmail is blocked because of security in some workplaces and machines. Using this we will be able to view our emails directly in Slack.</p>
<h2 id="heading-slack-account">Slack Account</h2>
<p>You need to have a slack account to follow this tutorial, you can create a free account from here <a target="_blank" href="http://slack.com/intl/en-in/">Join Slack</a></p>
<p>Once we create or log in to the slack workspace, we need to get an email address where we can forward the unread emails from Gmail.</p>
<p>Follow the steps from this <a target="_blank" href="https://slack.com/intl/en-in/help/articles/206819278-Send-emails-to-Slack#h_01F4WE06MBF06BBHQNZ1G0H2K5">slack article</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650198597684/dOIA6ehgU.png" alt="image.png" /></p>
<h2 id="heading-google-script-account">Google Script Account</h2>
<p>As we are going to use google script API to pull emails, please log into your google account and then go to <a target="_blank" href="https://script.google.com/home/start">Google Script</a>. Click on <strong>New Project</strong> on the top left screen.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650197124960/i9xDA2ahC.png" alt="image.png" /></p>
<p>A new script editing window will open, remove the dummy code and paste the below code. Save the script, and provide a name to the project.</p>
<pre><code class="lang-plaintext">function fetchUnreadGmailAndSendToSlack() {
  var threads = GmailApp.search('is:unread');
    for (var i = 0; i &lt; threads.length; i++) {
        console.log("found message with subject :: "+threads[i].getMessages()[0].getSubject());
        threads[i].getMessages()[0].forward('email-id-from-slack@team-name.slack.com');
        threads[i].getMessages()[0].markRead();
        console.log("Mail forwareded to slack.");
    }
   console.log("finished.");
}

function doPost(request){
  fetchUnreadGmailAndSendToSlack();
  console.log(request);
  var responseContent = '{"text": "Message Received, Bots are Working On ! "}';
  var response = ContentService.createTextOutput(responseContent);
  response.setMimeType(ContentService.MimeType.JSON);
  return response;
}
</code></pre>
<p>On line number 5, please change the email id to your slack email id received from the above step.</p>
<p>In this code there are two methods, as Slack makes a POST request, we have created one doPost() which will receive the request and call the fetchUnreadGmailAndSendToSlack() method. It will also reply back in the slack chat with a success message, which will indicate the request is processed successfully. fetchUnreadGmailAndSendToSlack this method will search for the unread emails using Gmail APP API. Once it finds the list, it will iterate through those emails and forward them to provided slack mail-id and then mark them as read.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650198896777/xUQrM-2xw.png" alt="image.png" /></p>
<p>Now, that we are done with coding, we need to deploy this script and get the URL using which we can execute it. Click on the Deploy button on the top right, then select New deployment. Here as it's our first deployment, we need to tell google which kind of script is this, so select the type as Web app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650199251199/w2-A2NY_y.png" alt="image.png" /></p>
<p>Then write some description, select your email id in the Execute as an option, this will make sure, whenever this script is run it will use your access, and in the last drop-down select anyone under "Who has access". Click Deploy, and then authorize. It will ask you to then select your google account, a warning will pop up, click on advanced, and then click on your project name.</p>
<p>A similar screen should come up, copy the Web app URL. Anyone with this endpoint can trigger your script.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650199653033/OVpSD1S8Q.png" alt="image.png" /></p>
<h2 id="heading-head-back-to-slack">Head Back to Slack</h2>
<p>As we have our script which will pull the unread email, we need to create an app on Slack to access this script using our slash commands. Go to this <a target="_blank" href="https://api.slack.com/apps?new_app=1">Slack APP URL</a>, click on the <strong>Create New App</strong> Button, select from scratch, enter details and click create.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650199965775/yiWAE0em9.png" alt="image.png" /></p>
<p>Now, we need to set slash commands, which will invoke our google script.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650201848845/GVP33awok.png" alt="image.png" /></p>
<p>Click on <strong>Create New Command</strong> and fill details. Text which you enter in the command box will be used to invoke this app, whereas in the Request URL enter your Google Web app Deployment URL. Add short description and hit save.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650200078746/FBzIUlFw5.png" alt="image.png" /></p>
<p>One Last Step is to install this app in your workspace. Under Settings, select Install App, authorize the app by selecting the workspace.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650200578753/vDq7gB1Cj.png" alt="image.png" /></p>
<p>Now, we are all set to pull our unread emails directly in slack. So, go to slack chat and invoke this app by typing / and then your command entered above in any chat window. In my case its <strong>/supermanfetchemails</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650200741022/2ZJZK-xV9.png" alt="image.png" /></p>
<p>You should see a message in the chat as **Message Received, Bots are Working On! ** and unread emails must have arrived from the slack bot chat.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1650201368978/cAJs1QoGU.png" alt="image.png" /></p>
<p>Now whenever you need to find your un-read emails from Gmail, you call your own superman bot. Further customization can be done in google script, as Gmail APP Api provides multiple functions.</p>
<p><strong>That's a wrap. Let me know your feedback on this.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Elastic Search Cheatsheet]]></title><description><![CDATA[Recently in my project, we started working on improving our Search Capability. We were using the couchbase full-text search and wanted to move to something more powerful and dynamic. After going through multiple suggestions, we decided to go ahead wi...]]></description><link>https://aemslate.com/elastic-search-cheatsheet</link><guid isPermaLink="true">https://aemslate.com/elastic-search-cheatsheet</guid><category><![CDATA[elasticsearch]]></category><category><![CDATA[kibana]]></category><category><![CDATA[cheatsheet]]></category><category><![CDATA[search]]></category><dc:creator><![CDATA[Ashish Mishra]]></dc:creator><pubDate>Sun, 02 Jan 2022 17:24:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753257344564/35ed8f91-797f-477c-8e2b-34de13ab1be4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently in my project, we started working on improving our Search Capability. We were using the couchbase full-text search and wanted to move to something more powerful and dynamic. After going through multiple suggestions, we decided to go ahead with Elastic Search. This post includes all the necessary APIs you would need to build a simple search on <a target="_blank" href="https://www.elastic.co/elastic-stack/">Elastic</a>, using <a target="_blank" href="https://www.elastic.co/kibana/">Kibana</a>.</p>
<p>In these all examples, we have considered our Index name as Cat.</p>
<ol>
<li>To get all the Indexes in the Elastic, use this.</li>
</ol>
<pre><code class="lang-plaintext">GET /_cat/indices?h=index
</code></pre>
<ol start="2">
<li>Now, to create an Index in Elastic, make a PUT request with the index name.</li>
</ol>
<pre><code class="lang-plaintext">PUT cat
</code></pre>
<ol start="3">
<li>To get the Index Mapping, use</li>
</ol>
<pre><code class="lang-plaintext">GET cat/_mapping
</code></pre>
<ol start="4">
<li>Delete Index- In case you want to remove an index altogether, just make a delete request in Kibana with the index name.</li>
</ol>
<pre><code class="lang-plaintext">DELETE cat
</code></pre>
<ol start="5">
<li>Reindexing-Sometimes you will face an issue, where you have to copy all the documents from one index to another. So, in that case, _reindex will come in the picture, just enter the source and destination index name, it will copy the documents. You can also add ingest pipeline option, in case you want all your documents to run through this pipeline before indexing, this is optional.</li>
</ol>
<pre><code class="lang-plaintext">POST _reindex
{
  "source": {
    "index": "cat"
  },
  "dest": {
    "index": "cat_backup",
    "pipeline": "some_ingest_pipeline"
  }
}
</code></pre>
<ol start="6">
<li>Delete one document- In case you want to delete a document from one of the indexes, just follow the below API syntax.</li>
</ol>
<pre><code class="lang-plaintext">DELETE  cat/_doc/123456789
</code></pre>
<p>Now, As you are up and running with the Elastic Index creation. Let's talk about a few requests with basic elastic search queries. 7. To delete documents matching specific conditions, use Delete By Query calls.</p>
<pre><code class="lang-plaintext">POST /cat/_delete_by_query
{
  "query": {
    "match_all": {}
  }
}
</code></pre>
<ol start="8">
<li>Get count of all Documents, in an index.</li>
</ol>
<pre><code class="lang-plaintext">GET cat/_count
</code></pre>
<ol start="9">
<li>To search for documents with id.</li>
</ol>
<pre><code class="lang-plaintext">GET cat/_search
{
  "query": {
    "match": {
      "_id": "97351395-2241-4287-953e-6987dbf93827"
    }
  }
}
</code></pre>
<ol start="10">
<li>The above query will return all the fields of the document, but in case you only need some specific fields. Use the query with _source key, which allows us to pass the fields which we need in the document.</li>
</ol>
<pre><code class="lang-plaintext">GET cat/_search
{
  "_source": [
    "title",
    "description"
  ],
  "query": {
    "match": {
      "_id": "97351395-2241-4287-953e-6987dbf93827"
    }
  }
}
</code></pre>
<ol start="11">
<li>If you are not aware of the field, where you will get the search term, and want to search all the fields for the search text, then use <strong>query_string</strong></li>
</ol>
<pre><code class="lang-plaintext">GET cat/_search
{
    "query": {
        "query_string": {
           "query": "kiwi"
        }
    }
}
</code></pre>
<ol start="12">
<li>To search for the exact terms, use match_phrase.</li>
</ol>
<pre><code class="lang-plaintext">GET cat/_search
{
  "query": {
    "match_phrase": {
      "name": "deals of the day"
    }
  }
}
</code></pre>
<ol start="13">
<li>To search for the parent document using child, try the below request.</li>
</ol>
<pre><code class="lang-plaintext">GET cat/_search
{
  "query": {
      "parent_id": {
          "type": "child",
          "id": "00000001"
      }
  }
}
</code></pre>
<ol start="14">
<li>Using the below query, you can search for a document, where the colour field is either black or white, whereas the breed is Persian.</li>
</ol>
<pre><code class="lang-plaintext">GET cat/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "breed": "Persian"
        }
      },
      "should": [
        {
          "match": {
            "color": "black"
          }
        },
        {
          "match": {
            "color": "white"
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}
</code></pre>
<ol start="15">
<li>In case you are using any <a target="_blank" href="https://www.elastic.co/guide/en/elasticsearch/reference/7.16/analyzer.html">analyzer</a> in the index, so to verify how search terms will behave use analyze requests. As result, you will get the tokens, on which the actual search will take place.</li>
</ol>
<pre><code class="lang-plaintext">GET cat/_analyze
{
  "analyzer": "ascii-stop-analyzer",
  "text": "açaí a la carte"
}
</code></pre>
<p>That's a wrap, for now, Further, I will update this post with the component template, index template, Ingest Pipeline and Index Mapping.</p>
<p>Please Ignore the spacing issue in query JSONs because of the markdown format.</p>
<p>Important Links:</p>
<ol>
<li><p><a target="_blank" href="https://www.elastic.co/guide/index.html">Elastic Documentation.</a></p>
</li>
<li><p><a target="_blank" href="https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html">Elastic Quick Start</a></p>
</li>
<li><p><a target="_blank" href="https://medium.com/swlh/parent-and-child-joins-with-elasticsearch-7-381f6cca73fe">Parent child relationship</a></p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Twitter Bot which tweets Mars Photos]]></title><description><![CDATA[Hi! In this article, we will build a simple Twitter bot with Python, which will use NASA Open source API to tweet Mars Image every 1 hour. I have built this Bot and hosted it on Heroku and it's online for the past few months. You can view it here Mar...]]></description><link>https://aemslate.com/twitter-bot-which-tweets-mars-photos</link><guid isPermaLink="true">https://aemslate.com/twitter-bot-which-tweets-mars-photos</guid><category><![CDATA[bot]]></category><category><![CDATA[Twitter]]></category><category><![CDATA[Python]]></category><category><![CDATA[APIs]]></category><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[Ashish Mishra]]></dc:creator><pubDate>Sun, 03 Oct 2021 14:33:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753257279937/9a44a601-a5df-4d64-a576-5c483bd2bc36.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hi! In this article, we will build a simple Twitter bot with Python, which will use NASA Open source API to tweet Mars Image every 1 hour. I have built this Bot and hosted it on Heroku and it's online for the past few months. You can view it here <a target="_blank" href="https://twitter.com/ImageMars">Mars Image Bot</a>. So, follow along, as I detail below the entire process.</p>
<p><strong>Step 1: Getting Credentials from NASA Open API</strong></p>
<p>As we are going to pull our Images from NASA Open source API, which in turn gets these Images from Mars Rover. So, let's first visit https://api.nasa.gov/, Here we have to enter few details, and it will give us an API Key to access its APIs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633268342669/T15bU5RoL.png" alt="image.png" /></p>
<p>After entering all details, you should see the below screen, where API Key will be shown, please note this carefully, we will need it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633268600733/kMnKk5-5B.png" alt="image.png" /></p>
<p>Now, from the menu, select Browse APIs and enter Mars in the search box, you should see all mars related APIs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633268715422/RUXdBFtaq.png" alt="image.png" /></p>
<p>Now, we are going to use the below API, which sends us random Image Data every time we change the SOL number.</p>
<p>https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=12&amp;api_key=DEMO_KEY</p>
<p>Replace the DEMO_KEY with your API Key.</p>
<p>This Endpoint returns a Photos Array, which contains all information. we will be traversing it and finding the Image Endpoint.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633268891214/LWD78mWGA.png" alt="image.png" /></p>
<p>Okay, So we have all the info which we needed from NASA.</p>
<p><strong>Step 2: Code our Python Bot</strong></p>
<p>We will first get the Twitter API keys, for that go to this URL <a target="_blank" href="https://developer.twitter.com/en/portal/dashboard">Developer Portal</a> and follow the process, which is self-explanatory. In the end, you should have your Access Key and Consumer Key. (Do let me know in the comments if you face any issues.)</p>
<p>Let's start coding. 😈</p>
<p>First Lets import the <strong>tweepy</strong> and other libraries, and set the Twitter keys in a variable. I have used config variables, as I am going to deploy it to a Server Later on, you can directly assign the values, for local development.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633269617128/_LTNx7tKQ.png" alt="image.png" /></p>
<p>Now, try to make a connection with Twitter, If all goes well you should see the "Connection Done!" message, in the terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633269088073/HhLW4UY__.png" alt="image.png" /></p>
<p>Now, as we are connected to Twitter, let's try to pull the Image from NASA API, which we are going to use. First, we are getting the current date and time, and printing it out. Next, we are trying to fetch a random Integer, if you remember in NASA API, we need to pass a random SOL. So, we will add this number in the API Url, and make a get request. If the request code is 200, we will start reading the response, or else will print an error message.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633270094814/LsSzSeA-h.png" alt="image.png" /></p>
<p>As our Image URL is in the photos array, we will save this array in photos_array, and as you can see in the response, there are multiple images object in the array, so let's pick a random object from the array by again using the random library. Once we found the Image, pass the message and image to another function, from where we will actually update the Twitter status.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633270528687/-ScuqlJlN.png" alt="image.png" /></p>
<p>This function is very simple, it makes a web call to the Image URL and then pulls it. Further, it uses the below method of tweepy library to update Twitter status.</p>
<pre><code class="lang-plaintext">api.update_with_media(filename, status=message)
</code></pre>
<p>Now, if you have followed all these steps and call the <strong>get_random_image_from_nasa</strong> function, you should be able to tweet an image, but let's schedule it to run every one hour.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1633270965994/ulTzWYp89.png" alt="image.png" /></p>
<p>Here, we are using, schedule library, to run the above function every hour.</p>
<p>Congrats!, now your bot is working.</p>
<p>Let me know if you want me to write about how to host this bot to Heroku and run free for life. 🐶</p>
<p>Github Repo URL - https://github.com/a-blank-slate/mars-image-tweeting-bot/blob/main/tweet.py</p>
<p>Thanks! for reading this.</p>
]]></content:encoded></item><item><title><![CDATA[Handy Commands For Developers]]></title><description><![CDATA[Chrome
Copy data to Clipboard
If you want to copy an object or variable to the clipboard from the console, then use the below copy command. The below line will copy AABB text to the system clipboard.
        copy("AABB");
Copy query param of page to ...]]></description><link>https://aemslate.com/handy-commands-for-developers</link><guid isPermaLink="true">https://aemslate.com/handy-commands-for-developers</guid><category><![CDATA[maven]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><category><![CDATA[Google Chrome]]></category><category><![CDATA[Redis]]></category><dc:creator><![CDATA[Ashish Mishra]]></dc:creator><pubDate>Sun, 11 Jul 2021 18:54:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753081758317/5aaae4f1-b8bf-4b4d-bc9d-76b966eea572.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-chrome">Chrome</h2>
<h4 id="heading-copy-data-to-clipboard">Copy data to Clipboard</h4>
<p>If you want to copy an object or variable to the clipboard from the console, then use the below copy command. The below line will copy AABB text to the system clipboard.</p>
<p>        copy("AABB");</p>
<h4 id="heading-copy-query-param-of-page-to-clipboard">Copy query param of page to Clipboard</h4>
<p>        const urlSearchParams = new URLSearchParams(window.location.search);
        const params = Object.fromEntries(urlSearchParams.entries()); 
        copy(params['pageId']);
Run this command in chrome console, after opening the specific page, this will copy the value of pageId param to the system clipboard</p>
<h2 id="heading-couchbase">Couchbase</h2>
<h4 id="heading-scenario-1-copy-all-documents-from-one-bucket-to-another">Scenario 1: Copy all documents from One Bucket to another</h4>
<p>        INSERT INTO `bucket-name` (KEY _k, VALUE _v) SELECT META().id _k, _v from `bucket-name` _v</p>
<h4 id="heading-scenario-2-deleting-everything-from-bucket">Scenario 2: Deleting everything from bucket</h4>
<p>        DELETE FROM `bucket-name`</p>
<h2 id="heading-git">Git</h2>
<h4 id="heading-scenario-1-need-to-remove-a-modified-file-from-git-pull-request">Scenario 1: Need to remove a Modified File from Git pull request</h4>
<p>Switch to the branch from which you created the pull request:</p>
<p>     git checkout pull-request-branch</p>
<p>Overwrite the modified file(s) with the file in another branch, let's consider it's <strong>master</strong>:</p>
<p>    git checkout origin/master -- src/main/java/HelloWorld.java</p>
<p>Commit and push it to the remote:</p>
<p>    git commit -m "Removed a modified file from pull request"
    git push origin pull-request-branch</p>
<h4 id="heading-scenario-2-update-the-commit-user-name-and-email-in-the-respective-git-repo">Scenario 2: Update the commit user name and email in the respective Git repo.</h4>
<p>    git config user.name user-name
    git config user.email username-git@gmail.com</p>
<h4 id="heading-scenario-3-git-updating-the-buffer-size-in-git-repo-helps-in-case-a-large-file-is-present-in-git-and-you-are-trying-to-clone-it-in-the-local-system">Scenario 3: Git updating the buffer size in Git repo, helps in case a large file is present in Git and you are trying to clone it in the local system.</h4>
<p>    git config --global http.postBuffer 1048576000</p>
<h4 id="heading-scenario-4-stop-tracking-changes-to-a-file-in-sourcetree">Scenario 4: Stop tracking changes to a file in Sourcetree.</h4>
<p>I guess This is the issue which troubled me most in git. Let's say, I have a file in my code base that I don't want to commit as this is specific to my local. But still, Sourcetree shows this file to me, every time I try to commit. This is really very annoying. to fix this run the below command in the repo folder and the Sourcetree will understand our feelings.</p>
<p>    git update-index --assume-unchanged file.filename</p>
<p>For folder use this</p>
<p>    git rm -r --cached path_to_your_folder/</p>
<p>To get undo/show dir's/files that are set to assume-unchanged run this:</p>
<p>    git update-index --no-assume-unchanged file.filename</p>
<p>To get a list of dir's/files that are assume-unchanged run this:</p>
<p>    git ls-files -v|grep '^h'</p>
<p> To delete all local checked out branches</p>
<p>    git branch -D <code>git branch</code></p>
<h2 id="heading-maven">Maven</h2>
<h4 id="heading-scenario-1-start-the-aem-in-debug-mode">Scenario 1: Start the AEM in debug mode</h4>
<p>    java -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=127.0.0.1:8765 -XX:+HeapDumpOnOutOfMemoryError -XX:MaxPermSize=256M -Xmx1024m -Dorg.apache.sling.commons.log.level=INFO -jar cq-quickstart-6.5.0.jar</p>
<h4 id="heading-scenario-2-build-and-deploy-aem">Scenario 2: Build and deploy AEM</h4>
<p>    mvn -PautoInstallPackage -Padobe-public clean install</p>
<h4 id="heading-scenario-3-start-the-spring-boot-application">Scenario 3: Start the Spring boot application</h4>
<p>    mvn clean spring-boot:run</p>
<h4 id="heading-scenario-4-start-spring-boot-in-specific-mode">Scenario 4: Start Spring boot in specific mode</h4>
<p>    mvn clean spring-boot:run -Dspring-boot.run.profiles=local</p>
<h4 id="heading-scenario-5-skip-j-unit-test">Scenario 5: Skip J-Unit test</h4>
<p>    -Dmaven.test.skip=true</p>
<h4 id="heading-scenario-6-start-spring-boot-in-debug-mode-and-also-run-a-specific-profile-and-skip-j-unit-test-compilation">Scenario 6: Start Spring boot in debug mode and also run a specific profile and skip j unit test compilation</h4>
<p>    mvn spring-boot:run -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" -Dspring-boot.run.profiles=local -Dmaven.test.skip=true</p>
<h2 id="heading-redis">Redis</h2>
<h4 id="heading-scenario-1-start-redis-server">Scenario 1: Start Redis Server</h4>
<p>    redis-server</p>
<h4 id="heading-scenario-2-clear-redis-cache">Scenario 2: Clear redis cache</h4>
<p>    redis-cli flushall</p>
<h4 id="heading-to-be-continued">To Be Continued....</h4>
]]></content:encoded></item></channel></rss>