Blog Posts

PHP talking to Magnolia CMS

Since we started working on Jackalope, we always claimed it would also provide an integration point with other enterprise systems. Last week, we set out to proof this idea. Grégory Joseph of the Java based Magnolia CMS came to help us on the Magnolia side of things. Magnolia is using the JCR reference implementation Jackrabbit for storing its content. After an interesting exchange on their design decisions and our ideas, we started to hack.

Trying to import an XML export from Magnolia in the JCR standard format showed that the Jackalope importer is not yet perfect. In the end Greg was faster in building a Magnolia module that exposes the Jackrabbit Davex binding that Jackalope uses to communicate with the server.

After that, things really started flowing. We managed to not only read data, but also write using the frontend editing feature of the CMF. Additionally, we managed to configure PHPCR-ODM to determine document classes based on the MetaData child every Magnolia page node has. And all of that using several parallel connections to the repositories, so that the normal pages and navigation can still be loaded from our standalone jackrabbit backend (you could just as well use another PHPCR implementation like jackalope-doctrine).

All in all a very successful hackday. We integrated the demos into the cmf-sandbox (branch magnolia_integration). See the MagnoliaController in src/Sandbox/MagnoliaBundle/Controller.

Screencapture showing the edit functionality

Installing the Sandbox with Magnolia

If you want to try things out for yourself, you need to:

  • Download Magnolia Community Edition
  • unpack it into a folder
  • Download the Magnolia Jackrabbit-Davex Module and drop all .jar files from this archive into apache-tomcat-6.0.32/webapps/magnoliaAuthor/WEB-INF/lib/
  • edit apache-tomcat-6.0.32/conf/server.xml to change the port from the default 8080 to 8888, so it looks something like <connector port="8888" protocol="HTTP/1.1">...
  • remove open file limitations. in bash, this will be ulimit -n 5000
  • launch magnolia with something like this: apache-tomcat-6.0.32/bin/magnolia_control.sh start tail -f apache-tomcat-6.0.32/logs/catalina.out
  • visit localhost:8888 and follow the Magnolia installation (login with superuser/superuser)

If you followed the above steps, you should be able to access Magnolia data with Jackalope-Jackrabbit at http://localhost:8888/magnoliaAuthor/.davex/ (Note that there is no web browser access, as the servlet does not issue a basic auth challenge but just expect the credentials to be set from the beginning).

Now try the urls /magnolia_edit, /magnolia/about/subsection-articles/article and /magnolia_odm and read the code to see what we do.

Related Entries:
- Progress on PHPCR with a hackday
- Doctrine PHPCR-ODM now handles versioning
- PHPCR workshop in zurich 8th of May 2011
- A (simple) PHPCR browser
- Touring North America

Comments [1]

Big leap forward for Opendata

make.opendata.ch This year, the second make.opendata.ch-hackdays took place in Geneva and Zurich. More than 120 developers, designers and ideators including a handful of Liipers met to work on “public transport”, which was set as the hackday’s main focus. A goal was to show to the SBB and the public what open data sources allow and how they can be used.

The Liipers present at the hackdays got involved in some of the projects:

Zurich: The Swiss Public Transport API

Team: Colin Frei, Danilo Bargen, Dominic Lüchinger, Fabian Vogler, Roland Schilter

Following our internal Transport API hackday in the beginning of the year, some others joined us to continue working on the Rest-API. The goal of the project was to provide a public transport Rest-API that allows every interested developer to create his own applications based on public transport schedules. Basically the API transforms the complex SBB XML response into a JSON format. Documentation and examples can be found at transport.opendata.ch.

During the two days, the team including three Liipers reacted on user requests and implemented new features to the existing API. Besides little changes, we added a location-based station search as well as the output of the full stage details.

Today, the API is already in use by several projects, including a command line interface and a wheelchair map.

Feel free to use it, extend it, and share it. Feedback is welcome as well.

Zurich: Transport Flows visualization

