Skip to content

Commit ed6ae30

Browse files
committedFeb 22, 2019
init sample
1 parent f82401c commit ed6ae30

19 files changed

+452
-0
lines changed
 

‎Fragments.sln

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.28307.329
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "src\Sample\Sample.csproj", "{E10F63B8-001B-426C-97C5-1288FB0C1071}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fragments", "src\Fragments\Fragments.csproj", "{631B3B52-4045-4F89-973E-0B94AE7EFADE}"
9+
EndProject
10+
Global
11+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12+
Debug|Any CPU = Debug|Any CPU
13+
Release|Any CPU = Release|Any CPU
14+
EndGlobalSection
15+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
16+
{E10F63B8-001B-426C-97C5-1288FB0C1071}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17+
{E10F63B8-001B-426C-97C5-1288FB0C1071}.Debug|Any CPU.Build.0 = Debug|Any CPU
18+
{E10F63B8-001B-426C-97C5-1288FB0C1071}.Release|Any CPU.ActiveCfg = Release|Any CPU
19+
{E10F63B8-001B-426C-97C5-1288FB0C1071}.Release|Any CPU.Build.0 = Release|Any CPU
20+
{631B3B52-4045-4F89-973E-0B94AE7EFADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{631B3B52-4045-4F89-973E-0B94AE7EFADE}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{631B3B52-4045-4F89-973E-0B94AE7EFADE}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{631B3B52-4045-4F89-973E-0B94AE7EFADE}.Release|Any CPU.Build.0 = Release|Any CPU
24+
EndGlobalSection
25+
GlobalSection(SolutionProperties) = preSolution
26+
HideSolutionNode = FALSE
27+
EndGlobalSection
28+
GlobalSection(ExtensibilityGlobals) = postSolution
29+
SolutionGuid = {59927C28-603A-458B-8143-014F08ABE481}
30+
EndGlobalSection
31+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Text.RegularExpressions;
4+
using Fragments.Areas.Fragments.Models;
5+
using Microsoft.AspNetCore.Mvc.Infrastructure;
6+
7+
namespace Fragments.Areas.Fragments
8+
{
9+
public static class Extensions
10+
{
11+
private static readonly Regex FragmentsTypeRegex = new Regex(@"^fragments\/([^.]+)", RegexOptions.IgnoreCase);
12+
13+
public static IReadOnlyCollection<FragmentModel> ToFragments(
14+
this IActionDescriptorCollectionProvider actionDescriptorsProvider)
15+
{
16+
return actionDescriptorsProvider.ActionDescriptors.Items
17+
.Select(x => (match: FragmentsTypeRegex.Match(x.AttributeRouteInfo?.Template ?? ""),
18+
template: x.AttributeRouteInfo?.Template ?? ""))
19+
.Where(x => !x.template.EndsWith("frame", System.StringComparison.Ordinal))
20+
.Where(x => x.match.Success)
21+
.GroupBy(x => x.match.Groups[1].Value)
22+
.Select(x => new FragmentModel(x.Key, x.Select(f => f.template)))
23+
.ToList();
24+
}
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Fragments.Areas.Fragments.Models;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.AspNetCore.Mvc.Infrastructure;
4+
5+
namespace Fragments.Areas.Fragments
6+
{
7+
[Route("fragments")]
8+
public class FragmentsController : Controller
9+
{
10+
private readonly IActionDescriptorCollectionProvider _actionDescriptorsProvider;
11+
12+
public FragmentsController(IActionDescriptorCollectionProvider actionDescriptorsProvider)
13+
{
14+
_actionDescriptorsProvider = actionDescriptorsProvider;
15+
}
16+
17+
[HttpGet]
18+
public IActionResult Index()
19+
{
20+
return PartialView("~/Areas/Fragments/Index.cshtml", _actionDescriptorsProvider.ToFragments());
21+
}
22+
23+
[HttpGet("frame")]
24+
[ValidateModelState]
25+
public IActionResult Frame([FromQuery] FrameModel model)
26+
{
27+
return PartialView("~/Areas/Fragments/Frame.cshtml", model);
28+
}
29+
}
30+
}
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@model Fragments.Areas.Fragments.Models.FrameModel
2+
<!DOCTYPE html>
3+
<html>
4+
<head>
5+
<title>Fragment - @Model.Name</title>
6+
<esi:include src="/assets/fragments/baseline.css.html"></esi:include>
7+
@foreach (var css in Model.Css)
8+
{
9+
<esi:include src="@css" onerror="continue"></esi:include>
10+
}
11+
</head>
12+
<body>
13+
@foreach (var html in Model.Html)
14+
{
15+
<esi:include src="@html" onerror="continue"></esi:include>
16+
}
17+
18+
<esi:include src="/assets/fragments/baseline.js.html" />
19+
20+
@foreach (var js in Model.Js)
21+
{
22+
<esi:include src="@js" onerror="continue"></esi:include>
23+
}
24+
</body>
25+
</html>
+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
@model IReadOnlyCollection<Fragments.Areas.Fragments.Models.FragmentModel>
2+
<!DOCTYPE html>
3+
<html>
4+
<head>
5+
<title>All the fragments!</title>
6+
</head>
7+
<body>
8+
<style>
9+
.expandable {
10+
}
11+
12+
.fragments {
13+
margin-bottom: 50px;
14+
font-family: Open Sans, sans-serif;
15+
border: 1px solid lightgrey;
16+
}
17+
18+
.fragments-iframe {
19+
display: block;
20+
width: 100%;
21+
height: 800px;
22+
border: 0;
23+
overflow: hidden;
24+
}
25+
26+
.hide {
27+
display: none;
28+
}
29+
30+
.title {
31+
cursor: pointer;
32+
display: block;
33+
font-size: xx-large;
34+
width: 400px;
35+
}
36+
</style>
37+
@foreach (var fragment in Model.Where(x => !string.IsNullOrEmpty(x.Html)))
38+
{
39+
<span id="@fragment.Id" class="expandable title">@fragment.FragmentGroupName</span>
40+
<blockquote id="@($"{fragment.Id}details")" class="fragments hide">
41+
<div>
42+
<iframe id="@($"{fragment.Id}frame")"
43+
class="fragments-iframe"
44+
scrolling="no"
45+
data-src="@($"{Url.Action("Frame")}?name={fragment.FragmentGroupName}&css=/{fragment.Css}&html=/{fragment.Html}&js=/{fragment.Js}")"></iframe>
46+
</div>
47+
</blockquote>
48+
}
49+
<script type="text/javascript">
50+
51+
document.addEventListener("DOMContentLoaded", function () {
52+
var nodes = document.querySelectorAll('.expandable');
53+
54+
Array.from(nodes).forEach(x => x.addEventListener('click', onClick));
55+
56+
function onClick(e) {
57+
e.preventDefault();
58+
59+
var container = document.querySelector('#' + e.currentTarget.id + 'details');
60+
var frame = document.querySelector('#' + e.currentTarget.id + 'frame');
61+
if (frame.getAttribute('src') !== null) {
62+
container.classList.toggle('hide');
63+
return;
64+
}
65+
66+
frame.setAttribute('src', frame.dataset.src);
67+
68+
frame.onload = function () {
69+
container.classList.toggle('hide');
70+
}
71+
}
72+
});
73+
74+
</script>
75+
</body>
76+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Text.RegularExpressions;
4+
5+
namespace Fragments.Areas.Fragments.Models
6+
{
7+
public class FragmentModel
8+
{
9+
private const string HtmlType = "html";
10+
private const string JsType = "js";
11+
private const string CssType = "css";
12+
13+
private static readonly Regex FragmentTypeRegex = new Regex(@"/[^.]+\.([^.]+)\.[^.]+$");
14+
15+
public FragmentModel(string fragmentGroupName, IEnumerable<string> templates)
16+
{
17+
FragmentGroupName = fragmentGroupName;
18+
var fragmentTypes = GetFragmentTypes(templates);
19+
Html = GetFragmentType(HtmlType, fragmentTypes);
20+
Js = GetFragmentType(JsType, fragmentTypes);
21+
Css = GetFragmentType(CssType, fragmentTypes);
22+
}
23+
24+
public string FragmentGroupName { get; }
25+
public string Html { get; }
26+
public string Js { get; }
27+
public string Css { get; }
28+
public string Id => FragmentGroupName.Replace("/", string.Empty).ToLowerInvariant();
29+
30+
private static string GetFragmentType(string type, IEnumerable<(string type, string template)> types)
31+
{
32+
return types
33+
.Where(ft => ft.type == type)
34+
.Select(ft => ft.template)
35+
.FirstOrDefault() ?? string.Empty;
36+
}
37+
38+
private static List<(string type, string template)> GetFragmentTypes(IEnumerable<string> templates)
39+
{
40+
return templates
41+
.Select(t =>
42+
{
43+
var match = FragmentTypeRegex.Match(t);
44+
return !match.Success ? (HtmlType, t) : (match.Groups[1].Value, t);
45+
})
46+
.ToList();
47+
}
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace Fragments.Areas.Fragments.Models
4+
{
5+
public class FrameModel
6+
{
7+
[Required]
8+
public string Name { get; set; }
9+
[RelativeUrl]
10+
public string[] Css { get; set; }
11+
[RelativeUrl]
12+
public string[] Html { get; set; }
13+
[RelativeUrl]
14+
public string[] Js { get; set; }
15+
}
16+
}

‎src/Fragments/Fragments.csproj

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Razor">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
9+
</ItemGroup>
10+
</Project>

‎src/Fragments/RelativeUrlAttribute.cs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.ComponentModel.DataAnnotations;
3+
using System.Linq;
4+
5+
namespace Fragments
6+
{
7+
public class RelativeUrlAttribute : ValidationAttribute
8+
{
9+
public override bool IsValid(object value)
10+
{
11+
if (!(value is string[] urls) || !urls.Any())
12+
{
13+
return true;
14+
}
15+
16+
if (urls.All(x => Uri.TryCreate(x, UriKind.Relative, out _)))
17+
{
18+
return true;
19+
}
20+
21+
return false;
22+
}
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.AspNetCore.Mvc.Filters;
3+
4+
namespace Fragments
5+
{
6+
public class ValidateModelStateAttribute : ActionFilterAttribute
7+
{
8+
public override void OnActionExecuting(ActionExecutingContext context)
9+
{
10+
if (!context.ModelState.IsValid)
11+
{
12+
context.Result = new BadRequestObjectResult(context.ModelState);
13+
}
14+
}
15+
}
16+
}

‎src/Sample/Features/Demo/Demo.cshtml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h3>Demo</h3>
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<style type="text/css">
2+
body {
3+
background-color : pink;
4+
}
5+
</style>
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<script language="javascript">
2+
console.log("Demo fragment");
3+
</script>
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
7+
namespace Sample.Features.Demo
8+
{
9+
[Route("fragments")]
10+
public class DemoController : Controller
11+
{
12+
[HttpGet("demo.html")]
13+
[ResponseCache(Duration = 60 * 10)]
14+
public async Task<IActionResult> Demo() => PartialView("Demo");
15+
16+
[HttpGet("demo.js.html")]
17+
[ResponseCache(Duration = 60 * 60)]
18+
public async Task<IActionResult> DemoJs() => PartialView("Demo.js");
19+
20+
[HttpGet("demo.css.html")]
21+
[ResponseCache(Duration = 60 * 60)]
22+
public async Task<IActionResult> DemoCss() => PartialView("Demo.css");
23+
}
24+
}

‎src/Sample/Program.cs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore;
7+
using Microsoft.AspNetCore.Hosting;
8+
using Microsoft.Extensions.Configuration;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace Sample
12+
{
13+
public class Program
14+
{
15+
public static void Main(string[] args)
16+
{
17+
CreateWebHostBuilder(args).Build().Run();
18+
}
19+
20+
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
21+
WebHost.CreateDefaultBuilder(args)
22+
.UseStartup<Startup>();
23+
}
24+
}

0 commit comments

Comments
 (0)
Please sign in to comment.