// GET: /<controller>/ public async Task <IActionResult> Raw([FromServices] IContentProvider contentProvider, string path) { var cp = ContentPath.FromUrlPath(path); var content = contentProvider.Read(cp); var mimeType = cp.Name.Select(MimeTypes.GetMimeType).ValueOr(MimeTypes.FallbackMimeType); return(File(content.Value, mimeType)); }
public async Task <IViewComponentResult> InvokeAsync(ContentPath path) { var parts = path.Lineage.Select(_ => new Link { Name = _.GetDisplayName("Home"), Href = _.VirtualPath }); return(View(parts)); }
static Option <string> GetDirectoryReadme(IContentProvider provider, ContentPath path) { var candidates = new[] { "Readme.md", "Index.md" }; var markdown = candidates .Select(_ => GetText(provider, path.CatDir(_))) .FirstOrNone(); return(markdown); }
static Option <string> GetText(IContentProvider provider, ContentPath path) { return(provider.Read(path).Select(_ => { using (var r = new StreamReader(_)) { return r.ReadToEnd(); } })); }
internal static GrepResult ParseGitGrepOutputLine(string line) { var p = line.Split(":", 3); return(new GrepResult { Path = ContentPath.FromUrlPath(p[0]), LineNumber = Int32.Parse(p[1]), Text = p[2] }); }
static Option <string> GetChildList(ContentPath directory, IEnumerable <ContentPath> children) { if (children.Any()) { return(String.Join(String.Empty, children.Select(_ => $"* [{_.Name.ValueOr(_.ToString())}]({_.AbsoluteHref})\r\n"))); } else { return(Option.None); } }
internal static ContentPath FromUrlPath(string path) { if (String.IsNullOrEmpty(path)) { return(ContentPath.Root); } var cp = new ContentPath(path.Split(separator, StringSplitOptions.RemoveEmptyEntries) .Select(WebUtility.UrlDecode)); return(cp); }
public async Task <IActionResult> Grep( [FromServices] IContentProvider contentProvider, [FromServices] IProcessRunner runner, [FromServices] IMarkdownRenderer renderer, [FromServices] IContentGrep grep, [FromQuery] string q) { var result = await grep.Grep(q); var markdown = result.Select(_ => $"* [{_.Path}]({_.Path.AbsoluteHref}#{_.LineNumber})({_.LineNumber}): `{_.Text.Truncate(256)}`").JoinLines(); return(await MarkdownView(renderer, ContentPath.FromUrlPath("/"), markdown)); }
public Option <Stream> Read(ContentPath path) { var fsPath = path.GetFileSystemPath(rootDirectory); if (File.Exists(fsPath)) { return(File.OpenRead(fsPath)); } else { return(Option.None); } }
static bool IsText(IContentProvider contentProvider, ContentPath contentPath) { var reader = contentProvider.Read(contentPath); if (!reader.HasValue) { return(true); } using (var r = reader.Value) { var b = Read(r, 4096); return(!b.Any(_ => _ == 0) && b.Any(_ => _ == '\n')); } }
public IDisposable NotifyChange(ContentPath contentPath, Action <ContentPath> onChanged) { var fsPath = this.GetFileSystemPath(contentPath); var directoryToWatch = fsPath = (Directory.Exists(fsPath)) ? fsPath : Path.GetDirectoryName(fsPath); var w = new FileSystemWatcher(directoryToWatch); w.BeginInit(); FileSystemEventHandler changed = (s, e) => { onChanged(contentPath); }; w.Changed += changed; w.EndInit(); w.EnableRaisingEvents = true; return(w); }
public IEnumerable <ContentPath> GetChildren(ContentPath path) { var fsPath = GetFileSystemPath(path); if (Directory.Exists(fsPath)) { var c = new DirectoryInfo(fsPath).GetFileSystemInfos() .OrderByDescending(_ => _ is DirectoryInfo).ThenBy(_ => _.Name) .Where(_ => !_.Name.Equals(".git")) .Select(_ => GetChildPath(path, _)) .ToList(); return(c); } else { return(Enumerable.Empty <ContentPath>()); } }
public bool Exists(ContentPath path) { var fsPath = GetFileSystemPath(path); return(File.Exists(fsPath) || Directory.Exists(fsPath)); }
static ContentPath GetChildPath(ContentPath p, FileSystemInfo c) { return(p.CatDir(c.Name)); }
string GetFileSystemPath(ContentPath path) { return(path.GetFileSystemPath(rootDirectory)); }
async Task <IActionResult> MarkdownView(IMarkdownRenderer renderer, ContentPath path, Option <string> markdown) { var result = await renderer.Render(markdown.ValueOr("empty")); return(View("Index", new ContentModel(path, result.Title.ValueOr(path.GetDisplayName("Home")), result.Body))); }
public static Task <RendererResult> Render(this IRenderer renderer, ContentPath path, string content) { return(renderer.Render(path, new MemoryStream(System.Text.Encoding.UTF8.GetBytes(content)))); }
public bool IsAncestorOrSelf(ContentPath child) { return(parts.SequenceEqual(child.parts.Take(parts.Length))); }
public async Task <Option <string> > GetRecentChanges(ContentPath path) { var r = await git.Run(new[] { "log", "-100", $"--pretty=format:* [%ar: %s]({path.AbsoluteHref}?log=--stat+-p+-1+-U+%H), by %an", "--", git.GetPath(path) }); return(r.Success ? UrlEncode(r.Output) : Option <string> .None); }
public async Task <IActionResult> Stats( [FromServices] IMarkdownRenderer renderer, [FromServices] IGit git, [FromQuery] string q, [FromQuery] string since, [FromQuery] string until, [FromQuery] string author ) { var logArgs = Utils.SplitArguments(q).Concat(new[] { Param(nameof(since), since), Param(nameof(until), until), Param(nameof(author), author) }.WhereValue()).ToArray(); var c = git.GetChanges(logArgs); var byName = c.GroupBy(_ => _.Commit.author.Email) .Select(_ => new { User = _.Key, AddedLines = _.Sum(_c => _c.Stats.AddedLines), RemovedLines = _.Sum(_c => _c.Stats.RemovedLines) }) .OrderByDescending(_ => _.AddedLines + _.RemovedLines) .ToList(); var byPath = c .SelectMany(_ => ContentPath.FromUrlPath(_.Stats.Path).Lineage .Select(dir => new { Path = dir, Change = _ })) .GroupBy(_ => _.Path) .Select(_ => new { Path = _.Key, AddedLines = _.Sum(_c => _c.Change.Stats.AddedLines), RemovedLines = _.Sum(_c => _c.Change.Stats.RemovedLines) }) .OrderByDescending(_ => _.AddedLines) .Take(100) .ToList(); var markdown = $@" <form action=""/stats"" method=""get"" > <input name=""since"" placeholder=""since yyyy-mm-dd"" value={Attribute(since)} /> <input name=""until"" placeholder=""until yyyy-mm-dd"" value={Attribute(until)} /> <input name=""author"" placeholder=""author"" value={Attribute(author)} /> <input type=""submit"" /> </form> ## By User {byName.MarkdownTable() .With("User", _ => $"[{_.User}](/stats?author={_.User})") .With("Added Lines", _ => _.AddedLines) .With("Removed Lines", _ => _.RemovedLines) } ## By Path {byPath.MarkdownTable() .With("Path", _ => $"[{_.Path}](/{_.Path})") .With("Added Lines", _ => _.AddedLines) .With("Removed Lines", _ => _.RemovedLines) } "; return(await MarkdownView(renderer, new ContentPath(), markdown)); }
private static async Task NotifyContentChanged(Microsoft.AspNetCore.Http.WebSocketManager webSockets, IContentProvider contentProvider, ContentPath contentPath) { var webSocket = await webSockets.AcceptWebSocketAsync(); using (contentProvider.NotifyChange(contentPath, cp => { webSocket.SendAsync(UTF8Encoding.UTF8.GetBytes(cp.ToString()), WebSocketMessageType.Text, true, CancellationToken.None); })) { await WaitForClose(webSocket); } }
public static bool IsImage(this ContentPath cp) { return(cp.IsExtension(new[] { ".png", ".svg", ".jpg", ".jpeg" })); }
Option <string> GetSourceAsMarkdown(IContentProvider contentProvider, IHistory history, ContentPath path) { return(string.Join(hr, new[] { GetText(contentProvider, path).Select(_ => $@"```{path.ExtensionWithoutDot.ValueOr(String.Empty)} {_} ```" ), history.GetRecentChanges(path).Result }.WhereValue())); }
static string GetMarkdown(IContentProvider content, IHistory history, ContentPath path) { return(String.Join(hr, new[] { GetText(content, path), history.GetRecentChanges(path).Result }.WhereValue())); }
static string GetDirectoryMarkdown(IContentProvider provider, IHistory history, ContentPath path, IEnumerable <ContentPath> children) { return(String.Join(hr, new[] { GetChildList(path, children), GetDirectoryReadme(provider, path), history.GetRecentChanges(path).Result }.WhereValue())); }
public static string GetPath(this IGit git, ContentPath path) { return(path.IsRoot ? "." : String.Join("/", path.Parts)); }
// GET: /<controller>/ public async Task <IActionResult> Index( [FromServices] IContentProvider contentProvider, [FromServices] IMarkdownRenderer renderer, [FromServices] IHistory history, [FromServices] IGit git, [FromQuery] string log, [FromQuery] string q, string path) { var contentPath = ContentPath.FromUrlPath(path); if (this.HttpContext.WebSockets.IsWebSocketRequest) { await NotifyContentChanged(this.HttpContext.WebSockets, contentProvider, contentPath); return(Ok()); } if (log != null) { var r = await git.Run(new[] { "log" }.Concat(Utils.SplitArguments(log)).Concat(new[] { "--", git.GetPath(contentPath) })); return(await MarkdownView(renderer, contentPath, AsCode(r.Output))); } if (q != null) { var pretty = $"--pretty=format:* [%ar: %s]({git.GetPath(contentPath)}?log=--stat+-p+-1+-U+%H), by %an"; var grepLog = await git.Run(new[] { "log", pretty, "-100", "-S", q, git.GetPath(contentPath) }); var grepText = git.GrepOutputToMarkdown((await git.Run(new[] { "grep", "-I", "-n", q })).Output); return(await MarkdownView(renderer, contentPath, grepLog.Output + hr + grepText)); } var children = contentProvider.GetChildren(contentPath); if (children.Any()) { if (!this.HttpContext.Request.Path.Value.EndsWith("/")) { var url = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetEncodedUrl(this.HttpContext.Request); var redirectTo = url + "/"; return(this.Redirect(redirectTo)); } return(await MarkdownView(renderer, contentPath, GetDirectoryMarkdown(contentProvider, history, contentPath, children))); } if (contentPath.IsExtension(new[] { ".md" })) { return(await MarkdownView(renderer, contentPath, GetMarkdown(contentProvider, history, contentPath))); } var mdFile = contentPath.CatName(".md"); if (mdFile.HasValue && contentProvider.Exists(mdFile.Value)) { return(await MarkdownView(renderer, contentPath, GetText(contentProvider, mdFile.Value))); } if (IsText(contentProvider, contentPath)) { return(await MarkdownView(renderer, contentPath, GetSourceAsMarkdown(contentProvider, history, contentPath))); } contentProvider.Pull(); // raw file return(await Raw(contentProvider, path)); }