Team: Benjamin Wiederkehr, Dagmar Muth, Ilya Boyandin, Joel Bez, Patrick Stählin, Patrick Zahnd, Sylke Gruhnwald, Thomas Preusse

In the second project Patrick got involved in visualizing Transport Flows. Adapting the idea of the Villevivante project, the goal was to visualize the Swiss transport flows nicely and in an interactive way.

We collected the data based on the swisstrains.ch JSON output and processed it with Python scripts. With the given information, we created some interactive graphics using the JavaScript visualization framework d3. It turned out that it just perfectly matched our requirements and provided a wide range of features.

In the end we were able to visualize facts like sector-based train speeds and counts. We also visualized the transport hubs on a minute and hour basis.

An interesting statistic is the transport hub list, especially that Lucerne is the number three after Zurich and Berne. Also interesting is that the fastest railway line is still the “Bahn2000” between Berne and Zurich, which some of us use regularly.

The result can be found on flows.transport.opendata.ch.

Geneva: SiesteApp

Team: Andreas Kuendig, Benoît Pointet, Raphaël Halloran

The Geneva hackday crowd grew many interests which were more focused on the Geneva region, since a delegation of the territorial information systems department of Geneva (SITG) was present and provided great help and insights in the available geo-informations for the city. Topics like “bike mobility” or “multi-modality” got under heavy scrutiny, discussion and ideation.

Benoît got involved in a team who focused on a vague but non-the-less fascinating topic around individuality, emotions and comfort. He ended up working on a mobile app to help people find out where they could take a nap in Geneva; have a rest or just breath some fresh air in a quiet (or even dog-free) environment.

Follow the project at http://make.opendata.ch/doku.php?id=project:sieste.

Conclusion

Not only in the eyes of the Liip attendees the hackday was a success, but also in those of the participants and of the many institutional delegations visiting the hackday, like the SBB participants, who were impressed and willing to support the Swiss public transport API.

– Andreas Amsler, Benoît Pointet, Colin Frei, Fabian Vogler, Patrick Zahnd, Roland Schilter

Related Entries:
- How to manage patching a github hosted repo
- Packaging solution for (php-)projects
- Integrating Magento into Symfony2
- First release of proxy-object
- Buzzing in Berlin

Comments [0]

Touring North America

After pretty much exactly a month I have concluded my north american "tour" featuring speaking at Confoo in Montreal, a PHP UG meeting on Boca Raton and last, but definitely not least DrupalCon Denver. In between relaxing at my parents place in Florida. Lets me review the experience in chronological order.

I had previously attended Confoo in 2008 where it was still very much focused on PHP developers. These days Confoo aims to cover all web technologies all the while also including non-developers in some tracks. My first talk was on PHPCR, while everyone back home was celebrating Liip Day. This usually attracts a smaller crowd that have both experienced the pains of todays CMS and are ready to look beyond. I always ask the audience at the end of they think that PHPCR looks relevant to them and pretty much everyone raised their hand. The other talk I gave was a presentation on Symfony2. I came with zero slides, but only my editor, the shell and a browser to show people the guts of Symfony2 in its raw beauty. I did this before at PHP Conference in Mainz and I think I am improving in terms of finding the right pace and order of topics. However a single 60 min slot is just too short, so if I am going to submit this talk in the future I will need to ensure I have 120 min. In general interested in Symfony2 with all around at the conference. It seemed to be the dominant PHP framework discussed which surprised me, since I thought that Symfony2 was struggling in North America, but maybe it isn't or maybe its just struggling in the USA.

My next speaking engagement was at the South Florida PHP UG. Pablo from ServerGrove suggested that I leverage the fact that I will be in Florida to give a talk. I also held my PHPCR talk there to quite a diverse group. Most people were using home grown CMS solutions or heavily customized solutions based on other CMS. Once again I felt like in the end people really saw the value in having a mature and consistent storage API as well as the benefits of collaboration across CMS for implementing this.

