implemented multi language on docs module basicly

pull/1180/head
Yunus Emre Kalkan 6 years ago
parent d0d1b9ae75
commit 83679fb8ba

@ -22,16 +22,6 @@ namespace VoloDocs.Web.Pages
{
Projects = (await _projectAppService.GetListAsync()).Items;
if (Projects.Count == 1)
{
return RedirectToPage("./Documents/Project/Index", new
{
projectName = Projects[0].ShortName,
version = DocsAppConsts.Latest,
documentName = Projects[0].DefaultDocumentName
});
}
return Page();
}
}

@ -10,5 +10,8 @@ namespace Volo.Docs.Documents
[StringLength(ProjectConsts.MaxVersionNameLength)]
public string Version { get; set; }
[StringLength(ProjectConsts.MaxLanguageCodeLength)]
public string LanguageCode { get; set; }
}
}

@ -14,5 +14,8 @@ namespace Volo.Docs.Documents
[StringLength(ProjectConsts.MaxVersionNameLength)]
public string Version { get; set; }
[StringLength(ProjectConsts.MaxLanguageCodeLength)]
public string LanguageCode { get; set; }
}
}

@ -14,5 +14,8 @@ namespace Volo.Docs.Documents
[StringLength(ProjectConsts.MaxVersionNameLength)]
public string Version { get; set; }
[StringLength(ProjectConsts.MaxLanguageCodeLength)]
public string LanguageCode { get; set; }
}
}

@ -10,5 +10,8 @@ namespace Volo.Docs.Documents
[StringLength(ProjectConsts.MaxVersionNameLength)]
public string Version { get; set; }
[StringLength(ProjectConsts.MaxLanguageCodeLength)]
public string LanguageCode { get; set; }
}
}

@ -26,5 +26,7 @@ namespace Volo.Docs.Projects
public string DocumentStoreType { get; set; }
public Dictionary<string, object> ExtraProperties { get; set; }
public Dictionary<string, string> Languages { get; set; }
}
}

