Friday, November 6, 2020

Artificial intelligence in AEM

 

Artificial intelligence in AEM


Adobe is taking us into futuristic AI enabled solutions by leveraging Sensei to improve, enhance and help content creators and marketers to provide an optimal digital experience.

Adobe Sensei helps to improve

Image content

Automatically add image descriptors - including metadata, design elements, and captions - to new and existing assets.

Image quality

Score image quality on professional dimensions such as color harmony, depth of field, and rule of thirds.

Content position

Automatically crop and mask images with content-aware intelligence to create images to brand specifications and channel requirements.

Video content

Quickly generate metadata including action descriptors for video.

Below are the features provided by Adobe Sensei in AEM: 

 

·        Smart Crop - Experience Manager can now automatically suggest the best crops for images by automatically detecting their focal points. It can also automatically adjust the image based on the available bandwidth and device type, so you don’t send a high-res image to a low-res screen. It auto-generates crops based on point of focus that is generated by AI algorithms.

 

·        Smart Video Crop - Automatically re-crop a video to new aspect ratios while preserving the points of interest (POIs) inside that video.

 

 

·        Smart Tags for Images - Experience Manager can now automatically tag images. The system learns as you add new tags, so that it can go beyond the generic terms and learn the names of your products.

 

·        Video Smart Tags – Automatically add suggested tags based on the video’s content.

 

 

·        Smart Layout - Smart layouts allow authors to drag and drop modules on a page and lets Adobe Sensei Artificial Intelligence optimize the layout of content, and surface the most engaging content within a page based on analysis across your key business performance metrics.

 

·        Smart Translations - AEM Smart Translation Search allows users to perform searches for content in AEM using non-English terms, to match the assets in AEM that have equivalent English terms on them.

 

 

css coding convention in AEM

BEM is a methodology used in Core Components that helps to create reusable components through a CSS class naming convention. It makes front-end code semantic, easier to read, understandable and maintainable.

Which Components Use BEM?

The latest Core Component versions use the BEM class naming scheme. To check if a component is using BEM have a look at the README.md file of a component version you're interested in and look for a BEM Description section.

All new components or features employ BEM.

BEM 101

  • Block - An independent entity that has meaning on its own and represents a UI section. For example, a navigation, list, tabs or form. A web interface is represented as a hierarchical collection of blocks. Example: cmp-tabs
  • Element - Part of a block, to which it's tied semantically and functionally. An element has no meaning outside of the block it belongs to. For example, a navigation item, list item, tab panel or form field. Example: cmp-tabs__tabpanel
  • Modifier - Flags set on either blocks or elements, to define properties or states (i.e. to change appearance or behavior). Example: cmp-tabs__tabpanel--active

BEM Example

Simplified example from the Tabs Component markup:

<div class="cmp-tabs">

  <ol class="cmp-tabs__tablist">

    <li class="cmp-tabs__tab--active">Tab 1</li>

    <li class="cmp-tabs__tab">Tab 2</li>

    <li class="cmp-tabs__tab">Tab 3</li>

  </ol>

  <div class="cmp-tabs__tabpanel--active">Tab Panel 1 Content</div>

  <div class="cmp-tabs__tabpanel">Tab Panel 2 Content</div>

  <div class="cmp-tabs__tabpanel">Tab Panel 3 Content</div>

</div>

That employs the following BEM notation:

BLOCK cmp-tabs

    ELEMENT cmp-tabs__tablist

    ELEMENT cmp-tabs__tab

        MOD cmp-tabs__tab--active

    ELEMENT cmp-tabs__tabpanel

        MOD cmp-tabs__tabpanel--active

Namespace

The BEM notation used in the Core Components follows the standard BEM naming scheme, but adds an additional namespace prefix cmp- to encapsulate Core Component style rules.

State Modifiers

Some BEM implementations prefix state modifiers (active, disabled etc.) with an "is" keyword block__element--is-state. In Core Components the preferred pattern is to use a standard modifier with no prefix block__element--state.

 


Localization in AEM

 

Localization in AEM


Many websites require content to be delivered in a localized format across multiple languages and geographies or we call them as multilingual websites

Selected Core Components make it simple to create a unified template for all of your localized content that automatically adapts based on your localized site structure.

As almost all sites require a header/footer to be present across all pages. These are generally consistent across all content of the page.

However, for a localized content page, a localized version of that header or footer needs to be displayed.


AEM provides solution for this problem:

 

Using the localization features of the Navigation Core Component and Experience Fragment Core Component along with the editable templates of AEM, this becomes a simple task.