My next stop was Denver for DrupalCon. I was quite excited to be going because I had invested quite a bit of my personal time as well as my Liip sponsored weekly "do what you want on OSS" day in supporting the WSCCI initiative for Drupal8. In a way its strange, since I have never done a Drupal project in my life. Furthermore I am one of the lead people behind the Symfony CMF initiative. However it is simply an exciting opportunity for the entire PHP world, heck given that Drupal runs close to 2% of all websites in the world, its should be exciting for quite a large portion of people on this planet! Furthermore I know that collaboration with Drupal will have benefits for Symfony2 as well as the CMF project as the will continue to push the common code pieces.

The first talk I did together with Fabien Potencier. He has submitted a talk on the Symfony2 components, which Dries announced the day before at the keynote would become a key piece in Drupal8's core architecture. Since I was involved in the initial evaluation of Symfony2 for Drupal, Fabien figured it would make sense if I start things off with a little introduction about why this development could be beneficial for the Drupal community. Needless to say the attendance was jaw-dropping: This was easily the biggest presentation on Symfony2 .. ever .. at a DrupalCon no less! During the entire conference people walked up to me expressing their excitement and so I just want to pass along the countless "thank you for Symfony2" I heard to the Symfony2 community. Speaking of which, many people also asked me why the Symfony community cares so much about Drupal? To which I replied, we realize that this will mean we have a lot more users which will lead to more developers, leading to more code contributions. By the way, DrupalCon Denver has over 3000 attendees, I have been to plenty of PHP conference that had less attendees than were present at the hack day on the day following the conference. Of course we also realize that with Drupal8 using the HttpKernel, it will be a breeze to integrate Drupal8 and Symfony2 applications. This is something many of us currently have to deal with already.

My other talk was a core conversation together with Damien Tournoud about defining the next steps towards a documented oriented storage API for Drupal. I think this makes me the first DrupalCon core conversation speaker to never have installed Drupal. I started things off again with a 15 min intro to PHPCR. Then Damien discussed how to refactor the current Entity and Field API's to a unified API that should eventually be replaced with PHPCR. The goal is to get things far along with Drupal8 that it would be possible to provide a contrib module that would already switch things to PHPCR and at the latest with Drupal9 PHPCR would become the native storage API. We would of course have to push the Doctrine DBAL implementation of Jackalope quite a bit to be able to handle larger use cases in order to make this a viable option for Drupal. As this was a core conversation we then had about 30 min of Q&A where to my surprise no tomatoes were thrown. Instead all attendees seemed to be quite excited and so it seems like things are pretty much on track to get PHPCR into Drupal!

I cannot express how huge this is as the only way I could imagine I could would be with a video of me doing a back flip, but I can't do back flips. With Dries emphasis on mobile and content authoring which was also pushed in Luke's mobile first keynote I think that Drupal is well on track to become the first big CMS to adopt the Decoupled CMS vision that Henri and I have been shouting from the roof tops for a while now. I also discussed CMIS briefly during the talk and then later on with a guy from Alfresco (darn forgot the name) and then again at the hack day with several Drupal core people. We might look into providing an implementation that can expose any PHPCR repository via the CMIS protocol, similar to what Apache Chemistry does for JCR. For an introduction to CMIS see this blog post by Jeff.

Needless to day I am thrilled with what is going on with Symfony2, Drupal and of course PHPCR. Really enjoyed each speaking opportunity and I want to especially thank Liip for funding my travel expenses to Denver as core conversation speakers need to pay for travel and hotel on their own.

Related Entries:
- Paris PHPCR meetup
- Doctrine PHPCR-ODM now handles versioning
- News for the symfony2 cmf: Second PHPCR implementation, hackday announcement and PHPCR to become "official"
- Multilanguage support for Doctrine PHPCR-ODM
- How to manage patching a github hosted repo

Comments [1]

Jackalope observation and import repository dumps

Daniel Barsotti and me were reviewing the observation API of PHPCR and decided to just implement getting the observation journal. The journal contains all add, remove and update operations that happened on a PHPCR repository. You can also filter the journal by event type, path, node type and other criteria. This way, PHPCR can become almost a message queue (but just almost, there is no guaranteed delivery of messages).