@ -9,7 +9,7 @@ namespace Volo.Docs
{
public DocsApplicationAutoMapperProfile()
{
CreateMap<Project, ProjectDto>();
CreateMap<Project, ProjectDto>().Ignore(x=>x.Languages);
CreateMap<VersionInfo, VersionInfoDto>();
CreateMap<Document, DocumentWithDetailsDto>()
.Ignore(x => x.Project).Ignore(x => x.Contributors);

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
@ -15,17 +16,20 @@ namespace Volo.Docs.Documents
private readonly IProjectRepository _projectRepository;
private readonly IDocumentStoreFactory _documentStoreFactory;
protected IDistributedCache<DocumentWithDetailsDto> DocumentCache { get; }
protected IDistributedCache<LanguageConfig> LanguaCache { get; }
protected IDistributedCache<DocumentResourceDto> ResourceCache { get; }
public DocumentAppService(
IProjectRepository projectRepository,
IDocumentStoreFactory documentStoreFactory,
IDistributedCache<DocumentWithDetailsDto> documentCache,
IDistributedCache<LanguageConfig> languaCache,
IDistributedCache<DocumentResourceDto> resourceCache)
{
_projectRepository = projectRepository;
_documentStoreFactory = documentStoreFactory;
DocumentCache = documentCache;
LanguaCache = languaCache;
ResourceCache = resourceCache;
}
@ -33,9 +37,10 @@ namespace Volo.Docs.Documents
{
var project = await _projectRepository.GetAsync(input.ProjectId);
return await GetDocumentWithDetailsDto(
return await GetDocument(
project,
input.Name,
input.LanguageCode,
input.Version
);
}
@ -44,9 +49,10 @@ namespace Volo.Docs.Documents
{
var project = await _projectRepository.GetAsync(input.ProjectId);
return await GetDocumentWithDetailsDto(
return await GetDocument(
project,
project.DefaultDocumentName,
input.LanguageCode,
input.Version
);
}
@ -55,9 +61,10 @@ namespace Volo.Docs.Documents
{
var project = await _projectRepository.GetAsync(input.ProjectId);
return await GetDocumentWithDetailsDto(
return await GetDocument(
project,
project.NavigationDocumentName,
input.LanguageCode,
input.Version
);
}
@ -92,20 +99,23 @@ namespace Volo.Docs.Documents
);
}
protected virtual async Task<DocumentWithDetailsDto> GetDocumentWithDetailsDto(
protected virtual async Task<DocumentWithDetailsDto> GetDocument(
Project project,
string documentName,
string languageCode,
string version)
{
var cacheKey = $"Document@{project.ShortName}#{documentName}#{version}";
var cacheKey = $"Document@{project.ShortName}#{languageCode}#{documentName}#{version}";
async Task<DocumentWithDetailsDto> GetDocumentAsync()
{
Logger.LogInformation($"Not found in the cache. Requesting {documentName} from the store...");
var store = _documentStoreFactory.Create(project.DocumentStoreType);
var document = await store.GetDocumentAsync(project, documentName, version);
var languages = await GetLanguageListAsync(store, project, version);
var language = GetLanguageByCode(languages, languageCode);
var document = await store.GetDocumentAsync(project, documentName, language.Code, version);
Logger.LogInformation($"Document retrieved: {documentName}");
return CreateDocumentWithDetailsDto(project, document);
return CreateDocumentWithDetailsDto(project, document, languages);
}
if (Debugger.IsAttached)
@ -125,11 +135,40 @@ namespace Volo.Docs.Documents
);
}
protected virtual DocumentWithDetailsDto CreateDocumentWithDetailsDto(Project project, Document document)
protected virtual LanguageConfigElement GetLanguageByCode(LanguageConfig languageCodes, string languageCode)
{
var language = languageCodes.Languages.FirstOrDefault(l => l.Code == languageCode);
return language ?? languageCodes.Languages.Single(l => l.IsDefault);
}
protected virtual async Task<LanguageConfig> GetLanguageListAsync(IDocumentStore store, Project project, string version)
{
async Task<LanguageConfig> GetLanguagesAsync()
{
return await store.GetLanguageListAsync(project, version);
}
return await LanguaCache.GetOrAddAsync(
project.ShortName,
GetLanguagesAsync,
() => new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(24)
}
);
}
protected virtual DocumentWithDetailsDto CreateDocumentWithDetailsDto(Project project, Document document, LanguageConfig languages)
{
var documentDto = ObjectMapper.Map<Document, DocumentWithDetailsDto>(document);
documentDto.Project = ObjectMapper.Map<Project, ProjectDto>(project);
documentDto.Contributors = ObjectMapper.Map<List<DocumentContributor>, List<DocumentContributorDto>>(document.Contributors);
documentDto.Project.Languages = new Dictionary<string, string>();
foreach (var language in languages.Languages)
{
documentDto.Project.Languages.Add(language.Code,language.DisplayName);
}
return documentDto;
}
}

@ -16,17 +16,15 @@ namespace Volo.Docs.Projects
private readonly IProjectRepository _projectRepository;
private readonly IDistributedCache<List<VersionInfo>> _versionCache;
private readonly IDocumentStoreFactory _documentStoreFactory;
private readonly IGuidGenerator _guidGenerator;
public ProjectAppService(
IProjectRepository projectRepository,
IDistributedCache<List<VersionInfo>> versionCache,
IDocumentStoreFactory documentStoreFactory, IGuidGenerator guidGenerator)
IDocumentStoreFactory documentStoreFactory)
{
_projectRepository = projectRepository;
_versionCache = versionCache;
_documentStoreFactory = documentStoreFactory;
_guidGenerator = guidGenerator;
}
public async Task<ListResultDto<ProjectDto>> GetListAsync()

@ -8,5 +8,6 @@
public const int MaxNavigationDocumentNameLength = 128;
public const int MaxLatestVersionBranchNameLength = 128;
public const int MaxVersionNameLength = 128;
public const int MaxLanguageCodeLength = 10;
}
}

@ -1,16 +1,19 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.Docs.GitHub.Documents;
using Volo.Docs.Projects;
namespace Volo.Docs.Documents
{
public interface IDocumentStore : IDomainService
{
Task<Document> GetDocumentAsync(Project project, string documentName, string version);
Task<Document> GetDocumentAsync(Project project, string documentName, string languageCode, string version);
Task<List<VersionInfo>> GetVersionsAsync(Project project);
Task<DocumentResource> GetResource(Project project, string resourceName, string version);
Task<LanguageConfig> GetLanguageListAsync(Project project, string version);
}
}

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Volo.Docs.Documents
{
public class LanguageConfig
{
public List<LanguageConfigElement> Languages { get; set; }
}
}

@ -0,0 +1,11 @@
namespace Volo.Docs.Documents
{
public class LanguageConfigElement
{
public string DisplayName { get; set; }
public string Code { get; set; }
public bool IsDefault { get; set; }
}
}

