Skip to content

Commit

Permalink
Add documentation comments to various binding-related classes
Browse files Browse the repository at this point in the history
Part is taken from #1392 which won't be merged for a while.
  • Loading branch information
exyi committed Jan 4, 2023
1 parent e7cc5c8 commit 8680b47
Show file tree
Hide file tree
Showing 23 changed files with 89 additions and 11 deletions.
5 changes: 5 additions & 0 deletions src/Framework/Core/ViewModel/AllowStaticCommandAttribute.cs
Expand Up @@ -3,6 +3,11 @@

namespace DotVVM.Framework.ViewModel
{
/// <summary> Allows DotVVM to call the method from staticCommand. </summary>
/// <remarks>
/// This attribute must be used to prevent attackers from calling any method in your system.
/// While DotVVM signs the method names used staticCommand and it shouldn't be possible to execute any other method,
/// the attribute offers a decent protection against RCE in case the Asp.Net Core encryption keys are compromised. </remarks>
public class AllowStaticCommandAttribute : Attribute
{
}
Expand Down
16 changes: 14 additions & 2 deletions src/Framework/Core/ViewModel/Direction.cs
Expand Up @@ -5,19 +5,31 @@
namespace DotVVM.Framework.ViewModel
{
///<summary>
/// ServerToClient, ServerToClient on postback, ClientToServer, C2S iff in command path
/// Specifies on which requests should the property should serialized and sent. Default is <see cref="Direction.Both">Both</see>.
/// Set to <see cref="Direction.None">None</see> to disable serialization of the property.
/// This enums is flags, the directions can be arbitrarily combined.
///</summary>
[Flags]
public enum Direction
{
/// <summary> Never send this property to the client, it won't be allowed to use this property from value and staticCommand bindings. </summary>
None = 0,
/// <summary> Sent to client on the initial GET request, but not sent again on postbacks </summary>
ServerToClientFirstRequest = 1,
/// <summary> Property is updated on postbacks, but not sent on the first request (initially it will be set to null or default value of the primitive type) </summary>
ServerToClientPostback = 2,
/// <summary> Sent from server to client, but not sent back. </summary>
ServerToClient = ServerToClientFirstRequest | ServerToClientPostback,
/// <summary> Complement to <see cref="ClientToServerInPostbackPath" />, not meant to be used on its own. </summary>
ClientToServerNotInPostbackPath = 4,
/// <summary> Sent from client to server, but only if the current data context is this property. If the data context is a child object of this property, only that part of the object will be sent, all other properties are ignored.
/// To sent the initial value to client, use <c>Direction.ServerToClientFirstRequest | Direction.ClientToServerInPostbackPath</c> </summary>
ClientToServerInPostbackPath = 8,
/// <summary> Sent back on postbacks. Initially the property will set to null or primitive default value. To send the initial value to client, use <c>Direction.ServerToClientFirstRequest | Direction.ClientToServer</c> </summary>
ClientToServer = ClientToServerInPostbackPath | ClientToServerNotInPostbackPath,
/// <summary> Always sent to client, sent back only when the object is the current data context (see also <see cref="ClientToServerInPostbackPath"/>) </summary>
IfInPostbackPath = ServerToClient | ClientToServerInPostbackPath,
/// <summary> Value is sent on each request. This is the default value. </summary>
Both = 15,
}
}
}
6 changes: 3 additions & 3 deletions src/Framework/Core/ViewModel/ProtectMode.cs
Expand Up @@ -11,17 +11,17 @@ namespace DotVVM.Framework.ViewModel
public enum ProtectMode
{
/// <summary>
/// The property value is sent to the client unencrypted and it is not signed. It can be modified on the client with no restrictions.
/// The property value is sent to the client unencrypted and it is not signed. It can be modified on the client with no restrictions. This is the default.
/// </summary>
None,

/// <summary>
/// The property value is sent to the client unencrypted, but it is also signed. If it is modified on the client, the server will throw an exception during postback.
/// The property value is sent to the client in both unencrypted and encrypted form. On server, the encrypted value is read, so it cannot be modified on the client.
/// </summary>
SignData,

/// <summary>
/// The property value is encrypted before it is sent to the client.
/// The property value is encrypted before it is sent to the client. Encrypted properties thus cannot be used in value bindings.
/// </summary>
EncryptData
}
Expand Down
1 change: 1 addition & 0 deletions src/Framework/Framework/Binding/ActiveDotvvmProperty.cs
Expand Up @@ -10,6 +10,7 @@

