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