diff --git a/src/main/java/org/olat/admin/user/UserSearchFlexiTableModel.java b/src/main/java/org/olat/admin/user/UserSearchFlexiTableModel.java
index 50faaf416619ea4b49713064bdd8fbc6d2df4e93..31c4231fffc7b9ef90cc5e8b9462f27d270248d4 100644
--- a/src/main/java/org/olat/admin/user/UserSearchFlexiTableModel.java
+++ b/src/main/java/org/olat/admin/user/UserSearchFlexiTableModel.java
@@ -23,7 +23,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
-import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModel;
 import org.olat.core.gui.components.table.DefaultTableDataModel;
@@ -57,11 +56,6 @@ public class UserSearchFlexiTableModel extends DefaultTableDataModel<UserResultW
 	public void setTableColumnModel(FlexiTableColumnModel tableColumnModel) {
 		columnModel = tableColumnModel;
 	}
-	
-	@Override
-	public void load(int firstResult, int maxResults, SortKey... sortedCol) {
-		//already loaded
-	}
 
 	@Override
 	public int getColumnCount() {
diff --git a/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java b/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java
index c4a8b5d646eef2d7565c471c82163d8e4c44c3ba..c7259871af876fa7f61210489ca0ec54e3eca4f2 100644
--- a/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java
+++ b/src/main/java/org/olat/admin/user/groups/GroupOverviewController.java
@@ -29,7 +29,6 @@ import org.olat.NewControllerFactory;
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.core.CoreSpringFactory;
-import org.olat.core.commons.persistence.DBFactory;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.link.Link;
diff --git a/src/main/java/org/olat/admin/user/groups/GroupSearchController.java b/src/main/java/org/olat/admin/user/groups/GroupSearchController.java
index bee65b41739d2e4b93c88aa8b8dadfe4bb192524..897eaf068c0c82b2adc716bce94ab81c962a86f7 100644
--- a/src/main/java/org/olat/admin/user/groups/GroupSearchController.java
+++ b/src/main/java/org/olat/admin/user/groups/GroupSearchController.java
@@ -25,7 +25,6 @@ import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.persistence.PersistenceHelper;
-import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
@@ -360,11 +359,6 @@ public class GroupSearchController extends StepFormBasicController {
 		public void setTableColumnModel(FlexiTableColumnModel tableColumnModel) {
 			columnModel = tableColumnModel;
 		}
-		
-		@Override
-		public void load(int firstResult, int maxResults, SortKey... orderBy) {
-			//already loaded
-		}
 
 		@Override
 		public int getColumnCount() {
diff --git a/src/main/java/org/olat/core/commons/persistence/DefaultResultInfos.java b/src/main/java/org/olat/core/commons/persistence/DefaultResultInfos.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c91f1430a57ebad26339105bba8efc327e026be
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/persistence/DefaultResultInfos.java
@@ -0,0 +1,64 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.core.commons.persistence;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * 
+ * Initial date: 04.03.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class DefaultResultInfos<U> implements ResultInfos<U> {
+
+	private int nextFirstResult;
+	private int correctedRowCount;
+	private List<U> objects;
+	
+	
+	public DefaultResultInfos() {
+		nextFirstResult = 0;
+		correctedRowCount = 0;
+		objects = new ArrayList<U>();
+	}
+	
+	public DefaultResultInfos(int nextFirstResult, int correctedRowCount, List<U> objects) {
+		this.nextFirstResult = nextFirstResult;
+		this.correctedRowCount = correctedRowCount;
+		this.objects = objects;
+	}
+
+	@Override
+	public int getNextFirstResult() {
+		return nextFirstResult;
+	}
+
+	@Override
+	public int getCorrectedRowCount() {
+		return correctedRowCount;
+	}
+
+	public List<U> getObjects() {
+		return objects;
+	}
+}
diff --git a/src/main/java/org/olat/core/commons/persistence/ResultInfos.java b/src/main/java/org/olat/core/commons/persistence/ResultInfos.java
new file mode 100644
index 0000000000000000000000000000000000000000..96d7d275bed26bb5014e333c0b6489f350291296
--- /dev/null
+++ b/src/main/java/org/olat/core/commons/persistence/ResultInfos.java
@@ -0,0 +1,39 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.core.commons.persistence;
+
+import java.util.List;
+
+
+/**
+ * 
+ * Initial date: 04.03.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface ResultInfos<U> {
+	
+	public int getNextFirstResult();
+	
+	public int getCorrectedRowCount();
+	
+	public List<U> getObjects();
+
+}
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java b/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java
index 0c2262d0cac46aa85f115e8ac38ff7e7829acdeb..907854a030709ca8ec4a3c853e0b1ba62cb92df4 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java
@@ -67,6 +67,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.TextBoxListEleme
 import org.olat.core.gui.components.form.flexible.impl.elements.TextElementImpl;
 import org.olat.core.gui.components.form.flexible.impl.elements.richText.RichTextElementImpl;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataSource;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableElementImpl;
 import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.components.tree.TreeModel;
@@ -915,8 +916,8 @@ public class FormUIFactory {
 	}
 	
 	public FlexiTableElement addTableElement(UserRequest ureq, String name, FlexiTableDataModel tableModel,
-			int pageSize, Translator translator, FormItemContainer formLayout) {
-		FlexiTableElementImpl fte = new FlexiTableElementImpl(ureq, name, translator, tableModel, pageSize);
+			FlexiTableDataSource dataSource, int pageSize, boolean search, Translator translator, FormItemContainer formLayout) {
+		FlexiTableElementImpl fte = new FlexiTableElementImpl(ureq, name, translator, tableModel, dataSource, pageSize, search);
 		formLayout.add(fte);
 		return fte;
 	}
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/elements/FlexiTableElement.java b/src/main/java/org/olat/core/gui/components/form/flexible/elements/FlexiTableElement.java
index e1a413318d9c4ef811a82ab2986df3b72dfbdb9e..834b5b7a4e84dff4bed3d0b8cb6409155791b37e 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/elements/FlexiTableElement.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/elements/FlexiTableElement.java
@@ -64,6 +64,12 @@ public interface FlexiTableElement extends FormItem {
 	
 	public boolean isMultiSelectedIndex(int index);
 	
+	/**
+	 * Is a search field enabled
+	 * @return
+	 */
+	public boolean isSearch();
+	
 	
 	/**
 	 * Return the page size
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/AbstractTextElement.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/AbstractTextElement.java
index e3e76fb9396097aee505449258dcf4d23dc220c3..1ce86a2488a647d25620af38896741ff725b8a91 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/AbstractTextElement.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/AbstractTextElement.java
@@ -85,9 +85,8 @@ public abstract class AbstractTextElement extends FormItemImpl implements TextEl
 	private boolean originalInitialised=false;
 	
 	
-	@SuppressWarnings("unchecked")
 	@Override
-	public void validate(List validationResults) {
+	public void validate(List<ValidationStatus> validationResults) {
 		if(checkForNotEmpty && !notEmpty()){
 			validationResults.add(new ValidationStatusImpl(ValidationStatus.ERROR));
 			return;
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormButton.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormButton.java
index a8fae56ffbc77aaf7db0b53a5686610815b80717..54d1651aac766c9a405b64ed292465bcbeed38fa 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormButton.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormButton.java
@@ -29,6 +29,7 @@ import java.util.List;
 
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.impl.FormItemImpl;
+import org.olat.core.util.ValidationStatus;
 
 /**
  * @author patrickb
@@ -63,7 +64,7 @@ abstract class FormButton extends FormItemImpl {
 	 * @see org.olat.core.gui.components.form.flexible.impl.FormItemImpl#validate(java.util.List)
 	 */
 	@Override
-	public void validate(List validationResults) {
+	public void validate(List<ValidationStatus> validationResults) {
 		// Buttons do not validate
 	}
 
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormButtonRenderer.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormButtonRenderer.java
index 70bed0a648000f60715f0d157fd62161266e652a..8ed3632246787fc339300238a728df0a67cd758a 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormButtonRenderer.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormButtonRenderer.java
@@ -45,7 +45,7 @@ import org.olat.core.gui.translator.Translator;
  * @author patrickb
  */
 class FormButtonRenderer implements ComponentRenderer {
-	@SuppressWarnings("unused")
+
 	public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator,
 			RenderResult renderResult, String[] args) {
 		//
@@ -88,17 +88,12 @@ class FormButtonRenderer implements ComponentRenderer {
 		}
 	}
 
-	@SuppressWarnings("unused")
 	public void renderBodyOnLoadJSFunctionCall(Renderer renderer, StringOutput sb, Component source, RenderingState rstate) {
-	// TODO Auto-generated method stub
-
+		//
 	}
 
-	@SuppressWarnings("unused")
 	public void renderHeaderIncludes(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator,
 			RenderingState rstate) {
-	// TODO Auto-generated method stub
-
+		//
 	}
-
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormCancel.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormCancel.java
index 93ae80a2b4fc9e45f984198a579dd2f1be196476..05799d2bad17d742f91d1266e86703c50fbc6b7d 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormCancel.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormCancel.java
@@ -36,6 +36,7 @@ import org.olat.core.gui.control.Disposable;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.util.ValidationStatus;
 
 /**
  * Description:<br>
@@ -121,9 +122,8 @@ public class FormCancel extends FormItemImpl implements Disposable, Cancel {
 	/**
 	 * @see org.olat.core.gui.components.form.flexible.impl.FormItemImpl#validate(java.util.List)
 	 */
-	@SuppressWarnings("unchecked")
 	@Override
-	public void validate(List validationResults) {
+	public void validate(List<ValidationStatus> validationResults) {
 		// nothing to do
 	}
 
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormLinkImpl.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormLinkImpl.java
index b4b1a770145a8e4fef9bbc91ad27452cb97d663e..ba1fb2d33c1e1df271fdd694bfbf610fe472c000 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormLinkImpl.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormLinkImpl.java
@@ -36,6 +36,7 @@ import org.olat.core.gui.components.link.FormLinkFactory;
 import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.control.Event;
 import org.olat.core.util.StringHelper;
+import org.olat.core.util.ValidationStatus;
 
 /**
  * Description:<br>
@@ -149,7 +150,7 @@ public class FormLinkImpl extends FormItemImpl implements FormLink {
 	}
 
 	@Override
-	public void validate(List validationResults) {
+	public void validate(List<ValidationStatus> validationResults) {
 		// typically a link does not validate its data
 	}
 	
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormSubmit.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormSubmit.java
index 88fed64bd551851d5ac1e747370a553fff6f0b50..2ae8086bb0477c3c85b8bf50b8951593a0abb162 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormSubmit.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/FormSubmit.java
@@ -33,6 +33,7 @@ import org.olat.core.gui.components.form.flexible.elements.Submit;
 import org.olat.core.gui.components.form.flexible.impl.FormEvent;
 import org.olat.core.logging.AssertException;
 import org.olat.core.util.StringHelper;
+import org.olat.core.util.ValidationStatus;
 
 /**
  * Description:<br>
@@ -82,7 +83,6 @@ public class FormSubmit extends FormButton implements Submit{
 	 *      long[], int)
 	 */
 	@Override
-	@SuppressWarnings("unused")
 	public void evalFormRequest(UserRequest ureq) {
 		// no values with submit to be evaluated
 		getComponent().setDirty(true);
@@ -91,8 +91,7 @@ public class FormSubmit extends FormButton implements Submit{
 	/**
 	 * @see org.olat.core.gui.components.form.flexible.FormComponent#validate()
 	 */
-	@SuppressWarnings("unused")
-	public void validate(List statusDescriptinons) {
+	public void validate(List<ValidationStatus> statusDescriptinons) {
 		// submit is not validating itself
 	}
 
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/AbstractFlexiTableRenderer.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/AbstractFlexiTableRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..af33d85e68dec4cca32d4f4c03aed835b9639634
--- /dev/null
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/AbstractFlexiTableRenderer.java
@@ -0,0 +1,229 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.core.gui.components.form.flexible.impl.elements.table;
+
+import org.olat.core.gui.components.Component;
+import org.olat.core.gui.components.ComponentRenderer;
+import org.olat.core.gui.components.form.flexible.FormItem;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.FormJSHelper;
+import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit;
+import org.olat.core.gui.render.RenderResult;
+import org.olat.core.gui.render.Renderer;
+import org.olat.core.gui.render.RenderingState;
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.gui.render.URLBuilder;
+import org.olat.core.gui.translator.Translator;
+
+/**
+ * 
+ * Initial date: 01.03.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public abstract class AbstractFlexiTableRenderer implements ComponentRenderer {
+
+	@Override
+	public void render(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator,
+			RenderResult renderResult, String[] args) {
+		
+		FlexiTableComponent ftC = (FlexiTableComponent) source;
+		FlexiTableElementImpl ftE = ftC.getFlexiTableElement();
+
+		if(ftE.isSearch()) {
+			renderSearchFields(renderer, sb, ftE, ubu, translator, renderResult, args);
+		}
+		
+		String id = ftC.getFormDispatchId();
+		sb.append("<div class=\"b_table_wrapper b_floatscrollbox\">")
+		  .append("<table id=\"").append(id).append("\">");
+		
+		//render headers
+		renderHeaders(sb, ftE, translator);
+		//render body
+		renderBody(renderer, sb, ftC, ubu, translator, renderResult);
+		
+		sb.append("</table></div>");
+		
+		//source
+		if (source.isEnabled()) {
+			sb.append(FormJSHelper.getJSStartWithVarDeclaration(id));
+			sb.append(FormJSHelper.getSetFlexiFormDirty(ftE.getRootForm(), id));
+			sb.append(FormJSHelper.getJSEnd());
+		}
+	}
+	
+	protected void renderSearchFields(Renderer renderer, StringOutput sb, FlexiTableElementImpl ftE, URLBuilder ubu, Translator translator,
+			RenderResult renderResult, String[] args) {
+		TextElement searchEl = ftE.getSearchElement();
+		if(searchEl != null) {
+			Component searchC = searchEl.getComponent();
+			searchC.getHTMLRendererSingleton().render(renderer, sb, searchC, ubu, translator, renderResult, args);
+		}
+		
+		FormSubmit searchButton = ftE.getSearchButton();
+		if(searchButton != null) {
+			Component searchB = searchButton.getComponent();
+			searchB.getHTMLRendererSingleton().render(renderer, sb, searchB, ubu, translator, renderResult, args);
+		}
+	}
+	
+	protected void renderHeaders(StringOutput target, FlexiTableElementImpl ftE, Translator translator) {
+		FlexiTableDataModel dataModel = ftE.getTableDataModel();
+		FlexiTableColumnModel columnModel = dataModel.getTableColumnModel();
+		      
+		target.append("<thead><tr>");
+
+		int col = 0;
+		if(ftE.isMultiSelect()) {
+			target.append("<th class='b_first_child'>").append("choice").append("</th>");
+			col++;
+		}
+		
+		int cols = columnModel.getColumnCount();
+		for(int i=0; i<cols; i++) {
+			FlexiColumnModel fcm = columnModel.getColumnModel(i);
+			renderHeader(target, fcm, col, cols, translator);
+			col++;
+  	}
+		
+		target.append("</tr></thead>");
+	}
+	
+	protected void renderHeader(StringOutput target, FlexiColumnModel fcm, int colPos, int numOfCols, Translator translator) {
+		String header = translator.translate(fcm.getHeaderKey());	
+		target.append("<th class=\"");
+		// add css class for first and last column to support older browsers
+		if (colPos == 0) target.append(" b_first_child");
+		if (colPos == numOfCols-1) target.append(" b_last_child");
+		target.append("\">").append(header).append("</th>");
+	}
+	
+	protected void renderBody(Renderer renderer, StringOutput target, FlexiTableComponent ftC,
+			URLBuilder ubu, Translator translator, RenderResult renderResult) {
+		
+		String id = ftC.getFormDispatchId();
+		FlexiTableElementImpl ftE = ftC.getFlexiTableElement();
+		FlexiTableDataModel dataModel = ftE.getTableDataModel();
+
+		// build rows
+		target.append("<tbody>");
+		
+		// the really selected rowid (from the tabledatamodel)
+		int firstRow = ftE.getFirstRow();
+		int maxRows = ftE.getMaxRows();
+		int rows = dataModel.getRowCount();
+		int lastRow = Math.min(rows, firstRow + maxRows);
+
+		String rowIdPrefix = "row_" + id + "-";
+		for (int i = firstRow; i < lastRow; i++) {
+			renderRow(renderer, target, ftC, rowIdPrefix,	i, rows, ubu, translator, renderResult);
+		}				
+		// end of table table
+		target.append("</tbody>");
+	}
+	
+	protected void renderRow(Renderer renderer, StringOutput target, FlexiTableComponent ftC, String rowIdPrefix,
+			int row, int rows, URLBuilder ubu, Translator translator, RenderResult renderResult) {
+
+		FlexiTableElementImpl ftE = ftC.getFlexiTableElement();
+		FlexiTableColumnModel columnsModel = ftE.getTableDataModel().getTableColumnModel();
+		int numOfCols = columnsModel.getColumnCount();
+		
+		// use alternating css class
+		String cssClass;
+		if (row % 2 == 0) cssClass = "";
+		else cssClass = "b_table_odd";
+		// add css class for first and last column to support older browsers
+		if (row == 0) cssClass += " b_first_child";
+		if (row == rows-1) cssClass += " b_last_child";
+
+		target.append("<tr id='").append(rowIdPrefix).append(row)
+				  .append("' class=\"").append(cssClass).append("\">");
+				
+		int col = 0;
+		if(ftE.isMultiSelect()) {
+			target.append("<td class='b_first_child'>")
+			      .append("<input type='checkbox' name='ftb_ms' value='").append(rowIdPrefix).append(row).append("'");
+			if(ftE.isMultiSelectedIndex(row)) {
+				target.append(" checked='checked'");
+			}   
+			target.append("/></td>");
+			col++;
+		}
+				
+		for (int j = 0; j<numOfCols; j++) {
+			FlexiColumnModel fcm = columnsModel.getColumnModel(j);
+			renderCell(renderer, target, ftC, fcm, row, col, numOfCols, ubu, translator, renderResult); 
+			col++;
+		}
+		target.append("</tr>");
+	}
+
+	protected void renderCell(Renderer renderer, StringOutput target, FlexiTableComponent ftC, FlexiColumnModel fcm,
+			int row, int col, int numOfCols, URLBuilder ubu, Translator translator, RenderResult renderResult) {
+
+		FlexiTableElementImpl ftE = ftC.getFlexiTableElement();
+		FlexiTableDataModel dataModel = ftE.getTableDataModel();
+
+		int alignment = fcm.getAlignment();
+		String cssClass = (alignment == FlexiColumnModel.ALIGNMENT_LEFT ? "b_align_normal" : (alignment == FlexiColumnModel.ALIGNMENT_RIGHT ? "b_align_inverse" : "b_align_center"));
+		// add css class for first and last column to support older browsers
+		if (col == 0) cssClass += " b_first_child";
+		if (col == numOfCols-1) cssClass += " b_last_child";				
+		target.append("<td class=\"").append(cssClass).append("\">");
+		if (col == 0) target.append("<a name=\"table\"></a>"); //add once for accessabillitykey
+
+		int columnIndex = fcm.getColumnIndex();
+		Object cellValue = columnIndex >= 0 ? 
+				dataModel.getValueAt(row, columnIndex) : null;
+		if (cellValue instanceof FormItem) {
+			FormItem formItem = (FormItem)cellValue;
+			formItem.setTranslator(translator);
+			if(ftE.getRootForm() != formItem.getRootForm()) {
+				formItem.setRootForm(ftE.getRootForm());
+			}
+			ftE.addFormItem(formItem);
+			formItem.getComponent().getHTMLRendererSingleton().render(renderer, target, formItem.getComponent(),
+					ubu, translator, renderResult, null);
+		} else {
+			fcm.getCellRenderer().render(target, cellValue, row, ftC, ubu, translator);
+		}
+		target.append("</td>");
+	}
+
+
+	@Override
+	public void renderHeaderIncludes(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu,
+			Translator translator, RenderingState rstate) {
+		//
+	}
+
+	@Override
+	public void renderBodyOnLoadJSFunctionCall(Renderer renderer, StringOutput sb, Component source,
+			RenderingState rstate) {
+		//
+	}
+	
+	
+
+
+
+}
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiDataTablesRenderer.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiDataTablesRenderer.java
index 4e422a7a3890e8eb02ef3e7bbeba995586ca7d86..db7f62d01fd21d886db9c3f51c2d3bea9136ef15 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiDataTablesRenderer.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiDataTablesRenderer.java
@@ -22,12 +22,10 @@ package org.olat.core.gui.components.form.flexible.impl.elements.table;
 
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.ComponentRenderer;
-import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.impl.Form;
 import org.olat.core.gui.components.form.flexible.impl.FormJSHelper;
 import org.olat.core.gui.render.RenderResult;
 import org.olat.core.gui.render.Renderer;
-import org.olat.core.gui.render.RenderingState;
 import org.olat.core.gui.render.StringOutput;
 import org.olat.core.gui.render.URLBuilder;
 import org.olat.core.gui.translator.Translator;
@@ -36,7 +34,7 @@ import org.olat.core.gui.translator.Translator;
  * Render the table using the jQuery plugin DataTables
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
-class FlexiDataTablesRenderer implements ComponentRenderer {
+class FlexiDataTablesRenderer extends AbstractFlexiTableRenderer implements ComponentRenderer {
 
 	/**
 	 * @see org.olat.core.gui.components.ComponentRenderer#render(org.olat.core.gui.render.Renderer,
@@ -46,10 +44,13 @@ class FlexiDataTablesRenderer implements ComponentRenderer {
 	 *      org.olat.core.gui.translator.Translator,
 	 *      org.olat.core.gui.render.RenderResult, java.lang.String[])
 	 */
+	@Override
 	public void render(Renderer renderer, StringOutput target, Component source,
 			URLBuilder ubu, Translator translator, RenderResult renderResult,
 			String[] args) {
-		//
+
+		super.render(renderer, target, source, ubu, translator, renderResult, args);
+		
 		FlexiTableComponent ftC = (FlexiTableComponent) source;
 		FlexiTableElementImpl ftE = ftC.getFlexiTableElement();
 		FlexiTableDataModel dataModel = ftE.getTableDataModel();
@@ -57,22 +58,6 @@ class FlexiDataTablesRenderer implements ComponentRenderer {
 		
 		Form rootForm = ftE.getRootForm();
 		String id = ftC.getFormDispatchId();
-
-		target.append("<div class=\"b_table_wrapper b_floatscrollbox\">")
-		      .append("<table id=\"").append(id).append("\">");
-		      
-		renderTHead(target, ftE, translator);
-		renderTBody(renderer, target, ftC, ubu, translator, renderResult);
-
-		target.append("</table>")
-		       .append("</div>");
-		
-		if (source.isEnabled()) {
-			target.append(FormJSHelper.getJSStartWithVarDeclaration(id));
-			target.append(FormJSHelper.getSetFlexiFormDirty(ftE.getRootForm(), id));
-			target.append(FormJSHelper.getJSEnd());
-		}
-		
 		int rows = dataModel.getRowCount();
 		
 		target.append("<script>")
@@ -131,140 +116,4 @@ class FlexiDataTablesRenderer implements ComponentRenderer {
       .append("});\n")
 		  .append("</script>\n");
 	}
-	
-	/**
-	 * 
-	 * @param target
-	 * @param ftE
-	 * @param translator
-	 */
-	private void renderTHead(StringOutput target, FlexiTableElementImpl ftE, Translator translator) {
-		FlexiTableDataModel dataModel = ftE.getTableDataModel();
-		FlexiTableColumnModel columnModel = dataModel.getTableColumnModel();
-		      
-		target.append("<thead><tr>");
-
-		int col = 0;
-		if(ftE.isMultiSelect()) {
-			target.append("<th class='b_first_child'>").append("choice").append("</th>");
-			col++;
-		}
-		
-		int cols = columnModel.getColumnCount();
-		for(int i=0; i<cols; i++) {
-			FlexiColumnModel fcm = columnModel.getColumnModel(i);
-			String header = translator.translate(fcm.getHeaderKey());
-				
-			target.append("<th class=\"");
-			// add css class for first and last column to support older browsers
-			if (col == 0) target.append(" b_first_child");
-			if (col == cols-1) target.append(" b_last_child");
-			target.append("\">").append(header).append("</th>");
-			col++;
-  	}
-		target.append("</tr></thead>");
-	}
-	
-	public void renderTBody(Renderer renderer, StringOutput target, FlexiTableComponent ftC,
-			URLBuilder ubu, Translator translator, RenderResult renderResult) {
-		
-		String id = ftC.getFormDispatchId();
-		FlexiTableElementImpl ftE = ftC.getFlexiTableElement();
-		FlexiTableDataModel dataModel = ftE.getTableDataModel();
-		FlexiTableColumnModel columnsModel = dataModel.getTableColumnModel();
-
-		// build rows
-		target.append("<tbody>");
-		
-		// the really selected rowid (from the tabledatamodel)
-		int firstRow = ftE.getFirstRow();
-		int maxRows = ftE.getMaxRows();
-		int rows = dataModel.getRowCount();
-		int lastRow = Math.min(rows, firstRow + maxRows);
-		int cols = columnsModel.getColumnCount();
-		
-		String rowIdPrefix = "row_" + id + "-";
-		dataModel.load(firstRow, maxRows);//load needed rows
-		for (int i = firstRow; i < lastRow; i++) {
-			// use alternating css class
-			String cssClass;
-			if (i % 2 == 0) cssClass = "";
-			else cssClass = "b_table_odd";
-			// add css class for first and last column to support older browsers
-			if (i == 0) cssClass += " b_first_child";
-			if (i == rows-1) cssClass += " b_last_child";
-
-			target.append("<tr id='").append(rowIdPrefix).append(i)
-			      .append("' class=\"").append(cssClass).append("\">");
-			
-			int col = 0;
-			if(ftE.isMultiSelect()) {
-				target.append("<td class='b_first_child'>")
-				      .append("<input type='checkbox' name='ftb_ms' value='").append(rowIdPrefix).append(i).append("'");
-				if(ftE.isMultiSelectedIndex(i)) {
-					target.append(" checked='checked'");
-				}   
-				target.append("/></td>");
-				col++;
-			}
-			
-			for (int j = 0; j < cols; j++) {
-				FlexiColumnModel fcm = columnsModel.getColumnModel(j);
-				int alignment = fcm.getAlignment();
-				cssClass = (alignment == FlexiColumnModel.ALIGNMENT_LEFT ? "b_align_normal" : (alignment == FlexiColumnModel.ALIGNMENT_RIGHT ? "b_align_inverse" : "b_align_center"));
-				// add css class for first and last column to support older browsers
-				if (col == 0) cssClass += " b_first_child";
-				if (col == cols-1) cssClass += " b_last_child";				
-				target.append("<td class=\"").append(cssClass).append("\">");
-				if (col == 0) target.append("<a name=\"table\"></a>"); //add once for accessabillitykey
-				
-				
-				int columnIndex = fcm.getColumnIndex();
-				Object cellValue = columnIndex >= 0 ? 
-						dataModel.getValueAt(i, columnIndex) : null;
-				if (cellValue instanceof FormItem) {
-					FormItem formItem = (FormItem)cellValue;
-					formItem.setTranslator(translator);
-					if(ftE.getRootForm() != formItem.getRootForm()) {
-						formItem.setRootForm(ftE.getRootForm());
-					}
-					ftE.addFormItem(formItem);
-					formItem.getComponent().getHTMLRendererSingleton().render(renderer, target, formItem.getComponent(),
-							ubu, translator, renderResult, null);
-				} else {
-					fcm.getCellRenderer().render(target, cellValue, i, ftC, ubu, translator);
-				}
-				target.append("</td>");
-			}
-			target.append("</tr>");
-			
-		}				
-		// end of table table
-		target.append("</tbody>");
-	}
-
-	/**
-	 * @see org.olat.core.gui.components.ComponentRenderer#renderBodyOnLoadJSFunctionCall(org.olat.core.gui.render.Renderer,
-	 *      org.olat.core.gui.render.StringOutput,
-	 *      org.olat.core.gui.components.Component,
-	 *      org.olat.core.gui.render.RenderingState)
-	 */
-	public void renderBodyOnLoadJSFunctionCall(Renderer renderer,
-			StringOutput sb, Component source, RenderingState rstate) {
-		//
-	}
-
-	/**
-	 * @see org.olat.core.gui.components.ComponentRenderer#renderHeaderIncludes(org.olat.core.gui.render.Renderer,
-	 *      org.olat.core.gui.render.StringOutput,
-	 *      org.olat.core.gui.components.Component,
-	 *      org.olat.core.gui.render.URLBuilder,
-	 *      org.olat.core.gui.translator.Translator,
-	 *      org.olat.core.gui.render.RenderingState)
-	 */
-	public void renderHeaderIncludes(Renderer renderer, StringOutput sb,
-			Component source, URLBuilder ubu, Translator translator,
-			RenderingState rstate) {
-		//
-	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableClassicRenderer.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableClassicRenderer.java
index c67e7224d0f9c7749f39a2e49c6cae00b27b7e1d..6df2e9c3a4d26b42923debbbef23b4476b340851 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableClassicRenderer.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableClassicRenderer.java
@@ -28,11 +28,8 @@ package org.olat.core.gui.components.form.flexible.impl.elements.table;
 
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.ComponentRenderer;
-import org.olat.core.gui.components.form.flexible.FormItem;
-import org.olat.core.gui.components.form.flexible.impl.FormJSHelper;
 import org.olat.core.gui.render.RenderResult;
 import org.olat.core.gui.render.Renderer;
-import org.olat.core.gui.render.RenderingState;
 import org.olat.core.gui.render.StringOutput;
 import org.olat.core.gui.render.URLBuilder;
 import org.olat.core.gui.translator.Translator;
@@ -41,97 +38,11 @@ import org.olat.core.gui.translator.Translator;
  * Render the table as a long HTML table
  * @author Christian Guretzki
  */
-class FlexiTableClassicRenderer implements ComponentRenderer {
+class FlexiTableClassicRenderer extends AbstractFlexiTableRenderer implements ComponentRenderer {
 
-	public void render(Renderer renderer, StringOutput target, Component source,
-			URLBuilder ubu, Translator translator, RenderResult renderResult,
-			String[] args) {
-		//
-		FlexiTableComponent ftC = (FlexiTableComponent) source;
-		FlexiTableElementImpl ftE = ftC.getFlexiTableElement();
-
-		String id = ftC.getFormDispatchId();
-		target.append("<div class=\"b_table_wrapper b_floatscrollbox\">")
-		  .append("<table id=\"").append(id).append("\">");
-		
-		FlexiTableDataModel dataModel = ftE.getTableDataModel();
-		FlexiTableColumnModel columnsModel = dataModel.getTableColumnModel();
-
-		int rows = dataModel.getRowCount();
-		int cols = columnsModel.getColumnCount();
-
-		// 1. build header links
-		target.append("<thead><tr>");
-
-		for (int i = 0; i < cols; i++) {
-				FlexiColumnModel fcm = columnsModel.getColumnModel(i);
-				String header = translator.translate(fcm.getHeaderKey());
-
-				target.append("<th class=\"");
-				// add css class for first and last column to support older browsers
-				if (i == 0) target.append(" b_first_child");
-				if (i == cols-1) target.append(" b_last_child");
-				target.append("\">").append(header).append("</th>");
-  	}
-		target.append("</tr></thead>");
-
-		// build rows
-		target.append("<tbody>");
-		// the really selected rowid (from the tabledatamodel)
-
-		dataModel.load(0, rows);
-		for (int i = 0; i < rows; i++) {
-			// use alternating css class
-			String cssClass;
-			if (i % 2 == 0) cssClass = "";
-			else cssClass = "b_table_odd";
-			// add css class for first and last column to support older browsers
-			if (i == 0) cssClass += " b_first_child";
-			if (i == rows-1) cssClass += " b_last_child";
-
-			target.append("<tr class=\"").append(cssClass).append("\">");
-			for (int j = 0; j < cols; j++) {
-				FlexiColumnModel fcm = columnsModel.getColumnModel(j);
-				int alignment = fcm.getAlignment();
-				cssClass = (alignment == FlexiColumnModel.ALIGNMENT_LEFT ? "b_align_normal" : (alignment == FlexiColumnModel.ALIGNMENT_RIGHT ? "b_align_inverse" : "b_align_center"));
-				// add css class for first and last column to support older browsers
-				if (j == 0) cssClass += " b_first_child";
-				if (j == cols-1) cssClass += " b_last_child";				
-				target.append("<td class=\"").append(cssClass).append("\">");
-				if (j == 0) target.append("<a name=\"table\"></a>"); //add once for accessabillitykey
-				
-				int columnIndex = fcm.getColumnIndex();
-				Object cellValue = columnIndex >= 0 ? dataModel.getValueAt(i, columnIndex) : null;
-				if (cellValue instanceof FormItem) {
-					FormItem formItem = (FormItem)cellValue;
-					formItem.setTranslator(translator);
-					formItem.setRootForm(ftE.getRootForm());
-					formItem.getComponent().getHTMLRendererSingleton().render(renderer, target, formItem.getComponent(), ubu, translator, renderResult, args);
-				} else {
-					fcm.getCellRenderer().render(target, cellValue, i, ftC, ubu, translator);
-				}
-				target.append("</td>");
-			}
-			target.append("</tr>");
-			
-		}				
-		// end of table table
-		target.append("</tbody></table>");
-		target.append("</div>");
-		
-		if (source.isEnabled()) {
-			// add set dirty form only if enabled
-			target.append(FormJSHelper.getJSStartWithVarDeclaration(id));
-			target.append(FormJSHelper.getSetFlexiFormDirty(ftE.getRootForm(), id));
-			target.append(FormJSHelper.getJSEnd());
-		}
-	}
-
-	public void renderBodyOnLoadJSFunctionCall(Renderer renderer, StringOutput sb, Component source, RenderingState rstate) {
-		// 
-	}
-
-	public void renderHeaderIncludes(Renderer renderer, StringOutput sb, Component source, URLBuilder ubu, Translator translator, RenderingState rstate) {
-		//
+	@Override
+	public void render(Renderer renderer, StringOutput target, Component source, URLBuilder ubu,
+			Translator translator, RenderResult renderResult, String[] args) {
+		super.render(renderer, target, source, ubu, translator, renderResult, args);
 	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableColumnModelImpl.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableColumnModelImpl.java
index ee3a344bdac3b1b92d8e5096287db70b6bd181e4..2a32e5f7f5b9b685fdd01ca9ff9f121e24fb07ba 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableColumnModelImpl.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableColumnModelImpl.java
@@ -35,7 +35,7 @@ import java.util.List;
  */
 public class FlexiTableColumnModelImpl implements FlexiTableColumnModel {
 
-	List<FlexiColumnModel> columnModelList = new ArrayList<FlexiColumnModel>();
+	private final List<FlexiColumnModel> columnModelList = new ArrayList<FlexiColumnModel>();
 	
 	/**
 	 * @return
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataModel.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataModel.java
index c687835c3f3a5217862a96b0c60f8af6d1a64ed5..1d0a2c08537557a0f8adc44c30b8ae36e50d1eec 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataModel.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataModel.java
@@ -26,7 +26,6 @@
 
 package org.olat.core.gui.components.form.flexible.impl.elements.table;
 
-import org.olat.core.commons.persistence.SortKey;
 
 /**
  * Interface for table data model including column models.
@@ -39,12 +38,6 @@ public interface FlexiTableDataModel {
 	 */
 	public int getRowCount();
 	
-	/**
-	 * Load the rows needed for paging
-	 * @param firstResult
-	 * @param maxResults
-	 */
-	public void load(int firstResult, int maxResults, SortKey... orderBy);
 
 	/**
 	 * Return Object for certain table cell.
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataModelImpl.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataModelImpl.java
index 549c65433fe524bb056ee928cb05ddbcab04ab6d..eb9b881cb70a828a9de65348052be35fd916b622 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataModelImpl.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataModelImpl.java
@@ -26,7 +26,6 @@
 
 package org.olat.core.gui.components.form.flexible.impl.elements.table;
 
-import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.table.TableDataModel;
 
 /**
@@ -59,11 +58,6 @@ public class FlexiTableDataModelImpl<T> implements FlexiTableDataModel {
 		return tableModel.getObject(row);
 	}
 
-	@Override
-	public void load(int firstResult, int maxResults, SortKey... orderby) {
-		//already loaded
-	}
-
 	/**
 	 * Return Object for certain table cell.
 	 * @param row Row number [0...row]
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataSource.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataSource.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a1d6bf0c9a30fb13d6155bfe8ed00461053eaff
--- /dev/null
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableDataSource.java
@@ -0,0 +1,51 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.core.gui.components.form.flexible.impl.elements.table;
+
+import java.util.List;
+
+import org.olat.core.commons.persistence.ResultInfos;
+import org.olat.core.commons.persistence.SortKey;
+
+/**
+ * 
+ * Initial date: 01.03.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface FlexiTableDataSource<U> {
+	
+	
+	/**
+	 * Load the rows needed for paging
+	 * @param firstResult
+	 * @param maxResults
+	 */
+	public ResultInfos<U> load(int firstResult, int maxResults, SortKey... orderBy);
+	
+	/**
+	 * 
+	 * @param firstResult
+	 * @param maxResults
+	 * @param orderBy
+	 */
+	public ResultInfos<U> search(String query, List<String> addQueries, int firstResult, int maxResults, SortKey... orderBy);
+
+}
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableElementImpl.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableElementImpl.java
index ab54b2d7967f8aa1b18cbc565a2c051a45e81a08..5bb57c7192b74bf9d1c8efdab000e870c5e9acf7 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableElementImpl.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableElementImpl.java
@@ -38,8 +38,11 @@ import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemCollection;
 import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
 import org.olat.core.gui.components.form.flexible.impl.FormEvent;
 import org.olat.core.gui.components.form.flexible.impl.FormItemImpl;
+import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit;
+import org.olat.core.gui.components.form.flexible.impl.elements.TextElementImpl;
 import org.olat.core.gui.translator.Translator;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.ValidationStatus;
@@ -56,9 +59,17 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 	private FlexiTableRendererType rendererType = FlexiTableRendererType.classic;
 	
 	private int rowCount = -1;
+	
+	private int currentPage;
+	private int currentFirstResult;
 	private int pageSize;
+	private boolean searchField;
+	
+	private TextElement searchFieldEl;
+	private FormSubmit searchButton;
 	
-	private FlexiTableDataModel tableModel;
+	private FlexiTableDataModel dataModel;
+	private FlexiTableDataSource<?> dataSource;
 	private FlexiTableComponent component;
 	private String mapperUrl;
 	
@@ -66,24 +77,45 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 	private Map<String,FormItem> components = new HashMap<String,FormItem>();
 	
 	public FlexiTableElementImpl(UserRequest ureq, String name, FlexiTableDataModel tableModel) {
-		this(ureq, name, null, tableModel, -1);
+		this(ureq, name, null, tableModel, null, -1, false);
 	}
+	
 	public FlexiTableElementImpl(UserRequest ureq, String name, Translator translator, FlexiTableDataModel tableModel) {
-		this(ureq, name, translator, tableModel, -1);
+		this(ureq, name, translator, tableModel, null, -1, false);
 	}
 	
-	public FlexiTableElementImpl(UserRequest ureq, String name, Translator translator, FlexiTableDataModel tableModel, int pageSize) {
+	public FlexiTableElementImpl(UserRequest ureq, String name, Translator translator,
+			FlexiTableDataModel tableModel, FlexiTableDataSource<?> dataSource, int pageSize, boolean searchField) {
 		super(name);
-		this.tableModel = tableModel;
+		
+		this.dataModel = tableModel;
+		this.dataSource = dataSource;
 		component = new FlexiTableComponent(this, translator);
 		
+		
 		MapperService mapper = CoreSpringFactory.getImpl(MapperService.class);
 		mapperUrl = mapper.register(ureq.getUserSession(), new FlexiTableModelMapper(component));
 		
+		this.searchField = searchField;
+		if(searchField) {
+			String dispatchId = component.getDispatchID();
+			searchFieldEl = new TextElementImpl(dispatchId + "_searchField", "search", "");
+			searchFieldEl.showLabel(false);
+			components.put("rSearch", searchFieldEl);
+			searchButton = new FormSubmit(dispatchId + "_searchButton", "rSearchButton", "search");
+			searchButton.setTranslator(translator);
+			components.put("rSearchB", searchButton);
+		}
+		
 		this.pageSize = pageSize;
 		if(pageSize > 0) {
 			setPage(0);
 		}
+		
+		if(dataSource != null) {
+			//preload it
+			dataSource.load(0, pageSize);
+		}
 	}
 
 	public FlexiTableRendererType getRendererType() {
@@ -103,7 +135,24 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 	public void setMultiSelect(boolean multiSelect) {
 		this.multiSelect = multiSelect;
 	}
+
+	@Override
+	public boolean isSearch() {
+		return searchField;
+	}
+	
+	public String getSearchText() {
+		return searchFieldEl == null ? null : searchFieldEl.getValue();
+	}
+	
+	public TextElement getSearchElement() {
+		return searchFieldEl;
+	}
 	
+	public FormSubmit getSearchButton() {
+		return searchButton;
+	}
+
 	@Override
 	public int getPageSize() {
 		return pageSize;
@@ -114,9 +163,22 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 		this.pageSize = pageSize;
 	}
 	
+	public int getPage() {
+		return currentPage;
+	}
+	
 	@Override
 	public void setPage(int page) {
-		tableModel.load(0, getPageSize());
+		currentPage = page;//TODO
+		//tableModel.load(0, getPageSize());
+	}
+
+	public int getCurrentFirstResult() {
+		return currentFirstResult;
+	}
+
+	public void setCurrentFirstResult(int currentFirstResult) {
+		this.currentFirstResult = currentFirstResult;
 	}
 
 	public String getMapperUrl() {
@@ -136,7 +198,19 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 	protected void addFormItem(FormItem item) {
 		components.put(item.getName(), item);
 	}
+	
+	
 
+	@Override
+	public void doDispatchFormRequest(UserRequest ureq) {
+		super.doDispatchFormRequest(ureq);
+	}
+	
+	@Override
+	protected void dispatchFormRequest(UserRequest ureq) {
+		super.dispatchFormRequest(ureq);
+	}
+	
 	/**
 	 * @see org.olat.core.gui.components.form.flexible.FormItemImpl#evalFormRequest(org.olat.core.gui.UserRequest)
 	 */
@@ -148,16 +222,26 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 		}
 
 		String selectedIndex = getRootForm().getRequestParameter("rSelect");
+		String dispatchuri = getRootForm().getRequestParameter("dispatchuri");
 		if(StringHelper.containsNonWhitespace(selectedIndex)) {
 			int index = selectedIndex.lastIndexOf('-');
 			if(index > 0 && index+1 < selectedIndex.length()) {
 				String pos = selectedIndex.substring(index+1);
 				doSelect(ureq, Integer.parseInt(pos));
 			}
+		} else if(searchButton != null
+				&& searchButton.getFormDispatchId().equals(dispatchuri)) {
+			//snap the request
+			searchFieldEl.evalFormRequest(ureq);
+			String search = searchFieldEl.getValue();
+			if(StringHelper.containsNonWhitespace(search)) {
+				doSearch(ureq, search);
+			} else {
+				doResetSearch(ureq);
+			}
 		} else {
-			
 			boolean actionEvent = false;
-			FlexiTableColumnModel colModel = tableModel.getTableColumnModel();
+			FlexiTableColumnModel colModel = dataModel.getTableColumnModel();
 			for(int i=colModel.getColumnCount(); i-->0; ) {
 				FlexiColumnModel col = colModel.getColumnModel(i);
 				if(col.getAction() != null) {
@@ -189,6 +273,20 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 		getRootForm().fireFormEvent(ureq, new SelectionEvent(action, index, this, FormEvent.ONCLICK));
 	}
 	
+	protected void doSearch(UserRequest ureq, String search) {
+		if(dataSource != null) {
+			resetInternComponts();
+			dataSource.search(search, null, 0, getPageSize());
+		}
+	}
+	
+	protected void doResetSearch(UserRequest ureq) {
+		if(dataSource != null) {
+			resetInternComponts();
+			dataSource.load(0, getPageSize());
+		}
+	}
+	
 	public Set<Integer> getMultiSelectedIndex() {
 		return multiSelectedIndex == null ? Collections.<Integer>emptySet() : multiSelectedIndex;
 	}
@@ -223,15 +321,32 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 	@Override
 	public void validate(List<ValidationStatus> validationResults) {
 		//static text must not validate
+		if(searchFieldEl != null) {
+			searchFieldEl.validate(validationResults);
+		}
+		if(searchButton != null) {
+			searchButton.validate(validationResults);
+		}
 	}
 
 	@Override
 	public void reset() {
+		resetInternComponts();
+		reloadData();
+	}
+	
+	private void resetInternComponts() {
 		rowCount = -1;
 		component.setDirty(true);
 		multiSelectedIndex = null;
 	}
 	
+	private void reloadData() {
+		if(dataSource != null) {
+			dataSource.load(0, getPageSize());//reload needed rows
+		}
+	}
+	
 	/**
 	 * Prevent parent to be set as dirty for every request
 	 */
@@ -243,6 +358,12 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 	@Override
 	protected void rootFormAvailable() {
 		//root form not interesting for Static text
+		if(searchFieldEl != null && searchFieldEl.getRootForm() != getRootForm()) {
+			searchFieldEl.setRootForm(getRootForm());
+		}
+		if(searchButton != null && searchButton.getRootForm() != getRootForm()) {
+			searchButton.setRootForm(getRootForm());
+		}
 	}
 
 	protected FlexiTableComponent getFormItemComponent() {
@@ -251,7 +372,7 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 	
 	public int getRowCount() {
 		if(rowCount < 0) {
-			rowCount = tableModel.getRowCount();
+			rowCount = dataModel.getRowCount();
 		}
 		return rowCount;
 	}
@@ -268,6 +389,10 @@ public class FlexiTableElementImpl extends FormItemImpl implements FlexiTableEle
 	}
 	
 	public FlexiTableDataModel getTableDataModel() {
-		return tableModel;
+		return dataModel;
+	}
+	
+	public FlexiTableDataSource<?> getTableDataSource() {
+		return dataSource;
 	}
 }
diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableModelMapper.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableModelMapper.java
index 43b1ac877e59ec4347e0f219322bdc5caa700483..fc23f3c8d5c1523d28f082caee0ce21964cc9b45 100644
--- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableModelMapper.java
+++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/table/FlexiTableModelMapper.java
@@ -25,6 +25,7 @@ import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.dispatcher.mapper.Mapper;
 import org.olat.core.gui.Windows;
@@ -79,6 +80,7 @@ public class FlexiTableModelMapper implements Mapper {
 
 		if(StringHelper.isLong(firstRowStr) && StringHelper.isLong(maxRowStr)) {
 			FlexiTableDataModel dataModel = ftE.getTableDataModel();
+			FlexiTableDataSource<?> dataSource = ftE.getTableDataSource();
 			FlexiTableColumnModel columnsModel = dataModel.getTableColumnModel();
 			
 			SortKey orderBy = null;
@@ -104,8 +106,14 @@ public class FlexiTableModelMapper implements Mapper {
 				int maxRows = Integer.parseInt(maxRowStr);
 				int lastRow = Math.min(rows, firstRow + maxRows);
 				//paged loading
-				dataModel.load(firstRow, maxRows, orderBy);
-
+				ResultInfos<?> results;
+				if(StringHelper.containsNonWhitespace(ftE.getSearchText())) {
+					results = dataSource.search(ftE.getSearchText(), null, firstRow, maxRows, orderBy);
+				} else {
+					results = dataSource.load(firstRow, maxRows, orderBy);
+				}
+				ftE.setCurrentFirstResult(results.getNextFirstResult());
+				
 				for (int i = firstRow; i < lastRow; i++) {
 					JSONObject row = new JSONObject();
 
diff --git a/src/main/java/org/olat/course/groupsandrights/GroupsAndRightsController.java b/src/main/java/org/olat/course/groupsandrights/GroupsAndRightsController.java
index 84d0fdb971b143409752a043fb6553ad50001294..7b221d00f9dc0c0fd87a90ca85a4ac887b12b49e 100644
--- a/src/main/java/org/olat/course/groupsandrights/GroupsAndRightsController.java
+++ b/src/main/java/org/olat/course/groupsandrights/GroupsAndRightsController.java
@@ -27,7 +27,6 @@ import java.util.Map;
 import java.util.UUID;
 
 import org.olat.core.CoreSpringFactory;
-import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
@@ -268,11 +267,6 @@ public class GroupsAndRightsController extends FormBasicController {
 			columnModel = tableColumnModel;
 		}
 
-		@Override
-		public void load(int firstResult, int maxResults, SortKey... sortedCol) {
-			//already loaded
-		}
-
 		@Override
 		public int getColumnCount() {
 			return columnModel.getColumnCount();
diff --git a/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewDataModel.java b/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewDataModel.java
index 917fca8dbf278c8ab907841b32733af9f4c04699..95caece510b40f42e7225d29b077a3df5932706f 100644
--- a/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewDataModel.java
+++ b/src/main/java/org/olat/course/member/wizard/ImportMemberOverviewDataModel.java
@@ -23,7 +23,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
-import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModel;
 import org.olat.core.gui.components.table.DefaultTableDataModel;
@@ -63,11 +62,6 @@ public class ImportMemberOverviewDataModel extends DefaultTableDataModel<Identit
 	public int getColumnCount() {
 		return columnModel.getColumnCount();
 	}
-	
-	@Override
-	public void load(int firstResult, int maxResults, SortKey... orderBy) {
-		//already loaded
-	}
 
 	@Override
 	public Object getValueAt(int row, int col) {
diff --git a/src/main/java/org/olat/group/ui/main/EditMembershipController.java b/src/main/java/org/olat/group/ui/main/EditMembershipController.java
index b926a687a95675d38c1de20f99db70be365c97a3..29fabcefaff19b4c9abb0659766147dc7cf8e3f6 100644
--- a/src/main/java/org/olat/group/ui/main/EditMembershipController.java
+++ b/src/main/java/org/olat/group/ui/main/EditMembershipController.java
@@ -28,7 +28,6 @@ import java.util.UUID;
 
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.persistence.PersistenceHelper;
-import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
 import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
@@ -378,11 +377,6 @@ public class EditMembershipController extends FormBasicController {
 			columnModel = tableColumnModel;
 		}
 
-		@Override
-		public void load(int firstResult, int maxResults, SortKey... sortedCol) {
-			//already loaded
-		}
-
 		@Override
 		public int getColumnCount() {
 			return columnModel.getColumnCount();
diff --git a/src/main/java/org/olat/modules/qpool/QuestionItem.java b/src/main/java/org/olat/modules/qpool/QuestionItem.java
index ed6057940ed0c17e2f2d70069d647777f7912b57..7da9cc7a83017d7a47d9bb72e6349c6feabe12f3 100644
--- a/src/main/java/org/olat/modules/qpool/QuestionItem.java
+++ b/src/main/java/org/olat/modules/qpool/QuestionItem.java
@@ -42,6 +42,18 @@ public interface QuestionItem extends QuestionItemShort {
 	public String getEditor();
 	
 	public String getItemVersion();
+	
+	/**
+	 * Field can be lazy loaded
+	 * @return
+	 */
+	public String getStudyFieldPath();
+	
+	/**
+	 * Field can be lazy loaded
+	 * @return
+	 */
+	public String getStudyFieldName();
 
 
 }
diff --git a/src/main/java/org/olat/modules/qpool/QuestionPoolService.java b/src/main/java/org/olat/modules/qpool/QuestionPoolService.java
index dcb24f125f7b88b30865a1bf44628afa8239dec2..f3e9abac24ad9e4c7d4906ea5f72911295beef31 100644
--- a/src/main/java/org/olat/modules/qpool/QuestionPoolService.java
+++ b/src/main/java/org/olat/modules/qpool/QuestionPoolService.java
@@ -22,11 +22,13 @@ package org.olat.modules.qpool;
 import java.io.File;
 import java.util.List;
 
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.group.BusinessGroup;
+import org.olat.modules.qpool.model.SearchQuestionItemParams;
 import org.olat.resource.OLATResource;
 
 /**
@@ -41,6 +43,8 @@ public interface QuestionPoolService {
 	
 	public void deleteItems(List<QuestionItem> items);
 	
+	public List<Identity> getAuthors(QuestionItem item);
+	
 	public void addAuthors(List<Identity> authors, List<QuestionItem> items);
 	
 	public QuestionItem importItem(Identity owner, String filename, File file);
@@ -49,10 +53,12 @@ public interface QuestionPoolService {
 	
 	public VFSContainer getRootDirectory(QuestionItem item);
 	
+	public QuestionItem updateItem(QuestionItem item);
+	
 
 	public int countItems(Identity author);
 
-	public List<QuestionItem> getItems(Identity author, int firstResult, int maxResults, SortKey... orderBy);
+	public ResultInfos<QuestionItem> getItems(Identity author, SearchQuestionItemParams params, int firstResult, int maxResults, SortKey... orderBy);
 
 	public List<QuestionItem> getAllItems(int firstResult, int maxResults);
 	
@@ -63,7 +69,7 @@ public interface QuestionPoolService {
 	
 	public int getNumOfItemsInPool(Pool pool);
 	
-	public List<QuestionItem> getItemsOfPool(Pool pool, int firstResult, int maxResults, SortKey... orderBy);
+	public ResultInfos<QuestionItem> getItemsOfPool(Pool pool, SearchQuestionItemParams params, int firstResult, int maxResults, SortKey... orderBy);
 	
 	public void addItemToPool(QuestionItem item, Pool pool);
 	
@@ -71,7 +77,7 @@ public interface QuestionPoolService {
 	//favorit
 	public int getNumOfFavoritItems(Identity identity);
 	
-	public List<QuestionItem> getFavoritItems(Identity identity, int firstResult, int maxResults, SortKey... orderBy);
+	public ResultInfos<QuestionItem> getFavoritItems(Identity identity, SearchQuestionItemParams params, int firstResult, int maxResults, SortKey... orderBy);
 	
 	
 	
@@ -82,8 +88,7 @@ public interface QuestionPoolService {
 	
 	public int countSharedItemByResource(OLATResource resource);
 	
-	public List<QuestionItem> getSharedItemByResource(OLATResource resource, int firstResult, int maxResults, SortKey... orderBy);
-		
+	public ResultInfos<QuestionItem> getSharedItemByResource(OLATResource resource, SearchQuestionItemParams params, int firstResult, int maxResults, SortKey... orderBy);
 	
 	//list
 	public QuestionItemCollection createCollection(Identity owner, String collectionName, List<QuestionItem> initialItems);
@@ -94,7 +99,7 @@ public interface QuestionPoolService {
 	
 	public int countItemsOfCollection(QuestionItemCollection collection);
 	
-	public List<QuestionItem> getItemsOfCollection(QuestionItemCollection collection, int firstResult, int maxResults, SortKey... orderBy);
+	public ResultInfos<QuestionItem> getItemsOfCollection(QuestionItemCollection collection, SearchQuestionItemParams params, int firstResult, int maxResults, SortKey... orderBy);
 
 	
 	//study field admin
@@ -110,7 +115,7 @@ public interface QuestionPoolService {
 	
 	public int countPools();
 
-	public List<Pool> getPools(int firstResult, int maxResults, SortKey... orderBy);
+	public ResultInfos<Pool> getPools(int firstResult, int maxResults, SortKey... orderBy);
 	
 	
 }
diff --git a/src/main/java/org/olat/modules/qpool/StudyField.java b/src/main/java/org/olat/modules/qpool/StudyField.java
index 6ae0a75546090066b0ce27c286d37416fddfea47..d6532c288f2df709813fa112431a9815cb6584b5 100644
--- a/src/main/java/org/olat/modules/qpool/StudyField.java
+++ b/src/main/java/org/olat/modules/qpool/StudyField.java
@@ -38,4 +38,8 @@ public interface StudyField {
 	public String getField();
 	
 	public StudyField getParentField();
+	
+	public String getMaterializedPathKeys();
+
+	public String getMaterializedPathNames();
 }
diff --git a/src/main/java/org/olat/modules/qpool/manager/CollectionDAO.java b/src/main/java/org/olat/modules/qpool/manager/CollectionDAO.java
index 8e47f2accb123c51d3a6c5af6406c8ed7177837d..b925a68360e72c3dec6f6b19e017b4394fd0438d 100644
--- a/src/main/java/org/olat/modules/qpool/manager/CollectionDAO.java
+++ b/src/main/java/org/olat/modules/qpool/manager/CollectionDAO.java
@@ -86,7 +86,7 @@ public class CollectionDAO {
 		dbInstance.commit();
 	}
 	
-	public boolean isInCollection(QuestionItemCollection collection, QuestionItemImpl item) {
+	protected boolean isInCollection(QuestionItemCollection collection, QuestionItemImpl item) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select count(coll2item) from qcollection2item coll2item where coll2item.collection.key=:collectionKey and coll2item.item.key=:itemKey");
 		Number count = dbInstance.getCurrentEntityManager()
@@ -107,14 +107,24 @@ public class CollectionDAO {
 				.getSingleResult().intValue();
 	}
 	
-	public List<QuestionItem> getItemsOfCollection(QuestionItemCollection collection, int firstResult, int maxResults, SortKey... orderBy) {
+	public List<QuestionItem> getItemsOfCollection(QuestionItemCollection collection, List<Long> inKeys,
+			int firstResult, int maxResults, SortKey... orderBy) {
 		StringBuilder sb = new StringBuilder();
-		sb.append("select coll2item.item from qcollection2item coll2item where coll2item.collection.key=:collectionKey");
+		sb.append("select item from qcollection2item coll2item ")
+		  .append(" inner join coll2item.item item ")
+		  .append(" left join fetch item.studyField studyField ")
+		  .append(" where coll2item.collection.key=:collectionKey");
+		if(inKeys != null && !inKeys.isEmpty()) {
+			sb.append(" and item.key in (:itemKeys)");
+		}
 		PersistenceHelper.appendGroupBy(sb, "coll2item.item", orderBy);
 		
 		TypedQuery<QuestionItem> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), QuestionItem.class)
 				.setParameter("collectionKey", collection.getKey());
+		if(inKeys != null && !inKeys.isEmpty()) {
+			query.setParameter("itemKeys", inKeys);
+		}
 		if(firstResult >= 0) {
 			query.setFirstResult(firstResult);
 		}
@@ -124,6 +134,16 @@ public class CollectionDAO {
 		return query.getResultList();
 	}
 	
+	public List<Long> getItemKeysOfCollection(QuestionItemCollection collection) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select distinct(coll2item.item.key) from qcollection2item coll2item ")
+		  .append(" where coll2item.collection.key=:collectionKey");
+		TypedQuery<Long> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Long.class)
+				.setParameter("collectionKey", collection.getKey());
+		return query.getResultList();
+	}
+	
 	public List<QuestionItemCollection> getCollections(Identity me) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select coll from qcollection coll where coll.owner.key=:identityKey");
diff --git a/src/main/java/org/olat/modules/qpool/manager/NullPoolService.java b/src/main/java/org/olat/modules/qpool/manager/NullPoolService.java
index 6bacf5973ce0ece5ef21440556606543abe3e4d1..3ac325488772d3667360f1dcfca847ea38e5534b 100644
--- a/src/main/java/org/olat/modules/qpool/manager/NullPoolService.java
+++ b/src/main/java/org/olat/modules/qpool/manager/NullPoolService.java
@@ -53,7 +53,7 @@ public class NullPoolService implements ApplicationListener<ContextRefreshedEven
 	
 	@Override
 	public void onApplicationEvent(ContextRefreshedEvent event) {
-		int numOfPools = poolDao.getNumOfPools();
+		int numOfPools = poolDao.countPools();
 		if(numOfPools == 0) {
 			//create a pool if there isn't any
 			createPools();
diff --git a/src/main/java/org/olat/modules/qpool/manager/PoolDAO.java b/src/main/java/org/olat/modules/qpool/manager/PoolDAO.java
index 8f241341c59c5efd0225ed138f07e7cad65adef9..a344459d712301c25cb4fb7d78236871f9049bbc 100644
--- a/src/main/java/org/olat/modules/qpool/manager/PoolDAO.java
+++ b/src/main/java/org/olat/modules/qpool/manager/PoolDAO.java
@@ -96,14 +96,6 @@ public class PoolDAO {
 		dbInstance.getCurrentEntityManager().remove(poolRef);
 	}
 	
-	public int getNumOfPools() {
-		StringBuilder sb = new StringBuilder();
-		sb.append("select count(pool) from qpool pool");
-		return dbInstance.getCurrentEntityManager()
-				.createQuery(sb.toString(), Number.class)
-				.getSingleResult().intValue();
-	}
-	
 	public int countPools() {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select count(pool) from qpool pool");
@@ -127,6 +119,18 @@ public class PoolDAO {
 		return query.getResultList();
 	}
 	
+	public List<Pool> getPools(QuestionItem item) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select distinct(pool) from qpool2item pool2item")
+		  .append(" inner join pool2item.pool pool")
+		  .append(" where pool2item.item.key=:itemKey");
+		
+		TypedQuery<Pool> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Pool.class)
+				.setParameter("itemKey", item.getKey());
+		return query.getResultList();
+	}
+	
 	public void addItemToPool(QuestionItem item, Pool pool) {
 		QuestionItem lockedItem = questionItemDao.loadForUpdate(item.getKey());
 		if(!isInPool(lockedItem, pool)) {
@@ -139,7 +143,7 @@ public class PoolDAO {
 		dbInstance.commit();//release lock asap
 	}
 	
-	public boolean isInPool(QuestionItem item, Pool pool) {
+	protected boolean isInPool(QuestionItem item, Pool pool) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select count(pool2item.item) from qpool2item pool2item where pool2item.pool.key=:poolKey and pool2item.item.key=:itemKey");
 		Number count = dbInstance.getCurrentEntityManager()
@@ -159,14 +163,24 @@ public class PoolDAO {
 				.getSingleResult().intValue();
 	}
 	
-	public List<QuestionItem> getItemsOfPool(Pool pool, int firstResult, int maxResults, SortKey... orderBy) {
+	public List<QuestionItem> getItemsOfPool(Pool pool, List<Long> inKeys, int firstResult, int maxResults, SortKey... orderBy) {
 		StringBuilder sb = new StringBuilder();
-		sb.append("select pool2item.item from qpool2item pool2item where pool2item.pool.key=:poolKey");
+		sb.append("select item from qpool2item pool2item")
+		  .append(" inner join pool2item.item item ")
+		  .append(" left join fetch item.studyField studyField ")
+		  .append(" where pool2item.pool.key=:poolKey");
+		if(inKeys != null && inKeys.size() > 0) {
+			sb.append(" and item.key in (:inKeys)");
+		}
+		
 		PersistenceHelper.appendGroupBy(sb, "pool2item.item", orderBy);
 		
 		TypedQuery<QuestionItem> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), QuestionItem.class)
 				.setParameter("poolKey", pool.getKey());
+		if(inKeys != null && inKeys.size() > 0) {
+			query.setParameter("inKeys", inKeys);
+		}
 		if(firstResult >= 0) {
 			query.setFirstResult(firstResult);
 		}
diff --git a/src/main/java/org/olat/modules/qpool/manager/QuestionItemDAO.java b/src/main/java/org/olat/modules/qpool/manager/QuestionItemDAO.java
index 48c275365305edb8a403f72b28e1b349b1b1c39b..887b534accdc98d20130f13ce301b494ab3bdb56 100644
--- a/src/main/java/org/olat/modules/qpool/manager/QuestionItemDAO.java
+++ b/src/main/java/org/olat/modules/qpool/manager/QuestionItemDAO.java
@@ -71,8 +71,7 @@ public class QuestionItemDAO {
 	public QuestionItem create(Identity owner, String subject, String format, String language,
 			StudyField field, String uuid, String rootFilename, QuestionType type) {
 		QuestionItemImpl item = new QuestionItemImpl();
-		
-		
+
 		item.setUuid(uuid);
 		item.setCreationDate(new Date());
 		item.setLastModified(new Date());
@@ -110,6 +109,10 @@ public class QuestionItemDAO {
 		return sb.toString();
 	}
 	
+	public QuestionItem merge(QuestionItem item) {
+		return dbInstance.getCurrentEntityManager().merge(item);
+	}
+	
 	public void addAuthors(List<Identity> authors, QuestionItem item) {
 		QuestionItemImpl lockedItem = loadForUpdate(item.getKey());
 		SecurityGroup secGroup = lockedItem.getOwnerGroup();
@@ -135,18 +138,25 @@ public class QuestionItemDAO {
 				.getSingleResult().intValue();
 	}
 	
-	public List<QuestionItem> getItems(Identity me, int firstResult, int maxResults, SortKey... orderBy) {
+	public List<QuestionItem> getItems(Identity me, List<Long> inKeys, int firstResult, int maxResults, SortKey... orderBy) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select item from questionitem item")
 		  .append(" inner join item.ownerGroup ownerGroup ")
+		  .append(" left join fetch item.studyField studyField ")
 		  .append(" where ownerGroup in (")
 		  .append("   select vmember.securityGroup from ").append(SecurityGroupMembershipImpl.class.getName()).append(" as vmember ")
 		  .append("     where vmember.identity.key=:identityKey and vmember.securityGroup=ownerGroup")
 		  .append(" )");
+		if(inKeys != null && !inKeys.isEmpty()) {
+			sb.append(" and item.key in (:itemKeys)");
+		}
 
 		TypedQuery<QuestionItem> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), QuestionItem.class)
 				.setParameter("identityKey", me.getKey());
+		if(inKeys != null && !inKeys.isEmpty()) {
+			query.setParameter("itemKeys", inKeys);
+		}
 		if(firstResult >= 0) {
 			query.setFirstResult(firstResult);
 		}
@@ -225,16 +235,33 @@ public class QuestionItemDAO {
 				.getSingleResult().intValue();
 	}
 	
-	public List<QuestionItem> getFavoritItems(Identity identity, int firstResult, int maxResults) {
+	public List<Long> getFavoritKeys(Identity identity) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select distinct(mark.resId) from ").append(MarkImpl.class.getName()).append(" mark ")
+		  .append(" where mark.creator.key=:identityKey and mark.resName='QuestionItem'");
+		TypedQuery<Long> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Long.class)
+				.setParameter("identityKey", identity.getKey());
+		return query.getResultList();
+	}
+	
+	public List<QuestionItem> getFavoritItems(Identity identity, List<Long> inKeys, int firstResult, int maxResults) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select item from questionitem item")
+		  .append(" left join fetch item.studyField studyField")
 		  .append(" where item.key in (")
 		  .append("   select mark.resId from ").append(MarkImpl.class.getName()).append(" mark where mark.creator.key=:identityKey and mark.resName='QuestionItem'")
 		  .append(" )");
+		if(inKeys != null && !inKeys.isEmpty()) {
+			sb.append(" and item.key in (:itemKeys)");
+		}
 
 		TypedQuery<QuestionItem> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), QuestionItem.class)
 				.setParameter("identityKey", identity.getKey());
+		if(inKeys != null && !inKeys.isEmpty()) {
+			query.setParameter("itemKeys", inKeys);
+		}
 		if(firstResult >= 0) {
 			query.setFirstResult(firstResult);
 		}
@@ -297,14 +324,23 @@ public class QuestionItemDAO {
 		return count.intValue();
 	}
 	
-	public List<QuestionItem> getSharedItemByResource(OLATResource resource, int firstResult, int maxResults, SortKey... orderBy) {
+	public List<QuestionItem> getSharedItemByResource(OLATResource resource, List<Long> inKeys,
+			int firstResult, int maxResults, SortKey... orderBy) {
 		StringBuilder sb = new StringBuilder();
-		sb.append("select share.item from qshareitem share")
+		sb.append("select item from qshareitem share")
+		  .append(" inner join share.item item")
+		  .append(" left join fetch item.studyField studyField")
 		  .append(" where share.resource.key=:resourceKey");
+		if(inKeys != null && !inKeys.isEmpty()) {
+			sb.append(" and item.key in (:itemKeys)");
+		}
 
 		TypedQuery<QuestionItem> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), QuestionItem.class)
 				.setParameter("resourceKey", resource.getKey());
+		if(inKeys != null && !inKeys.isEmpty()) {
+			query.setParameter("itemKeys", inKeys);
+		}
 		if(firstResult >= 0) {
 			query.setFirstResult(firstResult);
 		}
@@ -334,6 +370,18 @@ public class QuestionItemDAO {
 		return query.getResultList();
 	}
 	
+	public List<OLATResource> getSharedResources(QuestionItem item) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select resource from qshareitem share")
+		  .append(" inner join share.resource resource")
+		  .append(" where share.item.key=:itemKey");
+
+		TypedQuery<OLATResource> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), OLATResource.class)
+				.setParameter("itemKey", item.getKey());
+		return query.getResultList();
+	}
+	
 	public int deleteFromShares(List<QuestionItem> items) {
 		List<Long> keys = new ArrayList<Long>();
 		for(QuestionItem item:items) {
@@ -346,6 +394,4 @@ public class QuestionItemDAO {
 				.setParameter("itemKeys", keys)
 				.executeUpdate();
 	}
-	
-	
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/modules/qpool/manager/QuestionItemDocumentFactory.java b/src/main/java/org/olat/modules/qpool/manager/QuestionItemDocumentFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..64fc3601f0e1b147e1aae0675c32f8e7393ba2b9
--- /dev/null
+++ b/src/main/java/org/olat/modules/qpool/manager/QuestionItemDocumentFactory.java
@@ -0,0 +1,137 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.qpool.manager;
+
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.document.TextField;
+import org.olat.basesecurity.BaseSecurity;
+import org.olat.core.id.Identity;
+import org.olat.core.id.User;
+import org.olat.core.id.UserConstants;
+import org.olat.core.util.StringHelper;
+import org.olat.modules.qpool.Pool;
+import org.olat.modules.qpool.QuestionItem;
+import org.olat.modules.qpool.QuestionPoolService;
+import org.olat.modules.qpool.model.QuestionItemDocument;
+import org.olat.resource.OLATResource;
+import org.olat.search.model.OlatDocument;
+import org.olat.search.service.SearchResourceContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 
+ * Initial date: 28.02.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Service("questionItemDocumentFactory")
+public class QuestionItemDocumentFactory {
+
+	@Autowired
+	private PoolDAO poolDao;
+	@Autowired
+	private BaseSecurity securityManager;
+	@Autowired
+	private QuestionItemDAO questionItemDao;
+	@Autowired
+	private QuestionPoolService qpoolService;
+
+
+	public Document createDocument(SearchResourceContext searchResourceContext, Long itemKey) {
+		QuestionItem item = questionItemDao.loadById(itemKey);
+		if(item != null) {
+			return createDocument(searchResourceContext, item);
+		}
+		return null;
+	}
+
+	public Document createDocument(SearchResourceContext searchResourceContext, QuestionItem item) {	
+		
+		OlatDocument oDocument = new OlatDocument();
+		oDocument.setId(item.getKey());
+		oDocument.setCreatedDate(item.getCreationDate());
+		oDocument.setTitle(item.getSubject());
+		oDocument.setDescription(item.getDescription());
+		oDocument.setResourceUrl("[QuestionItem:" + item.getKey() + "]");
+		oDocument.setDocumentType(QuestionItemDocument.TYPE);
+		oDocument.setCssIcon("o_qitem_icon");
+		oDocument.setParentContextType(searchResourceContext.getParentContextType());
+		oDocument.setParentContextName(searchResourceContext.getParentContextName());
+		oDocument.setContent(item.getDescription());
+		//author
+		StringBuilder authorSb = new StringBuilder();
+		List<Identity> owners = qpoolService.getAuthors(item);
+		for(Identity owner:owners) {
+			User user = owner.getUser();
+			authorSb.append(user.getProperty(UserConstants.FIRSTNAME, null))
+			  .append(" ")
+			  .append(user.getProperty(UserConstants.LASTNAME, null))
+			  .append(" ");
+		}
+		oDocument.setAuthor(authorSb.toString());
+
+		//save owners key
+		Document document = oDocument.getLuceneDocument();
+		for(Identity owner:owners) {
+			document.add(new StringField(QuestionItemDocument.OWNER_FIELD, owner.getKey().toString(), Field.Store.NO));
+		}
+		
+		//link resources
+		List<OLATResource> resources = questionItemDao.getSharedResources(item);
+		for(OLATResource resource:resources) {
+			document.add(new StringField(QuestionItemDocument.SHARE_FIELD, resource.getKey().toString(), Field.Store.NO));
+		}
+		
+		//need pools
+		List<Pool> pools = poolDao.getPools(item);
+		for(Pool pool:pools) {
+			document.add(new StringField(QuestionItemDocument.POOL_FIELD, pool.getKey().toString(), Field.Store.NO));
+		}
+
+		//need path
+		String path = item.getStudyFieldPath();
+		if(StringHelper.containsNonWhitespace(path)) {
+			for(StringTokenizer tokenizer = new StringTokenizer(path, "/"); tokenizer.hasMoreTokens(); ) {
+				String nextToken = tokenizer.nextToken();
+				document.add(new TextField(QuestionItemDocument.STUDY_FIELD, nextToken, Field.Store.NO));
+			}
+		}
+		return document;
+	}
+	
+	/**
+	 * indexed and tokenized
+	 * @param fieldName
+	 * @param content
+	 * @param boost
+	 * @return
+	 */
+	protected Field createTextField(String fieldName, String content, float boost) {
+		TextField field = new TextField(fieldName,content, Field.Store.YES);
+		field.setBoost(boost);
+		return field;
+	}
+}
diff --git a/src/main/java/org/olat/modules/qpool/manager/QuestionPoolServiceImpl.java b/src/main/java/org/olat/modules/qpool/manager/QuestionPoolServiceImpl.java
index 2bca0b4dd3223d42c73bda3b3d68141d0cb60a9b..9b84e007005e85df654ebbfe601f91c12d0c018e 100644
--- a/src/main/java/org/olat/modules/qpool/manager/QuestionPoolServiceImpl.java
+++ b/src/main/java/org/olat/modules/qpool/manager/QuestionPoolServiceImpl.java
@@ -32,10 +32,13 @@ import java.util.UUID;
 import org.apache.commons.io.IOUtils;
 import org.olat.basesecurity.BaseSecurity;
 import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.persistence.DefaultResultInfos;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
+import org.olat.core.util.StringHelper;
 import org.olat.core.util.ZipUtil;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSItem;
@@ -49,8 +52,13 @@ import org.olat.modules.qpool.QuestionPoolSPI;
 import org.olat.modules.qpool.QuestionPoolService;
 import org.olat.modules.qpool.StudyField;
 import org.olat.modules.qpool.model.PoolImpl;
+import org.olat.modules.qpool.model.QuestionItemDocument;
 import org.olat.modules.qpool.model.QuestionItemImpl;
+import org.olat.modules.qpool.model.SearchQuestionItemParams;
 import org.olat.resource.OLATResource;
+import org.olat.search.model.AbstractOlatDocument;
+import org.olat.search.service.indexer.LifeFullIndexer;
+import org.olat.search.service.searcher.SearchClient;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -79,12 +87,19 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 	private QuestionPoolModule qpoolModule;
 	@Autowired
 	private BaseSecurity securityManager;
+	@Autowired
+	private SearchClient searchClient;
+	@Autowired
+	private LifeFullIndexer lifeIndexer;
 	
 
 	@Override
 	public String getMateriliazedPathOfStudyFields(QuestionItem item) {
-		QuestionItemImpl reloadedItem = (QuestionItemImpl)questionItemDao.loadById(item.getKey());
-		return studyFieldDao.getMaterializedPath(reloadedItem.getStudyField());
+		QuestionItemImpl reloadedItem = questionItemDao.loadById(item.getKey());
+		if(reloadedItem.getStudyField() == null) {
+			return "";
+		}
+		return reloadedItem.getStudyField().getMaterializedPathNames();
 	}
 
 	@Override
@@ -110,6 +125,28 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 			questionItemDao.addAuthors(authors, item);
 		}
 	}
+	
+	@Override
+	public List<Identity> getAuthors(QuestionItem item) {
+		QuestionItemImpl itemImpl;
+		if(item instanceof QuestionItemImpl) {
+			itemImpl = (QuestionItemImpl)item;
+		} else {
+			itemImpl = questionItemDao.loadById(item.getKey());
+		}
+		return securityManager.getIdentitiesOfSecurityGroup(itemImpl.getOwnerGroup());
+	}
+	
+	public QuestionItem getItem() {
+		return null;
+	}
+	
+	public QuestionItem updateItem(QuestionItem item) {
+		QuestionItem mergedItem = questionItemDao.merge(item);
+		dbInstance.commit();//
+		lifeIndexer.indexDocument(QuestionItemDocument.TYPE, mergedItem.getKey());
+		return mergedItem;
+	}
 
 	@Override
 	public QuestionItem importItem(Identity owner, String filename, File file) {
@@ -224,8 +261,31 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 	}
 
 	@Override
-	public List<QuestionItem> getItems(Identity author, int firstResult, int maxResults, SortKey... orderBy) {
-		return questionItemDao.getItems(author, firstResult, maxResults, orderBy);
+	public ResultInfos<QuestionItem> getItems(Identity author, SearchQuestionItemParams searchParams, int firstResult, int maxResults, SortKey... orderBy) {
+		if(searchParams != null && StringHelper.containsNonWhitespace(searchParams.getSearchString())) {
+			try {
+				String queryString = searchParams.getSearchString();
+				List<String> condQueries = new ArrayList<String>();
+				condQueries.add(QuestionItemDocument.OWNER_FIELD + ":" + author.getKey());
+				List<Long> results = searchClient.doSearch(queryString, condQueries,
+						searchParams.getIdentity(), searchParams.getRoles(), firstResult, 10000, orderBy);
+
+				int initialResultsSize = results.size();
+				if(results.isEmpty()) {
+					return new DefaultResultInfos<QuestionItem>();
+				} else if(results.size() > maxResults) {
+					results = results.subList(0, Math.min(results.size(), maxResults * 2));
+				}
+				List<QuestionItem> items = questionItemDao.getItems(author, results, firstResult, maxResults, orderBy);
+				return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), firstResult + initialResultsSize, items);
+			} catch (Exception e) {
+				log.error("", e);
+			}
+			return new DefaultResultInfos<QuestionItem>();
+		} else {
+			List<QuestionItem> items = questionItemDao.getItems(author, null, firstResult, maxResults, orderBy);
+			return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), -1, items);
+		}
 	}
 
 	@Override
@@ -244,8 +304,33 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 	}
 
 	@Override
-	public List<QuestionItem> getItemsOfPool(Pool pool, int firstResult, int maxResults, SortKey... orderBy) {
-		return poolDao.getItemsOfPool(pool, firstResult, maxResults, orderBy);
+	public ResultInfos<QuestionItem> getItemsOfPool(Pool pool, SearchQuestionItemParams searchParams,
+			int firstResult, int maxResults, SortKey... orderBy) {
+		
+		if(searchParams != null && StringHelper.containsNonWhitespace(searchParams.getSearchString())) {
+			try {
+				String queryString = searchParams.getSearchString();
+				List<String> condQueries = new ArrayList<String>();
+				condQueries.add("pool:" + pool.getKey());
+				List<Long> results = searchClient.doSearch(queryString, condQueries,
+						searchParams.getIdentity(), searchParams.getRoles(), firstResult, 10000, orderBy);
+
+				int initialResultsSize = results.size();
+				if(results.isEmpty()) {
+					return new DefaultResultInfos<QuestionItem>();
+				} else if(results.size() > maxResults) {
+					results = results.subList(0, Math.min(results.size(), maxResults * 2));
+				}
+				List<QuestionItem> items = poolDao.getItemsOfPool(pool, results, 0, maxResults, orderBy);
+				return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), firstResult + initialResultsSize, items);
+			} catch (Exception e) {
+				log.error("", e);
+			}
+			return new DefaultResultInfos<QuestionItem>();
+		} else {
+			List<QuestionItem> items = poolDao.getItemsOfPool(pool, null, firstResult, maxResults, orderBy);
+			return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), -1, items);
+		}
 	}
 
 	@Override
@@ -259,8 +344,43 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 	}
 
 	@Override
-	public List<QuestionItem> getFavoritItems(Identity identity, int firstResult, int maxResults, SortKey... orderBy) {
-		return questionItemDao.getFavoritItems(identity, firstResult, maxResults);
+	public ResultInfos<QuestionItem> getFavoritItems(Identity identity, SearchQuestionItemParams searchParams,
+			int firstResult, int maxResults, SortKey... orderBy) {
+		
+		if(searchParams != null && StringHelper.containsNonWhitespace(searchParams.getSearchString())) {
+			try {
+				//filter with all favorits
+				List<Long> favoritKeys = questionItemDao.getFavoritKeys(identity);
+
+				String queryString = searchParams.getSearchString();
+				List<String> condQueries = new ArrayList<String>();
+				condQueries.add(getDbKeyConditionalQuery(favoritKeys));
+				List<Long> results = searchClient.doSearch(queryString, condQueries,
+						searchParams.getIdentity(), searchParams.getRoles(), firstResult, maxResults * 5, orderBy);
+
+				if(results.isEmpty()) {
+					return new DefaultResultInfos<QuestionItem>();
+				}
+				List<QuestionItem> items = questionItemDao.getFavoritItems(identity, results, firstResult, maxResults);
+				return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), firstResult + results.size(), items);
+			} catch (Exception e) {
+				log.error("", e);
+			}
+			return new DefaultResultInfos<QuestionItem>();
+		} else {
+			List<QuestionItem> items = questionItemDao.getFavoritItems(identity, null, firstResult, maxResults);
+			return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), -1, items);
+		}
+	}
+	
+	private String getDbKeyConditionalQuery(List<Long> keys) {
+		StringBuilder sb = new StringBuilder();
+		sb.append(AbstractOlatDocument.DB_ID_NAME).append(":(");
+		for(Long key:keys) {
+			if(sb.length() > 9) sb.append(" ");
+			sb.append(key);
+		}
+		return sb.append(')').toString();
 	}
 
 	@Override
@@ -290,9 +410,33 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 	}
 
 	@Override
-	public List<QuestionItem> getSharedItemByResource(OLATResource resource,
+	public ResultInfos<QuestionItem> getSharedItemByResource(OLATResource resource, SearchQuestionItemParams searchParams,
 			int firstResult, int maxResults, SortKey... orderBy) {
-		return questionItemDao.getSharedItemByResource(resource, firstResult, maxResults, orderBy);
+		
+		if(searchParams != null && StringHelper.containsNonWhitespace(searchParams.getSearchString())) {
+			try {
+				String queryString = searchParams.getSearchString();
+				List<String> condQueries = new ArrayList<String>();
+				condQueries.add(QuestionItemDocument.SHARE_FIELD + ":" + resource.getKey());
+				List<Long> results = searchClient.doSearch(queryString, condQueries,
+						searchParams.getIdentity(), searchParams.getRoles(), firstResult, maxResults * 5, orderBy);
+
+				int initialResultsSize = results.size();
+				if(results.isEmpty()) {
+					return new DefaultResultInfos<QuestionItem>();
+				} else if(results.size() > maxResults) {
+					results = results.subList(0, Math.min(results.size(), maxResults * 2));
+				}
+				List<QuestionItem> items = questionItemDao.getSharedItemByResource(resource, results, firstResult, maxResults);
+				return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), firstResult + initialResultsSize, items);
+			} catch (Exception e) {
+				log.error("", e);
+			}
+			return new DefaultResultInfos<QuestionItem>();
+		} else {
+			List<QuestionItem> items = questionItemDao.getSharedItemByResource(resource, null, firstResult, maxResults, orderBy);
+			return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), -1, items);
+		}
 	}
 
 	@Override
@@ -320,9 +464,32 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 	}
 
 	@Override
-	public List<QuestionItem> getItemsOfCollection(QuestionItemCollection collection, int firstResult,
-			int maxResults, SortKey... orderBy) {
-		return collectionDao.getItemsOfCollection(collection, firstResult, maxResults, orderBy);
+	public ResultInfos<QuestionItem> getItemsOfCollection(QuestionItemCollection collection, SearchQuestionItemParams searchParams,
+			int firstResult, int maxResults, SortKey... orderBy) {
+		
+		if(searchParams != null && StringHelper.containsNonWhitespace(searchParams.getSearchString())) {
+			try {
+				List<Long> content = collectionDao.getItemKeysOfCollection(collection);
+
+				String queryString = searchParams.getSearchString();
+				List<String> condQueries = new ArrayList<String>();
+				condQueries.add(getDbKeyConditionalQuery(content));
+				List<Long> results = searchClient.doSearch(queryString, condQueries, searchParams.getIdentity(), searchParams.getRoles(),
+						firstResult, maxResults * 5, orderBy);
+
+				if(results.isEmpty()) {
+					return new DefaultResultInfos<QuestionItem>();
+				}
+				List<QuestionItem> items = collectionDao.getItemsOfCollection(collection, results, firstResult, maxResults, orderBy);
+				return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), firstResult + results.size(), items);
+			} catch (Exception e) {
+				log.error("", e);
+			}
+			return new DefaultResultInfos<QuestionItem>();
+		} else {
+			List<QuestionItem> items = collectionDao.getItemsOfCollection(collection, null, firstResult, maxResults, orderBy);
+			return new DefaultResultInfos<QuestionItem>(firstResult + items.size(), -1, items);
+		}
 	}
 
 	@Override
@@ -347,8 +514,9 @@ public class QuestionPoolServiceImpl implements QuestionPoolService {
 	}
 
 	@Override
-	public List<Pool> getPools(int firstResult, int maxResults, SortKey... orderBy) {
-		return poolDao.getPools(firstResult, maxResults);
+	public ResultInfos<Pool> getPools(int firstResult, int maxResults, SortKey... orderBy) {
+		List<Pool> pools = poolDao.getPools(firstResult, maxResults);
+		return new DefaultResultInfos<Pool>(firstResult + pools.size(), -1, pools);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/qpool/manager/StudyFieldDAO.java b/src/main/java/org/olat/modules/qpool/manager/StudyFieldDAO.java
index 51b55392caa72fc19af81e290bc4237fb60038bc..189b8296fa3379dc6eda098115e8c3135b20bc66 100644
--- a/src/main/java/org/olat/modules/qpool/manager/StudyFieldDAO.java
+++ b/src/main/java/org/olat/modules/qpool/manager/StudyFieldDAO.java
@@ -19,7 +19,6 @@
  */
 package org.olat.modules.qpool.manager;
 
-import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
@@ -29,8 +28,6 @@ import org.olat.modules.qpool.model.StudyFieldImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import edu.emory.mathcs.backport.java.util.Collections;
-
 /**
  * 
  * Initial date: 20.02.2013<br>
@@ -48,16 +45,57 @@ public class StudyFieldDAO {
 		newStudyField.setCreationDate(new Date());
 		newStudyField.setLastModified(new Date());
 		newStudyField.setField(field);
-		newStudyField.setParentField(parentField);
+		if(parentField != null) {
+			newStudyField.setParentField(parentField);
+			
+			String parentPathOfKeys = parentField.getMaterializedPathKeys();
+			if(parentPathOfKeys == null) {
+				parentPathOfKeys = "";
+			}
+			String parentPathOfNames = parentField.getMaterializedPathNames();
+			if(parentPathOfNames == null) {
+				parentPathOfNames = "";
+			}
+
+			newStudyField.setMaterializedPathKeys(parentPathOfKeys + "/" + parentField.getKey());
+			newStudyField.setMaterializedPathNames(parentPathOfNames + "/" + parentField.getField());
+		}
 		dbInstance.getCurrentEntityManager().persist(newStudyField);
 		return newStudyField;
 	}
-	
+
 	public StudyField update(String name, StudyField field) {
 		StudyField reloadedField = loadStudyFieldById(field.getKey());
+		String path = reloadedField.getMaterializedPathNames() + "/" + reloadedField.getField();
+		String newPath = reloadedField.getMaterializedPathNames() + "/" + name;
+
 		((StudyFieldImpl)reloadedField).setField(name);
-		return dbInstance.getCurrentEntityManager().merge(reloadedField);
+		StudyField mergedField = dbInstance.getCurrentEntityManager().merge(reloadedField);
+		List<StudyField> descendants = getDescendants(mergedField);
+		
+		for(StudyField descendant:descendants) {
+			String descendantPath = descendant.getMaterializedPathNames();
+			if(descendantPath.indexOf(path) == 0) {
+				String end = descendantPath.substring(path.length(), descendantPath.length());
+				String updatedPath = newPath + end;
+				((StudyFieldImpl)descendant).setMaterializedPathNames(updatedPath);
+			}
+			dbInstance.getCurrentEntityManager().merge(descendant);
+		}
+		return mergedField;
+	}
+	
+	public List<StudyField> getDescendants(StudyField field) {
+		String path = field.getMaterializedPathKeys() + "/" + field.getKey();
+		StringBuilder sb = new StringBuilder();
+		sb.append("select f from qstudyfield f ")
+		  .append(" where f.materializedPathKeys like :path");
+		return dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), StudyField.class)
+				.setParameter("path", path + "%")
+				.getResultList();
 	}
+
 	
 	public StudyField loadStudyFieldById(Long key) {
 		List<StudyField> fields = dbInstance.getCurrentEntityManager()
@@ -86,45 +124,4 @@ public class StudyFieldDAO {
 				.setParameter("parentKey", parent.getKey())
 				.getResultList();
 	}
-
-	public String getMaterializedPath(StudyField field) {
-		if(field == null) {
-			return "";
-		}
-		
-		List<StudyField> parentLine = new ArrayList<StudyField>();
-		
-		StringBuilder sb = new StringBuilder();
-		sb.append("select f from qstudyfield f ")
-		  .append(" left join fetch f.parentField pf ")
-		  .append(" left join fetch pf.parentField ppf ")
-		  .append(" left join fetch ppf.parentField pppf ")
-		  .append(" where f.key=:key");
-
-		StudyFieldImpl fetchedField = dbInstance.getCurrentEntityManager()
-				.createQuery(sb.toString(), StudyFieldImpl.class)
-				.setParameter("key", field.getKey())
-				.getSingleResult();
-		
-		parentLine.add(fetchedField);
-		if(fetchedField.getParentField() != null) {
-			StudyFieldImpl pf = (StudyFieldImpl)fetchedField.getParentField();
-			parentLine.add(pf);
-			if(pf.getParentField() != null) {
-				StudyFieldImpl ppf = (StudyFieldImpl)pf.getParentField();
-				parentLine.add(ppf);
-				if(ppf.getParentField() != null) {
-					StudyFieldImpl pppf = (StudyFieldImpl)ppf.getParentField();
-					parentLine.add(pppf);
-				}
-			}
-		}
-		
-		Collections.reverse(parentLine);
-		StringBuilder path = new StringBuilder();
-		for(StudyField f:parentLine) {
-			path.append("/").append(f.getField());
-		}
-		return path.toString();
-	}
 }
diff --git a/src/main/java/org/olat/modules/qpool/model/QuestionItemDocument.java b/src/main/java/org/olat/modules/qpool/model/QuestionItemDocument.java
new file mode 100644
index 0000000000000000000000000000000000000000..3112479ebad8ebe142b5936c286c21691839e050
--- /dev/null
+++ b/src/main/java/org/olat/modules/qpool/model/QuestionItemDocument.java
@@ -0,0 +1,43 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.qpool.model;
+
+import org.olat.search.model.OlatDocument;
+
+/**
+ * 
+ * Initial date: 28.02.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class QuestionItemDocument extends OlatDocument {
+
+	private static final long serialVersionUID = 2137366338712446727L;
+	public static final String TYPE = "type.question.item";
+	
+	public static final String OWNER_FIELD = "owner";
+	public static final String SHARE_FIELD = "share";
+	public static final String POOL_FIELD = "pool";
+	public static final String STUDY_FIELD = "field";
+	
+
+
+
+}
diff --git a/src/main/java/org/olat/modules/qpool/model/QuestionItemImpl.java b/src/main/java/org/olat/modules/qpool/model/QuestionItemImpl.java
index c1b7735b03b6a47febacdff22fb226645d28ca3c..05b2e50384e1f52cb93cb261c8f7edba42c85a0f 100644
--- a/src/main/java/org/olat/modules/qpool/model/QuestionItemImpl.java
+++ b/src/main/java/org/olat/modules/qpool/model/QuestionItemImpl.java
@@ -273,6 +273,27 @@ public class QuestionItemImpl implements QuestionItem, CreateInfo, ModifiedInfo,
 	public void setStudyField(StudyField studyField) {
 		this.studyField = studyField;
 	}
+	
+	@Transient
+	@Override
+	public String getStudyFieldPath() {
+		if(studyField != null) {
+			String path = studyField.getMaterializedPathNames();
+			if(StringHelper.containsNonWhitespace(path)) {
+				return path + "/" + studyField.getField();
+			} 
+			return "/" + studyField.getField();
+		}
+		return null;
+	}
+
+	@Override
+	public String getStudyFieldName() {
+		if(studyField != null) {
+			return studyField.getField();
+		}
+		return null;
+	}
 
 	public String getCopyright() {
 		return copyright;
diff --git a/src/main/java/org/olat/modules/qpool/model/SearchQuestionItemParams.java b/src/main/java/org/olat/modules/qpool/model/SearchQuestionItemParams.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c4a857f6229019fe1bd4309584a2b86ff91ddd7
--- /dev/null
+++ b/src/main/java/org/olat/modules/qpool/model/SearchQuestionItemParams.java
@@ -0,0 +1,69 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.modules.qpool.model;
+
+import org.olat.core.id.Identity;
+import org.olat.core.id.Roles;
+
+/**
+ * 
+ * Initial date: 28.02.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class SearchQuestionItemParams {
+	
+	private Long poolKey;
+	private String searchString;
+	
+	private final Identity identity;
+	private final Roles roles;
+	
+	public SearchQuestionItemParams(Identity identity, Roles roles) {
+		this.identity = identity;
+		this.roles = roles;
+	}
+
+	public Long getPoolKey() {
+		return poolKey;
+	}
+
+	public void setPoolKey(Long poolKey) {
+		this.poolKey = poolKey;
+	}
+
+	public String getSearchString() {
+		return searchString;
+	}
+
+	public void setSearchString(String searchString) {
+		this.searchString = searchString;
+	}
+
+	public Identity getIdentity() {
+		return identity;
+	}
+	
+	public Roles getRoles() {
+		return roles;
+	}
+
+
+}
diff --git a/src/main/java/org/olat/modules/qpool/model/StudyFieldImpl.java b/src/main/java/org/olat/modules/qpool/model/StudyFieldImpl.java
index 742b6f785f655de8e561ee41ccf1b4c7b06db9c3..476d9e5b0b510e85156531c8ce2dd0b79cb49218 100644
--- a/src/main/java/org/olat/modules/qpool/model/StudyFieldImpl.java
+++ b/src/main/java/org/olat/modules/qpool/model/StudyFieldImpl.java
@@ -72,6 +72,11 @@ public class StudyFieldImpl implements StudyField, CreateInfo, ModifiedInfo, Per
 	@Column(name="q_field", nullable=false, insertable=true, updatable=true)
 	private String field;
 	
+	@Column(name="q_mat_path_ids", nullable=false, insertable=true, updatable=true)
+	private String materializedPathKeys;
+	@Column(name="q_mat_path_names", nullable=false, insertable=true, updatable=true)
+	private String materializedPathNames;
+	
 	@ManyToOne(targetEntity=StudyFieldImpl.class)
   @JoinColumn(name="fk_parent_field", nullable=true, insertable=true, updatable=true)
 	private StudyField parentField;
@@ -120,6 +125,22 @@ public class StudyFieldImpl implements StudyField, CreateInfo, ModifiedInfo, Per
 		this.parentField = parentField;
 	}
 
+	public String getMaterializedPathKeys() {
+		return materializedPathKeys;
+	}
+
+	public void setMaterializedPathKeys(String materializedPathKeys) {
+		this.materializedPathKeys = materializedPathKeys;
+	}
+
+	public String getMaterializedPathNames() {
+		return materializedPathNames;
+	}
+
+	public void setMaterializedPathNames(String materializedPathNames) {
+		this.materializedPathNames = materializedPathNames;
+	}
+
 	@Override
 	public int hashCode() {
 		return key == null ? 97489 : key.hashCode();
diff --git a/src/main/java/org/olat/modules/qpool/ui/CollectionOfItemsSource.java b/src/main/java/org/olat/modules/qpool/ui/CollectionOfItemsSource.java
index f3b98761a2f0a94439456a9ff53d32a5d3735eef..3df610d468c358576c23c4f786d859dffe7666f4 100644
--- a/src/main/java/org/olat/modules/qpool/ui/CollectionOfItemsSource.java
+++ b/src/main/java/org/olat/modules/qpool/ui/CollectionOfItemsSource.java
@@ -22,10 +22,14 @@ package org.olat.modules.qpool.ui;
 import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
+import org.olat.core.id.Identity;
+import org.olat.core.id.Roles;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.QuestionItemCollection;
 import org.olat.modules.qpool.QuestionPoolService;
+import org.olat.modules.qpool.model.SearchQuestionItemParams;
 
 /**
  * 
@@ -35,10 +39,14 @@ import org.olat.modules.qpool.QuestionPoolService;
  */
 public class CollectionOfItemsSource implements QuestionItemsSource {
 	
+	private final Roles roles;
+	private final Identity identity;
 	private final QuestionPoolService qpoolService;
 	private final QuestionItemCollection collection;
 	
-	public CollectionOfItemsSource(QuestionItemCollection collection) {
+	public CollectionOfItemsSource(QuestionItemCollection collection, Identity identity, Roles roles) {
+		this.roles = roles;
+		this.identity = identity;
 		this.collection = collection;
 		qpoolService = CoreSpringFactory.getImpl(QuestionPoolService.class);
 	}
@@ -49,7 +57,9 @@ public class CollectionOfItemsSource implements QuestionItemsSource {
 	}
 
 	@Override
-	public List<QuestionItem> getItems(int firstResult, int maxResults, SortKey... orderBy) {
-		return qpoolService.getItemsOfCollection(collection, firstResult, maxResults);
+	public ResultInfos<QuestionItem> getItems(String query, List<String> condQueries, int firstResult, int maxResults, SortKey... orderBy) {
+		SearchQuestionItemParams params = new SearchQuestionItemParams(identity, roles);
+		params.setSearchString(query);
+		return qpoolService.getItemsOfCollection(collection, params, firstResult, maxResults);
 	}
 }
diff --git a/src/main/java/org/olat/modules/qpool/ui/ImportAuthorOverviewDataModel.java b/src/main/java/org/olat/modules/qpool/ui/ImportAuthorOverviewDataModel.java
index 570ec7893c9c316b7f38360367b3a3408944db6e..84c80e8ff51126ce8860e657e9fa3977981e1e6c 100644
--- a/src/main/java/org/olat/modules/qpool/ui/ImportAuthorOverviewDataModel.java
+++ b/src/main/java/org/olat/modules/qpool/ui/ImportAuthorOverviewDataModel.java
@@ -23,7 +23,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
-import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModel;
 import org.olat.core.gui.components.table.DefaultTableDataModel;
@@ -66,11 +65,6 @@ public class ImportAuthorOverviewDataModel extends DefaultTableDataModel<Identit
 	public int getColumnCount() {
 		return columnModel.getColumnCount();
 	}
-	
-	@Override
-	public void load(int firstResult, int maxResults, SortKey... orderBy) {
-		//already loaded
-	}
 
 	@Override
 	public Object getValueAt(int row, int col) {
diff --git a/src/main/java/org/olat/modules/qpool/ui/ItemRowsSource.java b/src/main/java/org/olat/modules/qpool/ui/ItemRowsSource.java
index 1415ab92166f2f3e96c82ad7e4e2b06f13fae363..c9df55a8d31bca62d1cd100d1da9e1fb8bf5c503 100644
--- a/src/main/java/org/olat/modules/qpool/ui/ItemRowsSource.java
+++ b/src/main/java/org/olat/modules/qpool/ui/ItemRowsSource.java
@@ -21,6 +21,7 @@ package org.olat.modules.qpool.ui;
 
 import java.util.List;
 
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 
 /**
@@ -33,5 +34,5 @@ public interface ItemRowsSource {
 	
 	public int getRowCount();
 	
-	public List<QuestionItemRow> getRows(int firstResult, int maxResults, SortKey... orderBy);
+	public ResultInfos<QuestionItemRow> getRows(String query, List<String> condQueries, int firstResult, int maxResults, SortKey... orderBy);
 }
diff --git a/src/main/java/org/olat/modules/qpool/ui/MarkedItemsSource.java b/src/main/java/org/olat/modules/qpool/ui/MarkedItemsSource.java
index 19068ed9b7393b4f5a49bdf3c7f421c988874ce7..e707dbf136913af035b3266df52153b7a3e81163 100644
--- a/src/main/java/org/olat/modules/qpool/ui/MarkedItemsSource.java
+++ b/src/main/java/org/olat/modules/qpool/ui/MarkedItemsSource.java
@@ -22,10 +22,13 @@ package org.olat.modules.qpool.ui;
 import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
+import org.olat.core.id.Roles;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.QuestionPoolService;
+import org.olat.modules.qpool.model.SearchQuestionItemParams;
 
 /**
  * 
@@ -35,11 +38,13 @@ import org.olat.modules.qpool.QuestionPoolService;
  */
 public class MarkedItemsSource implements QuestionItemsSource {
 	
+	private final Roles roles;
 	private final Identity me;
 	private final QuestionPoolService qpoolService;
 	
-	public MarkedItemsSource(Identity me) {
+	public MarkedItemsSource(Identity me, Roles roles) {
 		this.me = me;
+		this.roles = roles;
 		qpoolService = CoreSpringFactory.getImpl(QuestionPoolService.class);
 	}
 
@@ -49,7 +54,9 @@ public class MarkedItemsSource implements QuestionItemsSource {
 	}
 
 	@Override
-	public List<QuestionItem> getItems(int firstResult, int maxResults, SortKey... orderBy) {
-		return qpoolService.getFavoritItems(me, firstResult, maxResults);
+	public ResultInfos<QuestionItem> getItems(String query, List<String> condQueries, int firstResult, int maxResults, SortKey... orderBy) {
+		SearchQuestionItemParams params = new SearchQuestionItemParams(me, roles);
+		params.setSearchString(query);
+		return qpoolService.getFavoritItems(me, params, firstResult, maxResults);
 	}
 }
diff --git a/src/main/java/org/olat/modules/qpool/ui/MyQuestionItemsSource.java b/src/main/java/org/olat/modules/qpool/ui/MyQuestionItemsSource.java
index e745872efbbce6dce6b4f4c3f5987220047aba7b..921422d0adce52563b58fee4b094860b7d05ff40 100644
--- a/src/main/java/org/olat/modules/qpool/ui/MyQuestionItemsSource.java
+++ b/src/main/java/org/olat/modules/qpool/ui/MyQuestionItemsSource.java
@@ -22,10 +22,13 @@ package org.olat.modules.qpool.ui;
 import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
+import org.olat.core.id.Roles;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.QuestionPoolService;
+import org.olat.modules.qpool.model.SearchQuestionItemParams;
 
 /**
  * 
@@ -35,11 +38,13 @@ import org.olat.modules.qpool.QuestionPoolService;
  */
 public class MyQuestionItemsSource implements QuestionItemsSource {
 	
+	private final Roles roles;
 	private final Identity me;
 	private final QuestionPoolService qpoolService;
 	
-	public MyQuestionItemsSource(Identity me) {
+	public MyQuestionItemsSource(Identity me, Roles roles) {
 		this.me = me;
+		this.roles = roles;
 		qpoolService = CoreSpringFactory.getImpl(QuestionPoolService.class);
 	}
 
@@ -49,7 +54,9 @@ public class MyQuestionItemsSource implements QuestionItemsSource {
 	}
 
 	@Override
-	public List<QuestionItem> getItems(int firstResult, int maxResults, SortKey... orderBy) {
-		return qpoolService.getItems(me, firstResult, maxResults);
+	public ResultInfos<QuestionItem> getItems(String query, List<String> condQueries, int firstResult, int maxResults, SortKey... orderBy) {
+		SearchQuestionItemParams params = new SearchQuestionItemParams(me, roles);
+		params.setSearchString(query);
+		return qpoolService.getItems(me, params, firstResult, maxResults);
 	}
 }
diff --git a/src/main/java/org/olat/modules/qpool/ui/PoolDataModel.java b/src/main/java/org/olat/modules/qpool/ui/PoolDataModel.java
index 53687914abca7f826fc8c9830c4f196cb8ac939e..d4ce4cb084e443b78f8494c7db1cfdd8f8f7eed8 100644
--- a/src/main/java/org/olat/modules/qpool/ui/PoolDataModel.java
+++ b/src/main/java/org/olat/modules/qpool/ui/PoolDataModel.java
@@ -22,9 +22,12 @@ package org.olat.modules.qpool.ui;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.olat.core.commons.persistence.DefaultResultInfos;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataSource;
 import org.olat.core.gui.components.table.TableDataModel;
 import org.olat.core.gui.translator.Translator;
 import org.olat.modules.qpool.ui.PoolsAdminController.PoolSource;
@@ -35,7 +38,7 @@ import org.olat.modules.qpool.ui.PoolsAdminController.PoolSource;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class PoolDataModel implements FlexiTableDataModel, TableDataModel<PoolRow> {
+public class PoolDataModel implements FlexiTableDataModel, FlexiTableDataSource<PoolRow>, TableDataModel<PoolRow> {
 
 	private List<PoolRow> rows;
 	private PoolSource source;
@@ -74,7 +77,7 @@ public class PoolDataModel implements FlexiTableDataModel, TableDataModel<PoolRo
 	}
 
 	@Override
-	public void load(int firstResult, int maxResults, SortKey... orderBy) {
+	public ResultInfos<PoolRow> load(int firstResult, int maxResults, SortKey... orderBy) {
 		if(rows == null) {
 			rows = new ArrayList<PoolRow>();
 		}
@@ -82,15 +85,21 @@ public class PoolDataModel implements FlexiTableDataModel, TableDataModel<PoolRo
 		for(int i=rows.size(); i<firstResult; i++) {
 			rows.add(null);
 		}
-		List<PoolRow> newRows = source.getRows(firstResult, maxResults, orderBy);
-		for(int i=0; i<newRows.size(); i++) {
+		ResultInfos<PoolRow> newRows = source.getRows(firstResult, maxResults, orderBy);
+		for(int i=0; i<newRows.getObjects().size(); i++) {
 			int rowIndex = i + firstResult;
 			if(rowIndex < rows.size()) {
-				rows.set(rowIndex, newRows.get(i));
+				rows.set(rowIndex, newRows.getObjects().get(i));
 			} else {
-				rows.add(newRows.get(i));
+				rows.add(newRows.getObjects().get(i));
 			}
 		}
+		return new DefaultResultInfos<PoolRow>(newRows.getNextFirstResult(), newRows.getCorrectedRowCount(), rows);
+	}
+
+	@Override
+	public ResultInfos<PoolRow> search(String query, List<String> addQueries, int firstResult, int maxResults, SortKey... orderBy) {
+		return load(firstResult, maxResults, orderBy);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/modules/qpool/ui/PooledItemsSource.java b/src/main/java/org/olat/modules/qpool/ui/PooledItemsSource.java
index ab797fc43879cc25d52c4e7a7efa4d5d8c7582c8..d6562ea66e346c0d154725285d2b5440e0dbf24e 100644
--- a/src/main/java/org/olat/modules/qpool/ui/PooledItemsSource.java
+++ b/src/main/java/org/olat/modules/qpool/ui/PooledItemsSource.java
@@ -22,10 +22,14 @@ package org.olat.modules.qpool.ui;
 import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
+import org.olat.core.id.Identity;
+import org.olat.core.id.Roles;
 import org.olat.modules.qpool.Pool;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.QuestionPoolService;
+import org.olat.modules.qpool.model.SearchQuestionItemParams;
 
 /**
  * 
@@ -36,10 +40,14 @@ import org.olat.modules.qpool.QuestionPoolService;
 public class PooledItemsSource implements QuestionItemsSource {
 	
 	private final Pool pool;
+	private final Roles roles;
+	private final Identity identity;
 	private final QuestionPoolService qpoolService;
 	
-	public PooledItemsSource(Pool pool) {
+	public PooledItemsSource(Identity identity, Roles roles, Pool pool) {
 		this.pool = pool;
+		this.roles = roles;
+		this.identity = identity;
 		qpoolService = CoreSpringFactory.getImpl(QuestionPoolService.class);
 	}
 
@@ -49,7 +57,9 @@ public class PooledItemsSource implements QuestionItemsSource {
 	}
 
 	@Override
-	public List<QuestionItem> getItems(int firstResult, int maxResults, SortKey... orderBy) {
-		return qpoolService.getItemsOfPool(pool, firstResult, maxResults, orderBy);
+	public ResultInfos<QuestionItem> getItems(String query, List<String> condQueries, int firstResult, int maxResults, SortKey... orderBy) {
+		SearchQuestionItemParams params = new SearchQuestionItemParams(identity, roles);
+		params.setSearchString(query);
+		return qpoolService.getItemsOfPool(pool, params, firstResult, maxResults, orderBy);
 	}
 }
diff --git a/src/main/java/org/olat/modules/qpool/ui/PoolsAdminController.java b/src/main/java/org/olat/modules/qpool/ui/PoolsAdminController.java
index 4bc368e2327a2e33a9fe659cfa9d07778bfdc9a1..64eef54b505c60cf465a825ed8770d4a39987c25 100644
--- a/src/main/java/org/olat/modules/qpool/ui/PoolsAdminController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/PoolsAdminController.java
@@ -23,6 +23,8 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.DefaultResultInfos;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.form.flexible.FormItem;
@@ -87,7 +89,7 @@ public class PoolsAdminController extends FormBasicController {
 		columnsModel.addFlexiColumnModel(new StaticFlexiColumnModel("delete", translate("delete"), "delete-pool"));
 
 		model = new PoolDataModel(columnsModel, new PoolSource(), getTranslator());
-		poolTable = uifactory.addTableElement(ureq, "pools", model, 20, getTranslator(), formLayout);
+		poolTable = uifactory.addTableElement(ureq, "pools", model, model, 20, false, getTranslator(), formLayout);
 		poolTable.setRendererType(FlexiTableRendererType.classic);
 		
 		createPool = uifactory.addFormLink("create.pool", formLayout, Link.BUTTON);
@@ -195,13 +197,13 @@ public class PoolsAdminController extends FormBasicController {
 			return qpoolService.countPools();
 		}
 
-		public List<PoolRow> getRows(int firstResult, int maxResults, SortKey... orderBy) {
-			List<Pool> pools = qpoolService.getPools(firstResult, maxResults, orderBy);
-			List<PoolRow> rows = new ArrayList<PoolRow>(pools.size());
-			for(Pool pool:pools) {
+		public ResultInfos<PoolRow> getRows(int firstResult, int maxResults, SortKey... orderBy) {
+			ResultInfos<Pool> pools = qpoolService.getPools(firstResult, maxResults, orderBy);
+			List<PoolRow> rows = new ArrayList<PoolRow>(pools.getObjects().size());
+			for(Pool pool:pools.getObjects()) {
 				rows.add(forgeRow(pool));
 			}
-			return rows;
+			return new DefaultResultInfos<PoolRow>(pools.getNextFirstResult(), pools.getCorrectedRowCount(), rows);
 		}
 	}
 }
diff --git a/src/main/java/org/olat/modules/qpool/ui/QuestionItemDataModel.java b/src/main/java/org/olat/modules/qpool/ui/QuestionItemDataModel.java
index 8b6076f9949ebdb40618612470b526e4e0fe61af..1c1fff6d5819c7a2c06bdba461420fd61340143d 100644
--- a/src/main/java/org/olat/modules/qpool/ui/QuestionItemDataModel.java
+++ b/src/main/java/org/olat/modules/qpool/ui/QuestionItemDataModel.java
@@ -22,9 +22,12 @@ package org.olat.modules.qpool.ui;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.olat.core.commons.persistence.DefaultResultInfos;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModel;
+import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataSource;
 import org.olat.core.gui.components.table.TableDataModel;
 import org.olat.core.gui.translator.Translator;
 import org.olat.modules.qpool.QuestionStatus;
@@ -36,13 +39,15 @@ import org.olat.modules.qpool.QuestionType;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class QuestionItemDataModel implements FlexiTableDataModel, TableDataModel<QuestionItemRow> {
+public class QuestionItemDataModel implements FlexiTableDataModel, FlexiTableDataSource<QuestionItemRow>, TableDataModel<QuestionItemRow> {
 
 	private List<QuestionItemRow> rows;
 	private FlexiTableColumnModel columnModel;
 	private ItemRowsSource source;
 	private final Translator translator;
 	
+	private int rowCount;
+	
 	public QuestionItemDataModel(FlexiTableColumnModel columnModel, ItemRowsSource source, Translator translator) {
 		this.columnModel = columnModel;
 		this.source = source;
@@ -66,7 +71,7 @@ public class QuestionItemDataModel implements FlexiTableDataModel, TableDataMode
 
 	@Override
 	public int getRowCount() {
-		return source.getRowCount();
+		return rowCount;
 	}
 
 	@Override
@@ -80,25 +85,61 @@ public class QuestionItemDataModel implements FlexiTableDataModel, TableDataMode
 	}
 
 	@Override
-	public void load(int firstResult, int maxResults, SortKey... orderBy) {
+	public ResultInfos<QuestionItemRow> load(int firstResult, int maxResults, SortKey... orderBy) {
 		if(rows == null) {
 			rows = new ArrayList<QuestionItemRow>();
 		}
-		
 		for(int i=rows.size(); i<firstResult; i++) {
 			rows.add(null);
 		}
-		List<QuestionItemRow> newRows = source.getRows(firstResult, maxResults, orderBy);
-		for(int i=0; i<newRows.size(); i++) {
+		
+		ResultInfos<QuestionItemRow> newRows = source.getRows(null, null, firstResult, maxResults, orderBy);
+		if(firstResult == 0) {
+			if(newRows.getObjects().size() < maxResults) {
+				rowCount = newRows.getObjects().size();
+			} else {
+				rowCount = source.getRowCount();
+			}
+		}
+		
+		for(int i=0; i<newRows.getObjects().size(); i++) {
 			int rowIndex = i + firstResult;
 			if(rowIndex < rows.size()) {
-				rows.set(rowIndex, newRows.get(i));
+				rows.set(rowIndex, newRows.getObjects().get(i));
 			} else {
-				rows.add(newRows.get(i));
+				rows.add(newRows.getObjects().get(i));
 			}
 		}
+		return new DefaultResultInfos<QuestionItemRow>(newRows.getNextFirstResult(), newRows.getCorrectedRowCount(), rows);
+	}
+	
+	@Override
+	public ResultInfos<QuestionItemRow> search(String query, List<String> condQueries, int firstResult,
+			int maxResults, SortKey... orderBy) {
+		if(firstResult == 0) {
+			rows = new ArrayList<QuestionItemRow>();
+		} else {
+			for(int i=rows.size(); i<firstResult; i++) {
+				rows.add(null);
+			}
+		}
+		ResultInfos<QuestionItemRow> newRows = source.getRows(query, condQueries, firstResult, maxResults, orderBy);
+		if(newRows.getCorrectedRowCount() >= 0) {
+			rowCount = newRows.getCorrectedRowCount();
+		} else if(firstResult == 0) {
+			rowCount = source.getRowCount();
+		}
+		
+		for(int i=0; i<newRows.getObjects().size(); i++) {
+			int rowIndex = i + firstResult;
+			if(rowIndex < rows.size()) {
+				rows.set(rowIndex, newRows.getObjects().get(i));
+			} else {
+				rows.add(newRows.getObjects().get(i));
+			}
+		}
+		return new DefaultResultInfos<QuestionItemRow>(newRows.getNextFirstResult(), newRows.getCorrectedRowCount(), rows);
 	}
-
 
 	@Override
 	public int getColumnCount() {
@@ -116,7 +157,7 @@ public class QuestionItemDataModel implements FlexiTableDataModel, TableDataMode
 		switch(Cols.values()[col]) {
 			case id: return item.getKey();
 			case subject: return item.getSubject();
-			case studyField: return null;
+			case studyField: return item.getStudyFieldName();
 			case point: return item.getPoint();
 			case type: {
 				QuestionType type = item.getQuestionType();
diff --git a/src/main/java/org/olat/modules/qpool/ui/QuestionItemDetailsController.java b/src/main/java/org/olat/modules/qpool/ui/QuestionItemDetailsController.java
index 87d22742fc961c701fbe59f45289a88a3d42920d..1075ba9308063dcde69d80244cd67069a11bfea6 100644
--- a/src/main/java/org/olat/modules/qpool/ui/QuestionItemDetailsController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/QuestionItemDetailsController.java
@@ -54,7 +54,7 @@ import org.olat.modules.qpool.QuestionPoolService;
  */
 public class QuestionItemDetailsController extends BasicController {
 	
-	private Link deleteItem, shareItem;
+	private Link deleteItem, shareItem, saveItem;
 
 	private Controller editCtrl;
 	private CloseableModalController cmc;
@@ -96,6 +96,7 @@ public class QuestionItemDetailsController extends BasicController {
 		listenTo(commentsAndRatingCtr);
 
 		mainVC = createVelocityContainer("item_details");
+		saveItem = LinkFactory.createButton("save", mainVC, this);
 		shareItem = LinkFactory.createButton("share.item", mainVC, this);
 		deleteItem = LinkFactory.createButton("delete.item", mainVC, this);
 		
@@ -117,6 +118,8 @@ public class QuestionItemDetailsController extends BasicController {
 			doConfirmDelete(ureq, metadatasCtrl.getItem());
 		} else if(source == shareItem) {
 			doSelectGroup(ureq, metadatasCtrl.getItem());
+		} else if(source == saveItem) {
+			doSave(ureq, metadatasCtrl.getItem());
 		}
 	}
 	
@@ -152,6 +155,11 @@ public class QuestionItemDetailsController extends BasicController {
 		selectGroupCtrl = null;
 	}
 	
+	protected void doSave(UserRequest ureq, QuestionItem item) {
+		QuestionItem mergedItem = qpoolService.updateItem(item);
+		System.out.println(mergedItem);
+	}
+	
 	protected void doSelectGroup(UserRequest ureq, QuestionItem item) {
 		removeAsListenerAndDispose(selectGroupCtrl);
 		selectGroupCtrl = new SelectBusinessGroupController(ureq, getWindowControl());
diff --git a/src/main/java/org/olat/modules/qpool/ui/QuestionItemRow.java b/src/main/java/org/olat/modules/qpool/ui/QuestionItemRow.java
index 026b4f3cd105eafe1f60b76bef607d8f4a10b756..2f5f59e6d0b7c35e9c68b8e34f93b12c29459b89 100644
--- a/src/main/java/org/olat/modules/qpool/ui/QuestionItemRow.java
+++ b/src/main/java/org/olat/modules/qpool/ui/QuestionItemRow.java
@@ -117,6 +117,14 @@ public class QuestionItemRow implements QuestionItemShort {
 	public BigDecimal getDifficulty() {
 		return delegate.getDifficulty();
 	}
+	
+	public String getStudyFieldPath() {
+		return delegate.getStudyFieldPath();
+	}
+
+	public String getStudyFieldName() {
+		return delegate.getStudyFieldName();
+	}
 
 	public QuestionItem getItem() {
 		return delegate;
diff --git a/src/main/java/org/olat/modules/qpool/ui/QuestionItemsSource.java b/src/main/java/org/olat/modules/qpool/ui/QuestionItemsSource.java
index 3a12113a69b02a0b5e1865a967bdefa44242d045..7f658d4226857aa5b969ee4c6b4e2c2c129bf5cc 100644
--- a/src/main/java/org/olat/modules/qpool/ui/QuestionItemsSource.java
+++ b/src/main/java/org/olat/modules/qpool/ui/QuestionItemsSource.java
@@ -21,6 +21,7 @@ package org.olat.modules.qpool.ui;
 
 import java.util.List;
 
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.modules.qpool.QuestionItem;
 
@@ -34,6 +35,6 @@ public interface QuestionItemsSource {
 	
 	public int getNumOfItems();
 	
-	public List<QuestionItem> getItems(int firstResult, int maxResults, SortKey... orderBy);
+	public ResultInfos<QuestionItem> getItems(String query, List<String> condQueries, int firstResult, int maxResults, SortKey... orderBy);
 
 }
diff --git a/src/main/java/org/olat/modules/qpool/ui/QuestionListController.java b/src/main/java/org/olat/modules/qpool/ui/QuestionListController.java
index 142661ae58e9b3a7a94c89d3f4bcfb6f74a6c82c..a7f0c82810847d9c584d4b6d4ddbd08e30a2b792 100644
--- a/src/main/java/org/olat/modules/qpool/ui/QuestionListController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/QuestionListController.java
@@ -26,6 +26,8 @@ import java.util.Set;
 
 import org.olat.core.CoreSpringFactory;
 import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
+import org.olat.core.commons.persistence.DefaultResultInfos;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.commons.services.mark.MarkManager;
 import org.olat.core.gui.UserRequest;
@@ -108,7 +110,7 @@ public class QuestionListController extends FormBasicController implements Stack
 		FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel();
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.id.i18nKey(), Cols.id.ordinal(), true, "key"));
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.subject.i18nKey(), Cols.subject.ordinal(), true, "subject"));
-		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.studyField.i18nKey(), Cols.studyField.ordinal()));
+		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.studyField.i18nKey(), Cols.studyField.ordinal(), true, "studyField.field"));
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.point.i18nKey(), Cols.point.ordinal(), true, "point"));
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.type.i18nKey(), Cols.type.ordinal(), true, "type"));
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.status.i18nKey(), Cols.status.ordinal(), true, "status"));
@@ -116,7 +118,7 @@ public class QuestionListController extends FormBasicController implements Stack
 		columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.mark.i18nKey(), Cols.mark.ordinal()));
 
 		model = new QuestionItemDataModel(columnsModel, this, getTranslator());
-		itemsTable = uifactory.addTableElement(ureq, "items", model, 20, getTranslator(), formLayout);
+		itemsTable = uifactory.addTableElement(ureq, "items", model, model, 20, true, getTranslator(), formLayout);
 		itemsTable.setMultiSelect(true);
 		itemsTable.setRendererType(FlexiTableRendererType.dataTables);
 		
@@ -143,7 +145,7 @@ public class QuestionListController extends FormBasicController implements Stack
 
 	@Override
 	protected void formOK(UserRequest ureq) {
-		// 
+		//
 	}
 
 	@Override
@@ -384,16 +386,16 @@ public class QuestionListController extends FormBasicController implements Stack
 	}
 
 	@Override
-	public List<QuestionItemRow> getRows(int firstResult, int maxResults, SortKey... orderBy) {
+	public ResultInfos<QuestionItemRow> getRows(String query, List<String> condQueries, int firstResult, int maxResults, SortKey... orderBy) {
 		Set<Long> marks = markManager.getMarkResourceIds(getIdentity(), "QuestionItem", Collections.<String>emptyList());
 
-		List<QuestionItem> items = source.getItems(firstResult, maxResults, orderBy);
-		List<QuestionItemRow> rows = new ArrayList<QuestionItemRow>(items.size());
-		for(QuestionItem item:items) {
+		ResultInfos<QuestionItem> items = source.getItems(query, condQueries, firstResult, maxResults, orderBy);
+		List<QuestionItemRow> rows = new ArrayList<QuestionItemRow>(items.getObjects().size());
+		for(QuestionItem item:items.getObjects()) {
 			QuestionItemRow row = forgeRow(item, marks);
 			rows.add(row);
 		}
-		return rows;
+		return new DefaultResultInfos<QuestionItemRow>(items.getNextFirstResult(), items.getCorrectedRowCount(), rows);
 	}
 	
 	protected QuestionItemRow forgeRow(QuestionItem item, Set<Long> markedQuestionKeys) {
diff --git a/src/main/java/org/olat/modules/qpool/ui/QuestionPoolMainEditorController.java b/src/main/java/org/olat/modules/qpool/ui/QuestionPoolMainEditorController.java
index 4dd7623d3ef1f011fbc5a6b141106221f2dfd81c..743c50462304ed1daa278ec3332d0ec0ea763389 100644
--- a/src/main/java/org/olat/modules/qpool/ui/QuestionPoolMainEditorController.java
+++ b/src/main/java/org/olat/modules/qpool/ui/QuestionPoolMainEditorController.java
@@ -223,7 +223,7 @@ public class QuestionPoolMainEditorController extends BasicController implements
 	
 	private void doSelectMyQuestions(UserRequest ureq) {
 		if(myQuestionsCtrl == null) {
-			myQuestionsCtrl = new QuestionsController(ureq, getWindowControl(), new MyQuestionItemsSource(getIdentity()));
+			myQuestionsCtrl = new QuestionsController(ureq, getWindowControl(), new MyQuestionItemsSource(getIdentity(), ureq.getUserSession().getRoles()));
 			myQuestionsCtrl.setStackedController(stackPanel);
 			listenTo(myQuestionsCtrl);
 		}
@@ -233,11 +233,11 @@ public class QuestionPoolMainEditorController extends BasicController implements
 	
 	private void doSelectMarkedQuestions(UserRequest ureq) {
 		if(markedQuestionsCtrl == null) {
-			markedQuestionsCtrl = new QuestionsController(ureq, getWindowControl(), new MarkedItemsSource(getIdentity()));
+			markedQuestionsCtrl = new QuestionsController(ureq, getWindowControl(), new MarkedItemsSource(getIdentity(), ureq.getUserSession().getRoles()));
 			markedQuestionsCtrl.setStackedController(stackPanel);
 			listenTo(markedQuestionsCtrl);
 		} else {
-			markedQuestionsCtrl.updateSource(new MarkedItemsSource(getIdentity()));
+			markedQuestionsCtrl.updateSource(new MarkedItemsSource(getIdentity(), ureq.getUserSession().getRoles()));
 		}
 		currentCtrl = markedQuestionsCtrl;
 		content.setContent(markedQuestionsCtrl.getInitialComponent());
@@ -245,11 +245,11 @@ public class QuestionPoolMainEditorController extends BasicController implements
 	
 	private void doSelect(UserRequest ureq, Pool pool) {
 		if(selectedPoolCtrl == null) {
-			selectedPoolCtrl = new QuestionsController(ureq, getWindowControl(), new PooledItemsSource(pool));
+			selectedPoolCtrl = new QuestionsController(ureq, getWindowControl(), new PooledItemsSource(getIdentity(), ureq.getUserSession().getRoles(), pool));
 			selectedPoolCtrl.setStackedController(stackPanel);
 			listenTo(selectedPoolCtrl);
 		} else {
-			selectedPoolCtrl.updateSource(new PooledItemsSource(pool));
+			selectedPoolCtrl.updateSource(new PooledItemsSource(getIdentity(), ureq.getUserSession().getRoles(), pool));
 		}
 		currentCtrl = selectedPoolCtrl;
 		content.setContent(selectedPoolCtrl.getInitialComponent());
@@ -257,11 +257,11 @@ public class QuestionPoolMainEditorController extends BasicController implements
 	
 	private void doSelect(UserRequest ureq, BusinessGroup group) {
 		if(sharedItemsCtrl == null) {
-			sharedItemsCtrl = new QuestionsController(ureq, getWindowControl(), new SharedItemsSource(group));
+			sharedItemsCtrl = new QuestionsController(ureq, getWindowControl(), new SharedItemsSource(group, getIdentity(), ureq.getUserSession().getRoles()));
 			sharedItemsCtrl.setStackedController(stackPanel);
 			listenTo(sharedItemsCtrl);
 		} else {
-			sharedItemsCtrl.updateSource(new SharedItemsSource(group));
+			sharedItemsCtrl.updateSource(new SharedItemsSource(group, getIdentity(), ureq.getUserSession().getRoles()));
 		}
 		currentCtrl = sharedItemsCtrl;
 		content.setContent(sharedItemsCtrl.getInitialComponent());
@@ -269,11 +269,11 @@ public class QuestionPoolMainEditorController extends BasicController implements
 	
 	private void doSelect(UserRequest ureq, QuestionItemCollection coll) {
 		if(collItemsCtrl == null) {
-			collItemsCtrl = new QuestionsController(ureq, getWindowControl(), new CollectionOfItemsSource(coll));
+			collItemsCtrl = new QuestionsController(ureq, getWindowControl(), new CollectionOfItemsSource(coll, getIdentity(), ureq.getUserSession().getRoles()));
 			collItemsCtrl.setStackedController(stackPanel);
 			listenTo(collItemsCtrl);
 		} else {
-			collItemsCtrl.updateSource(new CollectionOfItemsSource(coll));
+			collItemsCtrl.updateSource(new CollectionOfItemsSource(coll, getIdentity(), ureq.getUserSession().getRoles()));
 		}
 		currentCtrl = collItemsCtrl;
 		content.setContent(collItemsCtrl.getInitialComponent());
diff --git a/src/main/java/org/olat/modules/qpool/ui/SharedItemsSource.java b/src/main/java/org/olat/modules/qpool/ui/SharedItemsSource.java
index 844b5cfdcf92e1e02c67bc9940908eaa8792a883..3621046b36de4abbf09b24ce39b0dd4775811f1f 100644
--- a/src/main/java/org/olat/modules/qpool/ui/SharedItemsSource.java
+++ b/src/main/java/org/olat/modules/qpool/ui/SharedItemsSource.java
@@ -22,10 +22,14 @@ package org.olat.modules.qpool.ui;
 import java.util.List;
 
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.commons.persistence.SortKey;
+import org.olat.core.id.Identity;
+import org.olat.core.id.Roles;
 import org.olat.group.BusinessGroup;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.QuestionPoolService;
+import org.olat.modules.qpool.model.SearchQuestionItemParams;
 import org.olat.resource.OLATResource;
 
 /**
@@ -36,10 +40,14 @@ import org.olat.resource.OLATResource;
  */
 public class SharedItemsSource implements QuestionItemsSource {
 	
+	private final Roles roles;
+	private final Identity identity;
 	private final OLATResource resource;
 	private final QuestionPoolService qpoolService;
 	
-	public SharedItemsSource(BusinessGroup group) {
+	public SharedItemsSource(BusinessGroup group, Identity identity, Roles roles) {
+		this.roles = roles;
+		this.identity = identity;
 		this.resource = group.getResource();
 		qpoolService = CoreSpringFactory.getImpl(QuestionPoolService.class);
 	}
@@ -50,7 +58,9 @@ public class SharedItemsSource implements QuestionItemsSource {
 	}
 
 	@Override
-	public List<QuestionItem> getItems(int firstResult, int maxResults, SortKey... orderBy) {
-		return qpoolService.getSharedItemByResource(resource, firstResult, maxResults, orderBy);
+	public ResultInfos<QuestionItem> getItems(String query, List<String> condQueries, int firstResult, int maxResults, SortKey... orderBy) {
+		SearchQuestionItemParams params = new SearchQuestionItemParams(identity, roles);
+		params.setSearchString(query);
+		return qpoolService.getSharedItemByResource(resource, params, firstResult, maxResults, orderBy);
 	}
 }
diff --git a/src/main/java/org/olat/modules/qpool/ui/_content/item_details.html b/src/main/java/org/olat/modules/qpool/ui/_content/item_details.html
index 4d2157c43d133fdb27af72054690b6e2fbb890e3..c28b62c3a6d25bbc1a6823dde6925bc740247c0c 100644
--- a/src/main/java/org/olat/modules/qpool/ui/_content/item_details.html
+++ b/src/main/java/org/olat/modules/qpool/ui/_content/item_details.html
@@ -3,6 +3,7 @@ $r.render("type_specifics")
 $r.render("metadatas")
 <div class="b_clearfix o_qpool_button_bar_box">
 	<div class="o_qpool_button_bar">
+		$r.render("save")
 		$r.render("share.item")
 		$r.render("delete.item")
 	</div>
diff --git a/src/main/java/org/olat/search/SearchModule.java b/src/main/java/org/olat/search/SearchModule.java
index 823188d63f898b6da8ed29506172f27521f19be2..5f6965b912b559f8c6a3f714b8dc6e240eb9b8c8 100644
--- a/src/main/java/org/olat/search/SearchModule.java
+++ b/src/main/java/org/olat/search/SearchModule.java
@@ -50,6 +50,7 @@ public class SearchModule extends AbstractOLATModule {
 	private static final OLog log = Tracing.createLoggerFor(SearchModule.class);
 	
 	// Definitions config parameter names in module-config
+	public final static String CONF_SEARCH_SERVICE = "searchService";
 	public final static String CONF_INDEX_PATH = "indexPath";
 	public final static String CONF_PERMANENT_INDEX_PATH = "permanentIndexPath";
 	public final static String CONF_TEMP_INDEX_PATH = "tempIndexPath";
@@ -89,6 +90,7 @@ public class SearchModule extends AbstractOLATModule {
 	private static final int    DEFAULT_RESTART_DAY_OF_WEEK = 8;
 	private static final String DEFAULT_RAM_BUFFER_SIZE_MB = "48";
 	
+	private String searchService;
 	private String fullIndexPath;
 	private String fullPermanentIndexPath;
 	private String fullTempIndexPath;
@@ -157,6 +159,9 @@ public class SearchModule extends AbstractOLATModule {
 	@Override
 	public void initDefaultProperties() {
 		log.debug("init start...");
+
+		searchService = getStringConfigParameter(CONF_SEARCH_SERVICE, "enabled", false);
+		
 		String indexPath = getStringConfigParameter(CONF_INDEX_PATH, "/tmp", false);
 		String permanentIndexPath = getStringConfigParameter(CONF_PERMANENT_INDEX_PATH, "/sidx", false);
 
@@ -252,6 +257,10 @@ public class SearchModule extends AbstractOLATModule {
 		}
 		setStringProperty(CONF_FILE_BLACK_LIST, sb.toString(), true);
 	}
+	
+	public boolean isSearchServiceEnabled() {
+		return "enabled".equals(searchService);
+	}
 
 	/**
 	 * @return Absolute file path for the full-index.
diff --git a/src/main/java/org/olat/search/SearchService.java b/src/main/java/org/olat/search/SearchService.java
index f1b279c9082cba584b2742f9a0a6ebb3d21c220c..5193efe651598d14209172b6d7f55e0ffbae8ac3 100644
--- a/src/main/java/org/olat/search/SearchService.java
+++ b/src/main/java/org/olat/search/SearchService.java
@@ -30,6 +30,7 @@ import java.util.Set;
 
 import org.apache.lucene.queryparser.classic.ParseException;
 import org.apache.lucene.util.Version;
+import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
 
@@ -53,6 +54,25 @@ public interface SearchService {
 			int firstResult, int maxReturns, boolean doHighlighting)
 	throws ServiceNotAvailableException, ParseException, QueryException;
 	
+	/**
+	 * There isn't any check access made on this search.
+	 * 
+	 * @param queryString
+	 * @param condQueries
+	 * @param identity
+	 * @param roles
+	 * @param firstResult
+	 * @param maxReturns
+	 * @param orderBy
+	 * @return A list of dbKey's
+	 * @throws ServiceNotAvailableException
+	 * @throws ParseException
+	 * @throws QueryException
+	 */
+	public List<Long> doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles,
+			int firstResult, int maxReturns, SortKey... orderBy)
+	throws ServiceNotAvailableException, ParseException, QueryException;
+	
 	/**
 	 * Check a query for similar words.
 	 * @param query
diff --git a/src/main/java/org/olat/search/_spring/searchContext.xml b/src/main/java/org/olat/search/_spring/searchContext.xml
index 3e24e3b57d8b25435b56d9d78b5b3b55d316de4a..1bfb1b9a333449494aea61ca6c73843c660d9f56 100644
--- a/src/main/java/org/olat/search/_spring/searchContext.xml
+++ b/src/main/java/org/olat/search/_spring/searchContext.xml
@@ -12,15 +12,29 @@
    	</bean>
    	
 	<bean id="org.olat.search.service.enabled" class="org.olat.search.service.SearchServiceImpl"
-	init-method="init" destroy-method="stop" depends-on="searchModuleInit" lazy-init="true">
+		init-method="init" destroy-method="stop" depends-on="searchModuleInit" lazy-init="true">
 		<constructor-arg index="0" ref="searchModule" />
 		<constructor-arg index="1" ref="mainIndexer" />
 		<constructor-arg index="2" ref="searchProvider" />
 		<constructor-arg index="3" ref="schedulerFactoryBean"/>
+		<constructor-arg index="4" ref="jmsIndexer"/>
 		<property name="metadataFields" ref="SearchMetadataFieldsProvider" />
 	</bean>
 
-	<bean id="org.olat.search.service.disabled" class="org.olat.search.service.SearchServiceDisabled" init-method="init" destroy-method="stop" lazy-init="true">
+	<bean id="org.olat.search.service.disabled" class="org.olat.search.service.SearchServiceDisabled"
+		init-method="init" destroy-method="stop" lazy-init="true">
+	</bean>
+	
+	<bean id="jmsIndexer" class="org.olat.search.service.indexer.JmsIndexer" init-method="springInit" destroy-method="stop">
+		<constructor-arg index="0" ref="searchModule" />
+		<property name="connectionFactory" ref="indexConnectionFactory"/>
+		<property name="jmsQueue" ref="indexQueue"/>
+		<property name="searchService" ref="org.olat.search.service.${search.service}" />
+		<property name="indexers">
+			<list>
+				<ref bean="questionItemIndexer" />
+			</list>
+		</property>
 	</bean>
 	
 	<bean id="searchModule" class="org.olat.search.SearchModule" lazy-init="true">
@@ -68,7 +82,9 @@
 	       <property name="targetMethod" value="init" />
 	       <property name="arguments">
 	             <value>
-	        		generateIndexAtStartup=${generate.index.at.startup}		
+	        searchService=${search.service}
+	             
+	        generateIndexAtStartup=${generate.index.at.startup}		
 					tempIndexPath=${search.index.tempIndex}
 					tempSpellCheckPath=${search.index.tempSpellcheck}
 					pdfTextBufferPath=${search.index.pdfBuffer}
@@ -109,7 +125,6 @@
 				<ref bean="identityIndexer" />
 				<ref bean="epDefaultMapIndexer" />
 				<ref bean="epStructuredMapIndexer" />
-				<ref bean="questionItemIndexer" />
 			</list>
 		</property>
 	</bean>
@@ -271,6 +286,7 @@
 		<property name="receiveTimeout" value="45000"/>
 		<property name="timeToLive" value="45000"/>
 	</bean>
+	
 
 	<!-- SEARCH COMMON CONFIGURATION (PROXY-SIDE AND SERVICE-SIDE) -->
 	<!-- ========================================================= -->
diff --git a/src/main/java/org/olat/search/_spring/searchJms_activemq.xml b/src/main/java/org/olat/search/_spring/searchJms_activemq.xml
index bec829336397ccc6c2ef820d01954c2855bf318b..fc48b90365c222a5164f40e05c1587bde5217339 100644
--- a/src/main/java/org/olat/search/_spring/searchJms_activemq.xml
+++ b/src/main/java/org/olat/search/_spring/searchJms_activemq.xml
@@ -12,4 +12,17 @@
 	<bean id="searchQueue" class="org.apache.activemq.command.ActiveMQQueue" lazy-init="true">
 		<constructor-arg value="olat/searchQueue" />
 	</bean>
+	
+	
+	<bean id="indexConnectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory" lazy-init="true">
+		<property name="brokerURL" value="${index.broker.url}" /> 
+	</bean>
+
+	<bean id="indexQueue" class="org.apache.activemq.command.ActiveMQQueue" lazy-init="true">
+		<constructor-arg value="olat/indexQueue" />
+	</bean>
+	
+	
+	
+	
 </beans>
diff --git a/src/main/java/org/olat/search/model/AbstractOlatDocument.java b/src/main/java/org/olat/search/model/AbstractOlatDocument.java
index 9f7968be54ac787db4c071e4a72689aa8b470e6a..dc82d18c6657290a9dd01c59fe30650e366abd20 100644
--- a/src/main/java/org/olat/search/model/AbstractOlatDocument.java
+++ b/src/main/java/org/olat/search/model/AbstractOlatDocument.java
@@ -48,6 +48,8 @@ public abstract class AbstractOlatDocument implements Serializable {
 	private static final long serialVersionUID = 3477625468662703214L;
 
 	// Field names
+	public  static final String DB_ID_NAME = "key";
+	
 	public  static final String TITLE_FIELD_NAME = "title";
 
 	public  static final String DESCRIPTION_FIELD_NAME = "description";
@@ -78,6 +80,7 @@ public abstract class AbstractOlatDocument implements Serializable {
 	
 	public static final Set<String> getFields() {
 		Set<String> fields = new HashSet<String>();
+		fields.add(DB_ID_NAME);
 		fields.add(TITLE_FIELD_NAME);
 		fields.add(DESCRIPTION_FIELD_NAME);
 		fields.add(CONTENT_FIELD_NAME);
@@ -97,6 +100,7 @@ public abstract class AbstractOlatDocument implements Serializable {
 
 	
   // Lucene Attributes
+	private Long id;
 	private String title = "";
 	protected String description = "";
 	/** E.g. 'Group','ForumMessage'. */
@@ -122,6 +126,10 @@ public abstract class AbstractOlatDocument implements Serializable {
 	}
 
 	public AbstractOlatDocument(Document document) {
+		String idStr = document.get(DB_ID_NAME);
+		if(StringHelper.containsNonWhitespace(idStr)) {
+			id = Long.parseLong(idStr);
+		}
 		title = document.get(TITLE_FIELD_NAME);
 		description = document.get(DESCRIPTION_FIELD_NAME);
 		documentType = document.get(DOCUMENTTYPE_FIELD_NAME);
@@ -158,6 +166,14 @@ public abstract class AbstractOlatDocument implements Serializable {
 		cssIcon = document.get(CSS_ICON);
 	}
 
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
 	/**
 	 * @return Returns the author.
 	 */
@@ -380,13 +396,12 @@ public abstract class AbstractOlatDocument implements Serializable {
 	@Override
 	public String toString() {
 		StringBuilder buf = new StringBuilder();
-		buf.append(this.getDocumentType());
-		buf.append("|");
-		buf.append(getTitle());
-		buf.append("|");
+		buf.append(getDocumentType())
+		   .append("|")
+		   .append(getTitle())
+		   .append("|");
 		if (getDescription() != null) buf.append(getDescription());
-		buf.append("|");
-		buf.append(getResourceUrl());
+		buf.append("|").append(getResourceUrl());
 		return buf.toString();
 	}
 }
diff --git a/src/main/java/org/olat/search/model/OlatDocument.java b/src/main/java/org/olat/search/model/OlatDocument.java
index f183ea3a9b51cc8bc4a2d0ddc2971bd4320f13c4..7a2472f421cf41afcafcf4f4079bd8a8e9fa34c2 100644
--- a/src/main/java/org/olat/search/model/OlatDocument.java
+++ b/src/main/java/org/olat/search/model/OlatDocument.java
@@ -80,6 +80,9 @@ public class OlatDocument extends AbstractOlatDocument {
 	 */
 	public Document getLuceneDocument() {
 		Document document = new Document();
+		if(getId() != null) {
+			document.add(new StringField(DB_ID_NAME, getId().toString(), Field.Store.YES));
+		}
 		document.add(createTextField(TITLE_FIELD_NAME,getTitle(), 4));
 		document.add(createTextField(DESCRIPTION_FIELD_NAME,getDescription(), 2));
 		document.add(createTextField(CONTENT_FIELD_NAME,getContent(), 0.5f ) );
@@ -123,8 +126,8 @@ public class OlatDocument extends AbstractOlatDocument {
 			}
 		}
 		
-		document.add(new TextField(PARENT_CONTEXT_TYPE_FIELD_NAME, getParentContextType(), Field.Store.YES));
-		document.add(new TextField(PARENT_CONTEXT_NAME_FIELD_NAME, getParentContextName(), Field.Store.YES));
+		document.add(createTextField(PARENT_CONTEXT_TYPE_FIELD_NAME, getParentContextType(), 1.0f));
+		document.add(createTextField(PARENT_CONTEXT_NAME_FIELD_NAME, getParentContextName(), 1.0f));
 		if(StringHelper.containsNonWhitespace(getReservedTo())) {
 			for(StringTokenizer tokenizer = new StringTokenizer(getReservedTo(), " "); tokenizer.hasMoreTokens(); ) {
 				String reserved = tokenizer.nextToken();
@@ -136,8 +139,14 @@ public class OlatDocument extends AbstractOlatDocument {
 	  return document;
 	}
 
-
-	private Field createTextField(String fieldName, String content, float boost) {
+	/**
+	 * Create a field which is indexed, tokenized and stored
+	 * @param fieldName
+	 * @param content
+	 * @param boost
+	 * @return
+	 */
+	protected static Field createTextField(String fieldName, String content, float boost) {
 		TextField field = new TextField(fieldName,content, Field.Store.YES);
 		field.setBoost(boost);
 		return field;
diff --git a/src/main/java/org/olat/search/service/SearchServiceDisabled.java b/src/main/java/org/olat/search/service/SearchServiceDisabled.java
index a0dcfed117661ab5500083ea01f4a70cf197a095..3bdd8b5203cfabf8eeff98a1eb7022912d527ae6 100644
--- a/src/main/java/org/olat/search/service/SearchServiceDisabled.java
+++ b/src/main/java/org/olat/search/service/SearchServiceDisabled.java
@@ -30,6 +30,7 @@ import java.util.Set;
 
 import org.apache.lucene.document.Document;
 import org.apache.lucene.queryparser.classic.ParseException;
+import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
 import org.olat.core.logging.OLog;
@@ -118,4 +119,10 @@ public class SearchServiceDisabled implements SearchService {
 		throw new ServiceNotAvailableException("call doSearch on disabled search service");
 	}
 
+	@Override
+	public List<Long> doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles, int firstResult, int maxReturns, SortKey... orderBy)
+			throws ServiceNotAvailableException, ParseException, QueryException {
+		log.error("call doSearch on disabled search service");
+		throw new ServiceNotAvailableException("call doSearch on disabled search service");
+	}
 }
diff --git a/src/main/java/org/olat/search/service/SearchServiceImpl.java b/src/main/java/org/olat/search/service/SearchServiceImpl.java
index cd8696af6ef3c8d4d64a99fdc299e18a907d6bfa..2394444e050850b1b340cbadde29344dca7fe9c8 100644
--- a/src/main/java/org/olat/search/service/SearchServiceImpl.java
+++ b/src/main/java/org/olat/search/service/SearchServiceImpl.java
@@ -27,13 +27,16 @@ package org.olat.search.service;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.MultiReader;
 import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
@@ -43,10 +46,13 @@ import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.FSDirectory;
 import org.olat.core.CoreSpringFactory;
+import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
 import org.olat.core.logging.AssertException;
@@ -54,6 +60,8 @@ import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.ArrayHelper;
 import org.olat.core.util.StringHelper;
+import org.olat.modules.qpool.model.QuestionItemDocument;
+import org.olat.search.QueryException;
 import org.olat.search.SearchModule;
 import org.olat.search.SearchResults;
 import org.olat.search.SearchService;
@@ -62,6 +70,7 @@ import org.olat.search.ServiceNotAvailableException;
 import org.olat.search.model.AbstractOlatDocument;
 import org.olat.search.service.indexer.FullIndexerStatus;
 import org.olat.search.service.indexer.Index;
+import org.olat.search.service.indexer.LifeFullIndexer;
 import org.olat.search.service.indexer.MainIndexer;
 import org.olat.search.service.searcher.JmsSearchProvider;
 import org.olat.search.service.searcher.SearchResultsImpl;
@@ -87,6 +96,7 @@ public class SearchServiceImpl implements SearchService {
 	private DirectoryReader reader;
 	private DirectoryReader permReader;
 	
+	private LifeFullIndexer lifeIndexer;
 	private SearchSpellChecker searchSpellChecker;
 	private String indexPath;
 	private String permanentIndexPath;
@@ -98,18 +108,21 @@ public class SearchServiceImpl implements SearchService {
 	private String fields[] = {
 			AbstractOlatDocument.TITLE_FIELD_NAME, AbstractOlatDocument.DESCRIPTION_FIELD_NAME,
 			AbstractOlatDocument.CONTENT_FIELD_NAME, AbstractOlatDocument.AUTHOR_FIELD_NAME,
-			AbstractOlatDocument.DOCUMENTTYPE_FIELD_NAME, AbstractOlatDocument.FILETYPE_FIELD_NAME
+			AbstractOlatDocument.DOCUMENTTYPE_FIELD_NAME, AbstractOlatDocument.FILETYPE_FIELD_NAME,
+			QuestionItemDocument.STUDY_FIELD
 	};
 
 	
 	/**
 	 * [used by spring]
 	 */
-	private SearchServiceImpl(SearchModule searchModule, MainIndexer mainIndexer, JmsSearchProvider searchProvider, Scheduler scheduler) {
+	private SearchServiceImpl(SearchModule searchModule, MainIndexer mainIndexer, JmsSearchProvider searchProvider,
+			Scheduler scheduler, LifeFullIndexer lifeIndexer) {
 		log.info("Start SearchServiceImpl constructor...");
 		this.scheduler = scheduler;
 		this.searchModuleConfig = searchModule;
 		this.mainIndexer = mainIndexer;
+		this.lifeIndexer = lifeIndexer;
 		analyzer = new StandardAnalyzer(SearchService.OO_LUCENE_VERSION);
 		searchProvider.setSearchService(this);
 	}
@@ -163,7 +176,7 @@ public class SearchServiceImpl implements SearchService {
 		searchSpellChecker.setSpellDictionaryPath(searchModuleConfig.getSpellCheckDictionaryPath());
 		searchSpellChecker.setSpellCheckEnabled(searchModuleConfig.getSpellCheckEnabled());
 		
-	  indexer = new Index(searchModuleConfig, searchSpellChecker, mainIndexer);
+	  indexer = new Index(searchModuleConfig, searchSpellChecker, mainIndexer, lifeIndexer);
 
 	  indexPath = searchModuleConfig.getFullIndexPath();	
 	  permanentIndexPath = searchModuleConfig.getFullPermanentIndexPath();
@@ -198,32 +211,16 @@ public class SearchServiceImpl implements SearchService {
 			
 			log.info("queryString=" + queryString);
 			IndexSearcher searcher = getIndexSearcher();
-			
-			BooleanQuery query = new BooleanQuery();
-			if(StringHelper.containsNonWhitespace(queryString)) {
-				QueryParser queryParser = new MultiFieldQueryParser(SearchService.OO_LUCENE_VERSION, fields, analyzer);
-				queryParser.setLowercaseExpandedTerms(false);//some add. fields are not tokenized and not lowered case
-		  	Query multiFieldQuery = queryParser.parse(queryString.toLowerCase());
-		  	query.add(multiFieldQuery, Occur.MUST);
-			}
-			
-			if(condQueries != null && !condQueries.isEmpty()) {
-				for(String condQueryString:condQueries) {
-					QueryParser condQueryParser = new QueryParser(SearchService.OO_LUCENE_VERSION, condQueryString, analyzer);
-					condQueryParser.setLowercaseExpandedTerms(false);
-			  	Query condQuery = condQueryParser.parse(condQueryString);
-			  	query.add(condQuery, Occur.MUST);
-				}
-			}
+			BooleanQuery query = createQuery(queryString, condQueries);
 			if (log.isDebug()) log.debug("query=" + query);
-
+			
 	    long startTime = System.currentTimeMillis();
 	    int n = SearchServiceFactory.getService().getSearchModuleConfig().getMaxHits();
 
 	    TopDocs docs = searcher.search(query, n);
 	    long queryTime = System.currentTimeMillis() - startTime;
 	    if (log.isDebug()) log.debug("hits.length()=" + docs.totalHits);
-	    SearchResultsImpl searchResult = new SearchResultsImpl(mainIndexer, searcher, docs, query, analyzer, identity, roles, firstResult, maxResults, doHighlighting);
+	    SearchResultsImpl searchResult = new SearchResultsImpl(mainIndexer, searcher, docs, query, analyzer, identity, roles, firstResult, maxResults, doHighlighting, false);
 	    searchResult.setQueryTime(queryTime);
 	    searchResult.setNumberOfIndexDocuments(docs.totalHits);
 	    queryCount++;
@@ -238,6 +235,79 @@ public class SearchServiceImpl implements SearchService {
 			throw new ServiceNotAvailableException(ex.getMessage());
 		}
  	}
+	
+	@Override
+	public List<Long> doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles,
+			int firstResult, int maxResults, SortKey... orderBy)
+	throws ServiceNotAvailableException, ParseException, QueryException {
+		try {
+			if (!existIndex()) {
+				log.warn("Index does not exist, can't search for queryString: "+queryString);
+				throw new ServiceNotAvailableException("Index does not exist");
+			}
+			
+			log.info("queryString=" + queryString);
+			IndexSearcher searcher = getIndexSearcher();
+			BooleanQuery query = createQuery(queryString, condQueries);
+
+	    int n = SearchServiceFactory.getService().getSearchModuleConfig().getMaxHits();
+	    TopDocs docs;
+	    if(orderBy != null && orderBy.length > 0 && orderBy[0] != null) {
+	    	SortField[] sortFields = new SortField[orderBy.length];
+	    	for(int i=0; i<orderBy.length; i++) {
+	    		sortFields[i] = new SortField(orderBy[i].getKey(), SortField.Type.STRING_VAL, orderBy[i].isAsc());
+	    	}
+	    	Sort sort = new Sort(sortFields);
+	    	docs = searcher.search(query, n, sort);
+	    } else {
+	    	docs = searcher.search(query, n);
+	    }
+
+			int numOfDocs = Math.min(n, docs.totalHits);
+			Set<String> fields = new HashSet<String>();
+			fields.add(AbstractOlatDocument.DB_ID_NAME);
+			
+			List<Long> res = new ArrayList<Long>(maxResults + 1);
+	    for (int i=firstResult; i<numOfDocs && res.size() < maxResults; i++) {
+	    	Document doc = searcher.doc(docs.scoreDocs[i].doc, fields);
+	    	String dbKeyStr = doc.get(AbstractOlatDocument.DB_ID_NAME);
+	    	if(StringHelper.containsNonWhitespace(dbKeyStr)) {
+	    		res.add(Long.parseLong(dbKeyStr));
+	    	}
+	    }
+	    queryCount++;
+	    return res;
+		} catch (ServiceNotAvailableException naex) {
+			// pass exception 
+			throw new ServiceNotAvailableException(naex.getMessage());
+		} catch (ParseException pex) {
+			throw new ParseException("can not parse query=" + queryString);
+		} catch (Exception ex) {
+			log.warn("Exception in search", ex);
+			throw new ServiceNotAvailableException(ex.getMessage());
+		}
+	}
+	
+	private BooleanQuery createQuery(String queryString, List<String> condQueries)
+	throws ParseException {
+		BooleanQuery query = new BooleanQuery();
+		if(StringHelper.containsNonWhitespace(queryString)) {
+			QueryParser queryParser = new MultiFieldQueryParser(SearchService.OO_LUCENE_VERSION, fields, analyzer);
+			queryParser.setLowercaseExpandedTerms(false);//some add. fields are not tokenized and not lowered case
+	  	Query multiFieldQuery = queryParser.parse(queryString.toLowerCase());
+	  	query.add(multiFieldQuery, Occur.MUST);
+		}
+		
+		if(condQueries != null && !condQueries.isEmpty()) {
+			for(String condQueryString:condQueries) {
+				QueryParser condQueryParser = new QueryParser(SearchService.OO_LUCENE_VERSION, condQueryString, analyzer);
+				condQueryParser.setLowercaseExpandedTerms(false);
+		  	Query condQuery = condQueryParser.parse(condQueryString);
+		  	query.add(condQuery, Occur.MUST);
+			}
+		}
+		return query;
+	}
 
 	/**
 	 * Delegates impl to the searchSpellChecker.
@@ -288,7 +358,6 @@ public class SearchServiceImpl implements SearchService {
 		} catch (Exception e) {
 			log.error("", e);
 		}
-		indexer.close();
 	}
 
 	public boolean isEnabled() {
diff --git a/src/main/java/org/olat/search/service/indexer/Index.java b/src/main/java/org/olat/search/service/indexer/Index.java
index 7b6af341323601637242e85e9b9c3b73bd205a8f..956c9df855559e5938d789115bab98df01d433f3 100644
--- a/src/main/java/org/olat/search/service/indexer/Index.java
+++ b/src/main/java/org/olat/search/service/indexer/Index.java
@@ -53,6 +53,7 @@ public class Index {
 	
 	private OlatFullIndexer fullIndexer;
 	private SearchSpellChecker spellChecker;
+	private LifeFullIndexer lifeIndexer;
 
 	/**
 	 * 
@@ -61,18 +62,14 @@ public class Index {
 	 * @param restartInterval Restart interval of full-index in milliseconds.
 	 * @param indexInterval   Sleeping time in milliseconds between adding documents to index.
 	 */
-	public Index(SearchModule searchModuleConfig, SearchSpellChecker spellChecker, MainIndexer mainIndexer) {
+	public Index(SearchModule searchModuleConfig, SearchSpellChecker spellChecker, MainIndexer mainIndexer, LifeFullIndexer lifeIndexer) {
 		this.spellChecker = spellChecker;
 		this.indexPath = searchModuleConfig.getFullIndexPath();
 		this.tempIndexPath = searchModuleConfig.getFullTempIndexPath();
 		this.permanentIndexPath = searchModuleConfig.getFullPermanentIndexPath();
+		this.lifeIndexer = lifeIndexer;
 		
 		fullIndexer = new OlatFullIndexer(this, searchModuleConfig, mainIndexer);
-		fullIndexer.init();
-	}
-	
-	public void close() {
-		fullIndexer.close();
 	}
 
 	/**
@@ -82,6 +79,7 @@ public class Index {
 		// do not start search engine in test mode, some repository tests might lead to nullpointers
 		// since only dummy entries are generated (or fix the search service to handle those correctly)
 		if ( ! Settings.isJUnitTest()) {
+			lifeIndexer.fullIndex();
 		  fullIndexer.startIndexing();
 		}
 	}
@@ -141,6 +139,10 @@ public class Index {
 		FileUtils.copyDirContentsToDir(new File(tempIndexDir, "main") , indexDir ,true, "search indexer move tmp index");
 		log.info("New generated Index ready to use." );
 	}
+	
+	public OlatFullIndexer getIndexer() {
+		return fullIndexer;
+	}
 
 	/**
 	 * @return  Return current status of full-indexer.
diff --git a/src/main/java/org/olat/search/service/indexer/JmsIndexWork.java b/src/main/java/org/olat/search/service/indexer/JmsIndexWork.java
new file mode 100644
index 0000000000000000000000000000000000000000..257e89cd85f6c18da33725ab2d2575a4f9ffe3e0
--- /dev/null
+++ b/src/main/java/org/olat/search/service/indexer/JmsIndexWork.java
@@ -0,0 +1,64 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.search.service.indexer;
+
+import java.io.Serializable;
+
+/**
+ * 
+ * Initial date: 04.03.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class JmsIndexWork implements Serializable {
+
+	private static final long serialVersionUID = 8790611181901676640L;
+	
+	private String indexType;
+	private Long key;
+	
+	public JmsIndexWork() {
+		//
+	}
+	
+	public JmsIndexWork(String indexType, Long key) {
+		this.indexType = indexType;
+		this.key = key;
+	}
+	
+	public String getIndexType() {
+		return indexType;
+	}
+	
+	public void setIndexType(String indexType) {
+		this.indexType = indexType;
+	}
+	
+	public Long getKey() {
+		return key;
+	}
+	
+	public void setKey(Long key) {
+		this.key = key;
+	}
+	
+	
+
+}
diff --git a/src/main/java/org/olat/search/service/indexer/JmsIndexer.java b/src/main/java/org/olat/search/service/indexer/JmsIndexer.java
new file mode 100644
index 0000000000000000000000000000000000000000..6f4868d4b8f54f5e8dc4e85159b11175d5bf7c73
--- /dev/null
+++ b/src/main/java/org/olat/search/service/indexer/JmsIndexer.java
@@ -0,0 +1,320 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.search.service.indexer;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jms.ConnectionFactory;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageConsumer;
+import javax.jms.MessageListener;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.jms.Session;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.IndexWriterConfig.OpenMode;
+import org.apache.lucene.index.LogDocMergePolicy;
+import org.apache.lucene.index.LogMergePolicy;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.search.SearchModule;
+import org.olat.search.SearchService;
+import org.olat.search.model.AbstractOlatDocument;
+import org.olat.search.service.SearchServiceImpl;
+
+/**
+ * TODO or not: to make the Indexer cluster wide functional. It would be
+ * possible to create on the fly an IndexWriter with a doInSync.
+ * 
+ * Initial date: 04.03.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class JmsIndexer implements MessageListener, LifeFullIndexer {
+	private static final int INDEX_MERGE_FACTOR = 1000;
+	private static final OLog log = Tracing.createLoggerFor(JmsIndexer.class);
+	
+	private Queue jmsQueue;
+	private Session indexerSession;
+	private MessageConsumer consumer;
+	private ConnectionFactory connectionFactory;
+	private QueueConnection connection;
+	
+	private SearchService searchService;
+
+	private String permanentIndexPath;
+	private DirectoryReader reader;
+	private IndexWriter permanentIndexWriter;
+	
+	private double ramBufferSizeMB;
+	private boolean useCompoundFile;
+	private boolean indexingNode;
+	
+	private List<LifeIndexer> indexers = new ArrayList<LifeIndexer>();
+	
+	public JmsIndexer(SearchModule searchModuleConfig) {
+		indexingNode = searchModuleConfig.isSearchServiceEnabled();
+    ramBufferSizeMB = searchModuleConfig.getRAMBufferSizeMB();
+    useCompoundFile = searchModuleConfig.getUseCompoundFile();
+		permanentIndexPath = searchModuleConfig.getFullPermanentIndexPath();
+	}
+
+	public Queue getJmsQueue() {
+		return jmsQueue;
+	}
+	
+	/**
+	 * [Used by Spring]
+	 * @param jmsQueue
+	 */
+	public void setJmsQueue(Queue jmsQueue) {
+		this.jmsQueue = jmsQueue;
+	}
+
+	/**
+	 * [Used by Spring]
+	 * @param connectionFactory
+	 */
+	public void setConnectionFactory(ConnectionFactory connectionFactory) {
+		this.connectionFactory = connectionFactory;
+	}
+	
+	/**
+	 * [used by Spring]
+	 * @param searchService
+	 */
+	public void setSearchService(SearchService searchService) {
+		this.searchService = searchService;
+	}
+
+	public void setIndexers(List<LifeIndexer> indexers) {
+		if(indexers != null) {
+			for(LifeIndexer indexer:indexers){
+				addIndexer(indexer);
+			}
+		}
+	}
+
+	@Override
+	public void addIndexer(LifeIndexer indexer) {
+		indexers.add(indexer);
+	}
+	
+	public List<LifeIndexer> getIndexerByType(String type) {
+		List<LifeIndexer> indexerByType = new ArrayList<LifeIndexer>();
+		for(LifeIndexer indexer:indexers) {
+			if(type.equals(indexer.getSupportedTypeName())) {
+				indexerByType.add(indexer);
+			}
+		}
+		return indexerByType;
+	}
+
+	/**
+	 * [used by Spring]
+	 * @throws JMSException
+	 */
+	public void springInit() throws JMSException {
+		initQueue();
+		initDirectory();
+	}
+	
+	public void initQueue() throws JMSException {
+		connection = (QueueConnection)connectionFactory.createConnection();
+		connection.start();
+		log.info("springInit: JMS connection started with connectionFactory=" + connectionFactory);
+
+		if(indexingNode) {
+			//listen to the queue only if indexing node
+			indexerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+			consumer = indexerSession.createConsumer(jmsQueue);
+			consumer.setMessageListener(this);
+		}
+	}
+	
+	public void initDirectory() {
+		try {
+			File tempIndexDir = new File(permanentIndexPath);
+			Directory indexPath = FSDirectory.open(tempIndexDir);
+			
+			if(indexingNode) {
+				permanentIndexWriter = new IndexWriter(indexPath, newIndexWriterConfig());
+				permanentIndexWriter.commit();
+			}
+	
+			reader = DirectoryReader.open(indexPath);
+		} catch (IOException e) {
+			log.error("", e);
+		}
+	}
+	
+	public LogMergePolicy newLogMergePolicy() {
+		LogMergePolicy logmp = new LogDocMergePolicy();
+		logmp.setUseCompoundFile(useCompoundFile);
+		logmp.setCalibrateSizeByDeletes(true);
+		logmp.setMergeFactor(INDEX_MERGE_FACTOR);
+		return logmp;
+	}
+	
+	public IndexWriterConfig newIndexWriterConfig() {
+		Analyzer analyzer = new StandardAnalyzer(SearchService.OO_LUCENE_VERSION);
+		IndexWriterConfig indexWriterConfig = new IndexWriterConfig(SearchService.OO_LUCENE_VERSION, analyzer);
+		indexWriterConfig.setMergePolicy(newLogMergePolicy());
+		indexWriterConfig.setRAMBufferSizeMB(ramBufferSizeMB);// for better performance set to 48MB (see lucene docu 'how to make indexing faster")
+		indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
+		return indexWriterConfig;
+	}
+	
+	/**
+	 * [used by Spring]
+	 */
+	public void stop() {
+		closeQueue();
+		closeWriter();
+	}
+	
+	public void closeQueue() {
+		if(consumer != null) {
+			try {
+				consumer.close();
+			} catch (JMSException e) {
+				log.error("", e);
+			}
+		}
+		if(connection != null) {
+			try {
+				indexerSession.close();
+				connection.close();
+			} catch (JMSException e) {
+				log.error("", e);
+			}
+		}
+	}
+	
+	public void closeWriter() {
+		try {
+			permanentIndexWriter.commit();
+			permanentIndexWriter.close();
+		} catch (IOException e) {
+			log.error("", e);
+		}
+	}
+	
+	@Override
+	public void fullIndex() {
+		for(LifeIndexer indexer:indexers) {
+			indexer.fullIndex(this);
+		}
+	}
+
+	@Override
+	public void indexDocument(String type, Long key) {
+		QueueSender sender;
+		QueueSession session;
+		try {
+			JmsIndexWork workUnit = new JmsIndexWork(type, key);
+			session = connection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE );
+			ObjectMessage message = session.createObjectMessage();
+			message.setObject(workUnit);
+
+			sender = session.createSender(getJmsQueue());
+			sender.send( message );
+			session.close();
+		} catch (JMSException e) {
+			log.error("", e );
+		}
+	}
+	
+	@Override
+	public void onMessage(Message message) {
+		if(message instanceof ObjectMessage) {
+			try {
+				ObjectMessage objMsg = (ObjectMessage)message;
+				JmsIndexWork workUnit = (JmsIndexWork)objMsg.getObject();
+				doIndex(workUnit);
+				message.acknowledge();
+			} catch (JMSException e) {
+				log.error("", e);
+			}
+		}
+	}
+	
+	private void doIndex(JmsIndexWork workUnit) {
+		if(searchService instanceof SearchServiceImpl) {
+			String type = workUnit.getIndexType();
+			List<LifeIndexer> indexers = getIndexerByType(type);
+			for(LifeIndexer indexer:indexers) {
+				indexer.indexDocument(workUnit.getKey(), this);
+			}
+		}
+	}
+	
+	private DirectoryReader getReader() throws IOException {
+		DirectoryReader newReader = DirectoryReader.openIfChanged(reader);
+		if(newReader != null) {
+			reader = newReader;
+		}
+		return reader;
+	}
+	
+	/**
+	 * Add or update a lucene document in the permanent index.
+	 * @param uuid
+	 * @param document
+	 */
+	@Override
+	public void addDocument(Document document) {
+		try {
+			String resourceUrl = document.get(AbstractOlatDocument.RESOURCEURL_FIELD_NAME);
+			Term uuidTerm = new Term(AbstractOlatDocument.RESOURCEURL_FIELD_NAME, resourceUrl);
+
+			DirectoryReader reader = getReader();
+			IndexSearcher searcher = new IndexSearcher(reader);
+	    TopDocs hits = searcher.search(new TermQuery(uuidTerm), 10);
+			if(hits.totalHits > 0) {
+				permanentIndexWriter.updateDocument(uuidTerm, document);
+			} else {
+				permanentIndexWriter.addDocument(document);
+				permanentIndexWriter.commit();
+			}
+		} catch (IOException e) {
+			log.error("", e);
+		}
+	}
+}
diff --git a/src/main/java/org/olat/search/service/indexer/LifeFullIndexer.java b/src/main/java/org/olat/search/service/indexer/LifeFullIndexer.java
new file mode 100644
index 0000000000000000000000000000000000000000..952b8215ae2a4ba0fc65f0e2b9446e9aecd14a98
--- /dev/null
+++ b/src/main/java/org/olat/search/service/indexer/LifeFullIndexer.java
@@ -0,0 +1,52 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.search.service.indexer;
+
+import org.apache.lucene.document.Document;
+
+/**
+ * 
+ * Initial date: 05.03.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public interface LifeFullIndexer {
+	
+	public void addIndexer(LifeIndexer indexer);
+	
+	/**
+	 * Start a full index
+	 */
+	public void fullIndex();
+	
+	/**
+	 * Ask to index the document with the specified key
+	 * @param type
+	 * @param key
+	 */
+	public void indexDocument(String type, Long key);
+	
+	/**
+	 * Add a document to the index
+	 * @param doc
+	 */
+	public void addDocument(Document doc);
+
+}
diff --git a/src/main/java/org/olat/search/service/indexer/LifeIndexer.java b/src/main/java/org/olat/search/service/indexer/LifeIndexer.java
new file mode 100644
index 0000000000000000000000000000000000000000..38bbcaae8bb87ad2b7d3959c6a9c26420350da1f
--- /dev/null
+++ b/src/main/java/org/olat/search/service/indexer/LifeIndexer.java
@@ -0,0 +1,44 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.search.service.indexer;
+
+/**
+ * 
+ * Initial date: 05.03.2013<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+public interface LifeIndexer {
+	
+	public String getSupportedTypeName();
+	
+	/**
+	 * Trigger a full index
+	 * @param indexWriter
+	 */
+	public void fullIndex(LifeFullIndexer indexWriter);
+
+	/**
+	 * Trigger only the specificied object/document
+	 * @param key
+	 * @param indexWriter
+	 */
+	public void indexDocument(Long key, LifeFullIndexer indexWriter);
+	
+}
diff --git a/src/main/java/org/olat/search/service/indexer/OlatFullIndexer.java b/src/main/java/org/olat/search/service/indexer/OlatFullIndexer.java
index 7daf6687894477292a8ff1b53de55df52030dd6e..5ca4ce7d6be83bac1e81fa32ed4f64fb8c200840 100644
--- a/src/main/java/org/olat/search/service/indexer/OlatFullIndexer.java
+++ b/src/main/java/org/olat/search/service/indexer/OlatFullIndexer.java
@@ -36,17 +36,11 @@ import org.apache.lucene.LucenePackage;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
 import org.apache.lucene.document.Document;
-import org.apache.lucene.index.DirectoryReader;
-import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
 import org.apache.lucene.index.LogDocMergePolicy;
 import org.apache.lucene.index.LogMergePolicy;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.FSDirectory;
 import org.olat.core.commons.persistence.DBFactory;
@@ -54,7 +48,6 @@ import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.search.SearchModule;
 import org.olat.search.SearchService;
-import org.olat.search.model.AbstractOlatDocument;
 import org.olat.search.model.OlatDocument;
 import org.olat.search.service.SearchResourceContext;
 
@@ -71,14 +64,12 @@ public class OlatFullIndexer {
 	private int numberIndexWriter = 5;
 
 	private String  tempIndexPath;
-	private String permanentIndexPath;
 
 	/**
 	 * Reference to indexer for done callback.
 	 */
 	private Index index;
 	private IndexWriter indexWriter;
-	private IndexWriter permanentIndexWriter;
 	
 	
 	/** Flag to stop indexing. */
@@ -121,7 +112,6 @@ public class OlatFullIndexer {
     this.index = index;
     this.mainIndexer = mainIndexer;
     tempIndexPath = searchModuleConfig.getFullTempIndexPath();
-    permanentIndexPath = searchModuleConfig.getFullPermanentIndexPath();
     indexInterval = searchModuleConfig.getIndexInterval();
     numberIndexWriter = searchModuleConfig.getNumberIndexWriter();
     documentsPerInterval = searchModuleConfig.getDocumentsPerInterval();
@@ -162,16 +152,7 @@ public class OlatFullIndexer {
 		logmp.setMergeFactor(INDEX_MERGE_FACTOR);
 		return logmp;
 	}
-	
-	public void init() {
-		try {
-			File tempIndexDir = new File(permanentIndexPath);
-			Directory indexPath = FSDirectory.open(tempIndexDir);
-			permanentIndexWriter = new IndexWriter(indexPath, newIndexWriterConfig());
-		} catch (IOException e) {
-			log.error("", e);
-		}
-	}
+
 	
 	public IndexWriterConfig newIndexWriterConfig() {
 		Analyzer analyzer = new StandardAnalyzer(SearchService.OO_LUCENE_VERSION);
@@ -321,46 +302,8 @@ public class OlatFullIndexer {
 		}
 	}
 	
-	public void close() {
-		try {
-			permanentIndexWriter.commit();
-			permanentIndexWriter.close();
-		} catch (IOException e) {
-			log.error("", e);
-		}
-	}
-	
-	/**
-	 * Add or update a lucene document in the permanent index.
-	 * @param uuid
-	 * @param document
-	 */
-	public void addPermDocument(Document document) {
-		try {
-			String resourceUrl = document.get(AbstractOlatDocument.RESOURCEURL_FIELD_NAME);
-			Term uuidTerm = new Term(AbstractOlatDocument.RESOURCEURL_FIELD_NAME, resourceUrl);
-			Directory dir = FSDirectory.open(new File(permanentIndexPath));
-			if(DirectoryReader.indexExists(dir)) {
-				IndexReader reader = DirectoryReader.open(dir);
-				IndexSearcher searcher = new IndexSearcher(reader);
-				TermQuery query = new TermQuery(uuidTerm);
-	      TopDocs hits = searcher.search(query, 10);
-				boolean exists = hits.totalHits > 0;
-				if(exists) {
-					permanentIndexWriter.updateDocument(uuidTerm, document);
-				} else {
-					permanentIndexWriter.addDocument(document);
-					permanentIndexWriter.commit();
-				}
-				reader.close();
-			} else {
-				permanentIndexWriter.addDocument(document);
-				permanentIndexWriter.commit();
-			}
-		} catch (IOException e) {
-			log.error("", e);
-		}
-	}
+
+
 	
 	/**
 	 * Callback to addDocument to indexWriter.
diff --git a/src/main/java/org/olat/search/service/indexer/QuestionItemIndexer.java b/src/main/java/org/olat/search/service/indexer/QuestionItemIndexer.java
index f1a80bcffe7c4b1ac62e4724daf6b35e13ec87b5..5f606d2b9c6bc322804c5b2ac118657bddd235b8 100644
--- a/src/main/java/org/olat/search/service/indexer/QuestionItemIndexer.java
+++ b/src/main/java/org/olat/search/service/indexer/QuestionItemIndexer.java
@@ -19,14 +19,14 @@
  */
 package org.olat.search.service.indexer;
 
-import java.io.IOException;
 import java.util.List;
 
 import org.apache.lucene.document.Document;
 import org.olat.core.CoreSpringFactory;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.QuestionPoolService;
-import org.olat.search.model.OlatDocument;
+import org.olat.modules.qpool.manager.QuestionItemDocumentFactory;
+import org.olat.modules.qpool.model.QuestionItemDocument;
 import org.olat.search.service.SearchResourceContext;
 
 /**
@@ -35,51 +35,39 @@ import org.olat.search.service.SearchResourceContext;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class QuestionItemIndexer extends AbstractHierarchicalIndexer {
-	
-	public static final String TYPE = "type.question.item";
-	
+public class QuestionItemIndexer implements LifeIndexer {
+
 	private static final int BATCH_SIZE = 100;
 
 	@Override
 	public String getSupportedTypeName() {
-		return TYPE;
+		return QuestionItemDocument.TYPE;
 	}
-	
+
 	@Override
-	public void doIndex(SearchResourceContext parentResourceContext, Object businessObj, OlatFullIndexer indexWriter)
-	throws IOException,InterruptedException {
+	public void indexDocument(Long key, LifeFullIndexer indexWriter) {
+		QuestionItemDocumentFactory docFactory = CoreSpringFactory.getImpl(QuestionItemDocumentFactory.class);
 		
-		QuestionPoolService qpoolService = CoreSpringFactory.getImpl(QuestionPoolService.class);
+		SearchResourceContext ctxt = new SearchResourceContext();
+		Document doc = docFactory.createDocument(ctxt, key);
+		indexWriter.addDocument(doc);	
+	}
 
+	@Override
+	public void fullIndex(LifeFullIndexer indexWriter) {
+		QuestionPoolService qpoolService = CoreSpringFactory.getImpl(QuestionPoolService.class);
+		QuestionItemDocumentFactory docFactory = CoreSpringFactory.getImpl(QuestionItemDocumentFactory.class);
+		SearchResourceContext ctxt = new SearchResourceContext();
+		
 		int counter = 0;
 		List<QuestionItem> items;
 		do {
 			items = qpoolService.getAllItems(counter, BATCH_SIZE);
 			for(QuestionItem item:items) {
-				processItem(item, parentResourceContext, indexWriter);
+				Document doc = docFactory.createDocument(ctxt, item);
+				indexWriter.addDocument(doc);
 			}
 			counter += items.size();
 		} while(items.size() == BATCH_SIZE);
 	}
-	
-	private void processItem(QuestionItem item, SearchResourceContext parentResourceContext, OlatFullIndexer indexWriter) {
-		OlatDocument oDocument = new OlatDocument();
-		/*Identity author = artefact.getAuthor();
-		if(author != null) {
-			document.setAuthor(author.getName());
-		}*/
-		oDocument.setCreatedDate(item.getCreationDate());
-		oDocument.setTitle(item.getSubject());
-		oDocument.setDescription(item.getDescription());
-		oDocument.setResourceUrl("[QuestionItem:" + item.getKey() + "]");
-		oDocument.setDocumentType(TYPE);
-		oDocument.setCssIcon("o_qitem_icon");
-		oDocument.setParentContextType(parentResourceContext.getParentContextType());
-		oDocument.setParentContextName(parentResourceContext.getParentContextName());
-		oDocument.setContent(item.getDescription());
-		
-		Document document = oDocument.getLuceneDocument();
-		indexWriter.addPermDocument(document);
-	}
 }
diff --git a/src/main/java/org/olat/search/service/searcher/SearchClient.java b/src/main/java/org/olat/search/service/searcher/SearchClient.java
index 214b8bafe46defedd98b85dbcc31ad22e955a827..adbda778b21fd88ee2bb9d7b3aeec0a3be13f7f7 100644
--- a/src/main/java/org/olat/search/service/searcher/SearchClient.java
+++ b/src/main/java/org/olat/search/service/searcher/SearchClient.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.lucene.queryparser.classic.ParseException;
+import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
 import org.olat.search.QueryException;
@@ -42,6 +43,10 @@ public interface SearchClient {
 	
 	public SearchResults doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles, int firstResult, int maxResults, boolean doHighlighting)
 			throws ServiceNotAvailableException, ParseException, QueryException;
+	
+	public List<Long> doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles,
+			int firstResult, int maxResults, SortKey... orderBy)
+			throws ServiceNotAvailableException, ParseException, QueryException;
 
 	public Set<String> spellCheck(String query) throws ServiceNotAvailableException;
 
diff --git a/src/main/java/org/olat/search/service/searcher/SearchClientLocal.java b/src/main/java/org/olat/search/service/searcher/SearchClientLocal.java
index ab59902eb250bd911f1da519e0b6b907895e1fd6..9aa09ed299eebd7633dc0b89174bcc801b7689da 100644
--- a/src/main/java/org/olat/search/service/searcher/SearchClientLocal.java
+++ b/src/main/java/org/olat/search/service/searcher/SearchClientLocal.java
@@ -23,6 +23,7 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.lucene.queryparser.classic.ParseException;
+import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
 import org.olat.search.QueryException;
@@ -43,11 +44,19 @@ import org.olat.search.service.SearchServiceFactory;
 public class SearchClientLocal implements SearchClient {
 
 	@Override
-	public SearchResults doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles, int firstResult, int maxReturns, boolean doHighlighting)
+	public SearchResults doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles,
+			int firstResult, int maxReturns, boolean doHighlighting)
 	throws ServiceNotAvailableException, ParseException, QueryException {
 		return SearchServiceFactory.getService().doSearch(queryString, condQueries, identity, roles, firstResult, maxReturns, doHighlighting);
 	}
 
+	@Override
+	public List<Long> doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles,
+			int firstResult, int maxResults, SortKey... orderBy)
+	throws ServiceNotAvailableException, ParseException, QueryException {
+		return SearchServiceFactory.getService().doSearch(queryString, condQueries, identity, roles, firstResult, maxResults, orderBy);
+	}
+
 	@Override
 	public Set<String> spellCheck(String query) throws ServiceNotAvailableException {
 		return SearchServiceFactory.getService().spellCheck(query);
diff --git a/src/main/java/org/olat/search/service/searcher/SearchClientProxy.java b/src/main/java/org/olat/search/service/searcher/SearchClientProxy.java
index e489a9aa254396f70c1f6a9facbe3396b80e75eb..f2e415d4fca582043f0c5af841a971fa8adf0a5c 100644
--- a/src/main/java/org/olat/search/service/searcher/SearchClientProxy.java
+++ b/src/main/java/org/olat/search/service/searcher/SearchClientProxy.java
@@ -46,6 +46,7 @@ import javax.jms.Session;
 import javax.jms.TextMessage;
 
 import org.apache.lucene.queryparser.classic.ParseException;
+import org.olat.core.commons.persistence.SortKey;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
 import org.olat.core.logging.OLATRuntimeException;
@@ -199,6 +200,17 @@ public class SearchClientProxy implements SearchClient {
 		}
 	}
 	
+	
+	
+	@Override
+	public List<Long> doSearch(String queryString, List<String> condQueries,
+			Identity identity, Roles roles, int firstResult, int maxResults,
+			SortKey... orderBy) throws ServiceNotAvailableException, ParseException,
+			QueryException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
 	/**
 	 * Uses Request/reply mechanism for synchronous operation.
 	 * @see org.olat.search.service.searcher.OLATSearcher#spellCheck(java.lang.String)
diff --git a/src/main/java/org/olat/search/service/searcher/SearchResultsImpl.java b/src/main/java/org/olat/search/service/searcher/SearchResultsImpl.java
index c73ab8053d1259999c3b7edc37057668bb66d584..ee9ec4429cf6a2e69f42ace14b55cd1a1492da8e 100644
--- a/src/main/java/org/olat/search/service/searcher/SearchResultsImpl.java
+++ b/src/main/java/org/olat/search/service/searcher/SearchResultsImpl.java
@@ -93,9 +93,10 @@ public class SearchResultsImpl implements SearchResults {
 	 * @throws IOException
 	 */
 	public SearchResultsImpl(Indexer mainIndexer, IndexSearcher searcher, TopDocs docs, Query query, Analyzer analyzer, Identity identity,
-			Roles roles, int firstResult, int maxReturns, boolean doHighlighting) throws IOException{
+			Roles roles, int firstResult, int maxReturns, boolean doHighlighting, boolean onlyDbKeys)
+	throws IOException {
 		this.mainIndexer = mainIndexer;
-		resultList = initResultList(identity, roles, query, analyzer, searcher, docs, firstResult, maxReturns, doHighlighting);
+		resultList = initResultList(identity, roles, query, analyzer, searcher, docs, firstResult, maxReturns, doHighlighting, onlyDbKeys);
 	}
 	
 	/**
@@ -164,10 +165,14 @@ public class SearchResultsImpl implements SearchResults {
 	}
 
 	private List<ResultDocument> initResultList(Identity identity, Roles roles, Query query, Analyzer analyzer, IndexSearcher searcher, TopDocs docs,
-			int firstResult, int maxReturns, final boolean doHighlight) throws IOException {
+			int firstResult, int maxReturns, final boolean doHighlight, boolean onlyDbKeys)
+	throws IOException {
 
 		Set<String> fields = AbstractOlatDocument.getFields();
-		if(!doHighlight) {
+		if(onlyDbKeys) {
+			fields.clear();
+			fields.add(AbstractOlatDocument.DB_ID_NAME);
+		} else if(!doHighlight) {
 			fields.remove(AbstractOlatDocument.CONTENT_FIELD_NAME);
 		}
 		
@@ -183,6 +188,7 @@ public class SearchResultsImpl implements SearchResults {
 			} else {
 				doc = searcher.doc(docs.scoreDocs[i].doc, fields);
 			}
+			
 			String reservedTo = doc.get(AbstractOlatDocument.RESERVED_TO);
 			if(StringHelper.containsNonWhitespace(reservedTo) && !"public".equals(reservedTo)
 					&& !reservedTo.contains(identity.getKey().toString())) {
diff --git a/src/main/resources/database/mysql/alter_8_4_0_to_9_0_0.sql b/src/main/resources/database/mysql/alter_8_4_0_to_9_0_0.sql
index a56993ae711339d369a5f2e8b4c000b812770a7f..54d47581ac7b6232864ed12f2f6abeee0d949d3e 100644
--- a/src/main/resources/database/mysql/alter_8_4_0_to_9_0_0.sql
+++ b/src/main/resources/database/mysql/alter_8_4_0_to_9_0_0.sql
@@ -22,6 +22,8 @@ create table if not exists o_qp_study_field (
    creationdate datetime not null,
    lastmodified datetime not null,
    q_field varchar(255) not null,
+   q_mat_path_ids varchar(1024),
+   q_mat_path_names varchar(2048),
    fk_parent_field bigint,
    primary key (id)
 );
diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties
index 32a27d07da5ffa6bfda4378037050ccb44741339..21cd3d4088155bed7fa1afd8cdabcb4b5a39547d 100644
--- a/src/main/resources/serviceconfig/olat.properties
+++ b/src/main/resources/serviceconfig/olat.properties
@@ -634,6 +634,7 @@ cluster.singleton.services = enabled
 # SingleVM jms.broker.url
 jms.broker.url=vm://embedded?broker.persistent=false
 search.broker.url=vm://embedded?broker.persistent=false
+index.broker.url=vm://embedded?broker.persistent=false
 codepoint.jms.broker.url=vm://embedded?broker.persistent=false
 # Cluster (remote) jms.broker.url
 #jms.broker.url=failover:(tcp://localhost:61616?wireFormat.maxInactivityDuration=0)
diff --git a/src/test/java/org/olat/modules/qpool/manager/CollectionDAOTest.java b/src/test/java/org/olat/modules/qpool/manager/CollectionDAOTest.java
index a10b1336d4ad315880cb5a7b9f366efdb7492edb..fa4aacff37e8dd491f4b83af45f4bf59e831beb8 100644
--- a/src/test/java/org/olat/modules/qpool/manager/CollectionDAOTest.java
+++ b/src/test/java/org/olat/modules/qpool/manager/CollectionDAOTest.java
@@ -19,6 +19,7 @@
  */
 package org.olat.modules.qpool.manager;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 
@@ -35,6 +36,7 @@ import org.olat.test.JunitTestHelper;
 import org.olat.test.OlatTestCase;
 import org.springframework.beans.factory.annotation.Autowired;
 
+
 /**
  * 
  * Initial date: 22.02.2013<br>
@@ -99,19 +101,46 @@ public class CollectionDAOTest extends OlatTestCase {
 		dbInstance.commit();//check if it's alright
 		
 		//load the items of the collection
-		List<QuestionItem> items = collectionDao.getItemsOfCollection(coll, 0, -1);
+		List<QuestionItem> items = collectionDao.getItemsOfCollection(coll, null, 0, -1);
 		Assert.assertNotNull(items);
 		Assert.assertEquals(2, items.size());
 		Assert.assertTrue(items.contains(item1));
 		Assert.assertTrue(items.contains(item2));
+		//count them
+		int numOfItems = collectionDao.countItemsOfCollection(coll);
+		Assert.assertEquals(2, numOfItems);
+		
+		//load limit sub set
+		List<QuestionItem> limitedItems = collectionDao.getItemsOfCollection(coll, Collections.singletonList(item1.getKey()), 0, -1);
+		Assert.assertNotNull(limitedItems);
+		Assert.assertEquals(1, limitedItems.size());
+		Assert.assertTrue(limitedItems.contains(item1));
 	}
 	
+	@Test
+	public void getItemKeysOfCollection() {
+		//create a collection with 2 items
+		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Coll-Onwer-4-" + UUID.randomUUID().toString());
+		QuestionItemCollection coll = collectionDao.createCollection("NGC collection 4", id);
+		QuestionItem item1 = questionDao.create(null, "NGC 99", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, null, QuestionType.FIB);
+		QuestionItem item2 = questionDao.create(null, "NGC 101", QTIConstants.QTI_12_FORMAT, Locale.GERMAN.getLanguage(), null, null, QuestionType.FIB);
+		collectionDao.addItemToCollection(item1, coll);
+		collectionDao.addItemToCollection(item2, coll);
+		dbInstance.commit();//check if it's alright
+		
+		//load the items of the collection
+		List<Long> items = collectionDao.getItemKeysOfCollection(coll);
+		Assert.assertNotNull(items);
+		Assert.assertEquals(2, items.size());
+		Assert.assertTrue(items.contains(item1.getKey()));
+		Assert.assertTrue(items.contains(item2.getKey()));
+	}
 	
 	@Test
 	public void getCollections_myOhMy() {
-		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Coll-Onwer-3-" + UUID.randomUUID().toString());
-		QuestionItemCollection coll1 = collectionDao.createCollection("NGC collection part. 4", id);
-		QuestionItemCollection coll2 = collectionDao.createCollection("NGC collection part. 5", id);
+		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Coll-Onwer-5-" + UUID.randomUUID().toString());
+		QuestionItemCollection coll1 = collectionDao.createCollection("NGC collection part. 6", id);
+		QuestionItemCollection coll2 = collectionDao.createCollection("NGC collection part. 7", id);
 		dbInstance.commit();//check if it's alright
 		
 		//load the items of the collection
@@ -121,5 +150,4 @@ public class CollectionDAOTest extends OlatTestCase {
 		Assert.assertTrue(items.contains(coll1));
 		Assert.assertTrue(items.contains(coll2));
 	}
-	
 }
diff --git a/src/test/java/org/olat/modules/qpool/manager/PoolDAOTest.java b/src/test/java/org/olat/modules/qpool/manager/PoolDAOTest.java
index 210f92d2e34b9e57820291b247b62ec9679a92c6..7096f5d626ca4525e62beb597f8584484a03c6e2 100644
--- a/src/test/java/org/olat/modules/qpool/manager/PoolDAOTest.java
+++ b/src/test/java/org/olat/modules/qpool/manager/PoolDAOTest.java
@@ -73,7 +73,8 @@ public class PoolDAOTest extends OlatTestCase {
 		List<Pool> pools = poolDao.getPools(0, -1);
 		Assert.assertNotNull(pools);
 		Assert.assertTrue(pools.size() >= 1);
-		int numOfPools = poolDao.getNumOfPools();
+		//count
+		int numOfPools = poolDao.countPools();
 		Assert.assertEquals(pools.size(), numOfPools);
 		//retrieve our pool
 		boolean foundIt = false;
@@ -110,10 +111,33 @@ public class PoolDAOTest extends OlatTestCase {
 		poolDao.addItemToPool(item, pool);
 		dbInstance.commitAndCloseSession();
 		
-		List<QuestionItem> items = poolDao.getItemsOfPool(pool, 0 , -1);
+		//retrieve
+		List<QuestionItem> items = poolDao.getItemsOfPool(pool, null, 0 , -1);
 		Assert.assertNotNull(items);
 		Assert.assertEquals(1, items.size());
 		Assert.assertTrue(items.contains(item));
+		//count
+		int numOfItems = poolDao.getNumOfItemsInPool(pool);
+		Assert.assertEquals(1, numOfItems);
+	}
+	
+	@Test
+	public void getPools_ofItem() {
+		//create a pool
+		String name = "NGC-" + UUID.randomUUID().toString();
+		Pool pool1 = poolDao.createPool(name);
+		Pool pool2 = poolDao.createPool(name + "-b");
+		QuestionItem item = questionItemDao.create(null, "Galaxy", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		poolDao.addItemToPool(item, pool1);
+		poolDao.addItemToPool(item, pool2);
+		dbInstance.commitAndCloseSession();
+		
+		//retrieve the pools
+		List<Pool> pools = poolDao.getPools(item);
+		Assert.assertNotNull(pools);
+		Assert.assertEquals(2, pools.size());
+		Assert.assertTrue(pools.contains(pool1));
+		Assert.assertTrue(pools.contains(pool2));
 	}
 	
 	@Test
@@ -126,14 +150,14 @@ public class PoolDAOTest extends OlatTestCase {
 		dbInstance.commitAndCloseSession();
 		
 		//check the pool and remove the items
-		List<QuestionItem> items = poolDao.getItemsOfPool(pool, 0 , -1);
+		List<QuestionItem> items = poolDao.getItemsOfPool(pool, null, 0 , -1);
 		Assert.assertEquals(1, items.size());
 		int count = poolDao.deleteFromPools(items);
 		Assert.assertEquals(1, count);
 		dbInstance.commitAndCloseSession();
 		
 		//check if the pool is empty
-		List<QuestionItem> emptyItems = poolDao.getItemsOfPool(pool, 0 , -1);
+		List<QuestionItem> emptyItems = poolDao.getItemsOfPool(pool, null, 0 , -1);
 		Assert.assertTrue(emptyItems.isEmpty());
 	}
 	
diff --git a/src/test/java/org/olat/modules/qpool/manager/QuestionDAOTest.java b/src/test/java/org/olat/modules/qpool/manager/QuestionDAOTest.java
index 5efa4c1028810f2a953a70072ef4305c6851a0e4..24e2f0ab8e8381b8140aeafcbb31f959ed1ad1cb 100644
--- a/src/test/java/org/olat/modules/qpool/manager/QuestionDAOTest.java
+++ b/src/test/java/org/olat/modules/qpool/manager/QuestionDAOTest.java
@@ -19,6 +19,8 @@
  */
 package org.olat.modules.qpool.manager;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.UUID;
@@ -34,6 +36,7 @@ import org.olat.group.manager.BusinessGroupDAO;
 import org.olat.ims.qti.QTIConstants;
 import org.olat.modules.qpool.QuestionItem;
 import org.olat.modules.qpool.QuestionType;
+import org.olat.resource.OLATResource;
 import org.olat.test.JunitTestHelper;
 import org.olat.test.OlatTestCase;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -95,11 +98,17 @@ public class QuestionDAOTest extends OlatTestCase {
 		int numOfItems = questionDao.countItems(id);
 		Assert.assertEquals(2, numOfItems);
 		//retrieve the items of the author
-		List<QuestionItem> items = questionDao.getItems(id, 0, -1);
+		List<QuestionItem> items = questionDao.getItems(id, null, 0, -1);
 		Assert.assertNotNull(items);
 		Assert.assertEquals(2, items.size());
 		Assert.assertTrue(items.contains(item1));
 		Assert.assertTrue(items.contains(item2));
+		
+		//limit the list
+		List<QuestionItem> limitedItems = questionDao.getItems(id, Collections.singletonList(item1.getKey()), 0, -1);
+		Assert.assertNotNull(limitedItems);
+		Assert.assertEquals(1, limitedItems.size());
+		Assert.assertTrue(limitedItems.contains(item1));
 	}
 
 	@Test
@@ -123,12 +132,36 @@ public class QuestionDAOTest extends OlatTestCase {
 		markManager.setMark(item2, id, null, "[QuestionItem:" + item2 + "]");
 		dbInstance.commitAndCloseSession();
 		
-		List<QuestionItem> favorits = questionDao.getFavoritItems(id, 0, -1);
+		List<QuestionItem> favorits = questionDao.getFavoritItems(id, null, 0, -1);
 		Assert.assertNotNull(favorits);
 		Assert.assertEquals(2, favorits.size());
 		Assert.assertTrue(favorits.contains(item1));
 		Assert.assertTrue(favorits.contains(item2));
 		Assert.assertFalse(favorits.contains(item3));
+		
+		//limit to the first favorit
+		List<QuestionItem> limitedFavorits = questionDao.getFavoritItems(id, Collections.singletonList(item1.getKey()), 0, -1);
+		Assert.assertNotNull(limitedFavorits);
+		Assert.assertEquals(1, limitedFavorits.size());
+		Assert.assertTrue(limitedFavorits.contains(item1));
+	}
+	
+	@Test
+	public void getFavoritItemKeys() {
+		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("fav-item-" + UUID.randomUUID().toString());
+		QuestionItem item1 = questionDao.create(id, "NGC 331", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		QuestionItem item2 = questionDao.create(id, "NGC 332", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		QuestionItem item3 = questionDao.create(id, "NGC 333", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		markManager.setMark(item1, id, null, "[QuestionItem:" + item1 + "]");
+		markManager.setMark(item2, id, null, "[QuestionItem:" + item2 + "]");
+		dbInstance.commitAndCloseSession();
+		
+		List<Long> favoritKeys = questionDao.getFavoritKeys(id);
+		Assert.assertNotNull(favoritKeys);
+		Assert.assertEquals(2, favoritKeys.size());
+		Assert.assertTrue(favoritKeys.contains(item1.getKey()));
+		Assert.assertTrue(favoritKeys.contains(item2.getKey()));
+		Assert.assertFalse(favoritKeys.contains(item3.getKey()));
 	}
 
 	@Test
@@ -144,11 +177,62 @@ public class QuestionDAOTest extends OlatTestCase {
 		questionDao.share(item2, group.getResource());
 		
 		//retrieve them
-		List<QuestionItem> sharedItems = questionDao.getSharedItemByResource(group.getResource(), 0, -1);
+		List<QuestionItem> sharedItems = questionDao.getSharedItemByResource(group.getResource(), null, 0, -1);
 		Assert.assertNotNull(sharedItems);
 		Assert.assertEquals(2, sharedItems.size());
 		Assert.assertTrue(sharedItems.contains(item1));
 		Assert.assertTrue(sharedItems.contains(item2));
+		
+		//retrieve limited sub set
+		List<QuestionItem> limitedSharedItems = questionDao.getSharedItemByResource(group.getResource(), Collections.singletonList(item1.getKey()), 0, -1);
+		Assert.assertNotNull(limitedSharedItems);
+		Assert.assertEquals(1, limitedSharedItems.size());
+		Assert.assertTrue(limitedSharedItems.contains(item1));
+	}
+	
+	@Test
+	public void shareItems_countSharedItemByResource() {
+		//create a group to share 2 items
+		BusinessGroup group = businessGroupDao.createAndPersist(null, "gdao", "gdao-desc", -1, -1, false, false, false, false, false);
+		QuestionItem item1 = questionDao.create(null, "Count-shared-Item-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		QuestionItem item2 = questionDao.create(null, "Count-shared-Item-2", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		QuestionItem item3 = questionDao.create(null, "Count-shared-Item-3", QTIConstants.QTI_12_FORMAT, Locale.FRENCH.getLanguage(), null, null, QuestionType.FIB);
+		dbInstance.commit();
+		
+		//share them
+		questionDao.share(item1, group.getResource());
+		questionDao.share(item2, group.getResource());
+		questionDao.share(item3, group.getResource());
+		dbInstance.commitAndCloseSession();
+		
+		//retrieve them
+		int sharedItems = questionDao.countSharedItemByResource(group.getResource());
+		Assert.assertEquals(3, sharedItems);
+	}
+	
+	@Test
+	public void shareItem_resources() {
+		//create a group to share 2 items
+		BusinessGroup group1 = businessGroupDao.createAndPersist(null, "gdao-1", "gdao-desc", -1, -1, false, false, false, false, false);
+		BusinessGroup group2 = businessGroupDao.createAndPersist(null, "gdao-2", "gdao-desc", -1, -1, false, false, false, false, false);
+		QuestionItem item = questionDao.create(null, "Share-Item-3", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		dbInstance.commit();
+		
+		//share them
+		List<OLATResource> resources = new ArrayList<OLATResource>();
+		resources.add(group1.getResource());
+		resources.add(group2.getResource());
+		questionDao.share(item, resources);
+		
+		//retrieve them
+		List<QuestionItem> sharedItems1 = questionDao.getSharedItemByResource(group1.getResource(), null, 0, -1);
+		Assert.assertNotNull(sharedItems1);
+		Assert.assertEquals(1, sharedItems1.size());
+		Assert.assertTrue(sharedItems1.contains(item));
+		List<QuestionItem> sharedItems2 = questionDao.getSharedItemByResource(group2.getResource(), null, 0, -1);
+		Assert.assertNotNull(sharedItems2);
+		Assert.assertEquals(1, sharedItems2.size());
+		Assert.assertTrue(sharedItems2.contains(item));
 	}
 	
 	@Test
@@ -164,14 +248,14 @@ public class QuestionDAOTest extends OlatTestCase {
 		questionDao.share(item, group.getResource());
 		
 		//retrieve them
-		List<QuestionItem> sharedItems = questionDao.getSharedItemByResource(group.getResource(), 0, -1);
+		List<QuestionItem> sharedItems = questionDao.getSharedItemByResource(group.getResource(), null, 0, -1);
 		Assert.assertNotNull(sharedItems);
 		Assert.assertEquals(1, sharedItems.size());
 		Assert.assertTrue(sharedItems.contains(item));
 	}
 	
 	@Test
-	public void shareItems_resources() {
+	public void shareItems_businessGroups() {
 		//create a group to share 2 items
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Share-item-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupDao.createAndPersist(id, "gdao", "gdao-desc", -1, -1, false, false, false, false, false);
@@ -190,6 +274,28 @@ public class QuestionDAOTest extends OlatTestCase {
 		Assert.assertTrue(shared.contains(group));
 	}
 	
+	@Test
+	public void getSharedResources() {
+		//create a group to share 2 items
+		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("Share-item-" + UUID.randomUUID().toString());
+		BusinessGroup group1 = businessGroupDao.createAndPersist(id, "gdao", "gdao-desc", -1, -1, false, false, false, false, false);
+		BusinessGroup group2 = businessGroupDao.createAndPersist(id, "gdao", "gdao-desc", -1, -1, false, false, false, false, false);
+		QuestionItem item = questionDao.create(id, "Share-Item-Dup-1", QTIConstants.QTI_12_FORMAT, Locale.ENGLISH.getLanguage(), null, null, QuestionType.MC);
+		dbInstance.commit();
+		
+		//share them
+		questionDao.share(item, group1.getResource());
+		questionDao.share(item, group2.getResource());
+		dbInstance.commitAndCloseSession();
+		
+		//retrieve them
+		List<OLATResource> shared = questionDao.getSharedResources(item);
+		Assert.assertNotNull(shared);
+		Assert.assertEquals(2, shared.size());
+		Assert.assertTrue(shared.contains(group1.getResource()));
+		Assert.assertTrue(shared.contains(group2.getResource()));
+	}
+	
 	@Test
 	public void removeFromShare() {
 		//create a group to share 2 items
@@ -201,18 +307,11 @@ public class QuestionDAOTest extends OlatTestCase {
 		questionDao.share(item, group.getResource());
 		
 		//retrieve them as a check
-		List<QuestionItem> shared = questionDao.getSharedItemByResource(group.getResource(), 0, -1);
+		List<QuestionItem> shared = questionDao.getSharedItemByResource(group.getResource(), null, 0, -1);
 		Assert.assertEquals(1, shared.size());
 		//and remove the items
 		int count = questionDao.deleteFromShares(shared);
 		Assert.assertEquals(1, count);
 		dbInstance.commit();//make sure that changes are committed
 	}
-	
-
-	
-	
-	
-	
-
-}
+}
\ No newline at end of file
diff --git a/src/test/java/org/olat/modules/qpool/manager/QuestionPoolServiceTest.java b/src/test/java/org/olat/modules/qpool/manager/QuestionPoolServiceTest.java
index 4028907f9ebf896cdf45c2ae50a750ade3b37991..636f79c5edd499df0a5f12d601b830e40ce5704e 100644
--- a/src/test/java/org/olat/modules/qpool/manager/QuestionPoolServiceTest.java
+++ b/src/test/java/org/olat/modules/qpool/manager/QuestionPoolServiceTest.java
@@ -34,6 +34,7 @@ import junit.framework.Assert;
 
 import org.junit.Test;
 import org.olat.core.commons.persistence.DB;
+import org.olat.core.commons.persistence.ResultInfos;
 import org.olat.core.id.Identity;
 import org.olat.group.BusinessGroup;
 import org.olat.group.manager.BusinessGroupDAO;
@@ -109,11 +110,11 @@ public class QuestionPoolServiceTest extends OlatTestCase {
 		//retrieve the list of items in the collection
 		int numOfItemsInCollection = qpoolService.countItemsOfCollection(newColl);
 		Assert.assertEquals(2, numOfItemsInCollection);
-		List<QuestionItem> itemsOfCollection = qpoolService.getItemsOfCollection(newColl, 0, -1);
+		ResultInfos<QuestionItem> itemsOfCollection = qpoolService.getItemsOfCollection(newColl, null, 0, -1);
 		Assert.assertNotNull(itemsOfCollection);
-		Assert.assertEquals(2, itemsOfCollection.size());
-		Assert.assertTrue(itemsOfCollection.contains(item1));
-		Assert.assertTrue(itemsOfCollection.contains(item2));
+		Assert.assertEquals(2, itemsOfCollection.getObjects().size());
+		Assert.assertTrue(itemsOfCollection.getObjects().contains(item1));
+		Assert.assertTrue(itemsOfCollection.getObjects().contains(item2));
 	}
 
 	@Test
diff --git a/src/test/java/org/olat/modules/qpool/manager/StudyFieldDAOTest.java b/src/test/java/org/olat/modules/qpool/manager/StudyFieldDAOTest.java
index 30a49cc1d329d98e1b1d341c443480a32d47d27d..355427e60fd6fe650726813e931c92b7e56737d9 100644
--- a/src/test/java/org/olat/modules/qpool/manager/StudyFieldDAOTest.java
+++ b/src/test/java/org/olat/modules/qpool/manager/StudyFieldDAOTest.java
@@ -107,12 +107,68 @@ public class StudyFieldDAOTest extends OlatTestCase {
 		dbInstance.commitAndCloseSession();
 		
 		//reload and check parents
-		String path = studyFieldDao.getMaterializedPath(graph);
+		StudyField path = studyFieldDao.loadStudyFieldById(graph.getKey());
 		Assert.assertNotNull(path);
+		Assert.assertNotNull(path.getMaterializedPathNames());
+		Assert.assertEquals("/Science/Mathematics/Topology", path.getMaterializedPathNames());
+	}
+	
+	@Test
+	public void getDescendants() {
+		StudyField science = studyFieldDao.createAndPersist(null, "Science");
+		StudyField mathematics = studyFieldDao.createAndPersist(science, "Mathematics");
+		StudyField numerical = studyFieldDao.createAndPersist(mathematics, "Numerical");
+		StudyField topology = studyFieldDao.createAndPersist(mathematics, "Topology");
+		StudyField graph = studyFieldDao.createAndPersist(topology, "Graph theory");
+		dbInstance.commitAndCloseSession();
 		
-		Assert.assertEquals("/Science/Mathematics/Topology/Graph theory", path);
+		//load the descendants of mathematics
+		List<StudyField> descendants = studyFieldDao.getDescendants(mathematics);
+		Assert.assertNotNull(descendants);
+		Assert.assertEquals(3, descendants.size());
+		Assert.assertTrue(descendants.contains(numerical));
+		Assert.assertTrue(descendants.contains(topology));
+		Assert.assertTrue(descendants.contains(graph));
 		
+		//load the descendants of topology
+		List<StudyField> topologyDescendants = studyFieldDao.getDescendants(topology);
+		Assert.assertNotNull(topologyDescendants);
+		Assert.assertEquals(1, topologyDescendants.size());
+		Assert.assertTrue(topologyDescendants.contains(graph));
+		
+		//load the descendants of mathematics
+		List<StudyField> graphDescendants = studyFieldDao.getDescendants(graph);
+		Assert.assertNotNull(descendants);
+		Assert.assertTrue(graphDescendants.isEmpty());
 	}
 	
-
+	@Test
+	public void updateWithDescendants() {
+		StudyField animals = studyFieldDao.createAndPersist(null, "Animals");
+		StudyField cats = studyFieldDao.createAndPersist(animals, "Cats");
+		StudyField dogs = studyFieldDao.createAndPersist(animals, "Dogs");
+		StudyField huskies = studyFieldDao.createAndPersist(dogs, "Huskies");
+		StudyField lion = studyFieldDao.createAndPersist(cats, "Lion");
+		StudyField mountainLion = studyFieldDao.createAndPersist(lion, "Mountain Lion");
+		StudyField tiger = studyFieldDao.createAndPersist(cats, "Tiger");
+		dbInstance.commitAndCloseSession();
+		
+		//update the cats
+		studyFieldDao.update("Felids", cats);
+		dbInstance.commit();
+		
+		//check if descendants are correctly updated
+		StudyField reloadedLion = studyFieldDao.loadStudyFieldById(lion.getKey());
+		Assert.assertEquals("/Animals/Felids", reloadedLion.getMaterializedPathNames());
+		StudyField reloadedMountainLion = studyFieldDao.loadStudyFieldById(mountainLion.getKey());
+		Assert.assertEquals("/Animals/Felids/Lion", reloadedMountainLion.getMaterializedPathNames());
+		StudyField reloadedTiger = studyFieldDao.loadStudyFieldById(tiger.getKey());
+		Assert.assertEquals("/Animals/Felids", reloadedTiger.getMaterializedPathNames());
+		
+		//dogs are not changed
+		StudyField reloadedDogs = studyFieldDao.loadStudyFieldById(dogs.getKey());
+		Assert.assertEquals("/Animals", reloadedDogs.getMaterializedPathNames());
+		StudyField reloadedHuskies = studyFieldDao.loadStudyFieldById(huskies.getKey());
+		Assert.assertEquals("/Animals/Dogs", reloadedHuskies.getMaterializedPathNames());	
+	}
 }
diff --git a/src/test/java/org/olat/modules/wiki/versioning/diff/CookbookDiffTest.java b/src/test/java/org/olat/modules/wiki/versioning/diff/CookbookDiffTest.java
index b3127c9f4b977b71e6326728b11bb0b2aa1e30eb..6a91a1e2358b086f48b3b4607f627fd41a23e6de 100644
--- a/src/test/java/org/olat/modules/wiki/versioning/diff/CookbookDiffTest.java
+++ b/src/test/java/org/olat/modules/wiki/versioning/diff/CookbookDiffTest.java
@@ -31,7 +31,6 @@ import static org.junit.Assert.assertEquals;
 import java.util.Iterator;
 import java.util.List;
 
-import org.apache.log4j.Logger;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -43,8 +42,7 @@ import org.olat.modules.wiki.versioning.ChangeInfo;
  * @author Christian Guretzki
  */
 public class CookbookDiffTest {
-	private static Logger log = Logger.getLogger(CookbookDiffTest.class);
-	
+
 	private CookbookDifferenceService differenceService;
 
 
@@ -57,10 +55,9 @@ public class CookbookDiffTest {
 	@Test public void testAddText() {
 		String text1 = "Line1\nLine2\nDies ist ein Text.";
 		String text2 = text1 + "Text2";
-		List diffList = differenceService.diff(text1,text2);
-		int i = 1;
-		for (Iterator iter = diffList.iterator(); iter.hasNext();) {
-			ChangeInfo changeInfo = (ChangeInfo) iter.next();
+		List<ChangeInfo> diffList = differenceService.diff(text1,text2);
+		for (Iterator<ChangeInfo> iter = diffList.iterator(); iter.hasNext();) {
+			ChangeInfo changeInfo = iter.next();
 			assertEquals("Type must be CHANGE",changeInfo.getType(),ChangeInfo.CHANGE);
 			assertEquals("Wrong line content.",changeInfo.getLines()[0],"Dies ist ein Text.");
 			assertEquals("Wrong line content.",changeInfo.getLines()[1],"Dies ist ein Text.Text2");
@@ -70,14 +67,11 @@ public class CookbookDiffTest {
 	@Test public void testMove() {
 		String text1 = "Line1\nLine2\nDies ist ein Text.\nbla bla\nText2 Text2.1 Text2.2";
 		String text2 = "Line1\nLine2\nDies ist ein Text.\nText2 Text2.1 Text2.2\nbla bla";
-		List diffList = differenceService.diff(text1,text2);
-		int i = 1;
-		for (Iterator iter = diffList.iterator(); iter.hasNext();) {
-			ChangeInfo changeInfo = (ChangeInfo) iter.next();
+		List<ChangeInfo> diffList = differenceService.diff(text1,text2);
+		for (Iterator<ChangeInfo> iter = diffList.iterator(); iter.hasNext();) {
+			ChangeInfo changeInfo = iter.next();
 			assertEquals("Type must be MOVE",changeInfo.getType(),ChangeInfo.MOVE);
 			assertEquals("Wrong line content.",changeInfo.getLines()[0],"Text2 Text2.1 Text2.2");
 		}
 	}
-
-
 }