use PHPCR\Observation\EventInterface; // Contains the constants for event types

// Get the observation manager
$workspace = $session->getWorkspace();
$observationManager = $workspace->getObservationManager();

// Get the unfiltered event journal and go through its content
$journal = $observationManager->getEventJournal();
$journal->skipTo(strtotime('-1 day'); // Skip all the events prior to yesterday
foreach ($journal as $event) {
    // Do something with $event (it's a Jackalope\Observation\Event instance)
    echo $event->getType() . ' - ' . $event->getPath()
}

// You can filter the event journal on several criteria.
// here we are only interested in events for node and properties added
$journal = $observationManager->getEventJournal(
    EventInterface::NODE_ADDED | EventInterface::PROPERTY_ADDED);

foreach ($journal as $event) {
    $event = $journal->current();
    // Do something with $event
    echo $event->getType() . ' - ' . $event->getPath()
}

The PHPCR / JCR standard also defines how to attach event listeners. The problem with this is that for any situation with concurrent repository access, the implementation needs to poll for events to trigger the listener. We assume that the JCR use case was a long running Java application that updates some local state based on polling events in a separate thread. For PHP, you neither have long running processes nor a thread system where you could do the polling. We decided that the journal will cover the most important use case: A cronjob that looks for specific events to act upon, instead of searching the whole repository each time and needing to determine if there is something changed that it needs to act upon.

Additionally, we implemented Session::importXML to import XML data into the repository. You can import both the JCR system view documents that are an exact dump of the repository and general XML documents where element names will be translated to PHPCR nodes and attributes to PHPCR properties.


Comments [2]

Table Inheritance with Doctrine

Introduction

Lately we had several projects where we had to store in a database very different items that shared a common state.

As an example take the RocketLab website you are reading: Events and BlogPosts are aggregated in the LabLog list as if they were similar items. And indeed they all have a Title, a Date and a Description.

But if you get the detail page of an Event or a BlogPost you can see that they actually don't contain the same information: a BlogPost contains essentially formatted text when an Event contains more structured information such as the place where the event will take place, the type of event it is, if people need to register to attend, etc..

Still we have to access those entities sometimes as similar items (in the LabLog list) or as different items (in the events list and in the blog posts list).

Naïve database model

Our first idea, and it was not that bad, Drupal does just the same, was to have a database table with the common fields, a field containing the type of item (it's either an event or a blog post) and a data field where we serialized the corresponding PHP object. This approach was ok until we had to filter or search LabLog items based on fields that were contained in the serialized data.

Indeed SQL does not know anything about PHP serialized data, thus you cannot use any of it's features on that data.

So how do you get all the LabLog items that are Events, happen in April 2012 and are "techtalks"? The only way is to go through all the Events records of April, unserialize the data and check if it's a techtalk event. In SQL you would normally only do a single request to find those items.

A better database model

There is a better way to model this in a database, it's called table inheritance. It exists in two forms: single table inheritance and multiple table inheritance.

Multiple table inheritance

Multiple table inheritance requires to use three tables instead of a single one. The idea is to keep the common data in a "parent" table, which will reference items either in the Event table or in the BlogPost table. The type column (called the discriminator) helps to find out if the related item should be searched in the Event table or in the BlogPost table. This is called multiple table inheritance because it tries to model the same problem as object inheritance using multiple database tables.

Multiple table inheritance

When you have a LabLogItem you check the type field to know in which table to find the related item, then you look for that item with the ID equals to related_id.

Single table inheritance

Alternatively the same can be modelled in a single table. All the fields are present for all the types of LabLogItem but the one that do not pertain to this particular type of item are left empty. This is called single table inheritance.

Single table inheritance

Single or multiple table inheritance

The difference is really only in how the data is stored in the database. On the PHP side this will not change anything. One may notice that single table inheritance will promote performance because everything is in a single table and there is no need to use joins to get all the information. On the other hand, multiple table inheritance will allow a cleaner separation of the data and will not introduce "dead data fields", i.e. fields that will remain NULL most of the time.

Table inheritance with Symfony and Doctrine

Symfony and Doctrine make it extremely easy to use table inheritance. All you need to do is to model your entities as PHP classes and then create the correct database mapping. Doctrine will take care of the hassle of implementing the inheritance in the database server.

Please note that the code I present here is not exactly what we use in RocketLab; we are developers and as such we always have to make things harder. But the idea is there...

The parent entity

In the case of RocketLab we created a parent (abstract) entity, called LabLogItem, that contains the common properties.

/**
 * This class represents a LabLog item, either a BlogPost or an Event.
 * It is abstract because we never have a LabLog entity, it's either an event or a blog post.
 * @ORM\Entity
 * @ORM\Table(name="lablog")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap( {"event" = "Event", "blogpost" = "BlogPost"} )
 */
abstract class LabLogItem
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="date")
     */
    protected $date;

    /**
     * @ORM\Column(type="string")
     */
    protected $title;

    /**
     * @ORM\Column(type="text")
     */
    protected $description;

    /***** Getters and setters *****/

    public function getId()
    {
        return $this->id;
    }

    public function setDate($date)
    {
        $this->date = $date;
    }

    public function getDate()
    {
        return $this->date;
    }

    // And so on...
}

