Ephesoft – Missing Images in BatchList.html

Version 3.0.3.4 SP2

A client contacted me today with a support issue. When attempting to view documents over their network in Review or Validate the documents would fail to load. The same documents would load fine over the localhost URL when viewing on the server.

After a consultation with the fine support staff at Ephesoft it was a potential cross domain scripting issue. The following steps resolved the issue:

1. Navigate to <Ephesoft install folder>\Application\WEB-INF\classes\META-INF\dcma-batch\

2. Backup the existing file

3. Update the batch.base_http_url parameter to the URL base used by the end users

4. Restart the server

 

Support staff noted “the http path will look unique. It is supposed to. It will have back slashes before the colons and that is because they are needed by the application to interpret the path properly.”

 

Upon restarting the server, Ephesoft responded as expected.

Custom Share role breaks existing sites solution

ADDED: Your new group is added to the ACL of both site and document library spaces with the role taken from your new group name i.e. new group is  GROUP_site_SOMESITEID_SiteCollaboratorPlus, SiteCollaboratorPlus is the role.

When you create a custom role in Share you must know up front what roles you need – before your project begins because it breaks your existing sites if you deploy the role after the fact. This isn’t always how things work out so here is a quick little webscript that will fix existing sites when deploying a new custom role.

This is a java backed webscript that will search all existing sites that do not have your custom role security group. It will then take your new role name via Spring and dynamically add it to all sites in share (I put it in spring because we don’t want people creating new roles to ALL sites via a URL parameter).

If you are un-familiar with how the files talk to eachother or where to put things please see the Alfresco Wiki on Java backed webscripts. Once the webscript is installed, simply go to http://localhost/alfresco/wcs/ecm/sites/fixrole (Make sure you change the spring context to add YOUR custom role name)

The context file (webscript-context.xml):

This descriptor file is where you want to put your custom group name that its complaining about. Just replace your siteid with the :SITE param you see below.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN 2.0//EN' 'http://www.springframework.org/dtd/spring-beans-2.0.dtd'>

<beans>

    <bean id="webscript.ca.abstractive.ecm.alfresco.sitefix.fix-role.get" 

      parent="webscript">

        <property name="serviceRegistry" ref="ServiceRegistry" />
        <property name="searchService" ref="SearchService" /> 
        <property name="nodeService" ref="NodeService" />
        <property name="authorityService" ref="AuthorityService" /> 

        <!-- Custom role SiteCollaboratorPlus -->
        <property name="authorityName">
            <value>GROUP_site_:SITE_SiteCollaboratorPlus</value>
        </property>  
    </bean>
</beans>

The webscript descriptor (fix-role.get.desc.xml):

<webscript>
  <shortname>Fix Share Roles</shortname>
  <description>Adds new role to all existing sites</description>
  <url>/ecm/sites/fixrole</url>
  <authentication>user</authentication>
  <format default="">argument</format>
  <family>Toolkit</family> 
</webscript>

The Java (UtilityRoleCreation.java):

package ca.abstractive.ecm.alfresco.sites;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.alfresco.model.ContentModel;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.web.bean.repository.Repository;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;

public class UtilityRoleCreation extends AbstractWebScript {
 
    protected ServiceRegistry serviceRegistry = null;
    protected SearchService searchService = null; 
    protected NodeService nodeService = null;
    protected AuthorityService authorityService = null;
    protected String authorityName = null;
    protected PermissionService permissionService = null;
    
    
    /**
     * Returns a list of sites in Alfresco
     *   
     * @return a list of site nodes 
     */  
    protected List<NodeRef> getSites(){
    
        //Get all sites
        String nodeQuery = "TYPE:\"st:site\"";

        SearchParameters params = new SearchParameters();
        params.setLanguage(SearchService.LANGUAGE_LUCENE);
        params.addStore(Repository.getStoreRef());
        params.setQuery(nodeQuery);
        ResultSet results = null;
        List<NodeRef> siteNodeRefs = new ArrayList<NodeRef>();
        
        try {
            results = this.searchService.query(params);
            if (results == null) {
                return null;

            } else {

                Iterator<ResultSetRow> siteNodes = results.iterator();
                while(siteNodes.hasNext()){
                     
                     ResultSetRow siteRow = siteNodes.next();
                     NodeRef nodeRef = siteRow.getNodeRef();
                     
                     siteNodeRefs.add(nodeRef);
                }
                
                return siteNodeRefs;
            }
            
        } finally {
            if (results != null) {
                results.close();
            }
        } 
         
    }    
    
