/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2014 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.server.ngclient.template; import java.io.PrintWriter; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.json.JSONObject; import org.jsoup.helper.StringUtil; import org.sablo.specification.PropertyDescription; import com.servoy.base.persistence.constants.IFormConstants; import com.servoy.j2db.BasicFormManager; import com.servoy.j2db.IFormController; import com.servoy.j2db.persistence.BaseComponent; import com.servoy.j2db.persistence.FlattenedForm; import com.servoy.j2db.persistence.Form; import com.servoy.j2db.persistence.IAnchorConstants; import com.servoy.j2db.persistence.IPersist; import com.servoy.j2db.persistence.Part; import com.servoy.j2db.server.ngclient.FormElement; import com.servoy.j2db.server.ngclient.FormElementHelper; import com.servoy.j2db.server.ngclient.IServoyDataConverterContext; import com.servoy.j2db.server.ngclient.WebFormUI; import com.servoy.j2db.util.Settings; import com.servoy.j2db.util.Utils; /** * Generates HTML for a absolute layout form * @author lvostinar */ @SuppressWarnings("nls") public class FormLayoutGenerator { public static void generateRecordViewForm(PrintWriter writer, Form form, String realFormName, IServoyDataConverterContext context, boolean design, boolean highlight) { generateFormStartTag(writer, form, realFormName, false, design); Iterator<Part> it = form.getParts(); if (design) { while (it.hasNext()) { Part part = it.next(); if (!Part.rendersOnlyInPrint(part.getPartType())) { writer.print("<div ng-style=\""); writer.print(PartWrapper.getName(part)); writer.print("Style\""); String partClass = "svy-" + PartWrapper.getName(part); if (part.getStyleClass() != null) { partClass += " " + part.getStyleClass(); } writer.print(" class=\""); writer.print(partClass); writer.println("\">"); generateEndDiv(writer); } } } Map<IPersist, FormElement> cachedElementsMap = new HashMap<IPersist, FormElement>(); if (context != null && context.getApplication() != null) { IFormController controller = ((BasicFormManager)context.getApplication().getFormManager()).getCachedFormController(realFormName); if (controller != null && controller.getFormUI() instanceof WebFormUI) { List<FormElement> cachedFormElements = ((WebFormUI)controller.getFormUI()).getFormElements(); for (FormElement fe : cachedFormElements) { if (fe.getPersistIfAvailable() != null) { cachedElementsMap.put(fe.getPersistIfAvailable(), fe); } } } } it = form.getParts(); while (it.hasNext()) { Part part = it.next(); if (!Part.rendersOnlyInPrint(part.getPartType())) { if (!design) { writer.print("<div ng-style=\""); writer.print(PartWrapper.getName(part)); writer.print("Style\""); String partClass = "svy-" + PartWrapper.getName(part); if (part.getStyleClass() != null) { partClass += " " + part.getStyleClass(); } writer.print(" class=\""); writer.print(partClass); writer.println("\">"); } for (BaseComponent bc : PartWrapper.getBaseComponents(part, form, context, design)) { FormElement fe = null; if (cachedElementsMap.containsKey(bc)) { fe = cachedElementsMap.get(bc); } if (fe == null) { fe = FormElementHelper.INSTANCE.getFormElement(bc, context, null); } generateFormElementWrapper(writer, fe, design, form); generateFormElement(writer, fe, form, false, highlight); generateEndDiv(writer); } if (!design) generateEndDiv(writer); } } generateFormEndTag(writer); } public static void generateFormStartTag(PrintWriter writer, Form form, String realFormName, boolean responsiveMode, boolean design) { writer.print(String.format("<svy-formload formname=\"%1$s\"><div ng-controller=\"%1$s\" ", realFormName)); if (Utils.getAsBoolean(Settings.getInstance().getProperty("servoy.ngclient.testingMode", "false"))) { writer.print(String.format("data-svy-name=\"%1$s\" ", realFormName)); } if (!form.isResponsiveLayout() && !responsiveMode) { writer.print("svy-formstyle=\"formStyle\" "); } else if (design) { writer.print(" style=\"height:100%\""); } writer.print("svy-layout-update svy-autosave "); // skip the scrollbars for forms in table or list view then the portal component does this. if (!isTableOrListView(form)) { writer.print(" svy-scrollbars='formProperties.scrollbars'"); } String formClass = "svy-form"; if (form.getStyleClass() != null) { formClass += " " + form.getStyleClass(); } writer.print(" class=\""); writer.print(formClass); writer.print("\""); writer.println(">"); } public static boolean isTableOrListView(Form form) { return (form.getView() == IFormConstants.VIEW_TYPE_TABLE || form.getView() == IFormConstants.VIEW_TYPE_TABLE_LOCKED || form.getView() == IFormConstants.VIEW_TYPE_LIST || form.getView() == IFormConstants.VIEW_TYPE_LIST_LOCKED); } public static void generateEndDiv(PrintWriter writer) { writer.println("</div>"); } public static void generateFormEndTag(PrintWriter writer) { generateEndDiv(writer); writer.println("</svy-formload>"); } public static void generateFormElementWrapper(PrintWriter writer, FormElement fe, boolean design, Form form) { writer.print("<div ng-style=\"layout."); writer.print(fe.getName()); writer.print("\""); if (!form.isResponsiveLayout() && fe.getPersistIfAvailable() instanceof BaseComponent) { BaseComponent bc = (BaseComponent)fe.getPersistIfAvailable(); int anchors = bc.getAnchors(); String style = ""; if (((anchors & IAnchorConstants.EAST) > 0) && ((anchors & IAnchorConstants.WEST) > 0)) { style += "min-width:" + bc.getSize().width + "px;"; } if (((anchors & IAnchorConstants.NORTH) > 0) && ((anchors & IAnchorConstants.SOUTH) > 0)) { style += "min-height:" + bc.getSize().height + "px"; } if (!style.isEmpty()) { writer.print(" style='"); writer.print(style); writer.print("'"); } } writer.print(" ng-class=\"'svy-wrapper'\" "); if (design) { writer.print(" svy-id='"); writer.print(getDesignId(fe)); writer.print("'"); writer.print(" name='"); writer.print(fe.getName()); writer.print("'"); if (isNotSelectable(fe)) writer.print(" svy-non-selectable"); Form currentForm = form; if (form instanceof FlattenedForm) currentForm = ((FlattenedForm)form).getForm(); if (fe.getPersistIfAvailable() != null && Utils.isInheritedFormElement(fe.getPersistIfAvailable(), currentForm)) { writer.print(" class='inherited_element'"); } List<String> typeNames = fe.getSvyTypesNames(); if (typeNames.size() > 0) { writer.print(" svy-types='"); writer.print("[" + StringUtil.join(typeNames, ",") + "]"); writer.print("'"); } List<String> forbiddenComponentNames = fe.getForbiddenComponentNames(); if (forbiddenComponentNames.size() > 0) { writer.print(" svy-forbidden-components='"); writer.print("[" + StringUtil.join(forbiddenComponentNames, ",") + "]"); writer.print("'"); } String directEditPropertyName = getDirectEditProperty(fe); if (directEditPropertyName != null) { writer.print(" directEditPropertyName='"); writer.print(directEditPropertyName); writer.print("'"); } } writer.println(">"); } // private static boolean canContainComponents(WebComponentSpecification spec) // { // Map<String, PropertyDescription> properties = spec.getProperties(); // for (PropertyDescription propertyDescription : properties.values()) // { // String simpleTypeName = propertyDescription.getType().getName().replaceFirst(spec.getName() + ".", ""); // if (simpleTypeName.equals(ComponentPropertyType.TYPE_NAME)) return true; // Object configObject = propertyDescription.getConfig(); // if (configObject != null) // { // try // { // if (configObject instanceof JSONObject && ((JSONObject)configObject).has(DesignerFilter.DROPPABLE)) // { // Object droppable = ((JSONObject)configObject).get(DesignerFilter.DROPPABLE); // if (droppable instanceof Boolean && (Boolean)droppable) // { // if (simpleTypeName.equals("tab")) return true; // } // } // } // catch (JSONException e) // { // Debug.log(e); // } // } // } // return false; // } public static void generateFormElement(PrintWriter writer, FormElement fe, Form form, boolean design, boolean highlight) { if (highlight) writer.print("<div class='highlight_element" + (form.isResponsiveLayout() ? "" : " inherit_size") + "'>"); writer.print("<"); writer.print(fe.getTagname()); writer.print(" name='"); writer.print(fe.getName()); writer.print("'"); if (Utils.getAsBoolean(Settings.getInstance().getProperty("servoy.ngclient.testingMode", "false"))) { String elementName = fe.getName(); if (elementName.startsWith("svy_") && fe.getPersistIfAvailable() != null) { elementName = "svy_" + fe.getPersistIfAvailable().getUUID().toString(); } writer.print(" data-svy-name='"); writer.print(form.getName() + "." + elementName); writer.print("'"); } writer.print(" svy-model='model."); writer.print(fe.getName()); writer.print("'"); writer.print(" svy-api='api."); writer.print(fe.getName()); writer.print("'"); writer.print(" svy-handlers='handlers."); writer.print(fe.getName()); writer.print("'"); if (design)//this is false in absolute layout { writer.print(" svy-id='"); writer.print(fe.getDesignId()); writer.print("'"); if (form.isResponsiveLayout()) { List<String> typeNames = fe.getSvyTypesNames(); if (typeNames.size() > 0) { writer.print(" svy-types='"); writer.print("[" + StringUtil.join(typeNames, ",") + "]"); writer.print("'"); } List<String> forbiddenComponentNames = fe.getForbiddenComponentNames(); if (forbiddenComponentNames.size() > 0) { writer.print(" svy-forbidden-components='"); writer.print("[" + StringUtil.join(forbiddenComponentNames, ",") + "]"); writer.print("'"); } JSONObject ngClass = new JSONObject(); if (!fe.getForm().equals(form))//is this inherited? { ngClass.put("inheritedElement", true); } if (ngClass.length() > 0) writer.print(" ng-class='" + ngClass + "'"); } String directEditPropertyName = getDirectEditProperty(fe); if (directEditPropertyName != null) { writer.print(" directEditPropertyName='"); writer.print(directEditPropertyName); writer.print("'"); } } writer.print(" svy-servoyApi='handlers."); writer.print(fe.getName()); writer.print(".svy_servoyApi'"); writer.println(">"); writer.print("</"); writer.print(fe.getTagname()); writer.println(">"); if (highlight) writer.print("</div>"); } /** * When the formElement represents a portal of a list / table view form, we should return the uuid of the form. * @param fe * @return */ private static String getDesignId(FormElement fe) { return (fe.getDesignId() == null && fe.getTypeName().equals("servoycore-portal")) ? fe.getForm().getUUID().toString() : fe.getDesignId(); } private static String getDirectEditProperty(FormElement fe) { Map<String, PropertyDescription> properties = fe.getWebComponentSpec(false).getProperties(); for (PropertyDescription pd : properties.values()) { if (Utils.getAsBoolean(pd.getTag("directEdit"))) { return pd.getName(); } } return null; } private static boolean isNotSelectable(FormElement fe) { return (fe.getDesignId() == null && fe.getTypeName().equals("servoycore-portal")); } }