namespace DotVVM.Framework.Binding
{
/// <summary> An abstract DotvvmProperty which contains code to be executed when the assigned control is being rendered. </summary>
public abstract class ActiveDotvvmProperty : DotvvmProperty
{
public abstract void AddAttributesToRender(IHtmlWriter writer, IDotvvmRequestContext context, DotvvmControl control);
Expand Down
2 changes: 2 additions & 0 deletions src/Framework/Framework/Binding/AttachedPropertyAttribute.cs
Expand Up @@ -6,6 +6,8 @@

namespace DotVVM.Framework.Binding
{
/// <summary> Used to mark DotvvmProperty which are used on other control than the declaring type. For example, Validation.Target is an attached property. </summary>
/// <remark> Note that DotVVM allows this for any DotvvmProperty, but this attribute instructs editor extension to include the property in autocompletion. </remark>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class AttachedPropertyAttribute : Attribute
{
Expand Down
@@ -1,11 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
using DotVVM.Framework.Binding.Expressions;
using DotVVM.Framework.Compilation.Binding;

namespace DotVVM.Framework.Binding
{
/// <summary> Allow to adjust how bindings are compiled. Can be placed on custom binding type (for example, see <see cref="ValueBindingExpression" />) or on a dotvvm property </summary>
public abstract class BindingCompilationOptionsAttribute : Attribute
{
/// <summary> Returns a list of resolvers - functions which accept any set of existing binding properties and returns one new binding property.
/// It will be automatically invoked when the returned property is needed.
/// See <see cref="BindingPropertyResolvers" /> for a list of default property resolvers - to adjust how the binding is compiled, you'll want to redefine one of the default resolvers.
/// See <see cref="StaticCommandBindingExpression.OptionsAttribute" /> for example how to use this method. </summary>
public abstract IEnumerable<Delegate> GetResolvers();
}
}
5 changes: 4 additions & 1 deletion src/Framework/Framework/Binding/BindingCompilationService.cs
Expand Up @@ -26,10 +26,13 @@ public class BindingCompilationOptions
public List<object> TransformerClasses { get; set; } = new List<object>();
}

/// <summary> A service used to create new bindings and compute binding properties. </summary>
public class BindingCompilationService
{
private readonly IExpressionToDelegateCompiler expressionCompiler;
private readonly Lazy<BindingCompilationService> noInitService;

/// <summary> Utilities for caching bindings created at runtime </summary>
public DotvvmBindingCacheHelper Cache { get; }

public BindingCompilationService(IOptions<BindingCompilationOptions> options, IExpressionToDelegateCompiler expressionCompiler, IDotvvmCacheAdapter cache)
Expand Down Expand Up @@ -123,7 +126,7 @@ public BindingCompilationRequirementsAttribute GetRequirements(IBinding binding,
}

/// <summary>
/// Resolves required and optional properties
/// Resolves required properties of the binding. If the binding contains <see cref="BindingErrorReporterProperty" /> it will be used to report errors instead of throwing an exception.
/// </summary>
public virtual void InitializeBinding(IBinding binding, IEnumerable<BindingCompilationRequirementsAttribute>? bindingRequirements = null)
{
Expand Down
4 changes: 3 additions & 1 deletion src/Framework/Framework/Binding/BindingHelper.cs
Expand Up @@ -24,8 +24,10 @@ namespace DotVVM.Framework.Binding
{
public static partial class BindingHelper
{
/// <summary> Gets the binding property identified by the type. The result may be null, if <paramref name="errorMode"/> is <see cref="ErrorHandlingMode.ReturnNull">ReturnNul</see> This method should always return the same result and should run fast (may rely on caching, so first call might not be that fast). </summary>
[return: MaybeNull]
public static T GetProperty<T>(this IBinding binding, ErrorHandlingMode errorMode = ErrorHandlingMode.ThrowException) => (T)binding.GetProperty(typeof(T), errorMode)!;
public static T GetProperty<T>(this IBinding binding, ErrorHandlingMode errorMode) => (T)binding.GetProperty(typeof(T), errorMode)!;
/// <summary> Gets the binding property identified by the type. This method should always return the same result and should run fast (may rely on caching, so first call might not be that fast). </summary>
public static T GetProperty<T>(this IBinding binding) => GetProperty<T>(binding, ErrorHandlingMode.ThrowException)!;

[Obsolete]
Expand Down
3 changes: 3 additions & 0 deletions src/Framework/Framework/Binding/BindingPageInfo.cs
Expand Up @@ -11,8 +11,11 @@ namespace DotVVM.Framework.Binding
{
public class BindingPageInfo
{
/// <summary> Returns true if any command or staticCommand is currently running. Always returns false on the server. </summary>
public bool IsPostbackRunning => false;
/// <summary> Returns true on server and false in JavaScript. </summary>
public bool EvaluatingOnServer => true;
/// <summary> Returns false on server and true in JavaScript. </summary>
public bool EvaluatingOnClient => false;

internal static void RegisterJavascriptTranslations(JavascriptTranslatableMethodCollection methods)
Expand Down
Expand Up @@ -10,6 +10,7 @@

namespace DotVVM.Framework.Binding
{
/// <summary> Sets data context type to the element type of current data context. </summary>
public class CollectionElementDataContextChangeAttribute : DataContextChangeAttribute
{
public override int Order { get; }
Expand Down
Expand Up @@ -7,7 +7,7 @@
namespace DotVVM.Framework.Binding
{
/// <summary>
/// The DotvvmProperty that fallbacks to another DotvvmProperty's value.
/// The DotvvmProperty that can only be used at compile time (in server-side styles or precompiled CompositeControls)
/// </summary>
public class CompileTimeOnlyDotvvmProperty : DotvvmProperty
{
Expand Down
Expand Up @@ -9,6 +9,7 @@

namespace DotVVM.Framework.Binding
{
/// <summary> Changes the data context type to the type specified in the attribute constructor. </summary>
public class ConstantDataContextChangeAttribute : DataContextChangeAttribute
{
public Type Type { get; }
Expand Down
Expand Up @@ -10,6 +10,7 @@

namespace DotVVM.Framework.Binding
{
/// <summary> Sets data context type to the result type of binding the specified property. </summary>
public class ControlPropertyBindingDataContextChangeAttribute : DataContextChangeAttribute
{
public string PropertyName { get; set; }
Expand Down
Expand Up @@ -8,6 +8,7 @@

namespace DotVVM.Framework.Binding
{
[Obsolete("Use ControlPropertyBindingDataContextChangeAttribute instead.")]
public class ControlPropertyTypeDataContextChangeAttribute : DataContextChangeAttribute
{
public string PropertyName { get; set; }
Expand Down
1 change: 1 addition & 0 deletions src/Framework/Framework/Binding/DelegateActionProperty.cs
Expand Up @@ -10,6 +10,7 @@

namespace DotVVM.Framework.Binding
{
/// <summary> DotvvmProperty which calls the function passed in the Register method, when the assigned control is being rendered. </summary>
public sealed class DelegateActionProperty<TValue>: ActiveDotvvmProperty
{
private Action<IHtmlWriter, IDotvvmRequestContext, DotvvmProperty, DotvvmControl> func;
Expand Down
2 changes: 2 additions & 0 deletions src/Framework/Framework/Binding/DotvvmBindingCacheHelper.cs
Expand Up @@ -22,6 +22,7 @@ public DotvvmBindingCacheHelper(IDotvvmCacheAdapter cache, BindingCompilationSer
this.compilationService = compilationService;
}

/// <summary> Created a new binding using the <paramref name="factory"/>, unless an existing cache entry is found. Entries are identified using the identifier and keys. By default, the cache is LRU with size=1000 </summary>
public T CreateCachedBinding<T>(string identifier, object?[] keys, Func<T> factory) where T: IBinding
{
return this.cache.GetOrAdd(new CacheKey(typeof(T), identifier, keys), _ => {
Expand All @@ -31,6 +32,7 @@ public DotvvmBindingCacheHelper(IDotvvmCacheAdapter cache, BindingCompilationSer
});
}

/// <summary> Created a new binding of type <typeparamref name="T"/> with the specified properties, unless an existing cache entry is found. Entries are identified using the identifier and keys. By default, the cache is LRU with size=1000 </summary>
public T CreateCachedBinding<T>(string identifier, object[] keys, object[] properties) where T: IBinding
{
return CreateCachedBinding<T>(identifier, keys, () => (T)BindingFactory.CreateBinding(this.compilationService, typeof(T), properties));
Expand Down
Expand Up @@ -18,6 +18,9 @@

namespace DotVVM.Framework.Binding.Expressions
{
/// <summary> Represents a data-binding in DotVVM.
/// This is a base class for all bindings, BindingExpression in general does not guarantee that the binding will have any property.
/// This class only contains the glue code which automatically calls resolvers and caches the results when <see cref="GetProperty(Type, ErrorHandlingMode)" /> is invoked. </summary>
[BindingCompilationRequirements(optional: new[] { typeof(BindingResolverCollection) })]
[Newtonsoft.Json.JsonConverter(typeof(BindingDebugJsonConverter))]
public abstract class BindingExpression : IBinding, ICloneableBinding
Expand Down
11 changes: 9 additions & 2 deletions src/Framework/Framework/Binding/Expressions/IBinding.cs
Expand Up @@ -4,26 +4,33 @@

namespace DotVVM.Framework.Binding.Expressions
{
/// <summary> Controls what happens when the binding property does not exist on this binding or when it's resolver throws an exception. </summary>
public enum ErrorHandlingMode
{
/// <summary> Returns null. The null is returned even in case when resolver throws an exception, you can't distinguish between the "property does not exist", "resolver failed" states using this mode. </summary>
ReturnNull,
/// <summary> Throws the exception. Always throws <see cref="BindingPropertyException" />. In case the property is missing, message = "resolver not found". Otherwise, the exception will have the resolver error as InnerException. </summary>
ThrowException,
/// <summary> Behaves similarly to ThrowException, but the exception is returned instead of being thrown. This is useful when you'd catch the exception immediately to avoid annoying debugger by throwing too many exceptions. </summary>
ReturnException
}

/// <summary> General interface which all DotVVM data binding types must implement. This interface does not provide any specific binding properties, only the basic building blocks - that bindings are composed of binding properties (<see cref="GetProperty(Type, ErrorHandlingMode)" />), should have a DataContext and may have resolvers. </summary>
public interface IBinding
{
/// <summary> Gets the binding property identified by the type. Returned object will always be of type <paramref name="type"/>, null, or Exception (this depends on the <paramref name="errorMode" />). This method should always return the same result and should run fast (may rely on caching, so first call might not be that fast). </summary>
object? GetProperty(Type type, ErrorHandlingMode errorMode = ErrorHandlingMode.ThrowException);

/// <summary> If the binding expects a specific data context, this property should return it. "Normal" binding coming from dothtml markup won't return null since they always depend on the data context. </summary>
DataContextStack? DataContext { get; }

BindingResolverCollection? GetAdditionalResolvers();
//IDictionary<Type, object> Properties { get; }
//IList<Delegate> AdditionalServices { get; }
}


public interface ICloneableBinding: IBinding
{
/// <summary> Returns a list of all properties which are already cached. Creating a new binding with these properties will produce the same binding. </summary>
IEnumerable<object> GetAllComputedProperties();
}
}
Expand Up @@ -10,6 +10,7 @@

namespace DotVVM.Framework.Binding.Expressions
{
/// <summary> The `{staticCommand: ...}` binding. It is a command, so should be used to handle events, but compared to `command`, it runs primarily client-side. Compared to `value` binding, `staticCommand`s are expected to have side effects and run asynchronously (the binding will return a Promise or a Task). </summary>
[BindingCompilationRequirements(
required: new[] { typeof(StaticCommandOptionsLambdaJavascriptProperty), /*typeof(BindingDelegate)*/ }
)]
Expand Down
Expand Up @@ -8,6 +8,7 @@ namespace DotVVM.Framework.Binding.HelperNamespace
{
public static class ListExtensions
{
/// <summary> Updates all entries identified by <paramref name="matcher"/> using the <paramref name="updater"/>. If none match, the <paramref name="element"/> is appended to the list. </summary>
public static void AddOrUpdate<T>(this List<T> list, T element, Func<T,bool> matcher, Func<T,T> updater)
{
var found = false;
Expand All @@ -24,6 +25,7 @@ public static void AddOrUpdate<T>(this List<T> list, T element, Func<T,bool> mat
list.Add(element);
}

/// <summary> Removes the first entry identified by <paramref name="predicate"/>. </summary>
public static void RemoveFirst<T>(this List<T> list, Func<T,bool> predicate)
{
for (var index = 0; index < list.Count; index++)
Expand All @@ -36,6 +38,7 @@ public static void RemoveFirst<T>(this List<T> list, Func<T,bool> predicate)
}
}

/// <summary> Removes the last entry identified by <paramref name="predicate"/>. </summary>
public static void RemoveLast<T>(this List<T> list, Func<T, bool> predicate)
{
for (var index = list.Count - 1; index >= 0; index--)
Expand Down

0 comments on commit 8680b47

Please sign in to comment.