/*
* Copyright (c) 2010-2016 Evolveum
*
* 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 com.evolveum.midpoint.web.page.admin.configuration.component;
import com.evolveum.midpoint.gui.api.model.LoadableModel;
import com.evolveum.midpoint.gui.api.page.PageBase;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.match.PolyStringNormMatchingRule;
import com.evolveum.midpoint.prism.polystring.PolyStringNormalizer;
import com.evolveum.midpoint.prism.query.AndFilter;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.query.SubstringFilter;
import com.evolveum.midpoint.prism.query.builder.QueryBuilder;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.AjaxButton;
import com.evolveum.midpoint.web.component.BasicSearchPanel;
import com.evolveum.midpoint.web.component.data.ObjectDataProvider;
import com.evolveum.midpoint.web.component.data.TablePanel;
import com.evolveum.midpoint.web.component.data.column.LinkColumn;
import com.evolveum.midpoint.web.component.util.SelectableBean;
import com.evolveum.midpoint.web.component.util.VisibleEnableBehaviour;
import com.evolveum.midpoint.web.page.admin.configuration.dto.ObjectSearchDto;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.Component;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;
import javax.xml.namespace.QName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author shood
* */
public class ObjectSelectionPanel extends Panel {
private static final Trace LOGGER = TraceManager.getTrace(ObjectSelectionPanel.class);
private static final String DEFAULT_SORTABLE_PROPERTY = null;
private static final String ID_EXTRA_CONTENT_CONTAINER = "extraContentContainer";
private static final String ID_TABLE = "table";
private static final String ID_BUTTON_CANCEL = "cancelButton";
private static final String ID_SEARCH_FORM = "searchForm";
private static final String ID_BASIC_SEARCH = "basicSearch";
private Class<? extends ObjectType> objectType;
private boolean initialized;
private IModel<ObjectSearchDto> searchModel;
private ModalWindow modalWindow;
private Context context;
/**
* Used to communicate between this panel (shown in a popup window) and the calling component (panel).
* Above all, this is the place where customization of popup behavior is done, by overriding methods
* in this class. (Originally this was done by subclassing the panel itself; it is not possible anymore.)
*/
public static abstract class Context implements Serializable {
// It is not possible to refer to the calling page using Java object, because it is (seemingly)
// reinstantiated by the wicket during processing. So we have to use the reference, and the
// context.getCallingPage() method.
protected PageReference callingPageReference;
protected Component callingComponent;
public Context(Component callingComponent) {
this.callingComponent = callingComponent;
}
public abstract void chooseOperationPerformed(AjaxRequestTarget target, ObjectType object);
public ObjectQuery getDataProviderQuery() {
return null;
}
/**
* Determines if search capabilities in this modal window are or are not enabled
* */
public boolean isSearchEnabled(){
return false;
}
/**
* provides search property to the search filter
* */
public QName getSearchProperty(){
return ObjectType.F_NAME;
}
public String getSortableProperty(){
return DEFAULT_SORTABLE_PROPERTY;
}
public abstract Class<? extends ObjectType> getObjectTypeClass();
public PageReference getCallingPageReference() {
return callingPageReference;
}
public PageBase getCallingPage() {
return (PageBase) callingComponent.getPage();
}
protected WebMarkupContainer createExtraContentContainer(String extraContentId, ObjectSelectionPanel objectSelectionPanel) {
WebMarkupContainer container = new WebMarkupContainer(extraContentId);
container.setOutputMarkupId(true);
container.setOutputMarkupPlaceholderTag(true);
return container;
}
public Collection<SelectorOptions<GetOperationOptions>> getDataProviderOptions(){
return null;
}
}
public ObjectSelectionPanel(String id, Class<? extends ObjectType> type, ModalWindow modalWindow, Context context) {
super(id);
this.modalWindow = modalWindow;
this.context = context;
searchModel = new LoadableModel<ObjectSearchDto>(false) {
@Override
protected ObjectSearchDto load() {
return new ObjectSearchDto();
}
};
objectType = type;
initLayout(context.getCallingPage());
initialized = true;
}
public void initLayout(PageBase pageBase){
Form searchForm = new Form(ID_SEARCH_FORM);
searchForm.setOutputMarkupId(true);
add(searchForm);
searchForm.add(new VisibleEnableBehaviour(){
@Override
public boolean isVisible() {
return context.isSearchEnabled();
}
});
BasicSearchPanel<ObjectSearchDto> basicSearch = new BasicSearchPanel<ObjectSearchDto>(ID_BASIC_SEARCH) {
@Override
protected IModel<String> createSearchTextModel() {
return new PropertyModel<>(searchModel, ObjectSearchDto.F_SEARCH_TEXT);
}
@Override
protected void searchPerformed(AjaxRequestTarget target) {
ObjectSelectionPanel.this.searchPerformed(target);
}
@Override
protected void clearSearchPerformed(AjaxRequestTarget target) {
ObjectSelectionPanel.this.clearSearchPerformed(target);
}
};
searchForm.add(basicSearch);
add(context.createExtraContentContainer(ID_EXTRA_CONTENT_CONTAINER, this));
List<IColumn<SelectableBean<ObjectType>, String>> columns = initColumns();
ObjectDataProvider provider = new ObjectDataProvider(pageBase, this.objectType);
provider.setQuery(context.getDataProviderQuery());
provider.setOptions(context.getDataProviderOptions());
TablePanel table = new TablePanel<>(ID_TABLE, provider, columns);
table.setOutputMarkupId(true);
addOrReplace(table);
AjaxButton cancelButton = new AjaxButton(ID_BUTTON_CANCEL,
createStringResource("chooseTypeDialog.button.cancel")) {
@Override
public void onClick(AjaxRequestTarget ajaxRequestTarget) {
modalWindow.close(ajaxRequestTarget);
}
};
add(cancelButton);
}
private List<IColumn<SelectableBean<ObjectType>, String>> initColumns() {
List<IColumn<SelectableBean<ObjectType>, String>> columns = new ArrayList<>();
IColumn column = new LinkColumn<SelectableBean<ObjectType>>(createStringResource("chooseTypeDialog.column.name"), context.getSortableProperty(), "value.name"){
@Override
public void onClick(AjaxRequestTarget target, IModel<SelectableBean<ObjectType>> rowModel){
ObjectType object = rowModel.getObject().getValue();
context.chooseOperationPerformed(target, object);
}
};
columns.add(column);
return columns;
}
private TablePanel getTablePanel() {
return (TablePanel) get(StringUtils.join(new String[]{ID_TABLE}, ":"));
}
private Form getSearchForm(){
return (Form) get(StringUtils.join(new String[]{ID_SEARCH_FORM}, ":"));
}
private WebMarkupContainer getExtraContentContainer(){
return (WebMarkupContainer) get(StringUtils.join(new String[]{ID_EXTRA_CONTENT_CONTAINER}, ":"));
}
public void updateTableByTypePerformed(AjaxRequestTarget target, Class<? extends ObjectType> newType) {
this.objectType = newType;
TablePanel table = getTablePanel();
DataTable dataTable = table.getDataTable();
ObjectDataProvider provider = (ObjectDataProvider)dataTable.getDataProvider();
provider.setType(objectType);
target.add(this, WebComponentUtil.getPageBase(this).getFeedbackPanel(), table);
}
public void updateTablePerformed(AjaxRequestTarget target, ObjectQuery query){
TablePanel table = getTablePanel();
DataTable dataTable = table.getDataTable();
ObjectDataProvider provider = (ObjectDataProvider)dataTable.getDataProvider();
provider.setQuery(query);
target.add(this, WebComponentUtil.getPageBase(this).getFeedbackPanel(), table);
}
public StringResourceModel createStringResource(String resourceKey, Object... objects) {
return PageBase.createStringResourceStatic(this, resourceKey, objects);
// return new StringResourceModel(resourceKey, this, null, resourceKey, objects);
}
private void searchPerformed(AjaxRequestTarget target){
ObjectQuery query = createObjectQuery();
TablePanel panel = getTablePanel();
DataTable table = panel.getDataTable();
ObjectDataProvider provider = (ObjectDataProvider) table.getDataProvider();
provider.setQuery(query);
target.add(panel);
}
private ObjectQuery createObjectQuery() {
ObjectSearchDto dto = searchModel.getObject();
ObjectQuery query = null;
if(StringUtils.isEmpty(dto.getText())) {
if(context.getDataProviderQuery() != null){
return context.getDataProviderQuery();
} else {
return query;
}
}
try {
PageBase pageBase = WebComponentUtil.getPageBase(this);
PrismContext prismContext = pageBase.getPrismContext();
PolyStringNormalizer normalizer = prismContext.getDefaultPolyStringNormalizer();
String normalized = normalizer.normalize(dto.getText());
ObjectFilter filter = QueryBuilder.queryFor(objectType, prismContext)
.item(context.getSearchProperty()).contains(normalized).matchingNorm()
.buildFilter();
if (context.getDataProviderQuery() != null) {
AndFilter and = AndFilter.createAnd(context.getDataProviderQuery().getFilter(), filter);
query = ObjectQuery.createObjectQuery(and);
} else {
query = ObjectQuery.createObjectQuery(filter);
}
} catch (Exception e){
error(getString("chooseTypeDialog.message.queryError") + " " + e.getMessage());
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't create query filter.", e);
}
return query;
}
private void clearSearchPerformed(AjaxRequestTarget target) {
searchModel.setObject(new ObjectSearchDto());
TablePanel panel = getTablePanel();
DataTable table = panel.getDataTable();
ObjectDataProvider provider = (ObjectDataProvider) table.getDataProvider();
if(context.getDataProviderQuery() != null){
provider.setQuery(context.getDataProviderQuery());
} else {
provider.setQuery(null);
}
target.add(panel, getSearchForm());
}
}