There are several things to note about the mapping:

  • @ORM\InheritanceType: indicates that this entity is used as parent class in the table inheritance. This example uses single table inheritance, but using multiple tables inheritance is as easy as setting the parameter to "JOINED". Doctrine will create an manage the unique or multiple database tables for you !
  • @ORM\DiscriminatorColumn: indicates which column will be used as discriminator (i.e. to store the type of item). You don't have to define this column in the entity, it will be automagically created by Doctrine.
  • @ORM\DiscriminatorMap: this is used to define the possible values of the discriminator column as well as associating a specific entity class with each type of item. Here the discriminator columns may contain the string "event" or "blogpost". When its value is "event" the class Event will be used, when its value is "blogpost", the class BlogPost will be used.

Basically that's the only thing you need to use table inheritance, but let's have a look at the children entities.

The children entities

We have two regular entities to model the events and blog posts. Those entities extend LabLogItem.

/**
 * Represent a blog post item.
 * Note that this class extends LabLogItem
 */
class LabLogItemBlog extends LabLogItem
{
    /**
     * @ORM\Column(type="text")
     */
    protected $content;

    /***** Getters and setters *****/

    public function getContent()
    {
        return $this->content;
    }

    public function setContent($content)
    {
        $this->content = $content;
    }
}

/**
 * Represent an event item.
 * Note that this class extends LabLogItem
 */
class LabLogItemEvent extends LabLogItem
{
    /**
     * @ORM\Column(type="string")
     */
    protected $eventType;

    /**
     * @ORM\Column(type="string")
     */
    protected $location;

    /**
     * @ORM\Column(type="boolean")
     */
    protected $requiresRegistration;

    /***** Getters and setters *****/

    public function getEventType()
    {
        return $this->eventType;
    }

    public function setEventType($type)
    {
        $this->eventType = $type;
    }

    // And so on...
}

There is not much special in the children entities. An important thing to note is that the common fields defined in the parent entity LabLogItem SHOULD NOT be repeated here. Also you may notice that there is no annotations in the children such as @ORM\Entity to indicate that they are entities. Indeed they will inherit the annotations of LabLogItem and become entities.

From now on, when you create a PHP object of type Event and ask the entity manager to persist it, Doctrine will automatically do the complex work for you. From the developper point of view, Events and BlogPosts are just entities like any other.

It's easy to do operations on items which you don't know exactly the type:

$item = $entityManager->getRepository('RocketLabBundle:LabLogItem')->findOneByDate($someDate);

// Here we don't know exactly whether $item contains a blog post or an event...

