Email: dev@concena.com       Twitter: @dbrucegrant

Entries with tag <em>concena</em>.

Nuxeo Partner Purge - What the ....

I find it sad and disheartening how Nuxeo (the company) behaves when it comes to long time partners and promoters of their technology.

Case in point: today I found out from a third party that Concena (my company) has been removed from the Nuxeo partner site. No communication from Nuxeo - no warning - no notification - just gone. One might think after 3 or 4 years on the site, as an active fan of Nuxeo, as somebody who spends time answering Nuxeo questions, that at least some form of communication from Nuxeo might be warranted. Apparently not.

Sadly this is not a unique incident, for me and for other vendors. I am almost used to this type of treatment from Nuxeo. I don't even know who to contact to discuss and rectify as the partner program appears leaderless. The last few times I have tried my inquiries went nowhere. 

Very strange behavior for a company that wants to grow their business, but ignores their longtime partners. 

Nuxeo - Room for Improvement - Part 2

Given that I'm working with DAM at the moment thought I would critique the current (5.5) version and suggest a couple of areas where there is room for improvement!

When 5.5 was released the alignment and integration of DAM/DM was a notable achievement. There are as I found still differences under the cover. A simple, but surprising difference - when I added in a new icon to replace the standard nuxeo logo I used the new 5.5 styling service. This worked for everything but DAM which still uses pre-5.5 styling (at least as far as I can see). From an ease of branding perspective it would be great if these were aligned across all 5.5 products and not just DM.

More importantly, in investigating custom filtering widgets I noticed that the FilterActions.java class has hard-coded Asset document definitions. I found this because I wanted to split out office documents into true office documents and PDF files. Adding a file type PDF and making it show up in the filter requires some work (beyond the scope of this blog entry) but it will only work by customizing and overriding the FilterActions class. The current class has a static arraylist that limits DAM asset types to File, Picture, Video and Audio. It would be much more flexible if this list were dynamic; based either on contribution or (even better) by any type contribution that has the facet Asset!

[Added 2012.02.08] And a recent post to Nuxeo Answers makes me pine for already-integrated Adobe XMP support!

Anyways, some DAM food for thought.

>> Nuxeo - Room for Improvement - Part 1

Nuxeo - Room for Improvement - Part 1

Maybe I'm biased, but I think Nuxeo is a great platform on which to build next-generation content solutions that will greatly improve on existing business processes and practices. And I'm also a big proponent of building solutions on top of Nuxeo to laser focus content management needs for specific business processes. That being said, there are still many situations where the use of the native Nuxeo client is both required and necessary. So, I thought it was time to start documenting some of the improvements I would like to see over the next 12 months! My two cents worth in any event.

First up is vocabularies. An absolutely critical part of any system - that is, the value sets that can be used in drop down lists, et.al., to populate document fields in an easy and controlled manner. I look at this particular aspect of the Nuxeo platform and think there is a lot of opportunity for improvement.

The top 3 improvements I would like to see with vocabularies:

  1. Support for an arbitray number of levels of hierarchical relationships. This would make it simpler to model client, brand, product, etc. type relationships common in many industries (and typically the data is fairly static).
  2. An improved user interface would go a long way to making vocabularies easier to use (and much better to demo). Better control over ordering of data should be included in the remake.
  3. Don't know about anyone else but it's typical in larger organization to have a separation of accountability for vocabularies. Would be nice to have a security model built around vocabularies to allow portions of the vocabularly model to be exposed (via rights assignments) to individuals and groups. And it would be great if that model allowed granular controls over addition, update, and deletion. And, although this is really a fourth item - it would be wonderful if part of the security model could force some vocabulary contributors to submit modifications through a controlled workflow!

And of course you can always workaround this missing functionality (at least sme of it) with a relatively small contribution. In my case I wanted the users in the "Voculary Managers" group to have a new User tab added to allow them to maintain vocabularies, so I made the following changes...

1. create a new action and a filter based on inclusion in a specified group

<component name="com.concena.my.dam.core.actions">
    <require>org.nuxeo.runtime.started</require>
    <require>org.nuxeo.ecm.platform.actions</require>
    <extension target="org.nuxeo.ecm.platform.actions.ActionService" point="actions">
        <action id="myDirectoriesManager" link="directory/view_directories.xhtml" label="title.vocabularies" order="50">
            <category>USER_CENTER</category>
            <filter-id>only_for_vocabmgrs</filter-id>
            <filter-id>not_readonly</filter-id>       
        </action>
    </extension>
    <extension target="org.nuxeo.ecm.platform.actions.ActionService" point="filters">
        <filter id="only_for_vocabmgrs">
            <rule grant="true">
                <condition>#{myUserServicesContext.vocabularyManagerEnabled}</condition>
            </rule>
        </filter>
    </extension>
