Latest Posts

Archives [+]

Categories [+]

Authors [+]

Entries filed under 'everything is content'

    Posted by Michael Marth JUN 10, 2010

    Posted in cms, everything is content and modelling Comments 2

    Last week I have uploaded a Twitter clone application to Day's Package Share. The application's content package not only contains some sample content and the jsp files with the application code. It also includes sample users and their respective access rights on different JCR nodes. Putting all this information in one content package is possible (and even simple) because users, ACLs etc are stored in the content repository as JCR nodes.

    The experience of putting together this package nicely reminded me of the power of the concept of storing all of a a web application's artefacts in the content repository - which can be considered the technical implementation of Day's mantra "everything is content".

    Classically, the image of web content management systems one has in mind looks something like this:

    /content/ddc/blog/2010/06/comment_on_contenta/jcr:content/par/image_0/file

    Content is the input and a web page is the end result of some rendering process. There is nothing wrong with that image, but considering "everything is content" an alternative prototypical image of a CMS came to my mind:

    /content/ddc/blog/2010/06/comment_on_contenta/jcr:content/par/image_1/file

    A web content management system's repository is the place to store and manage all aspects that make up your web site. The web page is not only the end result, but also the source.

    Posted by Lars Trieloff APR 01, 2009

    Posted in cq5, everything is content and social Add comment

    Today, our CQ5 Digital Asset Management and CQ5 Social Collaboration become available with the launch of CQ 5.2. This is a great day for me, because my two products that I have been working on for the last one and a half years are going to market.

    Both with CQ DAM 5.2 and CQ Social Collaboration 5.2 I have been standing on the shoulders of giants, which have enabled me to implement a bold vision of Digital Asset Management and Social Collaboration integrated into Web Content Management in such a short time, following our CQ5.1 release last November. These giants are our developer team and our product marketing team who have a done a marvelous job on getting this product to countless demos, POCs, beta projects, press and analyst briefings and who have implemented one cool feature after the other.

    But standing on the shoulders of giants also means reusing a great platform to build upon. We did not have to think a single second about clustering, backup, scaling, permissions, LDAP integration, workflow, development environment, because all this is at some level provided out of the box by CRX (and its underlying Apache Jackrabbit and Apache Sling foundations) and the CQ5 Platform we share with CQ5 Web Content Management.

    I would like to take the opportunity to shed the light on two smaller features, one in CQ5 DAM and one in Social Collaboration that we typically do not include in demos, but that offer a huge potential for everyone who sees our products as a platform to realize his own ideas.

    Feature number one is the workflow launcher we are using especially for CQ5 DAM. This workflow launcher will listen for repository events at a certain part of your content repository, for example the DAM bulk upload hot zone or a Sharepoint repository we are monitoring through our connectors. Once a new file is being created here, we launch the workflow that will take care of the actual processing. For the end user this means: more flexibility in creating complex workflow-driven processing solutions for digital assets. And it means a reduction of magic, because everything that will happen to your assets is visible (in the Workflow Models tab) and the reason why it happens is visible in the workflow launcher tab. Not attempting to do magic and to appear as magician should be a goal for every software engineer.

    The second feature is even less graphic (guess why we are not demoing it), but it is one of the small pieces that makes Social Collaboration so great. We call it the 'Feed Importer', but it actually does a lot more. The idea of the feed importer is to poll a remote resource at a specified interval, to parse it and to create nodes in the content repository that represent the content of the remote resource. We are using this at two places right now - for subscribing to remote iCalendar files just like you do in Apple's iCal or Google Calendar and in the blog, where you can aggregate existing blogs in one 'planet blog' that for instance contains all bloggers of the JCR community. This auto-blogging-feature has been in the blog since Advanced Collaboration 1.1, but with Social Collaboration 5.2 there is a proper and powerful API that can be used in customer projects as well. Needless to say, in order to implement a new parser & importer all you have to do is implement one OSGi component and I expect it to be not too long till we see Twitter-mashups on CQ5-powered websites that are using the Feed Importer.

     

    So thank you very much for all the help with the 5.2 release and I am looking forward to see many CQ5-powered community websites and public asset repositories soon.

    Posted by Michael Marth AUG 14, 2008

    Posted in everything is content Comments 2

    If you happen to live in Germany, Switzerland or Austria (and speak German) make sure you do not miss this month's Javamagazin. The title story is "Was ist Content?" ("what is content?") and there is also an introductory article about JCR and Apache Jackrabbit.

    The abstracts:

    Was ist Content?

    Bilder, Videos, Texte – man erkennt Content, wenn man ihn sieht. Aber hat Content charakteristische Eigenschaften, die ihn von reinen Daten unterscheiden? In Gesprächen über Content und Content-Management ist gelegentlich festzustellen, dass Content mit relationalen Datenbanken oder Dateisystemen gleich gesetzt wird. Obwohl Content letzten Endes oft im Dateisystem oder einer Datenbank gespeichert wird, wird eine Reduktion auf diese Low-Level-Sicht der Thematik nicht gerecht. Content ist eine spezielle Form von Daten, denen eine eigene Qualität innewohnt. Insofern ist auch Content-Management mehr als nur eine Datenbankmaske.
    David Nüscheler, Michael Marth

    JCR und Apache Jackrabbit

    Der JCR-Standard (Content Repository API for Java) beschreibt eine klare Schnittstelle zwischen Anwendung und Content-Ablage. Das hierdurch definierte Content Repository stellt die dem Content typischen Eigenschaften und Funktionalitäten in standardisierter Weise zur Verfügung. Dies erleichtert erheblich die systemübergreifende Nutzung von Content und erschließt damit ein bisher nur mit viel Mühen auszuschöpfendes Potenzial.
    Michael Marth, Gerd Handke, David Nüscheler, Carsten Ziegeler

    Posted by Michael Marth MAY 22, 2008

    Posted in cms, everything is content and link of the day Add comment

    Seth Gottlieb has written an excellent post on how content is different from data. He also outlines features of a content management application that distinguish it from a database application.

    Posted by Michael Marth APR 18, 2008

    Posted in crx quickstart, data first, davids model, everything is content, graph, jcr, open, rest, sling, social and tutorial Comments 4

    In case you follow emerging Internet standards you will have come across OpenSocial, the Google-led spec for social network applications. Major supporters are MySpace, LinkedIn, XING, Google's own Orkut, Hi5 and others. The Apache Software Foundation's implementation of this spec is called Apache Shindig. It is a container (runtime) for OpenSocial applications (which are called gadgets).

    In my opinion OpenSocial and Apache Sling are a good technical fit for at least two reasons:

    1. On a raw technology level both use the same technology building blocks, e.g. JavaScript: in Sling JS is used on the server-side for .esp templates and on the client-side in the case of JST templates. OpenSocial gadgets are coded in JS as well. Moreover, associated technologies like JSON, feeds and REST are supported by both.
    2. On a more conceptional level: As a spec that must work across a number of different social networks the majority of information that is accessible through the OpenSocial API is optional, i.e. it is up to the container if data is returned or not. This situation is a good fit to the unstructured, "Data First" approach that is enabled by Sling (respectively the underlying JCR).

    I would like to show Apache Shindig (Apache's OpenSocial container implementation) and CRX Quickstart (a bundle of Apache Sling and Day's JCR-compliant repository) working together in this blog post.

    Installation

    In this screencast I have shown how to install CRX Quickstart: double-click on its icon (CRX Quickstart is not available, yet, but it will be very soon). Strictly speaking, you do not need CRX Quickstart for the examples below. It all works with "plain" Sling as well.

    Installing Shindig is a tad more complicated and described on Shindig's web site. You need to check out, do a Maven build (I used revision 648157 for this example) and start Shindig's Jetty server on port 8080 with:

    mvn jetty:run-war

    Once you started Shindig hit /gadgets/files/samplecontainer/samplecontainer.html on http://localhost:8080. You should get a kind of gadget console that looks like this (click to enlarge):

     

    Shindig comes with an example implementation of a social network. By default it runs the "Hello World" example gadget located at:/gadgets/files/samplecontainer/examples/SocialHelloWorld.xml on http://localhost:8080.(btw Shindig comes with some example data so don't worry, if you have no friends - Shindig has some imaginary ones for you).

    Friends are Content

    What I would like do is: grab the gadget's viewer's friends and all the available data about them and store this data in the repository. For this purpose I have written a little gadget (see below) and saved it in my JCR repository at /apps/friends/friendsaver.html. By default the repository is running on port 7402, so when I point the gadget console to http://localhost:7402/apps/friends/friendsaver.html I get (click to enlarge):

     

    The gadget retrieves the viewer's friends and displays them in HTML. Moreover, in the background the viewer's data and the available friend data is posted to my repository. In the Content Explorer this looks like (click to enlarge):

     

    Hey, remember the "Everything is Content" mantra? Well, your imaginary friends are content, too.

    Please note that this works without setting up any schema or any other configuration of the repository. I ran it on an out-of-the-box CRX Quickstart (see also this screencast and this post about Data First). Only for the fields that are actually sent node properties are created.

    The Gadget Code

    The gadget is completely standard OpenSocial code, no surprises here. In onLoadFriends() the viewer's friends (variable viewerFriends) are iterated and displayed in HTML. For each opensocial.Person object the function createFriendNode() is called. In this function an HTTP POST request is sent to the repository that persists the person. Available opensocial.Person.Field data is sent as POST parameters (in the code only gender and first phone number are implemented) and thus persisted as node properties. I want to leverage the repository's hierarchy and store the friends as child nodes below the viewer (see David's model, rule 2). Here's the relevant snippet:

     /**  * Request for friend information.  */function getData() {      var req = opensocial.newDataRequest();  req.add(req.newFetchPersonRequest(opensocial.    DataRequest.PersonId.VIEWER), 'viewer');  req.add(req.newFetchPeopleRequest(opensocial.    DataRequest.Group.VIEWER_FRIENDS),    'viewerFriends');  req.send(onLoadFriends);}; /**  * Parses the response to the friend request  * @param {Object} dataResponse Friend      information that was requested.  */function onLoadFriends(dataResponse) {  var viewer = dataResponse.get('viewer').    getData();  var html = 'Friends of ' + viewer.    getDisplayName();   html += ':<br><ul>';  createFriendNode(viewer);  var viewerFriends = dataResponse.    get('viewerFriends').getData();  viewerFriends.each(function(person) {    html += '<li>'      + person.getDisplayName()      + '</li>';    createFriendNode(person, viewer);  });  html += '</ul>';  document.getElementById('message').    innerHTML = html;};  function createFriendNode(person, parent) {     var url = "http://localhost:7402/content/friends/";  if(parent) {    url += sanitizeId(parent.getId())+"/*";     } else {     url += "*";    }        var params = {};    params[gadgets.io.    RequestParameters.CONTENT_TYPE] =    gadgets.io.ContentType.TEXT;    params[gadgets.io.    RequestParameters.METHOD] =    gadgets.io.MethodType.POST;    var postParams = "";  postParams += 'name=' +    sanitizeId(person.getId()) + '&fullname=' +    person.getDisplayName();  if(person.getField(opensocial.Person.Field.    PHONE_NUMBERS)) postParams +=    ('&phone=' +    person.getField(opensocial.Person.Field.    PHONE_NUMBERS)[0].    getField(opensocial.Phone.Field.NUMBER))  if(person.getField(opensocial.Person.Field.    GENDER)) postParams += ('&gender=' +    person.getField(opensocial.Person.Field.    GENDER).getKey())  // I could add more fields here...        params[gadgets.io.RequestParameters.    POST_DATA] = postParams  gadgets.io.makeRequest(url, null, params);};    function sanitizeId(id) {  return id.replace(".", "_");   }gadgets.util.registerOnLoadHandler(getData);  

    Round-Tripping

    Now that the friends are stored in the repository each one has a URL. Displaying a friend in a simple HTML page can be done with e.g. server-side Javascript. Storing this file in the repository in /apps/friends/html.esp

    <html>  <body>    <h1><%= currentNode["fullname"] %></h1><ul><li>gender: <%= currentNode["gender"] %></li><li>phone: <%= currentNode["phone"] %></li></ul>  </body></html>

    will yield for the URL http://localhost:7402/content/friends/john_doe/jane_doe.html

    But this is only half the fun. It is much more interesting to retrieve the friends data in another OpenSocial gadget. This can easily be done without any repository-side code as Sling natively supports the json format. For example the URL http://localhost:7402/content/friends/john_doe/jane_doe.json will return this node in json format. Like that, we can easily access the friends nodes through a gadget containing this snippet:

    function makeCRXRequest() {    var params = {};    params[gadgets.io.RequestParameters.    CONTENT_TYPE] =    gadgets.io.ContentType.JSON;    var url =    "http://localhost:7402/content/friends/john_doe/"+    document.getElementById("person_name").value+    ".json";    gadgets.io.makeRequest(url, response, params);};function response(person) {    var html = "";  html += "name: " + person.data.fullname +    "<br/>";  html += "phone: " + person.data.phone +     "<br/>";  html += "gender: " + person.data.gender +    "<br/>";    document.getElementById('content_div').     innerHTML = html;};

    The gadget in action looks like this (click to enlarge)

     

    This little hack could be the starting point for a cross-social network phone book application.

    Final remarks

    I hope I could show that Sling and Shindig go really well together. Especially, being able to utilize the JCR repository as a backend without any coding on the repository side looks tempting to me. Maybe at one point Sling will even be able to run OpenSocial gadgets natively.

    In this post I concentrated on frontend intergration technologies. But OpenSocial will soon add a REST API next to its JS API. For Shindig the implementation of this REST API is likely to be Apache Abdera which uses JCR as an optional persistence storage. So there will be additional points of contact.