Browse Source

Merge pull request #2797 from abpframework/Docs-NotFoundPage

Docs not found page
pull/2902/head
Yunus Emre Kalkan 6 years ago
committed by GitHub
parent
commit
a1efa5bcfd
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs
  2. 70
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController.cs
  3. 130
      framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs
  4. 8
      modules/docs/src/Volo.Docs.Web/DocsWebModule.cs
  5. 436
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml
  6. 17
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs
  7. 53
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/Default.cshtml
  8. 15
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundPageModel.cs
  9. 26
      modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundViewComponent.cs
  10. 1
      modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj

8
framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AbpAuditingOptions.cs

@ -30,10 +30,10 @@ namespace Volo.Abp.Auditing
/// Default: true.
/// </summary>
public bool IsEnabledForAnonymousUsers { get; set; }
/// <summary>
/// Audit log on exceptions.
/// Default: true.
/// <summary>
/// Audit log on exceptions.
/// Default: true.
/// </summary>
public bool AlwaysLogOnException { get; set; }

70
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController.cs

@ -1,35 +1,35 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Volo.Abp.Auditing;
namespace Volo.Abp.AspNetCore.Mvc.Auditing
{
[Route("api/audit-test")]
[Audited]
public class AuditTestController : AbpController
{
private readonly AbpAuditingOptions _options;
public AuditTestController(IOptions<AbpAuditingOptions> options)
{
_options = options.Value;
}
[Route("audit-success")]
public IActionResult AuditSuccessForGetRequests()
{
return Ok();
}
[Route("audit-fail")]
public IActionResult AuditFailForGetRequests()
{
throw new UserFriendlyException("Exception occurred!");
}
[Route("audit-fail-object")]
public object AuditFailForGetRequestsReturningObject()
{
throw new UserFriendlyException("Exception occurred!");
}
}
}
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Volo.Abp.Auditing;
namespace Volo.Abp.AspNetCore.Mvc.Auditing
{
[Route("api/audit-test")]
[Audited]
public class AuditTestController : AbpController
{
private readonly AbpAuditingOptions _options;
public AuditTestController(IOptions<AbpAuditingOptions> options)
{
_options = options.Value;
}
[Route("audit-success")]
public IActionResult AuditSuccessForGetRequests()
{
return Ok();
}
[Route("audit-fail")]
public IActionResult AuditFailForGetRequests()
{
throw new UserFriendlyException("Exception occurred!");
}
[Route("audit-fail-object")]
public object AuditFailForGetRequestsReturningObject()
{
throw new UserFriendlyException("Exception occurred!");
}
}
}

130
framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs

