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"); } } - - }