<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Nuxeo Blogs</title>
	<atom:link href="http://www.nuxeo.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.nuxeo.com/blog</link>
	<description>About the Nuxeo Content Management Platform and Its Open Source Ecosystem</description>
	<lastBuildDate>Mon, 20 May 2013 12:47:13 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Extend Nuxeo Drive Series #3 &#8211; How to synchronize a document without attached binaries</title>
		<link>http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-3/</link>
		<comments>http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-3/#comments</comments>
		<pubDate>Mon, 20 May 2013 12:47:13 +0000</pubDate>
		<dc:creator>Laurent Doguin</dc:creator>
				<category><![CDATA[Product & Development]]></category>
		<category><![CDATA[intermediate]]></category>
		<category><![CDATA[Nuxeo Drive]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5141</guid>
		<description><![CDATA[<p><p>As we&#8217;ve seen <a href="http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-series-2/" title="Extend Nuxeo Drive Series #2 – Override existing adapter parameters">last week</a> in the Extend Nuxeo Drive series, files and folders are represented server side using adapters. By default it&#8217;s the <em>DocumentBackedFileItem</em> adapter that is used for simple, non-folderish documents. When you get the file from a document, the document <em>BlobHolder</em> is used. It&#8217;s a document adapter used to get or set the main file of a document. It means that the file you see on the desktop while using Drive has been retrived with this code:</p>
<p>It also means that when you modify a file on the desktop, it&#8217;s updated to the document using the BlobHolder.setBlob method. Once you know that, you can do some interesting stuff without having to write Nuxeo Drive factories or adapters (sorry, not yet, maybe in the next post :-) ).</p>
<p>Here&#8217;s a fairly simple example. The book document type available in the BookTraining-Day3 studio template has no binary field, only &#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-3/">Extend Nuxeo Drive Series #3 &#8211; How to synchronize a document without attached binaries</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>As we&#8217;ve seen <a href="http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-series-2/" title="Extend Nuxeo Drive Series #2 – Override existing adapter parameters">last week</a> in the Extend Nuxeo Drive series, files and folders are represented server side using adapters. By default it&#8217;s the <em>DocumentBackedFileItem</em> adapter that is used for simple, non-folderish documents. When you get the file from a document, the document <em>BlobHolder</em> is used. It&#8217;s a document adapter used to get or set the main file of a document. It means that the file you see on the desktop while using Drive has been retrived with this code:</p>
<pre class="brush: java; title: ; notranslate">
    BlobHolder bh = documentModel.getAdapter(BlobHolder.class);
    Blob b = bh.getBlob();
</pre>
<p>It also means that when you modify a file on the desktop, it&#8217;s updated to the document using the BlobHolder.setBlob method. Once you know that, you can do some interesting stuff without having to write Nuxeo Drive factories or adapters (sorry, not yet, maybe in the next post :-) ).</p>
<p>Here&#8217;s a fairly simple example. The book document type available in the BookTraining-Day3 studio template has no binary field, only metadata. So we have to think of a specific blob holder. We can easily represent it as a CSV file like this:</p>
<pre><code>
"Name","Value"
"Author","Soulcie"
"Borrowed By","Administrator"
"Category","comics/manga/managa1;comics/manga;"
"ISBN","1307483092"
"Publication date","5/23/13 12:00 AM"
"Rating","5"
</code></pre>
<p>Now imagine that when the user modifies some of the values, we update the document&#8217;s metadata. This will happen in the BlobHolder.setBlob method. Here&#8217;s a simple implementation of a BookBlobHolder:</p>
<pre class="brush: java; title: ; notranslate">
package org.nuxeo.sample;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;

import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder;
import org.nuxeo.ecm.core.api.impl.blob.StringBlob;
import org.nuxeo.ecm.core.api.model.PropertyException;
import org.nuxeo.ecm.core.schema.DocumentType;
import org.nuxeo.ecm.core.schema.SchemaManager;
import org.nuxeo.ecm.core.schema.types.Field;
import org.nuxeo.ecm.core.schema.types.ListType;
import org.nuxeo.ecm.core.schema.types.Type;
import org.nuxeo.ecm.core.schema.types.primitives.BooleanType;
import org.nuxeo.ecm.core.schema.types.primitives.DateType;
import org.nuxeo.ecm.core.schema.types.primitives.DoubleType;
import org.nuxeo.ecm.core.schema.types.primitives.IntegerType;
import org.nuxeo.ecm.core.schema.types.primitives.LongType;
import org.nuxeo.ecm.core.schema.types.primitives.StringType;
import org.nuxeo.runtime.api.Framework;

import au.com.bytecode.opencsv.CSVReader;
import au.com.bytecode.opencsv.CSVWriter;

public class BookBlobHolder extends DocumentBlobHolder {

    public static final String[] HEADERS = { &quot;Name&quot;, &quot;Value&quot; };

    public enum BookProperties {
        AUTHOR(&quot;Author&quot;, &quot;bk:author&quot;), BORROWED_BY(&quot;Borrowed By&quot;,
                &quot;bk:borrowedBy&quot;), CATEGORY(&quot;Category&quot;, &quot;bk:category&quot;), ISBN(
                &quot;ISBN&quot;, &quot;bk:isbn&quot;), PUBLICATION_DATE(&quot;Publication date&quot;,
                &quot;bk:publicationDate&quot;), RATING(&quot;Rating&quot;, &quot;bk:rating&quot;);

        private String propertyName;

        private String name;

        private BookProperties(String name, String propertyName) {
            this.name = name;
            this.propertyName = propertyName;
        }

        public void setProperty(DocumentModel doc, String stringValue)
                throws PropertyException, ClientException {
            DocumentType docType = Framework.getLocalService(
                    SchemaManager.class).getDocumentType(doc.getType());
            Serializable value = convertStringValue(docType, propertyName,
                    stringValue);
            doc.setPropertyValue(propertyName, value);
        }

        public String getPropertyValue(DocumentModel doc)
                throws PropertyException, ClientException {
            DocumentType docType = Framework.getLocalService(
                    SchemaManager.class).getDocumentType(doc.getType());
            Serializable value = doc.getProperty(propertyName).getValue();
            if (value == null) {
                return &quot;&quot;;
            }
            return convertValueToString(docType, propertyName, value);
        }

        public String[] getLine(DocumentModel doc) throws PropertyException,
                ClientException {
            String[] line = new String[2];
            line[0] = name;
            line[1] = getPropertyValue(doc);
            return line;
        }

        public static BookProperties fromString(String name) {
            if (name != null) {
                for (BookProperties b : BookProperties.values()) {
                    if (name.equalsIgnoreCase(b.name)) {
                        return b;
                    }
                }
            }
            return null;
        }

        protected Serializable convertStringValue(DocumentType docType,
                String fieldName, String stringValue) {
            if (docType.hasField(fieldName)) {
                Field field = docType.getField(fieldName);
                if (field != null) {
                    try {
                        Serializable fieldValue = null;
                        Type fieldType = field.getType();
                        if (fieldType.isListType()) {
                            Type listFieldType = ((ListType) fieldType).getFieldType();
                            if (listFieldType.isSimpleType()) {
                                fieldValue = stringValue.split(&quot;;&quot;);
                            } else {
                                fieldValue = (Serializable) Arrays.asList(stringValue.split(&quot;;&quot;));
                            }
                        } else {
                            if (field.getType().isSimpleType()) {
                                if (field.getType() instanceof StringType) {
                                    fieldValue = stringValue;
                                } else if (field.getType() instanceof IntegerType) {
                                    fieldValue = Integer.parseInt(stringValue);
                                } else if (field.getType() instanceof LongType) {
                                    fieldValue = Long.parseLong(stringValue);
                                } else if (field.getType() instanceof DoubleType) {
                                    fieldValue = Double.parseDouble(stringValue);
                                } else if (field.getType() instanceof BooleanType) {
                                    fieldValue = Boolean.valueOf(stringValue);
                                } else if (field.getType() instanceof DateType) {
                                    fieldValue = SimpleDateFormat.getInstance().parse(
                                            stringValue);
                                }
                            }
                        }
                        return fieldValue;
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return null;
        }

        protected String convertValueToString(DocumentType docType,
                String fieldName, Serializable value) {
            if (docType.hasField(fieldName)) {
                Field field = docType.getField(fieldName);
                if (field != null) {
                    try {
                        String stringValue = null;
                        Type fieldType = field.getType();
                        if (fieldType.isListType()) {
                            Type listFieldType = ((ListType) fieldType).getFieldType();
                            if (listFieldType.isSimpleType()) {
                                String[] arrayValue = (String[]) value;
                                StringBuilder sb = new StringBuilder();
                                for (int i = 0; i &lt; arrayValue.length; i++) {
                                    sb.append(arrayValue[i]);
                                    sb.append(&quot;;&quot;);
                                }
                                stringValue = sb.toString();
                            } else {
                                List&lt;String&gt; listValue = (List&lt;String&gt;) value;
                                StringBuilder sb = new StringBuilder();
                                for (String string : listValue) {

                                    sb.append(string);
                                    sb.append(&quot;;&quot;);
                                }
                                stringValue = sb.toString();
                            }
                        } else {
                            if (field.getType().isSimpleType()) {
                                if (field.getType() instanceof DateType) {
                                    Calendar date = (Calendar) value;
                                    stringValue = SimpleDateFormat.getInstance().format(
                                            date.getTime());
                                } else {
                                    stringValue = String.valueOf(value);
                                }
                            }
                        }
                        return stringValue;
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return null;
        }
    }

    public BookBlobHolder(DocumentModel doc, String path) {
        super(doc, path);
    }

    @Override
    public Blob getBlob() throws ClientException {
        try {
            StringWriter sw = new StringWriter();
            CSVWriter csw = new CSVWriter(sw, ',', '&quot;');
            List&lt;String[]&gt; allLines = new ArrayList&lt;String[]&gt;();
            allLines.add(HEADERS);
            for (BookProperties bookProperty : BookProperties.values()) {
                allLines.add(bookProperty.getLine(doc));
            }
            csw.writeAll(allLines);
            csw.flush();
            csw.close();
            sw.close();
            Blob b = new StringBlob(sw.toString(), &quot;text/csv&quot;);
            String filename = doc.getTitle() + &quot;.csv&quot;;
            b.setFilename(filename);
            return b;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void setBlob(Blob blob) throws ClientException {
        if (blob == null) {
            return;
        }
        CSVReader csvReader = null;
        try {
            csvReader = new CSVReader(blob.getReader(), ',', '&quot;');
            List&lt;String[]&gt; lines = csvReader.readAll();
            for (String[] line : lines) {
                String name = line[0];
                BookProperties bookProperty = BookProperties.fromString(name);
                if (bookProperty != null &amp;&amp; line.length == 2) {
                    String value = line[1];
                    bookProperty.setProperty(doc, value);
                }
            }
            csvReader.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String getHash() throws ClientException {
        return doc.getId() + getModificationDate().toString();
    }

}
</pre>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;component name=&quot;org.nuxeo.sample.drive.bh.adapters&quot;&gt;

  &lt;extension
    target=&quot;org.nuxeo.ecm.core.api.blobholder.BlobHolderAdapterComponent&quot;
    point=&quot;BlobHolderFactory&quot;&gt;
    &lt;blobHolderFactory name=&quot;bookBh&quot; docType=&quot;Book&quot;
      class=&quot;org.nuxeo.sample.BookBlobHolderFactory&quot; /&gt;
  &lt;/extension&gt;

&lt;/component&gt;
</pre>
<p>There&#8217;s a lot of boring code to get and set the values of the different properties used by the Book document type. All types are not supported, but it&#8217;s a start. It handles all scalar properties and simple lists of scalar item (using &#8216;;&#8217; char as a separator). Now you will see all your Book documents as CSV files on the desktop. See, just with BlobHolder you can do some interesting things :)</p>
<p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-3/">Extend Nuxeo Drive Series #3 &#8211; How to synchronize a document without attached binaries</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Extend Nuxeo Drive Series #2 &#8211; Override existing adapter parameters</title>
		<link>http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-series-2/</link>
		<comments>http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-series-2/#comments</comments>
		<pubDate>Mon, 13 May 2013 16:07:20 +0000</pubDate>
		<dc:creator>Laurent Doguin</dc:creator>
				<category><![CDATA[Product & Development]]></category>
		<category><![CDATA[beginner]]></category>
		<category><![CDATA[customization]]></category>
		<category><![CDATA[Nuxeo Drive]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5137</guid>
		<description><![CDATA[<p><p>Last week I started a series of blog post on how to extend Nuxeo Drive. Today I&#8217;ll write about factories and adapters. Every file or folder on the client file system is represented on the server side using adapters. Those adapters are associated to a particular document through a factory.</p>
<p>We can take the default configuration as an example to better understand this.</p>
<h2>A look at the default configuration</h2>
<p>Using Drive on the client side, the first entry point is the <em>Nuxeo Drive</em> folder. It contains all the documents you have synchronized. This folder is represented on the server side through the <em>DefaultTopLevelFolderItem</em> adapter, and returned by the <em>DefaultTopLevelFolderItemFactory</em> factory. This configuration is declared in the <em>org.nuxeo.drive.service.FileSystemItemAdapterService#topLevelFolderItemFactory</em> extension point:</p>
<p>As you can see, you can change the <em>folderName</em> parameter to something else. Some might prefer &#8216;My Drive&#8217; or &#8216;My Nuxeo Files&#8217; as name for your sync documents. The default implementation &#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-series-2/">Extend Nuxeo Drive Series #2 &#8211; Override existing adapter parameters</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>Last week I started a series of blog post on how to extend Nuxeo Drive. Today I&#8217;ll write about factories and adapters. Every file or folder on the client file system is represented on the server side using adapters. Those adapters are associated to a particular document through a factory.</p>
<pre class="brush: xml; title: ; notranslate">
  - FileSystemItem  // Represents any file on the fs
    | -- FileItem    // A File
         |-- DocumentBackedFileItem // Default adapter implementation for non-folderish document
    | -- FolderItem  // A Folder
         | -- AbstractVirtualFolderItem // Represents a folder on the client side, that is not backed by an actual document
             | -- DefaultTopLevelFolderItem // The default Nuxeo Drive folder, containing your synchronized documents.
         | -- DocumentBackedFolderItem // Default adapter for folderish document
              | -- DefaultSyncRootFolderItem // Adapter for the root synchronized doc. (put simply, the docs where you clicked on the sync button)
              | -- UserWorkspaceTopLevelFolderItem  // User workspace based implementation of the top level
              | -- UserWorkspaceSyncRootParentFolderItem // User workspace based implementation of the synchronization root parent
</pre>
<p>We can take the default configuration as an example to better understand this.</p>
<h2>A look at the default configuration</h2>
<p>Using Drive on the client side, the first entry point is the <em>Nuxeo Drive</em> folder. It contains all the documents you have synchronized. This folder is represented on the server side through the <em>DefaultTopLevelFolderItem</em> adapter, and returned by the <em>DefaultTopLevelFolderItemFactory</em> factory. This configuration is declared in the <em>org.nuxeo.drive.service.FileSystemItemAdapterService#topLevelFolderItemFactory</em> extension point:</p>
<pre class="brush: xml; title: ; notranslate">
  &lt;extension target=&quot;org.nuxeo.drive.service.FileSystemItemAdapterService&quot;
    point=&quot;topLevelFolderItemFactory&quot;&gt;

    &lt;topLevelFolderItemFactory
      class=&quot;org.nuxeo.drive.service.impl.DefaultTopLevelFolderItemFactory&quot;&gt;
      &lt;parameters&gt;
        &lt;parameter name=&quot;folderName&quot;&gt;Nuxeo Drive&lt;/parameter&gt;
      &lt;/parameters&gt;
    &lt;/topLevelFolderItemFactory&gt;

  &lt;/extension&gt;
</pre>
<p>As you can see, you can change the <em>folderName</em> parameter to something else. Some might prefer &#8216;My Drive&#8217; or &#8216;My Nuxeo Files&#8217; as name for your sync documents. The default implementation simply lists all the synchronization roots, which is to say all the document where you clicked on the Sync Drive icon. You can imagine different implementations like one that would display the content of your personal workspace along your synchronization roots. You could also have a hard-coded implementation that shows the same documents for every Nuxeo users. That&#8217;s really up to you :) . It doesn&#8217;t even have to be documents. You could have a folder called My Directories that would have a CSV file per directories declared in Nuxeo. (lots of coding involved but why not?)</p>
<p>Anyway, inside the default Nuxeo Drive folder you will see your synchronized roots and their children. This is managed by the <em>org.nuxeo.drive.service.FileSystemItemAdapterService#fileSystemItemFactory</em> extension point. Every one of these factories has an order attribute. They are resolved from the lowest to the highest. The first factory that returns an adapter is used. As you can see the lowest order is set on the <em>defaultSyncRootFolderItemFactory</em>. It will be returned if the adapted document has the <em>DriveSynchronized</em> facet. And the thing is, each time you click on the sync button, this facet is added to the document. As the child documents won&#8217;t have this facet, <em>defaultSyncRootFolderItemFactory</em> won&#8217;t match and the <em>defaultFileSystemItemFactory</em> will be used.</p>
<pre class="brush: xml; title: ; notranslate">
  &lt;extension target=&quot;org.nuxeo.drive.service.FileSystemItemAdapterService&quot;
    point=&quot;fileSystemItemFactory&quot;&gt;

    &lt;fileSystemItemFactory name=&quot;defaultSyncRootFolderItemFactory&quot;
      order=&quot;10&quot; facet=&quot;DriveSynchronized&quot;
      class=&quot;org.nuxeo.drive.service.impl.DefaultSyncRootFolderItemFactory&quot; /&gt;
    &lt;fileSystemItemFactory name=&quot;defaultFileSystemItemFactory&quot;
      order=&quot;50&quot; class=&quot;org.nuxeo.drive.service.impl.DefaultFileSystemItemFactory&quot;&gt;
      &lt;parameters&gt;
        &lt;parameter name=&quot;versioningDelay&quot;&gt;3600&lt;/parameter&gt;
        &lt;parameter name=&quot;versioningOption&quot;&gt;MINOR&lt;/parameter&gt;
      &lt;/parameters&gt;
    &lt;/fileSystemItemFactory&gt;

  &lt;/extension&gt;
</pre>
<p>As you can see the <em>defaultFileSystemItemFactory</em> factory takes some parameters. They describe the default versioning behaviour. The adapter returned by the factory is <em>DocumentBackedFileItem</em>, which has a <em>versionIfNeeded</em> method called when the associated file is changing. This method first looks if the document needs to be versioned. This will occur if the current contributor is different from the last contributor or if the last modification was done more than 3600 seconds ago. As you have already guessed, 3600 is not arbitrary and comes from the <em>versioningDelay</em> parameter of the factory. If the doc needs to be versioned, the following code is executed:</p>
<pre class="brush: java; title: ; notranslate">
            doc.putContextData(VersioningService.VERSIONING_OPTION,
                    factory.getVersioningOption());
</pre>
<p>We put the <em>versioningOption</em> parameter in the document context map. This information will be retrieved and used next time the document is saved. So by default, we do minor increments. Possible values are MINOR, MAJOR and NONE.</p>
<h2>Customize the default configuration</h2>
<p>Here&#8217;s an example of custom configuration of the default factories (no, we&#8217;re not going to write a complete new adapter or factory just yet). Let&#8217;s say I don&#8217;t want my Drive folder to be called Nuxeo Drive. I want it to be called &#8216;My Favorite Nuxeo Files&#8217;. And I also want to upgrade the major increment version number each time a document is modified using drive. This can be done using the following contributions.</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;component name=&quot;org.nuxeo.sample.drive.adapters&quot; version=&quot;1.0&quot;&gt;

  &lt;!-- Make sure your contribution is registered after the default one --&gt;
  &lt;require&gt;org.nuxeo.drive.adapters&lt;/require&gt;

  &lt;!-- Override the folderName parameter. No need to specify the class again as there can be only one topLevelFolderItemFactory. --&gt;
  &lt;extension target=&quot;org.nuxeo.drive.service.FileSystemItemAdapterService&quot;
    point=&quot;topLevelFolderItemFactory&quot;&gt;
    &lt;topLevelFolderItemFactory&gt;
      &lt;parameters&gt;
        &lt;parameter name=&quot;folderName&quot;&gt;My Favorite Nuxeo Files&lt;/parameter&gt;
      &lt;/parameters&gt;
    &lt;/topLevelFolderItemFactory&gt;
  &lt;/extension&gt;

  &lt;!-- Override the versioningOption parameter. We need to keep the name of the contribution we want to override. The parameters will be merge with the existing one.--&gt;
  &lt;extension target=&quot;org.nuxeo.drive.service.FileSystemItemAdapterService&quot;
    point=&quot;fileSystemItemFactory&quot;&gt;
    &lt;fileSystemItemFactory name=&quot;defaultFileSystemItemFactory&quot;&gt;
      &lt;parameters&gt;
        &lt;parameter name=&quot;versioningOption&quot;&gt;MAJOR&lt;/parameter&gt;
      &lt;/parameters&gt;
    &lt;/fileSystemItemFactory&gt;

  &lt;/extension&gt;
&lt;/component&gt;
</pre>
<p>Next week we&#8217;ll dig deeper into these factories and adapters.</p>
<p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-series-2/">Extend Nuxeo Drive Series #2 &#8211; Override existing adapter parameters</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-series-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Extend Nuxeo Drive Series #1 &#8211; Override Operations</title>
		<link>http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-1/</link>
		<comments>http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-1/#comments</comments>
		<pubDate>Tue, 07 May 2013 11:53:17 +0000</pubDate>
		<dc:creator>Laurent Doguin</dc:creator>
				<category><![CDATA[Product & Development]]></category>
		<category><![CDATA[beginner]]></category>
		<category><![CDATA[content automation]]></category>
		<category><![CDATA[Nuxeo Drive]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5135</guid>
		<description><![CDATA[<p><p>Like most parts of the Nuxeo Platform, we designed <a href="http://www.nuxeo.com/en/products/drive-desktop-sync" title="Desktop Sync for the Nuxeo Content Repository">Nuxeo Drive</a> as something extensible. And there are indeed different ways you can customize it to fit your needs. I&#8217;ll be writing more posts about how to extend Nuxeo Drive in the coming weeks. Today we&#8217;ll take a look at what you can do by simply overriding the different operations used by Nuxeo Drive.</p>
<p>Let’s take a very simple example. When Drive detects a conflict, the file is renamed a certain way. This is done using the <em>NuxeoDrive.GenerateConflictedItemName</em> operation. So what we can do is override this operation to specify our own way of renaming a file. Right now what it does is split the name of the file and the extension. Then we generate the <em>contextSection</em>. It&#8217;s the user&#8217;s first and last name concatenated with the current date formatted as &#8220;yyyy-MM-dd hh-mm&#8221;. Then it puts them back together.</p>
<p>To &#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-1/">Extend Nuxeo Drive Series #1 &#8211; Override Operations</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<div id="attachment_5092" class="wp-caption alignright" style="width: 160px"><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/Screen-shot-2013-04-10-at-5.40.33-PM.png"><img class="size-thumbnail wp-image-5092" alt="Extend Nuxeo Drive" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/Screen-shot-2013-04-10-at-5.40.33-PM-150x150.png" width="150" height="150" /></a><p class="wp-caption-text">Extend Nuxeo Drive</p></div>
<p>Like most parts of the Nuxeo Platform, we designed <a href="http://www.nuxeo.com/en/products/drive-desktop-sync" title="Desktop Sync for the Nuxeo Content Repository">Nuxeo Drive</a> as something extensible. And there are indeed different ways you can customize it to fit your needs. I&#8217;ll be writing more posts about how to extend Nuxeo Drive in the coming weeks. Today we&#8217;ll take a look at what you can do by simply overriding the different operations used by Nuxeo Drive.</p>
<p>Let’s take a very simple example. When Drive detects a conflict, the file is renamed a certain way. This is done using the <em>NuxeoDrive.GenerateConflictedItemName</em> operation. So what we can do is override this operation to specify our own way of renaming a file. Right now what it does is split the name of the file and the extension. Then we generate the <em>contextSection</em>. It&#8217;s the user&#8217;s first and last name concatenated with the current date formatted as &#8220;yyyy-MM-dd hh-mm&#8221;. Then it puts them back together.</p>
<p>To override it, you need to declare your operation as usual, but don&#8217;t forget to use the exact same ID as the operation you want to override. Here I&#8217;m changing the date format to &#8220;dd-MM-yyyy hh-mm&#8221; and adding the user&#8217;s email address.</p>
<pre class="brush: java; title: ; notranslate">
/*
 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     ldoguin
 */

package org.nuxeo.sample;

import java.text.SimpleDateFormat;

/**
 * @author ldoguin
 */
// Use the same ID as the operation to override
@Operation(id = NuxeoDriveGenerateConflictedItemName.ID, category = Constants.CAT_SERVICES, label = &quot;Nuxeo Drive: Generate Conflicted Item Name&quot;)
public class NuxeoDriveCustomGenerateConflictedItemName {

    @Context
    protected OperationContext ctx;

    @Param(name = &quot;name&quot;)
    protected String name;

    @Param(name = &quot;timezone&quot;, required = false)
    protected String timezone;

    @OperationMethod
    public Blob run() throws Exception {

        String extension = &quot;&quot;;
        if (name.contains(&quot;.&quot;)) {
            // Split on the last occurrence of . using a negative lookahead
            // regexp.
            String[] parts = name.split(&quot;\\.(?=[^\\.]+$)&quot;);
            name = parts[0];
            extension = &quot;.&quot; + parts[1];
        }
        NuxeoPrincipal principal = (NuxeoPrincipal) ctx.getPrincipal();
        String userName = principal.getName(); // fallback
        if (!StringUtils.isBlank(principal.getLastName())
                &amp;&amp; !StringUtils.isBlank(principal.getFirstName())) {
            // build more user friendly name from user info
            userName = principal.getFirstName() + &quot; &quot; + principal.getLastName();
        }
        Calendar userDate;
        if (timezone != null) {
            userDate = Calendar.getInstance(TimeZone.getTimeZone(timezone));
        } else {
            userDate = Calendar.getInstance();
        }
        // I'll use a French date format
        SimpleDateFormat dateFormat = new SimpleDateFormat(&quot;dd-MM-yyyy hh-mm&quot;);
        dateFormat.setCalendar(userDate);
        String formatedDate = dateFormat.format(userDate.getTime());
        // and I'll add the user's email
        String contextSection = String.format(&quot; (%s - %s - %s)&quot;, userName,
                principal.getEmail(), formatedDate);
        String conflictedName = name + contextSection + extension;
        return NuxeoDriveOperationHelper.asJSONBlob(conflictedName);
    }

}
</pre>
<p>Don&#8217;t forget the require tag and the <em>replace)&#8221;true&#8221;</em> attribute to make sure your operation override the default one:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;component name=&quot;org.nuxeo.sample.NuxeoDriveCustomGenerateConflictedItemName&quot;&gt;

  &lt;require&gt;org.nuxeo.drive.operations.NuxeoDriveGetRootsOperation&lt;/require&gt;

  &lt;extension target=&quot;org.nuxeo.ecm.core.operation.OperationServiceComponent&quot;
    point=&quot;operations&quot; &gt;
      &lt;operation replace=&quot;true&quot; class=&quot;org.nuxeo.sample.NuxeoDriveCustomGenerateConflictedItemName&quot; /&gt;
   &lt;/extension&gt;

&lt;/component&gt;
</pre>
<p>That&#8217;s just a simple example, but you can do a lot more. Every call from Drive to the server are made through Content Automation. So that means you can override pretty much everything it does. All these operations are located in the nuxeo-drive-operations bundle. Most of them are using the <em>FileSystemItemManager</em> and <em>NuxeoDriveManager</em> services. I&#8217;ll write more about this in the next posts.</p>
<p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-1/">Extend Nuxeo Drive Series #1 &#8211; Override Operations</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/development/2013/05/extend-nuxeo-drive-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Make It Red and Uppercase with Nuxeo Studio</title>
		<link>http://www.nuxeo.com/blog/development/2013/05/red-uppercase-nuxeo-studio/</link>
		<comments>http://www.nuxeo.com/blog/development/2013/05/red-uppercase-nuxeo-studio/#comments</comments>
		<pubDate>Mon, 06 May 2013 02:31:45 +0000</pubDate>
		<dc:creator>Thibaud Arguillere</dc:creator>
				<category><![CDATA[Product & Development]]></category>
		<category><![CDATA[Studio]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5072</guid>
		<description><![CDATA[<p><p>Building a wonderful content-centric application with a perfect taxonomy, with damn smart workflows, with incredible automation chains, etc. is great, sure. Still. You still need to paint it, if you see what I mean.</p>
<p>Nuxeo Studio lets you easily define the main branding of your application: login screen, colors used for text, background, links, … It is documented <a href="http://doc.nuxeo.com/display/Studio/Branding" target="_blank">here</a>. That said, you also often need to be more context-specific. For example, you may want to display a text widget in red so the user knows it is an important part of the layout. Or you may also want to force data entry with upper case letters for example.</p>
<p>Let&#8217;s take the following example: A workflow…</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/01-Workflow.jpg"><img class="alignnone  wp-image-5074" alt="blogta2-01-Workflow" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/01-Workflow.jpg" width="296" height="350" /></a></p>
<p>… has a &#8220;name&#8221; workflow variable. The form of its &#8220;First task&#8221; node displays this variable in a text widget…</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-02-DropField.jpg"><img class="alignnone  wp-image-5075" alt="blogta2-02-DropField" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-02-DropField.jpg" width="570" height="242" /></a></p>
<p>… and you want it to be displayed in red.</p>
<p>So, you set its &#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/red-uppercase-nuxeo-studio/">Make It Red and Uppercase with Nuxeo Studio</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>Building a wonderful content-centric application with a perfect taxonomy, with damn smart workflows, with incredible automation chains, etc. is great, sure. Still. You still need to paint it, if you see what I mean.</p>
<p>Nuxeo Studio lets you easily define the main branding of your application: login screen, colors used for text, background, links, … It is documented <a href="http://doc.nuxeo.com/display/Studio/Branding" target="_blank">here</a>. That said, you also often need to be more context-specific. For example, you may want to display a text widget in red so the user knows it is an important part of the layout. Or you may also want to force data entry with upper case letters for example.</p>
<p>Let&#8217;s take the following example: A workflow…</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/01-Workflow.jpg"><img class="alignnone  wp-image-5074" alt="blogta2-01-Workflow" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/01-Workflow.jpg" width="296" height="350" /></a></p>
<p>… has a &#8220;name&#8221; workflow variable. The form of its &#8220;First task&#8221; node displays this variable in a text widget…</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-02-DropField.jpg"><img class="alignnone  wp-image-5075" alt="blogta2-02-DropField" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-02-DropField.jpg" width="570" height="242" /></a></p>
<p>… and you want it to be displayed in red.</p>
<p>So, you set its &#8220;Style class&#8221; property to &#8220;doItRed&#8221;…</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-03-StetStyleClass.jpg"><img class="alignnone  wp-image-5076" alt="blogta2-03-StetStyleClass" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-03-StetStyleClass.jpg" width="326" height="231" /></a></p>
<p>…you save, and in the branding you create the <span style="font-family: 'Courier New';">doItRed</span> style:</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/bogta2-00-Red-StyleInBranding.jpg"><img class="alignnone  wp-image-5086" alt="bogta2-00-Red-StyleInBranding" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/bogta2-00-Red-StyleInBranding.jpg" width="331" height="166" /></a></p>
<p>Then, you test your work<sup>(1)</sup>.</p>
<p>. . . <em>[here, I'm waiting while you are testing]</em> . . .</p>
<p>You probably noticed something very unfortunate happened: It does not work as expected. The value we entered in the &#8220;Name&#8221; field is displayed in black. We want it red, right? So, how can we do this? How can we change the style of a widget?</p>
<p>Before explaining how you can customize this widget, I&#8217;d like to say you could have used one of the <a href="http://showcase.nuxeo.com/nuxeo/styleGuide/Messages" target="_blank">predefined classes</a> provided by Nuxeo (and explained in the <a target="_blank" title="Nuxeo UI Style Guide" href="http://showcase.nuxeo.com/nuxeo/styleGuide/">Nuxeo UI Style Guide</a>.) For example, by using &#8220;warning&#8221; or &#8220;error&#8221;, our text would have been displayed in red. But here, we want it red-red, so we have an example of customization that can&#8217;t use a predefined class.</p>
<p>If you need the thing to be red now, immediately, can&#8217;t wait, emergency-emergency, then you may have the idea to use <span style="font-family: 'Courier New';">!important</span>:</p>
<pre class="brush: css; title: ; notranslate">.doItRed {
    color:red !important;
}</pre>
<p>And well, yes, it works. But the thing is: You don&#8217;t want to use it. Really. I believe <span style="font-family: 'Courier New';">!important</span> has been added to the specification when the CSS team was spending time in Vegas with The Hangover folks. If you start using it, you&#8217;ll soon find yourself using it everywhere and at some point in a short future, nothing will work at all, your user interface will be broken. Instead of using it, it is better to understand <em>why</em> your style is not used. So, we remove this <span style="font-family: 'Courier New';">!important</span> thing, save, update our project in the server and run it.</p>
<p>A web inspector is the perfect tool for the purpose of understanding what happened to our style.  So, at runtime, we display the inspector and select our widget. The right pane clearly shows that the class has been overridden (it is in strikethrough):￼</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-04-WebInspectorStyle.jpg"><img class="alignnone  wp-image-5077" alt="blogta2-04-WebInspectorStyle" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-04-WebInspectorStyle.jpg" width="328" height="470" /></a></p>
<p>What happened here is that the default nuxeo theme, &#8220;galaxyDefaultCssNNN&#8221; (with NN being a number dynamically evaluated by the server) has been applied <em>after</em> our own class, hence the fact that our red color is not used.</p>
<p>What can we do to display our widget in red? We must (besides <em>not</em> using The Suffix That Must Not Be Named) use a CSS definition that will have a higher priority than the Galaxy one. Using the DOM ID of the widget would work, but it is complicated to get this ID, since it is generated at runtime (we don&#8217;t know this ID at the time we are adding the class in Studio). The best solution is to define our style by using the class hierarchy that is applied to our widget:</p>
<pre class="brush: css; title: ; notranslate">.classOfAncestor [...] .classOfParent .classOfElement {...}</pre>
<p>So, we take look at the web inspector and find the classes:</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-05-CheckInheritance.jpg"><img class="alignnone  wp-image-5078" alt="blogta2-05-CheckInheritance" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-05-CheckInheritance.jpg" width="498" height="438" /></a></p>
<p>It is good to be specific in the path and get the right ancestors. In our example, we want to apply the style only in the context of a task, so we walk the path up to &#8220;single_tasks_block&#8221;. The CSS we must define is:</p>
<pre class="brush: css; title: ; notranslate">.single_tasks_block .dataInput .fieldColumn .doItRed {
    color:red;
}</pre>
<p>If you want to apply your red style in a wider area than a table, don&#8217;t hesitate to use the framework CSS classes: <span style="font-family: 'Courier New';">.nxHeader</span>, <span style="font-family: 'Courier New';">.nxMainContainer</span>, <span style="font-family: 'Courier New';">.nxFooter</span>. These classes are called in all the pages of the platform.</p>
<p>There is one last thing to do actually: Our <span style="font-family: 'Courier New';">doItRed</span> class becomes a place holder that we use for the &#8220;Style class&#8221; widget property. We can leave it empty. So, here is our CSS definition in the branding:</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blobta2-00-StyleInBranding.jpg"><img class="alignnone  wp-image-5087" alt="blobta2-00-StyleInBranding" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blobta2-00-StyleInBranding.jpg" width="489" height="293" /></a></p>
<p>Save. Update<sup>(2)</sup>. Run. Enjoy.</p>
<p>We can now move a step further. Because at the beginning of the article, I was talking about displaying the field in red, but also as upper case. We want every character, every key hit by the user to be displayed as upper case.</p>
<p>How you implement this in Nuxeo Studio depends on what you need. Either you just want to display data entry in upper case, or you want the field itself, its content, to hold only upper case letters. Just displaying uppercase is quite easy &#8212; we add the appropriate text-transform style:</p>
<pre class="brush: css; title: ; notranslate">.single_tasks_block .dataInput .fieldColumn .doItRed {
    color:red;
    text-transform:uppercase;
}</pre>
<p>That&#8217;s all. CSS handles the conversion at runtime. I&#8217;ll add that we should then rename our class, because giving useful names is useful<sup>(3)</sup>. If we do rename the class, we will not forget to change the &#8220;Style class&#8221; widget property in our task form.</p>
<p>Also notice that it is only about how the data is displayed: CSS does not change the actual content. In our example if the user enters &#8220;john&#8221; in the <span style="font-family: 'Courier New';">name</span> workflow variable, it will be displayed as &#8220;JOHN&#8221;, but the variable itself still holds &#8220;john&#8221;.</p>
<p>If we want the transformation to be persistent, then we have to move to something different. Here is my suggestion: To stay with the CSS transformation for the display, and to add a &#8220;Transform to Uppercase&#8221; automation chain to the workflow. In our example, we added a basic &#8220;OK&#8221; button to the &#8220;First task&#8221; form. This button does nothing but move to &#8220;Second task&#8221;. We can now add an automation chain to the transition, which will be run when the user hits this &#8220;Ok&#8221; button. In the &#8220;Transitions&#8221; tab of the node properties dialog, click the &#8220;Create&#8221; button:</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-06-createChain.jpg"><img class="alignnone  wp-image-5079" alt="blogta2-06-createChain" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-06-createChain.jpg" width="574" height="176" /></a></p>
<p>We name the chain &#8220;upperCaseName&#8221;. Our challenge is to set the <span style="font-family: 'Courier New';">name</span> field to uppercase. It is a workflow variable. So we use the &#8220;Workflow context &gt; Set context variable&#8221; operation chain. We can safely remove the first, default action (&#8220;Fetch &gt; Context document&#8221;) that was created by Studio because we are not &#8211; in this example &#8211; using the document.</p>
<p>Here is what our chain looks like:</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-07-EmptyChain.jpg"><img class="alignnone  wp-image-5080" alt="blogta2-07-EmptyChain" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-07-EmptyChain.jpg" width="626" height="311" /></a></p>
<p>The &#8220;name&#8221; field is obvious: our workflow variable&#8217;s name is &#8220;name&#8221; (funny, isn&#8217;t it?).</p>
<p>Now what should we put in the &#8220;value&#8221; field of this chain to transform the characters to uppercase? You can use here a MVEL expression. Not everything is allowed in this context, but to make a long story short, basically, everything that is put between <span style="font-family: 'Courier New';">@{</span> and <span style="font-family: 'Courier New';">}</span> is evaluated as a Java expression. Some expressions are very specific to Nuxeo, but you can also use a bit of Java here. And even more interesting for us, we can use its String class. And guess what? The String class has a toUpperCase() method whose function is quite clear in my opinion. This means that when you have a string, you just have to write…</p>
<pre class="brush: java; title: ; notranslate">myString.toUpperCase()</pre>
<p>…to return a capitalized string. And a hard coded expression is also valid. I mean something like…</p>
<pre class="brush: java; title: ; notranslate">&quot;nuxeo studio rocks&quot;.toUpperCase()</pre>
<p>…returns &#8220;NUXEO STUDIO ROCKS&#8221;.</p>
<p>So basically, what we have to do now is to get the current value of the <span style="font-family: 'Courier New';">name</span> workflow variable, and to transform it to upper case. Getting its value is done using the helper drop down menus of the expression dialog. We select &#8220;Workflow Variables&#8221; in first drop-down, then ["var"] in the second, and then we click &#8220;Insert&#8221;…</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-11-Expression.jpg"><img class="alignnone size-full wp-image-5082" alt="blogta2-11-Expression" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-11-Expression.jpg" width="420" height="40" /></a></p>
<p>…this inserts the expression:</p>
<pre class="brush: java; title: ; notranslate">@{WorkflowVariables[&quot;var&quot;]}</pre>
<p>We replace &#8220;var&#8221; with &#8220;name&#8221;, since it is the name of our variable. The expression between the curly brackets is evaluated at runtime and returns a string: The characters the user entered. Because it is a string, we can call the toUpperCase() Java String method. We just need to be careful and to let the whole thing inside the curly brackets:</p>
<pre class="brush: java; title: ; notranslate">@{WorkflowVariables[&quot;name&quot;].toUpperCase()}</pre>
<p>We&#8217;re done with our automation chain, which contains actually one single operation:</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-12-ExpressionFinal.jpg"><img class="alignnone size-full wp-image-5083" alt="blogta2-12-ExpressionFinal" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-12-ExpressionFinal.jpg" width="472" height="119" /></a></p>
<p>We save, update and run. After starting a workflow on a document, here is my first task. I entered everything in lower case, &#8220;working well&#8221;. The CSS transformation displays it as we want:</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-13-WorkWellTask1.jpg"><img class="alignnone size-full wp-image-5084" alt="blogta2-13-WorkWellTask1" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-13-WorkWellTask1.jpg" width="464" height="217" /></a></p>
<p>Then, when I hit the &#8220;OK&#8221; button, the chain is run and &#8220;working well&#8221; is transformed using Java. So its value is now &#8220;WORKING WELL&#8221;:</p>
<p><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-14-WorkWellTask2.jpg"><img class="alignnone size-full wp-image-5085" alt="blogta2-14-WorkWellTask2" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/blogta2-14-WorkWellTask2.jpg" width="464" height="206" /></a></p>
<p>Isn&#8217;t it wonderful that you can customize your application in such detail with Nuxeo Studio?</p>
<p>And don&#8217;t forget to add comments in your CSS to make it readable and easy to follow for you team<sup>(5)<sup>.</sup></sup></p>
<p>(1) Yes. Checking that what we do works as expected is part of our lives<br />
(2) Update the project in the server. Something we sometimes forget. Then we wonder why our changes don&#8217;t work. Come on.<br />
(3) Yes, that&#8217;s what I wrote: Useful is useful.<br />
(4) If you are not very familiar with Java, it is not a problem at all. Finding the appropriate function requires a simple Internet search on keywords like &#8220;java upper case&#8221;. You&#8217;ll get an answer such as &#8220;use the toUpperCase method of the String class&#8221;<br />
(5) Lise Kemen helped me a lot on this article. She is our CSS super specialist (see the <a href="http://showcase.nuxeo.com/nuxeo/styleGuide/" target="_blank">Nuxeo UI Style Guide</a> for example). She also she really insisted for this &#8220;comment your class&#8221; to be added. And when Lise <em>really</em> insists, well, you know, you just follow the orders.</p>
<div class="zemanta-pixie" style="margin-top: 10px; height: 15px;"><a class="zemanta-pixie-a" href="http://www.zemanta.com/?px" title="Enhanced by Zemanta"><img class="zemanta-pixie-img" style="border: none; float: right;" alt="Enhanced by Zemanta" src="http://img.zemanta.com/zemified_e.png?x-id=6a151a4a-2f2b-4fae-8df7-197d76d87da5" /></a></div>
<p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/red-uppercase-nuxeo-studio/">Make It Red and Uppercase with Nuxeo Studio</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/development/2013/05/red-uppercase-nuxeo-studio/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Nuxeo World 2013 &#8211; Save the Date!</title>
		<link>http://www.nuxeo.com/blog/updates/2013/05/nuxeo-world-save-the-date/</link>
		<comments>http://www.nuxeo.com/blog/updates/2013/05/nuxeo-world-save-the-date/#comments</comments>
		<pubDate>Fri, 03 May 2013 16:59:13 +0000</pubDate>
		<dc:creator>julie</dc:creator>
				<category><![CDATA[Nuxeo Updates]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5113</guid>
		<description><![CDATA[<p><script charset="ISO-8859-1" src="http://fast.wistia.com/static/concat/E-v1.js"></script>
<p>First of all, we&#8217;re very excited to announce that Nuxeo World is coming back in October 2013. You can already save the date: <strong>October 9-10, 2013</strong>. We&#8217;re putting together 2 action-packed days for all things Nuxeo. Since last year&#8217;s format was well received, we&#8217;re going to do it again. The first day will feature the latest technology from Nuxeo R&#38;D, case studies, vertical application demonstrations, community contributions, and much more. The second day will be a full day of hands-on workshops.</p>
<p>For a glimpse of last year, check this out!</p>
<div id="wistia_v8agcsuk7y" class="wistia_embed" style="width:650px;height:394px;" data-video-width="650" data-video-height="366"><div itemprop="video" itemscope itemtype="http://schema.org/VideoObject"><meta itemprop="duration" content="PT1M44S" /><meta itemprop="thumbnailUrl" content="https://wistia.sslcs.cdngc.net/deliveries/343fc41b31ef892ddb67e442b0966a16c7f3a411.bin" /><meta itemprop="contentURL" content="https://wistia.sslcs.cdngc.net/deliveries/19e7f29f72a55158ae293e615b862a90aa1997b9.bin" /><meta itemprop="embedURL" content="https://wistia.sslcs.cdngc.net/flash/embed_player_v2.0.swf?2013-01-16&#038;controlsVisibleOnLoad=true&#038;customColor=3670b9&#038;mediaDuration=104.0&#038;showVolume=true&#038;stillUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F343fc41b31ef892ddb67e442b0966a16c7f3a411.jpg%3Fimage_crop_resized%3D650x366&#038;unbufferedSeek=true&#038;videoUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F19e7f29f72a55158ae293e615b862a90aa1997b9.bin" /><meta itemprop="uploadDate" content="2013-05-01T21:58:21Z" /><object id="wistia_v8agcsuk7y_seo" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" style="display:block;height:394px;position:relative;width:650px;"><param name="movie" value="https://wistia.sslcs.cdngc.net/flash/embed_player_v2.0.swf?2013-01-16"/><param name="allowfullscreen" value="true"/><param name="allowscriptaccess" value="always"/><param name="bgcolor" value="#000000"/><param name="wmode" value="opaque"/><param name="flashvars" value="controlsVisibleOnLoad=true&#038;customColor=3670b9&#038;mediaDuration=104.0&#038;showVolume=true&#038;stillUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F343fc41b31ef892ddb67e442b0966a16c7f3a411.jpg%3Fimage_crop_resized%3D650x366&#038;unbufferedSeek=true&#038;videoUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F19e7f29f72a55158ae293e615b862a90aa1997b9.bin"/><embed src="https://wistia.sslcs.cdngc.net/flash/embed_player_v2.0.swf?2013-01-16" allowfullscreen="true" allowscriptaccess="always" bgcolor=#000000 flashvars="controlsVisibleOnLoad=true&#038;customColor=3670b9&#038;mediaDuration=104.0&#038;showVolume=true&#038;stillUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F343fc41b31ef892ddb67e442b0966a16c7f3a411.jpg%3Fimage_crop_resized%3D650x366&#038;unbufferedSeek=true&#038;videoUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F19e7f29f72a55158ae293e615b862a90aa1997b9.bin" name="wistia_v8agcsuk7y_html" style="display:block;height:100%;position:relative;width:100%;" type="application/x-shockwave-flash" wmode="opaque"/></object><noscript itemprop="description">NUXEO_2013_TEASER</noscript></div></div>
<script charset="ISO-8859-1" src="https://fast.wistia.com/static/concat/E-v1%2Csocialbar-v1.js"></script>
<script>
wistiaEmbed = Wistia.embed("v8agcsuk7y", {
  version: "v1",
  videoWidth: 650,
  videoHeight: 366,
  volumeControl: true,
  controlsVisibleOnLoad: true,
  playerColor: "3670b9",
  plugin: {
    "socialbar-v1": {
      buttons: "embed-email-twitter-linkedIn-googlePlus-facebook",
      logo: true,
      badgeUrl: "http://nuxeo.com",
      badgeImage: "https://wistia.sslcs.cdngc.net/deliveries/69dd187a8507510b932208750621ac3008837e2e.jpg?image_crop_resized=100x20"
    }
  }
});
</script>
<script charset="ISO-8859-1" src="https://fast.wistia.com/embed/medias/v8agcsuk7y/metadata.js"></script>
<p><br /></p>
<p>Stay tuned also for other upcoming events. We&#8217;re preparing something special for our Connect clients in France and in North America.We&#8217;d like to share a cup of coffee &#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/updates/2013/05/nuxeo-world-save-the-date/">Nuxeo World 2013 &#8211; Save the Date!</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<script charset="ISO-8859-1" src="http://fast.wistia.com/static/concat/E-v1.js"></script>
<p>First of all, we&#8217;re very excited to announce that Nuxeo World is coming back in October 2013. You can already save the date: <strong>October 9-10, 2013</strong>. We&#8217;re putting together 2 action-packed days for all things Nuxeo. Since last year&#8217;s format was well received, we&#8217;re going to do it again. The first day will feature the latest technology from Nuxeo R&amp;D, case studies, vertical application demonstrations, community contributions, and much more. The second day will be a full day of hands-on workshops.</p>
<p>For a glimpse of last year, check this out!</p>
<div id="wistia_v8agcsuk7y" class="wistia_embed" style="width:650px;height:394px;" data-video-width="650" data-video-height="366"><div itemprop="video" itemscope itemtype="http://schema.org/VideoObject"><meta itemprop="duration" content="PT1M44S" /><meta itemprop="thumbnailUrl" content="https://wistia.sslcs.cdngc.net/deliveries/343fc41b31ef892ddb67e442b0966a16c7f3a411.bin" /><meta itemprop="contentURL" content="https://wistia.sslcs.cdngc.net/deliveries/19e7f29f72a55158ae293e615b862a90aa1997b9.bin" /><meta itemprop="embedURL" content="https://wistia.sslcs.cdngc.net/flash/embed_player_v2.0.swf?2013-01-16&controlsVisibleOnLoad=true&customColor=3670b9&mediaDuration=104.0&showVolume=true&stillUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F343fc41b31ef892ddb67e442b0966a16c7f3a411.jpg%3Fimage_crop_resized%3D650x366&unbufferedSeek=true&videoUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F19e7f29f72a55158ae293e615b862a90aa1997b9.bin" /><meta itemprop="uploadDate" content="2013-05-01T21:58:21Z" /><object id="wistia_v8agcsuk7y_seo" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" style="display:block;height:394px;position:relative;width:650px;"><param name="movie" value="https://wistia.sslcs.cdngc.net/flash/embed_player_v2.0.swf?2013-01-16"></param><param name="allowfullscreen" value="true"></param><param name="allowscriptaccess" value="always"></param><param name="bgcolor" value="#000000"></param><param name="wmode" value="opaque"></param><param name="flashvars" value="controlsVisibleOnLoad=true&customColor=3670b9&mediaDuration=104.0&showVolume=true&stillUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F343fc41b31ef892ddb67e442b0966a16c7f3a411.jpg%3Fimage_crop_resized%3D650x366&unbufferedSeek=true&videoUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F19e7f29f72a55158ae293e615b862a90aa1997b9.bin"></param><embed src="https://wistia.sslcs.cdngc.net/flash/embed_player_v2.0.swf?2013-01-16" allowfullscreen="true" allowscriptaccess="always" bgcolor=#000000 flashvars="controlsVisibleOnLoad=true&customColor=3670b9&mediaDuration=104.0&showVolume=true&stillUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F343fc41b31ef892ddb67e442b0966a16c7f3a411.jpg%3Fimage_crop_resized%3D650x366&unbufferedSeek=true&videoUrl=https%3A%2F%2Fwistia.sslcs.cdngc.net%2Fdeliveries%2F19e7f29f72a55158ae293e615b862a90aa1997b9.bin" name="wistia_v8agcsuk7y_html" style="display:block;height:100%;position:relative;width:100%;" type="application/x-shockwave-flash" wmode="opaque"></embed></object><noscript itemprop="description">NUXEO_2013_TEASER</noscript></div></div>
<script charset="ISO-8859-1" src="https://fast.wistia.com/static/concat/E-v1%2Csocialbar-v1.js"></script>
<script>
wistiaEmbed = Wistia.embed("v8agcsuk7y", {
  version: "v1",
  videoWidth: 650,
  videoHeight: 366,
  volumeControl: true,
  controlsVisibleOnLoad: true,
  playerColor: "3670b9",
  plugin: {
    "socialbar-v1": {
      buttons: "embed-email-twitter-linkedIn-googlePlus-facebook",
      logo: true,
      badgeUrl: "http://nuxeo.com",
      badgeImage: "https://wistia.sslcs.cdngc.net/deliveries/69dd187a8507510b932208750621ac3008837e2e.jpg?image_crop_resized=100x20"
    }
  }
});
</script>
<script charset="ISO-8859-1" src="https://fast.wistia.com/embed/medias/v8agcsuk7y/metadata.js"></script>
<p><br/></p>
<p>Stay tuned also for other upcoming events. We&#8217;re preparing something special for our Connect clients in France and in North America.We&#8217;d like to share a cup of coffee at a <strong>special breakfast event in Paris</strong>. The Nuxeo team will show you the latest innovations from our R&amp;D department. This will take place in our Paris office on Tuesday, June 11, so watch your email inbox for the invitation.</p>
<p>For our North American clients, who are spread out all over the U.S., Canada, and Latin America, it would be challenging to organize a meet-and-greet over coffee and croissants. But we’d still like to give you the opportunity to meet other clients and to connect with the Nuxeo team, so we’re organizing a <strong>Google Hangout for US customers</strong>. The first Hangout will be held on June 5th, so check your email for the invitation!</p>
<p>The post <a href="http://www.nuxeo.com/blog/updates/2013/05/nuxeo-world-save-the-date/">Nuxeo World 2013 &#8211; Save the Date!</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/updates/2013/05/nuxeo-world-save-the-date/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Q&amp;A Friday] How to add tagging capability during drag&#8217;n&#039;drop</title>
		<link>http://www.nuxeo.com/blog/development/2013/05/qa-friday-add-tagging-drag-drop/</link>
		<comments>http://www.nuxeo.com/blog/development/2013/05/qa-friday-add-tagging-drag-drop/#comments</comments>
		<pubDate>Fri, 03 May 2013 13:41:35 +0000</pubDate>
		<dc:creator>Laurent Doguin</dc:creator>
				<category><![CDATA[Product & Development]]></category>
		<category><![CDATA[document creation]]></category>
		<category><![CDATA[Friday Q&A]]></category>
		<category><![CDATA[intermediate]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5132</guid>
		<description><![CDATA[<p><p>Here&#8217;s a question that comes back often, asked by <a href="http://concena.com/">bruce</a>: <a href="http://answers.nuxeo.com/questions/5271/adding-tag-capability-during-drag-and-drop">How to add tag capability during drag&#8217;n'drop?</a>. Since Thierry added HTML5 drag&#8217;n'drop to the Nuxeo Platform, it&#8217;s possible to fill in metadata right after the import, and apply them to all the imported documents. But I understand it can be frustrating not being able to add tags. And fortunately the <a href="http://doc.nuxeo.com/x/BAJu" title=" Drag and Drop Service for Content Capture (HTML5-based)">drag&#8217;n'drop service for content capture</a> is extensible. The full code sample is available on <a href="https://github.com/ldoguin/nuxeo-dragndrop-sample">GitHub</a>.</p>
<p>So here&#8217;s how it works. When dragging files in a content view for at least 2 seconds, you&#8217;ll be prompted with a <em>Select import operation</em> choice. Those select operations are actually actions. It means you can add as any as you want through the action extension point. These actions have a specific behavior.</p>
<p>The ID of the action must, for instance, be the ID of the operation or the operation chain that &#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/qa-friday-add-tagging-drag-drop/">[Q&amp;A Friday] How to add tagging capability during drag&#8217;n'drop</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<div id="attachment_4678" class="wp-caption alignright" style="width: 160px"><a href="http://www.nuxeo.com/blog/wp-content/uploads/2012/02/question1.png"><img class="size-thumbnail wp-image-4678" alt="How to add tagging capability during Drag and Drop" src="http://www.nuxeo.com/blog/wp-content/uploads/2012/02/question1-150x123.png" width="150" height="123" /></a><p class="wp-caption-text">How to add tagging capability during drag&#8217;n'drop</p></div>
<p>Here&#8217;s a question that comes back often, asked by <a href="http://concena.com/">bruce</a>: <a href="http://answers.nuxeo.com/questions/5271/adding-tag-capability-during-drag-and-drop">How to add tag capability during drag&#8217;n'drop?</a>. Since Thierry added HTML5 drag&#8217;n'drop to the Nuxeo Platform, it&#8217;s possible to fill in metadata right after the import, and apply them to all the imported documents. But I understand it can be frustrating not being able to add tags. And fortunately the <a href="http://doc.nuxeo.com/x/BAJu" title=" Drag and Drop Service for Content Capture (HTML5-based)">drag&#8217;n'drop service for content capture</a> is extensible. The full code sample is available on <a href="https://github.com/ldoguin/nuxeo-dragndrop-sample">GitHub</a>.</p>
<p>So here&#8217;s how it works. When dragging files in a content view for at least 2 seconds, you&#8217;ll be prompted with a <em>Select import operation</em> choice. Those select operations are actually actions. It means you can add as any as you want through the action extension point. These actions have a specific behavior.</p>
<p>The ID of the action must, for instance, be the ID of the operation or the operation chain that will be executed when dropping the files. The link will display an iFrame itself displaying the layout passed as query parameter (<em>layout=dndTagsEdit</em>). The schema query parameter (<em>schema=dc</em>) is used to specify which schema you need to update with the chosen layout. Take a look at the documentation for the other specificities of those actions.</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;component name=&quot;org.nuxeo.sample.dnd.actions.contrib&quot;&gt;

  &lt;require&gt;org.nuxeo.ecm.platform.actions&lt;/require&gt;

  &lt;extension target=&quot;org.nuxeo.ecm.platform.actions.ActionService&quot;
    point=&quot;actions&quot;&gt;

    &lt;action id=&quot;Chain.FileManager.ImportWithMetaDataAndTagsInSeam&quot;
      link=&quot;${org.nuxeo.ecm.contextPath}/dndFormCollector.faces?schema=dc&amp;#038;layout=dndTagsEdit&quot;
      order=&quot;30&quot; label=&quot;label.smart.import.with.mdTags&quot;
      help=&quot;desc.smart.import.with.md.mdTags&quot;&gt;
      &lt;category&gt;ContentView&lt;/category&gt;
      &lt;filter-id&gt;create&lt;/filter-id&gt;
    &lt;/action&gt;

  &lt;/extension&gt;

&lt;/component&gt;
</pre>
<p>There are two things different from the classic import with metadata: the layout and the operation chain. The operation chain as an intermediate operation called <em>Document.TagDocument</em>. It&#8217;s responsible for the actual tagging of every document.</p>
<pre class="brush: java; title: ; notranslate">
/*
 * (C) Copyright 2013 Nuxeo SA (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     ldoguin
 */

package org.nuxeo.sample.dnd;

import java.io.Serializable;
import java.util.List;

import org.jboss.seam.contexts.Contexts;
import org.nuxeo.ecm.automation.OperationContext;
import org.nuxeo.ecm.automation.core.Constants;
import org.nuxeo.ecm.automation.core.annotations.Context;
import org.nuxeo.ecm.automation.core.annotations.Operation;
import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
import org.nuxeo.ecm.automation.core.collectors.DocumentModelCollector;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.platform.tag.TagService;

/**
 * @author ldoguin
 */
@Operation(id = TagDocument.ID, category = Constants.CAT_DOCUMENT, requires = Constants.SEAM_CONTEXT, label = &quot;TagDocument&quot;, description = &quot;&quot;)
public class TagDocument {

    public static final String ID = &quot;Document.TagDocument&quot;;

    @Context
    protected CoreSession session;

    @Context
    protected OperationContext ctx;

    @Context
    TagService tagService;

    @OperationMethod(collector = DocumentModelCollector.class)
    public DocumentModel run(DocumentModel doc) throws Exception {
        if (!isSeamContextAvailable()) {
            return doc;
        }
        List tags = getDndTagsFormAction().getDndTags();
        for (Serializable tag : tags) {
            tagService.tag(session, doc.getId(), (String) tag,
                    ctx.getPrincipal().getName());
        }
        return doc;
    }

    public static boolean isSeamContextAvailable() {
        return Contexts.isSessionContextActive();
    }

    public static DndTagsFormActionBean getDndTagsFormAction() {
        return (DndTagsFormActionBean) Contexts.getConversationContext().get(
                &quot;dndTagsFormActions&quot;);
    }

}
</pre>
<p>As you can see, it retrieves the list of tags from a <a href="https://github.com/ldoguin/nuxeo-dragndrop-sample/blob/master/src/main/java/org/nuxeo/sample/dnd/DndTagsFormActionBean.java">SEAM bean</a>. It&#8217;s the backing bean of a new widget I have defined in my custom layout <em>dndTagsEdit</em>. This layout is almost the same as the original one, except it displays the <a href="https://github.com/ldoguin/nuxeo-dragndrop-sample/blob/master/src/main/resources/web/nuxeo.war/widgets/dnd_tags_template.xhtml">tag widget</a>. Take a look at the code and the documentation &#8211; it&#8217;s pretty easy to understand.</p>
<p>The post <a href="http://www.nuxeo.com/blog/development/2013/05/qa-friday-add-tagging-drag-drop/">[Q&amp;A Friday] How to add tagging capability during drag&#8217;n'drop</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/development/2013/05/qa-friday-add-tagging-drag-drop/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Monday Dev Heaven] Nuxeo and Atlassian HipChat Integration</title>
		<link>http://www.nuxeo.com/blog/development/2013/04/nuxeo-atlassian-hipchat-integration/</link>
		<comments>http://www.nuxeo.com/blog/development/2013/04/nuxeo-atlassian-hipchat-integration/#comments</comments>
		<pubDate>Mon, 29 Apr 2013 11:00:02 +0000</pubDate>
		<dc:creator>Laurent Doguin</dc:creator>
				<category><![CDATA[Product & Development]]></category>
		<category><![CDATA[beginner]]></category>
		<category><![CDATA[Monday Dev Heaven]]></category>
		<category><![CDATA[notification]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5119</guid>
		<description><![CDATA[<p><p>Being big <a href="http://www.atlassian.com/" title="Software Development and Collaboration Tools &#124; Atlassian">Atlassian</a> fans here at Nuxeo, we recently started using <a href="https://www.hipchat.com/" title="Private group chat and IM, business and team collaboration - HipChat">HipChat</a>. It&#8217;s an enterprise chat room. One of the cool things about HipChat is its very simple web API. It makes it really easy to send notifications to a chat room.</p>
<p>To show you how dead easy it is, I did a project showing how to send Nuxeo events to a Hipchat room using their web API. I did it using Nuxeo IDE to generate my plugin structure, using the <em>Nuxeo Plugin Project</em> and <em>Nuxeo Listener</em> wizards. My listener only listens to <em>documentCreated</em> and <em>documentModified</em> events. Each time they occur, we send a small message to a HipChat room, containing the URL of the document, its title, date and creator. The code is really simple (especially because most of it is generated by Nuxeo IDE):</p>
<p>What it does is simply relay the modify or create events to a room. &#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/development/2013/04/nuxeo-atlassian-hipchat-integration/">[Monday Dev Heaven] Nuxeo and Atlassian HipChat Integration</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<div id="attachment_5120" class="wp-caption alignright" style="width: 160px"><a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/hipchatLodo.jpg"><img class="size-thumbnail wp-image-5120" alt="HipChat" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/hipchatLodo-150x150.jpg" width="150" height="150" /></a><p class="wp-caption-text">Atlassian HipChat</p></div>
<p>Being big <a href="http://www.atlassian.com/" title="Software Development and Collaboration Tools | Atlassian">Atlassian</a> fans here at Nuxeo, we recently started using <a href="https://www.hipchat.com/" title="Private group chat and IM, business and team collaboration - HipChat">HipChat</a>. It&#8217;s an enterprise chat room. One of the cool things about HipChat is its very simple web API. It makes it really easy to send notifications to a chat room.</p>
<p>To show you how dead easy it is, I did a project showing how to send Nuxeo events to a Hipchat room using their web API. I did it using Nuxeo IDE to generate my plugin structure, using the <em>Nuxeo Plugin Project</em> and <em>Nuxeo Listener</em> wizards. My listener only listens to <em>documentCreated</em> and <em>documentModified</em> events. Each time they occur, we send a small message to a HipChat room, containing the URL of the document, its title, date and creator. The code is really simple (especially because most of it is generated by Nuxeo IDE):</p>
<pre class="brush: java; title: ; notranslate">
/*
 * (C) Copyright ${year} Nuxeo SA (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     ldoguin
 */

package org.nuxeo.sample;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.event.Event;
import org.nuxeo.ecm.core.event.EventContext;
import org.nuxeo.ecm.core.event.EventListener;
import org.nuxeo.ecm.core.event.impl.DocumentEventContext;
import org.nuxeo.ecm.platform.url.DocumentViewImpl;
import org.nuxeo.ecm.platform.url.api.DocumentView;
import org.nuxeo.ecm.platform.url.api.DocumentViewCodecManager;
import org.nuxeo.runtime.api.Framework;

/**
 * @author ldoguin
 */
public class HipChatListener implements EventListener {

    public static Log log = LogFactory.getLog(HipChatListener.class);

    public static final String MESSAGE_TEMPLATE = &quot;Document &lt;a href=\&quot;%s\&quot;&gt;%s&lt;/a&gt; has been modified by %s on the %s&quot;;

    public static final String BASE_URL = &quot;https://api.hipchat.com/v1/rooms/message?format=json&amp;auth_token=%s&quot;;

    public void handleEvent(Event event) throws ClientException {
        // First we make sure that the event is about a document
        EventContext ctx = event.getContext();
        if (!(ctx instanceof DocumentEventContext)) {
            return;
        }
        DocumentEventContext docCtx = (DocumentEventContext) ctx;
        DocumentModel doc = docCtx.getSourceDocument();
        // Once we have the document, we can start building the message we'll send to HipChat

        // First we build the document's URL
        DocumentView docView = new DocumentViewImpl(doc);
        DocumentViewCodecManager docLocator = Framework.getLocalService(DocumentViewCodecManager.class);
        String nuxeoUrl = Framework.getProperty(&quot;nuxeo.url&quot;);
        String docUrl = docLocator.getUrlFromDocumentView(docView, true,
                nuxeoUrl + &quot;/&quot;);
        // Than the message body
        Date date = new Date(event.getTime());
        String chatMessage = String.format(MESSAGE_TEMPLATE, docUrl,
                doc.getTitle(), docCtx.getPrincipal().getName(),
                date.toString());
        log.debug(chatMessage);
        try {
            // now we can send it to HipChat
            sendMessage(chatMessage);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void sendMessage(String message) throws IOException {
        // We retrieve every needed property from nuxeo.conf
        String roomId = Framework.getProperty(&quot;hipchat.roomId&quot;);
        String authToken = Framework.getProperty(&quot;hipchat.authToken&quot;);
        String userName = Framework.getProperty(&quot;hipchat.userName&quot;);
        String notify = Framework.getProperty(&quot;hipchat.notify&quot;);
        String color = Framework.getProperty(&quot;hipchat.color&quot;);

        // then we do the actual rest call to HipChat web API
        String url = String.format(BASE_URL, authToken);
        HttpClient client = new HttpClient();
        PostMethod post = new PostMethod(url);
        try {
            post.addParameter(&quot;from&quot;, userName);
            post.addParameter(&quot;room_id&quot;, roomId);
            post.addParameter(&quot;message&quot;, message);
            post.addParameter(&quot;color&quot;, color);
            post.addParameter(&quot;notify&quot;, notify);
            post.getParams().setContentCharset(&quot;UTF-8&quot;);
            client.executeMethod(post);
        } finally {
            post.releaseConnection();
        }
    }
}

</pre>
<p>What it does is simply relay the modify or create events to a room. This is the simplest integration possible, but there are many other cool use cases you can implement. You could have a dedicated room per Nuxeo group, you could integrate this with the notification module and send personal notifications, etc. Let us know what you think about this &#8212; are you using the same kind of tools to collaborate? Would like to see integrations between other collaboration tools and Nuxeo?</p>
<p>The post <a href="http://www.nuxeo.com/blog/development/2013/04/nuxeo-atlassian-hipchat-integration/">[Monday Dev Heaven] Nuxeo and Atlassian HipChat Integration</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/development/2013/04/nuxeo-atlassian-hipchat-integration/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Q&amp;A Friday] How to Access a Resource Bundle with MVEL in Content Automation</title>
		<link>http://www.nuxeo.com/blog/development/2013/04/qa-friday-content-automation-labels/</link>
		<comments>http://www.nuxeo.com/blog/development/2013/04/qa-friday-content-automation-labels/#comments</comments>
		<pubDate>Fri, 26 Apr 2013 16:08:15 +0000</pubDate>
		<dc:creator>Laurent Doguin</dc:creator>
				<category><![CDATA[Product & Development]]></category>
		<category><![CDATA[beginner]]></category>
		<category><![CDATA[content automation]]></category>
		<category><![CDATA[Friday Q&A]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5121</guid>
		<description><![CDATA[<p><p>There&#8217;s an interesting question today from <a href="http://answers.nuxeo.com/users/912/milonette/">milonette</a>, asking <a href="http://answers.nuxeo.com/questions/5391/automation-chain-mvel-get-label-in-properties">how to access labels with MVEL</a>. Sometimes the Content Automation API doesn&#8217;t provide everything you need out of the box, hence the question. But the good news is this API is extensible.</p>
<p>While some functions are already available in the automation context, there is currently nothing to access the resource bundles. We&#8217;ll need to add our own function into it. This is quite easy to do. First we need to write the new function we need, then we&#8217;ll write an operation that adds this function to the content automation context.</p>
<p>Our localized function needs different parameters. We want the locale, the name of the resource bundle, the key of our message and some optional parameters. Here&#8217;s a simple implementation:</p>
<p>Now that I have my function, I need to create an operation that will make this available within the automation context:&#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/development/2013/04/qa-friday-content-automation-labels/">[Q&#038;A Friday] How to Access a Resource Bundle with MVEL in Content Automation</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<div id="attachment_4678" class="wp-caption alignright" style="width: 160px"><a href="http://www.nuxeo.com/blog/wp-content/uploads/2012/02/question1.png"><img class="size-thumbnail wp-image-4678" alt="Access resource bundle label with MVEL" src="http://www.nuxeo.com/blog/wp-content/uploads/2012/02/question1-150x123.png" width="150" height="123" /></a><p class="wp-caption-text">Access resource bundle label with MVEL</p></div>
<p>There&#8217;s an interesting question today from <a href="http://answers.nuxeo.com/users/912/milonette/">milonette</a>, asking <a href="http://answers.nuxeo.com/questions/5391/automation-chain-mvel-get-label-in-properties">how to access labels with MVEL</a>. Sometimes the Content Automation API doesn&#8217;t provide everything you need out of the box, hence the question. But the good news is this API is extensible.</p>
<p>While some functions are already available in the automation context, there is currently nothing to access the resource bundles. We&#8217;ll need to add our own function into it. This is quite easy to do. First we need to write the new function we need, then we&#8217;ll write an operation that adds this function to the content automation context.</p>
<p>Our localized function needs different parameters. We want the locale, the name of the resource bundle, the key of our message and some optional parameters. Here&#8217;s a simple implementation:</p>
<pre class="brush: java; title: ; notranslate">
package org.nuxeo.sample;

import java.util.Locale;

import org.nuxeo.common.utils.i18n.I18NUtils;

public class LocalizationFunctions {

    public static String localize(String bundleName, String localeStr, String key, String... params) {
        Locale locale = Locale.forLanguageTag(localeStr);
        return I18NUtils.getMessageString(bundleName, key, params, locale);
    }

}
</pre>
<p>Now that I have my function, I need to create an operation that will make this available within the automation context:</p>
<pre class="brush: java; title: ; notranslate">
package org.nuxeo.sample;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.automation.OperationContext;
import org.nuxeo.ecm.automation.core.Constants;
import org.nuxeo.ecm.automation.core.annotations.Context;
import org.nuxeo.ecm.automation.core.annotations.Operation;
import org.nuxeo.ecm.automation.core.annotations.OperationMethod;

@Operation(id = AddLocalizationFunctions.ID, category = Constants.CAT_SCRIPTING, label = &quot;AddLocalizationFunctions&quot;, description = &quot;Add new function namespace to automation context.&quot;)
public class AddLocalizationFunctions {

    public static final String ID = &quot;AddLocalizationFunctions&quot;;

    public static final String LOCALIZATION_FUNCTION_NAME = &quot;LocFn&quot;;

    protected static final Log log = LogFactory.getLog(AddLocalizationFunctions.class);

    @Context
    protected OperationContext ctx;

    @OperationMethod
    public void run() {
        ctx.put(LOCALIZATION_FUNCTION_NAME, new LocalizationFunctions());
    }

}
</pre>
<p>This should do the trick. Now if I do an operation chain starting with this operation, I&#8217;ll be able to use this function:</p>
<pre class="brush: xml; title: ; notranslate">
    &lt;chain id=&quot;test2&quot;&gt;
      &lt;operation id=&quot;AddLocalizationFunctions&quot;/&gt;
      &lt;operation id=&quot;Seam.AddInfoMessage&quot;&gt;
        &lt;param type=&quot;string&quot; name=&quot;message&quot;&gt;expr:LocFn.localize(&quot;messages&quot;, &quot;en&quot;, &quot;label.permalink.description&quot;,{&quot;param1&quot;, &quot;param2&quot;})&lt;/param&gt;
      &lt;/operation&gt;
    &lt;/chain&gt;
</pre>
<p>Notice the use of an inline MVEL array to comply with <em>String&#8230; params</em> argument. Now you can go wild and add any functions you want. In the future we&#8217;ll try to make this extensible through an extension point.</p>
<p>The post <a href="http://www.nuxeo.com/blog/development/2013/04/qa-friday-content-automation-labels/">[Q&#038;A Friday] How to Access a Resource Bundle with MVEL in Content Automation</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/development/2013/04/qa-friday-content-automation-labels/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[meet the team] Benjamin Jalon</title>
		<link>http://www.nuxeo.com/blog/updates/2013/04/meet-team-benjamin-jalon/</link>
		<comments>http://www.nuxeo.com/blog/updates/2013/04/meet-team-benjamin-jalon/#comments</comments>
		<pubDate>Fri, 26 Apr 2013 10:20:26 +0000</pubDate>
		<dc:creator>Jimena Ballina</dc:creator>
				<category><![CDATA[Nuxeo Updates]]></category>
		<category><![CDATA[Meet the team]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5099</guid>
		<description><![CDATA[<p><p dir="ltr" id="internal-source-marker_0.7695218276588046">Originally a Nuxeo customer at EADS, a European industrial flagship, Benjamin Jalon worked with the Nuxeo Platform and helped improve Nuxeo Document Management during their project. Benjamin, impressed with all of the Nuxeo Platform’s capabilities, joined the Nuxeo team as a developer at the end of 2008.<a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/benjamin.jpeg"><img class="alignright size-medium wp-image-5101" alt="Benjamin Jalon" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/benjamin-300x300.jpeg" width="300" height="300" /></a></p>
<p dir="ltr"><strong>Where are you originally from?</strong></p>
<p dir="ltr">I am from Paris. I was born in Paris, grew up in Paris, and I have  lived in a suburb of Paris for four years now. I went to college in Paris and studied mathematics for my undergraduate degree. For my master’s degree, I also studied mathematics. After my master’s, I completed two years at ENSTA (École Nationale Supérieure de Techniques Avancées) to earn an engineering degree, with emphasis in Computer Science.</p>
<p dir="ltr"><strong>What made you want to be an engineer after studying mathematics for so long?</strong></p>
<p dir="ltr">I wanted to be an engineer because after a master’s in mathematics, &#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/updates/2013/04/meet-team-benjamin-jalon/">[meet the team] Benjamin Jalon</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<p dir="ltr" id="internal-source-marker_0.7695218276588046">Originally a Nuxeo customer at EADS, a European industrial flagship, Benjamin Jalon worked with the Nuxeo Platform and helped improve Nuxeo Document Management during their project. Benjamin, impressed with all of the Nuxeo Platform’s capabilities, joined the Nuxeo team as a developer at the end of 2008.<a href="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/benjamin.jpeg"><img class="alignright size-medium wp-image-5101" alt="Benjamin Jalon" src="http://www.nuxeo.com/blog/wp-content/uploads/2013/04/benjamin-300x300.jpeg" width="300" height="300" /></a></p>
<p dir="ltr"><strong>Where are you originally from?</strong></p>
<p dir="ltr">I am from Paris. I was born in Paris, grew up in Paris, and I have  lived in a suburb of Paris for four years now. I went to college in Paris and studied mathematics for my undergraduate degree. For my master’s degree, I also studied mathematics. After my master’s, I completed two years at ENSTA (École Nationale Supérieure de Techniques Avancées) to earn an engineering degree, with emphasis in Computer Science.</p>
<p dir="ltr"><strong>What made you want to be an engineer after studying mathematics for so long?</strong></p>
<p dir="ltr">I wanted to be an engineer because after a master’s in mathematics, you usually work in research or in theoretical areas. I liked mathematics, but I like to build things too, so I was really interested in computer science and I chose to go to engineering school. I didn’t like school so much before college but once I went to university for math and computer science, it was easy for me since I like these subjects a lot. I found my calling.</p>
<p dir="ltr"><strong>What is your background?</strong></p>
<p dir="ltr">I worked for a company named EADS, which owns Airbus, and I worked in the military domain. The company where I worked created the information systems for the French Army to use during conflict situations. I worked to make the system easy to install since, in this case, you had to install the networks and servers along with many other things, and have it integrate with the global systems all at the same time. It was a bit complex, but a very interesting experience.</p>
<p dir="ltr"><strong>You mentioned that you used the Nuxeo Platform for your projects at EADS. Can you tell me more about your use of the platform before joining Nuxeo?</strong></p>
<p dir="ltr">At EADS, we used the Nuxeo Platform for military intelligence (Renseignements Militaire). We used the Nuxeo Platform for its digital asset management capabilities, so that military intelligence had a repository to store images and video footage of military equipment. The information came from all over the world &#8211; television, pictures taken during military operations, all types of sources. This system provided a place to store all of these digital resources, with custom metadata that enabled us to clearly define the subject. I was really interested in the Nuxeo Platform after this experience and I decided I wanted to work at Nuxeo.</p>
<p dir="ltr"><strong>How has your career at Nuxeo progressed since you joined?</strong></p>
<p dir="ltr">I began at Nuxeo as a developer and was a developer for about four years. I was on the Starship team with Delphine. We named our team “Starship” because of the movie Starship Troopers, where the characters in the movie had to kill bugs from other planets. They’d say, ”A good bug is a dead bug,” so we chose this name for our development team. During this period, we started moving to scrum methodology for more regulated development activity. We planned tasks for weeks and we tried to not talk about tasks by days, but by complexity. We estimated the length of a project based on complexity. My role then was Developer and Scrum Master, and my main responsibility was to organize and coordinate with the team to find out how complex a project was, and then translate that into days for Delphine. Each day at the beginning of the day I would ask the team what their plans were and what they had completed already, and I would map which developer worked on specific tasks. If the developer was blocked on his or her activity, then at the daily scrum (our daily meeting), the entire team would try to help the developer figure out a solution. After two weeks of development, we would have a retrospective meeting and talk about all the tasks done and how they went. I did that for about five years. Then, last year, I became a PreSales Engineer.</p>
<p dir="ltr"><strong>What do you do now as a PreSales Engineer?</strong></p>
<p dir="ltr">Now, my activities are to explain why our platform is great for customers. I present the platform, and I help potential customers figure out if the platform can do what they want, and if so, how much development it would take. I also do some consulting activities; I do an initial meeting with the customer when they do not know Nuxeo yet, and I stay with them until they are in the development process. I do training, consulting and give technology guidance to help them start a project. Basically, I help them with all of the stuff they need before beginning a project, and sometimes I even help them start the project.</p>
<p dir="ltr"><strong>What is your favorite Nuxeo project you’ve worked on so far?</strong></p>
<p dir="ltr">I started the mobile application. I was the first to contribute to this project because I did it in my free time. The first implementation was for the first Nuxeo World. I had a presentation, and my goal was to show what we can do in two weeks with Nuxeo and the IUI Java Script Library. The goal was to have a mobile application that was usable on both iOS and Android smartphones.</p>
<p dir="ltr"><strong>In the time you’ve spent at Nuxeo, how have you seen the company and products grow? And what has been most exciting for you?</strong></p>
<p dir="ltr">At the beginning, we were more in a start-up mode, where we all worked a lot. Now, we have more standard activities. It was hard at the beginning because we were both integrators and software developers &#8212; we wanted to integrate the solution for our customers. Now that we have many integrators around us, we are more focused on the software development part. Even if we do some integration activities, like we did for Netflix, Nuxeo is now more focused on just improving the platform. It’s interesting because we now really take the time to improve everything around the platform for our integrators and we try to do our best to deliver the best platform and the best tools for our platform. That’s why Nuxeo Studio and Nuxeo IDE were created. The idea of Nuxeo is to try to give our own tools to developers who use our platform. All the tools that we are using, we give them to our customers. It’s a really good example of open source, I think.</p>
<p dir="ltr"><strong>You’ve presented at Nuxeo World and other conferences before; what points do you make in these presentations and what do you want to make sure you get across to anyone interested in the platform?</strong></p>
<p dir="ltr">The platform, basically, is made for developers. It’s open source, so developers can see the Java code, and it can inspire developers. With Nuxeo, I learned to create and write better quality code. Since the platform is open source, you can see beautiful lines of Java code, and this is why I say that this is a platform that inspires developers, because they see good code. Secondly, for integrators, there are different levels of understanding the Nuxeo Platform. The first step is just using Nuxeo &#8211; installing it and using it. You don’t need to know anything technical. The second step is configuring it with Nuxeo Studio, which takes just three days of training or reading the documentation that is freely available. The third step is that by reading the documentation you can go deeply in the platform and produce what Nuxeo Studio produces to suit your needs.</p>
<p dir="ltr"><strong>What is the coolest thing you have learned while at Nuxeo?</strong></p>
<p>I think the coolest thing is writing code and working toward building something. That is not easy in all companies, especially in France. I like producing something that is trustworthy, writing code, and I also like that we share our knowledge.</p>
<p dir="ltr"><strong>What do you do in your spare time?</strong></p>
<p>I spend time with my family. I have a 3 1/2 year old daughter and a 1 1/2 year old son. I’m learning to draw clowns  and build Lego towers with my daughter. My son is very good at destroying the towers. So we have both forces at work in my house: construction and destruction. [laughs] We like to strike a nice balance.</p>
<p>The post <a href="http://www.nuxeo.com/blog/updates/2013/04/meet-team-benjamin-jalon/">[meet the team] Benjamin Jalon</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/updates/2013/04/meet-team-benjamin-jalon/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>[Nuxeo Tech Report] News from the Developer Front #6</title>
		<link>http://www.nuxeo.com/blog/development/2013/04/nuxeo-tech-report-6/</link>
		<comments>http://www.nuxeo.com/blog/development/2013/04/nuxeo-tech-report-6/#comments</comments>
		<pubDate>Thu, 25 Apr 2013 13:30:16 +0000</pubDate>
		<dc:creator>Laurent Doguin</dc:creator>
				<category><![CDATA[Product & Development]]></category>
		<category><![CDATA[techreport]]></category>

		<guid isPermaLink="false">http://www.nuxeo.com/blog/?p=5104</guid>
		<description><![CDATA[<p><p>Hi everyone, and welcome to this new tech report. It&#8217;s a rather short one this week &#8212; we&#8217;re mostly consolidating what you&#8217;ve read about in the previous report. Oh, and we released Nuxeo Drive :D</p>
<h2>Nuxeo Drive</h2>
<h3>Drive 1.0.1 released</h3>
<p>Drive 1.0.1 was release last week.</p>
<h3>Next steps</h3>
<p>There is a known issue when several files are created on the server: because the server may be busy, there is a time shift between the audit log event date and the time where the audit entry is actually persisted. This time shift can lead the client to &#8216;loose some events&#8217; and then not see some files.</p>
<p>To fix that, we should:</p>
<ul>
<li>Review the polling system: based on the audit sequence ID rather than the time window</li>
<li>Prepare the upgrade of the client side database</li>
<li>Make the client able to detect when a database update is needed.</li>
</ul>
<h3>Oracle issue</h3>
<p>There is a &#8230;</p></p><p>The post <a href="http://www.nuxeo.com/blog/development/2013/04/nuxeo-tech-report-6/">[Nuxeo Tech Report] News from the Developer Front #6</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></description>
				<content:encoded><![CDATA[<p>Hi everyone, and welcome to this new tech report. It&#8217;s a rather short one this week &#8212; we&#8217;re mostly consolidating what you&#8217;ve read about in the previous report. Oh, and we released Nuxeo Drive :D</p>
<h2>Nuxeo Drive</h2>
<h3>Drive 1.0.1 released</h3>
<p>Drive 1.0.1 was release last week.</p>
<h3>Next steps</h3>
<p>There is a known issue when several files are created on the server: because the server may be busy, there is a time shift between the audit log event date and the time where the audit entry is actually persisted. This time shift can lead the client to &#8216;loose some events&#8217; and then not see some files.</p>
<p>To fix that, we should:</p>
<ul>
<li>Review the polling system: based on the audit sequence ID rather than the time window</li>
<li>Prepare the upgrade of the client side database</li>
<li>Make the client able to detect when a database update is needed.</li>
</ul>
<h3>Oracle issue</h3>
<p>There is a date formatting issue when doing Hibernate queries on Audit logs. =&gt; We should simply use Hibernate queryMaker to avoid that (<code>AuditReader</code> provides a method to allow parameters merge and will handle that!).</p>
<h2>Technical tasks</h2>
<h3>Tomcat DS/Hibernate upgrade/Seam patch/H2 upgrade + MVCC branch</h3>
<p>As already raised in previous tech reports, we still have several branches holding important infrastructure changes. We&#8217;re starting the merge of these branches.</p>
<ul>
<li>NXP-10926: H2 upgrade + H2 MVCC mode + use H2 in memory so speed up tests</li>
<li>Tomcat Pool and <em>&#8216;real&#8217;</em> XA mode + upgrade hibernate + patch Seam</li>
<li>Improvements on test framework and error management (need to create ticket)</li>
<li>Tomcat upgrade.</li>
</ul>
<h3>BIRT</h3>
<p>A BIRT upgrade has been scheduled. The target is:</p>
<ul>
<li>upgrade to BIRT 4.2</li>
<li>make an ODA connector for NXQL so that we can use NXQL from within BIRT designer.</li>
</ul>
<h3>PreSales sandbox</h3>
<p>The PreSales team has started a sandbox where they code some extensions that can be useful for building demos and POCs. See <a href="https://github.com/nuxeo/nuxeo-presales-prototyping-toolkit">nuxeo-presales-prototyping-toolkit</a>.</p>
<p>Part of this work is also to fill the potential holes we can have in the API or in the Extension Point system. That&#8217;s why we did a quick review with Benjamin and a lot of the work should be directly merged inside the trunk:</p>
<ul>
<li>missing Operations</li>
<li>Operation with missing parameters</li>
<li>new Publisher extensions</li>
<li>&#8230;</li>
</ul>
<h3>Automation</h3>
<p>As already explained in previous reports, changes in Automation API are one of the cornerstone of the next release.</p>
<h4>Automation improvements</h4>
<ul>
<li>do some non-regression validation tests</li>
<li>merge Olivier&#8217;s changes on JSON marshaling</li>
</ul>
<h4>Angular Layout/CRUD Automation/Automation</h4>
<p>Damien has started working on:</p>
<ul>
<li>adding a REST CRUD API</li>
<li>improving the JS client and make integration with AngularJS</li>
<li>wrapping the Layout system</li>
</ul>
<h2>Layouts/Widgets/Studio/DAM</h2>
<h3>Widgets</h3>
<h4>Tabs widgets</h4>
<p>We are almost ready to have a Widget that renders tabs defined by actions and allows Ajax Switching. This will be useful for DAM, but more globally, this would good for CAP too. So, the next steps include:</p>
<ul>
<li>finishing the implementation</li>
<li>use by default in Nuxeo CAP tab system</li>
<li>may be merging with changes on RESTDocumentLink for conversation management <a href="https://jira.nuxeo.com/browse/NXP-11331">NXP-11331</a></li>
<li>updating Selenium tests</li>
</ul>
<h3>Action types // Widget types</h3>
<p>We are now very close to being able to use <code>WidgetTypes</code> to define Action types. The idea is basically to make action types more than a simple string attribute, but associate it with a set of property that can be defined by the user. Doing so would be good:</p>
<ul>
<li>for Studio, action configuration would be easier since we can have a form</li>
<li>for documentation and showcase</li>
<li>for Ajax behavior</li>
</ul>
<h3>Ajax Rerender and duplicated IDs</h3>
<p>Depending on layout and widgets config there may some cases where RichFaces + Facelet infrastructure fails to correctly manage Ids. For now, this is a problem that:</p>
<ul>
<li>can be avoided via small workarounds in most cases</li>
<li>can not be fixed in Nuxeo</li>
<li>can not be easily fixed in JSF/Facelets</li>
</ul>
<p>The JSF duplicate ID issue on ajax rerender has been identified, and fixed by using the nxu:repeat tag (revisited) and making it create new sub-components when it detects that the iteration list has changed. Otherwise existing components are reused and their ID is not reset, leading to potential duplicate IDs. See <a href="https://jira.nuxeo.com/browse/NXP-11434">https://jira.nuxeo.com/browse/NXP-11434</a>.</p>
<p>Anahide will try to submit the problem to RichFaces/JSF2 community to see if we have some feedback.</p>
<h3>DAM</h3>
<h4>Box listing</h4>
<p>The Box listing is used to manage the DAM thumbs view inside a <code>ContentView</code>. This works in DAM, but there is still some work to be done:</p>
<ul>
<li>integration inside CAP/DM to manage the <em>&#8216;BigIcon view&#8217;</em></li>
<li>standardize the DAM selection use case (clicking on the Thumb in DAM changes the <code>currentDocument</code>)</li>
<li>Studio editor UI to be adapter to Box listing display
<ul>
<li>this is not strictly needed for now, but this would be really better.</li>
</ul>
</li>
</ul>
<h4>Selection system</h4>
<p>For <code>ContentView</code> the current selection model is built on the use of the <code>DocumentListsManager</code>. This system that is also used for <code>Worklist</code> and <code>Clipboard</code> becomes an issue for selection:</p>
<ul>
<li>when DAM and DM show the same <code>ContentView</code> but with different filters</li>
<li>when there are several <code>ContentView</code> in the same page</li>
</ul>
<p>If we allow to have several selection lists, this should impact the Copy/Past/Move actions available on <code>ContentView</code>.</p>
<h4>Next steps</h4>
<p>The next steps on DAM include:</p>
<ul>
<li>Tab Widget integration</li>
<li>Html5 DnD integration for mass Import
<ul>
<li>start from existing code</li>
<li>add Canavas based preview if we have time</li>
<li>add support for async processing</li>
</ul>
</li>
<li><em>&#8216;Ajax Permlink&#8217;</em> (already discussed but not done)</li>
<li>functional testing</li>
</ul>
<h2>Connect/Studio</h2>
<h3>Advanced Studio</h3>
<p>Part of the ongoing work is about unlocking advanced feature so that people using studio don&#8217;t end up with a lot of XML Override in the XML extensions. This typically includes:</p>
<ul>
<li>Extended ContentView configuration</li>
<li>Widgets configuration</li>
<li>Selection configuration</li>
</ul>
<p>Having all these new options:</p>
<ul>
<li>will unlock feature for advanced users</li>
<li>may confuse <em>basic users</em> and lead them into problem
<ul>
<li>problems we will have to fix via support</li>
</ul>
</li>
</ul>
<p>This may be worth adding a disclaimer saying something like <em>&#8216;use advanced settings only if you now what you are doing, support won&#8217;t be able to easily help you if you mess up something&#8217;</em>.</p>
<p>The post <a href="http://www.nuxeo.com/blog/development/2013/04/nuxeo-tech-report-6/">[Nuxeo Tech Report] News from the Developer Front #6</a> appeared first on <a href="http://www.nuxeo.com/blog">Nuxeo Blogs</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.nuxeo.com/blog/development/2013/04/nuxeo-tech-report-6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