if ($item instanceof Event) {
    // Then it's an Event
    echo $item->getEventType();
} else {
    // Otherwise it's a BlogPost
    echo $item->getContent();
}

But, if you know the type of item you are using you still can use them as regular entities:

$item = $entityManager->getRepository('RocketLabBundle:Event')->findOneByDate($someDate);

//  We have searched the Event entity repository so what we get in $item MUST BE an Event
echo $item->getEventType();

Conclusion

As you can see above using table inheritance with Symfony and Doctrine is very easy. It's just a matter of creating the parent class and the correct mapping. Furthermore you can switch from single to multiple table inheritance by modifying one line of code.

This technique should be used whenever you need to store items with a common state but that are very different in their nature.


Comments [5]

The making of plaene.uzh.ch

Starting with a lot of information

Last autumn, we were asked to build a new version of plaene.uzh.ch

Alexandra Blankenhorn and her team from University Zurich (UZH) had already done a great deal of work and offered a detailed concept of the information they wanted to provide for every building. And they had a lot of information. Lists of all institutes, lecture rooms, museums, libraries, canteens and computer workstations for every building.

The application should be based on a map with all the buildings on it and some more information on the surroundings of every building. They didn't just provide us with a lot of data, but also with a lot of freedom and trust to create a good and simple solution for the task.

Designing for a small screen

These long lists of buildings and lecture rooms for every building were the major challenge when Zahida started with the first scribbles. She designed mobile first and started with the most difficult situation. A lot of data on a small screen.

To solve the problem, all additional information was banned from the map, since it's purpose is to find a building and see how you can get there. Information about rooms and services inside a building open in a separate layer if you tap on the building name. Loïc called it "Inspector" and the name stuck. The navigation inside the Inspector is smartphone-oriented (with tappable lists and horizontal movement) and we kept this behavior also for tablet and desktop. Designing mobile first helped us to focus on what's essential and also create easier-to-use concepts for the classic website.

Inspector Scripbles

Wireframes for all the different cases

To create the basic grid and functionality of the website, Zahida designed Wireframes for Desktop and for Tablet and Smartphone in both orientations.


Wireframes Inspector

Ala came up with very beautiful designs -- as always! And, they even built a clickable prototype.

Developers are the first test-users

As soon as they started developing, Reto, Colin, Sébi and Marco came back with a lot of feedback. They were the first ones to actually use the application and therefore the first ones to notice, when something didn't work out as we planned. We continually adapted the wireframes and provided solutions for emerging problems like "there are too many building markers on the map, they are overlapping" or "some institutes are distributed on different buildings, how do we solve that in the inspector?". We met with the UZH project team at least once a week to discuss open issues and decided which solution to go on with. The best way for us was to define only the basic functionality of the site, but then stay flexible to adapt the details in collaboration with the team representing the stakeholders.

Choice of the map service

The plan was to display all the buildings with clickable "markers" on an interactive map. The first thing that comes to mind when talking about maps is Google Maps. But because of privacy concerns about the way Google tracks user requests via cookie we also looked for other options. In the end we chose to use the open data solution provided by OpenStreetMap.

OpenStreetMap it is, now what?

Looking at the project from a technical point of view we were facing some challenges. First of all the decision to host the site at the university network has been made. Also the map tiles should have a customized style. That means we couldn't just access the tiles from the OpenStreetMap server. We had to setup our own server and serve customized tiles from there.

Setup the server

To be able to render our own map tiles, we installed Mapnik2[1] with all of its dependencies. Mapnik2 uses PostgreSQL as a database, so we decided to also use PostgreSQL for the project itself. We downloaded the map data from one of the several OpenStreetMap servers[2]. With all that stuff setup we could use the python scripts Mapnik2 provides to generate the tiles according to an xml which specifies how the tiles should look like. Depending on the bounding box of the area you are rendering the tiles for it can take quite a while. 

Openlayers and HTML markers