    /**
     *  Webscript execute method
     *  
     */
    public void execute(WebScriptRequest req, WebScriptResponse res) 
            throws IOException  {
         
        try
        { 
            
            // build a json object
            JSONObject obj = new JSONObject();
            
            List<NodeRef> sites = this.getSites();
              
            Iterator<NodeRef> siteNodes = sites.iterator();
            while(siteNodes.hasNext()){
                 
                NodeRef nodeRef = siteNodes.next();
                  
                 //Get the sitename
                 String siteid = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);  
                 String siteGroup = new StringBuilder().append("GROUP_site_").append(siteid).toString();
                       
                 if(this.authorityService.authorityExists(siteGroup)){
                    
                    Set<String> authorities = this.authorityService.getContainedAuthorities(AuthorityType.GROUP, siteGroup, true);
                    String customRoleAuthorityName = this.authorityName.replace(":SITE", siteid);
                    
                    if (authorities.contains(customRoleAuthorityName) == false)
                     { 
                        obj.put(siteGroup, customRoleAuthorityName);
                        
                        //create the authority
                        String newAuthName = this.authorityService.createAuthority(AuthorityType.GROUP, customRoleAuthorityName.replace("GROUP_", "")); 
                        this.authorityService.addAuthority(siteGroup, newAuthName);
                         
                        // Assign the group the relevant permission on the site and document library
                        String[] bits = customRoleAuthorityName.split("_");
                        String role = bits[bits.length-1];
                        
                        permissionService.setPermission(nodeRef, customRoleAuthorityName, role, true);                            
                        NodeRef libraryNodeRef = nodeService.getChildByName(nodeRef, ContentModel.ASSOC_CONTAINS, "documentLibrary");
                        permissionService.setPermission(libraryNodeRef, customRoleAuthorityName, role, true);
                     }
                 }
            }
             
            // build a JSON string and send it back
            String jsonString = obj.toString();
            res.getWriter().write(jsonString);
        }
        catch(JSONException e)
        {
            throw new WebScriptException("Unable to serialize JSON");
        } 
    }
    
     
    
    /* ------------------------------------------------------------ */
    /* Getter/Setter methods */
    /* ------------------------------------------------------------ */
    
    /**
     * @return the serviceRegistry
     */
    public ServiceRegistry getServiceRegistry() {
        return serviceRegistry;
    }

    /**
     * @param serviceRegistry the serviceRegistry to set
     */
    public void setServiceRegistry(ServiceRegistry serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
        this.searchService = serviceRegistry.getSearchService(); 
        this.nodeService = serviceRegistry.getNodeService();
        this.permissionService = serviceRegistry.getPermissionService();
    }

    /**
     * @return the searchService
     */
    public SearchService getSearchService() {
        return searchService;
    }

    /**
     * @param searchService the searchService to set
     */
    public void setSearchService(SearchService searchService) {
        this.searchService = searchService;
    }
    
    /**
     * @return the authorityService
     */
    public AuthorityService getAuthorityService() {
        return authorityService;
    }

    /**
     * @param authorityService the searchService to set
     */
    public void setAuthorityService(AuthorityService authorityService) {
        this.authorityService = authorityService;
    }
      

    /**
     * @return the nodeService
     */
    public NodeService getNodeService() {
        return nodeService;
    }

    /**
     * @param nodeService the nodeService to set
     */
    public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
    }


    public String getAuthorityName() {
        return authorityName;
    }


    public void setAuthorityName(String authorityName) {
        this.authorityName = authorityName;
    }
    
    /**
     * Set permission service
     */
    public void setPermissionService(PermissionService permissionService)
    {
        this.permissionService = permissionService;
    }  
    public PermissionService getPermissionService()
    {
        return permissionService;
    }
}

Creating Layer Overlays for Google Maps and WebFOCUS

BACKGROUND

 

Over the past several years WebFOCUS mapping functionality has come a long way down a darkly lit, sometimes hopeless hallway. I recall in my early years of learning the focexec ropes receiving a request for me to plot call center information on a map to visualize relationships in call types and their geography.

 

