Overriding the Favicon in DXP

So perusing the web, you'll notice there's a couple of resources out there explaining how to override the default favicon in Liferay. Of course, the standard way is to add it to /images folder of your theme and apply the theme to the site. As long as you got your path right and it's the default name, "favicon.ico," it should work just fine. 

While this satisfies most use cases, what if you have more strict business requirements? For example, "Company logo must be displayed at all times while on the site," which includes while navigating on the control panel which has it's own theme. You might be inclined then to look into the tomcat folder and simply replace the file directly for all the themes. While this would work, this is not very maintainable since you need to do this every time you deploy a new bundle (which can be very frequent depending on your build process) and for every instance of Liferay you have running.

With DXP, we've introduced another way to override certain JSPs through dynamic includes. Although not every JSP can be overridden this way, luckily for us, top_head.jsp (where the favicon is set) can be. There are a few things you need to do when creating your dynamic include for this. 

First thing is you're going to want to register which extension point you'll be using for the JSP:

/html/common/themes/top_head.jsp#post

You're going to use "post" and not "pre" because the latest favicon specified is what is going to be rendered on the page. In other words, by adding our favicon after the default one, ours will take precedence. 

Next you're going to need the line that replaces:

<link href="<%= themeDisplay.getPathThemeImages() %>/<%= PropsValues.THEME_SHORTCUT_ICON %>" rel="icon" />

For that, we need to specify the image we're going to be using, which can be placed in our module's resource folder: src/main/resources/META-INF/resources/images. 

Next, we need a way of accessing our module's resources, which can be done with its web context path. In your bnd.bnd add the following line with your desired path name. For mine, I'll just use /favicon-override:

Web-ContextPath: /favicon-override

With that in place, we should be able to build our url now. The finished url will look something like: http://localhost:8080/o/favicon-override/images/favicon.ico. The components include the portal url, /o (which is used to access an OSGi module's resources), the web context path, and the path to the resource.

Putting it all together, your class should look something like this:

public class TopHeadDynamicInclude implements DynamicInclude {

   @Override
   public void include(
         HttpServletRequest request, HttpServletResponse response, String key)
      throws IOException {
      PrintWriter printWriter = response.getWriter();
      ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
         WebKeys.THEME_DISPLAY);
      StringBundler url = new StringBundler(4);

      url.append(themeDisplay.getPortalURL());
      url.append("/o");
      url.append(_WEB_CONTEXT_PATH);
      url.append(_FAVICON_PATH);

      printWriter.println("<link href=\"" + url.toString() + "\" rel=\"icon\" />");
   }

   @Override
   public void register(DynamicIncludeRegistry dynamicIncludeRegistry) {
      dynamicIncludeRegistry.register("/html/common/themes/top_head.jsp#post");
   }

   private static final String _WEB_CONTEXT_PATH = "/favicon-override";

   private static final String _FAVICON_PATH = "/images/favicon.ico";

}

That's all there is to it! You could also add additional println statements to cover cases for mobile devices. The beauty of this solution is that you have a deployable module that can handle the configuration as opposed to manually doing it yourself which is prone to error. Also, by using dynamic includes, it's more maintainable when you need to upgrade to the next version of Liferay.

Github Repository: https://github.com/JoshuaStClair/favicon-override

Blogs

This seems to be adding a second <link href="..." rel="icon" /> tag and it might not work in all browsers. Chrome, for instance, probably ignores it. But the include mechanism works fine! :-)

is there mean, i need create a mvc-module, and do all the things, and add the widget in to front page(root site), and all favicon will be change?