Ejemplo n.º 1
0
    internal static IHtmlSanitizer GetHtmlSanitizer(IWikiOptions options)
    {
        if (string.IsNullOrEmpty(options.LinkTemplate) &&
            _HtmlSanitizerNoTemplate is not null)
        {
            return(_HtmlSanitizerNoTemplate);
        }

        var htmlSanitizer = new HtmlSanitizer();

        htmlSanitizer.AllowedAttributes.Add("class");
        htmlSanitizer.AllowedAttributes.Add("role");
        htmlSanitizer.AllowedAttributes.Add("id");

        htmlSanitizer.RemovingAttribute += (_, e) =>
        {
            if (e.Tag.TagName == "A" && options.LinkTemplate?.Contains(e.Attribute.Name) == true)
            {
                e.Cancel = true;
            }
            e.Cancel |= e.Attribute.Name.StartsWith("data-");
        };

        if (string.IsNullOrEmpty(options.LinkTemplate))
        {
            _HtmlSanitizerNoTemplate = htmlSanitizer;
        }

        return(htmlSanitizer);
    }
Ejemplo n.º 2
0
 /// <summary>
 /// Gets the given markdown content as plain text (i.e. strips all formatting).
 /// </summary>
 /// <param name="options">An <see cref="IWikiOptions"/> instance.</param>
 /// <param name="dataStore">An <see cref="IDataStore"/> instance.</param>
 /// <param name="markdown">The markdown content.</param>
 /// <param name="characterLimit">The maximum number of characters to return.</param>
 /// <param name="singleParagraph">
 /// If true, stops after the first paragraph break, even still under the allowed character limit.
 /// </param>
 /// <returns>The plain text.</returns>
 public string GetPlainText(
     IWikiOptions options,
     IDataStore dataStore,
     string?markdown,
     int?characterLimit   = 200,
     bool singleParagraph = true)
 => FormatPlainText(options, dataStore, PostprocessMarkdown(options, dataStore, markdown), characterLimit, singleParagraph);
Ejemplo n.º 3
0
 internal static MarkdownPipeline GetMarkdownPipeline(IWikiOptions options, IDataStore dataStore) =>
 new MarkdownPipelineBuilder()
 .UseWikiLinks(options, dataStore)
 .UseAbbreviations()
 .UseAutoIdentifiers()
 .UseTableOfContents(new MarkdownExtensions.TableOfContents.TableOfContentsOptions
 {
     DefaultDepth         = options.DefaultTableOfContentsDepth,
     DefaultStartingLevel = 1,
     MinimumTopLevel      = options.MinimumTableOfContentsHeadings,
     DefaultTitle         = options.DefaultTableOfContentsTitle,
 })
 .UseCitations()
 .UseCustomContainers()
 .UseDefinitionLists()
 .UseEmphasisExtras()
 .UseFigures()
 .UseFooters()
 .UseFootnotes()
 .UseGridTables()
 .UseMathematics()
 .UsePipeTables()
 .UseListExtras()
 .UseTaskLists()
 .UseAutoLinks()
 .UseGenericAttributes()
 .UseSmartyPants()
 .Build();
Ejemplo n.º 4
0
    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));
    }