@ -1,65 +1,65 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using NSubstitute;
using System.Threading.Tasks;
using Volo.Abp.Auditing;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.Auditing
{
public class AuditTestController_Tests : AspNetCoreMvcTestBase
{
private readonly AbpAuditingOptions _options;
private IAuditingStore _auditingStore;
public AuditTestController_Tests()
{
_options = ServiceProvider.GetRequiredService<IOptions<AbpAuditingOptions>>().Value;
_auditingStore = ServiceProvider.GetRequiredService<IAuditingStore>();
}
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
_auditingStore = Substitute.For<IAuditingStore>();
services.Replace(ServiceDescriptor.Singleton(_auditingStore));
base.ConfigureServices(context, services);
}
[Fact]
public async Task Should_Trigger_Middleware_And_AuditLog_Success_For_GetRequests()
{
_options.IsEnabledForGetRequests = true;
_options.AlwaysLogOnException = false;
await GetResponseAsync("api/audit-test/audit-success");
await _auditingStore.Received().SaveAsync(Arg.Any<AuditLogInfo>());
}
[Fact]
public async Task Should_Trigger_Middleware_And_AuditLog_Exception_Always()
{
_options.IsEnabled = true;
_options.AlwaysLogOnException = true;
try
{
await GetResponseAsync("api/audit-test/audit-fail", System.Net.HttpStatusCode.Forbidden);
}
catch { }
await _auditingStore.Received().SaveAsync(Arg.Any<AuditLogInfo>());
}
[Fact]
public async Task Should_Trigger_Middleware_And_AuditLog_Exception_When_Returns_Object()
{
_options.IsEnabled = true;
_options.AlwaysLogOnException = true;
await GetResponseAsync("api/audit-test/audit-fail-object", System.Net.HttpStatusCode.Forbidden);
await _auditingStore.Received().SaveAsync(Arg.Any<AuditLogInfo>());
}
}
}
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using NSubstitute;
using System.Threading.Tasks;
using Volo.Abp.Auditing;
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.Auditing
{
public class AuditTestController_Tests : AspNetCoreMvcTestBase
{
private readonly AbpAuditingOptions _options;
private IAuditingStore _auditingStore;
public AuditTestController_Tests()
{
_options = ServiceProvider.GetRequiredService<IOptions<AbpAuditingOptions>>().Value;
_auditingStore = ServiceProvider.GetRequiredService<IAuditingStore>();
}
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services)
{
_auditingStore = Substitute.For<IAuditingStore>();
services.Replace(ServiceDescriptor.Singleton(_auditingStore));
base.ConfigureServices(context, services);
}
[Fact]
public async Task Should_Trigger_Middleware_And_AuditLog_Success_For_GetRequests()
{
_options.IsEnabledForGetRequests = true;
_options.AlwaysLogOnException = false;
await GetResponseAsync("api/audit-test/audit-success");
await _auditingStore.Received().SaveAsync(Arg.Any<AuditLogInfo>());
}
[Fact]
public async Task Should_Trigger_Middleware_And_AuditLog_Exception_Always()
{
_options.IsEnabled = true;
_options.AlwaysLogOnException = true;
try
{
await GetResponseAsync("api/audit-test/audit-fail", System.Net.HttpStatusCode.Forbidden);
}
catch { }
await _auditingStore.Received().SaveAsync(Arg.Any<AuditLogInfo>());
}
[Fact]
public async Task Should_Trigger_Middleware_And_AuditLog_Exception_When_Returns_Object()
{
_options.IsEnabled = true;
_options.AlwaysLogOnException = true;
await GetResponseAsync("api/audit-test/audit-fail-object", System.Net.HttpStatusCode.Forbidden);
await _auditingStore.Received().SaveAsync(Arg.Any<AuditLogInfo>());
}
}
}

8
modules/docs/src/Volo.Docs.Web/DocsWebModule.cs

