/** * <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.ui.metadata; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.elements.SingleSelection; import org.olat.core.gui.components.form.flexible.elements.TextElement; import org.olat.core.gui.components.form.flexible.impl.Form; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.FormEvent; import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; import org.olat.core.gui.components.form.flexible.impl.elements.table.ExtendedFlexiTableSearchController; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.tree.GenericTreeNode; import org.olat.core.gui.components.tree.TreeNode; import org.olat.core.gui.control.Controller; import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.util.CodeHelper; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.nodes.INode; import org.olat.modules.qpool.QPoolService; import org.olat.modules.qpool.TaxonomyLevel; import org.olat.modules.qpool.model.QItemDocument; import org.olat.modules.qpool.model.QLicense; import org.olat.modules.qpool.ui.QuestionsController; import org.olat.modules.qpool.ui.admin.TaxonomyTreeModel; import org.olat.modules.qpool.ui.metadata.MetaUIFactory.KeyValues; import org.olat.search.model.AbstractOlatDocument; import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 03.05.2013<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class ExtendedSearchController extends FormBasicController implements ExtendedFlexiTableSearchController { private FormLink searchButton; private final SearchAttributes searchAttributes = new SearchAttributes(); private final List<ConditionalQuery> uiQueries = new ArrayList<ConditionalQuery>(); private final List<String> condQueries = new ArrayList<String>(); private final String prefsKey; private ExtendedSearchPrefs prefs; private boolean enabled = true; @Autowired private QPoolService qpoolService; public ExtendedSearchController(UserRequest ureq, WindowControl wControl, String prefsKey, Form mainForm) { super(ureq, wControl, LAYOUT_CUSTOM, "extended_search", mainForm); setTranslator(Util.createPackageTranslator(QuestionsController.class, getLocale(), getTranslator())); this.prefsKey = prefsKey; prefs = (ExtendedSearchPrefs) ureq.getUserSession().getGuiPreferences() .get(ExtendedFlexiTableSearchController.class, prefsKey); if(prefs != null && prefs.getCondQueries().size() > 0) { for(ExtendedSearchPref pref:prefs.getCondQueries()) { uiQueries.add(new ConditionalQuery(pref)); } } else { uiQueries.add(new ConditionalQuery()); } initForm(ureq); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { if(formLayout instanceof FormLayoutContainer) { FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; layoutCont.contextPut("uiQueries", uiQueries); } FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); buttonsCont.setRootForm(mainForm); formLayout.add(buttonsCont); searchButton = uifactory.addFormLink("search", buttonsCont, Link.BUTTON); uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl()); } @Override public void setEnabled(boolean enable) { this.enabled = enable; } @Override protected void formOK(UserRequest ureq) { if(enabled) { doSearch(ureq); } } @Override protected void formCancelled(UserRequest ureq) { fireEvent(ureq, Event.CANCELLED_EVENT); } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(source == searchButton) { doSearch(ureq); } else if (source instanceof SingleSelection) { SingleSelection attrEl = (SingleSelection)source; if(attrEl.isOneSelected()) { Object uObject = attrEl.getUserObject(); if(uObject instanceof ConditionalQuery) { ConditionalQuery query = (ConditionalQuery)uObject; query.selectAttributeType(attrEl.getSelectedKey(), null); } } } else if(source instanceof FormLink) { FormLink button = (FormLink)source; if(button.getCmd().startsWith("add")) { ConditionalQuery query = (ConditionalQuery)button.getUserObject(); addParameter(query); } else if(button.getCmd().startsWith("remove")) { ConditionalQuery query = (ConditionalQuery)button.getUserObject(); removeParameter(query); } } super.formInnerEvent(ureq, source, event); } private void addParameter(ConditionalQuery query) { int index = uiQueries.indexOf(query); ConditionalQuery newQuery = new ConditionalQuery(); if(index < 0 || (index + 1) > uiQueries.size()) { uiQueries.add(newQuery); } else { uiQueries.add(index+1, newQuery); } } private void removeParameter(ConditionalQuery query) { if(uiQueries.size() > 1 && uiQueries.remove(query)) { flc.setDirty(true); } } private void doSearch(UserRequest ureq) { condQueries.clear(); List<ExtendedSearchPref> params = new ArrayList<ExtendedSearchPref>(); for(ConditionalQuery uiQuery:uiQueries) { String query = uiQuery.getQuery(); if(StringHelper.containsNonWhitespace(query)) { condQueries.add(query); params.add(new ExtendedSearchPref(uiQuery.getAttribute(), uiQuery.getValue())); } } if (prefs == null){ prefs = new ExtendedSearchPrefs(); } prefs.setCondQueries(params); ureq.getUserSession().getGuiPreferences().putAndSave(ExtendedFlexiTableSearchController.class, prefsKey, prefs); fireEvent(ureq, Event.DONE_EVENT); } /** * Append 'AND' operation if buf is not empty. * @param buf */ private String append(String... strings) { StringBuilder query = new StringBuilder(); for(String string:strings) { query.append(string); } return query.toString(); } @Override public List<String> getConditionalQueries() { return condQueries; } @Override protected void doDispose() { // } public class ConditionalQuery { private SingleSelection attributeChoice; private FormItem parameter; private QueryParameterFactory parameterFactory; private FormLink addButton; private FormLink removeButton; public ConditionalQuery() { this(null); } public ConditionalQuery(ExtendedSearchPref pref) { long id = CodeHelper.getRAMUniqueID(); String[] attrKeys = searchAttributes.getKeys(); String[] attrValues = new String[attrKeys.length]; for(int i=attrValues.length; i-->0; ) { attrValues[i] = translate(attrKeys[i]); } attributeChoice = uifactory.addDropdownSingleselect("attr-" + id, null, flc, attrKeys, attrValues, null); if(pref == null) { selectAttributeType(attrKeys[0], null); } else { selectAttributeType(pref.getAttribute(), pref.getValue()); } boolean found = false; if(pref != null && StringHelper.containsNonWhitespace(pref.getAttribute())) { String attr = pref.getAttribute(); for(String attrKey:attrKeys) { if(attr.equals(attrKey)) { attributeChoice.select(attrKey, true); found = true; } } } if(!found) { attributeChoice.select(attrKeys[0], true); } if(pref == null) { selectAttributeType(attrKeys[0], null); } else { selectAttributeType(pref.getAttribute(), pref.getValue()); } attributeChoice.addActionListener(FormEvent.ONCHANGE); attributeChoice.setUserObject(this); flc.add(attributeChoice.getName(), attributeChoice); addButton = uifactory.addFormLink("add-" + id, "add", null, flc, Link.BUTTON); addButton.setUserObject(this); flc.add(addButton.getComponent().getComponentName(), addButton); removeButton = uifactory.addFormLink("remove-"+ id, "remove", null, flc, Link.BUTTON); removeButton.setUserObject(this); flc.add(removeButton.getComponent().getComponentName(), removeButton); } public String getAttribute() { return attributeChoice.isOneSelected() ? attributeChoice.getSelectedKey() : null; } public String getValue() { return "test"; } public SingleSelection getAttributChoice() { return attributeChoice; } public FormItem getParameterItem() { return parameter; } public FormLink getAddButton() { return addButton; } public FormLink getRemoveButton() { return removeButton; } public void selectAttributeType(String type, String value) { parameterFactory = searchAttributes.getQueryParameterFactory(type); if(parameterFactory != null) { parameter = parameterFactory.createItem(value); } } public String getQuery() { if(parameterFactory != null && parameter != null) { return parameterFactory.getQuery(parameter); } return null; } } public static interface QueryParameterFactory { public String getValue(FormItem item); public FormItem createItem(String startValue); public String getQuery(FormItem item); } private class SearchAttributes { private List<SearchAttribute> attributes = new ArrayList<SearchAttribute>(); public SearchAttributes() { //general attributes.add(new SearchAttribute("general.title", new StringQueryParameter(AbstractOlatDocument.TITLE_FIELD_NAME))); attributes.add(new SearchAttribute("general.keywords", new StringQueryParameter(QItemDocument.KEYWORDS_FIELD))); attributes.add(new SearchAttribute("general.coverage", new StringQueryParameter(QItemDocument.COVERAGE_FIELD))); attributes.add(new SearchAttribute("general.additional.informations", new StringQueryParameter(QItemDocument.ADD_INFOS_FIELD))); attributes.add(new SearchAttribute("general.language", new StringQueryParameter(QItemDocument.LANGUAGE_FIELD))); attributes.add(new SearchAttribute("classification.taxonomic.path", new TaxonomicPathQueryParameter())); //educational attributes.add(new SearchAttribute("educational.context", new ContextQueryParameter())); //question attributes.add(new SearchAttribute("question.type", new TypeQueryParameter())); attributes.add(new SearchAttribute("question.assessmentType", new AssessmentQueryParameter())); //lifecycle attributes.add(new SearchAttribute("lifecycle.status", new StatusQueryParameter())); //technical attributes.add(new SearchAttribute("technical.editor", new StringQueryParameter(QItemDocument.EDITOR_FIELD))); attributes.add(new SearchAttribute("technical.format", new FormatQueryParameter())); //rights attributes.add(new SearchAttribute("rights.copyright", new LicenseQueryParameter())); } public QueryParameterFactory getQueryParameterFactory(String type) { for(SearchAttribute attribute:attributes) { if(type.equals(attribute.getI18nKey())) { return attribute.getFactory(); } } return null; } public String[] getKeys() { String[] keys = new String[attributes.size()]; for(int i=keys.length; i-->0; ) { keys[i] = attributes.get(i).getI18nKey(); } return keys; } } public class StringQueryParameter implements QueryParameterFactory { private final String docAttribute; public StringQueryParameter(String docAttribute) { this.docAttribute = docAttribute; } @Override public String getValue(FormItem item) { if(item instanceof TextElement) { return ((TextElement)item).getValue(); } return null; } @Override public FormItem createItem(String startValue) { return uifactory.addTextElement("type-" + CodeHelper.getRAMUniqueID(), null, 50, startValue, flc); } @Override public String getQuery(FormItem item) { String val = getValue(item); if(StringHelper.containsNonWhitespace(val)) { return append(docAttribute, ":(", val, ") "); } return null; } } public class TaxonomicPathQueryParameter extends SingleChoiceQueryParameter { public TaxonomicPathQueryParameter() { super(QItemDocument.TAXONOMIC_FIELD); } @Override public FormItem createItem(String startValue) { TaxonomyTreeModel treeModel = new TaxonomyTreeModel(""); List<String> keys = new ArrayList<String>(); List<String> values = new ArrayList<String>(); flatTree(treeModel.getRootNode(), "", keys, values); String[] keysArr = keys.toArray(new String[keys.size()]); String[] valuesArr = values.toArray(new String[values.size()]); return createItem(keysArr, valuesArr, startValue); } private void flatTree(TreeNode node, String path, List<String> keys, List<String> values) { for(int i=0; i<node.getChildCount(); i++) { INode child = node.getChildAt(i); if(child instanceof GenericTreeNode) { GenericTreeNode gChild = (GenericTreeNode)child; TaxonomyLevel level = (TaxonomyLevel)gChild.getUserObject(); String field = level.getField(); keys.add(level.getKey().toString()); values.add(path + "" + field); flatTree(gChild, path + "\u00A0\u00A0\u00A0\u00A0", keys, values); } } } } public class LicenseQueryParameter extends SingleChoiceQueryParameter { public LicenseQueryParameter() { super(QItemDocument.COPYRIGHT_FIELD); } @Override public FormItem createItem(String startValue) { List<QLicense> allLicenses = qpoolService.getAllLicenses(); List<QLicense> licenses = new ArrayList<QLicense>(allLicenses); for(Iterator<QLicense> it=licenses.iterator(); it.hasNext(); ) { String key = it.next().getLicenseKey(); if(key != null && key.startsWith("perso-")) { it.remove(); } } String[] keys = new String[licenses.size()]; int count = 0; for(QLicense license:licenses) { keys[count++] = license.getLicenseKey(); } return createItem(keys, keys, startValue); } } public class TypeQueryParameter extends SingleChoiceQueryParameter { public TypeQueryParameter() { super(QItemDocument.ITEM_TYPE_FIELD); } @Override public FormItem createItem(String startValue) { KeyValues types = MetaUIFactory.getQItemTypeKeyValues(getTranslator(), qpoolService); return createItem(types.getKeys(), types.getValues(), startValue); } } public class FormatQueryParameter extends SingleChoiceQueryParameter { public FormatQueryParameter() { super(QItemDocument.FORMAT_FIELD); } @Override public FormItem createItem(String startValue) { KeyValues formats = MetaUIFactory.getFormats(); return createItem(formats.getKeys(), formats.getValues(), startValue); } @Override public String getQuery(FormItem item) { String val = getValue(item); if(StringHelper.containsNonWhitespace(val)) { return append(getDocAttribute(), ":\"", val, "\" "); } return null; } } public class ContextQueryParameter extends SingleChoiceQueryParameter { public ContextQueryParameter() { super(QItemDocument.EDU_CONTEXT_FIELD); } @Override public FormItem createItem(String startValue) { KeyValues contexts = MetaUIFactory.getContextKeyValues(getTranslator(), qpoolService); return createItem(contexts.getKeys(), contexts.getValues(), startValue); } } public class AssessmentQueryParameter extends SingleChoiceQueryParameter { public AssessmentQueryParameter() { super(QItemDocument.ASSESSMENT_TYPE_FIELD); } @Override public FormItem createItem(String startValue) { KeyValues types = MetaUIFactory.getAssessmentTypes(getTranslator()); return createItem(types.getKeys(), types.getValues(), startValue); } } public class StatusQueryParameter extends SingleChoiceQueryParameter { public StatusQueryParameter() { super(QItemDocument.ITEM_STATUS_FIELD); } @Override public FormItem createItem(String startValue) { KeyValues types = MetaUIFactory.getStatus(getTranslator()); return createItem(types.getKeys(), types.getValues(), startValue); } } public abstract class SingleChoiceQueryParameter implements QueryParameterFactory { private final String docAttribute; public SingleChoiceQueryParameter(String docAttribute) { this.docAttribute = docAttribute; } @Override public String getValue(FormItem item) { if(item instanceof SingleSelection && ((SingleSelection)item).isOneSelected()) { return ((SingleSelection)item).getSelectedKey(); } return null; } protected FormItem createItem(String[] keys, String[] values, String startValue) { SingleSelection choice = uifactory.addDropdownSingleselect(docAttribute + "-" + CodeHelper.getRAMUniqueID(), flc, keys, values, null); if(startValue != null) { for(String key:keys) { if(key.equals(startValue)) { choice.select(key, true); } } } return choice; } @Override public String getQuery(FormItem item) { String val = getValue(item); if(StringHelper.containsNonWhitespace(val)) { return append(docAttribute, ":(", val, ") "); } return null; } public String getDocAttribute() { return docAttribute; } } private static class SearchAttribute { private final String i18nKey; private final QueryParameterFactory factory; public SearchAttribute(String i18nKey, QueryParameterFactory factory) { this.i18nKey = i18nKey; this.factory = factory; } public String getI18nKey() { return i18nKey; } public QueryParameterFactory getFactory() { return factory; } } }