We have not found any possibility to display markers containing HTML by default, so we have created two classes which enabled us to have HTML-markers. They are called HtmlBox and HtmlMarker. Click here to check it out, there is also an example HTML file provided in the archive, in case you would like to use it too. There is a little catch with this though. You will need to edit two lines in the "lib/OpenLayers/Layer/Markers.js" respectively the OpenLayers.js file. We have opened a pull-request[3] for that matter, but sadly it has not been merged yet. There you can see what needs to be changed to get our classes to work flawlessly. 

Small and big screens

Since the website should be looking good on small devices too, responsive design was the way to go. We used media queries to apply different CSS styles based on the size of the viewport. That is a very easy but powerful way to make a site viewable on differently sized devices. 

And now it gets hollywoodesqe

Everybody involved in this project was very dedicated, committed and constructive. A special "Thank you" for the awesome collaboration goes out to UZH-Project-Team, to Ala and to Steve, who facilitated our regular project retrospectives and further improved our work together.

[1] http://mapnik.org/

[2] http://wiki.openstreetmap.org/wiki/Planet.osm#Country_and_area_extracts

[3] https://github.com/openlayers/openlayers/pull/53


Comments [2]

CMF editor: overview, screencasts and live demo

I will present you the CMF editor and some of its key features. It's a content editor for the CMF project that we designed at Liip AG to pave the way to better experiences in this somehow traditionnal field.

We didn't start reiventing the wheel, we wanted to create an editor based on the last technologies, leveraging the 'decoupled' content management approach to improve the user's efficiency and also to improve the fun while using a backend application. We based our work on some great open source components such as VIE and Hallo.

After having a first version we participated to the IKS semantic UX contest happening last winter. Winning the prize allowed us to add semantic functionalities in our product.

In the last weeks we also merged our work with the CreateJS project so that we would all work under the same project and share some important features that we did separately.

To give you a better overview of the software we built here is a list of the main features:

-  Edit in place: The editor doesn't live in the back of your website, it's right here on the front of your pages.

-  Rich but simple interface: The interface is very simple yet very powerful. For example, you can drag and drop your images in your content and directly see how it will look like or you don't have to worry about loosing your content as auto-saving and local recoveries are included.

-  Extendable: We wrote some plugins to pave the way, but the idea is that one can write new plugins for the users to have the right tools for each specific business cases. The interface has been designed to handle extensions and keep a uniform feeling even with plugins made by different people.

-  Semantic: We built many semantic functionalities thanks to IKS.

-    Tagging suggestions based on your content

-    Automatic tagging of assets to keep highly linked and qualitative content in your system

-    Automatic article relationship through auto tagging of content

-    Automatic external links suggestion based on your actual tags and semantic services

To present the editor in a more effective way, I created some Screencasts to show the main features of the editor and how to use them.

I hope you will get interested in this project and that you found this post interesting.

Further links:

-  The screencasts are on youtube.

-  Link to the live demo.

-  Link to createJS.

-  If you want to know more about the CMF project.

-  The live demo called sandbox is on Github.

Kind regards,

Loïc Schülé

Read whole post

Comments [3]

Doctrine PHPCR-ODM now handles versioning

The Doctrine PHPCR-ODM allows you to easily map your PHP objects onto content repository nodes. Since last week, the PHPCR-ODM leverages the versioning support of PHPCR in the ODM layer. This gives your application a very simple way to work with versioned content.

The versioning is pretty simple to use. DocumentManager::find() will always give you the current version of the document. The DocumentManager provides access to old versions with getAllLinearVersions($document) and findVersionByName($class, $id, $name). The later gives you a detached document with the values it had when the specified version was created. Using checkin/checkout/checkpoint you generate versions. Finally there is restoreVersion and removeVersion to restore the document to an old version resp. to remove something from the version history.

An example should help understanding the workflow:

$article = new Article();
$article->id = '/test';
$article->topic = 'Test';
$dm->persist($article);
$dm->flush();

// generate a version snapshot of the document as currently stored
$dm->checkpoint($article);

$article->topic = 'Newvalue';
$dm->flush();