@ -1,7 +1,9 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Volo.Abp.Domain.Services;
using Volo.Abp.IO;
using Volo.Docs.Documents;
@ -14,10 +16,10 @@ namespace Volo.Docs.FileSystem.Documents
{
public const string Type = "FileSystem";
public async Task<Document> GetDocumentAsync(Project project, string documentName, string version)
public async Task<Document> GetDocumentAsync(Project project, string documentName, string languageCode, string version)
{
var projectFolder = project.GetFileSystemPath();
var path = Path.Combine(projectFolder, documentName);
var path = Path.Combine(projectFolder, languageCode, documentName);
CheckDirectorySecurity(projectFolder, path);
@ -46,6 +48,13 @@ namespace Volo.Docs.FileSystem.Documents
return Task.FromResult(new List<VersionInfo>());
}
public async Task<LanguageConfig> GetLanguageListAsync(Project project, string version)
{
var configAsJson = project.GetFileSystemPath() + "languageConfig.json";
return JsonConvert.DeserializeObject<LanguageConfig>(configAsJson);
}
public async Task<DocumentResource> GetResource(Project project, string resourceName, string version)
{
var projectFolder = project.GetFileSystemPath();

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Volo.Abp.Domain.Services;
using Volo.Docs.Documents;
using Volo.Docs.GitHub.Projects;
@ -27,14 +28,14 @@ namespace Volo.Docs.GitHub.Documents
_githubRepositoryManager = githubRepositoryManager;
}
public virtual async Task<Document> GetDocumentAsync(Project project, string documentName, string version)
public virtual async Task<Document> GetDocumentAsync(Project project, string documentName, string languageCode, string version)
{
var token = project.GetGitHubAccessTokenOrNull();
var rootUrl = project.GetGitHubUrl(version);
var rawRootUrl = CalculateRawRootUrl(rootUrl);
var userAgent = project.GetGithubUserAgentOrNull();
var rawRootUrl = CalculateRawRootUrlWithLanguageCode(rootUrl, languageCode);
var rawDocumentUrl = rawRootUrl + documentName;
var commitHistoryUrl = project.GetGitHubUrlForCommitHistory() + documentName;
var userAgent = project.GetGithubUserAgentOrNull();
var isNavigationDocument = documentName == project.NavigationDocumentName;
var editLink = rootUrl.ReplaceFirst("/tree/", "/blob/") + documentName;
var localDirectory = "";
@ -102,6 +103,19 @@ namespace Volo.Docs.GitHub.Documents
return new DocumentResource(content);
}
public async Task<LanguageConfig> GetLanguageListAsync(Project project, string version)
{
var token = project.GetGitHubAccessTokenOrNull();
var rootUrl = project.GetGitHubUrl(version);
var userAgent = project.GetGithubUserAgentOrNull();
var url = CalculateRawRootUrl(rootUrl) + "languageConfig.json";
var configAsJson = await DownloadWebContentAsStringAsync(url, token, userAgent);
return JsonConvert.DeserializeObject<LanguageConfig>(configAsJson);
}
private async Task<IReadOnlyList<Release>> GetReleasesAsync(Project project)
{
var url = project.GetGitHubUrl();
@ -202,6 +216,16 @@ namespace Volo.Docs.GitHub.Documents
return contributors;
}
private static string CalculateRawRootUrlWithLanguageCode(string rootUrl, string languageCode)
{
return (rootUrl
.Replace("github.com", "raw.githubusercontent.com")
.ReplaceFirst("/tree/", "/")
.EnsureEndsWith('/')
+ languageCode
).EnsureEndsWith('/');
}
private static string CalculateRawRootUrl(string rootUrl)
{
return rootUrl

@ -39,8 +39,8 @@ namespace Volo.Docs
{
//TODO: Make configurable!
options.Conventions.AddPageRoute("/Documents/Project/Index", "documents/{projectName}");
options.Conventions.AddPageRoute("/Documents/Project/Index", "documents/{projectName}/{version}/{*documentName}");
options.Conventions.AddPageRoute("/Documents/Project/Index", "documents/{languageCode}/{projectName}");
options.Conventions.AddPageRoute("/Documents/Project/Index", "documents/{languageCode}/{projectName}/{version}/{*documentName}");
});
Configure<AbpAutoMapperOptions>(options =>

@ -21,16 +21,6 @@ namespace Volo.Docs.Pages.Documents
{
var listResult = await _projectAppService.GetListAsync();
if (listResult.Items.Count == 1)
{
return RedirectToPage("./Project/Index", new
{
projectName = listResult.Items[0].ShortName,
version = DocsAppConsts.Latest,
documentName = listResult.Items[0].DefaultDocumentName
});
}
Projects = listResult.Items;
return Page();

@ -158,6 +158,11 @@
</a>
</div>
@if (Model.LanguageSelectListItems.Count > 1)
{
<select asp-items="Model.LanguageSelectListItems" class="form-control" onchange="window.location.replace(this.value)"></select>
}
@if (!string.IsNullOrEmpty(Model.Document.EditLink))
{
<a href="@Model.Document.EditLink" target="_blank"> <i class="fa fa-edit"></i> @L["Edit"]</a>
@ -222,3 +227,4 @@
</div>
</div>
<input hidden id="LanguageCode" value="@Model.LanguageCode"/>

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
using Volo.Abp.Localization;
using Volo.Docs.Documents;
using Volo.Docs.HtmlConverting;
using Volo.Docs.Models;
@ -23,8 +24,13 @@ namespace Volo.Docs.Pages.Documents.Project
[BindProperty(SupportsGet = true)]
public string DocumentName { get; set; }
[BindProperty(SupportsGet = true)]
public string LanguageCode { get; set; }
public ProjectDto Project { get; set; }
public List<SelectListItem> LanguageSelectListItems { get; set; }
public string DocumentNameWithExtension { get; private set; }
public DocumentWithDetailsDto Document { get; private set; }
@ -58,6 +64,8 @@ namespace Volo.Docs.Pages.Documents.Project
await SetVersionAsync();
await SetDocumentAsync();
await SetNavigationAsync();
SetLanguageSelectListItems();
AddLanguageCodePrefixToLinks();
}
private async Task SetProjectAsync()
@ -72,7 +80,7 @@ namespace Volo.Docs.Pages.Documents.Project
ProjectSelectItems = projects.Items.Select(p => new SelectListItem
{
Text = p.Name,
Value = p.Id != Project.Id ? "/documents/" + p.ShortName + "/" + DocsAppConsts.Latest : null,
Value = p.Id != Project.Id ? "/documents/" + LanguageCode + "/" + p.ShortName + "/" + DocsAppConsts.Latest : null,
Selected = p.Id == Project.Id
}).ToList();
}
@ -133,6 +141,7 @@ namespace Volo.Docs.Pages.Documents.Project
new GetNavigationDocumentInput
{
ProjectId = Project.Id,
LanguageCode = LanguageCode,
Version = Version
}
);
@ -154,7 +163,7 @@ namespace Volo.Docs.Pages.Documents.Project
version = DocsAppConsts.Latest;
}
var link = "/documents/" + ProjectName + "/" + version;
var link = "/documents/" + LanguageCode + "/" + ProjectName + "/" + version;
if (documentName != null)
{
@ -193,6 +202,7 @@ namespace Volo.Docs.Pages.Documents.Project
new GetDefaultDocumentInput
{
ProjectId = Project.Id,
LanguageCode = LanguageCode,
Version = Version
}
);
@ -204,6 +214,7 @@ namespace Volo.Docs.Pages.Documents.Project
{
ProjectId = Project.Id,
Name = DocumentNameWithExtension,
LanguageCode = LanguageCode,
Version = Version
}
);
@ -211,13 +222,35 @@ namespace Volo.Docs.Pages.Documents.Project
}
catch (DocumentNotFoundException)
{
//TODO: Handle it!
throw;
Document = await _documentAppService.GetDefaultAsync(
new GetDefaultDocumentInput
{
ProjectId = Project.Id,
LanguageCode = LanguageCode,
Version = Version
}
);
}
ConvertDocumentContentToHtml();
}
private void SetLanguageSelectListItems()
{
LanguageSelectListItems = new List<SelectListItem>();
foreach (var language in Document.Project.Languages)
{
LanguageSelectListItems.Add(
new SelectListItem(
language.Value,
"/documents/" + language.Key + "/" + Project.ShortName + "/" + Version + "/" + DocumentName,
language.Key == LanguageCode
)
);
}
}
private void ConvertDocumentContentToHtml()
{
var converter = _documentToHtmlConverterFactory.Create(Document.Format ?? Project.Format);
@ -238,5 +271,10 @@ namespace Volo.Docs.Pages.Documents.Project
Document.Content = content;
}
private void AddLanguageCodePrefixToLinks()
{
Document.Content = Document.Content.Replace("href=\"/documents", "href=\"/documents/" + LanguageCode);
}
}
}

@ -2,10 +2,21 @@
$(function () {
var addLanguageCodePrefixToLinks = function () {
var anchors = $('#sidebar-scroll a');
for (var i = 0; i < anchors.length; i++) {
var anchor = $(anchors[i]);
var newhref = anchor.attr('href').replace("/documents", "/documents/" + $('#LanguageCode').val());
anchor.attr('href', newhref);
}
};
var initNavigationFilter = function (navigationContainerId) {
var $navigation = $("#" + navigationContainerId);
var getShownDocumentLinks = function () {
return $navigation.find(".mCSB_container > li a:visible").not(".tree-toggle");
};
@ -108,6 +119,8 @@
initSocialShareLinks();
addLanguageCodePrefixToLinks();
});
})(jQuery);

@ -28,7 +28,7 @@ namespace Volo.Docs
var project = await _projectRepository.FindAsync(_testData.PorjectId);
project.ShouldNotBeNull();
var document = await store.GetDocumentAsync(project, "index2", "0.123.0");
var document = await store.GetDocumentAsync(project, "index2", "en", "0.123.0");
document.ShouldNotBeNull();
document.Title.ShouldBe("index2");

Loading…
Cancel
Save