Quantcast
Channel: Nuxeo Blogs » Product & Development
Viewing all 161 articles
Browse latest View live

Nuxeo Tech Talks – JavaScript@Nuxeo

$
0
0

Yesterday was our first Nuxeo Tech Talks, held in our Paris Office. The goal of these Meetups is to talk about a technology used by Nuxeo developers. This time we invited Mathieu Robin to talk about “JavaScript without Bugs,” and our CTO Thierry Delprat talked about JavaScript in the Nuxeo Platform.

Mathieu Robin

As Thierry said in his presentation, Nuxeo is a Java shop. Some of us are not completely up to date with the latest evolutions, tools etc… And since we’re going to do a lot more JavaScript, we thought it would be a very good topic for the first Nuxeo Tech Talk.

Mathieu’s presentation was basically about what can seem/is weird in the language. You know like the scope of the this object… I also discovered that you could throw Exceptions in JavaScript just like Java. Then he showed us some of the tools available to ease the pain of writing JS code. I m sure we’ll find a lot of interesting things when we setup JSHint. I invite you to check out his slidedeck on his GitHub repository.

Thierry DelpratThen Thierry went on with JavaScript@Nuxeo. He gave his thoughts about JavaScript as a language and then explained how it’s currently used and will be used in the Nuxeo Platform. You can expect a lot of changes around this, one of the first being nuxeo.js. It’s here to wrap our REST API in a nice way. Take a look at the slide deck at the bottom for the details. And you can see the JS shell he developed on the presentation he gave at Nuxeo World 2013.

Now if you want to hear about a specific technology related to Nuxeo, tell us and we’ll schedule a tech talk about this. And if you want to get updates about the next sessions, make sure you join the meetup group!

A big thanks to Mathieu, who was our first speaker and to all the people that came to this first Nuxeo Tech Talk. I hope we’ll see you at the next one :)

The post Nuxeo Tech Talks – JavaScript@Nuxeo appeared first on Nuxeo Blogs.


Creating a Thumbnail Browsing Module with AngularJS and the REST API – Part 3

$
0
0

Two weeks ago I started a blog post series on how to create a thumbnail browsing module with AngularJS and our REST API. The first part was about getting familiar with the API. The second part was how to use it in AngularJS. The example was simple. A user goes to a URL with a tag and we display the search result with infinite scroll.

This third part focuses on getting the thumbnail with Busines Object adapters, making it look better, and deploying the project easily on Nuxeo.

Using the Business Object

In the first post we saw how to use a business object adapter with the new API. It’s really easy to plug into our app. All we have to do is tell the controller:

.controller("SlideshowCtrl",
['$scope','$routeParams','nxSearch'
($scope,$routeParams,nxSearch) ->
  $scope.searchTag = $routeParams.tag
  nxSearch.setQuery("SELECT * FROM Document WHERE ecm:tag = '" +  $scope.searchTag + "'")
  # set the BOAdapter using its name
  nxSearch.setBOAdapter("SlideShowElement")
  $scope.items = nxSearch.items
  # Expose the busy variable
  $scope.busy = nxSearch.busy
  # Expose the nextPage method
  $scope.nextPage = ()->
     nxSearch.nextPage()

])

This works because the nxSearch service was made to support this capability. All we have to do is add a bo object, a setter and one line of code in our nextPage method: if nxSearch.bo? then url += "/@bo/" + nxSearch.bo

angular.module('nuxeoAngularSampleApp')

.factory "nxSearch", ["$http","$q","nxUrl",($http,$q,nxUrl)->

  nxSearch = {}
  nxSearch.items = []
  nxSearch.busy = false
  nxSearch.isNextPageAvailable = true
  nxSearch.currentPageIndex = 0
  nxSearch.pageSize = 20
  nxSearch.query = undefined
  nxSearch.bo = undefined

  nxSearch.setBOAdapter = (bo)->
    nxSearch.bo = bo

  nxSearch.setPageSize = (pageSize)->
    nxSearch.pageSize = pageSize

  nxSearch.setQuery = (query)->
    nxSearch.query = query

  nxSearch.nextPage = ()->
    if !nxSearch.query?
      $q.reject("You need to set a query")
      return

    if !nxSearch.isNextPageAvailable
      return

    if nxSearch.busy
      return

    nxSearch.busy = true

    url = nxUrl + "/path/@search"
    # if the bo object exist, add the @bo adapter to the URL
    if nxSearch.bo? then url += "/@bo/" + nxSearch.bo
    url += "?currentPageIndex="+nxSearch.currentPageIndex+"&pageSize="+nxSearch.pageSize+"&query=" + nxSearch.query;
    $http.get(url).then (response) ->
      docs = response.data
      if(angular.isArray(docs.entries))
        nxSearch.currentPageIndex = docs.currentPageIndex + 1
        nxSearch.isNextPageAvailable = docs.isNextPageAvailable
        nxSearch.items.push doc for doc in docs.entries
        nxSearch.busy = false
      else
        nxSearch.busy = false
        $q.reject("just because")

  nxSearch
]

And of course now we need to update our slideshow.html template to reflect these changes. I’ve added a div tag containing an img tag that shows our thumbnail. Note that we have to use item.value instead of simply item because we are manipulating business objects instead of simple documents. We might try to be more consistent in the next version of the API. Let me remind you that it’s still a beta version, so some things might still change a bit.

<h2>{{searchTag}}</h2>

    <ul infinite-scroll='nextPage()' infinite-scroll-disabled='busy' infinite-scroll-distance='1'>
      <li ng-repeat='item in items'>
        <span>{{item.value.title}}</span>
        <div><img src="{{item.value.thumbnailURL}}"></div>
      </li>
    </ul>
  <div ng-show='busy'>Loading data...</div>

</div>

Now you should see something like this:
Thumbnails without style

Beautifying the Thumbs

It looks a little raw, so we can use semantic-ui to make it nicer – it’s a little more WYSIWYM than bootstrap. Check out a comparison of the two on their home page. To install semantic-ui, we use bower and simply type:

bower install semantic-ui --save

The module is installed, but we still need to add the css stylesheet to our index.html page.  We added this to our header:

    <link rel="stylesheet" href="components/semantic-ui/build/packaged/css/semantic.css">

Now we can start using it on slideshow.html and immediately see the results:

<h2>{{searchTag}}</h2>

<div class="main ui three items" infinite-scroll="nextPage()" infinite-scroll-disabled="busy" infinite-scroll-distance="1">
  <div class="item" ng-repeat="item in items">
    <div class="sides">
      <div class="active side">
        <div class="image">
          <img ng-src="{{item.value.mediumURL}}" width="100%">
        </div>
        <div class="content">
          <div class="name">{{item.value.title}}</div>
          <p class="description">{{item.value.description}}</p>
        </div>
      </div>
    </div>
  </div>

  <div ng-show='busy'>Loading data...</div>

</div>

It already looks much better:
Thumbnails with Style

But now we have bootstrap, which was already in the sample we started working on, and semantic-ui. So let’s clean this up. We can also remove all the parts we don’t use like the nxNavigation and nxSession services, or the controllers and pages we don’t use. We already did this work so you can check out the sources of the cleaned and package version on Damien’s GitHub repository.

About Packaging

Our goal is to deploy the angular project into nuxeo.war. To go further, we want to package the AngularJS app in a jar to deploy it in Nuxeo. The tool responsible for packaging the jar is Maven. We need to tell Maven to embed the app. To do so, we use the maven antrun plugin like this:

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.7</version>
        <executions>
          <execution>
            <id>grunt-build</id>
            <phase>process-resources</phase>
            <configuration>
              <target>
                <ant dir="${basedir}/src/main/yo" antfile="${basedir}/src/main/yo/build.xml" />
              </target>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

This configuration executes the ant using the ${basedir}/src/main/yo/build.xml ant build file. The build file actually calls Grunt to package the app. Grunt is a JavaScript task runner. As you can see in this file, ant executes the grunt build command.

<?xml version="1.0"?>
<project name="Grunt build" default="build" basedir=".">

  <target name="init" description="Download all node and web dependencies">
    <exec executable="npm">
      <arg value="install" />
    </exec>
    <exec executable="bower">
      <arg value="install" />
    </exec>
  </target>

  <target name="build" depends="init,build-with-tests,build-skip-tests" />
  <target name="build-with-tests" unless="skipTests">
    <echo message="build and test" />
    <exec executable="grunt">
      <arg value="build" />
    </exec>
    <mkdir dir="../../../target/surefire-reports" />
    <copy file="test-results.xml"
          todir="../../../target/surefire-reports"
          failonerror="false" />
  </target>
  <target name="build-skip-tests" if="skipTests">
    <exec executable="grunt">
      <arg value="build-skip-tests" />
    </exec>
  </target>

</project>

We still need to do one thing, which is to tell Grunt where to build our app. This can be done easily by adding the following code to our GruntFile:

  // configurable paths
  var yeomanConfig = {
    app: 'app',
    dist: '../../../target/classes/web/nuxeo.war/slideshow'
  };

Now when we run mvn clean install, we get a jar file (target/nuxeo-slideshow-sample*.jar) containing everything we need. Take a look at the content of the jar:

.
├── META-INF
│   ├── MANIFEST.MF
│   └── maven
│       └── org.nuxeo.ecm.angular.sample
│           └── nuxeo-slideshow-sample
│               ├── pom.properties
│               └── pom.xml
├── OSGI-INF
│   ├── authentication-contrib.xml
│   └── deployment-fragment.xml
├── extensions
│   └── org.nuxeo.slideshow.SlideShowElement.xml
├── nuxeo-slideshow-sample-1.0-SNAPSHOT.jar
├── org
│   └── nuxeo
│       └── slideshow
│           ├── SlideShowElement.class
│           └── SlideShowElementFactory.class
└── web
    └── nuxeo.war
        └── slideshow
            ├── 404.html
            ├── favicon.ico
            ├── fonts
            │   ├── glyphiconshalflings-regular.eot
            │   ├── glyphiconshalflings-regular.otf
            │   ├── glyphiconshalflings-regular.svg
            │   ├── glyphiconshalflings-regular.ttf
            │   └── glyphiconshalflings-regular.woff
            ├── index.html
            ├── robots.txt
            ├── scripts
            │   ├── 22d0f6c5.scripts.js
            │   └── 4c97588d.components.js
            ├── styles
            │   ├── d2f2e407.main.css
            │   └── e1cdc6e0.components.css
            └── views
                └── slideshow.html

