internal override AttributeBinding Evaluate(AjaxPage page) { // Evaluate the binding path var result = page.EvaluatePath(Path); // Invalid result if (result.Property == null) { result.IsValid = false; } // # syntax else if (Extension == "#") { string format = null; Parameters.TryGetValue("format", out format); string value; result.IsValid = Adapter.TryGetDisplayValue(result.Property, format, result.Value, out value); result.Value = value; } // @ syntax else if (Extension == "@") { if (string.IsNullOrEmpty(Path)) { if (!(page.Context.DataItem is Adapter)) { throw new ApplicationException("No path was specified for the \"@\" markup extension, and the source is not an adapter."); } result.Value = page.Context.DataItem; } else { ExoWeb.OnBeforeCreateAdapter(this, result.Source, result.Property); result = new BindingResult() { Value = new Adapter(result, Parameters), IsValid = result.IsValid, Property = result.Property, Source = result.Source }; } } return(new AttributeBinding(Attribute, result)); }
internal override void Render(AjaxPage page, string[] templateNames, TextWriter writer) { try { ExoWeb.OnBeginRender(page, this); foreach (var block in Blocks) { block.Render(page, templateNames.Concat(ContentTemplateNames).ToArray(), writer); } } finally { ExoWeb.OnEndRender(page, this); } }
void TestRequest(Action <Request> action, string template, string expected) { ExoWeb.Model(new { request = ExoWeb.Query <Request>((string)null) }, action); string output = Accessors.Render(template); template = template.Replace("\"", "'"); output = Regex.Replace(Regex.Replace(output.Replace("\"", "'"), "exo[0-9]+", "__id__"), "item:s\\d*\\-\\-", "item:s#--"); output.Should().Match( // template markup converted into an element embedded to be used as a Sys.UI.Template Regex.Replace(template, "sys\\-template(\"|')[^\\>]*\\>", "sys-template$1 id='__id__'>") + expected + linkScript ); }
protected virtual string Render(Action <Request> action, string templateMarkup, string pageMarkup) { ExoWeb.Model(new { request = ExoWeb.Query <Request>(null) }, (Request request) => { request.Description = "Something doesn't work"; request.User = new User() { UserName = "******", IsActive = true }; request.User.Requests.Add(request); if (action != null) { action(request); } }); if (!string.IsNullOrEmpty(templateMarkup)) { Accessors.AddTemplates(templateMarkup); } var outputMarkup = Accessors.Render(pageMarkup); // Wrap the xml in a container to provide namespace aliases for supported controls var xml = @"<output xmlns:sys='javascript:Sys' xmlns:dataview='javascript:Sys.UI.DataView' xmlns:content='javascript:ExoWeb.UI.Content' xmlns:template='javascript:ExoWeb.UI.Template' xmlns:toggle='javascript:ExoWeb.UI.Toggle' xmlns:behavior='javascript:ExoWeb.UI.Behavior' xmlns:html='javascript:ExoWeb.UI.Html' xmlns:togglegroup='javascript:ExoWeb.UI.ToggleGroup'>" + outputMarkup + "</output>"; return(PrettyPrintXml(xml)); }
static JsonUtility() { serializer = new JsonSerializer { DateParseHandling = DateParseHandling.None, Converters = { new UtcDateTimeConverter() } }; serializableTypes = new HashSet <Type>(); // Register converters for types implementing IJsonSerializable or that have DataContract attributes // Include all types in ExoWeb and ExoRule automatically foreach (var converter in JsonConverter.Infer( typeof(ServiceHandler).Assembly.GetTypes().Union( typeof(Rule).Assembly.GetTypes().Where(type => typeof(Rule).IsAssignableFrom(type))))) { RegisterConverter(converter); } // Deserialize Value Change Event Func <JsonReader, ModelValueChangeEvent> deserializeValueChangeEvent = (reader) => { string p; ModelInstance instance = null; ModelValueProperty property = null; object oldValue = null; object newValue = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; case "property": var propertyName = reader.ReadValue <string>(); property = (ModelValueProperty)instance.Type.Properties[propertyName]; if (property == null) { throw new SerializationException("Unable to deserialize ValueChange: property \"" + propertyName + "\" does not exist for type \"" + instance.Type.Name + "\"."); } break; case "oldValue": if (reader.TokenType == JsonToken.StartObject) { oldValue = reader.ReadValue(property.PropertyType); } else { oldValue = property.CoerceValue(reader.Value); reader.Read(); } break; case "newValue": if (reader.TokenType == JsonToken.StartObject) { newValue = reader.ReadValue(property.PropertyType); } else { newValue = property.CoerceValue(reader.Value); reader.Read(); } break; default: throw new ArgumentException("The specified property could not be deserialized.", p); } } return(new ModelValueChangeEvent(instance, property, oldValue, newValue)); }; // Deserialize Reference Change Event Func <JsonReader, ModelReferenceChangeEvent> deserializeReferenceChangeEvent = (reader) => { string p; ModelInstance instance = null; ModelReferenceProperty property = null; ModelInstance oldValue = null; ModelInstance newValue = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; case "property": var propertyName = reader.ReadValue <string>(); property = (ModelReferenceProperty)instance.Type.Properties[propertyName]; if (property == null) { throw new SerializationException("Unable to deserialize ReferenceChange: property \"" + propertyName + "\" does not exist for type \"" + instance.Type.Name + "\"."); } break; case "oldValue": oldValue = reader.ReadValue <ModelInstance>(); break; case "newValue": newValue = reader.ReadValue <ModelInstance>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelReferenceChangeEvent(instance, property, oldValue, newValue)); }; // Deserialize List Change Event Func <JsonReader, ModelListChangeEvent> deserializeListChangeEvent = (reader) => { string p; ModelInstance instance = null; ModelReferenceProperty property = null; ModelInstance[] added = null; ModelInstance[] removed = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; case "property": var propertyName = reader.ReadValue <string>(); property = (ModelReferenceProperty)instance.Type.Properties[propertyName]; if (property == null) { throw new SerializationException("Unable to deserialize ListChange: property \"" + propertyName + "\" does not exist for type \"" + instance.Type.Name + "\"."); } break; case "added": added = reader.ReadValue <ModelInstance[]>(); break; case "removed": removed = reader.ReadValue <ModelInstance[]>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelListChangeEvent(instance, property, added, removed)); }; // Deserialize Init New Event Func <JsonReader, ModelInitEvent.InitNew> deserializeInitNewEvent = (reader) => { string p; ModelInstance instance = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelInitEvent.InitNew(instance)); }; // Deserialize Init Existing Event Func <JsonReader, ModelInitEvent.InitExisting> deserializeInitExistingEvent = (reader) => { string p; ModelInstance instance = null; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelInitEvent.InitExisting(instance)); }; // Deserialize Delete Event Func <JsonReader, ModelDeleteEvent> deserializeDeleteEvent = (reader) => { string p; ModelInstance instance = null; bool isPendingDelete = false; while (reader.ReadProperty(out p)) { switch (p) { case "instance": instance = reader.ReadValue <ModelInstance>(); break; case "isPendingDelete": isPendingDelete = reader.ReadValue <bool>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return(new ModelDeleteEvent(instance, isPendingDelete)); }; // Construct Model Instance var createModelInstance = typeof(ModelInstance).GetConstructor( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(ModelType), typeof(string) }, null); // Register custom converters for ModelType, ModelProperty, ModelMethod, ModelInstance, ModelEvent foreach (var converter in new JsonConverter[] { // Model Type new JsonConverter <ModelType>( (modelType, json) => { // Base Type if (modelType.BaseType != null) { json.Set("baseType", JsonConverter.GetJsonReferenceType(modelType.BaseType)); } // Base Type if (!String.IsNullOrEmpty(modelType.Format)) { json.Set("format", modelType.Format); } // Properties if (modelType.Properties.Any()) { json.Set("properties", modelType.Properties .Where(property => property.DeclaringType == modelType && ExoWeb.IncludeInClientModel(property)) .ToDictionary(property => property.Name)); } // Methods if (modelType.Methods.Any()) { json.Set("methods", modelType.Methods.ToDictionary(method => method.Name)); } // Rules var rules = Rule.GetRegisteredRules(modelType).ToList(); var typeRules = rules.Where(rule => (rule.ExecutionLocation & RuleExecutionLocation.Client) > 0 && !(rule is IPropertyRule)).ToArray(); if (typeRules.Any()) { json.Set("rules", typeRules); } // Condition Types var serverConditionTypes = rules .Where(rule => (rule.ExecutionLocation & RuleExecutionLocation.Client) == 0) .SelectMany(rule => rule.ConditionTypes) .ToArray(); if (serverConditionTypes.Any()) { json.Set("conditionTypes", serverConditionTypes); } // Exports var exports = json.Global <Dictionary <string, string> >("exports"); if (exports.Any()) { json.Set("exports", json.Global <Dictionary <string, string> >("exports")); } }, json => { throw new NotSupportedException("ModelType cannot be deserialized."); }), // Model Property new JsonConverter <ModelProperty>( (property, json) => { // Type string type = (property is ModelValueProperty ? JsonConverter.GetJsonValueType(((ModelValueProperty)property).PropertyType) ?? "Object" : JsonConverter.GetJsonReferenceType(((ModelReferenceProperty)property).PropertyType)) + (property.IsList ? "[]" : ""); json.Set("type", type); // IsStatic if (property.IsStatic) { json.Set("isStatic", true); } // IsPersisted if (!property.IsPersisted && !property.IsStatic) { json.Set("isPersisted", false); } // IsCalculated if (property.IsCalculated) { json.Set("isCalculated", true); } // Index int index = 0; foreach (ModelProperty p in property.DeclaringType.Properties) { if (p == property) { break; } if (ExoWeb.IncludeInClientModel(p) && !p.IsStatic) { index++; } } if (!property.IsStatic) { json.Set("index", index); } // Format string format = property.Format; if (!string.IsNullOrEmpty(format)) { json.Set("format", format); } // Label string label = property.Label; if (!string.IsNullOrEmpty(label)) { json.Set("label", label); } // Help Text string helptext = property.HelpText; if (!string.IsNullOrEmpty(helptext)) { json.Set("helptext", helptext); } // Rules var rules = Rule .GetRegisteredRules(property.DeclaringType) .Where(rule => (rule.ExecutionLocation & RuleExecutionLocation.Client) > 0 && rule is IPropertyRule && rule.RootType.Properties[((IPropertyRule)rule).Property] == property) .ToDictionary(rule => rule is ICalculationRule ? "calculated" : String.Format("{0}.{1}.{2}", rule.RootType.Name, ((IPropertyRule)rule).Property, ((IPropertyRule)rule).Name) == rule.Name ? ((IPropertyRule)rule).Name.Substring(0, 1).ToLower() + ((IPropertyRule)rule).Name.Substring(1) : rule.Name); if (rules.Any()) { json.Set("rules", rules); } // Default Value if (property is ModelValueProperty) { var defaultValue = ((ModelValueProperty)property).DefaultValue; if (defaultValue != null) { json.Set("defaultValue", ((ModelValueProperty)property).DefaultValue); } } }, json => { throw new NotSupportedException("ModelProperty cannot be deserialized."); }), // Model Method new JsonConverter <ModelMethod>( (method, json) => { // Parameters json.Set("parameters", method.Parameters.Select(p => p.Name)); // IsStatic json.Set("isStatic", method.IsStatic); }, json => { throw new NotSupportedException("ModelMethod cannot be deserialized."); }), // Model Instance new JsonConverter <ModelInstance>( (instance, json) => { json.Set("id", instance.Id); json.Set("type", instance.Type.Name); }, reader => { string p; ModelType type = null; string id = null; while (reader.ReadProperty(out p)) { switch (p) { case "type": type = ModelContext.Current.GetModelType(reader.ReadValue <string>()); break; case "id": id = reader.ReadValue <string>(); break; // Ignore case "isNew": reader.ReadValue <bool>(); break; default: throw new ArgumentException(@"The specified property could not be deserialized.", p); } } return((ModelInstance)createModelInstance.Invoke(new object[] { type, id })); }), // Model Event new JsonConverter <ModelEvent>( (modelEvent, json) => { throw new NotSupportedException("ModelEvent cannot be deserialized."); }, (reader) => { string p; if (reader.ReadProperty(out p) && p == "type") { string eventName = reader.ReadValue <string>(); switch (eventName) { case "ValueChange": return(deserializeValueChangeEvent(reader)); case "ReferenceChange": return(deserializeReferenceChangeEvent(reader)); case "ListChange": return(deserializeListChangeEvent(reader)); case "InitNew": return(deserializeInitNewEvent(reader)); case "InitExisting": return(deserializeInitExistingEvent(reader)); case "Delete": return(deserializeDeleteEvent(reader)); } throw new NotSupportedException(eventName + " event cannot be deserialized."); } else { throw new FormatException("The type parameter 'type' must be the first serialized value in model event json."); } }), // Model Value Change Event new JsonConverter <ModelValueChangeEvent>( (modelEvent, json) => { //Property sequence matters json.Set("type", "ValueChange"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("property", modelEvent.Property.Name); json.Set("oldValue", modelEvent.OldValue); json.Set("newValue", modelEvent.NewValue); }, deserializeValueChangeEvent), // Model Reference Change Event new JsonConverter <ModelReferenceChangeEvent>( (modelEvent, json) => { json.Set("type", "ReferenceChange"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("property", modelEvent.Property.Name); json.Set("oldValue", GetEventInstance(modelEvent.OldValue, modelEvent.OldValueId)); json.Set("newValue", GetEventInstance(modelEvent.NewValue, modelEvent.NewValueId)); }, deserializeReferenceChangeEvent), // Model List Change Event new JsonConverter <ModelListChangeEvent>( (modelEvent, json) => { json.Set("type", "ListChange"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("property", modelEvent.Property.Name); json.Set("added", modelEvent.Added.Select((instance, index) => GetEventInstance(instance, modelEvent.AddedIds.ElementAt(index)))); json.Set("removed", modelEvent.Removed.Select((instance, index) => GetEventInstance(instance, modelEvent.RemovedIds.ElementAt(index)))); }, deserializeListChangeEvent), // Model Init New Event new JsonConverter <ModelInitEvent.InitNew>( (modelEvent, json) => { json.Set("type", "InitNew"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); }, deserializeInitNewEvent), // Model Init Existing Event new JsonConverter <ModelInitEvent.InitExisting>( (modelEvent, json) => { json.Set("type", "InitExisting"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); }, deserializeInitExistingEvent), // Model Delete Event new JsonConverter <ModelDeleteEvent>( (modelEvent, json) => { json.Set("type", "Delete"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("isPendingDelete", modelEvent.IsPendingDelete); }, deserializeDeleteEvent), // Model Save Event new JsonConverter <ModelSaveEvent>( (modelEvent, json) => { json.Set("type", "Save"); json.Set("instance", GetEventInstance(modelEvent.Instance, modelEvent.InstanceId)); json.Set("added", modelEvent.Added.Select(instance => new Dictionary <string, string>() { { "type", instance.Type.Name }, { "oldId", instance.OriginalId }, { "newId", instance.Id } })); json.Set("modified", modelEvent.Modified); json.Set("deleted", modelEvent.Deleted); }, json => { throw new NotSupportedException("ModelSaveEvent cannot be deserialized."); }), // Condition Type new JsonConverter <ConditionType>( (conditionType, json) => { json.Set("code", conditionType.Code); json.Set("category", conditionType.Category.ToString()); if (conditionType.Sets != null && conditionType.Sets.Any()) { json.Set("sets", conditionType.Sets.Select(set => set.Name)); } json.Set("message", conditionType.Message); }, json => { throw new NotSupportedException("ConditionType cannot be deserialized."); }), // Condition new JsonConverter <Condition>( (condition, json) => { if (condition.Message != condition.Type.Message) { json.Set("message", condition.Message); } json.Set("targets", condition.Targets.Where(ct => ct.Target != null)); }, json => { throw new NotSupportedException("Condition cannot be deserialized."); }), // Condition Target new JsonConverter <ConditionTarget>( (conditionTarget, json) => { json.Set("instance", conditionTarget.Target); json.Set("properties", conditionTarget.Properties); }, json => { throw new NotSupportedException("ConditionTarget cannot be deserialized."); }), // Rule new JsonConverter <Rule>( (rule, json) => { if (rule is ICalculationRule) { var calculation = (ICalculationRule)rule; json.Set("onChangeOf", calculation.Predicates); if (rule is IClientCalculationRule) { var clientCalc = (IClientCalculationRule)rule; json.Set("calculate", clientCalc.FunctionBody); foreach (var export in clientCalc.Exports) { json.Global <Dictionary <string, string> >("exports")[export.Key] = export.Value; } } else { json.Set("calculate", calculation.Calculation); } } else if (rule is IConditionRule) { var condition = (IConditionRule)rule; json.Set("type", "condition"); json.Set("properties", condition.Properties); json.Set("onChangeOf", condition.Predicates); json.Set("conditionType", condition.ConditionType); json.Set("assert", condition.Condition); } else { throw new NotSupportedException("Rules of type " + rule.GetType().FullName + " cannot be serialized. Call ExoWeb.RegisterConverters() to register a converter to support serializing rules of this type."); } }, json => { throw new NotSupportedException("Rule cannot be deserialized."); }), // AllowedValuesRule new JsonConverter <AllowedValuesRule>( (rule, json) => { SerializePropertyRule(rule, json); var sourceExpression = rule.SourceExpression; if (sourceExpression != null) { json.Set("fn", sourceExpression.Expression); if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } } else { json.Set("source", rule.Source); } if (rule.IgnoreValidation) { json.Set("ignoreValidation", rule.IgnoreValidation); } }, json => { throw new NotSupportedException("AllowedValuesRule cannot be deserialized."); }), // CompareRule new JsonConverter <CompareRule>( (rule, json) => { SerializePropertyRule(rule, json); json.Set("compareOperator", rule.CompareOperator.ToString()); json.Set("compareSource", rule.CompareSource); }, json => { throw new NotSupportedException("CompareRule cannot be deserialized."); }), // ListLengthRule new JsonConverter <ListLengthRule>( (rule, json) => { SerializePropertyRule(rule, json); // Min if (rule.MinExpression != null) { json.Set("minFn", rule.MinExpression.Expression); } else { json.Set("min", rule.Minimum); } // Max if (rule.MaxExpression != null) { json.Set("maxFn", rule.MaxExpression.Expression); } else { json.Set("max", rule.Maximum); } // OnChangeOf if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } }, json => { throw new NotSupportedException("ListLengthRule cannot be deserialized."); }), // RangeRule new JsonConverter <RangeRule>( (rule, json) => { SerializePropertyRule(rule, json); // Min if (rule.MinExpression != null) { json.Set("minFn", rule.MinExpression.Expression); } else { json.Set("min", rule.Minimum); } // Max if (rule.MaxExpression != null) { json.Set("maxFn", rule.MaxExpression.Expression); } else { json.Set("max", rule.Maximum); } // OnChangeOf if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } }, json => { throw new NotSupportedException("RangeRule cannot be deserialized."); }), // ValidationRule new JsonConverter <ValidationRule>( (rule, json) => { SerializePropertyRule(rule, json); // Validation json.Set("isError", rule.ValidationExpression.Expression); // ErrorMessage if (rule.ErrorMessageResource != null) { json.Set("message", rule.ErrorMessageResource); } else { json.Set("message", rule.ErrorMessageExpression.Expression); } json.Set("properties", rule.AdditionalTargets); // OnChangeOf if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } }, json => { throw new NotSupportedException("ValidationRule cannot be deserialized."); }), // RequiredRule new JsonConverter <RequiredRule>( (rule, json) => { SerializePropertyRule(rule, json); if (rule.RequiredValue != null) { json.Set("requiredValue", rule.RequiredValue); } }, json => { throw new NotSupportedException("RequiredRule cannot be deserialized."); }), // RequiredIfRule new JsonConverter <RequiredIfRule>( (rule, json) => { SerializePropertyRule(rule, json); if (rule.RequiredExpression != null) { json.Set("fn", rule.RequiredExpression.Expression); // OnChangeOf if (!String.IsNullOrEmpty(rule.Path)) { json.Set("onChangeOf", new string[] { rule.Path }); } } else { json.Set("compareOperator", rule.CompareOperator.ToString()); json.Set("compareSource", rule.CompareSource); json.Set("compareValue", rule.CompareValue); } if (rule.RequiredValue != null) { json.Set("requiredValue", rule.RequiredValue); } }, json => { throw new NotSupportedException("RequiredIfRule cannot be deserialized."); }), // OwnerRule new JsonConverter <OwnerRule>( (rule, json) => SerializePropertyRule(rule, json), json => { throw new NotSupportedException("OwnerRule cannot be deserialized."); }), // StringLengthRule new JsonConverter <StringLengthRule>( (rule, json) => { SerializePropertyRule(rule, json); if (rule.Minimum > 0) { json.Set("min", rule.Minimum); } if (rule.Maximum > 0) { json.Set("max", rule.Maximum); } }, json => { throw new NotSupportedException("StringLengthRule cannot be deserialized."); }), // StringFormatRule new JsonConverter <StringFormatRule>( (rule, json) => { SerializePropertyRule(rule, json); if (!String.IsNullOrEmpty(rule.FormatDescription)) { json.Set("description", rule.FormatDescription); } json.Set("expression", rule.FormatExpression.ToString()); if (!String.IsNullOrEmpty(rule.ReformatExpression)) { json.Set("reformat", rule.ReformatExpression); } }, json => { throw new NotSupportedException("StringFormatRule cannot be deserialized."); }), }) { JsonUtility.RegisterConverter(converter); } // Cache the method info of the deserialize method // The non-generic version of this method was added in .NET 4.0 deserialize = serializer.GetType().GetMethod("Deserialize", new Type[] { typeof(string) }); }