diff --git a/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs index 384869724..a5646f56a 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/Web/HttpAjaxContext.cs @@ -58,10 +58,67 @@ internal enum SessionType RO_SESSION, NO_SESSION, } + internal interface IDynAjaxEventContext + { + void Clear(); + void ClearParmsMetadata(); + bool isInputParm(string key); + void SetParmHash(string fieldName, object value); + bool isParmModified(string fieldName, object value); + JArray inParmsMetadata { get; } + HashSet inParmsMetadataHash { get; } + JArray outParmsMetadata { get; } + HashSet outParmsMetadataHash { get; } + } + internal class DynAjaxEventContext: IDynAjaxEventContext + { + public JArray inParmsMetadata { get; set; } + public HashSet inParmsMetadataHash { get; set; } + public JArray outParmsMetadata { get; set; } + public HashSet outParmsMetadataHash { get; set; } + private StringDictionary inParmsHashValue = new StringDictionary(); + + public void ClearParmsMetadata() + { + inParmsMetadata = new JArray(); + inParmsMetadataHash = new HashSet(); + outParmsMetadata = new JArray(); + outParmsMetadataHash = new HashSet(); + } + + public bool isInputParm(string key) + { + return inParmsMetadataHash.Contains(key); + } + + public void Clear() + { + inParmsHashValue.Clear(); + } + public void SetParmHash(string fieldName, object value) + { + IGxJSONSerializable jsonValue = value as IGxJSONSerializable; + if (jsonValue != null) + { + inParmsHashValue.Add(fieldName, GXUtil.GetHash(jsonValue.ToJSonString())); + } + } + public bool isParmModified(string fieldName, object value) + { + IGxJSONSerializable jsonValue = value as IGxJSONSerializable; + if (value != null) + { + if (!inParmsHashValue.ContainsKey(fieldName)) + return true; + return GXUtil.GetHash(jsonValue.ToJSonString()) != inParmsHashValue[fieldName]; + } + return true; + } + } public class HttpAjaxContext : IHttpAjaxContext { - private IGxContext _context; + private IGxContext _context; static readonly ILog log = log4net.LogManager.GetLogger(typeof(HttpAjaxContext)); private Stack cmpContents = new Stack(); private GXAjaxCommandCollection commands = new GXAjaxCommandCollection(); @@ -73,15 +130,19 @@ public class HttpAjaxContext : IHttpAjaxContext private JObject _Messages = new JObject(); private JArray _Grids = new JArray(); private JObject _ComponentObjects = new JObject(); - private JArray _StylesheetsToLoad = new JArray(); - private NameValueCollection _formVars; + private JArray _StylesheetsToLoad = new JArray(); + private NameValueCollection _formVars; + private IDynAjaxEventContext _DynAjaxEventContext = new DynAjaxEventContext(); + #if !NETCORE private GXWebRow _currentGridRow; #endif - private bool _isJsOutputEnabled = true; + private bool _isJsOutputEnabled = true; internal SessionType SessionType { get; set; } + internal IDynAjaxEventContext DynAjaxEventContext { get { return _DynAjaxEventContext; } } + public string FormCaption { get; set; } public JObject HiddenValues @@ -266,6 +327,18 @@ public void ajax_rsp_assign_attri(String CmpContext, bool IsMasterPage, String A } } } + + private bool isUndefinedOutParam(string key, Object SdtObj) { + + if (!DynAjaxEventContext.isInputParm(key)) + { + if (SdtObj is IGXAssigned parm) + { + return !parm.IsAssigned; + } + } + return false; + } public void ajax_rsp_assign_sdt_attri(String CmpContext, bool IsMasterPage, String AttName, Object SdtObj) { if (isJsOutputEnabled) @@ -275,7 +348,7 @@ public void ajax_rsp_assign_sdt_attri(String CmpContext, bool IsMasterPage, Stri try { JObject obj = GetGxObject(AttValues, CmpContext, IsMasterPage); - if (obj != null) + if (obj != null && (DynAjaxEventContext.isParmModified(AttName, SdtObj) || !isUndefinedOutParam( AttName, SdtObj))) { IGxJSONAble SdtObjJson = SdtObj as IGxJSONAble; if (SdtObjJson != null) diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs index 11edf6fe1..cde4a563c 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxCollections.cs @@ -288,7 +288,7 @@ public bool FromJSonFile(GxFile file, GXBaseCollection Mess [Serializable] [CollectionDataContract(Name = "GxSimpleCollection")] - public class GxSimpleCollection : List, IGxXMLSerializable, ICloneable, IGxJSONAble, IGxCollection, IGxJSONSerializable + public class GxSimpleCollection : GXBaseList, IGxXMLSerializable, ICloneable, IGxJSONAble, IGxCollection, IGxJSONSerializable { protected CollectionBase _jsonArr; @@ -1030,14 +1030,16 @@ public string Name [Serializable] [XmlType(IncludeInSchema = false)] - public class GxUserType : IGxXMLSerializable, ICloneable, IGxJSONAble, IGxJSONSerializable + public class GxUserType : IGxXMLSerializable, ICloneable, IGxJSONAble, IGxJSONSerializable, IGXAssigned { static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GxUserType)); protected GXProperties dirties = new GXProperties(); static object setupChannelObject = null; static bool setupChannelInitialized; - + [XmlIgnore] + public bool IsAssigned { get; set; } + static void loadConfigurator() { if (GxUserType.setupChannelObject == null && !GxUserType.setupChannelInitialized) @@ -1085,9 +1087,10 @@ JObject JsonObj public GxUserType() { + IsAssigned = true; } - public virtual void SetDirty(string fieldName) + public virtual void SetDirty(string fieldName) { dirties[fieldName.ToLower()] = "true"; } diff --git a/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs b/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs index 1de47ed49..1a22837ac 100644 --- a/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs +++ b/dotnet/src/dotnetframework/GxClasses/Domain/GxGenericCollections.cs @@ -11,9 +11,43 @@ namespace GeneXus.Utils { + internal interface IGXAssigned + { + bool IsAssigned { get; set; } + } + + public class GXBaseList : List, IGXAssigned + { + public GXBaseList() + { + IsAssigned = true; + } + [XmlIgnore] + public bool IsAssigned { get; set; } + + public new void Clear() { + base.Clear(); + IsAssigned = true; + } + public new void RemoveAt(int idx) + { + base.RemoveAt(idx); + IsAssigned = true; + } + public new void Add(T TObject) + { + base.Add(TObject); + IsAssigned = true; + } + public new void Insert(int idx, T TObject) + { + base.Insert(idx, TObject); + IsAssigned = true; + } + } [Serializable] - public class GXBaseCollection : List, IGxXMLSerializable, IGxJSONAble, IGxCollection, IGxJSONSerializable where T : GxUserType, IGxXMLSerializable, IGxJSONAble, new() + public class GXBaseCollection : GXBaseList, IGxXMLSerializable, IGxJSONAble, IGxCollection, IGxJSONSerializable where T : GxUserType, IGxXMLSerializable, IGxJSONAble, new() { static readonly ILog log = log4net.LogManager.GetLogger(typeof(GeneXus.Utils.GXBaseCollection)); @@ -364,7 +398,7 @@ public IList ExternalInstance { get { - return this; + return (IList) this; } set { diff --git a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs index 2e576f463..b94251187 100644 --- a/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs +++ b/dotnet/src/dotnetframework/GxClasses/Middleware/GXHttp.cs @@ -208,7 +208,7 @@ private static bool IsNumericType(Type t) } #if !NETCORE - protected IGxContext _Context; + protected IGxContext _Context; bool _isMain; #endif bool _isStatic; @@ -308,7 +308,7 @@ public DataIntegrityException(string message) : base(message) } } - public class DynAjaxEvent + internal class DynAjaxEvent { JArray inParmsValues; JArray inHashValues; @@ -319,9 +319,13 @@ public class DynAjaxEvent string cmpContext = string.Empty; int grid; string row, pRow = string.Empty; - JArray inParmsMetadata; - private HashSet inParmsMetadataHash; bool anyError; + internal IDynAjaxEventContext DynAjaxEventContext; + + internal DynAjaxEvent(IDynAjaxEventContext DynAjaxEventContext) + { + this.DynAjaxEventContext = DynAjaxEventContext; + } private void ParseInputJSonMessage(JObject objMessage, GXHttpHandler targetObj) { @@ -373,10 +377,10 @@ private void ParseInputJSonMessage(JObject objMessage, GXHttpHandler targetObj) pRow = (string)objMessage["pRow"]; if (objMessage.Contains("gxstate")) { - ParseGXStateParms((JObject)objMessage["gxstate"]); - } - if (objMessage.Contains("fullPost")) - { + ParseGXStateParms((JObject)objMessage["gxstate"]); + } + if (objMessage.Contains("fullPost")) + { this.targetObj._Context.httpAjaxContext.ParseGXState((Jayrock.Json.JObject)objMessage["fullPost"]); } } @@ -411,18 +415,12 @@ private string BuildOutputJSonMessage() return ((GxContext)(targetObj._Context)).getJSONResponse(this.cmpContext); } - private void ClearInputParmsMetadata() - { - inParmsMetadata = new JArray(); - inParmsMetadataHash = new HashSet(); - } - private bool IsInternalParm(JObject parm) { return parm.Contains("sPrefix") || parm.Contains("sSFPrefix") || parm.Contains("sCompEvt"); } - private void AddInputParmsMetadata(JObject inputParm) + private void AddParmsMetadata(JObject inputParm, JArray ParmsList, HashSet ParmsListHash) { string key = string.Empty; @@ -443,12 +441,12 @@ private void AddInputParmsMetadata(JObject inputParm) key = (string)inputParm["ctrl"]; } - if (String.IsNullOrEmpty(key) || !inParmsMetadataHash.Contains(key)) + if (String.IsNullOrEmpty(key) || !ParmsListHash.Contains(key)) { - inParmsMetadata.Add(inputParm); + ParmsList.Add(inputParm); if (!String.IsNullOrEmpty(key)) { - inParmsMetadataHash.Add(key); + ParmsListHash.Add(key); } } } @@ -457,7 +455,7 @@ private void ParseMetadata() { try { - ClearInputParmsMetadata(); + DynAjaxEventContext.ClearParmsMetadata(); eventHandlers = new string[events.Length]; eventUseInternalParms = new bool[events.Length]; int eventCount = 0; @@ -468,9 +466,14 @@ private void ParseMetadata() JArray eventInputParms = (JArray)eventMetadata["iparms"]; foreach (JObject inputParm in eventInputParms) { - AddInputParmsMetadata(inputParm); + AddParmsMetadata(inputParm, DynAjaxEventContext.inParmsMetadata, DynAjaxEventContext.inParmsMetadataHash); eventUseInternalParms[eventCount] = eventUseInternalParms[eventCount] || IsInternalParm(inputParm); } + JArray eventOutputParms = (JArray)eventMetadata["oparms"]; + foreach (JObject outputParm in eventOutputParms) + { + AddParmsMetadata(outputParm, DynAjaxEventContext.outParmsMetadata, DynAjaxEventContext.outParmsMetadataHash); + } eventCount++; } } @@ -527,7 +530,7 @@ private void SetScalarOrCollectionValue(string fieldName, object value, JArray v FieldInfo fieldInfo = getfieldInfo(targetObj, fieldName); if (fieldInfo != null) { - if (typeof(IGxCollection).IsAssignableFrom(fieldInfo.FieldType)) + if (typeof(IGxCollection).IsAssignableFrom(fieldInfo.FieldType)) SetCollectionFieldValue(fieldInfo, values); else SetFieldValue(fieldInfo, value); @@ -674,7 +677,21 @@ private Array GetArrayFieldValue(FieldInfo fieldInfo, object value) return null; } - + private void initializeOutParms() { + DynAjaxEventContext.Clear(); + foreach (JObject parm in DynAjaxEventContext.outParmsMetadata) { + string parmName = (string)parm["av"]; + if (!String.IsNullOrEmpty(parmName)) + { + object TypedValue = getFieldValue(targetObj, parmName); + DynAjaxEventContext.SetParmHash(parmName, TypedValue); + if (!DynAjaxEventContext.isInputParm(parmName) && TypedValue is IGXAssigned param) + { + param.IsAssigned = false; + } + } + } + } private object[] BeforeInvoke() { List MethodParms = new List(); @@ -684,7 +701,7 @@ private object[] BeforeInvoke() bool multipart = targetObj.context.IsMultipartRequest; int hash_i = 0; int parm_i = 0; - foreach (JObject parm in inParmsMetadata) + foreach (JObject parm in DynAjaxEventContext.inParmsMetadata) { if (parm["postForm"] != null) @@ -756,7 +773,7 @@ private object[] BeforeInvoke() { SetScalarOrCollectionValue((string)parm["av"], columnValues[rowIdx - 1], columnValues); object TypedValue = getFieldValue(targetObj, (string)parm["av"]); - CheckParmIntegrity(TypedValue, (string)columnHash, sRow, inParmsMetadata[parm_i], hash_i, Picture); + CheckParmIntegrity(TypedValue, (string)columnHash, sRow, DynAjaxEventContext.inParmsMetadata[parm_i], hash_i, Picture); } rowIdx++; } @@ -809,7 +826,7 @@ private object[] BeforeInvoke() string hash = hashObj.Contains("hsh") ? (string)hashObj["hsh"] : string.Empty; SetScalarOrCollectionValue((string)parm["av"], inParmsValues[parm_i], columnValues); object TypedValue = getFieldValue(targetObj, (string)parm["av"]); - CheckParmIntegrity(TypedValue, hash, sRow, inParmsMetadata[parm_i], hash_i, Picture); + CheckParmIntegrity(TypedValue, hash, sRow, DynAjaxEventContext.inParmsMetadata[parm_i], hash_i, Picture); } catch (Exception ex) { @@ -851,6 +868,7 @@ private object[] BeforeInvoke() } SetFieldValue("wbLoad", true); } + initializeOutParms(); return MethodParms.ToArray(); } @@ -963,7 +981,7 @@ public virtual void webAjaxEvent() } setAjaxCallMode(); context.setFullAjaxMode(); - DynAjaxEvent dynAjaxEvent = new DynAjaxEvent(); + DynAjaxEvent dynAjaxEvent = new DynAjaxEvent( context.httpAjaxContext.DynAjaxEventContext); string jsonRequest; if (context.IsMultipartRequest) jsonRequest = cgiGet(GX_AJAX_MULTIPART_ID);