diff --git a/.idea/.idea.HopFrame/.idea/workspace.xml b/.idea/.idea.HopFrame/.idea/workspace.xml
index 934a352..0705f65 100644
--- a/.idea/.idea.HopFrame/.idea/workspace.xml
+++ b/.idea/.idea.HopFrame/.idea/workspace.xml
@@ -9,9 +9,17 @@
-
+
-
+
+
+
+
+
+
+
+
+
@@ -31,7 +39,7 @@
-
+
@@ -84,24 +92,24 @@
- {
- "keyToString": {
- ".NET Launch Settings Profile.HopFrame.Testing.executor": "Run",
- ".NET Launch Settings Profile.HopFrame.Testing: https.executor": "Run",
- ".NET Project.HopFrame.Testing.executor": "Run",
- "RunOnceActivity.ShowReadmeOnStart": "true",
- "RunOnceActivity.git.unshallow": "true",
- "git-widget-placeholder": "feature/setup",
- "list.type.of.created.stylesheet": "CSS",
- "node.js.detected.package.eslint": "true",
- "node.js.detected.package.tslint": "true",
- "node.js.selected.package.eslint": "(autodetect)",
- "node.js.selected.package.tslint": "(autodetect)",
- "nodejs_package_manager_path": "npm",
- "settings.editor.selected.configurable": "preferences.environmentSetup",
- "vue.rearranger.settings.migration": "true"
+
+}]]>
@@ -158,7 +166,7 @@
-
+
@@ -272,7 +280,15 @@
1737202192471
-
+
+
+ 1737203441319
+
+
+
+ 1737203441319
+
+
@@ -296,6 +312,7 @@
-
+
+
\ No newline at end of file
diff --git a/src/HopFrame.Core/Config/DbContextConfig.cs b/src/HopFrame.Core/Config/DbContextConfig.cs
deleted file mode 100644
index 584a5b8..0000000
--- a/src/HopFrame.Core/Config/DbContextConfig.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Microsoft.EntityFrameworkCore;
-
-namespace HopFrame.Core.Config;
-
-public class DbContextConfig {
- public Type ContextType { get; }
- public List Tables { get; } = new();
-
- public DbContextConfig(Type context) {
- ContextType = context;
-
- foreach (var property in ContextType.GetProperties()) {
- if (!property.PropertyType.IsGenericType) continue;
- var innerType = property.PropertyType.GenericTypeArguments.First();
- var setType = typeof(DbSet<>).MakeGenericType(innerType);
- if (property.PropertyType != setType) continue;
-
- var table = new TableConfig(this, innerType, property.Name, Tables.Count);
- Tables.Add(table);
- }
- }
-}
-
-public class DbContextConfig(DbContextConfig config) {
- public DbContextConfig InnerConfig { get; } = config;
-
- public DbContextConfig Table(Action> configurator) where TModel : class {
- var table = Table();
- configurator.Invoke(table);
- return this;
- }
-
- public TableConfig Table() where TModel : class {
- var table = InnerConfig.Tables.Single(table => table.TableType == typeof(TModel));
- return new TableConfig(table);
- }
-
-}
diff --git a/src/HopFrame.Core/Config/DbContextConfigurator.cs b/src/HopFrame.Core/Config/DbContextConfigurator.cs
new file mode 100644
index 0000000..9daed12
--- /dev/null
+++ b/src/HopFrame.Core/Config/DbContextConfigurator.cs
@@ -0,0 +1,57 @@
+using Microsoft.EntityFrameworkCore;
+
+namespace HopFrame.Core.Config;
+
+public class DbContextConfig {
+ public Type ContextType { get; }
+ public List Tables { get; } = new();
+
+ public DbContextConfig(Type context) {
+ ContextType = context;
+
+ foreach (var property in ContextType.GetProperties()) {
+ if (!property.PropertyType.IsGenericType) continue;
+ var innerType = property.PropertyType.GenericTypeArguments.First();
+ var setType = typeof(DbSet<>).MakeGenericType(innerType);
+ if (property.PropertyType != setType) continue;
+
+ var table = new TableConfig(this, innerType, property.Name, Tables.Count);
+ Tables.Add(table);
+ }
+ }
+}
+
+///
+/// A helper class for editing the
+///
+public class DbContextConfigurator(DbContextConfig config) {
+
+ ///
+ /// The Internal DbContext configuration that's modified by the helper functions
+ ///
+ public DbContextConfig InnerConfig { get; } = config;
+
+ ///
+ /// Configures the table of the DbContext using the provided configurator
+ ///
+ /// Used for configuring the table
+ /// The model of the table for identifying the correct one
+ ///
+ public DbContextConfigurator Table(Action> configurator) where TModel : class {
+ var table = Table();
+ configurator.Invoke(table);
+ return this;
+ }
+
+ ///
+ /// Configures the table of the DbContext
+ ///
+ /// The model of the table for identifying the correct one
+ /// The configurator for the specified table
+ ///
+ public TableConfigurator Table() where TModel : class {
+ var table = InnerConfig.Tables.Single(table => table.TableType == typeof(TModel));
+ return new TableConfigurator(table);
+ }
+
+}
diff --git a/src/HopFrame.Core/Config/HopFrameConfig.cs b/src/HopFrame.Core/Config/HopFrameConfig.cs
index 4ac3bb4..1bf8227 100644
--- a/src/HopFrame.Core/Config/HopFrameConfig.cs
+++ b/src/HopFrame.Core/Config/HopFrameConfig.cs
@@ -10,31 +10,59 @@ public class HopFrameConfig {
public string? LoginPageRewrite { get; set; }
}
+///
+/// A helper class for editing the
+///
public class HopFrameConfigurator(HopFrameConfig config) {
+
+ ///
+ /// The Internal HopFrame configuration that's modified by the helper functions
+ ///
public HopFrameConfig InnerConfig { get; } = config;
- public HopFrameConfigurator AddDbContext(Action> configurator) where TDbContext : DbContext {
+ ///
+ /// Adds all tables defined in the DbContext to the HopFrame ui and configures it using the provided configurator
+ ///
+ /// Used for configuring the DbContext
+ /// The DbContext from which all tables should be added
+ ///
+ public HopFrameConfigurator AddDbContext(Action> configurator) where TDbContext : DbContext {
var context = AddDbContext();
configurator.Invoke(context);
return this;
}
- public DbContextConfig AddDbContext() where TDbContext : DbContext {
+ ///
+ /// Adds all tables defined in the DbContext to the HopFrame ui and configures it using the provided configurator
+ ///
+ /// The DbContext from which all tables should be added
+ /// The configurator used for the DbContext
+ ///
+ public DbContextConfigurator AddDbContext() where TDbContext : DbContext {
var context = new DbContextConfig(typeof(TDbContext));
InnerConfig.Contexts.Add(context);
- return new DbContextConfig(context);
+ return new DbContextConfigurator(context);
}
+ ///
+ /// Determines if the name of the currently logged-in user should be displayed in the top right corner of the admin ui
+ ///
public HopFrameConfigurator DisplayUserInfo(bool display) {
InnerConfig.DisplayUserInfo = display;
return this;
}
+ ///
+ /// Sets a default policy that every user needs to have in order to access the admin ui
+ ///
public HopFrameConfigurator SetBasePolicy(string basePolicy) {
InnerConfig.BasePolicy = basePolicy;
return this;
}
+ ///
+ /// Sets a custom login page to redirect to if the request to the admin ui was unauthorized
+ ///
public HopFrameConfigurator SetLoginPage(string url) {
InnerConfig.LoginPageRewrite = url;
return this;
diff --git a/src/HopFrame.Core/Config/PropertyConfig.cs b/src/HopFrame.Core/Config/PropertyConfig.cs
deleted file mode 100644
index 09d7b69..0000000
--- a/src/HopFrame.Core/Config/PropertyConfig.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-using System.Collections;
-using System.Linq.Expressions;
-using System.Reflection;
-
-namespace HopFrame.Core.Config;
-
-public class PropertyConfig(PropertyInfo info, TableConfig table, int nthProperty) {
- public PropertyInfo Info { get; } = info;
- public TableConfig Table { get; } = table;
- public string Name { get; set; } = info.Name;
- public bool List { get; set; } = true;
- public bool Sortable { get; set; } = true;
- public bool Searchable { get; set; } = true;
- public PropertyInfo? DisplayedProperty { get; set; }
- public Func? Formatter { get; set; }
- public Func? EnumerableFormatter { get; set; }
- public Func? Parser { get; set; }
- public Func>>? Validator { get; set; }
- public bool Editable { get; set; } = true;
- public bool Creatable { get; set; } = true;
- public bool DisplayValue { get; set; } = true;
- public bool TextArea { get; set; }
- public int TextAreaRows { get; set; } = 16;
- public bool IsRelation { get; set; }
- public bool IsRequired { get; set; }
- public bool IsEnumerable { get; set; }
- public bool IsListingProperty { get; set; }
- public int Order { get; set; } = nthProperty;
-}
-
-public class PropertyConfig(PropertyConfig config) {
- public PropertyConfig InnerConfig { get; } = config;
-
- public PropertyConfig SetDisplayName(string displayName) {
- InnerConfig.Name = displayName;
- return this;
- }
-
- public PropertyConfig List(bool list) {
- InnerConfig.List = list;
- InnerConfig.Searchable = !list;
- return this;
- }
-
- public PropertyConfig IsSortable(bool sortable) {
- InnerConfig.Sortable = sortable;
- return this;
- }
-
- public PropertyConfig IsSearchable(bool searchable) {
- InnerConfig.Searchable = searchable;
- return this;
- }
-
- public PropertyConfig SetDisplayedProperty(Expression> propertyExpression) {
- InnerConfig.DisplayedProperty = TableConfig.GetPropertyInfo(propertyExpression);
- return this;
- }
-
- public PropertyConfig Format(Func formatter) {
- InnerConfig.Formatter = (obj, provider) => formatter.Invoke((TProp)obj, provider);
- return this;
- }
-
- public PropertyConfig FormatEach(Func formatter) {
- InnerConfig.EnumerableFormatter = (obj, provider) => formatter.Invoke((TInnerProp)obj, provider);
- return this;
- }
-
- public PropertyConfig SetParser(Func parser) {
- InnerConfig.Parser = (str, provider) => parser.Invoke(str, provider)!;
- return this;
- }
-
- public PropertyConfig SetEditable(bool editable) {
- InnerConfig.Editable = editable;
- return this;
- }
-
- public PropertyConfig SetCreatable(bool creatable) {
- InnerConfig.Creatable = creatable;
- return this;
- }
-
- public PropertyConfig DisplayValue(bool display) {
- InnerConfig.DisplayValue = display;
- return this;
- }
-
- public PropertyConfig IsTextArea(bool textField) {
- InnerConfig.TextArea = textField;
- return this;
- }
-
- public PropertyConfig SetTextAreaRows(int rows) {
- InnerConfig.TextAreaRows = rows;
- return this;
- }
-
- public PropertyConfig SetValidator(Func> validator) {
- InnerConfig.Validator = (obj, provider) => Task.FromResult(validator.Invoke((TProp?)obj, provider));
- return this;
- }
-
- public PropertyConfig SetValidator(Func>> validator) {
- InnerConfig.Validator = (obj, provider) => validator.Invoke((TProp?)obj, provider);
- return this;
- }
-
- public PropertyConfig SetOrderIndex(int index) {
- InnerConfig.Order = index;
- return this;
- }
-}
diff --git a/src/HopFrame.Core/Config/PropertyConfigurator.cs b/src/HopFrame.Core/Config/PropertyConfigurator.cs
new file mode 100644
index 0000000..9058c45
--- /dev/null
+++ b/src/HopFrame.Core/Config/PropertyConfigurator.cs
@@ -0,0 +1,178 @@
+using System.Collections;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace HopFrame.Core.Config;
+
+public class PropertyConfig(PropertyInfo info, TableConfig table, int nthProperty) {
+ public PropertyInfo Info { get; } = info;
+ public TableConfig Table { get; } = table;
+ public string Name { get; set; } = info.Name;
+ public bool List { get; set; } = true;
+ public bool Sortable { get; set; } = true;
+ public bool Searchable { get; set; } = true;
+ public PropertyInfo? DisplayedProperty { get; set; }
+ public Func? Formatter { get; set; }
+ public Func? EnumerableFormatter { get; set; }
+ public Func? Parser { get; set; }
+ public Func>>? Validator { get; set; }
+ public bool Editable { get; set; } = true;
+ public bool Creatable { get; set; } = true;
+ public bool DisplayValue { get; set; } = true;
+ public bool TextArea { get; set; }
+ public int TextAreaRows { get; set; } = 16;
+ public bool IsRelation { get; set; }
+ public bool IsRequired { get; set; }
+ public bool IsEnumerable { get; set; }
+ public bool IsListingProperty { get; set; }
+ public int Order { get; set; } = nthProperty;
+}
+
+///
+/// A helper class for editing the
+///
+public class PropertyConfigurator(PropertyConfig config) {
+
+ ///
+ /// The Internal property configuration that's modified by the helper functions
+ ///
+ public PropertyConfig InnerConfig { get; } = config;
+
+ ///
+ /// Sets the title displayed in the table header and edit dialog
+ ///
+ public PropertyConfigurator SetDisplayName(string displayName) {
+ InnerConfig.Name = displayName;
+ return this;
+ }
+
+ ///
+ /// Determines if the property should appear in the table, if not the property is also set to be not searchable
+ ///
+ ///
+ public PropertyConfigurator List(bool list) {
+ InnerConfig.List = list;
+ InnerConfig.Searchable = !list;
+ return this;
+ }
+
+ ///
+ /// Determines if the table can be sorted by the property
+ ///
+ public PropertyConfigurator IsSortable(bool sortable) {
+ InnerConfig.Sortable = sortable;
+ return this;
+ }
+
+ ///
+ /// Determines if the property get taken into account for search results
+ ///
+ public PropertyConfigurator IsSearchable(bool searchable) {
+ InnerConfig.Searchable = searchable;
+ return this;
+ }
+
+ ///
+ /// Determines if the value that should be displayed instead of the string representation of the type
+ ///
+ ///
+ public PropertyConfigurator SetDisplayedProperty(Expression> propertyExpression) {
+ InnerConfig.DisplayedProperty = TableConfigurator.GetPropertyInfo(propertyExpression);
+ return this;
+ }
+
+ ///
+ /// Determines the value that's displayed in the admin ui
+ ///
+ ///
+ ///
+ public PropertyConfigurator Format(Func formatter) {
+ InnerConfig.Formatter = (obj, provider) => formatter.Invoke((TProp)obj, provider);
+ return this;
+ }
+
+ ///
+ /// Determines the value that's displayed for each entry in the list
+ ///
+ public PropertyConfigurator FormatEach(Func formatter) {
+ InnerConfig.EnumerableFormatter = (obj, provider) => formatter.Invoke((TInnerProp)obj, provider);
+ return this;
+ }
+
+ ///
+ /// Determines the function used for parsing the value provided in the editor dialog to the actual property value
+ ///
+ public PropertyConfigurator SetParser(Func parser) {
+ InnerConfig.Parser = (str, provider) => parser.Invoke(str, provider)!;
+ return this;
+ }
+
+ ///
+ /// Determines if the value can be edited in the admin ui. If true, the value can still be initially set, but not modified
+ ///
+ ///
+ public PropertyConfigurator SetEditable(bool editable) {
+ InnerConfig.Editable = editable;
+ return this;
+ }
+
+ ///
+ /// Determines if the initial value can be edited in the admin ui. If true the value will not be visible in the create dialog
+ ///
+ ///
+ public PropertyConfigurator SetCreatable(bool creatable) {
+ InnerConfig.Creatable = creatable;
+ return this;
+ }
+
+ ///
+ /// Determines if the value should be displayed in the admin ui (useful for secrets like passwords etc.)
+ ///
+ public PropertyConfigurator DisplayValue(bool display) {
+ InnerConfig.DisplayValue = display;
+ return this;
+ }
+
+ ///
+ /// Determines if the admin ui should use a text area for modifying the value
+ ///
+ ///
+ public PropertyConfigurator IsTextArea(bool textField) {
+ InnerConfig.TextArea = textField;
+ return this;
+ }
+
+ ///
+ /// Determines the initial size of the text area field
+ ///
+ ///
+ public PropertyConfigurator SetTextAreaRows(int rows) {
+ InnerConfig.TextAreaRows = rows;
+ return this;
+ }
+
+ ///
+ /// Determines the validator used for the property value before saving
+ ///
+ public PropertyConfigurator SetValidator(Func> validator) {
+ InnerConfig.Validator = (obj, provider) => Task.FromResult(validator.Invoke((TProp?)obj, provider));
+ return this;
+ }
+
+ ///
+ /// Determines the validator used for the property value before saving
+ ///
+ public PropertyConfigurator SetValidator(Func>> validator) {
+ InnerConfig.Validator = (obj, provider) => validator.Invoke((TProp?)obj, provider);
+ return this;
+ }
+
+ ///
+ /// Determines the order index for the property in the admin ui
+ ///
+ ///
+ public PropertyConfigurator SetOrderIndex(int index) {
+ InnerConfig.Order = index;
+ return this;
+ }
+}
diff --git a/src/HopFrame.Core/Config/TableConfig.cs b/src/HopFrame.Core/Config/TableConfig.cs
deleted file mode 100644
index ec0850d..0000000
--- a/src/HopFrame.Core/Config/TableConfig.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.ComponentModel.DataAnnotations.Schema;
-using System.Linq.Expressions;
-using System.Reflection;
-
-namespace HopFrame.Core.Config;
-
-public class TableConfig {
- public Type TableType { get; }
- public string PropertyName { get; }
- public string DisplayName { get; set; }
- public string? Description { get; set; }
- public DbContextConfig ContextConfig { get; }
- public bool Ignored { get; set; }
- public int Order { get; set; }
- internal bool Seeded { get; set; }
-
- public string? ViewPolicy { get; set; }
- public string? CreatePolicy { get; set; }
- public string? UpdatePolicy { get; set; }
- public string? DeletePolicy { get; set; }
-
- public List Properties { get; } = new();
-
- public TableConfig(DbContextConfig config, Type tableType, string propertyName, int nthTable) {
- TableType = tableType;
- PropertyName = propertyName;
- ContextConfig = config;
- DisplayName = PropertyName;
- Order = nthTable;
-
- var properties = tableType.GetProperties();
- for (var i = 0; i < properties.Length; i++) {
- var info = properties[i];
- var propConfig = new PropertyConfig(info, this, i);
-
- if (info.GetCustomAttributes(true).Any(a => a is DatabaseGeneratedAttribute)) {
- propConfig.Creatable = false;
- propConfig.Editable = false;
- }
-
- if (info.GetCustomAttributes(true).Any(a => a is KeyAttribute)) {
- propConfig.Editable = false;
- }
-
- Properties.Add(propConfig);
- }
- }
-}
-
-public class TableConfig(TableConfig config) {
- public TableConfig InnerConfig { get; } = config;
-
- public TableConfig Ignore() {
- InnerConfig.Ignored = true;
- return this;
- }
-
- public PropertyConfig Property(Expression> propertyExpression) {
- var info = GetPropertyInfo(propertyExpression);
- var prop = InnerConfig.Properties
- .Single(prop => prop.Info.Name == info.Name);
- return new PropertyConfig(prop);
- }
-
- public TableConfig Property(Expression> propertyExpression, Action> configurator) {
- var prop = Property(propertyExpression);
- configurator.Invoke(prop);
- return this;
- }
-
- public PropertyConfig AddListingProperty(string name, Func template) {
- var prop = new PropertyConfig(InnerConfig.Properties.First().Info, InnerConfig, InnerConfig.Properties.Count);
- prop.Name = name;
- prop.IsListingProperty = true;
- prop.Formatter = (obj, provider) => template.Invoke((TModel)obj, provider);
- InnerConfig.Properties.Add(prop);
- return new PropertyConfig(prop);
- }
-
- public TableConfig AddListingProperty(string name, Func template, Action> configurator) {
- var prop = AddListingProperty(name, template);
- configurator.Invoke(prop);
- return this;
- }
-
- public TableConfig SetDisplayName(string name) {
- InnerConfig.DisplayName = name;
- return this;
- }
-
- public TableConfig SetDescription(string description) {
- InnerConfig.Description = description;
- return this;
- }
-
- public TableConfig SetOrderIndex(int index) {
- InnerConfig.Order = index;
- return this;
- }
-
- public TableConfig SetViewPolicy(string policy) {
- InnerConfig.ViewPolicy = policy;
- return this;
- }
-
- public TableConfig SetUpdatePolicy(string policy) {
- InnerConfig.UpdatePolicy = policy;
- return this;
- }
-
- public TableConfig SetCreatePolicy(string policy) {
- InnerConfig.CreatePolicy = policy;
- return this;
- }
-
- public TableConfig SetDeletePolicy(string policy) {
- InnerConfig.DeletePolicy = policy;
- return this;
- }
-
- internal static PropertyInfo GetPropertyInfo(Expression> propertyLambda) {
- if (propertyLambda.Body is not MemberExpression member) {
- throw new ArgumentException($"Expression '{propertyLambda}' refers to a method, not a property.");
- }
-
- if (member.Member is not PropertyInfo propInfo) {
- throw new ArgumentException($"Expression '{propertyLambda}' refers to a field, not a property.");
- }
-
- Type type = typeof(TSource);
- if (propInfo.ReflectedType != null && type != propInfo.ReflectedType &&
- !type.IsSubclassOf(propInfo.ReflectedType)) {
- throw new ArgumentException($"Expression '{propertyLambda}' refers to a property that is not from type {type}.");
- }
-
- if (propInfo.Name is null)
- throw new ArgumentException($"Expression '{propertyLambda}' refers a not existing property.");
-
- return propInfo;
- }
-
-
-}
diff --git a/src/HopFrame.Core/Config/TableConfigurator.cs b/src/HopFrame.Core/Config/TableConfigurator.cs
new file mode 100644
index 0000000..c85c32a
--- /dev/null
+++ b/src/HopFrame.Core/Config/TableConfigurator.cs
@@ -0,0 +1,202 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace HopFrame.Core.Config;
+
+public class TableConfig {
+ public Type TableType { get; }
+ public string PropertyName { get; }
+ public string DisplayName { get; set; }
+ public string? Description { get; set; }
+ public DbContextConfig ContextConfig { get; }
+ public bool Ignored { get; set; }
+ public int Order { get; set; }
+ internal bool Seeded { get; set; }
+
+ public string? ViewPolicy { get; set; }
+ public string? CreatePolicy { get; set; }
+ public string? UpdatePolicy { get; set; }
+ public string? DeletePolicy { get; set; }
+
+ public List Properties { get; } = new();
+
+ public TableConfig(DbContextConfig config, Type tableType, string propertyName, int nthTable) {
+ TableType = tableType;
+ PropertyName = propertyName;
+ ContextConfig = config;
+ DisplayName = PropertyName;
+ Order = nthTable;
+
+ var properties = tableType.GetProperties();
+ for (var i = 0; i < properties.Length; i++) {
+ var info = properties[i];
+ var propConfig = new PropertyConfig(info, this, i);
+
+ if (info.GetCustomAttributes(true).Any(a => a is DatabaseGeneratedAttribute)) {
+ propConfig.Creatable = false;
+ propConfig.Editable = false;
+ }
+
+ if (info.GetCustomAttributes(true).Any(a => a is KeyAttribute)) {
+ propConfig.Editable = false;
+ }
+
+ Properties.Add(propConfig);
+ }
+ }
+}
+
+///
+/// A helper class for editing the
+///
+public class TableConfigurator(TableConfig config) {
+
+ ///
+ /// The Internal property configuration that's modified by the helper functions
+ ///
+ public TableConfig InnerConfig { get; } = config;
+
+ ///
+ /// Determines if the table should be ignored in the admin ui
+ ///
+ public TableConfigurator Ignore(bool ignore) {
+ InnerConfig.Ignored = ignore;
+ return this;
+ }
+
+ ///
+ /// Configures the property of the table
+ ///
+ /// Used for determining the property
+ /// The configurator for the specified property
+ ///
+ public PropertyConfigurator Property(Expression> propertyExpression) {
+ var info = GetPropertyInfo(propertyExpression);
+ var prop = InnerConfig.Properties
+ .Single(prop => prop.Info.Name == info.Name);
+ return new PropertyConfigurator(prop);
+ }
+
+ ///
+ /// Configures the property of the table using the provided configurator
+ ///
+ /// Used for determining the property
+ /// Used for configuring the property
+ ///
+ public TableConfigurator Property(Expression> propertyExpression, Action> configurator) {
+ var prop = Property(propertyExpression);
+ configurator.Invoke(prop);
+ return this;
+ }
+
+ ///
+ /// Adds a virtual property to the table view (this property will not appear in the editor)
+ ///
+ /// The name of the virtual property
+ /// The template used for generating the property value
+ /// The configurator for the virtual property
+ ///
+ public PropertyConfigurator AddVirtualProperty(string name, Func template) {
+ var prop = new PropertyConfig(InnerConfig.Properties.First().Info, InnerConfig, InnerConfig.Properties.Count);
+ prop.Name = name;
+ prop.IsListingProperty = true;
+ prop.Formatter = (obj, provider) => template.Invoke((TModel)obj, provider);
+ InnerConfig.Properties.Add(prop);
+ return new PropertyConfigurator(prop);
+ }
+
+ ///
+ /// Adds a virtual property to the table view and configures it using the provided configurator (this property will not appear in the editor)
+ ///
+ /// The name of the virtual property
+ /// The template used for generating the property value
+ /// Used for configuring the virtual property
+ ///
+ public TableConfigurator AddVirtualProperty(string name, Func template, Action> configurator) {
+ var prop = AddVirtualProperty(name, template);
+ configurator.Invoke(prop);
+ return this;
+ }
+
+ ///
+ /// Determines the name for the table used in the admin ui and url for the table page
+ ///
+ public TableConfigurator SetDisplayName(string name) {
+ InnerConfig.DisplayName = name;
+ return this;
+ }
+
+ ///
+ /// Determines the description displayed in the admin ui
+ ///
+ public TableConfigurator SetDescription(string description) {
+ InnerConfig.Description = description;
+ return this;
+ }
+
+ ///
+ /// Determines the order index for the table in the admin ui
+ ///
+ ///
+ public TableConfigurator SetOrderIndex(int index) {
+ InnerConfig.Order = index;
+ return this;
+ }
+
+ ///
+ /// Determines the policy needed by a user in order to view the table
+ ///
+ public TableConfigurator SetViewPolicy(string policy) {
+ InnerConfig.ViewPolicy = policy;
+ return this;
+ }
+
+ ///
+ /// Determines the policy needed by a user in order to edit the entries
+ ///
+ public TableConfigurator SetUpdatePolicy(string policy) {
+ InnerConfig.UpdatePolicy = policy;
+ return this;
+ }
+
+ ///
+ /// Determines the policy needed by a user in order to create entries
+ ///
+ public TableConfigurator SetCreatePolicy(string policy) {
+ InnerConfig.CreatePolicy = policy;
+ return this;
+ }
+
+ ///
+ /// Determines the policy needed by a user in order to delete entries
+ ///
+ public TableConfigurator SetDeletePolicy(string policy) {
+ InnerConfig.DeletePolicy = policy;
+ return this;
+ }
+
+ internal static PropertyInfo GetPropertyInfo(Expression> propertyLambda) {
+ if (propertyLambda.Body is not MemberExpression member) {
+ throw new ArgumentException($"Expression '{propertyLambda}' refers to a method, not a property.");
+ }
+
+ if (member.Member is not PropertyInfo propInfo) {
+ throw new ArgumentException($"Expression '{propertyLambda}' refers to a field, not a property.");
+ }
+
+ Type type = typeof(TSource);
+ if (propInfo.ReflectedType != null && type != propInfo.ReflectedType &&
+ !type.IsSubclassOf(propInfo.ReflectedType)) {
+ throw new ArgumentException($"Expression '{propertyLambda}' refers to a property that is not from type {type}.");
+ }
+
+ if (propInfo.Name is null)
+ throw new ArgumentException($"Expression '{propertyLambda}' refers a not existing property.");
+
+ return propInfo;
+ }
+
+
+}
diff --git a/src/HopFrame.Core/ServiceCollectionExtensions.cs b/src/HopFrame.Core/ServiceCollectionExtensions.cs
index b53ae3c..c8ea4df 100644
--- a/src/HopFrame.Core/ServiceCollectionExtensions.cs
+++ b/src/HopFrame.Core/ServiceCollectionExtensions.cs
@@ -7,9 +7,14 @@ namespace HopFrame.Core;
public static class ServiceCollectionExtensions {
+ ///
+ /// Adds all internal HopFrame services used by the web ui including the default insecure auth handler if not already provided
+ ///
+ /// The service collection to add the services to
+ /// The same service collection that is passed in
public static IServiceCollection AddHopFrameServices(this IServiceCollection services) {
- services.AddTransient();
- services.TryAddTransient();
+ services.AddScoped();
+ services.TryAddScoped();
return services;
}
diff --git a/src/HopFrame.Core/Services/IHopFrameAuthHandler.cs b/src/HopFrame.Core/Services/IHopFrameAuthHandler.cs
index f883c7a..2cfd3f3 100644
--- a/src/HopFrame.Core/Services/IHopFrameAuthHandler.cs
+++ b/src/HopFrame.Core/Services/IHopFrameAuthHandler.cs
@@ -1,6 +1,24 @@
namespace HopFrame.Core.Services;
+///
+/// This handler is used by the HopFrame for authenticating the user that wants to access the admin ui.
+/// By default, everyone is allowed to access the admin ui, but you can implement and register this interface
+/// yourself as a scoped dependency if you only want some users to access the admin ui (recommended)
+///
public interface IHopFrameAuthHandler {
+
+ ///
+ /// Gets called by the admin ui in order to verify if the user has all necessary policies to perform an action
+ ///
+ /// If specified, the policy the user needs to perform the action
+ /// True: The user is permitted to perform the action False: The user is not permitted to perform the action
public Task IsAuthenticatedAsync(string? policy);
+
+ ///
+ /// If enabled, this method is used to get the name of the currently logged-in user in order to display it in the admin ui
+ ///
+ /// The display name of the currently logged-in user
+ ///
public Task GetCurrentUserDisplayNameAsync();
+
}
\ No newline at end of file
diff --git a/src/HopFrame.Web/Components/Dialogs/HopFrameEditor.razor b/src/HopFrame.Web/Components/Dialogs/HopFrameEditor.razor
index 041929d..af35c6f 100644
--- a/src/HopFrame.Web/Components/Dialogs/HopFrameEditor.razor
+++ b/src/HopFrame.Web/Components/Dialogs/HopFrameEditor.razor
@@ -140,6 +140,7 @@
Value="@(GetPropertyValue(property))"
Style="width: 100%;"
Rows="@property.TextAreaRows"
+ Resize="TextAreaResize.Vertical"
Disabled="@(_currentlyEditing && !property.Editable)"
Required="@property.IsRequired"
ValueChanged="@(async v => await SetPropertyValue(property, v, InputType.Text))" />
diff --git a/src/HopFrame.Web/ServiceCollectionExtensions.cs b/src/HopFrame.Web/ServiceCollectionExtensions.cs
index ef238e8..d592567 100644
--- a/src/HopFrame.Web/ServiceCollectionExtensions.cs
+++ b/src/HopFrame.Web/ServiceCollectionExtensions.cs
@@ -7,12 +7,26 @@ namespace HopFrame.Web;
public static class ServiceCollectionExtensions {
+ ///
+ /// Configures the HopFrame using the provided configurator and adds all internal HopFrame services including the default insecure auth handler if not already provided
+ ///
+ /// The service collection to add the services to
+ /// The configurator used to build the HopFrame configuration
+ /// The configuration for the FluentUI components
+ /// The same service collection that is passed in
public static IServiceCollection AddHopFrame(this IServiceCollection services, Action configurator, LibraryConfiguration? fluentUiLibraryConfiguration = null) {
var config = new HopFrameConfig();
configurator.Invoke(new HopFrameConfigurator(config));
return AddHopFrame(services, config, fluentUiLibraryConfiguration);
}
+ ///
+ /// Configures the HopFrame using the provided configurator and adds all internal HopFrame services including the default insecure auth handler if not already provided
+ ///
+ /// The service collection to add the services to
+ /// The config used for the HopFrame admin ui
+ /// The configuration for the FluentUI components
+ /// The same service collection that is passed in
public static IServiceCollection AddHopFrame(this IServiceCollection services, HopFrameConfig config, LibraryConfiguration? fluentUiLibraryConfiguration = null) {
services.AddSingleton(config);
services.AddHopFrameServices();
diff --git a/testing/HopFrame.Testing/Program.cs b/testing/HopFrame.Testing/Program.cs
index a771dc8..72939af 100644
--- a/testing/HopFrame.Testing/Program.cs
+++ b/testing/HopFrame.Testing/Program.cs
@@ -35,7 +35,7 @@ builder.Services.AddHopFrame(options => {
.IsSortable(false)
.SetOrderIndex(3);
- table.AddListingProperty("Name", (user, _) => $"{user.FirstName} {user.LastName}")
+ table.AddVirtualProperty("Name", (user, _) => $"{user.FirstName} {user.LastName}")
.SetOrderIndex(2);
table.SetDisplayName("Benutzer");