This could be further extended to use the Language Navigation Component as well.

All of the localization features of AEM and its Core Components rely on a clear and logical content structure for localized content.

Let's also say that you author your site in English and offer it in French as well. So if you have a simple page called my-page it would be found in two localization branches in your site's content tree:



Page footers are generally made using Experience Fragments so you will need an English and French version just like your pages.

However, Experience Fragments are not pages, but are rather parts of pages that can be reused across pages, so they do not live directly under /content as the rest of your pages. Instead they live under their own folder :



since they also must be localized, their structure must mirror the localization structure of your site.


Creating page footer with Experience Fragments


The Experience Fragment Component is very flexible and is well-suited for a page header or footer.

Because our website is offered in English and French as taken example above, we will need to create two Experience Fragments, both called as footer in the locations we described previously.

 

English Footer:

/content/experience-fragments/my-site/en/footer

French Footer:

/content/experience-fragments/my-site/en/footer

 

Adding footer to view on every page:

Because the footer will appear on every page, we will need to add the Experience Fragment to our standard page template.

Our template is simply called my-template and is located with our other templates:

 

 conf/my-site/settings/wcm/templates/my-template

 

To this template we will add the basic components that we want our pages to be based on for example:

1.     Navigation component

2.     Container component

3.     Experience fragment component etc.

 

Creating Pages with created template:

By setting up the site structure and template, the content author simply needs to add the necessary content to the pages.

 

Using the templates and the localization logic of the components, the navigation and footers will be automatically added to the page and localized.

 

For example, the author would only need to add content such as a text component to the English and French pages.

 

The Navigation Component and Experience Fragment Component come from the page template and know to automatically display the correct content based on the localization structure and the location of the page itself.

 


      

 In AEM we use MSM in following cases:

·        Sites are in multiple countries.

·        Sites are in multiple languages.

·        Need to reuse/replicate same content in multiple websites.

·        Maintain consistent branding and content across websites.

 

Advantages of using MSM:

·        Easily manage multiple websites that share common content.

·        MSM allows aem developers to define relations between the sites so that content changes in one site are automatically replicated in all other sites.

·        Reduces the time to manage your websites and increases the re-use of common content.

·        Maintain common look and feel across all websites.

 

The biggest advantage of using MSM over internationalization is ease to add new languages to websites. To leverage MSM functionality a website must follow below structure:

·        The web site should have a single page as the root.

·        Each child page is the root page of each language version.

·        The name of the child page is the language code.

 

 

 

 

 

Creating JUnit Tests in AEM

 

Creating JUnit Tests in AEM

 

Unit tests are build-time tests written in Java that verify expected behavior of Java code.

 

Following are the benefits of writing Junit Tests in AEM:


·        It finds logical bugs early in the code, which makes our code more reliable.

 

·        JUnit is useful for developers, who work in a test-driven environment as it forces them to develop more readable, reliable and bug-free code which builds confidence during development.

 

 

·        It increases the productivity of the programmer and the stability of program code, which in turn reduces the stress on the programmer and the time spent on debugging.

 

Each unit tests are typically small, and validate the outcome of methods (or units of work) against expected results.

 

Below Maven dependencies are required to support writing and running the tests:

 

·        JUnit4

·        Mockito Testing Framework

·        Apache Sling Mocks

·        wcm.io Test Framework

 

The first three i.e. JUnit4, Mockito and Sling Mocks dependencies are automatically added to the project during project setup through Maven

But io.wcm Testing Framework dependency must be added to the project's root pom.xml.

<dependency>

        <groupId>io.wcm</groupId>

        <artifactId>io.wcm.testing.aem-mock.junit4</artifactId>

        <version>2.3.2</version>

        <scope>test</scope>

</dependency> 

 

Creating Junit Tests in Eclipse

 

We can do this in Eclipse, by right-clicking on the Java class to test, and selecting the New > Other > Java > JUnit > JUnit Test Case.

 

Below are the steps to create the JUnit test type is New JUnit 4 Test:



Verify the following while creating the Junit Tests:

·        The JUnit Maven dependencies set up in our pom.xml's.

·        The package is the java package of the class being tested (BylineImpl.java)

·        The Source folder points to the core project, which instructs Eclipse where the unit test files are stored.

·        The setUp() method stub will be created manually; we'll see how this is used later.

 

Click Next - The next step helps with the auto-generation of test methods.

Typically each public method of the Java class has at least one corresponding test method, validating its behavior.

Often a unit test will have multiple test methods testing a single public method, each representing a different set of inputs or states.