Ejemplo n.º 5
0
 /// <summary>
 /// Identifies the <see cref="WikiLink"/>s in the given <paramref name="markdown"/>.
 /// </summary>
 /// <param name="options">An <see cref="WikiOptions"/> instance.</param>
 /// <param name="dataStore">An <see cref="IDataStore"/> instance.</param>
 /// <param name="markdown">The markdown.</param>
 /// <param name="title">The title of the item.</param>
 /// <param name="wikiNamespace">The namespace of the item.</param>
 /// <returns>
 /// A <see cref="List{T}"/> of <see cref="WikiLink"/>s.
 /// </returns>
 protected static List <WikiLink> GetWikiLinks(
     IWikiOptions options,
     IDataStore dataStore,
     string?markdown,
     string?title         = null,
     string?wikiNamespace = null)
 => string.IsNullOrEmpty(markdown)
     ? new List <WikiLink>()
     : Markdown.Parse(markdown, WikiConfig.GetMarkdownPipeline(options, dataStore))
 .Descendants <WikiLinkInline>()
 .Where(x => !x.IsWikipedia &&
Ejemplo n.º 6
0
    /// <summary>
    /// Replaces all the transclusions in the given <paramref name="markdown"/> with their
    /// contents.
    /// </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 top-level article being generated.</param>
    /// <param name="fullTitle">
    /// The full title of the top-level article being generated (including namespace if the
    /// namespace is not <see cref="IWikiOptions.DefaultNamespace"/>).
    /// </param>
    /// <param name="markdown">A markdown string.</param>
    /// <param name="transcludedArticles">
    /// When this method returns, will be set to a <see cref="List{T}"/> of the full titles of
    /// all articles referenced by the transclusions within the given <paramref
    /// name="markdown"/> (including nested transclusions).
    /// </param>
    /// <param name="isTemplate">Whether the article is being rendered as a
    /// transclusion.</param>
    /// <param name="isPreview">Whether a preview is being rendered.</param>
    /// <param name="parameterValues">
    /// A collection of supplied parameter values, if the markdown is itself a transclusion.
    /// </param>
    /// <returns>The markdown with all transclusions substituted.</returns>
    public static string Transclude(
        IWikiOptions options,
        IDataStore dataStore,
        string?title,
        string?fullTitle,
        string markdown,
        out List <Wiki.Transclusion> transcludedArticles,
        bool isTemplate = false,
        bool isPreview  = false,
        Dictionary <string, string>?parameterValues = null)
    {
        transcludedArticles = new List <Wiki.Transclusion>();

        if (string.IsNullOrWhiteSpace(markdown))
        {
            return(markdown);
        }

        var parameterInclusions = new List <Transclusion>();
        var transclusions       = new List <Transclusion>();

        var lineReader = new LineReader(markdown);
        var codeFenced = false;

        while (true)
        {
            var lineText = lineReader.ReadLine();
            if (lineText.Text is null)
            {
                break;
            }

            var lineTransclusions = Parse(lineText, out var lineParameters, out var isCodeFence);

            if (isCodeFence)
            {
                codeFenced = false;
            }
            if (codeFenced)
            {
                continue;
            }

            if (parameterValues != null)
            {
                foreach (var parameter in lineParameters)
                {
                    var name = lineText.Text[(parameter.Start + 2)..(parameter.End - 2)].ToLower(CultureInfo.CurrentCulture);
Ejemplo n.º 7
0
 private static string Exec(
     IWikiOptions options,
     Dictionary <string, string> parameters,
     string?title,
     string?fullTitle,
     bool isTemplate,
     bool isPreview)
 {
     if (parameters.Count == 0 ||
         !parameters.TryGetValue("code", out var code) ||
         string.IsNullOrEmpty(code))
     {
         return(string.Empty);
     }
     parameters.Remove("code");
     return(GetScriptResult(options, code, false, parameters, title, fullTitle, isTemplate, isPreview));
 }
Ejemplo n.º 8
0
    /// <summary>
    /// Gets a preview of the given markdown's rendered HTML.
    /// </summary>
    /// <param name="options">An <see cref="IWikiOptions"/> instance.</param>
    /// <param name="dataStore">An <see cref="IDataStore"/> instance.</param>
    /// <param name="markdown">The markdown content.</param>
    /// <returns>A preview of the rendered HTML.</returns>
    public static string RenderPreview(IWikiOptions options, IDataStore dataStore, string?markdown)
    {
        if (string.IsNullOrWhiteSpace(markdown))
        {
            return(string.Empty);
        }

        var document = Markdown.Parse(markdown, WikiConfig.GetMarkdownPipeline(options, dataStore));

        if (AnyPreviews(document))
        {
            TrimNonPreview(document);
        }
        else
        {
            var minCharactersAvailable = PreviewCharacterMin;
            var maxCharactersAvailable = PreviewCharacterMax;
            Trim(document, ref minCharactersAvailable, ref maxCharactersAvailable);
        }

        string html;

        using (var writer = new StringWriter())
        {
            var renderer = new HtmlRenderer(writer);
            WikiConfig.GetMarkdownPipeline(options, dataStore).Setup(renderer);
            renderer.Render(document);
            html = writer.ToString();
        }

        if (!string.IsNullOrWhiteSpace(html) &&
            options.Postprocessors is not null)
        {
            foreach (var preprocessor in options.Postprocessors)
            {
                html = preprocessor.Process.Invoke(html);
            }
        }

        return(WikiConfig.GetHtmlSanitizer(options).Sanitize(html) ?? string.Empty);
    }
Ejemplo n.º 9
0
 internal static MarkdownPipeline GetMarkdownPipelinePlainText(IWikiOptions options, IDataStore dataStore) =>
 new MarkdownPipelineBuilder()
 .UseWikiLinks(options, dataStore)
 .UseAbbreviations()
 .UseAutoIdentifiers()
 .UseCitations()
 .UseCustomContainers()
 .UseDefinitionLists()
 .UseEmphasisExtras()
 .UseFigures()
 .UseFooters()
 .UseFootnotes()
 .UseGridTables()
 .UseMathematics()
 .UseMediaLinks()
 .UsePipeTables()
 .UseListExtras()
 .UseTaskLists()
 .UseGenericAttributes()
 .UseSmartyPants()
 .Build();
Ejemplo n.º 10
0
    /// <summary>
    /// Gets the latest revision for the file with the given title.
    /// </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 to retrieve.</param>
    /// <returns>The latest revision for the file with the given title; or <see
    /// langword="null"/> if no such file exists.</returns>
    public static WikiFile?GetFile(
        IWikiOptions options,
        IDataStore dataStore,
        string title)
    {
        WikiFile?file      = null;
        var      reference = PageReference.GetPageReference(dataStore, title, options.FileNamespace);

        if (reference is not null)
        {
            file = dataStore.GetItem <WikiFile>(reference.Reference);
        }
        // If no exact match exists, ignore case if only one such match exists.
        if (file is null)
        {
            var normalizedReference = NormalizedPageReference.GetNormalizedPageReference(dataStore, title, options.FileNamespace);
            if (normalizedReference?.References.Count == 1)
            {
                file = dataStore.GetItem <WikiFile>(normalizedReference.References[0]);
            }
        }

        return(file);
    }
Ejemplo n.º 11
0
    /// <summary>
    /// Renders the given <paramref name="markdown"/> as HTML.
    /// </summary>
    /// <param name="options">An <see cref="IWikiOptions"/> instance.</param>
    /// <param name="dataStore">An <see cref="IDataStore"/> instance.</param>
    /// <param name="markdown">The markdown content.</param>
    /// <returns>The rendered HTML.</returns>
    public static string RenderHtml(IWikiOptions options, IDataStore dataStore, string?markdown)
    {
        if (string.IsNullOrWhiteSpace(markdown))
        {
            return(string.Empty);
        }

        var html = Markdown.ToHtml(markdown, WikiConfig.GetMarkdownPipeline(options, dataStore));

        if (string.IsNullOrWhiteSpace(html))
        {
            return(string.Empty);
        }

        if (options.Postprocessors is not null)
        {
            foreach (var preprocessor in options.Postprocessors)
            {
                html = preprocessor.Process.Invoke(html);
            }
        }

        return(WikiConfig.GetHtmlSanitizer(options).Sanitize(html));
    }
Ejemplo n.º 12
0
    /// <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);
        }
    }
Ejemplo n.º 13
0
    /// <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);
    }
Ejemplo n.º 14
0
    private static string Format(IWikiOptions options, Dictionary <string, string> parameters, string?_, string?__, bool ___, bool ____)
    {
        if (!parameters.TryGetValue("1", out var first))
        {
            return(string.Empty);
        }
        string?format = null;

        if (parameters.TryGetValue("2", out var second))
        {
            format = second;
        }
        var result = string.Empty;

        if (long.TryParse(first, out var intValue))
        {
            var success = false;
            try
            {
                result  = intValue.ToString(format ?? "N0");
                success = true;
            }
            catch { }
            if (!success && format is null)
            {
                result = intValue.ToString("N0");
            }
        }
        else if (double.TryParse(first, out var floatValue))
        {
            var success = false;
            try
            {
                result  = floatValue.ToString(format ?? "N");
                success = true;
            }
            catch { }
            if (!success && format is null)
            {
                result = floatValue.ToString("N");
            }
        }
        else if (DateTimeOffset.TryParse(first, out var timestamp))
        {
            var success = false;
            try
            {
                result  = timestamp.ToString(format ?? "g");
                success = true;
            }
            catch { }
            if (!success && format is null)
            {
                result = timestamp.ToString("g");
            }
        }
        else
        {
            result = first;
        }
        return(result);
    }
Ejemplo n.º 15
0
 private static Task <Article> GetDefaultAboutAsync(IWikiOptions options, IDataStore dataStore, string adminId) => Article.NewAsync(
     options,
     dataStore,
     "About",
     adminId,
Ejemplo n.º 16
0
 /// <summary>
 /// Initializes a new instance of <see cref="WikiLinkInlineRenderer"/>.
 /// </summary>
 public WikiLinkInlineRenderer(IWikiOptions options) => Options = options;
Ejemplo n.º 17
0
 /// <summary>
 /// Gets a preview of this item's rendered HTML.
 /// </summary>
 /// <returns>A preview of this item's rendered HTML.</returns>
 public string GetPreview(IWikiOptions options, IDataStore dataStore)
 => RenderPreview(options, dataStore, PostprocessMarkdown(options, dataStore, MarkdownContent, isPreview: true));
Ejemplo n.º 18
0
    /// <summary>
    /// Gets the given markdown content as plain text (i.e. strips all formatting).
    /// </summary>
    /// <param name="options">An <see cref="IWikiOptions"/> instance.</param>
    /// <param name="dataStore">An <see cref="IDataStore"/> instance.</param>
    /// <param name="markdown">The markdown content.</param>
    /// <param name="characterLimit">The maximum number of characters to return.</param>
    /// <param name="singleParagraph">
    /// If true, stops after the first paragraph break, even still under the allowed character limit.
    /// </param>
    /// <returns>The plain text.</returns>
    public static string FormatPlainText(
        IWikiOptions options,
        IDataStore dataStore,
        string?markdown,
        int?characterLimit   = 200,
        bool singleParagraph = true)
    {
        if (string.IsNullOrEmpty(markdown))
        {
            return(string.Empty);
        }

        if (singleParagraph && markdown.Length > 1)
        {
            var paraIndex = markdown.IndexOf(Environment.NewLine + Environment.NewLine, 1);
            if (paraIndex > 0)
            {
                markdown = markdown.Substring(0, paraIndex);
            }
        }

        if (characterLimit.HasValue && markdown.Length > characterLimit.Value * 5)
        {
            markdown = markdown.Substring(0, characterLimit.Value * 5);
        }

        var html = Markdown.ToHtml(markdown, WikiConfig.GetMarkdownPipelinePlainText(options, dataStore));

        if (string.IsNullOrWhiteSpace(html))
        {
            return(string.Empty);
        }

        if (options.Postprocessors is not null)
        {
            foreach (var preprocessor in options.Postprocessors)
            {
                html = preprocessor.Process.Invoke(html);
            }
        }

        var sanitized = WikiConfig.HtmlSanitizerFull.Sanitize(html);

        if (characterLimit.HasValue && sanitized.Length > characterLimit)
        {
            var substring  = sanitized.Substring(0, characterLimit.Value);
            var i          = substring.Length - 1;
            var whitespace = false;
            for (; i > 0; i--)
            {
                if (substring[i].IsWhiteSpaceOrZero())
                {
                    whitespace = true;
                }
                else if (whitespace)
                {
                    break;
                }
            }
            sanitized = whitespace
                ? substring.Substring(0, i + 1)
                : substring;
        }
        return(sanitized);
    }
Ejemplo n.º 19
0
 /// <summary>
 /// Gets this item's content rendered as HTML.
 /// </summary>
 /// <returns>The rendered HTML.</returns>
 public string GetHtml(IWikiOptions options, IDataStore dataStore)
 => RenderHtml(options, dataStore, PostprocessMarkdown(options, dataStore, MarkdownContent));
Ejemplo n.º 20
0
 /// <summary>
 /// Gets a diff between the <see cref="MarkdownContent"/> of this item and the given one, as
 /// rendered HTML.
 /// </summary>
 /// <param name="options">An <see cref="IWikiOptions"/> instance.</param>
 /// <param name="dataStore">An <see cref="IDataStore"/> instance.</param>
 /// <param name="other">The other <see cref="MarkdownItem"/> insteance.</param>
 /// <returns>
 /// A string representing the diff between this instance and the <paramref name="other"/>
 /// instance, as rendered HTML.
 /// </returns>
 public string GetDiffHtml(IWikiOptions options, IDataStore dataStore, MarkdownItem other)
 => RenderHtml(options, dataStore, PostprocessMarkdown(options, dataStore, GetDiff(other, "html")));
Ejemplo n.º 21
0
 /// <summary>
 /// Initializes a new instance of <see cref="WikiLinkInlineParser"/>.
 /// </summary>
 public WikiLinkInlineParser(IWikiOptions options, IDataStore dataStore)
 {
     DataStore         = dataStore;
     OpeningCharacters = new[] { LinkOpenChar, LinkCloseChar, '!' };
     Options           = options;
 }
Ejemplo n.º 22
0
 /// <summary>
 /// Initializes a new instance of <see cref="WikiLinkExtension"/>.
 /// </summary>
 public WikiLinkExtension(IWikiOptions options, IDataStore dataStore)
 {
     Options   = options;
     DataStore = dataStore;
 }
Ejemplo n.º 23
0
 /// <summary>
 /// Adds wiki links.
 /// </summary>
 /// <returns>The <see cref="MarkdownPipelineBuilder"/>.</returns>
 internal static MarkdownPipelineBuilder UseWikiLinks(this MarkdownPipelineBuilder pipeline, IWikiOptions options, IDataStore dataStore)
 {
     pipeline.Extensions.ReplaceOrAdd <WikiLinkExtension>(new WikiLinkExtension(options, dataStore));
     return(pipeline);
 }