public static MarkdownItemTestSubclass New(IWikiOptions options, IDataStore dataStore, string?markdown) { var md = string.IsNullOrEmpty(markdown) ? null : TransclusionParser.Transclude( options, dataStore, null, null, markdown, out _); var wikiLinks = GetWikiLinks(options, dataStore, md); return(new MarkdownItemTestSubclass( md, RenderHtml(options, dataStore, md), RenderPreview( options, dataStore, string.IsNullOrEmpty(markdown) ? string.Empty : TransclusionParser.Transclude( options, dataStore, null, null, markdown, out _, isPreview: true)), wikiLinks)); }
/// <summary> /// Revises this <see cref="WikiFile"/> instance. /// </summary> /// <param name="options">An <see cref="IWikiOptions"/> instance.</param> /// <param name="dataStore">An <see cref="IDataStore"/> instance.</param> /// <param name="editor"> /// The ID of the user who made this revision. /// </param> /// <param name="title"> /// <para> /// The optional new title of the file. Must be unique within its namespace, and non-empty. /// </para> /// <para> /// If left <see langword="null"/> the existing title will be retained. /// </para> /// </param> /// <param name="path">The relative path to the file.</param> /// <param name="fileSize">The size of the file, in bytes.</param> /// <param name="type"> /// <para> /// The MIME type of the file. /// </para> /// <para> /// If left <see langword="null"/> the existing type will be retained. /// </para> /// </param> /// <param name="markdown">The markdown.</param> /// <param name="revisionComment"> /// An optional comment supplied for this revision (e.g. to explain the changes). /// </param> /// <param name="isDeleted">Indicates that this file has been marked as deleted.</param> /// <param name="owner"> /// <para> /// The new owner of the file. /// </para> /// <para> /// May be a user, a group, or <see langword="null"/>. /// </para> /// </param> /// <param name="allowedEditors"> /// <para> /// The user(s) and/or group(s) allowed to edit this file. /// </para> /// <para> /// If <see langword="null"/> the file can be edited by anyone. /// </para> /// <para> /// If non-<see langword="null"/> the file can only be edited by those listed, plus its /// owner (regardless of whether the owner is explicitly listed). An empty (but non-<see /// langword="null"/>) list allows only the owner to make edits. /// </para> /// </param> /// <param name="allowedViewers"> /// <para> /// The user(s) and/or group(s) allowed to view this file. /// </para> /// <para> /// If <see langword="null"/> the file can be viewed by anyone. /// </para> /// <para> /// If non-<see langword="null"/> the file can only be viewed by those listed, plus its /// owner (regardless of whether the owner is explicitly listed). An empty (but non-<see /// langword="null"/>) list allows only the owner to view the file. /// </para> /// </param> public async Task ReviseAsync( IWikiOptions options, IDataStore dataStore, string editor, string?title = null, string?path = null, int?fileSize = null, string?type = null, string?markdown = null, string?revisionComment = null, bool isDeleted = false, string?owner = null, IEnumerable <string>?allowedEditors = null, IEnumerable <string>?allowedViewers = null) { title ??= title?.ToWikiTitleCase() ?? Title; var newFile = false; if (!string.IsNullOrWhiteSpace(path)) { newFile = path != FilePath; FilePath = path; } if (fileSize.HasValue) { newFile |= fileSize.Value != FileSize; FileSize = fileSize.Value; } if (!string.IsNullOrEmpty(type)) { newFile |= type != FileType; FileType = type; } if (newFile) { Uploader = editor; } var previousTitle = Title; Title = title; var sameTitle = string.Equals(previousTitle, title, StringComparison.Ordinal); var previousMarkdown = MarkdownContent; var wasDeleted = IsDeleted || string.IsNullOrWhiteSpace(previousMarkdown); if (isDeleted || string.IsNullOrWhiteSpace(markdown)) { if (!wasDeleted) { Html = string.Empty; IsDeleted = true; MarkdownContent = string.Empty; Preview = string.Empty; Categories = new List <string>().AsReadOnly(); if (Transclusions is not null) { await RemovePageTransclusionsAsync(dataStore, Id, Transclusions) .ConfigureAwait(false); } await RemovePageLinksAsync(dataStore, Id, WikiLinks) .ConfigureAwait(false); Transclusions = null; WikiLinks = new List <WikiLink>().AsReadOnly(); } } else { IsDeleted = false; } if (!sameTitle && !IsDeleted) { await CreatePageReferenceAsync(dataStore, Id, title, options.FileNamespace) .ConfigureAwait(false); } if (!sameTitle) { await RemovePageReferenceAsync(dataStore, Id, previousTitle, options.FileNamespace) .ConfigureAwait(false); } var changed = wasDeleted != IsDeleted || !string.Equals(previousMarkdown, markdown, StringComparison.Ordinal); if (!IsDeleted && changed) { MarkdownContent = markdown !; var previousTransclusions = Transclusions?.ToList() ?? new List <Transclusion>(); var md = TransclusionParser.Transclude( options, dataStore, title, $"{options.FileNamespace}:{title}", markdown !, out var transclusions); Transclusions = transclusions.Count == 0 ? null : transclusions.AsReadOnly(); await RemovePageTransclusionsAsync(dataStore, Id, previousTransclusions.Except(transclusions)) .ConfigureAwait(false); await AddPageTransclusionsAsync(dataStore, Id, transclusions.Except(previousTransclusions)) .ConfigureAwait(false); var previousWikiLinks = WikiLinks.ToList(); WikiLinks = GetWikiLinks(options, dataStore, md, title, options.FileNamespace).AsReadOnly(); await RemovePageLinksAsync(dataStore, Id, previousWikiLinks.Except(WikiLinks)) .ConfigureAwait(false); await AddPageLinksAsync(dataStore, Id, WikiLinks.Except(previousWikiLinks)) .ConfigureAwait(false); } if (changed) { Categories = (await UpdateCategoriesAsync( options, dataStore, Id, editor, owner, allowedEditors, allowedViewers, WikiLinks, Categories) .ConfigureAwait(false)) .AsReadOnly(); Update(options, dataStore); } var oldOwner = Owner; Owner = owner; AllowedEditors = allowedEditors?.ToList().AsReadOnly(); AllowedViewers = allowedViewers?.ToList().AsReadOnly(); var revision = new Revision( Id, editor, title, options.FileNamespace, previousMarkdown, MarkdownContent, revisionComment); await dataStore.StoreItemAsync(revision).ConfigureAwait(false); TimestampTicks = revision.TimestampTicks; await dataStore.StoreItemAsync(this).ConfigureAwait(false); await UpdateReferencesAsync( options, dataStore, title, options.FileNamespace, IsDeleted, sameTitle, previousTitle, options.FileNamespace, false, false) .ConfigureAwait(false); if (isDeleted && !wasDeleted) { if (options.OnDeleted is not null) { await options.OnDeleted(this, oldOwner, Owner).ConfigureAwait(false); } else if (options.OnEdited is not null) { await options.OnEdited(this, revision, oldOwner, Owner).ConfigureAwait(false); } } else if (options.OnEdited is not null) { await options.OnEdited(this, revision, oldOwner, Owner).ConfigureAwait(false); } }
/// <summary> /// Gets a new <see cref="WikiFile"/> instance. /// </summary> /// <param name="options">An <see cref="IWikiOptions"/> instance.</param> /// <param name="dataStore">An <see cref="IDataStore"/> instance.</param> /// <param name="title">The title of the file. Must be unique and non-empty.</param> /// <param name="editor"> /// The ID of the user who created this file. /// </param> /// <param name="filePath">The relative path to the file.</param> /// <param name="fileSize">The size of the file, in bytes.</param> /// <param name="type"> /// The MIME type of the file. /// </param> /// <param name="markdown">The raw markdown content.</param> /// <param name="revisionComment"> /// An optional comment supplied for this revision (e.g. to explain the upload). /// </param> /// <param name="owner"> /// <para> /// The ID of the intended owner of the file. /// </para> /// <para> /// May be a user, a group, or <see langword="null"/>. /// </para> /// </param> /// <param name="allowedEditors"> /// <para> /// The user(s) and/or group(s) allowed to edit this file. /// </para> /// <para> /// If <see langword="null"/> the file can be edited by anyone. /// </para> /// <para> /// If non-<see langword="null"/> the file can only be edited by those listed, plus its /// owner (regardless of whether the owner is explicitly listed). An empty (but non-<see /// langword="null"/>) list allows only the owner to make edits. /// </para> /// </param> /// <param name="allowedViewers"> /// <para> /// The user(s) and/or group(s) allowed to view this file. /// </para> /// <para> /// If <see langword="null"/> the file can be viewed by anyone. /// </para> /// <para> /// If non-<see langword="null"/> the file can only be viewed by those listed, plus its /// owner (regardless of whether the owner is explicitly listed). An empty (but non-<see /// langword="null"/>) list allows only the owner to view the file. /// </para> /// </param> public static async Task <WikiFile> NewAsync( IWikiOptions options, IDataStore dataStore, string title, string editor, string filePath, int fileSize, string type, string?markdown = null, string?revisionComment = null, string?owner = null, IEnumerable <string>?allowedEditors = null, IEnumerable <string>?allowedViewers = null) { if (string.IsNullOrWhiteSpace(title)) { throw new ArgumentException($"{nameof(title)} cannot be empty.", nameof(title)); } title = title.ToWikiTitleCase(); var wikiId = dataStore.CreateNewIdFor <WikiFile>(); await CreatePageReferenceAsync(dataStore, wikiId, title, options.FileNamespace) .ConfigureAwait(false); var revision = new Revision( wikiId, editor, title, options.FileNamespace, null, markdown, revisionComment); await dataStore.StoreItemAsync(revision).ConfigureAwait(false); var md = markdown; List <Transclusion> transclusions; if (string.IsNullOrEmpty(markdown)) { transclusions = new List <Transclusion>(); } else { md = TransclusionParser.Transclude( options, dataStore, title, $"{options.FileNamespace}:{title}", markdown, out transclusions); } var wikiLinks = GetWikiLinks(options, dataStore, md, title, options.FileNamespace); var categories = await UpdateCategoriesAsync( options, dataStore, wikiId, editor, owner, allowedEditors, allowedViewers, wikiLinks) .ConfigureAwait(false); var file = new WikiFile( wikiId, title, filePath, fileSize, editor, type, markdown, RenderHtml(options, dataStore, PostprocessArticleMarkdown(options, dataStore, title, options.FileNamespace, markdown)), RenderPreview(options, dataStore, PostprocessArticleMarkdown(options, dataStore, title, options.FileNamespace, markdown, true)), new ReadOnlyCollection <WikiLink>(wikiLinks), revision.TimestampTicks, options.FileNamespace, isDeleted: false, owner, allowedEditors, allowedViewers, categories, transclusions); await dataStore.StoreItemAsync(file).ConfigureAwait(false); await AddPageTransclusionsAsync(dataStore, wikiId, transclusions) .ConfigureAwait(false); await AddPageLinksAsync(dataStore, wikiId, wikiLinks) .ConfigureAwait(false); await UpdateReferencesAsync( options, dataStore, title, options.FileNamespace, false, true, null, null, false, false) .ConfigureAwait(false); if (options.OnCreated is not null) { await options.OnCreated.Invoke(file, editor).ConfigureAwait(false); } return(file); }