Run the JUnit Test Case by right-clicking on the class name, and Run As > JUnit Test.

If the test is green it indicated by green color status bar. Otherwise, it’ll be shown in red color. Also, we will get the code coverage details in Coverage tab.



Below is the sample code to for user details component in my-page:

 

import org.mockito.runners.MockitoJUnitRunner;

 

@RunWith(MockitoJUnitRunner.class)

public class UserDetailsTest {

 

            private UserDetails userDetails;

            private Resource resource;

            private final String myPagePath = “/content/we-retail/us/en/my-page”;

 

            @Rule

            public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);

 

            @Before

            public void setUp() throws Exception {           

                        context.load().json(“/content.json”, myPagePath);

                        context.addModelsForPackage(“<mention-your-package-here>”);

            }

 

            @Test

            public void getFirstNameTest() throws Exception {

                        resource = context.resourceResolver()

                                                .getResource(myPagePath + “/jcr:content/root/responsivegrid/userdetails”);

                        userDetails = resource.adaptTo(UserDetails.class); 

                        assertEquals(userDetails.getFirstName(), “Lavanya”);

            }          

 

            @Test

            public void getLastNameTest() throws Exception {

                        resource = context.resourceResolver()

                                                .getResource(myPagePath + “/jcr:content/root/responsivegrid/userdetails”);

                        userDetails = resource.adaptTo(UserDetails.class); 

                        assertEquals(userDetails.getLastName(), “Malyala”);

            }

 

}

 

References:

https://helpx.adobe.com/gr_en/experience-manager/kt/sites/using/getting-started-wknd-tutorial-develop/part8.html

https://aem.adobemarketingclub.com/junit-in-aem/

Custom Service to Read Json Data from any provided URL

 

Custom Service to Read Json Data from any provided URL



AppConstants.java (class)

 

package com.community.config.core;

 

public class AppConstants {  

      public static final String URL = "https://jsonplaceholder.typicode.com/todos/1";

}

 

ReadJsonService.java (interface)

 

package com.community.config.core;

 

public interface ReadJsonService {

      public String getData();

}

 

ReadJsonDataImpl.java (class)

 

package com.community.config.core;

 

import org.osgi.service.component.annotations.Component;

import static com.community.config.core.AppConstants.URL;

import com.community.config.core.ReadJsonService;

import com.community.config.core.Network;

 

@Component(immediate = true, service = ReadJsonService.class)

public class ReadJsonDataImpl implements ReadJsonService {

 

      @Override

      public String getData() {    

            String response = Network.readJson(URL);       

            return response;

      }

 

}

 

Network.java (class)

 

package com.community.config.core;

 

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.MalformedURLException;

import java.net.URL;

import javax.net.ssl.HttpsURLConnection;

 

public final class Network {

      private static final String USER_AGENT = "Mozilla/5.0";

      public static String readJson(String url) {

            try {

                  URL requestURL = new URL(url);

                  HttpsURLConnection connection = (HttpsURLConnection) requestURL.openConnection();

                  connection.setRequestMethod("GET");

                  connection.setRequestProperty("User-Agent", USER_AGENT);

                  int responseCode = connection.getResponseCode();

                  if (responseCode == HttpsURLConnection.HTTP_OK) {

                        BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));

                        String inputLine;

                        StringBuffer response = new StringBuffer();

                        while ((inputLine = in.readLine()) != null) {

                              response.append(inputLine);

                        }

                        in.close();

                        return response.toString();

                  }

            } catch (MalformedURLException e) {

                  e.printStackTrace();

            } catch (IOException e) {

                  e.printStackTrace();

            }

            return "";

      }

}

 

 

 

Change the code in HelloWorld component’s .html file

Helloworld.html

<pre data-sly-use.jsondata="com.community.config.core.models.HelloWorldModel">

Json Data is:

${jsondata.message}

</pre>

 

Make Changes in HelloWorldModel.java to pass the value in OSGI Bundle

HelloWorldModel (class)

package com.community.config.core.models;

 

import javax.annotation.PostConstruct;

import javax.inject.Inject;

import org.apache.sling.api.resource.Resource;

import org.apache.sling.models.annotations.Model;

import com.community.config.core.ReadJsonService;

 

@Model(adaptables=Resource.class)

public class HelloWorldModel {

     

    @Inject

    ReadJsonService rj;

   

    private String message;

 

    @PostConstruct

    protected void init() {         

        message = rj.getData()+ "\n";

    }

 

    public String getMessage() {

        return message;

    }

}


By this we can hit any URL and get the Json data on Page