</component>

2. created a new seam component to test for inclusion in the group

@Name("myUserServicesContext")
@Scope(SESSION)
@Startup
public class myUserServicesContext implements Serializable {

    private static final long serialVersionUID = -4938620211123775755L;

    @In(required = false, create = true)
    private transient Principal currentUser;

    /**
     * Checks to see if a user is a member of the "Vocabulary Managers" group.
     *
     * @return - true if the user is a member<br>
     *         - false otherwise
     */
    public boolean getVocabularyManagerEnabled() {
        if (currentUser == null) {
            return false;
        } else {
            return ((NuxeoPrincipal) currentUser).isMemberOf("Vocabulary Managers");
        }
    }

    .....

3. Modified view_directories.xhtml (not included here - just too messy) to change currentuser.administrator test. Take a look at the source!

4. And finally, created a group called Vocabulary Managers and added a test user to try it out!

That's it for now!

>> Also check out Room for Improvement - Part 2

GWT and Nuxeo Integration - Code Example

I have finally found a little bit of time to get back to the subject of my GWT integration with Nuxeo. I use GWT as a presentation layer for a number of document-centric applications. Here is a simple example of the code that makes the integration work. Keep in mind this article is meant only to demonstrate some key concepts - it is not a complete example or a step by step tutorial!

There are four main parts of code to the solution

  1. A typical custom Nuxeo plugin to model the backend document management application, document types, workflow, etc. This code provides the custom content/relationships that will be used in the GWT front-end, but is not of direct interest in this example.
  2. A rest API that implements the rest calls used by GWT to pass data to and from Nuxeo.
  3. GWT code that makes the rest calls, parses returning data, etc.
  4. GWT code that registers the necessary callback to something with data when it's returned

In the sample code, users in the front-end application log-in using a simplified login page, where all they have to is click on the icon with their name. In order to make this work the list of available users needs to be seeded. This is done by creating a special group in Nuxeo called "visual". Users added to the visual group will have an icon on the login page.

Starting with Nuxeo...

The list of available users is served up by Nuxeo using the GetVisualUsers api call. A call to GetVisualUsers returns an XML list of available user names. The list of available user names is built using the names in the visual group.

public class GetVisualUsers extends BaseStatelessNuxeoRestlet {
    @Override
    public void handle(Request req, Response res) {
      
        super.handle(req, res);
      
        UserManager userManager;
        DocumentModel userDocModel;
        ArrayList<String> userList;
      
        // setup repository access ... this sets up the session
        Boolean init = initRepository(res, Resources.DEFAULT_REPO);
        Boolean isRoot=false;
       
        try {
            userManager = Framework.getService(UserManager.class);
        } catch (Exception e) {
            Utils.handleError(res, Resources.RS_USERMANAGER_SERVICE_FAILED, Resources.EM_VISUAL_USERS_NOT_FOUND + " (group missing)");
            return;
        }
      
        try {
            userDocModel = userManager.getGroupModel("visual");
        } catch (ClientException e) {
            Utils.handleError(res, Resources.RS_VISUAL_USERS_NOT_FOUND, Resources.EM_VISUAL_USERS_NOT_FOUND + " (group missing)");
            return;
        }

        try {
            userList = (ArrayList<String>) userDocModel.getProperty("group", "members");
        } catch (ClientException e) {
            Utils.handleError(res, Resources.RS_VISUAL_USERS_NOT_FOUND, Resources.EM_VISUAL_USERS_NOT_FOUND + " (members missing)");
            return;
        }
      
        if (userList.size() == 0) {
            Utils.handleError(res, Resources.RS_VISUAL_USERS_NOT_FOUND, Resources.EM_VISUAL_USERS_NOT_FOUND + " (no members)");
            return;
        } 
      
        // now need to return the XML result to caller in the documented structure
        XmlResponse xmlResponse = new XmlResponse();
        xmlResponse.createResponseStatusTag(Resources.RS_OK);
        xmlResponse.createDataTag();
       
        for (int i = 0; i < userList.size(); i++) {
            xmlResponse.createTagAtCurrentElement("user", userList.get(i));
        }

        // write the response
        res.setEntity(xmlResponse.asXML(), MediaType.TEXT_XML);
    }

The XML returned from Nuxeo looks as follows...

<?xml version="1.0" encoding="utf-8" ?>
<response>
     <result>OK</result>
     <data>
            <user>Bruce</user>
            <user>Joe</user>
            <user>Sarah</user>
      </data>
</response>

And now on to GWT

Nuxeo produces the list of visual users in response to a Rest request made from the GWT code. The results, if successful, are rendered by a callback method. The end result: the user see a list of icons with names and can login to the system by clicking the appropriate icon.

When the GWT application starts up it calls showVisualLogin(...). showVisualLogin receives a reference to the base display panel, which is used to initialize the display and show the icons/names representing the available users.

Since the call to get the visual users is asynchronous, the most important part of showVisualLogin is setting the callback. The callback takes the results passed from Nuxeo and displays the login page!

Here's the code for showVisualLogin...