Not having the current Google Map integration available to me, I began my journey towards what today is a fully functional mapping tool. The solution consisted of XML output from WebFOCUS being consumed by what by today’s standards is considered to be a rather primitive Yahoo map. The data, displayed as pins on the map was determined by the user’s selection of criteria via an HTML form.

 

Today mapping has spread throughout the Enterprise and is used in a multitude of ways. From call center applications used to visualize the location of clients and providers to managerial visualization of trending, mapping has become as popular as charting and trending.

 

THE HOOK

 

Recently, I was tasked with the requirement of pushing mapping further into the daily lives of the executive and management teams within our organization. As we know, the style of reporting required at this level is different than at the operational level, dealing more with summarizations and high level data than with individual clients.

 

I recalled a presentation I had been in with a colleague of mine at a previous Summit conference. In it Mr. Dirk Kuerbig, all around IBI genius and good guy, presented a solution for presenting data in layers overlaid on a Google Map. This was to be the basis point of my solution.

 

THE REQUIRMENTS

 

  • WebFOCUS 7.7.2
  • Google Map Integration
  • Application must allow for the selection of the following basic criteria
    • The statistic the user wished to see (Total Dollars, Total Clients, etc)
    • The overlay (City, Constituency, Province, etc)
    • The application must then prompt for subsequent parameters according to the statistic selected
    • The map would be presented based the selected layers. The layer color would be shown as a heat map (lower value =lighter color, higher value = higher colors)

 

THE ISSUES

 

BUILT IN INTEGRATION INSUFFICIENT

Looking at the solution requirements it became immediately clear that the built in Google Integration with WebFOCUS would not be sufficient. The following list of issues were identified:

  • Current to 7.7.2, Google map integration only allows for layers based on individual points, not a layer based on a zone or region or series of points.
  • No capability to do heat mapping (Although you can have dynamic pin colors based on a defined field)

 

GOOGLE MAP/BROWSER DEFICIENCIES

Once the build began we quickly found that in simple cases, where shapes being drawn as a layer only included a few points there were no issues. We also noted that in the case of complex shapes, like those of city or constituency boundaries, Google Maps would crash and cause serious performance issues.

 

CONSUMING WEBFOCUS DATA

Out of the box, WebFOCUS best option for consuming data from a webpage is XML. I don’t like XML. JSON was the preferred option for this project.

 

THE SOLUTION

 

THE DATA

Thanks to a tip and some code from Mr. Darin Lee, and a properly configured XSL template provided by my colleague, Mr. Nathan Wong of Abstractive, we were able to extend WebFOCUS allowing us to specify JSON output. This data is dynamically served through WebFOCUS based on the selected layer overlay and criteria.

 

THE LAYERS

This was the big time-muncher! To properly configure the overlay information to be able to display it on a Google Map I had followed the process outlined below. It is important to note that I began with a shape file in the .shp file extension (Typically ArcGIS). If you have the shape file in KML format, you may skip the initial steps.

 

Convert File to KML

