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(Controls.Validation.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); }
public async Task WriteViewModelResponse(DotvvmRequestContext context, DotvvmView view) { // return the response context.OwinContext.Response.ContentType = "application/json; charset=utf-8"; var serializedViewModel = context.GetSerializedViewModel(); await context.OwinContext.Response.WriteAsync(serializedViewModel); }
public void RenderPostbackUpdatedControls(DotvvmRequestContext 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); }
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); }
protected string RenderPage(DotvvmRequestContext 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(); } }
/// <summary> /// Initializes the view model for the specified view. /// </summary> public object InitializeViewModel(DotvvmRequestContext context, DotvvmView view) { string viewModel; if (!view.Directives.TryGetValue(Constants.ViewModelDirectiveName, out viewModel)) { throw new Exception("Couldn't find a viewmodel for the specified view!"); // TODO: exception handling } var viewModelType = Type.GetType(viewModel); if (viewModelType == null) { throw new Exception(string.Format("Couldn't create a class of type '{0}'!", viewModel)); // TODO: exception handling } return CreateViewModelInstance(viewModelType); }
/// <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> private void VerifySpaRequest(DotvvmRequestContext 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.OwinContext.Request.Uri.AbsoluteUri); } } }
public void RenderPage(DotvvmRequestContext context, DotvvmView view) { // embed resource links EmbedResourceLinks(view); // prepare the render context var renderContext = new RenderContext(context); // get the HTML using (var textWriter = new StringWriter()) { var htmlWriter = new HtmlWriter(textWriter, context); view.Render(htmlWriter, renderContext); context.RenderedHtml = textWriter.ToString(); } }
/// <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()); }
/// <summary> /// Builds the view model for the client. /// </summary> public void BuildViewModel(DotvvmRequestContext context, DotvvmView view) { // serialize the ViewModel var serializer = CreateJsonSerializer(); var viewModelConverter = new ViewModelJsonConverter() { EncryptedValues = new JArray(), UsedSerializationMaps = new HashSet<ViewModelSerializationMap>() }; serializer.Converters.Add(viewModelConverter); var writer = new JTokenWriter(); serializer.Serialize(writer, context.ViewModel); // persist CSRF token writer.Token["$csrfToken"] = context.CsrfToken; // persist encrypted values if (viewModelConverter.EncryptedValues.Count > 0) writer.Token["$encryptedValues"] = viewModelProtector.Protect(viewModelConverter.EncryptedValues.ToString(Formatting.None), context); // serialize validation rules var validationRules = SerializeValidationRules(viewModelConverter); // create result object var result = new JObject(); result["viewModel"] = writer.Token; result["url"] = context.OwinContext.Request.Uri.PathAndQuery; result["virtualDirectory"] = DotvvmMiddleware.GetVirtualDirectory(context.OwinContext); if (context.IsPostBack || context.IsSpaRequest) { result["action"] = "successfulCommand"; var renderedResources = new HashSet<string>(context.ReceivedViewModelJson?["renderedResources"]?.Values<string>() ?? new string[] { }); result["resources"] = BuildResourcesJson(context, rn => !renderedResources.Contains(rn)); } else { result["renderedResources"] = JArray.FromObject(context.ResourceManager.RequiredResources); } // TODO: do not send on postbacks if (validationRules.Count > 0) result["validationRules"] = validationRules; context.ViewModelJson = result; }
public void NamingContainers_EnsureControlHasID_GenerateUniqueID() { var page = new DotvvmView(); page.SetValue(Internal.UniqueIDProperty, "c0"); var head = new HtmlGenericControl("head"); head.SetValue(Internal.UniqueIDProperty, "c1"); page.Children.Add(head); var title = new HtmlGenericControl("title"); title.SetValue(Internal.UniqueIDProperty, "c2"); head.Children.Add(title); var body = new HtmlGenericControl("body"); body.SetValue(Internal.UniqueIDProperty, "c3"); page.Children.Add(body); var div1 = new HtmlGenericControl("div"); div1.SetValue(Internal.UniqueIDProperty, "c4"); body.Children.Add(div1); var div2 = new HtmlGenericControl("div"); div2.SetValue(Internal.UniqueIDProperty, "c5"); div2.SetValue(Internal.IsNamingContainerProperty, true); body.Children.Add(div2); var div3 = new HtmlGenericControl("div"); div3.SetValue(Internal.UniqueIDProperty, "c6"); div2.Children.Add(div3); div3.EnsureControlHasId(true); div1.EnsureControlHasId(true); Assert.AreEqual("c4", div1.ID); Assert.AreEqual("c5_c6", div3.ID); }
/// <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) { // 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 var children = content.Children.ToArray(); placeHolder.Children.Clear(); content.Children.Clear(); foreach (var child in children) { placeHolder.Children.Add(child); } } // copy the directives from content page to the master page (except the @masterpage) foreach (var directive in childPage.Directives) { if (directive.Key == Constants.MasterPageDirective) { continue; } masterPage.Directives[directive.Key] = directive.Value; } }
/// <summary> /// Initializes the view model for the specified view. /// </summary> public object InitializeViewModel(IDotvvmRequestContext context, DotvvmView view) { return CreateViewModelInstance(view.ViewModelType); }
/// <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> /// Populates the view model from the data received from the request. /// </summary> /// <returns></returns> public void PopulateViewModel(DotvvmRequestContext context, DotvvmView view, string serializedPostData) { var viewModelConverter = new ViewModelJsonConverter(); // get properties var data = context.ReceivedViewModelJson = JObject.Parse(serializedPostData); var viewModelToken = (JObject)data["viewModel"]; // load CSRF token context.CsrfToken = viewModelToken["$csrfToken"].Value<string>(); if (viewModelToken["$encryptedValues"] != null) { // load encrypted values var encryptedValuesString = viewModelToken["$encryptedValues"].Value<string>(); viewModelConverter.EncryptedValues = JArray.Parse(viewModelProtector.Unprotect(encryptedValuesString, context)); } else viewModelConverter.EncryptedValues = new JArray(); // get validation path context.ModelState.ValidationTargetPath = data["validationTargetPath"].Value<string>(); // populate the ViewModel var serializer = CreateJsonSerializer(); serializer.Converters.Add(viewModelConverter); viewModelConverter.Populate(viewModelToken, serializer, context.ViewModel); }
/// <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]; } } }
/// <summary> /// Determines whether the page is nested in master page. /// </summary> private bool IsNestedInMasterPage(DotvvmView page) { return page.Directives.ContainsKey(ParserConstants.MasterPageDirective); }
/// <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 content.Parent.Children.Remove(content); placeHolder.Children.Clear(); placeHolder.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; }