    private void showVisualLogin(final Panel panel) {
       
        setCurrentUserName("");
        userIsLoggedIn = false;
        basePresenter.getView().getLoginDataLine1().setText("");
        basePresenter.getView().getLoginDataLine2().setText("");
       
        modelSystemCache.loadCache();

        // GWT RPC: service call to get visual users
        LoginServiceAsync loginService = model.getRemoteLoginService();
        loginService.getVisualUsers(new SimpleCallback<String[]>() {

            @Override
            public void goBack(String[] result) {
                @SuppressWarnings("unused")
                String deleteMe = "";
               
                // GWT CALLBACK: on callback now is the time to build the login screen, display it and await for a click
                // on one of the user icons, at which time the history token will be set to 'search'
                final VisualLoginPresenter visualLoginForm = new VisualLoginPresenter(Environment.this, "", result,
                        new SimpleCallback<String>() {

                            @Override
                            public void goBack(String result) {
                                if (!result.equals("")) {
                                    userIsLoggedIn = true;
                                    startingToken = "search";
                                    setCurrentUserName(result);
                                    getMostRecent(result);
                                    timerActive = true;
                                    resetSessionTimer();
                                    showFirstPage();
                                } else {
                                    // need to handle error - display message and then let user try again!
                                    userIsLoggedIn = false;
                                    startingToken = "login";
                                    showAlert("Login failed. User no longer exists, password has changed, or system is down.");
                                }
                            }
                        });
            }});
       
        panel.clear();
    }

getVisualUsers is implemented in the GWT server package. The code is fairly straightforward, it creates a Nuxeo server object (defines connection details), sets up a rest call object, makes the rest call, and then confirms that the resulting XML returned an OK status (meaning there are no errors and the list of users is included in the returned XML).

Here's the code...

    @Override
    public String isVisualUserValid(final String name, final String password) {

        String baseUrl = getNuxeoUrl();
        // initialize the Nuxeo server and then make a rest call to pick up some real documents
        NuxeoServer ns = new NuxeoServer(baseUrl);
        NuxeoRestCall nrc = new NuxeoRestCall(ns); // setup a rest call
       
        String restCall = "sentiovaliduser";
        Representation myResult = nrc.doRestletGetCall(restCall, null, name, password);
       
        if (myResult == null) {
            return "0";                                    // call failed
        } else {
            // parse the result
            return parseStatus(myResult);                // call succeeded, but need to parse real status from XML
        }
    }

I have left out a lot of details, however, my intent is to give a sense of how to make GWT talk to Nuxeo, not (at least at this point) to provide a full tutorial on the subject.

Cheers,
Bruce.

Nuxeo Document Management Time to Market vs Out-of-the-box

Traditional, out-of-the-box vendors, may argue that the consulting effort and cost to implement a Nuxeo-based solution outweighs the no license cost benefit. This argument seems to be predicated on the fact that out-of-the-box document management solutions can be used out-of-the-box without customization or consulting. Maybe this is true in the most trivial case, but in any real application of document management I have never seen it. In fact, the rigidity of out-of-the-box solutions can have the effect of increasing the amount of consulting effort to realize client requirements.

Over the three years I have been working with Nuxeo I have seen a significant shift downward in the time and effort required to bring a customized Nuxeo solution online. Customers get the benefits of a solution that is tailored to meet their unique requirements along with reduced customization and implementation costs.

Much of this improvement is due to (1) being able to prototype in Studio, and (2) a bigger community from which to draw examples and resolve design issues. Moving forward, the new Nuxeo IDE will also contribute positively to the speed of development.

Showing 1 - 5 of 7 results.
Items per Page 5
of 2

Recent Entries Recent Entries

RSS (Opens New Window)
Showing 1 - 5 of 15 results.
of 3