@ -5,7 +5,9 @@ using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Prismjs;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
@ -18,7 +20,11 @@ namespace Volo.Docs
{
[DependsOn(
typeof(DocsHttpApiModule),
typeof(AbpAspNetCoreMvcUiBootstrapModule)
typeof(AbpAutoMapperModule),
typeof(AbpAspNetCoreMvcUiBootstrapModule),
typeof(AbpAspNetCoreMvcUiThemeSharedModule),
typeof(AbpAspNetCoreMvcUiPackagesModule),
typeof(AbpAspNetCoreMvcUiBundlingModule)
)]
public class DocsWebModule : AbpModule
{

436
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml

@ -5,6 +5,7 @@
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.Clipboard
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.MalihuCustomScrollbar
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.Popper
@using Volo.Docs.Pages.Documents.Shared.DocumentNotFoundComponent
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.Prismjs
@using Volo.Abp.AspNetCore.Mvc.UI.Theming
@using Volo.Docs
@ -39,269 +40,284 @@
<abp-script src="/Pages/Documents/Project/index.js" />
</abp-script-bundle>
}
<div class="docs-page" data-spy="scroll" data-target="#docs-sticky-index">
<div class="row justify-content-end">
<div class="col-md-3 docs-sidebar dark-sidebar">
<div class="docs-sidebar-wrapper">
<div class="docs-top">
<nav class="navbar navbar-logo">
@if (!Model.Project.Name.IsNullOrWhiteSpace())
{
<a class="navbar-brand w-100" href="@Model.CreateVersionLink(Model.LatestVersionInfo, Model.GetSpecificVersionOrLatest())">
<span id="ProjectName">@Model.Project.Name</span><br>
<strong class="display-block">
@L["Documents"]
</strong>
</a>
}
@if (Model.DocumentFound)
{
<div class="docs-page" data-spy="scroll" data-target="#docs-sticky-index">
<div class="row justify-content-end">
<div class="col-md-3 docs-sidebar dark-sidebar">
<div class="docs-sidebar-wrapper">
<div class="docs-top">
<nav class="navbar navbar-logo">
@if (!Model.Project.Name.IsNullOrWhiteSpace())
{
<a class="navbar-brand w-100" href="@Model.CreateVersionLink(Model.LatestVersionInfo, Model.GetSpecificVersionOrLatest())">
<span id="ProjectName">@Model.Project.Name</span><br>
<strong class="display-block">
@L["Documents"]
</strong>
</a>
}
@if (!Model.Document.Project.MainWebsiteUrl.IsNullOrWhiteSpace())
{
<a href="@Model.Document.Project.MainWebsiteUrl" class="go-back-site" id="GoToMainWebSite">
<i class="fa fa-chevron-left"></i>
@L["BackToWebsite"]
</a>
}
@if (!Model.Document.Project.MainWebsiteUrl.IsNullOrWhiteSpace())
{
<a href="@Model.Document.Project.MainWebsiteUrl" class="go-back-site" id="GoToMainWebSite">
<i class="fa fa-chevron-left"></i>
@L["BackToWebsite"]
</a>
}
<div class="for-mobile">
<div class="navbar-light">
<button type="button" class="open-dmenu navbar-toggler" aria-label="Close">
<span class="navbar-toggler-icon"></span>
</button>
<div class="for-mobile">
<div class="navbar-light">
<button type="button" class="open-dmenu navbar-toggler" aria-label="Close">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
</div>
</nav>
</div>
</nav>
</div>
<div class="docs-tree-list">
<div class="docs-tree-list">
@if (Model.ShowProjectsCombobox && Model.ProjectSelectItems.Count > 1)
{
<div class="docs-version">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text">
<i class="fa fa-folder-open" aria-hidden="true" data-toggle="tooltip" title="@L["Project"]"></i>
</label>
</div>
@if (Model.ShowProjectsCombobox && Model.ProjectSelectItems.Count > 1)
{
<div class="docs-version">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text">
<i class="fa fa-folder-open" aria-hidden="true" data-toggle="tooltip" title="@L["Project"]"></i>
</label>
</div>
<select asp-items="Model.ProjectSelectItems" class="form-control" onchange="window.location.pathname = this.value"></select>
<select asp-items="Model.ProjectSelectItems" class="form-control" onchange="window.location.pathname = this.value"></select>
</div>
</div>
</div>
</div>
}
}
<div class="row">
@if (Model.VersionSelectItems.Any())
{
<div class="col @(Model.LanguageSelectListItems.Count > 1?"pr-0":"")">
<div class="docs-version @(Model.LanguageSelectListItems.Count > 1?"pr-1":"")">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text">
<i class="fa fa-check-square-o" aria-hidden="true" data-toggle="tooltip" title="@L["Version"]"></i>
</label>
</div>
<div class="row">
@if (Model.VersionSelectItems.Any())
{
<div class="col @(Model.LanguageSelectListItems.Count > 1?"pr-0":"")">
<div class="docs-version @(Model.LanguageSelectListItems.Count > 1?"pr-1":"")">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text">
<i class="fa fa-check-square-o" aria-hidden="true" data-toggle="tooltip" title="@L["Version"]"></i>
</label>
</div>
<select asp-items="Model.VersionSelectItems" class="form-control" onchange="if (this.value) { window.location.replace(this.value) }"></select>
<select asp-items="Model.VersionSelectItems" class="form-control" onchange="if (this.value) { window.location.replace(this.value) }"></select>
</div>
</div>
</div>
</div>
</div>
}
}
@if (Model.LanguageSelectListItems.Count > 1)
{
<div class="col @(Model.VersionSelectItems.Any()?"pl-0":"")">
<div class="docs-version docs-language @(Model.VersionSelectItems.Any()?"pl-1":"")">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text">
<i class="fa fa-globe" aria-hidden="true" data-toggle="tooltip" title="@L["Language"]"></i>
</label>
@if (Model.LanguageSelectListItems.Count > 1)
{
<div class="col @(Model.VersionSelectItems.Any()?"pl-0":"")">
<div class="docs-version docs-language @(Model.VersionSelectItems.Any()?"pl-1":"")">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text">
<i class="fa fa-globe" aria-hidden="true" data-toggle="tooltip" title="@L["Language"]"></i>
</label>
</div>
<select asp-items="Model.LanguageSelectListItems" class="form-control" onchange="window.location.replace(this.value)"></select>
</div>
<select asp-items="Model.LanguageSelectListItems" class="form-control" onchange="window.location.replace(this.value)"></select>
</div>
</div>
</div>
</div>
}
</div>
}
</div>
<div class="docs-version mb-4">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text"><i class="fa fa-filter"></i></label>
</div>
<div class="docs-version mb-4">
<div class="version-select">
<div class="input-group">
<div class="input-group-prepend">
<label class="input-group-text"><i class="fa fa-filter"></i></label>
</div>
<input class="form-control" type="search" placeholder="@L["FilterTopics"].Value" aria-label="Filter">
<input class="form-control" type="search" placeholder="@L["FilterTopics"].Value" aria-label="Filter">
</div>
</div>
</div>
</div>
@if (Model.Navigation == null || !Model.Navigation.HasChildItems)
{
<div class="text-muted p-3">
<i class="fa fa-warning"></i> @L["NavigationDocumentNotFound"]
</div>
}
else
{
<ul root-node="@Model.Navigation"
version="@(Model.LatestVersionInfo == null || Model.LatestVersionInfo.IsSelected ? DocsAppConsts.Latest : Model.Version)"
project-name="@Model.ProjectName"
project-format="@Model.Project.Format"
selected-document-name="@Model.DocumentNameWithExtension"
language="@Model.LanguageCode"
id="sidebar-scroll"
class="nav nav-list"></ul>
}
</div>
</div>
</div>
@if (Model.Document != null)
{
<div class="col-md-7 docs-content bg-white">
<div class="docs-link-btns">
<div class="float-left">
@(L["ShareOn"].Value + " :")
<a href="#" target="_blank" class="share-button twitter" id="TwitterShareLink" title="Twitter">
<i class="fa fa-twitter"></i>
</a>
<a href="#" target="_blank" class="share-button linkedin" id="LinkedinShareLink" title="LinkedIn">
<i class="fa fa-linkedin"></i>
</a>
<a href="#" target="_blank" class="share-button email" id="EmailShareLink" title="E-mail">
<i class="fa fa-envelope-square"></i>
</a>
</div>
<div class="float-right">
@if (!string.IsNullOrEmpty(Model.Document.EditLink))
@if (Model.Navigation == null || !Model.Navigation.HasChildItems)
{
<a href="@Model.Document.EditLink" target="_blank"> <i class="fa fa-edit"></i> @(L["Edit"]) (@L["LastEditTime"]: @Model.Document.LastUpdatedTime.ToShortDateString())</a>
<div class="text-muted p-3">
<i class="fa fa-warning"></i> @L["NavigationDocumentNotFound"]
</div>
}
</div>
<div class="float-right mr-3">
@if (Model.Document.Contributors != null && Model.Document.Contributors.Count > 0)
else
{
@(L["Contributors"].Value + " :")
@foreach (var contributor in Model.Document.Contributors)
{
<a href="@contributor.UserProfileUrl" target="_blank">
<img src="@contributor.AvatarUrl"
class="rounded-circle" height="21" width="21" title="@contributor.Username" />
</a>
}
<ul root-node="@Model.Navigation"
version="@(Model.LatestVersionInfo == null || Model.LatestVersionInfo.IsSelected ? DocsAppConsts.Latest : Model.Version)"
project-name="@Model.ProjectName"
project-format="@Model.Project.Format"
selected-document-name="@Model.DocumentNameWithExtension"
language="@Model.LanguageCode"
id="sidebar-scroll"
class="nav nav-list"></ul>
}
</div>
</div>
</div>
@if (Model.Document != null)
{
<div class="col-md-7 docs-content bg-white">
<div class="docs-link-btns">
<div class="float-left">
@(L["ShareOn"].Value + " :")
<a href="#" target="_blank" class="share-button twitter" id="TwitterShareLink" title="Twitter">
<i class="fa fa-twitter"></i>
</a>
<a href="#" target="_blank" class="share-button linkedin" id="LinkedinShareLink" title="LinkedIn">
<i class="fa fa-linkedin"></i>
</a>
<a href="#" target="_blank" class="share-button email" id="EmailShareLink" title="E-mail">
<i class="fa fa-envelope-square"></i>
</a>
</div>
<div class="docs-content-field">
<div class="docs-text-field">
<div class="position-relative">
@if (Model.DocumentPreferences != null && Model.DocumentPreferences.Parameters != null && Model.DocumentPreferences.Parameters.Any())
<div class="float-right">
@if (!string.IsNullOrEmpty(Model.Document.EditLink))
{
<div class="alert alert-primary alert-criteria">
<abp-row>
<abp-column>
<p class="alert-p mb-2">
<i class="fa fa-info-circle"></i>
@L["MultipleVersionDocumentInfo"]
</p>
</abp-column>
</abp-row>
<abp-form-row>
@foreach (var parameter in Model.DocumentPreferences.Parameters)
{
<abp-column size="_4">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text" id="@("Section" + parameter.Name + "ComboboxAddonId")">@(parameter.DisplayName)</span>
</div>
<select class="doc-section-combobox form-control"
aria-describedby="@("Section" + parameter.Name + "ComboboxAddonId")"
id="@("Section" + parameter.Name + "ComboboxId")" data-key="@parameter.Name">
@foreach (var value in parameter.Values)
{
@if (value.Key == (Model.UserPreferences.ContainsKey(parameter.Name) ? Model.UserPreferences[parameter.Name] : null))
{
<option value="@value.Key" selected="selected">@value.Value</option>
}
else
{
<option value="@value.Key">@value.Value</option>
}
}
</select>
</div>
</abp-column>
}
</abp-form-row>
</div>
<a href="@Model.Document.EditLink" target="_blank"> <i class="fa fa-edit"></i> @(L["Edit"]) (@L["LastEditTime"]: @Model.Document.LastUpdatedTime.ToShortDateString())</a>
}
</div>
<div data-spy="scroll" data-target="#docs-sticky-index" data-offset="0">
<article class="docs-body">
<div class="float-right mr-3">
@if (Model.Document.Contributors != null && Model.Document.Contributors.Count > 0)
{
@(L["Contributors"].Value + " :")
@foreach (var contributor in Model.Document.Contributors)
{
<a href="@contributor.UserProfileUrl" target="_blank">
<img src="@contributor.AvatarUrl"
class="rounded-circle" height="21" width="21" title="@contributor.Username" />
</a>
}
}
</div>
@if (Model.DocumentLanguageIsDifferent)
</div>
<div class="docs-content-field">
<div class="docs-text-field">
<div class="position-relative">
@if (Model.DocumentPreferences != null && Model.DocumentPreferences.Parameters != null && Model.DocumentPreferences.Parameters.Any())
{
<abp-alert alert-type="Warning" dismissible="true" class="mb-0">
@L["DocumentNotFoundInSelectedLanguage"]
</abp-alert>
<div class="alert alert-primary alert-criteria">
<abp-row>
<abp-column>
<p class="alert-p mb-2">
<i class="fa fa-info-circle"></i>
@L["MultipleVersionDocumentInfo"]
</p>
</abp-column>
</abp-row>
<abp-form-row>
@foreach (var parameter in Model.DocumentPreferences.Parameters)
{
<abp-column size="_4">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text" id="@("Section" + parameter.Name + "ComboboxAddonId")">@(parameter.DisplayName)</span>
</div>
<select class="doc-section-combobox form-control"
aria-describedby="@("Section" + parameter.Name + "ComboboxAddonId")"
id="@("Section" + parameter.Name + "ComboboxId")" data-key="@parameter.Name">
@foreach (var value in parameter.Values)
{
@if (value.Key == (Model.UserPreferences.ContainsKey(parameter.Name) ? Model.UserPreferences[parameter.Name] : null))
{
<option value="@value.Key" selected="selected">@value.Value</option>
}
else
{
<option value="@value.Key">@value.Value</option>
}
}
</select>
</div>
</abp-column>
}
</abp-form-row>
</div>
}
@Html.Raw(Model.Document.Content)
</article>
</div>
<div data-spy="scroll" data-target="#docs-sticky-index" data-offset="0">
<article class="docs-body">
<div id="crawler_link" style="display:none;">
@if (Model.DocumentLanguageIsDifferent)
{
<abp-alert alert-type="Warning" dismissible="true" class="mb-0">
@L["DocumentNotFoundInSelectedLanguage"]
</abp-alert>
}
@Html.Raw(Model.Document.Content)
</article>
<div id="crawler_link" style="display:none;">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-2 docs-page-index position-relative bg-light">
<div class="col-md-2 docs-page-index position-relative bg-light">
<div id="scroll-index" class="docs-inner-anchors mt-2">
<div id="scroll-index" class="docs-inner-anchors mt-2">
<h5>@L["InThisDocument"]</h5>
<nav id="docs-sticky-index" class="navbar index-scroll">
</nav>
<h5>@L["InThisDocument"]</h5>
<nav id="docs-sticky-index" class="navbar index-scroll">
</nav>
<div class="row">
<div class="col p-0 py-3">
<a href="javascript:;" class="scroll-top-btn"><i class="fa fa-chevron-up"></i> @L["GoToTop"]</a>
<div class="row">
<div class="col p-0 py-3">
<a href="javascript:;" class="scroll-top-btn"><i class="fa fa-chevron-up"></i> @L["GoToTop"]</a>
</div>
</div>
</div>
</div>
</div>
</div>
}
else
{
<div class="col-md-9 p-5 bg-white">
<p>@L["DocumentNotFound"]</p>
}
else
{
<div class="col-md-9 p-5 bg-white">
<p>@L["DocumentNotFound"]</p>
<a href="/">
<i class="fa fa-chevron-left"></i>
@L["BackToWebsite"]
</a>
</div>
}
<a href="/">
<i class="fa fa-chevron-left"></i>
@L["BackToWebsite"]
</a>
</div>
}
</div>
</div>
</div>
}
else
{
@(await Component.InvokeAsync<DocumentNotFoundViewComponent>(new
{
model = new DocumentNotFoundPageModel
{
ProjectName = Model.ProjectName,
DocumentName = Model.DocumentName,
LanguageCode = Model.LanguageCode,
Version = Model.Version,
}
}))
}

17
modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml.cs

@ -33,6 +33,8 @@ namespace Volo.Docs.Pages.Documents.Project
[BindProperty(SupportsGet = true)]
public string LanguageCode { get; set; }
public bool DocumentFound { get; set; } = true;
public string DefaultLanguageCode { get; set; }
public ProjectDto Project { get; set; }
@ -86,6 +88,21 @@ namespace Volo.Docs.Pages.Documents.Project
}
public async Task<IActionResult> OnGetAsync()
{
try
{
return await SetPageAsync();
}
catch (DocumentNotFoundException exception)
{
Logger.LogWarning(exception.Message);
DocumentFound = false;
return Page();
}
}
private async Task<IActionResult> SetPageAsync()
{
DocumentsUrlPrefix = _uiOptions.RoutePrefix;
ShowProjectsCombobox = _uiOptions.ShowProjectsCombobox;

53
modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/Default.cshtml

@ -0,0 +1,53 @@
@using Volo.Docs.Pages.Documents.Shared.DocumentNotFoundComponent
@model DocumentNotFoundPageModel
@{
}
<div class="row position-relative vh-100" style="background: #e8e8e8;">
<div class="center">
<span class="notfound-404">404</span>
<h1>
<strong>"@Model.DocumentName"</strong> not found in <strong>@Model.ProjectName</strong> documents, with <strong>@Model.Version</strong> version and <strong>@Model.LanguageCode</strong> language.
</h1>
<br />
<a href="@(Model.DocumentsUrlPrefix + Model.LanguageCode + "/" +Model.ProjectName + "/" + Model.Version)" class="btn btn-primary px-4">
Go Back
</a>
</div>
</div>
<style>
h1 {
font-size: 1.75em;
line-height: 1.75;
color: #777;
font-weight: normal;
}
h1 strong {
color: #222;
}
.center {
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
text-align: center;
}
.notfound-404 {
font-size: 300px;
font-weight: 700;
line-height: 1.25;
margin: 0px;
color: #fff;
text-transform: uppercase;
display: block;
margin-bottom: -150px;
z-index: -1;
position: relative;
}
</style>

15
modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundPageModel.cs

@ -0,0 +1,15 @@
namespace Volo.Docs.Pages.Documents.Shared.DocumentNotFoundComponent
{
public class DocumentNotFoundPageModel
{
public string ProjectName { get; set; }
public string LanguageCode { get; set; }
public string Version { get; set; }
public string DocumentName { get; set; }
public string DocumentsUrlPrefix { get; set; }
}
}

26
modules/docs/src/Volo.Docs.Web/Pages/Documents/Shared/DocumentNotFoundComponent/DocumentNotFoundViewComponent.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc;
namespace Volo.Docs.Pages.Documents.Shared.DocumentNotFoundComponent
{
public class DocumentNotFoundViewComponent : AbpViewComponent
{
private readonly DocsUiOptions _options;
public DocumentNotFoundViewComponent(IOptions<DocsUiOptions> options)
{
_options = options.Value;
}
public IViewComponentResult Invoke(DocumentNotFoundPageModel model, string defaultErrorMessageKey)
{
model.DocumentsUrlPrefix = _options.RoutePrefix;
return View("~/Pages/Documents/Shared/DocumentNotFoundComponent/Default.cshtml", model);
}
}
}

1
modules/docs/src/Volo.Docs.Web/Volo.Docs.Web.csproj

@ -18,6 +18,7 @@
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Bundling\Volo.Abp.AspNetCore.Mvc.UI.Bundling.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap\Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Packages\Volo.Abp.AspNetCore.Mvc.UI.Packages.csproj" />
<ProjectReference Include="..\..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.csproj" />
<ProjectReference Include="..\Volo.Docs.HttpApi\Volo.Docs.HttpApi.csproj" />
<PackageReference Include="Markdig.Signed" Version="0.18.0" />
<PackageReference Include="Scriban" Version="2.1.1" />

Loading…
Cancel
Save