// get the version information
$versioninfos = $dm->getAllLinearVersions($article);
$firstVersion = reset($versioninfos);
// and use it to find the snapshot of an old version
$oldVersion = $dm->findVersionByName(null, $article->id, $firstVersion['name']);

echo $oldVersion->topic; // "Test"

// find the head version
$article = $dm->find('/test');
echo $article->topic; // "Newvalue"

// restore the head to the old version
$dm->restoreVersion($oldVersion);

// the article document is refreshed
echo $article->topic; // "Test"

// create a second version to demo removing a version
$article->topic = 'Newvalue';
$dm->flush();
$dm->checkpoint($article);

// remove the old version from the history (not allowed for the last version)
$dm->removeVersion($oldVersion);

Related Entries:
- Multilanguage support for Doctrine PHPCR-ODM
- News for the symfony2 cmf: Second PHPCR implementation, hackday announcement and PHPCR to become "official"
- Touring North America
- Paris PHPCR meetup
- Progress on PHPCR with a hackday

Comments [0]

Paris PHPCR meetup

 Last weekend I went to Paris to attend the Paris PHPCR meetup with about a dozen other people at the offices of CommerceGuys. Things started off with a short personal intro round, followed my presentations on PHPCR and PHPCR ODM. During the presentation there was a fair bit of discussion about the different available PHPCR implementations as well. Afterwards things split up in multiple groups working on integration of PHPCR with the Sonata Symfony2 Bundles, but also hacking on the Doctrine DBAL transport for Jackalope.

Overall I think the meetup managed to get quite a few new people up to speed with PHPCR, but more importantly was a great way to socialize with people around the common interest of CMS technology. Its always more efficient to communicate via email/irc etc after a bit of face time (not the apple kind). :)

As a bonus we even got progress on the code side of things and maybe got one step closer to bringing PHPCR to Drupal8, though its of course still too early to pop the champagne. Damien gave me the following topics to look into in regards to potential Drupal adoption:

- facetting

- query across version labels as a way to handle content staging workflows

- Jackalope Doctrine DBAL needs to be able to handle 10k+ documents with full text search on a shared hoster

- locking (pessimistic/optimistic locking, SELECT .. FOR UPDATE)

On facetting it should be noted thar I have already brought this topic up in the JSR-333 list.

Querying and versioning is something I need to look into, since I am very unsure if there is any support here. I will also try to find time to checkout HippoCMS and other JCR CMS and how they handle this.

For full text search in the Doctrine DBAL transport we discussed approaches briefly, but things might go in the direction similar to the full text search provided in Doctrine 1.x, which apparently is similar to how things are in Drupal today. Aside from this I am waiting for Damien to send a bunch of bug fixes that he worked on during the meetup.

On the topic of locking we are currently making progress in Jackalope, so I hope we can address the concerns soon.

Related Entries:
- Touring North America
- Doctrine PHPCR-ODM now handles versioning
- News for the symfony2 cmf: Second PHPCR implementation, hackday announcement and PHPCR to become "official"
- Multilanguage support for Doctrine PHPCR-ODM
- How to manage patching a github hosted repo

Comments [0]

What Liip did after winning the IKS semantics UX contest

As you maybe have heard, we won the IKS Semantics UX contest a few weeks ago (see this and this for more details). We of course we're not lazy in the meantime and integrated Apache Stanbol into this editor.

Loïc wrote a little summary on the IKS blog what we already did and what's coming in January 2012. Go and read it here. The most important part: There's a hackaton for merging all this with the excellent CreateJS (which was on Hacker News lately) from 5 to 7 January at our Liip office in Zurich. You're all welcome to take part, more details and subscription at lanyrd.com/2012/createjs-hackathon/

Related Entries:
- Symfony CMF hackday october 22nd in Cologne
- A frontend editor for Symfony2 CMF with the help of VIE
- Progress on PHPCR with a hackday
- Doctrine PHPCR-ODM now handles versioning
- Multilanguage support for Doctrine PHPCR-ODM

Comments [0]

Next1-10/100