/* * Copyright (c) 2010-2015 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; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.model.api.ModelExecuteOptions; import com.evolveum.midpoint.prism.PrismConstants; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.query.ObjectPaging; import com.evolveum.midpoint.prism.query.OrderDirection; import com.evolveum.midpoint.schema.*; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.AuthorizationConstants; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.Holder; import com.evolveum.midpoint.util.MiscUtil; 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.application.AuthorizationAction; import com.evolveum.midpoint.web.application.PageDescriptor; import com.evolveum.midpoint.web.component.AceEditor; import com.evolveum.midpoint.web.component.AjaxButton; import com.evolveum.midpoint.web.component.AjaxSubmitButton; import com.evolveum.midpoint.web.component.input.MultiStateHorizontalButton; import com.evolveum.midpoint.web.page.admin.dto.ObjectViewDto; import com.evolveum.midpoint.web.security.MidPointApplication; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.lang.StringUtils; import org.apache.wicket.RestartResponseException; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextArea; import org.apache.wicket.model.AbstractReadOnlyModel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.model.PropertyModel; import org.apache.wicket.util.string.StringValue; import javax.xml.namespace.QName; import java.util.Arrays; import java.util.Collection; import java.util.List; @PageDescriptor(url = "/admin/config/debug", action = { @AuthorizationAction(actionUri = PageAdminConfiguration.AUTH_CONFIGURATION_ALL, label = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_LABEL, description = PageAdminConfiguration.AUTH_CONFIGURATION_ALL_DESCRIPTION), @AuthorizationAction(actionUri = AuthorizationConstants.AUTZ_UI_CONFIGURATION_DEBUG_URL, label = "PageDebugView.auth.debug.label", description = "PageDebugView.auth.debug.description")}) public class PageDebugView extends PageAdminConfiguration { private static final String DOT_CLASS = PageDebugView.class.getName() + "."; private static final String OPERATION_LOAD_OBJECT = DOT_CLASS + "loadObject"; private static final String OPERATION_SAVE_OBJECT = DOT_CLASS + "saveObject"; private static final String OPERATION_VALIDATE_OBJECT = DOT_CLASS + "validateObject"; private static final String ID_PLAIN_TEXTAREA = "plain-textarea"; private static final String ID_VIEW_BUTTON_PANEL = "viewButtonPanel"; private static final Trace LOGGER = TraceManager.getTrace(PageDebugView.class); public static final String PARAM_OBJECT_ID = "objectId"; public static final String PARAM_OBJECT_TYPE = "objectType"; private IModel<ObjectViewDto> model; private AceEditor editor; private final IModel<Boolean> encrypt = new Model<>(true); private final IModel<Boolean> saveAsRaw = new Model<>(true); private final IModel<Boolean> reevaluateSearchFilters = new Model<>(false); private final IModel<Boolean> validateSchema = new Model<>(false); private final IModel<Boolean> switchToPlainText = new Model<>(false); private TextArea<String> plainTextarea; final Form mainForm = new Form("mainForm"); private String dataLanguage; private ObjectViewDto objectViewDto; private boolean isInitialized = false; public PageDebugView() { model = new IModel<ObjectViewDto>() { @Override public ObjectViewDto getObject() { if (!isInitialized) { objectViewDto = loadObject(); isInitialized = true; } return objectViewDto; } @Override public void setObject(ObjectViewDto o) { objectViewDto = o; } @Override public void detach() { } }; dataLanguage = determineDataLanguage(); initLayout(); } private String determineDataLanguage() { AdminGuiConfigurationType config = loadAdminGuiConfiguration(); if (config != null && config.getPreferredDataLanguage() != null) { if (PrismContext.LANG_JSON.equals(config.getPreferredDataLanguage())){ return PrismContext.LANG_JSON; } else if (PrismContext.LANG_YAML.equals(config.getPreferredDataLanguage())){ return PrismContext.LANG_YAML; } else { return PrismContext.LANG_XML; } } else { return PrismContext.LANG_XML; } } @Override protected IModel<String> createPageTitleModel() { return new AbstractReadOnlyModel<String>() { @Override public String getObject() { if (model == null) { return ""; } ObjectViewDto object; try { object = model.getObject(); } catch (RuntimeException e) { // e.g. when the object is unreadable LoggingUtils.logUnexpectedException(LOGGER, "Couldn't get object", e); return ""; } if (object == null) { return ""; } else { return createStringResource("PageDebugView.title", object.getName()).getString(); } } }; } private ObjectViewDto loadObject() { StringValue objectOid = getPageParameters().get(PARAM_OBJECT_ID); if (objectOid == null || StringUtils.isEmpty(objectOid.toString())) { getSession().error(getString("pageDebugView.message.oidNotDefined")); throw new RestartResponseException(PageDebugList.class); } Task task = createSimpleTask(OPERATION_LOAD_OBJECT); OperationResult result = task.getResult(); //todo is this result != null ? ObjectViewDto dto = null; try { MidPointApplication application = PageDebugView.this.getMidpointApplication(); GetOperationOptions rootOptions = GetOperationOptions.createRaw(); rootOptions.setResolveNames(true); rootOptions.setTolerateRawData(true); Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions.createCollection(rootOptions); // FIXME: ObjectType.class will not work well here. We need more specific type. //todo on page debug list create page params, put there oid and class for object type and send that to this page....read it here Class type = ObjectType.class; StringValue objectType = getPageParameters().get(PARAM_OBJECT_TYPE); if (objectType != null && StringUtils.isNotBlank(objectType.toString())){ type = getPrismContext().getSchemaRegistry().determineCompileTimeClass(new QName(SchemaConstantsGenerated.NS_COMMON, objectType.toString())); } // TODO make this configurable (or at least do not show campaign cases in production) if (UserType.class.isAssignableFrom(type)) { options.add(SelectorOptions.create(UserType.F_JPEG_PHOTO, GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE))); } if (LookupTableType.class.isAssignableFrom(type)) { options.add(SelectorOptions.create(LookupTableType.F_ROW, GetOperationOptions.createRetrieve( new RelationalValueSearchQuery( ObjectPaging.createPaging(PrismConstants.T_ID, OrderDirection.ASCENDING))))); } if (AccessCertificationCampaignType.class.isAssignableFrom(type)) { options.add(SelectorOptions.create(AccessCertificationCampaignType.F_CASE, GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE))); } PrismObject<ObjectType> object = getModelService().getObject(type, objectOid.toString(), options, task, result); PrismContext context = application.getPrismContext(); String lex = context.serializerFor(dataLanguage).serialize(object); dto = new ObjectViewDto(object.getOid(), WebComponentUtil.getName(object), object, lex); result.recomputeStatus(); } catch (Exception ex) { result.recordFatalError("Couldn't load object.", ex); } if (dto == null) { showResult(result); throw new RestartResponseException(PageDebugList.class); } showResult(result, false); if (!WebComponentUtil.isSuccessOrHandledErrorOrWarning(result)) { showResult(result, false); throw new RestartResponseException(PageDebugList.class); } return dto; } private void initLayout() { add(mainForm); mainForm.add(new AjaxCheckBox("encrypt", encrypt) { @Override protected void onUpdate(AjaxRequestTarget target) { } }); mainForm.add(new AjaxCheckBox("saveAsRaw", saveAsRaw) { @Override protected void onUpdate(AjaxRequestTarget target) { } }); mainForm.add(new AjaxCheckBox("reevaluateSearchFilters", reevaluateSearchFilters) { @Override protected void onUpdate(AjaxRequestTarget target) { } }); mainForm.add(new AjaxCheckBox("validateSchema", validateSchema) { @Override protected void onUpdate(AjaxRequestTarget target) { } }); mainForm.add(new AjaxCheckBox("switchToPlainText", switchToPlainText) { @Override protected void onUpdate(AjaxRequestTarget target) { if (switchToPlainText.getObject()){ editor.setVisible(false); plainTextarea.setVisible(true); } else { editor.setVisible(true); plainTextarea.setVisible(false); } target.add(mainForm); } }); plainTextarea = new TextArea<>(ID_PLAIN_TEXTAREA, new PropertyModel<String>(model, ObjectViewDto.F_XML)); plainTextarea.setVisible(false); mainForm.add(plainTextarea); addOrReplaceEditor(); initButtons(mainForm); initViewButton(mainForm); } private void addOrReplaceEditor(){ editor = new AceEditor("aceEditor", new PropertyModel<String>(model, ObjectViewDto.F_XML)); editor.setModeForDataLanguage(dataLanguage); editor.add(new AjaxFormComponentUpdatingBehavior("blur") { @Override protected void onUpdate(AjaxRequestTarget target) { } }); mainForm.addOrReplace(editor); } private void initViewButton(Form mainForm) { List<String> propertyKeysList = Arrays.asList("PageDebugView.xmlViewButton", "PageDebugView.xmlJsonButton", "PageDebugView.xmlYamlButton"); int selectedIndex = 0; if (PrismContext.LANG_JSON.equals(dataLanguage)){ selectedIndex = 1; } else if (PrismContext.LANG_YAML.equals(dataLanguage)){ selectedIndex = 2; } MultiStateHorizontalButton viewButtonPanel = new MultiStateHorizontalButton(ID_VIEW_BUTTON_PANEL, selectedIndex, propertyKeysList,PageDebugView.this){ @Override protected void onStateChanged(int index, AjaxRequestTarget target){ OperationResult result = new OperationResult(OPERATION_VALIDATE_OBJECT); Holder<PrismObject<ObjectType>> objectHolder = new Holder<>(null); try { validateObject(result, objectHolder); if (result.isAcceptable()) { if (index == 1){ dataLanguage = PrismContext.LANG_JSON; } else if (index == 2){ dataLanguage = PrismContext.LANG_YAML; } else { dataLanguage = PrismContext.LANG_XML; } PrismObject<ObjectType> updatedObject = objectHolder.getValue(); PrismContext context = getMidpointApplication().getPrismContext(); String objectStr = context.serializerFor(dataLanguage).serialize(updatedObject); objectViewDto.setXml(objectStr); setSelectedIndex(index); addOrReplaceEditor(); target.add(mainForm); target.add(getFeedbackPanel()); } else { showResult(result); target.add(getFeedbackPanel()); } } catch (Exception ex) { result.recordFatalError("Couldn't change the language.", ex); showResult(result); target.add(getFeedbackPanel()); } } }; viewButtonPanel.setOutputMarkupId(true); mainForm.add(viewButtonPanel); } private void initButtons(final Form mainForm) { AjaxSubmitButton saveButton = new AjaxSubmitButton("saveButton", createStringResource("pageDebugView.button.save")) { @Override protected void onSubmit(AjaxRequestTarget target, Form<?> form) { savePerformed(target); } @Override protected void onError(AjaxRequestTarget target, Form<?> form) { target.add(getFeedbackPanel()); } }; mainForm.add(saveButton); AjaxButton backButton = new AjaxButton("backButton", createStringResource("pageDebugView.button.back")) { @Override public void onClick(AjaxRequestTarget target) { redirectBack(); } }; mainForm.add(backButton); } private boolean isReport(PrismObject object){ if (object.getCompileTimeClass() != null && object.getCompileTimeClass() == ReportType.class){ return true; } if (object.getDefinition() != null && object.getDefinition().getName().equals(ReportType.COMPLEX_TYPE)){ return true; } return false; } public void savePerformed(AjaxRequestTarget target) { ObjectViewDto dto = model.getObject(); if (StringUtils.isEmpty(dto.getXml())) { error(getString("pageDebugView.message.cantSaveEmpty")); target.add(getFeedbackPanel()); return; } Task task = createSimpleTask(OPERATION_SAVE_OBJECT); OperationResult result = task.getResult(); try { PrismObject<ObjectType> oldObject = dto.getObject(); oldObject.revive(getPrismContext()); Holder<PrismObject<ObjectType>> objectHolder = new Holder<>(null); validateObject(result, objectHolder); if (result.isAcceptable()) { PrismObject<ObjectType> newObject = objectHolder.getValue(); ObjectDelta<ObjectType> delta = oldObject.diff(newObject, true, true); if (delta.getPrismContext() == null) { LOGGER.warn("No prism context in delta {} after diff, adding it", delta); delta.revive(getPrismContext()); } if (LOGGER.isTraceEnabled()) { LOGGER.trace("Delta to be applied:\n{}", delta.debugDump()); } //quick fix for now (MID-1910), maybe it should be somewhere in model.. // if (isReport(oldObject)){ // ReportTypeUtil.applyConfigurationDefinition((PrismObject)newObject, delta, getPrismContext()); // } Collection<ObjectDelta<? extends ObjectType>> deltas = (Collection) MiscUtil.createCollection(delta); ModelExecuteOptions options = new ModelExecuteOptions(); if (saveAsRaw.getObject()) { options.setRaw(true); } if (reevaluateSearchFilters.getObject()) { options.setReevaluateSearchFilters(true); } if(!encrypt.getObject()) { options.setNoCrypt(true); } getModelService().executeChanges(deltas, options, task, result); result.computeStatus(); } } catch (Exception ex) { result.recordFatalError("Couldn't save object.", ex); } if (result.isError()) { showResult(result); target.add(getFeedbackPanel()); } else { showResult(result); redirectBack(); } } private void validateObject(OperationResult result, Holder<PrismObject<ObjectType>> objectHolder){ validateObject(objectViewDto.getXml(), objectHolder, dataLanguage, validateSchema.getObject(), result); } }