/// <summary> /// Resolves the command for the specified post data. /// </summary> public void ResolveCommand(DotvvmRequestContext context, DotvvmView view, string serializedPostData, out ActionInfo actionInfo) { // get properties var data = JObject.Parse(serializedPostData); var path = data["currentPath"].Values <string>().ToArray(); var command = data["command"].Value <string>(); var controlUniqueId = data["controlUniqueId"].Value <string>(); if (string.IsNullOrEmpty(command)) { // empty command actionInfo = null; } else { // find the command target if (!string.IsNullOrEmpty(controlUniqueId)) { var target = view.FindControl(controlUniqueId); if (target == null) { throw new Exception(string.Format("The control with ID '{0}' was not found!", controlUniqueId)); } actionInfo = commandResolver.GetFunction(target, view, context, path, command); } else { actionInfo = commandResolver.GetFunction(view, context, path, command); } } }
/// <summary> /// Resolves the command for the specified post data. /// </summary> public ActionInfo ResolveCommand(IDotvvmRequestContext context, DotvvmView view) { // get properties var data = context.ReceivedViewModelJson; var path = data["currentPath"].Values <string>().ToArray(); var command = data["command"].Value <string>(); var controlUniqueId = data["controlUniqueId"]?.Value <string>(); var args = data["commandArgs"]?.ToObject <object[]>() ?? new object[0]; // empty command if (string.IsNullOrEmpty(command)) { return(null); } // find the command target if (!string.IsNullOrEmpty(controlUniqueId)) { var target = view.FindControlByUniqueId(controlUniqueId); if (target == null) { throw new Exception(string.Format("The control with ID '{0}' was not found!", controlUniqueId)); } // TODO(exyi) parameters from return(commandResolver.GetFunction(target, view, context, path, command, args)); } else { return(commandResolver.GetFunction(view, context, path, command, args)); } }
public void RenderPostbackUpdatedControls(DotvvmRequestContext request, DotvvmView page) { var context = new RenderContext(request); var stack = new Stack <DotvvmControl>(); stack.Push(page); do { var control = stack.Pop(); object val; if (control.properties != null && control.properties.TryGetValue(PostBack.UpdateProperty, out val) && val is bool && (bool)val) { using (var w = new StringWriter()) { control.EnsureControlHasId(); control.Render(new HtmlWriter(w, request), context); request.PostBackUpdatedControls[control.ID] = w.ToString(); } } else { foreach (var child in control.GetChildren()) { stack.Push(child); } } } while (stack.Count > 0); }
public void RenderPostbackUpdatedControls(IDotvvmRequestContext context, DotvvmView page) { var stack = new Stack <DotvvmControl>(); stack.Push(page); do { var control = stack.Pop(); object val; if (control.properties != null && control.properties.TryGetValue(PostBack.UpdateProperty, out val) && val is bool && (bool)val) { using (var w = new StringWriter()) { control.Render(new HtmlWriter(w, context), context); var clientId = control.GetDotvvmUniqueId() as string; if (clientId == null) { throw new DotvvmControlException(control, "This control cannot use PostBack.Update=\"true\" because it has dynamic ID. This happens when the control is inside a Repeater or other data-bound control and the RenderSettings.Mode=\"Client\"."); } context.PostBackUpdatedControls[clientId] = w.ToString(); } } else { foreach (var child in control.Children) { stack.Push(child); } } } while (stack.Count > 0); }
/// <summary> /// Checks that the content page does not contain invalid content. /// </summary> private List <Content> GetChildPageContents(DotvvmView childPage, List <ContentPlaceHolder> parentPlaceHolders) { // make sure that the body contains only whitespace and Content controls var nonContentElements = childPage.Children.Where(c => !((c is RawLiteral && ((RawLiteral)c).IsWhitespace) || (c is Content))); if (nonContentElements.Any()) { // show all error lines var innerExceptions = nonContentElements.Select(s => new Exception($"Error occurred near line: {(s.GetValue(Internal.MarkupLineNumberProperty)?.ToString() ?? "")}.")).ToList(); // the message cannot be specifically to the line, because MarkupLineNumber shows the last character position which is a line under the error in some cases. var corruptedFile = childPage.GetValue(Internal.MarkupFileNameProperty)?.ToString(); throw new AggregateException("If the page contains @masterpage directive, it can only contain white space and <dot:Content /> controls! \r\n" + (string.IsNullOrWhiteSpace(corruptedFile) ? "" : $"Corrupted file name: {corruptedFile}"), innerExceptions); } // make sure that the Content controls are not nested in other elements var contents = childPage.GetAllDescendants().OfType <Content>() .Where(c => !(bool)c.GetValue(Internal.IsMasterPageCompositionFinishedProperty)) .ToList(); if (contents.Any(c => c.Parent != childPage)) { throw new Exception("The control <dot:Content /> cannot be placed inside any control!"); // TODO: exception handling } return(contents); }
/// <summary> /// Resolves the command for the specified post data. /// </summary> public ActionInfo ResolveCommand(IDotvvmRequestContext context, DotvvmView view) { // get properties var data = context.ReceivedViewModelJson ?? throw new NotSupportedException("Could not find ReceivedViewModelJson in request context."); var path = data["currentPath"].Values <string>().ToArray(); var command = data["command"].Value <string>(); var controlUniqueId = data["controlUniqueId"]?.Value <string>(); var args = data["commandArgs"] is JArray argsJson? argsJson.Select(a => (Func <Type, object>)(t => a.ToObject(t))).ToArray() : new Func <Type, object> [0]; // empty command if (string.IsNullOrEmpty(command)) { return(null); } // find the command target if (!string.IsNullOrEmpty(controlUniqueId)) { var target = view.FindControlByUniqueId(controlUniqueId); if (target == null) { throw new Exception(string.Format("The control with ID '{0}' was not found!", controlUniqueId)); } return(commandResolver.GetFunction(target, view, context, path, command, args)); } else { return(commandResolver.GetFunction(view, context, path, command, args)); } }
public async Task WriteHtmlResponse(DotvvmRequestContext context, DotvvmView view) { // return the response context.OwinContext.Response.ContentType = "text/html; charset=utf-8"; context.OwinContext.Response.Headers["Cache-Control"] = "no-cache"; var html = RenderPage(context, view); await context.OwinContext.Response.WriteAsync(html); }
public async Task WriteViewModelResponse(DotvvmRequestContext context, DotvvmView view) { // return the response context.OwinContext.Response.ContentType = "application/json; charset=utf-8"; context.OwinContext.Response.Headers["Cache-Control"] = "no-cache"; var serializedViewModel = context.GetSerializedViewModel(); await context.OwinContext.Response.WriteAsync(serializedViewModel); }
public async Task WriteHtmlResponse(IDotvvmRequestContext context, DotvvmView view) { // return the response context.HttpContext.Response.ContentType = "text/html; charset=utf-8"; SetCacheHeaders(context.HttpContext); var html = RenderPage(context, view); await context.HttpContext.Response.WriteAsync(html); }
public async Task WriteViewModelResponse(IDotvvmRequestContext context, DotvvmView view) { // return the response context.HttpContext.Response.ContentType = "application/json; charset=utf-8"; SetCacheHeaders(context.HttpContext); var serializedViewModel = context.GetSerializedViewModel(); await context.HttpContext.Response.WriteAsync(serializedViewModel); }
public override Task WriteViewModelResponse(IDotvvmRequestContext context, DotvvmView view) { if (context.Configuration.Debug && context.Services.GetService <DiagnosticsRequestTracer>() is DiagnosticsRequestTracer tracer) { var viewModelJson = context.GetSerializedViewModel(); var vmBytes = Encoding.UTF8.GetBytes(viewModelJson); tracer.LogResponseSize(GetCompressedSize(vmBytes), vmBytes.LongLength); } return(base.WriteViewModelResponse(context, view)); }
protected override string RenderPage(IDotvvmRequestContext context, DotvvmView view) { var html = base.RenderPage(context, view); if (context.Configuration.Debug && context.Services.GetService <DiagnosticsRequestTracer>() is DiagnosticsRequestTracer tracer) { tracer.LogResponseSize(GetCompressedSize(html), Encoding.UTF8.GetByteCount(html)); } return(html); }
protected override MemoryStream RenderPage(IDotvvmRequestContext context, DotvvmView view) { var html = base.RenderPage(context, view); if (context.Configuration.Debug && context.Services.GetService <DiagnosticsRequestTracer>() is DiagnosticsRequestTracer tracer) { tracer.LogResponseSize(GetCompressedSize(html.ToArray()), html.Length); } return(html); }
/// <summary> /// Fills default directives if specific directives are not set /// </summary> private void FillsDefaultDirectives(DotvvmView page, DotvvmConfiguration configuration) { foreach (var key in configuration.Markup.DefaultDirectives.Keys) { if (!page.Directives.Keys.Contains(key)) { page.Directives[key] = configuration.Markup.DefaultDirectives[key]; } } }
protected virtual string RenderPage(IDotvvmRequestContext context, DotvvmView view) { // prepare the render context // get the HTML using (var textWriter = new StringWriter()) { var htmlWriter = new HtmlWriter(textWriter, context); view.Render(htmlWriter, context); return(textWriter.ToString()); } }
protected virtual MemoryStream RenderPage(IDotvvmRequestContext context, DotvvmView view) { var outStream = new MemoryStream(); using (var textWriter = new StreamWriter(outStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), 4096, leaveOpen: true)) { var htmlWriter = new HtmlWriter(textWriter, context); view.Render(htmlWriter, context); } outStream.Position = 0; return(outStream); }
public virtual async Task WriteHtmlResponse(IDotvvmRequestContext context, DotvvmView view) { // return the response context.HttpContext.Response.ContentType = "text/html; charset=utf-8"; SetCacheHeaders(context.HttpContext); using (var html = RenderPage(context, view)) { context.HttpContext.Response.Headers["Content-Length"] = html.Length.ToString(); CheckRenderedResources(context); await html.CopyToAsync(context.HttpContext.Response.Body); } }
protected virtual MemoryStream RenderPage(IDotvvmRequestContext context, DotvvmView view) { var outStream = new MemoryStream(); using (var textWriter = new StreamWriter(outStream, Encoding.UTF8, 4096, leaveOpen: true)) { var htmlWriter = new HtmlWriter(textWriter, context); view.Render(htmlWriter, context); } outStream.Position = 0; return(outStream); }
protected static string InvokeLifecycleAndRender(DotvvmView view, TestDotvvmRequestContext context) { view.DataContext = context.ViewModel; view.SetValue(Internal.RequestContextProperty, context); DotvvmControlCollection.InvokePageLifeCycleEventRecursive(view, LifeCycleEventType.PreRenderComplete); using (var text = new StringWriter()) { var html = new HtmlWriter(text, context); view.Render(html, context); return(text.ToString()); } }
protected string RenderPage(IDotvvmRequestContext context, DotvvmView view) { // embed resource links EmbedResourceLinks(view); // prepare the render context // get the HTML using (var textWriter = new StringWriter()) { var htmlWriter = new HtmlWriter(textWriter, context); view.Render(htmlWriter, context); return(textWriter.ToString()); } }
public void CommandResolver_Valid_SimpleTest() { var path = new[] { ValueBindingExpression.CreateBinding <object>(bindingService, vm => ((Test1)vm[0]).A[0], (DataContextStack)null) }; var commandId = "someCommand"; var command = new CommandBindingExpression(bindingService, vm => { ((TestA)vm[0]).Test(((TestA)vm[0]).StringToPass, ((dynamic)vm[1]).NumberToPass); }, commandId); var testObject = new Test1 { A = new[] { new TestA() { StringToPass = "******" } }, NumberToPass = 16 }; var viewRoot = new DotvvmView() { DataContext = testObject }; viewRoot.SetBinding(Controls.Validation.TargetProperty, ValueBindingExpression.CreateBinding(bindingService, vm => vm.Last(), new ParametrizedCode("$root"))); var placeholder = new HtmlGenericControl("div"); placeholder.SetBinding(DotvvmBindableObject.DataContextProperty, path[0]); viewRoot.Children.Add(placeholder); var button = new Button(); button.SetBinding(ButtonBase.ClickProperty, command); placeholder.Children.Add(button); var resolver = new CommandResolver(); var context = new TestDotvvmRequestContext() { ViewModel = testObject, ModelState = new ModelState() }; context.ModelState.ValidationTargetPath = KnockoutHelper.GetValidationTargetExpression(button); resolver.GetFunction(viewRoot, context, path.Select(v => v.GetProperty <SimplePathExpressionBindingProperty>().Code.FormatKnockoutScript(button, v)).ToArray(), commandId, new Func <Type, object> [0]).Action(); Assert.AreEqual(testObject.NumberToPass, testObject.A[0].ResultInt); Assert.AreEqual(testObject.A[0].ResultString, testObject.A[0].ResultString); }
/// <summary> /// If the request is SPA request, we need to verify that the page contains the same SpaContentPlaceHolder. /// Also we need to check that the placeholder is the same. /// </summary> protected void VerifySpaRequest(IDotvvmRequestContext context, DotvvmView page) { if (context.IsSpaRequest) { var spaContentPlaceHolders = page.GetAllDescendants().OfType <SpaContentPlaceHolder>().ToList(); if (spaContentPlaceHolders.Count > 1) { throw new Exception("Multiple controls of type <dot:SpaContentPlaceHolder /> found on the page! This control can be used only once!"); // TODO: exception handling } if (spaContentPlaceHolders.Count == 0 || spaContentPlaceHolders[0].GetSpaContentPlaceHolderUniqueId() != context.GetSpaContentPlaceHolderUniqueId()) { // the client has loaded different page which does not contain current SpaContentPlaceHolder - he needs to be redirected context.RedirectToUrl(context.HttpContext.Request.Url.AbsoluteUri.Replace("/" + HostingConstants.SpaUrlIdentifier, "")); } } }
/// <summary> /// Embeds the resource links in the page. /// </summary> private void EmbedResourceLinks(DotvvmView view) { var sections = view.GetThisAndAllDescendants() .OfType <HtmlGenericControl>() .Where(t => t.TagName == "head" || t.TagName == "body") .OrderBy(t => t.TagName) .ToList(); if (sections.Count != 2 || sections[0].TagName == sections[1].TagName) { throw new Exception("The page must have exactly one <head> and one <body> section!"); } sections[0].Children.Add(new BodyResourceLinks()); sections[1].Children.Add(new HeadResourceLinks()); }
public void CommandResolver_Valid_SimpleTest() { var path = new[] { new ValueBindingExpression(vm => ((dynamic)vm[0]).A[0], "A()[0]") }; var commandId = "someCommand"; var command = new CommandBindingExpression(vm => ((TestA)vm[0]).Test(((TestA)vm[0]).StringToPass, ((dynamic)vm[1]).NumberToPass), commandId); var testObject = new { A = new[] { new TestA() { StringToPass = "******" } }, NumberToPass = 16 }; var viewRoot = new DotvvmView() { DataContext = testObject }; viewRoot.SetBinding(Validate.TargetProperty, new ValueBindingExpression(vm => vm.Last(), "$root")); var placeholder = new HtmlGenericControl("div"); placeholder.SetBinding(DotvvmBindableObject.DataContextProperty, path[0]); viewRoot.Children.Add(placeholder); var button = new Button(); button.SetBinding(ButtonBase.ClickProperty, command); placeholder.Children.Add(button); var resolver = new CommandResolver(); var context = new DotvvmRequestContext() { ViewModel = testObject }; context.ModelState.ValidationTargetPath = KnockoutHelper.GetValidationTargetExpression(button); resolver.GetFunction(viewRoot, context, path.Select(v => v.Javascript).ToArray(), commandId).Action(); Assert.AreEqual(testObject.NumberToPass, testObject.A[0].ResultInt); Assert.AreEqual(testObject.A[0].ResultString, testObject.A[0].ResultString); }
/// <summary> /// Performs the master page nesting. /// </summary> private void PerformMasterPageComposition(DotvvmView childPage, DotvvmView masterPage) { if (!masterPage.ViewModelType.IsAssignableFrom(childPage.ViewModelType)) { throw new DotvvmControlException(childPage, $"Master page requires viewModel of type '{masterPage.ViewModelType}' and it is not assignable from '{childPage.ViewModelType}'."); } // find content place holders var placeHolders = GetMasterPageContentPlaceHolders(masterPage); // find contents var contents = GetChildPageContents(childPage, placeHolders); // perform the composition foreach (var content in contents) { // find the corresponding placeholder var placeHolder = placeHolders.SingleOrDefault(p => p.ID == content.ContentPlaceHolderID); if (placeHolder == null) { throw new Exception(string.Format("The placeholder with ID '{0}' was not found in the master page!", content.ContentPlaceHolderID)); } // replace the contents content.Parent.Children.Remove(content); placeHolder.Children.Clear(); placeHolder.Children.Add(content); content.SetValue(Internal.IsMasterPageCompositionFinished, true); } // copy the directives from content page to the master page (except the @masterpage) masterPage.ViewModelType = childPage.ViewModelType; foreach (var directive in childPage.Directives) { if (string.Equals(directive.Key, Constants.MasterPageDirective, StringComparison.InvariantCultureIgnoreCase)) { continue; } masterPage.Directives[directive.Key] = directive.Value; } }
/// <summary> /// Checks that the content page does not contain invalid content. /// </summary> private List <Content> GetChildPageContents(DotvvmView childPage, List <ContentPlaceHolder> parentPlaceHolders) { // make sure that the body contains only whitespace and Content controls if (!childPage.Children.All(c => (c is RawLiteral && ((RawLiteral)c).IsWhitespace) || (c is Content))) { throw new Exception("If the page contains @masterpage directive, it can only contain white space and <dot:Content /> controls!"); // TODO: exception handling } // make sure that the Content controls are not nested in other elements var contents = childPage.GetAllDescendants().OfType <Content>() .Where(c => !(bool)c.GetValue(Internal.IsMasterPageCompositionFinishedProperty)) .ToList(); if (contents.Any(c => c.Parent != childPage)) { throw new Exception("The control <dot:Content /> cannot be placed inside any control!"); // TODO: exception handling } return(contents); }
/// <summary> /// Performs the master page nesting. /// </summary> private void PerformMasterPageComposition(DotvvmView childPage, DotvvmView masterPage) { if (!masterPage.ViewModelType.IsAssignableFrom(childPage.ViewModelType)) { throw new DotvvmControlException(childPage, $"Master page requires viewModel of type '{masterPage.ViewModelType}' and it is not assignable from '{childPage.ViewModelType}'."); } // find content place holders var placeHolders = GetMasterPageContentPlaceHolders(masterPage); // find contents var contents = GetChildPageContents(childPage, placeHolders); // perform the composition foreach (var content in contents) { // find the corresponding placeholder var placeHolder = placeHolders.SingleOrDefault(p => p.ID == content.ContentPlaceHolderID); if (placeHolder == null) { throw new DotvvmControlException(content, $"The placeholder with ID '{content.ContentPlaceHolderID}' was not found in the master page '{masterPage.GetValue(Internal.MarkupFileNameProperty)}'!"); } // replace the contents var contentPlaceHolder = new PlaceHolder(); contentPlaceHolder.SetDataContextType(content.Parent.GetDataContextType()); (content.Parent as DotvvmControl)?.Children.Remove(content); placeHolder.Children.Clear(); placeHolder.Children.Add(contentPlaceHolder); contentPlaceHolder.Children.Add(content); content.SetValue(Internal.IsMasterPageCompositionFinishedProperty, true); content.SetValue(DotvvmView.DirectivesProperty, childPage.Directives); content.SetValue(Internal.MarkupFileNameProperty, childPage.GetValue(Internal.MarkupFileNameProperty)); } // copy the directives from content page to the master page (except the @masterpage) masterPage.ViewModelType = childPage.ViewModelType; }
public virtual IEnumerable <(string name, string html)> RenderPostbackUpdatedControls(IDotvvmRequestContext context, DotvvmView page) { var stack = new Stack <DotvvmControl>(); stack.Push(page); do { var control = stack.Pop(); if (control.properties.TryGet(PostBack.UpdateProperty, out var val) && true.Equals(val)) { using (var w = new StringWriter()) { control.Render(new HtmlWriter(w, context), context); var clientId = control.GetDotvvmUniqueId() as string; if (clientId == null) { throw new DotvvmControlException(control, "This control cannot use PostBack.Update=\"true\" because it has dynamic ID. This happens when the control is inside a Repeater or other data-bound control and the RenderSettings.Mode=\"Client\"."); } yield return(clientId, w.ToString()); } } else { foreach (var child in control.Children) { stack.Push(child); } } } while (stack.Count > 0); }
/// <summary> /// Initializes the view model for the specified view. /// </summary> public object InitializeViewModel(IDotvvmRequestContext context, DotvvmView view) { return(CreateViewModelInstance(view.ViewModelType, context)); }
/// <summary> /// Determines whether the page is nested in master page. /// </summary> private bool IsNestedInMasterPage(DotvvmView page) { return(page.Directives.ContainsKey(ParserConstants.MasterPageDirective)); }