*Owning a licence to ArcGIS client tools I utilized the tools available to me.

  1. Open ArcMap
  2. Import the desired layer
  3. Using the “Export to KML” Extension (http://arcscripts.esri.com/details.asp?dbid=14273) export the file to XML

 

Encode the Layer Information

Open the KML document in a text editor. Review its structure and notice the boundary information included. Each boundary may include thousands of points which make up the shape. Eack boundary was encoded in order to aid Google Maps in their timely creation.

 

*I am sure that someone smarter than I will think of a way to automate this, but I encoded each boundary manually.

 

  1. Copy each coordinate within a boundary “<coordinates>” to “</coordinates>”, keeping in mind that there may be inner and outer rings as well.
  2. Copy latitude and longitude comma delimited values into the form at http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/encodeForm.html
  3. Run the Form and save the results.

 

Build JavaScript Object to Display the Layers and Heat Map

Once we had collected all the necessary information I was then able to begin the fun part, painting the picture including:

 

  • Dynamic display of layer based on selection
  • Dynamic display of background color based on predefined color ranges (mine are yellow to blue), minimum and maximum data values (based on the data) and the value of the given boundary.
  • Dynamic tooltips and popup messages based on area of the map the user is viewing

 

SUMMARY

 

Today, within our enterprise we have another WebFOCUS based application ready to visualize data geographically. Whether it is a program manager wanting to know where his client base resides or an executive wanting to know the average age of his cliental within a specific geographic location, information is available and ready to aid in vital business decisions.

 

Please contact me at any time should you have any questions or corrections.

Our New Home

Welcome to the new and improved Abstractive Technology Consulting website. Over the past few months our in house design guru, Mr. Mike Priest, myself and the rest of the Abstractive team have been working to overhaul the previous Abstractive site.

 

Our small consulting company has travelled a long road since our inception just a few short years ago. We have been able to form strong partnerships between coworkers, partners and our amazing clients, and we look forward with great anticipation towards the growth and opportunity that will present itself in the weeks, months and years to come.

 

I wanted to take a few moments to introduce you to some of the features of our new home.:

 

The Design (Via Mike Priest)

The design used in throughout the website is dynamic and elegant. By utilizing the responsive web design concept, modern web frameworks and key user interaction methods; we created a site that feels like it was built specifically for the device the user is using.

The home page was built into one page. This allowed us to deliver a message from all parts of our company without having to navigate to those sections individually. As you know from mobile browsers, it can be slow and tedious to move between numerous pages on a website; especially if you are on the train. To make the usability a bit more fun we added auto scrolling on the main navigation for the home page. Since mobile devices have a limited viewport it makes it much easier to scroll to sections if it is automatically done for you, and as you can see there is a link to scroll right back to where you started. Simple.

On a computer or laptop the home page gives more of a pamphlet look and feel. As the page is quite long we move the navigation with you. This again allows for better usability eliminating the need  to scroll all the way to the top
of the page to get back home.

We are a big fan of cufon as it renders custom font much crisper than @font-face. Hopefully you can see, all in all we have more of an “abstract” art kind of delivery.

 

The Content

We have configured our new site to present to you information on our services and products. Please take a minute to read up on the various services we provide and contact us with any questions you might have.

 

We also wanted to provide our clients and potential employees with more insight into how we function as an organization. Our new site has added sections on:

  • The Abstractive Consulting Community Fund (ACCF), formed in 2011 to aid us in giving back to our community,
  • Our Partners – We have been lucky enough to form great relationships with industry leading technology providers
  • Careers – Our company is always looking for bright, creative minds to join with us in providing quality consulting services. Here we highlight some of our employee benefits and positions which may have openings
  • Blog – A new journey for us into the social media universe, we are happy to share our experiences within the ECM and BI world.

 

Abstractive Analytics

We are proud at Abstractive Technology Consulting not to be your typical consulting firm. We know the world of technology is driven by those who are willing to lead and innovate. Armed with that understanding and with minds suitable to the task, our team has developed Abstractive Analytics, an unique analytical tool capable of capturing and analyzing user interactions across your web-based systems, allowing you to improve your business flow. For more information or a demo please contact us at info@abstractive.ca.

 

Thanks for Visiting

Thanks for visiting the site. We certainly appreciate your interest in our company. If you have any questions or comments please feel free to contact us.

 

Gary Weller

Senior Business Intelligence Consultant

 

 

 

Eclipse Run As Java Application – NoClassDefFound

I started having a problem in Eclipse trying to run a Java class with a main() method. (Run As… Java Application).

It kept giving me a NoClassDefFoundException on the class I was trying to run. I swear this worked for this project before. It works fine if I create a new Java project.

I finally managed to fix it by adding a missing buildCommand to my .project file.

This was so frustrating that now that I finally figured it out I decided to share it. I’m using Eclipse Indigo on 64-bit Windows 7 by the way.

Before:

<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
  <name>MyJavaProject</name>
    <comment></comment>
    <projects>
    </projects>
    <buildSpec>
    </buildSpec>
    <natures>
      <nature>org.eclipse.jdt.core.javanature</nature>
    </natures>
</projectDescription>

After:

<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
  <name>MyJavaProject</name>
  <comment></comment>
  <projects>
  </projects>
  <buildSpec>
    <buildCommand>
      <name>org.eclipse.jdt.core.javabuilder</name>
      <arguments>
      </arguments>
    </buildCommand>
  </buildSpec>
  <natures>
    <nature>org.eclipse.jdt.core.javanature</nature>
  </natures>
</projectDescription>