There are two XML files I have not talked about yet. Let’s start with deployment-fragment.xml. It does two things. First, it adds the URL pattern /slideshow/* to the NuxeoAuthenticationFilter. This is to make sure authentication is required when going to URLs like /slideshow/*.  Next, unzip the app in nuxeo.war.

<?xml version="1.0"?>
<fragment version="1">

  <extension target="web#STD-AUTH-FILTER">

    <filter-mapping>
      <filter-name>NuxeoAuthenticationFilter</filter-name>
      <url-pattern>/slideshow/*</url-pattern>
      <dispatcher>REQUEST</dispatcher>
      <dispatcher>FORWARD</dispatcher>
    </filter-mapping>
  </extension>

  <install>
    <!-- Unzip the contents of our nuxeo.war into the server -->
    <unzip from="${bundle.fileName}" to="/" prefix="web">
      <include>web/nuxeo.war/**</include>
    </unzip>
  </install>

</fragment>

Now about authentication-contrib.xml. It defines slideshow as a startURLPattern. A start URL pattern is used the first time you browse the application. If you hit a URL for which a pattern is registered, you’ll see the login screen asking you to sign in. And if you do so, you’re redirected to the URL you’ve hit in the first place. If the URL you try to hit does not match any startURLPattern, you are still redirected to the login page, but once you sign in, you’re redirected to the default webapp.

<component name="org.nuxeo.angular.sample.authentication.contrib">
  <extension
      target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
      point="startURL">

    <startURLPattern>
      <patterns>
        <pattern>slideshow/</pattern>
      </patterns>
    </startURLPattern>

  </extension>

</component>

And now we’re ready to go. We can build the jar and copy it in our bundles directory. Then we go to http://localhost:8080/nuxeo/slideshow/ and try our app deployed in Nuxeo.

 

The post Creating a Thumbnail Browsing Module with AngularJS and the REST API – Part 3 appeared first on Nuxeo Blogs.

[Q&A Friday] How to get logger from within a MVEL script in Nuxeo Studio?

$
0
0
How to get logger from within a MVEL script in Nuxeo Studio?

How to get logger from within a MVEL script in Nuxeo Studio?

Today we have a question from regular Nuxeo Answers user Antoine. He asks how to get logger from within a MVEL script in Nuxeo Studio.

If you are not familiar with MVEL, it’s the script language that we use in Studio. It gives a lot of flexibility when writing operation chains.

When writing those scripts, you have access to certain variables:

  • CurrentDate: wrapper for a Calendar instance
  • Context: the operation context
  • This: the operation input
  • Session: the CoreSession
  • CurrentUser: Principal wrapper around the user running the chain
  • currentUser: CurrentUser alias
  • Env: the Framework properties
  • Fn: wrapper to the CoreFunctions class
  • Document only if the input is a document, wrap the input in a DocumentWrapper
  • CurrentDocument: alias to Document
  • Documents: only if the input is a list of documents if, wrap them in a DocumentWrapper.
  • WorkflowVariables: Available only in a workflow context
  • NodeVariables: available only in a workflow context

But in all those objects available in the context, there is nothing that gives you the logger. So you have to instantiate it yourself like this:

org.apache.commons.logging.LogFactory.getLog("RunScript").debug("Something happened.");

And if you have some long running script you can do it like this:

logger = org.apache.commons.logging.LogFactory.getLog("RunScript");
logger.debug("Something happened.");
....
logger.debug("Something else happened.");
...

This works because you have access to most of the objects available in Nuxeo’s classpath.

Just make sure log4j is configured according to the name of your logger. Here I’ve chosen >RunScript so I have to add the following line to my $NUXEO_HOME/lib/log4j.xml file.

  <category name="RunScript">
    <priority value="DEBUG" />
  </category>

The post [Q&A Friday] How to get logger from within a MVEL script in Nuxeo Studio? appeared first on Nuxeo Blogs.

Updated: Check Out the UI Style Guide for Nuxeo Platform 5.8

$
0
0
Nuxeo UI Style Guide for 5.8

Nuxeo UI Style Guide for 5.8

The new UI Style Guide for the Nuxeo Platform 5.8 is fresh and released!

This guide is an online demo to help you developers and designers learn how to design your platform interface.

We published a post for the first version explaining what’s inside and why you should follow it. A second post explained why a style guide is important for developers as well for designers when building a product.

What’s New in the Guide?

There are a few changes, including:

  • Tables: New options are available to design your tables and move from a simple listing to beautiful data organization.
  • Messages: Feedback messages are fully reviewed, status messages have been updated and new classes are available to highlight elements in your content.
  • Buttons: The classes are more accurate to adjust your buttons and lead the user to the logical action.
  • Navigation: The styles of tabs, modular tabs, subtabs and breadcrumbs are now more complete to improve the navigation experience.
  • Boxes: New styles are availables to improve the widgets, layouts and tab content display: grayBoxes, simpleBoxes and .textBlock.

Consistency, Maintainability, Compatibility, Yes!

This guide’s role is to improve consistency in UI patterns and usability in your interface for the key users of your product. But as we know, behind every product there are developers with their concerns, so the guide’s role is also to be a shield for maintainability and compatibility.

The great news is that even if styles have evolved to be sleeker, more robust and complete, we garantee you that no styles will be broken during your upgrade from the previous version to the new Nuxeo Platfrom 5.8.

That is the bonus effect. You feel more relaxed now, right?

The post Updated: Check Out the UI Style Guide for Nuxeo Platform 5.8 appeared first on Nuxeo Blogs.

[Q&A Friday] How to Create a Nuxeo Studio Defined Document on File Drag and Drop

$
0
0
How to Create Nuxeo Studio Defined Document on File Drag and Drop

How to Create Nuxeo Studio Defined Document on File Drag and Drop

Today we have a question from carlosrg and vjoussot who ask how to use a document type defined in Studio when a file is dragged and dropped in Nuxeo. A classic approach would be to override the classic Document Type like File, Picture, Audio or Video. But what happens when you want to keep the original document type? Well, you have to modify the way files are imported on drag and drop.

And the cool thing about this answer is that it comes from brian, another active community member. Here’s what he has to say:

Say you have document types SampleCustomPicture, CustomVideo, and CustomAudio — under “Projects>Advanced Settings>XML Extensions” do “+New” and create an extension with this content:

<require>org.nuxeo.ecm.platform.picture.filemanager.contrib</require>
<require>org.nuxeo.ecm.platform.video.filemanager.contrib</require>
<require>org.nuxeo.ecm.platform.audio.filemanager.contrib</require>

<extension target="org.nuxeo.ecm.platform.filemanager.service.FileManagerService" point="plugins">
    <plugin name="Imageplugin" merge="true" docType="SampleCustomPicture" />
</extension>

<extension target="org.nuxeo.ecm.platform.filemanager.service.FileManagerService" point="plugins">
    <plugin name="VideoImporter" merge="true" docType="CustomVideo" />
</extension>

<extension target="org.nuxeo.ecm.platform.filemanager.service.FileManagerService" point="plugins">
    <plugin name="AudioImporter" merge="true" docType="CustomAudio" />
</extension>

https://gist.github.com/tingletech/7574407

Kudos to Brian for providing the answer. Now how does it work? Well, when you drag and drop a file in Nuxeo, the plugins extension point is used. It lets you define a Java class to handle the import logic. Each plugin has an associated document type, some mime types and an order. The plugin with the lowest order and matching mime type is used. The plugin can either return a document or nothing. If it’s a document, the import is done. If it’s nothing, the next in order plugin is called.

Here all you have to do is modify the docType attribute to make sure your document types are used. Don’t forget the require tag to make sure your contribution will be deployed after the one you want to override.

If you want to go further, I recommend reading this blog post explaining how to create your own plugin implementation. This can help a lot especially for custom metadata you might have defined with Nuxeo Studio.

The post [Q&A Friday] How to Create a Nuxeo Studio Defined Document on File Drag and Drop appeared first on Nuxeo Blogs.

[Nuxeo Tech Report] News from the Developer Front #12

$
0
0

For DevelopersHere comes the first tech report since Nuxeo World and the release of our latest LTS Nuxeo Platform 5.8.

Focus for 5.9.1

Here are the main tasks we’ll be working on for the few next weeks.

nuxeo.io

API

Introspection

We have a REST API but so far we don’t have a way for a remote client to easily introspect the structures available on the server side:

  • Schemas
  • Document Types
  • Widgets
  • Layouts

Having such an API endpoint does make sense:

  • Mobile SDK needs require it to fetch document types and layouts to generate display
  • The Mule connector requires it to fetch document types to make data mapping easier
  • Studio would need it to build the target platform definition from a live distribution
  • CMIS has this (at least for schemas/types)

We already have:

  • A widget/layout endpoint that was initially done for Studio and Android SDK
  • A schema/doc endpoint that was added for the Mule Connector

Globally what we would like is a consistent API:

GET /nuxeo/api/v1/config/types
GET /nuxeo/api/v1/config/widgets
GET /nuxeo/api/v1/config/layouts

In addition to the pure REST binding, we probably want:

  • A simple WebEngine UI (Like what we have for Automation?)
  • A support in platform explorer to:
    • Persist definitions associated to distribution
    • Allow simple browsing

NXP-13282 was created as an umbrella task.

REST Bindings Pluggability

We have the first level of pluggability for WebEngine via the Fragments system. Unfortunately, we cannot at the same time contribute a WebEngine fragments to an extension point.

For now, we have to use two different modules, but:

  • It does not make sense
  • It can be tricky

The task to fix this problem is probably for Stephane: NXP-13283

REST Binding TCK

The current TCK page we have is not a ‘real’  TCK, meaning you cannot run the tests from it. And it is not really documentation either. But it can be useful for someone that wants to understand how Nuxeo HTTP APIs work. We can probably do better.

Damien suggested to use fitnesse to have readable tests. So far we did not find a way to use it for that purpose (validating a client), but any volunteers or ideas are welcome.

iOS SDK

Arnaud is working on the iOS connector with our friends from Smart&Soft.

API Bindings

We have an initial binding in Objective-C with:

  • REST Document endpoint
  • Operation API

The binding no longer uses RESTKit, but a bare HTTP lib (ASI Http). The API returns Document object wrapping of bare JSON objects.

SDK Packaging

Nuxeo SDK is exposed via CocoaPods for dependencies management: https://github.com/nuxeo/cocoapods-specs.

Build and Tests

The connector is build by Jenkins: tests include calling an API on a locally deployed Nuxeo server.

Ideally:

  • The existing test should be aligned with the TCK
  • iOS client should be added to the TCK page
Next Steps

Next, we’ll work on making the connector able to manage cache for offline usage.

Adding the Cache

ASI Request allows us to easily hook the HTTP request/response to add caching. But, this is still to be done:

  • Hook caching in HTTP layer
  • Manage storage (SQLLite for structure + Blob for response)
Tree Cache

Some apps will manage a tree of documents. This tree needs to be accessible offline. We have 2 options:

  • Retrieve the tree using n recursive 'getChildren' requests
    • We can use the standard request level caching
  • Retrieve the tree in one query
    • We need to introduce a dedicated API and a higher level of cache

NB: we should be able to compare with the Android logic that uses a “per list caching logic”.

API Playground

We should work on building a nice API playground, something like a hybrid between:

  • Nuxeo Explorer
  • The Automation Doc
  • The REST API doc

We’ll also have to define what is the target server for the playground:

  • For nuxeo.io users/customers, the target is clear: their instances
  • For other users that just want to take a look
    • Use a central site like demo.nuxeo.com?
    • Use Docker and a Core Server to provision on demand short lived instances?
    • Provide an AMI that people can use with a free AWS account?

Nuxeo IDE

Nuxeo IDE 1.2

Vlad is working on a new version of Nuxeo IDE.

IDE and Studio Integration

Anahide added a new registry on the Studio side so that the IDE can export operation without overriding existing configuration NXS-1242. For the multi-bundles use case, this is something we should probably handle on the IDE side (merge operation from all bundles before sending to Studio).

Make Nuxeo Less Stateful

Current Java API and WebFramework have inherited a stateful nature (EJB3 SFSB + JSF). With 5.9, it is time to start cleanup and lighten our framework.

Better Core Session Management

The stateful nature of the repository API creates concurrency issues. Rather that focusing on shielding problems of session sharing, we should work on making it almost stateless. It will be a much better solution.

The solution is to disconnect the DocumentModel object and make the reconnect dynamic. This implies:

  • Remove usages of sid
  • Remove usages of CoreInstance.getInstance

Florent started the work NXP-13148

We also need to discuss how we can make DocumentModel ThreadSafe. While doing this cleanup work, it will logically push our code to rely heavily on transaction demarcation. As a result, we may have to migrate all tests in Transactional mode:

Reduce Seam Beans Scopes

If we better manage CoreSession state, we should be able to make all our bean short lived:

  • This would reduce the memory footprint
  • We could remove part of the code to manage invalidations

We’ll try to address that in 2 steps:

  • 1: Reduce the Seam beans scope
    • Remove invalidation code
    • Simplify navigation logic
  • 2: Disconnect CoreSession and DocumentModels
    • Leverage LayoutDemo use case

Umbrella task is NXP-13163

CI and QA Status

Having a good CI chain continues to be a critical and never ending task since:

  • We continue extending the platform (hopefully)
  • We support more deployment targets
  • We continue adding new commiters
  • We do releases more often

Build and Path Length

In the context of Jenkins duty, Florent did some renaming to shorten the path that made the Windows build failed: NXP-8659

NB: because, yes, even in 2013, Windows command line still has path length restriction.

 Maven3 Migration

The main work is about distribution tools. Done so far:

  • Added some unit tests on previous assembly (needed to validate migration)
  • Validated that we can run a dependency tree inside a custom assembly plugin

To be done:

  • Port the existing commands of distribution tools to the new Maven 3 model

Considering that:

  • We need to migrate to maven3 for a long time
  • This should solve some bugs
  • This should improve the performances

Ongoing Tasks and Issues

Nuxeo Cluster Management

We want a way to define and deploy a set of Nuxeo Marketplace packages on all nodes of a cluster. Typical use cases include:

  • Install an addon on all nodes
  • Install a hoffix on all nodes
  • Update a studio package on all nodes

This requirement exists:

  • For nuxeo.io
  • For all customers using a cluster

We created NXP-13272 as an umbrella task, see the task description for more info.

Mule Connector

The Mule Connector is almost completed:

  • Automation Client is embedded
  • Mule Connector can now listen to Nuxeo events
  • Mule Connector can introspect Nuxeo types

Alain started writing nice documentation on using Mule’s format.

VCS Repository

VCS ACL System Optimizations

Ben played a lot recently with the ReadACL system. He also implemented a new Simpler ReadACL system that allows you to skip one indirection in the security checking process.

It should be faster, but the trade-off is that you cannot have negative permissions: only positive permission and block all. So far, the tests have not shown a direct improvement, but still:

  • We will add this mode for PGSQL even if we don’t activate it by default
  • We will add global switch (nuxeo.properties) to activate simple ACL mode

 Query and Big Sorting

We have some PageProvider queries that use sort on a nullable field. It looks like in this case, PGSQL cannot efficiently use the index. It was fixed by making the PageProvider implementation automatically add the not null clause in the NXQL Query.

Full text/TS_Vector issue

Since 5.7, in PGSQL, we store the full text in clear (not ‘vertorized’) inside the database: this is required to allow “phrase search”. Unfortunately, not storing directly the TS Vector makes the search query slower as the number of rows grows.

Options are:

  • Store only the TS_vector
    • Drop phrase search
  • Store TS_Vector and plain text
    • Make storage bigger

We need to:

  • Document the options
    • Explain the alternatives
  • Provide some migration tools
    • At least SQL scripts

NB: the real solution is to get the full text out of the SQL DB and use Elastic Search, but that’s for later.

Automation Server

Automation Marshaling Issue

The ResponseHelper used by default in Automation needs to be reviewed so that we avoid hard-coding marshaling logic. As long has we have this hardcoded class: adding a JAX-RS ResponseWriter also means updating this class.

Exception Handler is Different Between WebEngine and Automation

Automation Exception management has been improved, but we still have some specific processing that should not be here.

  • Returning 404 when a document is not found does not make sense in the context of an Operation
  • 404 should mean resource not found => operation or chain is not found
  • We sometimes return 403 instead of 401

This means we need to review how we manage errors and associated HTTP status.

500: generic server; error 404: resource not found (operation or chain); 403: forbidden (not enough rights); 401: not authenticated (need to be logged); 409: conflict (dirty update)

This work also includes some changes on the PageProvider.

Existing task (NXP-12493) to be completed.

Nuxeo Tech Talk

The topic of the next Nuxeo Tech Talk is AngularJS. We’ll welcome Julien Boucquillon who will talk about AngularJS in mobile apps. Damien Metzler will give an overview of the work we’ve done so far around AngularJS.

The post [Nuxeo Tech Report] News from the Developer Front #12 appeared first on Nuxeo Blogs.

[Q&A Friday] How to Remove Tabs, Buttons, Links Under a Specific Path

$
0
0
How to Remove Tabs, Buttons, Links Under a Specific Path

How to Remove Tabs, Buttons, Links Under a Specific Path

Today we have a question from guian who asks how to remove the notification tab in the administration panel for each folder inside: /default-domain/workspaces/myWorkspace/aSpecialFolder/.

To answer that question, the first thing you need to know is that a tab is what we call an Action. An action can also be a link (a tab is basically a link styled as a tab) or a button, anything clickable in fact. And as stated in the documentation, you can filter theses actions. Take a look at the filter extension point in Nuxeo Explorer. As you can see there are several ways to filter actions.

Let’s say that the documents where we want to hide the notification tabs are all of a special document type. We could add a filter like this:

  <extension point="filters" target="org.nuxeo.ecm.platform.actions.ActionService">
    <filter id="notSpecialDocType">
        <rule grant="false">
            <type>SpecialDocType</type>
        </rule>
    </filter>
  </extension>

The action associated with this filter would not be displayed if the current document type was SpecialDocType. To associate this filter to an existing action, you need to override the action definition. To do that you need to know the id of the action you want to override. Most of the times if you look at the html code of the button or action you want to override, you’ll see the action id in the element id. You can also use the search in Nuxeo Explorer. Here, the id of the notification sub tab is TAB_MANAGE_SUBSCRIPTIONS. So to override the action, you have to do as follow:

  <extension target="org.nuxeo.ecm.platform.actions.ActionService" point="actions">
    <action id="TAB_MANAGE_SUBSCRIPTIONS">
      <filter-id>notSpecialDocType</filter-id>
    </action>
  </extension>

And this is all you have to do. Now this is not exactly what was asked in the question. The filter needs to apply for all documents under a specific path. So you would have to use the condition filter as follow:

  <extension point="filters" target="org.nuxeo.ecm.platform.actions.ActionService">
    <filter id="notInASpecialFolder">
        <rule grant="false">
            <condition>#{currentDocument.getPathAsString().startsWith('/default-domain/worspaces/myWorkspace/aSpecialFolder/')}</condition>
        </rule>
    </filter>
  </extension>

Make sure you don’t use an EL too heavy to evaluate. Filters are evaluated regularly so badly designed filters can have an impact on performance. If you want to know more about filters, make sure you read the appropriate documentation.

The post [Q&A Friday] How to Remove Tabs, Buttons, Links Under a Specific Path appeared first on Nuxeo Blogs.

Nuxeo @ dotJS 2013

$
0
0

dotJS
Yesterday I was at DotJS with some Nuxeo folks because, you know, JavaScript is the new shit. So we need to stay up to date and we could also get some ideas for blog posts. It was quite interesting.

You were thrown off guard right at the beginning when Sylvain Zimmer, who was our MC for the day, asked us to introduce ourselves to our neighbors. They do this at every dot conference and it’s actually quite cool. It sets everybody in the right mood.

Addy Osmani had the difficult task to open the conference. He talked about JS tooling, Web Components, Polymer, about building big applications in HTML. One of the first things he showed was complex tags. Basically you hide complexity in a single HTML tag. Funny how it feels like JSF, only closer to the browser.

He also talked about the modern front end developer workflow, about the Grunt, Bower, Yeoman triptych. We started using it at Nuxeo and it does makes life easier. We actually plan to build a Yeoman generator for several basic JavaScript Nuxeo applications.

The Nuxeo Crew

The Nuxeo Crew

I think my favorite talk of the day was given by Guillermo Rauch. I strongly recommend that you go through his slidedeck, it’s already online. The title speaks for itself: The need for speed – Single-page apps. Optimistic UIs. Reactivity.

It’s about the benefits you’ll gain from building applications closer to the browser, using the browser as a platform. And it’s also about the perception of speed. Changing the layout of your page when the users starts an action will give an impression of speed. He took the Google landing page as an example. When you start typing something in the search box, the layout changes immediately, even if the search query is not over. This still gives an impression of speed, and you don’t have to display a lousy spinner or hourglass.. Seriously, go through his slidedeck, it’s great.

As a more general feeling, many speakers talked about the JavaScript tooling, to make JS easier to work with, easier to learn. Some went further and used languages that compile to JS. It feels like everybody wants to use the the browser as a platform, but are stuck with the native, cross-browser language that is JS. And everybody is trying to change that, whether by enhancing tooling, creating new web languages or enhancing JS virtual machines. Let’s just hope every browser vendor will find some common ground!

The post Nuxeo @ dotJS 2013 appeared first on Nuxeo Blogs.


A Simple Workflow for the Human Resources Department

$
0
0

I am going to publish a series of short posts on how we use the Nuxeo Platform internally at Nuxeo. As you will see, there are indeed several use cases that are worth sharing. Eating our own dog food (as it is called) is very important in our product management process: it helps us understand what the limits of the platform are, it gives us the desire to continuously improve it, and it is also a great way to test the product in some more specific ways.

As you can imagine, maintaining a platform is far more complex than a simple out-of-the-box application, with a framework the test cases can be infinite. Thanks to this process, we are able to discover and fix a great number of bugs before releasing!

I’ll skip the basic document sharing use case as there are no surprises here: Nuxeo Platform is our intranet tool, and everybody uses it as well as the Drive extension for sharing documents internally. We also have more advanced use cases that I will share with you in this series:

  • Our vacation request workflow
  • The internal Techlead report publishing using the template rendering module
  • Nuxeo Connect and Marketplace applications – all Studio based
  • A data extraction chain made using Studio
  • Our customer reference management tool
  • A simple Q&A module
  • Our QA report plugin
  • A training / consulting workflow

Vacation Request Workflow

I’ll start today with the vacation request workflow – one of the oldest use cases for our platform. As in every company, people have to take vacations at Nuxeo (note the very corporate “have to” ;-). Asking for vacation at Nuxeo used to be an email-based process. This was working quite well, but was a nightmare for Ornella, our HR manager, when it came to checking what was taken for a give period of time.

As soon as we introduced Automation in 2010, we decided to implement this simple process on our intranet, a bare Nuxeo Platform. Since then, the Time Off Request module has been continually improved, following the evolution of Nuxeo Platform’s workflow capabilities. This included the use of the Tab Designer in 2011, Content Routing at the end of 2011, and the newer Content Routing features in 2013 (like escalation).

The workflow is quite simple:

The user launches the request:

Launching a time off request

Then the user fills the vacation request: number of days, dates and comment:

Filling the form

Then the request is submitted to the manager of the employee. Finally it goes to the HR Manager, who logs it into our internal ERP for the wages edition. It is possible to go back and forth at every step.

Workflow graph

Users are notified via email, with details of the request included in the email. Some callbacks are sent when the manager processes the request late. It is also possible for the user to ask for a modification of an already validated request, as well as a cancellation.

A dashboard enables a user to see specific time-off-request process tasks, and to browse past requests. A specific ACL policy is applied so that a normal user always sees his/her own requests, whereas a manager can view the requests of his/her team, and the HR manager views all requests. Color codes are used at every step to show if the request has been accepted, is in the validation process, or was refused. The HR manager can filter by dates and user, and can do an Excel export of all the data, so as to be able to do some yearly aggregation, or other computation.

Dashboard_flou

After 2-3 years of being used, we can say the application did reach its goals of improving the trace-ability of vacation requests, and definitely saved Ornella’s time, while not consuming too much of mine. Of course we followed all of the Nuxeo Platform releases and improved the plugin each time.

One improvement we would like to add in the future is to post in our internally shared Google time-off calendar the periods where an employee is absent. This should not be too hard to add, although it will require a bit of Java (there is currently no Java in the plugin, it is all done via Studio). Ornella recently asked for more workflows – with Nuxeo hiring many consultants and developers, streamlining this process is getting critical!

The project is shared as an application template in Studio (I just re-shared it as it was a very old version that was shared up until now). Since the implementation started even before content routing existed, the implementation state is limpid as if I was starting this project just now. There is a Time Off Request document object. I use the workflow to fetch the data from the user, and then persist it on the document in the output chains of the nodes of the workflow. I use the operation StartWorkflow to start the workflow immediately after creation of the time off request, without requiring the user launch it manually.

The Modification/Cancellation request is another workflow that can be started on the Time Off Request document type. I use the setACL operation with a specific “HR” ACL (Nuxeo Platform handles named ACLs) that I set after each node for controlling whether the time off request can be modified, and by who. For letting the system know who is the boss of who, I use a vocabulary. Ornella fills the correspondence table between username of the employee (in the id field of the vocabulary) and username of their manager (field “label” of the vocabulary). Then, in the workflow, on the node where a task is assigned to the manager, I “compute” the manager username in the input automation chain, using the following MVEL instruction:

user:@{Fn.getVocabularyLabel(“EmployeeManagerMapping”,Document['dc:creator'])}

For the dashboard, I use some content views.

FullRequestListing

To better understand how to work with workflows in the Nuxeo Platform,  you can read the documentation and do this simple tutorial.

The post A Simple Workflow for the Human Resources Department appeared first on Nuxeo Blogs.

Nuxeo IDE 1.2 Released!

$
0
0

We have just released a new version of Nuxeo IDE. It’s an Eclipse plugin that dramatically eases the development of Nuxeo modules. You can get it from our update site or from the Eclipse marketplace.

Code Completion

Among the new features available is XML completion for components and extension points. This has been developed by an external contributor, Sun Tan, that some of you may know since he used to work at Nuxeo and now works at Serli. He gave a lightning talk about his contribution during Nuxeo World. Take a look at the video. It’s a great feature.  Thanks Sun!

Define Marketplace Information

New Wizards

As with most releases of Nuxeo IDE, we have added some new wizards. You can now generate a basic unit test skeleton, or Studio based unit test skeleton. This means that your Studio project will be deployed in your test setup, so you’ll be able to test it. The project’s POM will be updated accordingly.

The Marketplace package wizard also makes it to this new release. It lets you create a project that builds a marketplace package according to your deployment profile, and also generates some integration tests (Webdriver, Selenium, Funkload). It is only bound to one project for now but will be updated in the next release.

Reload Studio Projects from Nuxeo IDE

When you hit the server reload button, and one of the projects to redeploy is bound to a Studio project, it now checks the update date of the Studio project and will download and reload it if necessary.

Root POM Improvement

We’ve cleaned up the POM generation for Nuxeo IDE plugin projects. If you don’t fill the parent POM form, we generate a root POM with everything you need (maven plugins, repositories). And if you do choose a parent, the resulting POM will be much simpler.

Nuxeo IDE Operations to Studio Export

The Studio registry is no longer overwritten when exporting operations from an IDE project to Nuxeo Studio.

The post Nuxeo IDE 1.2 Released! appeared first on Nuxeo Blogs.

Nuxeo Tech Talks – AngularJS @Nuxeo

$
0
0

Julien BoucquillonLast Monday was our second Nuxeo Tech Talk, held in our Paris Office. The goal of these Meetups is to have open discussions about technologies used by Nuxeo developers.

This time we invited Julien Boucquillon to talk about “AngularJS for Mobile” and our own Damien Metzler talked about AngularJS in the Nuxeo Platform.

If you follow this blog, you know that we have been working with AngularJS for a little while now, and are pretty happy about it. But so far we have not tried it in a mobile context. So it was really interesting to have someone give us some context and real-life tips about writing mobile apps with AngularJS.

Damien Metzler

Julien started by explaining how the two way binding works, how it’s not magical at all and how quick it can become costly, especially on mobile browsers. So he gave us some tips and tools to control this. Take a look at his slide deck on his website. It contains some nice code snippets, which makes it very real and comprehensive.

Then Damien explained what we do with AngularJS. He started by presenting quickly the Nuxeo Platform and how sometimes customers want to write a new application from scratch. This is why we’ve tried AngularJS. And we quickly realized that we needed a REST API to manipulate documents or users directly instead of using Content Automation. Then he went on to show a sample application with code snippets and finally explained what we want to do at Nuxeo in the near future with REST APIs.

Be sure to join our Meetup group in Paris to get notified about future Nuxeo Tech Talks.

The post Nuxeo Tech Talks – AngularJS @Nuxeo appeared first on Nuxeo Blogs.

Using the Nuxeo Template Rendering Module For a Web Publishing Use Case

$
0
0

For the next post in my “eat your own dog food” series, I want to talk about one of our uses of the nuxeo-template-rendering module for the internal “Techlead report”.

(Check out the first post in the series: A Simple Workflow for the Human Resources Department.

About the Techlead report

The Techlead report is an on going institution at Nuxeo that everyone reads to keep up to date on what is happening, from the pre-sales team to the CEO, to the developers and the support team. Laurent also publishes minutes of the report on this blog – you may have read it already.

Approximately once every two-three weeks, all the developers meet Thierry D. and Florent, CTO and Director of research and development respectively, to discuss what they are working on, both on the product implementation side and on the customer consulting side. During this meeting we gather all the technical challenges that the platform meets so as to prioritize the evolution – try to find new solutions to old problems, find some help on which angle to take on a new module implementation, and so on…

Thierry writes down the minutes of each discussion and every two-three weeks produces the “Techlead report.” As you can guess, it’s very long and the challenge was to get something “light” to read and easy to browse, while staying easy to author. Its format has evolved quite a bit in the past years: a simple mail, a Nuxeo “Blog Post”, a bare Note document type…. The last format, a Note document type, written in Markdown, with a website generated using the nuxeo-template-rendering module has been there for long enough to let us think we finally found the right recipe. :)

Using Nuxeo Drive

Thierry writes the global report content using his favorite text editor (no troll here, I won’t give the name) and it’s automatically synchronized as a Note document type in the intranet using Nuxeo Drive. Thanks to Nuxeo Drive, Thierry can work on the report at any time, anywhere and the content of the report is always synced in the intranet.

Markdown format support on Nuxeo Platform

Thierry writes the global report content using the Markdown syntax. This syntax is easy to read: you barely need a rendering engine to understand the “format” the author wanted to display. Of course, you still want bold, titles, subtitles, etc… in the end. :) Nuxeo supports the Markdown format on its Note document type, and can act as a rendering engine for Markdown syntax.

You can test it by creating a Note and choose “Markdown”, in the syntax field. Then type the following string: “

# This is a title
## This a subtitle
- and this is a bullet point
- and another *bullet point with some bold text*.

and save.

Nuxeo_sample_note_in_markdown
You can see that Nuxeo automatically displays on the summary tab of the Note correctly formatted content. Yet, if the content is too long, the “summary tab” of Nuxeo is probably not the best option, plus it doesn’t allow you to browse among all the sections.

Another available option is to download it as a PDF, with correct formatting of the content (the markdown rendition is automatically integrated to the pdf conversion by the converter system of the platform). Still, being on the browser and then being redirected to another application (namely a PDF reader) is not 100% great in terms of flow.

Using the Nuxeo Template Rendering module

Thierry thought of using the nuxeo-template-rendering module to generate, on-the-fly, an autonomous website, based on the markdown Note content. The result is the following website, where users see an index of all the sections and subsections on the left, and can click on each subsection.

Nuxeo_Techlead_website_rendition

The first time Thierry creates the document in Nuxeo, he clicks on the “bind template” user action (on the top right bar of actions) and chooses the “Techlead report template”. The template had previously been created as a document under the Template folder, at the root of the domain.

The template document holds:

  • A main file, the freemarker template that will generate the website.
  • JavaScript/CSS resource files, used by the generated website.
  • Configuration properties.
  • A filter to set which Nuxeo document types it use.
  • The rendering engine to use, i.e. freemarker in this case (There are many others, like xdoc for renditions based on .doc files, jod for open office documents, jxl for Excel files, XSL/FOP – the platform allows you to contribute other rendition engines).
  • Variable replacement logic. The freemarker template below contains the variable @{htmlContent}. This variable must be referenced as a parameter on the Nuxeo Template. The nuxeo-template-rendering module handles several kinds of parameters: string, date, boolean, document property, image, inclusion. String, date and boolean include static values. Document propriety allows you to dynamically add the value of a property of a document. Image includes a picture, and inclusion is used for other kinds of content inclusion. For our use case, we use inclusion, and more specifically, HTML Preview – we include in the freemarker template the generated html preview (that will be tweaked by the JavaScript libraries that are added). Since there is a bug setting it on the template, the parameter is set specifically on the document, on the template parameters tab.
  • The “rendition” from which the result of the rendering engine is made accessible – Webview in our case.

Nuxeo_Techlead_template_configuration
In the end, a fixed URL can be used to provide access to the dynamically generated website:

https://${hostname}/nuxeo/nxrendition/default/${documentpath}@webView

This URL is accessible on the summary tab:

Nuxeo_Webview_rendition_link

Get your hands dirty
If you want to try, you can:

  • Install the nuxeo-template-rendering module on your server, from the marketplace
  • Download this zip, and drop it in the Template folder of your Nuxeo instance. It will automatically create the required template.
  • Create a note with some markdown content (don’t forget the little selector for Markdown).
  • Bind this note to the newly created template.
  • Set the htmlContent parameter (selecting Include > HTML Preview) and save.

To go further

The FreeMarker Template

<html>
<head>
   <title> ${doc.title} </title>
   <meta name="title" content="${doc.title}"/>
   <meta name="description" content="${doc.dublincore.description}"/>
   <meta name="author" content="${doc.dublincore.creator}"/>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link class="component" href="${jaxrs.getResourceUrl("jquery.tocify.css")}" rel="stylesheet" type="text/css" />
<link class="component" href="${jaxrs.getResourceUrl("bootstrap.css")}" rel="stylesheet" type="text/css" />
  <script type="text/javascript" src="${jaxrs.getResourceUrl("jquery-1.8.3.min.js")}"></script>
  <script type="text/javascript" src="${jaxrs.getResourceUrl("jquery-ui-1.9.1.custom.min.js")}"></script>
  <script type="text/javascript" src="${jaxrs.getResourceUrl("jquery.tocify.js")}"></script>
  <style>
    body {
      padding-top: 20px;
    }
    p {
      font-size: 16px;
    }
    .headerDoc {
      color: #005580;
    }
    .tlHeader {
     padding : 10px;
     background-color : #F8F8FA;
    }
@media (max-width: 767px) {
    #toc {
        position: relative;
        width: 100%;
        margin: 0px 0px 20px 0px;
    }
}
    </style>
</head>
<body>
        <div style="float:right">
        </div>

 <div class="container-fluid">
      <div class="row-fluid">
        <div class="span3">
          <div id="toc">
          </div><!--/.well -->
        </div><!--/span-->
        <div class="span9">
          <div class="hero-unit">
            <div>
            <h1>${doc.title}</h1>
             </div>
            <br/>
            <p><i>${doc.dublincore.description}<i></p>
          </div>
           ${htmlContent}
        </div>
     </div>
 </div>

        <script>
             $(function() {
                  var t = $("h1")[0];
                  var p = t.parentNode;
                  t.remove();
                  $("#toc").tocify();
                  $("h1").addClass("tlHeader");
                  p.appendChild(t);
                 $("h2,h3").css({borderWidth : "0px 0px 1px 0px", borderStyle : "solid", borderColor : "#CCCCCC", paddingTop : "5px", paddingLeft : "5px"});
                 $("h4,h5,h6").css({paddingTop : "5px"});
              });
</script>
</body>
</html>

The post Using the Nuxeo Template Rendering Module For a Web Publishing Use Case appeared first on Nuxeo Blogs.

Getting Started with the REST API Using iOS

$
0
0

As you may know, we just freed our first release of the iOS SDK to access our REST API. Before we blog about how to start a whole application with things like UIListView, we discuss the basics to get you started on something if you already are a UIKit compliant person.

Adding SDK to Any XCode Project

We recommend you use CocoaPods as your dependency management system to include our library. Do not hesitate to look at it deeper, it’s good, you can eat it. To sum it up, after installing CocoaPods:

  • Add Nuxeo pods specs repository:
$ pod repo add nuxeo https://github.com/nuxeo/cocoapods-specs
  • Create a text file named `Podfile` with this content:
platform :ios, '7.0'
pod 'NuxeoSDK', :head
  • Execute `pod` to build the workspace, then open it:
$ pod
$ open MyProject.xcworkspace

Requestinga  document

Instead of talking a lot before coding, let’s start with something concrete:

NSURL *url = [[NSURL alloc] initWithString:@"http://localhost:8080/nuxeo"];
// [1]
NUXSession *session = [[NUXSession alloc] initWithServerURL:url username:@"Administrator" password:@"Administrator"];
[session addDefaultSchemas:@[@"dublincore"]];

// [2]
NUXRequest *request = [[NUXRequest alloc] initWithSession:session];
[[request addURLSegment:@"path"] addURLSegment:@"default-domain"];
// [3]
[request setCompletionBlock:^(NUXRequest *request) {
    NSError *error;
    // [5]
    NUXDocument *doc = [request responseEntityWithError:&error];

    [self doSomethingGreatWith:doc];
}];
// [4]
[request start];

[1] Session initialisation

You can initialize a session from the init method, or by using the shared one filled with NUXSession-info.plist content. Session can also set default behaviors for further requests, such as if you want to get the dublincore schema for all requests; add it with addDefaultSchemas method.
-> NUXSession documentation

In this sample, we initialize a session binded to a server hosted on “http://localhost:8080/nuxeo”, authenticated with default credentials and expecting to have the dublincore schema for all requests.

[2] Request

Requests are attached to a session, and let you access the REST API. You have access to some methods that hide URL construction, such as if you want to use an adaptor, to set a category. It also lets you set HTTP stuff like request headers, parameters, method, etc…
-> NUXRequest documentation

In this sample, we manually instantiate a request and add some URL segments to access the fetch document endpoint (/path/default-domain).

[3] Result blocks

We need to use blocks patterns, so we define two blocks: one when the request is successful, and the other if something goes wrong.

[4] Requests execution

You can start the request synchronously, or asynchronously, depending on what you need. Keep in mind to not block the UI thread.
-> NUXRequest documentation

In this sample, we forget to handle a failure case (I’m confident.) but the request is started asynchronously.

[5] Do something with the response

As soon as the remote server gives an answer, your block is called with the executed request filled with response data: responseStatusCode, responseMessage, response. You can access the response as bytes (if you love fighting with bytes), as an UTF8 encoded string, as a JSON dictionnary or as a NUXEntity.
-> Response handling documentation
-> NUXEntity mapping documentation

In this sample, we get the response as a NUXDocument which is a NUXEntity implementation for “document” entity-type response, and do “somethingGreatWith”. We skip over error result …

To Go Further

This is all for today. If you want to go further, you should take a look at the following links:

The post Getting Started with the REST API Using iOS appeared first on Nuxeo Blogs.

Nuxeo Platform 5.9.1 is out!

$
0
0

Nuxeo Platform 5.9.1Nuxeo Platform 5.9.1 is out! It’s the first Fast Track release before our next LTS. Take a look at the release notes for the whole story. This new version does not contain many new user-visible features. Instead we focused on the infrastructure, doing some background work to enhance the platform and making it more accessible for developers.

One feature that you must take notice of and try is the Mule ESB connector. Thierry has been working on this for a while with Alain. They came up with a stable connector and a sample/documentation that will help you grasp the potential of this solution.

Another noticeable addition is the iOS SDK. It’s here to help you build iOS applications connected to the Nuxeo Platform through the REST API. It already has several cool features like the blob offline cache.

5.9.1 is also the first Nuxeo Platform release compatible with IE 11 thanks to Guillaume’s hard work on RichFaces.

As most of the changes are aimed at developers, don’t forget to read the upgrade notes. We have renamed a bunch of modules and packages so this might impact your code!

You can download Nuxeo Platform 5.9.1 from our website.

The next release will be 5.9.2 and is scheduled for February.

The post Nuxeo Platform 5.9.1 is out! appeared first on Nuxeo Blogs.

Real-Time Collaboration in Nuxeo with Mozilla’s TogetherJS

$
0
0

TogetherJS
The other day I started looking at TogetherJS. It’s a JavaScript library developed by Mozilla which enables collaboration between users browsing the same website. Take a look at the demo they have recorded, it’s really cool.

It uses WebSockets to connect to a server which will echo back messages to other connected browsers. This way you can chat with others and see what they do on a page. It synchronizes the different input fields of the page, tells you where other users go and if you want to follow them, etc… The default public server is hosted by Mozilla, which means they’ll see everything that goes through it. But you can host your own server if you want to.

It also uses WebRTC which enables P2P communications between browsers. Right now it only works on Firefox, Chrome and Opera. It allows TogetherJS to support audio communication, so you can start a conversation with users browsing the same website that you do, providing you invited them.

I’ve recorded a quick demo of TogetherJS in Nuxeo to give you an idea:

And if you want to try it yourself, it’s available on the Marketplace. You can take a look at the full code on Github.

In theory it’s really easy to add TogetherJS to any website. Actually all you have to do is add this code to your page:

  <button onclick="TogetherJS(this); return false;">Start TogetherJS</button>
  <script src="https://togetherjs.com/togetherjs-min.js"></script>

This will load TogetherJS when you click on the ‘Start TogetherJS’ button. I said in theory because I had to do a couple of changes to it.

Any action you do on a page, like simply moving your mouse cursor, will trigger events that will be sent to the Hub. The Hub is the server hosted by Mozilla that relays all the messages to the other connected clients. This event contains the id of the DOM object where the event occurred. When the element cannot be identified by an id, the CSS selector is used (like ‘:nth-child(1)’).  In Nuxeo, most of the DOM objects have an id also containing the colon character. This makes default TogetherJS behavior erratic to say the least, because it handles what’s after the first colon like a CSS selector. I had to fix this and package it, making the deployment a little more complicated.

Here’s what I did once I had my Nuxeo-compliant version of TogetherJS. First I took Nuxeo IDE and generated a new plugin project. There I created the src/main/resources/web/nuxeo.war/ folder. In this folder I put the togetherjs-min.js file and the associated togetherjs folder. This is what you get when building your own version. To make sure all of this gets deployed, I created a src/main/resources/OSGI-INF/deployment-fragment.xml file containing the following code:

<?xml version="1.0"?>
<fragment version="1">

  <require>org.nuxeo.ecm.platform.lang</require>
  <require>org.nuxeo.ecm.webapp.core</require>
  <require>org.nuxeo.ecm.webapp.ui</require>

  <install>
    <!--  unzip the war template -->
    <unzip from="${bundle.fileName}" to="/" prefix="web">
      <include>web/nuxeo.war/**</include>
    </unzip>
  </install>

</fragment>

Instead of adding a hard coded button like in the official example, I added an action. Notice that it’s in the FOOTER category so it will be available anywhere in Nuxeo.

  <extension point="actions" target="org.nuxeo.ecm.platform.actions.ActionService">

    <action accessKey="i" enabled="true" id="togetherJS" label=""
      link="javascript:TogetherJS()" order="100" icon="/icons/togetherjs.png" type="bare_link">
      <category>FOOTER</category>
      <properties>
        <property name="ajaxSupport">true</property>
        <property name="labelStyleClass">footerLabel</property>
      </properties>
    </action>

  </extension>

Eventually I added the script to the footer template. I did not go with the Theme solution as usual because TogetherJS infers the base URL. So it would not have worked with a URL like “/nuxeo/nxthemes-lib/prototype.js,togetherjs.js….”.

Here’s what I added at the end of the src/main/resources/web/nuxeo.war/incl/nuxeo_footer_template.xhtml file:

<script src="#{contextPath}/togetherjs-min.js"></script>

And this is pretty much all I had to do. Now I can enjoy real-time collaboration and chat when editing a document on the intranet or simply browsing it.

The post Real-Time Collaboration in Nuxeo with Mozilla’s TogetherJS appeared first on Nuxeo Blogs.


The Mule ESB Connector for the Nuxeo Platform

$
0
0

Mule ESBYou may have noticed our recent communication on the Mule ESB Connector for the Nuxeo Platform. We indeed did quite some work with implementing a fully workable connector for the Mule ESB platform. This connector gives you a way to leverage Nuxeo Platform Automation API (atomic operations) in the Mule ESB environment. Inside a Mule Flow, you can call Nuxeo Automation operations and transform the response payload in formats commonly accepted by other Mule ESB connectors. You can also set a “Nuxeo Listener” endpoint so that your Mule Flow is the reaction to an event happening in the Nuxeo Platform repository. Finally, we implemented the support of “DataSense,” which offers to Mule ESB users metadata autocompletion and automated introspection of payloads and schemas. There are dozens of connectors in the Mule library (http://www.mulesoft.org/connectors), meaning that this connector offers you the possibility to integrate your Nuxeo repository with other software platforms, such as Salesforce, Marketo, SAP, Magento, and many more.

There is a complete step by step tutorial to help you understand the basics of the Mule ESB Connector. In this blog post, I chose to show how to integrate the Nuxeo Platform with Twitter. The Mule Flow you will see in the following videos shows how to fetch some specific tweets and create notes in Nuxeo from the tweet content, setting up a sort of web intelligence agent. Instead of a verbose step-by-step written description, I decided to capture the process in three short videos:

  • A functional demo of the implemented flow (4:12),
  • A general technical explanation of the flow (5:23),
  • The live implementation (7 :03).

Do not hesitate to give feedback when you test this yourself!

Functional Demo

General Technical Explanation

Implementation

The post The Mule ESB Connector for the Nuxeo Platform appeared first on Nuxeo Blogs.

Build a Q&A Web App with AngularJS and Nuxeo REST API

$
0
0

Ask a Question

Still coding with AngularJS and Nuxeo, today I will show you another AngularJS sample I made using the REST API. The goal here was to let Nuxeo users ask and answer questions, a bit like we do on answers.nuxeo.com.

It’s a simple application which makes it a good use case for CRUD operations. It was really quick and easy to do even though I am far from an expert in AngularJS and JavaScript (but I could really feel it becoming easier). Most of the new stuff I learned came from egghead.io – you should start there if you don’t know AngularJS.

Here are a number of screenshots. Lise helped me a lot on this to make it look good, using Semantic UI/.

Question List

Let’s focus on the question asking. Here’s the template used for the form:

<div class="active side ui segment">
  <form name="myForm" class="ui form control-group">

  <div class="field" ng-class="{error: myForm.name.$invalid}">
    <h2>Ask a question</h2>
    <label>Title</label>
    <input type="text" name="title" placeholder="A question as clear and concise as possible" ng-model="doc.properties['dc:title']" required>
    <span ng-show="myForm.name.$error.required" class="help-inline">
        Required</span>
  </div>
  <div class="field" ng-class="{error: myForm.site.$invalid}">
  <label>Description</label>
  <textarea name="description" ng-model="doc.properties['dc:description']" placeholder=" A factual description so that everyone understands the question and issue and can respond correctly" required></textarea>
    <span ng-show="myForm.site.$error.required" class="help-inline">
        Required</span>
  </div>
  <div class="field">
    <label>Communities</label>
    <select multiple id="communities" class="ui input medium" ng-model="doc.properties['qs:community']">
       <option ng-repeat="community in communities">{{community.title}}</option>
    </select>
  </div>
  <div class="field">
  <button ng-click="save()" ng-disabled="isClean() || myForm.$invalid"
          class="ui blue submit button">Ask</button>
  <a href="#/questions" class="btn">Cancel</a>
  </div>
  </form>
</div>

As you can see it’s simple, and using semantic UI makes it even easier to understand. The communities are actually the social workspaces available on my Nuxeo instance. You can see how they are fetched by looking at the controller:

.controller("AskCtrl", ['$scope', 'nxSession', '$location', 'nxSearch'
 ($scope, nxSession, $location, nxSearch) ->
  $scope.doc = { type:"question", properties: {}}
  communitiesSearch = new nxSearch()
  .setQuery("SELECT * FROM SocialWorkspace WHERE ecm:currentLifeCycleState <> 'deleted'")
  .setPageSize(200)
  $scope.communities = communitiesSearch.items
  communitiesSearch.nextPage()

  $scope.save = () ->
    nxSession.createDocument("/collaboration/questions", $scope.doc).then (question) ->
      $location.path("/question/" + question.uid + '/view')

])

The first part of the controller creates an empty doc document of type question, without any properties. Then the nxSearch service is used to fetch the list of communities. That’s all we need to display the ‘Ask a Question’ form. If the user clicks on the Ask button, we execute the $scope.save() method. It uses the nxSession service’s createDocument method. The first argument is the path of the parent document, the second argument is our doc object updated with the fields of our form.

    Session.createDocument = (parentPath, doc)->
      doc['entity-type'] = "document"
      $http.post(apiRootPath + "/path" + parentPath , doc).then (response)->
        new nxDocument(response.data.uid, response.data)

As you can see, the createDocument is dead simple too. It’s an HTTP post of a JSON object. If you want to see all the other endpoints available for the REST API, I invite you to visit http://demo.nuxeo.com/nuxeo/api/v1/doc/ and try the API directly!

Single Question

The post Build a Q&A Web App with AngularJS and Nuxeo REST API appeared first on Nuxeo Blogs.

An Event Introspection with Nuxeo Studio

$
0
0

A big event occurred recently. Something huge I did not see coming.  Some days ago, I woke up in the morning and guess what? The year had changed. Can you believe it? Gosh! Is time running at light-speed or what? It’s the New Year. So – Happy New Year to you all!

Now, let’s start my first blog of the year. It’s about events and event handlers.

Event Handlers are at the core of many Nuxeo applications, because they are a great place to manage and centralize business rules. Basically, an event handler (also called a listener) subscribes to an event, and when this event occurs, the handler is run. It is actually subtler than that, because the event handler also defines conditions for which it allows itself to be triggered. So, for example, an event handler could subscribe to the “document created” event, but will be executed only if the created document is a File (i.e. it will not run if it is a Folder, or a Picture).

And Nuxeo provides many many(1) events. From the Studio point of view, we have this:

01-EventsList-1-80

Wait.

Wait wait wait.

I just wrote “many many” and we can only see four events here. Where are the others? Actually, they are in the scrollable list. Of course. As of today, with Studio 2.16, the full list is(2):

01-EventsList-2-80

You can find more information on each event here.

So, in Studio, you select the event(s), you setup the enablement, and then you pick-up a chain to be run when the event fires and the enablement conditions are met.

Now a little story about the reason why I am writing this blog. I had the following need: When “Empty document created” is fired on my Claim document, I want to pre-fill some values. But these default values depend on the parent document. So for example, say I have a claim:department field. If the Claim is created inside a Folder, then the department must be set to “Marketing”. In all other cases, the department must be set to “Management”.

The point is, in the context of the “Empty document created” event, the current document has no parent yet. I mean, from the automation chain, you cannot use something like @{Document.parent.type …etc…}.

Well. Technically, you can: After all, you can do whatever you want(3) can’t you? This is a good opportunity to see the awesome new error messages of 5.8 – look at the server log and check the description of the error. In case you want to save some time, I’m telling you: Document.parent is null, so Document.parent.type generates the hell in the log file.

How can I get the type of the parent in this context? After spending some time searching, I found myself asking for help from my colleagues. I received an answer because they are cool, patient, understanding and kind to me(4). The answer was: “Use the parentPath value of the event’s options”.

All I had to do then was find where these options were, and how I could use them. Well, quite simply: RTFM. It’s described in Understand Expression and Scripting Languages Used in Nuxeo. More precisely, in the Event Handlers part of the page. At this time, there was nothing about “parentPath”, “destinationRef” and “Availability of These Properties”: That’s also how we improve the documentation. We get in trouble, we clear the trouble, and we update the doc so other people will not feel the same pain.

Back to this story: All I had to do was to get the parent document using its path with the following expression:

Event.context.getProperty(“parentPath”)

My final chain triggered for the “Empty document created” event. For a Claim document it is…

Fetch > Context Document(s)
Push & Pop > Push Document
Fetch > Document
value: @{Event.context.getProperty("parentPath")}
Execution Context > Set Context Variable
name: parentIsFolder
value: @{Document.type == “Folder”}
Push & Pop > Pop Document
Document > Update Property
value: @{parentIsFolder ? “Marketing” : “Management”}
xpah: claim:department
save: no

…and it works like a charm. Important reminder: Make sure the “save” box is not checked. If you do save the document in the context of the Empty document created event, the server will gently throw an error.

Now, let’s push this a bit further. If you want to get a list of available properties for a given event, then a good trick is to use @{Event.context.getProperties().toString()}.

Note
When the documentation states that an expression used in MVEL is an object, you can look at the JavaDoc of the object, to check what’s interesting. So for instance, here, you have an open JavaDoc page, so find the Event class. Then, you follow the EventContext link and you see this getProperties method.

For quick testing, create a new event handler. Select all and every possible event, activate it for File, and bind it to a chain that logs the result of this expression. Something like:

Fetch > Context Document(s)
Notification > Log
level: warn
Message: @{Event.getName()}: @{Event.context.getProperties.toString()}

Save, update the server, play with a File and watch the server.log file: You will find all the events and all the available properties for each event that occurred. For example, after creating a new File, you will have:

emptyDocumentModelCreated: {category=eventDocumentCategory, BLOCK_JMS_PRODUCING=true, sessionId=default-8821762671641624678, repositoryName=default, comment=null, parentPath=/default-domain/workspaces/ws}
aboutToCreate: {destinationRef=/default-domain/workspaces/ws, category=eventDocumentCategory, sessionId=default-8821762671641624678, destinationName= TheNewFile, repositoryName=default, comment=null}
documentCreated: {destinationRef=/default-domain/workspaces/ws, category=eventDocumentCategory, sessionId=default-8821762671641624678, destinationName=TheNewFile, repositoryName=default, comment=null, documentLifeCycle=project}

Happy New Year event!

(1) Not a duplicate. I really mean “a lot”
(2) Display the Web Inspector, inspect the Select object, change its height.
(3) © Status Quo
(4) Let them be in a good mood for the next question

The post An Event Introspection with Nuxeo Studio appeared first on Nuxeo Blogs.

Using Docker Containers – Part 1 – Build a Full Fledged Nuxeo Image

$
0
0

homepage-docker-logoI have been toying around a bit with Docker recently. It’s an open source project to easily create lightweight, portable, self-sufficient containers from any application. The same container that a developer builds and tests on a laptop can run at scale, in production, on VMs, bare metal, OpenStack clusters, public clouds and more. An open source project to pack, ship and run any application as a lightweight container.

If you haven’t tried Docker yet, I invite you to do so. They have a neat getting started page.

Why Docker?

There could be a lot of reasons to use it at Nuxeo. Here are a few I have been thinking about:

  • Jenkins on demand build slaves
  • Cloud deployment (obviously)
  • Remote/unified dev environments
  • On demand converters for Nuxeo

Hence the Part 1 in the title. I can’t tell you when this will end, but I can tell you there will be more :-)

Anyway, I started doing a Nuxeo image. The Docker way would be to have an image for each process (apache2, postgresql, nuxeoctl) but I wanted to start with an all-inclusive image. I thought it would be simpler. :-)

And it is indeed quite simple once you know your way around the supervision tool. Because when you run a Docker image, you can only run one process. So you have to use tools like Supervisor in order to run more processes. I have chosen Supervisor because it was the most widely used as far as I could see on the web. So my first working image contained everything needed to run Nuxeo and used Supervisor to launch everything. And everything was in a single Dockerfile.

A Dockerfile is like a recipe to build an image. It contains a list of steps that are all versioned. Differences between each step of the image are stored on the filesystem. That means that if you add a new step at the end, for instance, you don’t have to go through all the steps again.

Putting every step in a single file is of course not the best way to do it when you want to do different Nuxeo images. For continuous integration, for example, I will need an image with H2, one with PostgreSQL, one with MySQL etc… So I started splitting my Dockerfile to have something more modular. Here’s the result.

The Base Image

The purpose of this image it to give you an up to date Ubuntu distribution with all the dependencies needed by Nuxeo, a nuxeo user and Supervisor to manage your processes. This will be the basis for the next images to build.

# Nuxeo Base image is a ubuntu precise image with all the dependencies needed by Nuxeo Platform
#
# VERSION               0.0.1

FROM      ubuntu:precise
MAINTAINER Laurent Doguin <ldoguin@nuxeo.com>

# Set locale
RUN locale-gen --no-purge en_US.UTF-8
ENV LC_ALL en_US.UTF-8

# Install dependencies
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get install -y python-software-properties wget sudo net-tools

RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list

# Add Nuxeo Repository
RUN apt-add-repository "deb http://apt.nuxeo.org/ precise fasttracks"
RUN wget -q -O - http://apt.nuxeo.org/nuxeo.key | apt-key add -

# Upgrade Ubuntu
RUN apt-get update
RUN apt-get upgrade -y

# Small trick to Install fuse(libreoffice dependency) because of container permission issue.
RUN apt-get -y install fuse || true
RUN rm -rf /var/lib/dpkg/info/fuse.postinst
RUN apt-get -y install fuse

# Install Nuxeo Dependencies
RUN sudo apt-get install -y acpid openjdk-7-jdk libreoffice imagemagick poppler-utils ffmpeg ffmpeg2theora ufraw libwpd-tools perl locales pwgen dialog supervisor unzip vim

RUN mkdir -p /var/log/supervisor

# create Nuxeo user
RUN useradd -m -d /home/nuxeo -p nuxeo nuxeo && adduser nuxeo sudo && chsh -s /bin/bash nuxeo
ENV NUXEO_USER nuxeo

To build this image, get into the folder containing the Dockerfile and run:

docker build -t nuxeo/nuxeobase .

It’s always good to use the -t option and give a name to the images you build. It’s even more important since it will be used in the next image.

Note that running this image won’t get you anywhere, there is no Nuxeo installed on it.

An All Inclusive Nuxeo Image

This Docker image is based on the work Mathieu did for our VM. I had to adapt one or two things but it’s really close to the original. I mostly added supervisor since I have this one process limitation. Here’s the configuration I used:

[supervisord]
nodaemon=true

[program:sshd]
command=/usr/sbin/sshd -D

[program:apache2]
command=/bin/bash -c "source /etc/apache2/envvars && /usr/sbin/apache2 -DFOREGROUND"
redirect_stderr=true

[program:postgresql]
user=postgres
command=/usr/lib/postgresql/9.3/bin/postgres -D /var/lib/postgresql/9.3/nuxeodb -c config_file=/etc/postgresql/9.3/nuxeodb/postgresql.conf
redirect_stderr=true
autorestart=true

[eventlistener:pgListener]
command=python pgListener.py
events=PROCESS_STATE_RUNNING
numprocs=1

It will run Apache, PostgreSQL and the ssh daemon. But as you can see there is still no trace of a Nuxeo process. It will actually be launched by the pgListener event listener. It’s a python script that waits for the postgresql process to be running. The code is pretty simple as you can see. You just have to use the Supervisor Python API to read Supervisor events. Once the event saying the postgresql process entered the running state occurs, the firstboot.sh script is launched.

#!/usr/bin/env python

import os
import sys

from supervisor import childutils

def main():
    while 1:
        headers, payload = childutils.listener.wait()
        if headers['eventname'].startswith('PROCESS_STATE_RUNNING'):
            pheaders, pdata = childutils.eventdata(payload+'\n')
            if pheaders['processname'] == "postgresql":
                os.system("sh /root/firstboot.sh")
                break

        childutils.listener.ok()

if __name__ == '__main__':
    main()

Now about the firstboot.sh script. It configures the database if it has not been done already and starts Nuxeo.

#!/bin/bash

# Prevent firstboot from being executed twice
if [ -f /root/firstboot_done ]; then
    su $NUXEO_USER -m -c "$NUXEOCTL --quiet restart"
    exit 0
fi

# PostgreSQL setup

pgpass=$(pwgen -c1)

su postgres -c "psql -p 5432 template1 --quiet -t -f-" << EOF > /dev/null
CREATE USER nuxeo WITH PASSWORD '$pgpass';
CREATE LANGUAGE plpgsql;
CREATE FUNCTION pg_catalog.text(integer) RETURNS text STRICT IMMUTABLE LANGUAGE SQL AS 'SELECT textin(int4out(\$1));';
CREATE CAST (integer AS text) WITH FUNCTION pg_catalog.text(integer) AS IMPLICIT;
COMMENT ON FUNCTION pg_catalog.text(integer) IS 'convert integer to text';
CREATE FUNCTION pg_catalog.text(bigint) RETURNS text STRICT IMMUTABLE LANGUAGE SQL AS 'SELECT textin(int8out(\$1));';
CREATE CAST (bigint AS text) WITH FUNCTION pg_catalog.text(bigint) AS IMPLICIT;
COMMENT ON FUNCTION pg_catalog.text(bigint) IS 'convert bigint to text';
EOF

su postgres -c "createdb -p 5432 -O nuxeo -E UTF-8 nuxeo"

# Nuxeo setup

cat << EOF >> /etc/nuxeo/nuxeo.conf
nuxeo.templates=postgresql
nuxeo.db.host=localhost
nuxeo.db.port=5432
nuxeo.db.name=nuxeo
nuxeo.db.user=nuxeo
nuxeo.db.password=$pgpass
EOF

su $NUXEO_USER -m -c "$NUXEOCTL --quiet restart"
# Prevent firstboot from being executed twice
touch /root/firstboot_done

Now about the Dockerfile. Notice the first line that says FROM nuxeo/nuxeobase. It’s the name of the image built earlier. Then what happens is we download the latest LTS, set up environment variables, install PostgreSQL, Apache and the OpenSSH server. Once this is done we add different configuration files and scripts. Supervisor.conf and nuxeo.apache2 are respectively the Supervisor configuration and the Apache configuration. I have already told you about pgListener.py and firstboot.sh. What about the two others?

Postinst.sh is dedicated to configuration. We use it to extract Nuxeo from its archive, create the various files and folders needed (like /etc/nuxeo/, /var/lib/nuxeo, etc..), setup the good permissions, modify nuxeo.conf, drop the main PostgreSQL cluster and create our own nuxeodb cluster and setup Apache.

Entrypoint.sh is run each time you start the Docker container thanks to the ENTRYPOINT command. I use it to re-generate the ssh keys and set a random password if it has not already been done. Then I echo the password and run what’s been given as CMD using exec “$@”

I am not sure if it’s supposed to be used like that, but it allows me to display the password and keep flexibility through CMD, because if you don’t want to run supervisiond directly, you can give /bin/bash at the end of your docker run command.

# Nuxeo Platform
#
# VERSION               0.0.1

FROM      nuxeo/nuxeobase
MAINTAINER Laurent Doguin <ldoguin@nuxeo.com>

# Download latest LTS nuxeo version
RUN wget http://community.nuxeo.com/static/releases/nuxeo-5.8/nuxeo-cap-5.8-tomcat.zip && mv nuxeo-cap-5.8-tomcat.zip nuxeo-distribution.zip

ENV NUXEOCTL /var/lib/nuxeo/server/bin/nuxeoctl
ENV NUXEO_CONF /etc/nuxeo/nuxeo.conf

# Add postgresql Repository
RUN apt-add-repository "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main"
RUN wget -q -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -

# Install apache and ssh server
RUN sudo apt-get install -y openssh-server apache2 postgresql-9.3
RUN mkdir -p /var/run/sshd

ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf
ADD nuxeo.apache2 /etc/apache2/sites-available/nuxeo
ADD postinst.sh /root/postinst.sh
ADD firstboot.sh /root/firstboot.sh
ADD entrypoint.sh /entrypoint.sh
ADD pgListener.py pgListener.py

RUN /root/postinst.sh

EXPOSE 22 80
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/bin/supervisord"]

You can run it like this:

docker run -P -d nuxeo/nuxeo

Now your container should be running, let’s see if this is true. Typing docker ps should output something like this:

CONTAINER ID        IMAGE                COMMAND                CREATED             STATUS              PORTS                                          NAMES
02717b82d503        nuxeo/nuxeo:latest   /entrypoint.sh /usr/   5 seconds ago       Up 4 seconds        0.0.0.0:49153->22/tcp, 0.0.0.0:49154->80/tcp   sick_tesla

Thanks to the -P option used in the run command, all the exposed ports are mapped automatically. So if you go to yourDockerHost:49154 with a web browser, you’ll have the running Nuxeo instance.

Now for some reason, like say debugging, you might want to open an ssh session to the server. We already have the port thanks to docker ps. Now we need the password. It’s in the logs thanks to the entrypoint.sh script, so if you type docker logs 02717b82d503, 02717b82d503 being the container id, you should see something like:

Creating SSH2 RSA key; this may take some time ...
Creating SSH2 DSA key; this may take some time ...
Creating SSH2 ECDSA key; this may take some time ...
start: Unable to connect to Upstart: Failed to connect to socket /com/ubuntu/upstart: Connection refused
Default password for the root and nuxeo users: EzeegaiN
2014-01-16 16:11:52,410 CRIT Supervisor running as root (no user in config file)
2014-01-16 16:11:52,410 WARN Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
2014-01-16 16:11:52,436 INFO RPC interface 'supervisor' initialized
2014-01-16 16:11:52,436 WARN cElementTree not installed, using slower XML parser for XML-RPC
2014-01-16 16:11:52,437 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2014-01-16 16:11:52,437 INFO supervisord started with pid 1
2014-01-16 16:11:53,439 INFO spawned: 'pgListener' with pid 70
2014-01-16 16:11:53,441 INFO spawned: 'sshd' with pid 71
2014-01-16 16:11:53,443 INFO spawned: 'postgresql' with pid 72
2014-01-16 16:11:53,445 INFO spawned: 'apache2' with pid 73
2014-01-16 16:11:54,737 INFO success: pgListener entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2014-01-16 16:11:54,738 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2014-01-16 16:11:54,739 INFO success: postgresql entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2014-01-16 16:11:54,739 INFO success: apache2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2014-01-16 16:12:05,617 INFO exited: pgListener (exit status 0; expected)

The password is on the fifth line. So now you can do something like ssh nuxeo@yourDockerHost -p 49153 and open a session to your container.

And there you go now you have a fully functional docker container to test Nuxeo.

The post Using Docker Containers – Part 1 – Build a Full Fledged Nuxeo Image appeared first on Nuxeo Blogs.

[Guest Post from Ikanow] Integrating a Single Sign On (SSO) with Nuxeo for Case Management

$
0
0

IKANOW
This blog post was written by Caleb Burch, Product Engineer at Ikanow. Ikanow is a software organization that has created the world’s first open source analytics platform.

This blog will demonstrate how we connected the Infinit.e platform’s sign on with Nuxeo’s to allow a seamless interaction (single sign on) for our clients using Nuxeo’s case management capabilities. Infinit.e is an open source analytic platform designed for documents not database records of which it provides a wide range of activities including search, visualization, and data science. Our customers use the platform for financial fraud detection, cyber security, geopolitical analysis, marketing, and other fields involving large sets of unstructured data. We use Nuxeo as a case manager to allow users to map abstract concepts like data sources, query sets, statistical recommendations, and important entities back to “real life” artifacts like suspects, leads, or evidence. The need to communicate with Infinit.e and manage user/group permissions meant we had to develop a single-sign on capability between the platforms.

The typical interaction between the two platforms usually involves mapping some entity discovered in a document to our Case Visualizer (pictured below). This is a lot like a working canvas for analysis so users can store data while they work between queries, exploring data. Once the user decides an entity is important, the entity can be promoted to a target.

When a target is created, we auto create Nuxeo-documents (pictured below) for these targets so a user can manage them more easily. We also have some more integrated actions that allow users to kick off Infinit.e data collection based on the targets in Nuxeo.

Based on these typical user interactions, the goals of connecting our 2 platforms were to:

  1. Allow users to automatically move from our web application into Nuxeo without having log in twice
  2. Transfer permissions from the Infinit.e community system directly to Nuxeo groups (they are similar authentication schemes of grouping users into permission groups)

Steps to connect Infinit.e and Nuxeo:

  1. Create a custom Authenticator that implements NuxeoAuthenticationPlugin, NuxeoAuthenticationPluginLogoutExtension (This authenticator will check we have an active connection with Infinit.e, get our user groups, and log us out)
  2. Create a custom Login Plugin that implements LoginPlugin
  3. Create a custom configuration file that is external to our extensions so we can adjust it for different Nuxeo instances.
  4. Adjust the default authentication chain order for both default login and automation
  5. Deploy to your Nuxeo server

Infinit.e architecture

Infinit.e is a web application powered by a RESTful API service. We used three of our API calls to authenticate users and get their communities (groups).

We authenticate login via the Auth – Login. This is a restful call in which we return a cookie named “infinitecookie” that will be active for 15 minutes from the most recent API call (e.g. if you keep making subsequent API calls, the timer will be updated for 15 more minutes).

> Login Example

http://infinite.ikanow.com/api/auth/login/username@email.com/encryptedpasswordand returned in the response would be:
{ "response" : { "action" : "Login",
      "success" : true,
      "time" : 0
    } }

your cookie will be attached named “infinitecookie”:

Once logged we get a user and their groups by calling Social – Person – Get

> Get Person Example

http://infinite.ikanow.com/api/social/person/get
{ "data" : { "WPUserID" : "cburch@ikanow.com",
 "_id" : "5069dd90e4b09156bad31aba",
 "accountStatus" : "active",
 "communities" : [ { "_id" : "5069dd90e4b09156bad31aba",
 "name" : "Caleb Burch's Personal Community"
 },
 { "_id" : "4c927585d591d31d7b37097a",
 "name" : "Infinit.e System"
 }
 ],
 "created" : "Oct 1, 2012 06:14:41 PM UTC",
 "displayName" : "Caleb Burch",
 "email" : "cburch@ikanow.com",
 "firstName" : "Caleb",
 "lastName" : "Burch",
 "modified" : "Oct 25, 2013 02:59:21 PM UTC",
 "phone" : "0"
 },
 "response" : { "action" : "Person Info",
 "message" : "Person info returned successfully",
 "success" : true,
 "time" : 4
 }
}

We also use the Auth-Keep Alive call, which just checks that an Infinit.e cookie is active, and adds another 15m of activity to the clock

> Keep Alive Example

http://infinite.ikanow.com/api/auth/keepalive

and if the cookie is valid the response will return something like:

{ "response" : { "action" : "Keep Alive",
 "message" : "Cookie kept alive, 15min left.",
 "success" : true,
 "time" : 1
 } }

Overriding Nuxeo’s default login architecture

Step 1: Create a custom Authenticator that implements NuxeoAuthenticationPlugin, NuxeoAuthenticationPluginLogoutExtension – this authenticator will check we have an active connection with Infinit.e, get our user groups, and log us out

We first needed to create our own Authenticator that will check if a user is logged into Infinit.e already, grab the user object, and create a user/groups in Nuxeo as necessary. We accomplished this by creating a new Nuxeo plugin and creating a class that implements both NuxeoAuthenticationPlugin and NuxeoAuthenticationPluginLogoutExtension, thus overriding those extension points.

The methods we needed to override included:

  1. handleRetrieveIdentity
    This is the meat of the Authenticator. Here we check if a user has a valid cookie in the httpRequest, then we grab an Infinit.e user object with that cookie and create a Nuxeo user if one does not exist, as well as any Nuxeo groups that do not exist. After we create all that, a UserIdentificationInfo object is returned for that user. This object will be sent off to the Login Plugin to validate the account is valid, but we are going to over ride that to make sure it always thinks it is valid so we can just put whatever password we like.

    NOTE: On as user’s first call to Nuxeo you may not yet have an active jsp session, so we must call httpRequest.getSession(true); to make sure one is created.

  1. initPlugin
    This method gets called when Nuxeo starts up, allows us to grab config params from the configuration file we are going to create later. (e.g. we get the Infinit.e API url and the Infinit.e login url so we can redirect unauthenticated users)
  2. needLoginPrompt
    Checks if an httprequest needs to be sent to handleLoginPrompt, we always return true
  3. handleLoginPrompt
    Here we check if a user is authenticated, if not we redirect them to the login url (we received from initPlugin). Return false if there is nothing to do
  4. getUnAuthenticatedURLPrefix
    Block any urls you don’t want to allow access, we just return null here
  5. handleLogout
    Occurs when someone pushes the logout button in Nuxeo, we send a request to log the user out of Infinit.e and redirect them to the Infinit.e login page.

>InfiniteAuthenticator.java

package com.ikanow.infinit.e.nuxeo.auth;

import java.net.URLEncoder;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.NuxeoPrincipal;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin;
import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPluginLogoutExtension;
import org.nuxeo.ecm.platform.usermanager.UserManager;
import org.nuxeo.runtime.api.Framework;

import com.ikanow.infinit.e.data_model.api.ResponsePojo.ResponseObject;
import com.ikanow.infinit.e.data_model.driver.InfiniteDriver;
import com.ikanow.infinit.e.data_model.store.social.person.PersonCommunityPojo;
import com.ikanow.infinit.e.data_model.store.social.person.PersonPojo;

public class InfiniteAuthenticator implements NuxeoAuthenticationPlugin, NuxeoAuthenticationPluginLogoutExtension {
 private static final Log log = LogFactory.getLog(InfiniteAuthenticator.class);

 private String infinite_api_url = "";
 private String infinite_login_url = "";

 private final String INFINITE_COOKIE = "infinitecookie";

 @Override
 public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
 Cookie cookie = getCookie(httpRequest, INFINITE_COOKIE);
 PersonPojo user = getInfiniteUser(httpRequest, cookie);
 if (user != null) {
 String user_email = user.getEmail();
 //try to get user, or create one if they don't exist
 try {
 getOrCreateUser(user_email, user.getCommunities());
 } catch (Exception ex) {
 log.error("Error get/create user", ex);
 return null;
 }

 httpRequest.getSession(true); //create a session if one does not exist, was having some issues w/ sessions breaking
 UserIdentificationInfo uii = new UserIdentificationInfo(user_email, ""); //NO PASSWORD NECESSARY
 return uii;
 } else {
 log.debug("Infinit.e Person was null");
 return null;
 }
 }

 private void getOrCreateUser(String username, List & lt; PersonCommunityPojo & gt; communities) throws Exception {
 UserManager userManager = Framework.getService(UserManager.class);
 //set up groups:
 String[] groups = new String[communities.size() + 1];
 groups[0] = "members"; //add default group
 for (int i = 0; i & lt; communities.size(); i++) {
 PersonCommunityPojo community = communities.get(i);
 //add group to this users list
 groups[i + 1] = community.getName();
 //make sure group exists
 DocumentModel group_doc_model = userManager.getGroupModel(community.getName());
 if (group_doc_model == null) {
 log.debug("Group: " + community.getName() + " did not exist, creating and adding this person as user.");
 //create it
 group_doc_model = userManager.getBareGroupModel();
 group_doc_model.setProperty("group", "grouplabel", community.getName());
 group_doc_model.setProperty("group", "groupname", community.getName());
 group_doc_model.setProperty("group", "description", community.getName());
 String[] members = new String[1];
 members[0] = username;
 group_doc_model.setProperty("group", "members", members);
 userManager.createGroup(group_doc_model);
 } else {
 //make sure this person is a member and update group
 @
 SuppressWarnings("unchecked")
 List & lt;
 String & gt;
 member_array = (List & lt; String & gt;) group_doc_model.getProperty("group", "members");
 Set & lt;
 String & gt;
 members = new HashSet & lt;
 String & gt;
 (member_array);
 members.add(username);
 group_doc_model.setProperty("group", "members", members.toArray());
 userManager.updateGroup(group_doc_model);
 }
 }

 NuxeoPrincipal principal = userManager.getPrincipal(username);
 if (principal != null) {

 DocumentModel user_doc_model = userManager.getUserModel(username);
 user_doc_model.setProperty("user", "groups", groups);
 userManager.updateUser(user_doc_model);
 } else {
 log.debug("principal was null, create a new user");
 DocumentModel user_doc_model = userManager.getBareUserModel();
 user_doc_model.setProperty("user", "username", username);
 user_doc_model.setProperty("user", "email", username);
 user_doc_model.setProperty("user", "password", "fakepassword" + new Random().nextInt());
 user_doc_model.setProperty("user", "groups", groups);
 userManager.createUser(user_doc_model);
 }
 }

 @Override
 public void initPlugin(Map & lt; String, String & gt; parameters) {
 log.info("init Infinite Authenticator");
 if (parameters.containsKey("infiniteAPIURL") & amp; & amp; parameters.containsKey("infiniteLoginURL")) {
 log.info("API_URL_PARAM: " + parameters.get("infiniteAPIURL"));
 log.info("API_LOGIN_PARAM: " + parameters.get("infiniteLoginURL"));
 infinite_api_url = parameters.get("infiniteAPIURL");
 infinite_login_url = parameters.get("infiniteLoginURL");
 }
 log.debug("end init");
 }

 @Override
 public Boolean needLoginPrompt(HttpServletRequest httpRequest) {
 return true;
 }

 @Override
 public Boolean handleLoginPrompt(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String baseURL) {
 Cookie cookie = getCookie(httpRequest, "infinitecookie");
 Boolean keepalive = isLoggedIn(httpRequest, cookie);
 log.debug("keepalive success: " + keepalive);
 if (!keepalive) {
 try {
 String redirect_url = httpRequest.getRequestURL().toString();
 String security_header = httpRequest.getHeader("X-Forwarded-Proto");
 if (security_header != null & amp; & amp; security_header.toLowerCase().equals("https")) {
 redirect_url = redirect_url.replace("http://", "https://");
 }
 redirect_url = URLEncoder.encode(redirect_url, "UTF-8");
 httpResponse.sendRedirect(getLoginUrl(httpRequest, infinite_login_url) + "?redirect=" + redirect_url);
 return true;
 } catch (Exception ex) {
 log.error("unable to redirect", ex);
 }
 }
 return false;
 }

 @Override
 public List & lt;
 String & gt;
 getUnAuthenticatedURLPrefix() {
 log.debug("In unauth url prefix: there are no urls we deny access");
 return null;
 }

 private Boolean isLoggedIn(HttpServletRequest httpRequest, Cookie cookie) {
 if (cookie != null) {
 InfiniteDriver inf_driver = new InfiniteDriver(getApiUrl(httpRequest, infinite_api_url));
 inf_driver.useExistingCookie(cookie.getValue());
 return inf_driver.sendKeepalive();
 }

 return false;
 }

 private PersonPojo getInfiniteUser(HttpServletRequest httpRequest, Cookie cookie) {
 if (cookie != null) {
 InfiniteDriver inf_driver = new InfiniteDriver(getApiUrl(httpRequest, infinite_api_url));
 inf_driver.useExistingCookie(cookie.getValue());
 ResponseObject ro = new ResponseObject();
 return inf_driver.getPerson(null, ro);
 }
 return null;
 }

 @Override
 public Boolean handleLogout(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
 //try to invalidate infinite cookie
 Cookie cookie = getCookie(httpRequest, "infinitecookie");
 if (cookie != null) {
 InfiniteDriver inf_driver = new InfiniteDriver(getApiUrl(httpRequest, infinite_api_url));
 inf_driver.useExistingCookie(cookie.getValue());
 inf_driver.logout();
 }

 try {
 //redirect to infinit.e login
 httpResponse.sendRedirect(getLoginUrl(httpRequest, infinite_login_url));
 return true;
 } catch (Exception ex) {
 log.error("unable to redirect", ex);
 }
 return false;
 }

 private String getApiUrl(HttpServletRequest httpRequest, String property) {
 if (property.equals("AUTOMATIC")) {
 try {
 return "http://" + httpRequest.getServerName() + "/api/";
 } catch (Exception ex) {
 log.debug("error converting to url");
 }

 }

 return property;
 }

 private String getLoginUrl(HttpServletRequest httpRequest, String property) {
 if (property.equals("AUTOMATIC")) {
 try {
 return "http://" + httpRequest.getServerName();
 } catch (Exception ex) {
 log.debug("error converting to url");
 }

 }

 return property;
 }

 private static Cookie getCookie(HttpServletRequest httpRequest, String cookieName) {
 log.debug("trying to get cookie: " + cookieName);
 Cookie cookies[] = httpRequest.getCookies();
 if (cookies != null) {
 for (Cookie cookie: cookies) {
 if (cookie.getName().equals(cookieName)) {
 return cookie;
 }
 }
 }
 return null;
 }
}

Step 2: Create a custom Login Plugin that implements LoginPlugin

Next we needed to override the Nuxeo default Login Plugin because the default login plugin TrustingLM will not authenticate our users correctly (I think this is due to our using of random passwords but I am not positive)

The login plugin is a new class that implements LoginPlugin and overrides all the methods. It is very basic and we just return the username anytime it comes to validateUserIdentity

>InfiniteLoginPlugin.java

package com.ikanow.infinit.e.nuxeo.auth;

import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
import org.nuxeo.ecm.platform.login.LoginPlugin;

public class InfiniteLoginPlugin implements LoginPlugin {

    private static final Log log = LogFactory.getLog(InfiniteLoginPlugin.class);
    private String name = "InfiniteLoginPlugin";
    private Map & lt;
    String, String & gt;
    params = null;

    @Override
    public String validatedUserIdentity(UserIdentificationInfo userIdent) {
        return userIdent.getUserName();
    }

    @Override
    public Boolean initLoginModule() {
        return true;
    }

    @Override
    public Map & lt;
    String, String & gt;
    getParameters() {
        return params;
    }

    @Override
    public void setParameters(Map & lt; String, String & gt; parameters) {
        params = parameters;

    }

    @Override
    public String getParameter(String parameterName) {
        return params.get(parameterName);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String pluginName) {
        name = pluginName;
    }
}

Step 3: Create a custom configuration file that is external to our extensions so we can adjust it for different Nuxeo instances

We could have put the authenticator properties in the extension contribution outlined in the next step, but we wanted the ability to adjust them for different server configurations so we moved some of the pieces outside (namely the API and Login urls). According to Nuxeo, this file’s name must end in -config.xml and be placed in NUXEO_INSTALL/nxserver/config/

The important thing to add here are any parameters you want – you can add and give whatever names/values you wish. They will be available in the Authenticator during the initPlugin function. We also let Nuxeo know the name of our Authenticator (INFINITE_AUTH), and the Login Plugin we wish to use (InfiniteLoginPlugin, the piece we made in Step 2).

>infinite-auth-config.xml

<?xml version="1.0″?>
<component name="com.ikanow.infinit.e.nuxeo.auth.InfiniteAuthenticatorConfig">
  <require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
  <extension target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService" point="authenticators">
    <authenticationPlugin name="INFINITE_AUTH" enabled="true" class="com.ikanow.infinit.e.nuxeo.auth.InfiniteAuthenticator">
      <loginModulePlugin>InfiniteLoginPlugin</loginModulePlugin>
      <parameters>
        <parameter name="infiniteLoginURL">http://infinite.ikanow.com/</parameter>
        <parameter name="infiniteAPIURL">http://infinite.ikanow.com/api/</parameter>
      </parameters>
    </authenticationPlugin>
  </extension>
</component>

Step 4: Adjust the default authentication chain order for both default login and automation

Next we needed to actually tell Nuxeo what extension point we were going to override, and change the order of the Authenticators to use ours first. To do this we needed to edit the config file that should have been automatically created in your plugin project at /src/main/resources/OSGI-INF/extensions/filename.xml

In the file:

  1. We first defined our login plugin and give it a name (the same one you used above in step 3 in the config.xml, e.g. InfiniteLoginPlugin)
  2. Next we overrode the authentication change, we tell Nuxeo to use our new InfiniteAuthenticator first for our platform, then resort to Form Auth if necessary (the only way to get to form Auth is via a direct link to nuxeo/login.jsp because we always redirect invalid logins back to Infinit.e).
  3. Lastly we connected to Nuxeo using the automation client. We needed to override that specfic chain as well, calling InfiniteAuthenticator first, as you can see in the last extension.
NOTE: You may have some code in a .xml file for your Login Plugin, I recommend deleting it all out and putting it in this one configuration file. See example below for how we left the LoginPlugin.xml

>InfiniteAuthenticator.xml

<?xml version="1.0″?>
<component name="com.ikanow.infinit.e.nuxeo.auth.InfiniteAuthenticator">
  <require>org.nuxeo.ecm.platform.ui.web.auth.defaultConfig</require>
  <!– We need to use the Infinit.e login plugin because trusting lm is doing something weird and failing to authenticate users –>
  <extension target="org.nuxeo.ecm.platform.login.LoginPluginRegistry" point="plugin">
    <LoginPlugin name="InfiniteLoginPlugin" class="com.ikanow.infinit.e.nuxeo.auth.InfiniteLoginPlugin">
      <enabled>true</enabled>
    </LoginPlugin>
  </extension>
  <extension target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService" point="chain">
    <authenticationChain>
      <plugins>
        <plugin>INFINITE_AUTH</plugin>
        <plugin>FORM_AUTH</plugin>
      </plugins>
    </authenticationChain>
  </extension>
  <extension target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService" point="specificChains">
    <specificAuthenticationChain name="Automation">
      <urlPatterns>
        <url>(.*)/automation.*</url>
      </urlPatterns>
      <replacementChain>
        <plugin>INFINITE_AUTH</plugin>
        <plugin>AUTOMATION_BASIC_AUTH</plugin>
      </replacementChain>
    </specificAuthenticationChain>
  </extension>
</component>

<?xml version="1.0″?>
<component name="com.ikanow.infinit.e.nuxeo.auth.InfiniteLoginPlugin">
</component>

Step 5: Deploy to Nuxeo

Finally now that we’ve programmed all this up, we deployed it to Nuxeo to test using the following steps:

  1. Build project by right clicking on project name > Nuxeo > Export Jar
  2. Place this jar in your nuxeo instance at NUXEO_INSTALL/nxserver/plugins/
  3. Place the -config.xml file we created in step 3 at NUXEO_INSTALL/nxserver/config/
  4. Restart Nuxeo

After that point you should be able to login to an external service, then attempt to go to Nuxeo and it will automatically create a user account, groups and let you in.

Conclusion

The Nuxeo platform offers a flexible API and multiple extension points that allowed us to tightly integrate our log in infrastructure and data models with their authentication system. Nuxeo actually offers a few standard solutions to allow SSO such as CAS, Portal Authentication, and Token Authentication. We decided to build our own because we wanted to pass on Infinit.e’s user groups permissions without having to manage Nuxeo groups in addition to Infinit.e’s as well as be able to perform some automatic actions as users (create documents). Ultimately, capability allows us to build an improved user experience for our users taking advantage of Nuxeo’s case management capabilities.

The post [Guest Post from Ikanow] Integrating a Single Sign On (SSO) with Nuxeo for Case Management appeared first on Nuxeo Blogs.

Viewing all 161 articles
Browse latest View live