/*******************************************************************************
* Copyright 2013
* Ubiquitous Knowledge Processing (UKP) Lab
* Technische Universität Darmstadt
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package de.tudarmstadt.ukp.csniper.webapp.project.page;
import java.io.Serializable;
import java.util.List;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.authorization.Action;
import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeAction;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.ListChoice;
import org.apache.wicket.markup.html.form.NumberTextField;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.radeox.api.engine.RenderEngine;
import org.radeox.api.engine.context.RenderContext;
import org.radeox.engine.BaseRenderEngine;
import org.radeox.engine.context.BaseRenderContext;
import org.springframework.security.core.context.SecurityContextHolder;
import de.tudarmstadt.ukp.csniper.webapp.evaluation.model.AdditionalColumn;
import de.tudarmstadt.ukp.csniper.webapp.project.ProjectRepository;
import de.tudarmstadt.ukp.csniper.webapp.project.model.AnnotationType;
import de.tudarmstadt.ukp.csniper.webapp.support.orm.EntityModel;
import de.tudarmstadt.ukp.csniper.webapp.support.wicket.DbFieldMaxLengthValidator;
import de.tudarmstadt.ukp.csniper.webapp.support.wicket.RepeatingEditor;
import de.tudarmstadt.ukp.csniper.webapp.support.wicket.RepeatingItem;
@AuthorizeAction(action = Action.RENDER, roles = { "ROLE_ADMIN" })
public class AnnotationTypePage
extends SettingsPageBase
{
private static final long serialVersionUID = -2102136855109258306L;
@SpringBean(name = "projectRepository")
private ProjectRepository projectRepository;
private class SelectionForm
extends Form<SelectionModel>
{
private static final long serialVersionUID = -1L;
public SelectionForm(String id)
{
super(id, new CompoundPropertyModel<SelectionModel>(new SelectionModel()));
add(new Button("create", new ResourceModel("label"))
{
private static final long serialVersionUID = 1L;
@Override
public void onSubmit()
{
SelectionForm.this.getModelObject().type = null;
detailForm.setModelObject(new AnnotationType());
detailForm.setVisible(true);
SelectionForm.this.setVisible(false);
}
});
add(new ListChoice<AnnotationType>("type")
{
private static final long serialVersionUID = 1L;
{
setChoices(new LoadableDetachableModel<List<AnnotationType>>()
{
private static final long serialVersionUID = 1L;
@Override
protected List<AnnotationType> load()
{
return projectRepository.listAnnotationTypes();
}
});
setChoiceRenderer(new ChoiceRenderer<AnnotationType>("name"));
setNullValid(false);
}
@Override
protected void onSelectionChanged(AnnotationType aNewSelection)
{
if (aNewSelection != null) {
SelectionForm.this.getModelObject().type = null;
AnnotationType type = projectRepository.readAnnotationType(aNewSelection
.getId());
detailForm.setModelObject(type);
detailForm.setVisible(true);
SelectionForm.this.setVisible(false);
}
}
@Override
protected boolean wantOnSelectionChangedNotifications()
{
return true;
}
@Override
protected CharSequence getDefaultChoice(String aSelectedValue)
{
return "";
}
});
}
}
static private class SelectionModel
implements Serializable
{
private static final long serialVersionUID = -1L;
private AnnotationType type;
}
private class DetailForm
extends Form<AnnotationType>
{
private static final long serialVersionUID = -1L;
public DetailForm(String id)
{
super(id, new CompoundPropertyModel<AnnotationType>(new EntityModel<AnnotationType>(
new AnnotationType())));
setOutputMarkupId(true);
add(new TextField<String>("name").add(new DbFieldMaxLengthValidator(projectRepository,
"AnnotationType", "name")));
add(new NumberTextField<Integer>("goal"));
add(new NumberTextField<Integer>("goalWrong"));
add(new TextArea<String>("description").add(
new DbFieldMaxLengthValidator(projectRepository, "AnnotationType",
"description")).setOutputMarkupPlaceholderTag(true));
add(new RepeatingEditor<AdditionalColumn>("additionalColumns")
{
private static final long serialVersionUID = 1L;
@Override
protected void onPopulateItem(final RepeatingItem<AdditionalColumn> aItem)
{
aItem.add(new TextField<String>("name", new PropertyModel<String>(aItem
.getModelObject(), "name")).setConvertEmptyInputStringToNull(false)
.add(new DbFieldMaxLengthValidator(projectRepository,
"AdditionalColumn", "name")));
aItem.add(new CheckBox("showColumn", new PropertyModel<Boolean>(aItem
.getModelObject(), "showColumn")));
aItem.add(new AjaxButton("removeButton", new ResourceModel("label"))
{
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm)
{
int idx = aItem.getIndex();
AdditionalColumn currCol = aItem.getModelObject();
String user = SecurityContextHolder.getContext().getAuthentication()
.getName();
int entryCount = projectRepository.countEntriesWithAdditionalColumn(
user, detailForm.getModelObject(), currCol);
if (entryCount > 0) {
// TODO popup dialog, on YES delete all associated entries
getFeedbackPanel().error(
"Currently it is not possible to remove columns for which entries exist. ["
+ entryCount + "]");
aTarget.add(getFeedbackPanel());
}
else {
// TODO do this also in case of YES on popup dialog
@SuppressWarnings("unchecked")
RepeatingEditor<AdditionalColumn> re = (RepeatingEditor<AdditionalColumn>) DetailForm.this
.get("additionalColumns");
// TODO grey out instead of removing the text field
re.getModelObject().remove(idx);
aTarget.add(detailForm);
}
}
@Override
protected void onError(AjaxRequestTarget aTarget, Form<?> aForm)
{
super.onError(aTarget, aForm);
// Make sure the feedback messages are rendered
aTarget.add(getFeedbackPanel());
}
});
}
}.setOutputMarkupPlaceholderTag(true));
add(new AjaxButton("addColumnButton", new ResourceModel("label"))
{
private static final long serialVersionUID = 1L;
@SuppressWarnings("unchecked")
@Override
protected void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm)
{
((RepeatingEditor<AdditionalColumn>) detailForm.get("additionalColumns"))
.addItem(new AdditionalColumn("", false));
// can't ajax update a repeater component, so update the whole form
aTarget.add(detailForm);
}
@Override
protected void onError(AjaxRequestTarget aTarget, Form<?> aForm)
{
super.onError(aTarget, aForm);
// Make sure the feedback messages are rendered
aTarget.add(getFeedbackPanel());
}
});
Label previewPane;
add(previewPane = new Label("previewPane", new LoadableDetachableModel<String>()
{
private static final long serialVersionUID = 1L;
@Override
protected String load()
{
String value = detailForm.getModelObject().getDescription();
if (value != null) {
RenderContext context = new BaseRenderContext();
RenderEngine engine = new BaseRenderEngine();
return engine.render(value, context);
}
else {
return "";
}
}
}));
previewPane.setOutputMarkupPlaceholderTag(true).setVisible(false);
previewPane.setEscapeModelStrings(false);
add(new AjaxButton("previewButton", new ResourceModel("previewLabel"))
{
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget aTarget, Form<?> aForm)
{
Component preview = detailForm.get("previewPane");
toggleVisibility(!preview.isVisible());
aTarget.add(this, preview.getParent());
}
@Override
protected void onError(AjaxRequestTarget aTarget, Form<?> aForm)
{
super.onError(aTarget, aForm);
// Make sure the feedback messages are rendered
aTarget.add(getFeedbackPanel());
}
}).setOutputMarkupId(true);
add(new Button("save", new ResourceModel("label"))
{
private static final long serialVersionUID = 1L;
@Override
public void onSubmit()
{
// set visibility to default
toggleVisibility(false);
projectRepository.writeAnnotationType(detailForm.getModelObject());
detailForm.setModelObject(new AnnotationType());
detailForm.setVisible(false);
selectionForm.setVisible(true);
}
});
add(new Button("cancel", new ResourceModel("label"))
{
private static final long serialVersionUID = 1L;
@Override
public void onSubmit()
{
// set visibility to default
toggleVisibility(false);
projectRepository.refreshEntity(detailForm.getModelObject());
detailForm.setModelObject(new AnnotationType());
detailForm.setVisible(false);
selectionForm.setVisible(true);
}
});
}
/**
* Toggles visibility of description input and preview pane. Also changes preview button
* text to match function.
*
* @param showPreview
* whether to show the preview (and thus not show the input)
*/
private void toggleVisibility(boolean showPreview)
{
Component textarea = detailForm.get("description");
Component preview = detailForm.get("previewPane");
Component button = detailForm.get("previewButton");
String hideClass = "hideCol";
String cssClasses = String.valueOf(textarea.getMarkupAttributes().get("class"));
// show preview, hide input, set button label to input
if (showPreview) {
button.setDefaultModel(new ResourceModel("inputLabel"));
preview.setVisible(true);
cssClasses = cssClasses.equals("null") ? "" : cssClasses;
cssClasses = cssClasses + " " + hideClass;
}
// hide preview, show input, set button label to preview
else {
button.setDefaultModel(new ResourceModel("previewLabel"));
preview.setVisible(false);
cssClasses = cssClasses.replaceFirst(hideClass, "").trim();
}
// use css display:none instead of wicket setVisible(), because otherwise the model
// reloads the AnnotationType object from the database
textarea.add(new AttributeModifier("class", cssClasses));
}
}
private SelectionForm selectionForm;
private DetailForm detailForm;
public AnnotationTypePage()
{
selectionForm = new SelectionForm("selectionForm");
detailForm = new DetailForm("detailForm");
detailForm.setVisible(false);
add(selectionForm);
add(detailForm);
}
}