Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring of GridViewDataSet #1548

Merged
merged 64 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
55d7388
Refactoring of GridViewDataSet
tomasherceg Jan 14, 2023
64a04db
Fixed warnings and unit tests
tomasherceg Jan 14, 2023
7c5b4de
Fixed refreshing after changing the page
tomasherceg Jan 14, 2023
bcd1ee0
Added prototype of JS translations for paging and sorting options
tomasherceg Jan 14, 2023
21dbd10
DataPager commands refactored and added support for static commands
tomasherceg Jan 20, 2023
04793ae
Fixed JS translations APIafter rebase
exyi Oct 7, 2023
0100ac6
datasets: fix generating client-side reload expression
exyi Oct 7, 2023
0520ca0
Add DataPager LoadData property
exyi Oct 7, 2023
5bdcbf9
MultiCriteriaSortingOptions implemented
tomasherceg Oct 7, 2023
e51b450
Sample for static command loading added
tomasherceg Oct 7, 2023
94d15c7
Made DataPager work, at least with commands
exyi Oct 7, 2023
69b960d
DataPager: returned client-side data context change
exyi Oct 7, 2023
3bacfc5
Fixed static command paging
tomasherceg Oct 7, 2023
3132d71
Introduce DataContextStart.CreateCollectionElement
exyi Oct 22, 2023
69682f0
Refactor DataPager to rely on Repeater, and not change its data context
exyi Oct 22, 2023
0d05655
Implemented support for GridView
tomasherceg Oct 22, 2023
5450478
Fix DataPager.RenderLinkForCurrentPage
exyi Oct 22, 2023
16156a6
StaticCommand Grid sample updated
tomasherceg Nov 11, 2023
0460f99
Fix DataPager.HideWhenOnlyOnePage
exyi Nov 11, 2023
20eaa0f
Add _dataPager.Load extension parameter
exyi Nov 11, 2023
ea4264e
AppendableDataPager added
tomasherceg Nov 19, 2023
1af1b4f
Uncommented commented-out samples
tomasherceg Nov 19, 2023
3b477a3
AppendableDataPager: make it work in automatic mode
exyi Nov 19, 2023
1f71b0f
Format knockout binding group as multiline expression when some expre…
exyi Nov 19, 2023
0cf410a
fixup! AppendableDataPager: make it work in automatic mode
exyi Nov 19, 2023
912743d
Fixed ClickArguments to support precompilation in composite controls
tomasherceg Feb 25, 2024
07c3d2f
Finished multi-criterion sorting
tomasherceg Feb 25, 2024
b156896
Sample for consuming GitHub API
tomasherceg Feb 25, 2024
645d97f
Add documentation comments to token pagers
exyi Feb 25, 2024
fb14887
datasets: add DataSource original string to the generated commands IDs
exyi Feb 25, 2024
a36b0bc
Added sample for GitHub API using static commands
tomasherceg Feb 25, 2024
0fb573c
DataPager: disable links when not clickable (on last page or first page)
exyi Feb 25, 2024
c9bacd8
Revert unwanted change in JavascriptTranslatableMethodCollection
exyi Mar 1, 2024
3324bf6
datasets: Data context fixes in AppendableDataPager + some tests
exyi Apr 26, 2024
766cbb1
Added tests to GridViewStaticCommand page
tomasherceg Apr 27, 2024
c50bb49
datasets: return DataPager extensibility point, add ISingleRowInsertO…
exyi Apr 27, 2024
7ab2811
AppendableDataPager fixed
tomasherceg Apr 27, 2024
487f86c
Merge branch 'feature/new-datasets' of https://github.com/riganti/dot…
tomasherceg Apr 27, 2024
ca5fa35
Added tests for AppendableDataPager
tomasherceg Apr 27, 2024
9a2be8e
Added tests for GitHub API test samples
tomasherceg Apr 27, 2024
390ba48
datasets: Remove setters from IRowEditOptions
exyi Apr 27, 2024
97c8966
datasets: Add NoSorting, NoPaging and NoRowEdit options
exyi Apr 27, 2024
e88faa7
datasets: Make GridViewDataSetResult fields nullable (as the construc…
exyi Apr 27, 2024
ad878e7
datasets: add JS translation for getOptions
exyi Apr 27, 2024
1295be1
datasets: error handling - missing sorting capabilities
exyi Apr 27, 2024
882b6e7
datasets: more doccomments
exyi Apr 27, 2024
24fc658
datasets: error handling - multiple (ambiguous) interface implementat…
exyi Apr 27, 2024
4fbe3cb
datasets: accept new test outputs
exyi Apr 27, 2024
6e8ce44
Merge branch 'main' into feature/new-datasets
exyi May 13, 2024
15ffc4c
Data pager extensibility improvements
May 13, 2024
cbe7f70
Merge pull request #1819 from riganti/feature/new-datasets-extensibility
exyi May 15, 2024
921b413
GridView: fix SortChanged precedence
exyi Jun 1, 2024
cdfb4ed
GridViewDataSet: simplify inheritance with C# default constructors
exyi Jun 1, 2024
309cc5c
GridViewDataSet: make IBaseGridViewDataSet.Items behave reasonably
exyi Jun 1, 2024
b2a59a9
Button: Move ClickArguments handling to the base class
exyi Jun 1, 2024
30b93b5
DataPager: Allow setting both Visible and HideWhenOnlyOnePage
exyi Jun 1, 2024
3fc9e91
Merge remote-tracking branch 'origin/main' into feature/new-datasets
exyi Jun 1, 2024
d94ca61
DataPager: fix UI tests
exyi Jun 1, 2024
e71881a
Button: unit test ClickArguments
exyi Jun 1, 2024
be4e3a4
DataSets: review adjustments
exyi Jun 20, 2024
8c433d6
Added authorization check for tests dependent on GitHub API
quigamdev Jul 7, 2024
6cab06d
Updated GitHub action
tomasherceg Jul 12, 2024
815700f
Fixed stale elements in tests
tomasherceg Jul 14, 2024
d209e2a
Fixed GitHub tests
tomasherceg Jul 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/uitest/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ runs:
shell: bash
env:
DOTVVM_SAMPLES_CONFIG_PROFILE: ${{ inputs.samples-config }}
GITHUB_TOKEN: ${{ inputs.github-token }}

- if: ${{ runner.os == 'Windows' }}
run: choco install dotnet-aspnetcoremodule-v2 -y
Expand All @@ -53,6 +54,7 @@ runs:
shell: pwsh
env:
DOTVVM_SAMPLES_CONFIG_PROFILE: ${{ inputs.samples-config }}
GITHUB_TOKEN: ${{ inputs.github-token }}

# publish the result to github
- uses: ./.github/test-report
Expand Down
13 changes: 12 additions & 1 deletion .github/uitest/uitest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,16 @@ function Start-Sample {
[int][parameter(Position = 2)]$port
)
Invoke-RequiredCmds "Start sample '$sampleName'" {
Reset-IISServerManager -Confirm:$false

Remove-IISSite -Confirm:$false -Name $sampleName -ErrorAction SilentlyContinue

icacls "$root\artifacts\" /grant "IIS_IUSRS:(OI)(CI)F"

New-IISSite -Name "$sampleName" `
-PhysicalPath "$path" `
-BindingInformation "*:${port}:"

# ensure IIS created the site
while ($true) {
$state = (Get-IISSite -Name $sampleName).State
Expand All @@ -131,6 +133,15 @@ function Start-Sample {
throw "Site '${sampleName}' could not be started. State: '${state}'."
}
}

# add or update environment variable to the application pool
$existingEnvVar = c:\windows\system32\inetsrv\appcmd.exe list config -section:system.applicationHost/applicationPools `
| out-string `
| select-xml -XPath "//add[@name='DefaultAppPool']/environmentVariables/add[@name='GITHUB_TOKEN']"
if ($existingEnvVar -ne $null) {
c:\windows\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/applicationPools /-"[name='DefaultAppPool'].environmentVariables.[name='GITHUB_TOKEN']" /commit:apphost
}
c:\windows\system32\inetsrv\appcmd.exe set config -section:system.applicationHost/applicationPools /+"[name='DefaultAppPool'].environmentVariables.[name='GITHUB_TOKEN',value='$env:GITHUB_TOKEN']" /commit:apphost
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
</PropertyGroup>

<PropertyGroup Label="Building">
<LangVersion>11.0</LangVersion>
<LangVersion>12.0</LangVersion>
<!-- Disable warning for missing XML doc comments. -->
<NoWarn>$(NoWarn);CS1591;CS1573</NoWarn>
<Deterministic>true</Deterministic>
Expand Down
141 changes: 141 additions & 0 deletions src/Framework/Core/Controls/GenericGridViewDataSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace DotVVM.Framework.Controls
{
public class GenericGridViewDataSet<
T,
TFilteringOptions,
TSortingOptions,
TPagingOptions,
TRowInsertOptions,
TRowEditOptions>
: IGridViewDataSet<T>,
IFilterableGridViewDataSet<TFilteringOptions>,
ISortableGridViewDataSet<TSortingOptions>,
IPageableGridViewDataSet<TPagingOptions>,
IRowInsertGridViewDataSet<TRowInsertOptions>,
IRowEditGridViewDataSet<TRowEditOptions>
where TFilteringOptions : IFilteringOptions
where TSortingOptions : ISortingOptions
where TPagingOptions : IPagingOptions
where TRowInsertOptions : IRowInsertOptions
where TRowEditOptions : IRowEditOptions
{

/// <summary>
/// Gets or sets the items for the current page.
/// </summary>
public IList<T> Items { get; set; } = new List<T>();

/// <summary>
/// Gets or sets whether the data should be refreshed. This property is set to true automatically
/// when paging, sorting or other options change.
/// </summary>
public bool IsRefreshRequired { get; set; } = true;

/// <summary>
/// Gets or sets the settings for filtering.
/// </summary>
public TFilteringOptions FilteringOptions { get; set; }

/// <summary>
/// Gets or sets the settings for sorting.
/// </summary>
public TSortingOptions SortingOptions { get; set; }

/// <summary>
/// Gets or sets the settings for paging.
/// </summary>
public TPagingOptions PagingOptions { get; set; }

/// <summary>
/// Gets or sets the settings for row (item) insert feature.
/// </summary>
public TRowInsertOptions RowInsertOptions { get; set; }

/// <summary>
/// Gets or sets the settings for row (item) edit feature.
/// </summary>
public TRowEditOptions RowEditOptions { get; set; }


IList IBaseGridViewDataSet.Items => Items is IList list ? list : new ReadOnlyCollection<T>(Items);

IFilteringOptions IFilterableGridViewDataSet.FilteringOptions => this.FilteringOptions;

ISortingOptions ISortableGridViewDataSet.SortingOptions => this.SortingOptions;

IPagingOptions IPageableGridViewDataSet.PagingOptions => this.PagingOptions;

IRowInsertOptions IRowInsertGridViewDataSet.RowInsertOptions => this.RowInsertOptions;

IRowEditOptions IRowEditGridViewDataSet.RowEditOptions => this.RowEditOptions;



public GenericGridViewDataSet(TFilteringOptions filteringOptions, TSortingOptions sortingOptions, TPagingOptions pagingOptions, TRowInsertOptions rowInsertOptions, TRowEditOptions rowEditOptions)
{
FilteringOptions = filteringOptions;
SortingOptions = sortingOptions;
PagingOptions = pagingOptions;
RowInsertOptions = rowInsertOptions;
RowEditOptions = rowEditOptions;
}


/// <summary>
/// Requests to reload data into the <see cref="GridViewDataSet{T}" />.
/// </summary>
public void RequestRefresh()
{
IsRefreshRequired = true;
}

/// <summary>
/// Applies the options from the specified <see cref="GridViewDataSetOptions{TFilteringOptions, TSortingOptions, TPagingOptions}" /> to this instance.
/// </summary>
public void ApplyOptions(GridViewDataSetOptions<TFilteringOptions, TSortingOptions, TPagingOptions> options)
{
if (options.FilteringOptions != null)
{
FilteringOptions = options.FilteringOptions;
}

if (options.SortingOptions != null)
{
SortingOptions = options.SortingOptions;
}

if (options.PagingOptions != null)
{
PagingOptions = options.PagingOptions;
}
}

public GridViewDataSetOptions<TFilteringOptions, TSortingOptions, TPagingOptions> GetOptions()
{
return new()
{
FilteringOptions = FilteringOptions,
SortingOptions = SortingOptions,
PagingOptions = PagingOptions
};
}

/// <summary> Sets new items + filtering, sorting, paging options. </summary>
public void ApplyResult(GridViewDataSetResult<T, TFilteringOptions, TSortingOptions, TPagingOptions> result)
{
Items = result.Items.ToList();
if (result.FilteringOptions is {})
FilteringOptions = result.FilteringOptions;
if (result.SortingOptions is {})
SortingOptions = result.SortingOptions;
if (result.PagingOptions is {})
PagingOptions = result.PagingOptions;
}
}
}
173 changes: 11 additions & 162 deletions src/Framework/Core/Controls/GridViewDataSet.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand All @@ -11,167 +9,18 @@ namespace DotVVM.Framework.Controls
/// <summary>
/// Represents a collection of items with paging, sorting and row edit capabilities.
/// </summary>
/// <typeparam name="T">The type of the <see cref="Items" /> elements.</typeparam>
public class GridViewDataSet<T> : IGridViewDataSet<T>
/// <typeparam name="T">The type of the elements in the collection.</typeparam>
public class GridViewDataSet<T>()
: GenericGridViewDataSet<T, NoFilteringOptions, SortingOptions, PagingOptions, NoRowInsertOptions, RowEditOptions>(new(), new(), new(), new(), new())
{
/// <summary>
/// Gets or sets whether the data should be refreshed. This property is set to true automatically
/// when paging, sorting or other options change.
/// </summary>
public bool IsRefreshRequired { get; set; } = true;

/// <summary>
/// Gets or sets the items for the current page.
/// </summary>
public IList<T> Items { get; set; } = new List<T>();

IList IBaseGridViewDataSet.Items => Items is List<T> list ? list : Items.ToList();

/// <summary>
/// Gets or sets the settings for paging.
/// </summary>
public IPagingOptions PagingOptions { get; set; } = new PagingOptions();

/// <summary>
/// Gets or sets the settings for row (item) edit feature.
/// </summary>
public IRowEditOptions RowEditOptions { get; set; } = new RowEditOptions();

/// <summary>
/// Gets or sets the settings for sorting.
/// </summary>
public ISortingOptions SortingOptions { get; set; } = new SortingOptions();

/// <summary>
/// Navigates to the specific page.
/// </summary>
/// <param name="index">The zero-based index of the page to navigate to.</param>
public void GoToPage(int index)
{
PagingOptions.PageIndex = index;
RequestRefresh();
}

/// <summary>
/// Loads data into the <see cref="GridViewDataSet{T}" /> from the given <see cref="IQueryable{T}" /> source.
/// </summary>
/// <param name="source">The source to load data from.</param>
public void LoadFromQueryable(IQueryable<T> source)
{
source = ApplyFilteringToQueryable(source);
Items = ApplyOptionsToQueryable(source).ToList();
PagingOptions.TotalItemsCount = source.Count();
IsRefreshRequired = false;
}

/// <summary>
/// Requests to reload data into the <see cref="GridViewDataSet{T}" />.
/// </summary>
public virtual void RequestRefresh()
{
IsRefreshRequired = true;
}

/// <summary>
/// Sets the sort expression. If the specified expression is already set, switches the sort direction.
/// </summary>
[Obsolete("This method has strange side-effects, assign the expression yourself and reload the DataSet.")]
public virtual void SetSortExpression(string expression)
{
if (SortingOptions.SortExpression == expression)
{
SortingOptions.SortDescending = !SortingOptions.SortDescending;
}
else
{
SortingOptions.SortExpression = expression;
SortingOptions.SortDescending = false;
}

GoToPage(0);
}

/// <summary>
/// Applies filtering to the <paramref name="queryable" /> before the total number
/// of items is retrieved.
/// </summary>
/// <param name="queryable">The <see cref="IQueryable{T}" /> to modify.</param>
public virtual IQueryable<T> ApplyFilteringToQueryable(IQueryable<T> queryable)
{
return queryable;
}

/// <summary>
/// Applies options to the <paramref name="queryable" /> after the total number
/// of items is retrieved.
/// </summary>
/// <param name="queryable">The <see cref="IQueryable{T}" /> to modify.</param>
public virtual IQueryable<T> ApplyOptionsToQueryable(IQueryable<T> queryable)
{
queryable = ApplySortingToQueryable(queryable);
queryable = ApplyPagingToQueryable(queryable);
return queryable;
}

/// <summary>
/// Applies sorting to the <paramref name="queryable" /> after the total number
/// of items is retrieved.
/// </summary>
/// <param name="queryable">The <see cref="IQueryable{T}" /> to modify.</param>
public virtual IQueryable<T> ApplySortingToQueryable(IQueryable<T> queryable)
{
if (SortingOptions?.SortExpression == null)
{
return queryable;
}

var parameterExpression = Expression.Parameter(typeof(T), "p");
Expression sortByExpression = parameterExpression;
foreach (var prop in (SortingOptions.SortExpression ?? "").Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries))
{
var property = sortByExpression.Type.GetTypeInfo().GetProperty(prop);
if (property == null)
{
throw new Exception($"Could not sort by property '{prop}', since it does not exists.");
}
if (property.GetCustomAttribute<BindAttribute>() is BindAttribute bind && bind.Direction == Direction.None)
{
throw new Exception($"Cannot sort by an property '{prop}' that has [Bind(Direction.None)].");
}
if (property.GetCustomAttribute<ProtectAttribute>() is ProtectAttribute protect && protect.Settings == ProtectMode.EncryptData)
{
throw new Exception($"Cannot sort by an property '{prop}' that is encrypted.");
}

sortByExpression = Expression.Property(sortByExpression, property);
}
if (sortByExpression == parameterExpression) // no sorting
{
return queryable;
}
var lambdaExpression = Expression.Lambda(sortByExpression, parameterExpression);
var methodCallExpression = Expression.Call(typeof(Queryable), GetSortingMethodName(),
new[] { parameterExpression.Type, sortByExpression.Type },
queryable.Expression,
Expression.Quote(lambdaExpression));
return queryable.Provider.CreateQuery<T>(methodCallExpression);
}

/// <summary>
/// Applies paging to the <paramref name="queryable" /> after the total number
/// of items is retrieved.
/// </summary>
/// <param name="queryable">The <see cref="IQueryable{T}" /> to modify.</param>
public virtual IQueryable<T> ApplyPagingToQueryable(IQueryable<T> queryable)
{
return PagingOptions != null && PagingOptions.PageSize > 0 ?
queryable.Skip(PagingOptions.PageSize * PagingOptions.PageIndex).Take(PagingOptions.PageSize) :
queryable;
}

private string GetSortingMethodName()
{
return SortingOptions.SortDescending ? "OrderByDescending" : "OrderBy";
// return specialized dataset options
public new GridViewDataSetOptions GetOptions()
{
return new GridViewDataSetOptions {
FilteringOptions = FilteringOptions,
SortingOptions = SortingOptions,
PagingOptions = PagingOptions
};
}
}
}
Loading
Loading