///////////////////////////////////////////////////////////////////////////// // // Project ProjectForge Community Edition // www.projectforge.org // // Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de) // // ProjectForge is dual-licensed. // // This community edition is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation; version 3 of the License. // // This community edition is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General // Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, see http://www.gnu.org/licenses/. // ///////////////////////////////////////////////////////////////////////////// package org.projectforge.web.task; import java.util.ArrayList; import java.util.List; import java.util.Locale; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.wicket.AttributeModifier; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.request.cycle.RequestCycle; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.util.convert.IConverter; import org.projectforge.core.BaseSearchFilter; import org.projectforge.task.TaskDO; import org.projectforge.task.TaskDao; import org.projectforge.task.TaskNode; import org.projectforge.task.TaskTree; import org.projectforge.web.wicket.autocompletion.PFAutoCompleteTextField; /** * @author TODO * */ public abstract class TaskSelectAutoCompleteFormComponent extends PFAutoCompleteTextField<TaskDO> { private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(TaskSelectAutoCompleteFormComponent.class); private static final long serialVersionUID = 2278347191215880396L; @SpringBean(name = "taskDao") private TaskDao taskDao; @SpringBean(name = "taskTree") private TaskTree taskTree; private TaskDO taskDo; private boolean autocompleteOnlyTaskBookableForTimesheets; /** * @param id */ public TaskSelectAutoCompleteFormComponent(final String id) { super(id, null); setModel(new PropertyModel<TaskDO>(this, "taskDo")); getSettings().withLabelValue(true).withMatchContains(true).withMinChars(2).withAutoSubmit(false); add(AttributeModifier.append("onkeypress", "if ( event.which == 13 ) { return false; }")); add(AttributeModifier.append("class", "mm_delayBlur")); add(new AjaxFormComponentUpdatingBehavior("onChange") { private static final long serialVersionUID = 3681828654557441560L; @Override protected void onUpdate(final AjaxRequestTarget target) { // just update the model } }); } @Override protected void onBeforeRender() { super.onBeforeRender(); // this panel should always start with an empty input field, therefore delete the current model taskDo = null; } /** * @see org.projectforge.web.wicket.autocompletion.PFAutoCompleteTextField#getChoices(java.lang.String) */ @Override protected List<TaskDO> getChoices(final String input) { final BaseSearchFilter filter = new BaseSearchFilter(); filter.setSearchFields("title", "taskpath"); filter.setSearchString(input); final List<TaskDO> list = taskDao.getList(filter); final List<TaskDO> choices = new ArrayList<TaskDO>(); for (final TaskDO task : list) { if (autocompleteOnlyTaskBookableForTimesheets == false) { choices.add(task); } else { final TaskNode taskNode = taskTree.getTaskNodeById(task.getId()); if (taskNode == null) { log.error("Oups, task node with id '" + task.getId() + "' not found in taskTree."); } else if (taskNode.isBookableForTimesheets() == true) { // Only add nodes which are bookable: choices.add(task); } } } return choices; } @Override protected String formatValue(final TaskDO value) { if (value == null) { return ""; } return "" + value.getId(); } @Override protected String formatLabel(final TaskDO value) { if (value == null) { return ""; } return createPath(value.getId()); } /** * create path to root * * @return */ private String createPath(final Integer taskId) { final StringBuilder builder = new StringBuilder(); final List<TaskNode> nodeList = taskTree.getPathToRoot(taskId); if (CollectionUtils.isEmpty(nodeList) == true) { return getString("task.path.rootTask"); } final String pipeSeparator = " | "; String separator = ""; for (final TaskNode node : nodeList) { builder.append(separator); builder.append(node.getTask().getTitle()); separator = pipeSeparator; } return builder.toString(); } protected void notifyChildren() { final AjaxRequestTarget target = RequestCycle.get().find(AjaxRequestTarget.class); if (target != null) { onModelSelected(target, taskDo); } } /** * Hook method which is called when the model is changed with a valid durin an ajax call * * @param target * @param taskDo */ protected abstract void onModelSelected(final AjaxRequestTarget target, TaskDO taskDo); @SuppressWarnings({ "unchecked", "rawtypes"}) @Override public <C> IConverter<C> getConverter(final Class<C> type) { return new IConverter() { private static final long serialVersionUID = -7729322118285105516L; @Override public Object convertToObject(final String value, final Locale locale) { if (StringUtils.isEmpty(value) == true) { getModel().setObject(null); notifyChildren(); return null; } try { final TaskDO task = taskTree.getTaskById(Integer.valueOf(value)); if (task == null) { error(getString("timesheet.error.invalidTaskId")); return null; } getModel().setObject(task); notifyChildren(); return task; } catch (final NumberFormatException e) { // just ignore the NumberFormatException, because this could happen during wrong inputs return null; } } @Override public String convertToString(final Object value, final Locale locale) { if (value == null) { return ""; } final TaskDO task = (TaskDO) value; return task.getTitle(); } }; } /** * @param autocompleteOnlyTaskBookableForTimesheets the autocompleteOnlyTaskBookableForTimesheets to set * @return this for chaining. */ void setAutocompleteOnlyTaskBookableForTimesheets(final boolean autocompleteOnlyTaskBookableForTimesheets) { this.autocompleteOnlyTaskBookableForTimesheets = autocompleteOnlyTaskBookableForTimesheets; } }