예제 #1
0
        /// <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);
                }
            }
        }
예제 #2
0
        /// <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));
            }
        }
예제 #3
0
        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);
        }
예제 #4
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);
        }
예제 #5
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);
        }
예제 #6
0
        /// <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));
            }
        }
예제 #7
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);
 }
예제 #8
0
 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);
 }
예제 #9
0
 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);
 }
예제 #10
0
 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);
 }
예제 #11
0
 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));
 }
예제 #12
0
        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);
        }
예제 #13
0
        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);
        }
예제 #14
0
 /// <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];
         }
     }
 }
예제 #15
0
 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());
     }
 }
예제 #16
0
        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);
        }
예제 #17
0
 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);
     }
 }
예제 #18
0
        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);
        }
예제 #19
0
        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());
            }
        }
예제 #20
0
        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());
            }
        }
예제 #21
0
        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);
        }
예제 #22
0
 /// <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, ""));
         }
     }
 }
예제 #23
0
        /// <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());
        }
예제 #24
0
        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);
        }
예제 #25
0
        /// <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;
            }
        }
예제 #26
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
            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);
        }
예제 #27
0
        /// <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;
        }
예제 #28
0
        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);
        }
예제 #29
0
 /// <summary>
 /// Initializes the view model for the specified view.
 /// </summary>
 public object InitializeViewModel(IDotvvmRequestContext context, DotvvmView view)
 {
     return(CreateViewModelInstance(view.ViewModelType, context));
 }
예제 #30
0
 /// <summary>
 /// Determines whether the page is nested in master page.
 /// </summary>
 private bool IsNestedInMasterPage(DotvvmView page)
 {
     return(page.Directives.ContainsKey(ParserConstants.MasterPageDirective));
 }