CS Nuggets

This blog is for addons, bugs, fixes, and issues pertaining to Community Server.

November 2006 - Posts

CS: BUG FIX: BBCodes and Emoticons

As many of you may have noticed, there are some issues with the proper rendering of BBCodes and Emoticons.  This bug fix covers:

  1. Implementation of the missing [code] BBCode.
    1. A "file" attribute was added for specifying a file name.
  2. Implementation of a new [note] BBCode that works just like the [quote] BBCode, but does not display a header image.
    1. It has a "header" attribute to allow for bolded header text.
  3. Implmentation of a new [] BBCode that will mainly be used by developers.

CommunityServerComponents(20)/Components/Transforms.cs:

Change the following method which begins around line 44 from:

public static string FormatPostText(string rawPostBody, PostContentType postType, bool allowCustomTransforms)
{
    string formattedPost = rawPostBody;

    // because of the way we receive the post from MSHTML, somethings can already be encoded. This breaks the regex patterns
    // so we're unencoding anything that is already encoded so regex patterns work correctly. Later we'll encode everything again.

    //SCOTTW: Why? Encoded markup should stay encoded...otherwise it will not render
    //formattedPost = HttpUtility.HtmlDecode( formattedPost );

    if (allowCustomTransforms)
    {
        // must do emoteicons first because they are restricted from finding any inside [code][/code] blocks. We have to do
        // this before the later steps remove the [code][/code] blocks.
        if (CSContext.Current.SiteSettings.EnableEmoticons)
        {
            formattedPost = EmoticonTransforms(formattedPost);
        }
    }

    // convert any html inside code/pre blocks into encoded html
    formattedPost = EncodeCodeBlocks(formattedPost);

    // strip the html posts of any offending html tags and encode any that don't meet the criteria.
    formattedPost = HtmlScrubber.Clean(formattedPost, false, true);//  ScrubHtml( formattedPost, postType );

    // convert all html tags to bbcode as the bbcode processor will only allow safe attributes to be included in the ponst.
    //   formattedPost = HtmlToBBCode( formattedPost );

    // Remove any script code
    //
    //   formattedPost = Transforms.StripScriptTags(formattedPost);

    // Peform specialized transforms first.
    //

    // Do BBCode transform, if any
    //
    formattedPost = BBcodeToHtml(formattedPost);

    // Perform HTML Encoding of any tag elements, if not PostType.HTML
    //
    if (postType != PostContentType.HTML)
    { //} && postType != PostContentType.Poll) {
        formattedPost = HttpUtility.HtmlEncode(formattedPost);
        formattedPost = formattedPost.Replace("\n", Globals.HtmlNewLine);

        // Fix to reverse certain items that were HtmlEncoded above
        //
        formattedPost = UnHtmlEncode(formattedPost);
    }

    return formattedPost;
}

to:

public static string FormatPostText(string rawPostBody, PostContentType postType, bool allowCustomTransforms)
{
    string formattedPost = rawPostBody;

    // because of the way we receive the post from MSHTML, somethings can already be encoded. This breaks the regex patterns
    // so we're unencoding anything that is already encoded so regex patterns work correctly. Later we'll encode everything again.

    //SCOTTW: Why? Encoded markup should stay encoded...otherwise it will not render
    //formattedPost = HttpUtility.HtmlDecode( formattedPost );

    // convert any html inside code/pre blocks into encoded html
    formattedPost = EncodeCodeBlocks(formattedPost);

    // strip the html posts of any offending html tags and encode any that don't meet the criteria.
    formattedPost = HtmlScrubber.Clean(formattedPost, false, true);//  ScrubHtml( formattedPost, postType );

    // convert all html tags to bbcode as the bbcode processor will only allow safe attributes to be included in the ponst.
    //   formattedPost = HtmlToBBCode( formattedPost );

    // Remove any script code
    //
    //   formattedPost = Transforms.StripScriptTags(formattedPost);

    // Peform specialized transforms first.
    //

    // Do BBCode transform, if any
    //
    formattedPost = BBcodeToHtml(formattedPost);

    if (allowCustomTransforms)
    {
        // must do emoteicons first because they are restricted from finding any inside [code][/code] blocks. We have to do
        // this before the later steps remove the [code][/code] blocks.
        if (CSContext.Current.SiteSettings.EnableEmoticons)
        {
            formattedPost = EmoticonTransforms(formattedPost);
        }
    }

    // Perform HTML Encoding of any tag elements, if not PostType.HTML
    //
    if (postType != PostContentType.HTML)
    { //} && postType != PostContentType.Poll) {
        formattedPost = HttpUtility.HtmlEncode(formattedPost);
        formattedPost = formattedPost.Replace("\n", Globals.HtmlNewLine);

        // Fix to reverse certain items that were HtmlEncoded above
        //
        formattedPost = UnHtmlEncode(formattedPost);
    }

    return formattedPost;
}

Change the following method which begins around line 164 from:

// *********************************************************************
//  BBDecode
//
/// <summary>
/// Transforms a BBCode encoded string in appropriate HTML
/// </summary>
///
// ********************************************************************/
public static string BBcodeToHtml(string encodedString)
{
    // TDD TODO this shouldn't be hard coded, should be in a style sheet
    //

    // Used for normal quoting with a "<pic> <username> wrote:" prefix.
    //
    string quoteStartHtml = "";
    string quoteEndHtml = "</div></BLOCKQUOTE>";
    //quoteStartHtml = "<BLOCKQUOTE><table width=\"85%\"><tr><td class=\"txt4\"><img src=\"" + Globals.GetSkinPath() +"/images/icon-quote.gif" + "\">&nbsp;<strong>$1 wrote:</strong></td></tr><tr><td class=\"quoteTable\"><table width=\"100%\"><tr><td width=\"100%\" valign=\"top\" class=\"txt4\">$3</td></tr></table></td></tr></table></BLOCKQUOTE>";

    // Used for when a username is not supplied.
    //
    string emptyquoteStartHtml = "<BLOCKQUOTE><div>";
    string emptyquoteEndHtml = "</div></BLOCKQUOTE>";
    //string emptyquoteStartHtml = "<BLOCKQUOTE><table width=\"85%\"><tr><td class=\"quoteTable\"><table width=\"100%\"><tr><td width=\"100%\" valign=\"top\" class=\"txt4\">$2</td></tr></table></td></tr></table></BLOCKQUOTE>";

    // When using the Importer, we do not have a skin path.  We hardcode it here to
    // the default path.
    //
    if (Globals.GetSkinPath() == "")
        quoteStartHtml = "<BLOCKQUOTE><div><img src=\"themes/default/images/icon-quote.gif" + "\"> <strong>$1:</strong></div><div>";
    else
        quoteStartHtml = "<BLOCKQUOTE><div><img src=\"" + Globals.GetSkinPath() + "/images/icon-quote.gif" + "\"> <strong>$1:</strong></div><div>";

    RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Compiled;

    // Bold, Italic, Underline
    //
    encodedString = Regex.Replace(encodedString, @"\[b(?:\s*)\]((.|\n)*?)\[/b(?:\s*)\]", "<b>$1</b>", options);
    encodedString = Regex.Replace(encodedString, @"\[i(?:\s*)\]((.|\n)*?)\[/i(?:\s*)\]", "<i>$1</i>", options);
    encodedString = Regex.Replace(encodedString, @"\[u(?:\s*)\]((.|\n)*?)\[/u(?:\s*)\]", "<u>$1</u>", options);

    // Left, Right, Center
    encodedString = Regex.Replace(encodedString, @"\[left(?:\s*)\]((.|\n)*?)\[/left(?:\s*)]", "<div style=\"text-align:left\">$1</div>", options);
    encodedString = Regex.Replace(encodedString, @"\[center(?:\s*)\]((.|\n)*?)\[/center(?:\s*)]", "<div style=\"text-align:center\">$1</div>", options);
    encodedString = Regex.Replace(encodedString, @"\[right(?:\s*)\]((.|\n)*?)\[/right(?:\s*)]", "<div style=\"text-align:right\">$1</div>", options);

    // Quote
    //
    //encodedString = Regex.Replace(encodedString, "\\[quote(?:\\s*)user=\"((.|\n)*?)\"\\]((.|\n)*?)\\[/quote(\\s*)\\]", quote, options);
    //encodedString = Regex.Replace(encodedString, "\\[quote(\\s*)\\]((.|\n)*?)\\[/quote(\\s*)\\]", emptyquote, options);
    encodedString = Regex.Replace(encodedString, "\\[quote(?:\\s*)user=(?:\"|&quot;|&#34;)(.*?)(?:\"|&quot;|&#34;)\\]", quoteStartHtml, options);
    encodedString = Regex.Replace(encodedString, "\\[/quote(\\s*)\\]", quoteEndHtml, options);
    encodedString = Regex.Replace(encodedString, "\\[quote(\\s*)\\]", emptyquoteStartHtml, options);
    encodedString = Regex.Replace(encodedString, "\\[/quote(\\s*)\\]", emptyquoteEndHtml, options);


    // Anchors
    //
    encodedString = Regex.Replace(encodedString, @"\[url(?:\s*)\]www\.(.*?)\[/url(?:\s*)\]", "<a href=\"http://www.$1\" target=\"_blank\" title=\"$1\">$1</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[url(?:\s*)\]((.|\n)*?)\[/url(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$1</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[url=(?:""|&quot;|&#34;)((.|\n)*?)(?:\s*)(?:""|&quot;|&#34;)\]((.|\n)*?)\[/url(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$3</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[url=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/url(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$3</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[link(?:\s*)\]((.|\n)*?)\[/link(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$1</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[link=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/link(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$3</a>", options);

    // Image
    //
    encodedString = Regex.Replace(encodedString, @"\[img(?:\s*)\]((.|\n)*?)\[/img(?:\s*)\]", "<img src=\"$1\" border=\"0\" />", options);
    encodedString = Regex.Replace(encodedString, @"\[img=((.|\n)*?)x((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/img(?:\s*)\]", "<img width=\"$1\" height=\"$3\" src=\"$5\" border=\"0\" />", options);

    // Color
    //
    encodedString = Regex.Replace(encodedString, @"\[color=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/color(?:\s*)\]", "<span style=\"color=$1;\">$3</span>", options);

    // Horizontal Rule
    //
    encodedString = Regex.Replace(encodedString, @"\[hr(?:\s*)\]", "<hr />", options);

    // Email
    //
    encodedString = Regex.Replace(encodedString, @"\[email(?:\s*)\]((.|\n)*?)\[/email(?:\s*)\]", "<a href=\"mailto:$1\">$1</a>", options);

    // Font size
    //
    encodedString = Regex.Replace(encodedString, @"\[size=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/size(?:\s*)\]", "<span style=\"font-size:$1\">$3</span>", options);
    encodedString = Regex.Replace(encodedString, @"\[font=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/font(?:\s*)\]", "<span style=\"font-family:$1;\">$3</span>", options);
    encodedString = Regex.Replace(encodedString, @"\[align=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/align(?:\s*)\]", "<div style=\"text-align:$1;\">$3</span>", options);
    encodedString = Regex.Replace(encodedString, @"\[float=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/float(?:\s*)\]", "<div style=\"float:$1;\">$3</div>", options);

    string sListFormat = "<ol class=\"anf_list\" style=\"list-style:{0};\">$1</ol>";
    // Lists
    encodedString = Regex.Replace(encodedString, @"\[\*(?:\s*)]\s*([^\[*)", "<li>$1</li>", options);
    encodedString = Regex.Replace(encodedString, @"\[list(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", "<ul class=\"anf_list\">$1</ul>", options);
    encodedString = Regex.Replace(encodedString, @"\[list=1(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "decimal"), options);
    encodedString = Regex.Replace(encodedString, @"\[list=i(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "lower-roman"), RegexOptions.Compiled);
    encodedString = Regex.Replace(encodedString, @"\[list=I(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "upper-roman"), RegexOptions.Compiled);
    encodedString = Regex.Replace(encodedString, @"\[list=a(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "lower-alpha"), RegexOptions.Compiled);
    encodedString = Regex.Replace(encodedString, @"\[list=A(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "upper-alpha"), RegexOptions.Compiled);

    return encodedString;
}

to:

// *********************************************************************
//  BBDecode
//
/// <summary>
/// Transforms a BBCode encoded string in appropriate HTML
/// </summary>
///
// ********************************************************************/
public static string BBcodeToHtml(string encodedString)
{
    // TDD TODO this shouldn't be hard coded, should be in a style sheet
    //

    // Used for normal quoting with a "<pic> <username> wrote:" prefix.
    //
    string skinPath;

    // When using the Importer, we do not have a skin path.  We hardcode it here to
    // the default path.
    //
    skinPath = Globals.GetSkinPath();

    if (skinPath == null || skinPath == string.Empty)
    {
        skinPath = Globals.ApplicationPath;

        if (skinPath.Equals("/"))
            skinPath = string.Empty;
    }

    RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Compiled;

    // Bold, Italic, Underline
    //
    encodedString = Regex.Replace(encodedString, @"\[b(?:\s*)\]((.|\n)*?)\[/b(?:\s*)\]", "<b>$1</b>", options);
    encodedString = Regex.Replace(encodedString, @"\[i(?:\s*)\]((.|\n)*?)\[/i(?:\s*)\]", "<i>$1</i>", options);
    encodedString = Regex.Replace(encodedString, @"\[u(?:\s*)\]((.|\n)*?)\[/u(?:\s*)\]", "<u>$1</u>", options);

    // Left, Right, Center
    encodedString = Regex.Replace(encodedString, @"\[left(?:\s*)\]((.|\n)*?)\[/left(?:\s*)]", "<div style=\"text-align:left\">$1</div>", options);
    encodedString = Regex.Replace(encodedString, @"\[center(?:\s*)\]((.|\n)*?)\[/center(?:\s*)]", "<div style=\"text-align:center\">$1</div>", options);
    encodedString = Regex.Replace(encodedString, @"\[right(?:\s*)\]((.|\n)*?)\[/right(?:\s*)]", "<div style=\"text-align:right\">$1</div>", options);

    // Quote
    //
    encodedString = Regex.Replace(encodedString, @"\[quote(?:\s*)user=(?:""|&quot;|&#34;)(.*?)(?:""|&quot;|&#34;)\]", string.Format("<blockquote><div><img src=\"{0}/images/icon-quote.gif\">&nbsp;<strong>$1:</strong></div><br /><div>", skinPath), options);
    encodedString = Regex.Replace(encodedString, @"\[quote(\s*)\]", "<blockquote><div>", options);
    encodedString = Regex.Replace(encodedString, @"\[/quote(\s*)\]", "</div></blockquote>", options);

    // Note
    //
    encodedString = Regex.Replace(encodedString, @"\[note(?:\s*)header=(?:""|&quot;|&#34;)(.*?)(?:""|&quot;|&#34;)\]", "<blockquote><div><strong>$1:</strong></div><br /><div>", options);
    encodedString = Regex.Replace(encodedString, @"\[note(\s*)\]", "<blockquote><div>", options);
    encodedString = Regex.Replace(encodedString, @"\[/note(\s*)\]", "</div></blockquote>", options);

    // Code
    //
    encodedString = Regex.Replace(encodedString, @"\[code(?:\s*)file=(?:""|&quot;|&#34;)(.*?)(?:""|&quot;|&#34;)\]", "<blockquote style=\"overflow-x: scroll;\"><div><strong>$1:</strong></div><br /><pre style=\"margin: 0px;\">", options);
    encodedString = Regex.Replace(encodedString, @"\[code(?:\s*)\]", "<blockquote style=\"overflow-x: scroll;\"><pre style=\"margin: 0px;\">", options);
    encodedString = Regex.Replace(encodedString, @"\[/code(?:\s*)\]", "</pre></blockquote>", options);

    // Anchors
    //
    encodedString = Regex.Replace(encodedString, @"\[url(?:\s*)\]www\.(.*?)\[/url(?:\s*)\]", "<a href=\"http://www.$1\" target=\"_blank\" title=\"$1\">$1</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[url(?:\s*)\]((.|\n)*?)\[/url(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$1</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[url=(?:""|&quot;|&#34;)((.|\n)*?)(?:\s*)(?:""|&quot;|&#34;)\]((.|\n)*?)\[/url(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$3</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[url=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/url(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$3</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[link(?:\s*)\]((.|\n)*?)\[/link(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$1</a>", options);
    encodedString = Regex.Replace(encodedString, @"\[link=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/link(?:\s*)\]", "<a href=\"$1\" target=\"_blank\" title=\"$1\">$3</a>", options);

    // Image
    //
    encodedString = Regex.Replace(encodedString, @"\[img(?:\s*)\]((.|\n)*?)\[/img(?:\s*)\]", "<img src=\"$1\" border=\"0\" />", options);
    encodedString = Regex.Replace(encodedString, @"\[img=((.|\n)*?)x((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/img(?:\s*)\]", "<img width=\"$1\" height=\"$3\" src=\"$5\" border=\"0\" />", options);

    // Color
    //
    encodedString = Regex.Replace(encodedString, @"\[color=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/color(?:\s*)\]", "<span style=\"color:$1;\">$3</span>", options);

    // Horizontal Rule
    //
    encodedString = Regex.Replace(encodedString, @"\[hr(?:\s*)\]", "<hr />", options);

    // Email
    //
    encodedString = Regex.Replace(encodedString, @"\[email(?:\s*)\]((.|\n)*?)\[/email(?:\s*)\]", "<a href=\"mailto:$1\">$1</a>", options);

    // Font size
    //
    encodedString = Regex.Replace(encodedString, @"\[size=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/size(?:\s*)\]", "<span style=\"font-size:$1\">$3</span>", options);
    encodedString = Regex.Replace(encodedString, @"\[font=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/font(?:\s*)\]", "<span style=\"font-family:$1;\">$3</span>", options);
    encodedString = Regex.Replace(encodedString, @"\[align=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/align(?:\s*)\]", "<div style=\"text-align:$1;\">$3</span>", options);
    encodedString = Regex.Replace(encodedString, @"\[float=((.|\n)*?)(?:\s*)\]((.|\n)*?)\[/float(?:\s*)\]", "<div style=\"float:$1;\">$3</div>", options);

    // Lists
    string sListFormat = "<ol class=\"anf_list\" style=\"list-style:{0};\">$1</ol>";
    encodedString = Regex.Replace(encodedString, @"\[\*(?:\s*)]\s*([^\[*)", "<li>$1</li>", options);
    encodedString = Regex.Replace(encodedString, @"\[list(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", "<ul class=\"anf_list\">$1</ul>", options);
    encodedString = Regex.Replace(encodedString, @"\[list=1(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "decimal"), options);
    encodedString = Regex.Replace(encodedString, @"\[list=i(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "lower-roman"), RegexOptions.Compiled);
    encodedString = Regex.Replace(encodedString, @"\[list=I(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "upper-roman"), RegexOptions.Compiled);
    encodedString = Regex.Replace(encodedString, @"\[list=a(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "lower-alpha"), RegexOptions.Compiled);
    encodedString = Regex.Replace(encodedString, @"\[list=A(?:\s*)\]((.|\n)*?)\[/list(?:\s*)\]", string.Format(sListFormat, "upper-alpha"), RegexOptions.Compiled);

    // New code for developers
    encodedString = Regex.Replace(encodedString, @"\[\]", "[", options);

    return encodedString;
}

communityserver.config:

The final part of this bug fix requires a change in the order of lines in the <CSModules> section of your communityserver.config file.  To do this, find the following lines near the end of the file:

<add name = "AutoApproveForumModule" type="CommunityServer.Discussions.Components.AutoApproveForumModule, CommunityServer.Discussions" />
<add name = "BBcodeToHtml" type = "CommunityServer.Discussions.Components.BBcodeToHtmlModule, CommunityServer.Discussions" />
<add name = "IrcCommands" type = "CommunityServer.Discussions.Components.IrcCommandsModule, CommunityServer.Discussions" />
<add name = "ForumCensorship" type = "CommunityServer.Discussions.Components.CensorshipModule, CommunityServer.Discussions" />
<add name = "ForumEmoticon" type = "CommunityServer.Discussions.Components.EmoticonModule, CommunityServer.Discussions" />
<add name = "ForumSourceCode" type = "CommunityServer.Discussions.Components.SourceCodeModule, CommunityServer.Discussions" />
<add name = "ForumPlainText" type = "CommunityServer.Discussions.Components.PlainTextModule, CommunityServer.Discussions" />
<add name = "ForumHtmlScrubbing" type = "CommunityServer.Discussions.Components.HtmlScrubbingModule, CommunityServer.Discussions" />
<add name = "ForumCollapse" type = "CommunityServer.Discussions.Components.CollapseModule, CommunityServer.Discussions" />

and change the order to:

<add name = "AutoApproveForumModule" type="CommunityServer.Discussions.Components.AutoApproveForumModule, CommunityServer.Discussions" />
<add name = "IrcCommands" type = "CommunityServer.Discussions.Components.IrcCommandsModule, CommunityServer.Discussions" />
<add name = "ForumCensorship" type = "CommunityServer.Discussions.Components.CensorshipModule, CommunityServer.Discussions" />
<add name = "ForumSourceCode" type = "CommunityServer.Discussions.Components.SourceCodeModule, CommunityServer.Discussions" />
<add name = "ForumPlainText" type = "CommunityServer.Discussions.Components.PlainTextModule, CommunityServer.Discussions" />
<add name = "ForumHtmlScrubbing" type = "CommunityServer.Discussions.Components.HtmlScrubbingModule, CommunityServer.Discussions" />
<add name = "BBcodeToHtml" type = "CommunityServer.Discussions.Components.BBcodeToHtmlModule, CommunityServer.Discussions" />
<add name = "ForumEmoticon" type = "CommunityServer.Discussions.Components.EmoticonModule, CommunityServer.Discussions" />
<add name = "ForumCollapse" type = "CommunityServer.Discussions.Components.CollapseModule, CommunityServer.Discussions" />

 

To use the new code for developers, you just type a closing bracket immediately after the opening bracket.  For example, entering:

[]b]This is how you bold text[]/b]

in your post, will display:

[b]This is how you bold text[/b]

which in turn would display the following when used in a post:

This is how you bold text

If you have any questions or comments, please post them here.

CS: Deleting Forum Posts from the Database

When you delete a forum post, it doesn't actually get deleted from the database.  It gets moved to the "Deleted Posts" forum in the "Administrators" group.  By default, this forum will hold posts until they have not been viewed for 90 days and then the "Autodelete" feature physically removes the post from the database.  If you have a very large community, you may want to enable the "Autodelete" feature on each of your forums.

To do this, set the "Enable Post Autodelete" and "Auto-Delete Time Window" settings for each forum.  By default, the "Enable Post Autodelete" setting is turned off and "Auto-Delete Time Window" is set to 90 days.  If you turn it on, it will automatically delete posts from the database that have not been viewed within the past 90 days.  Setting the "Auto-Delete Time Window" to 0 will cause the post to be deleted from the database immediately after it is added/moved to that forum.

Here is a little trick you can use too.  Create a new forum in the "Administrators" group, name it something like "DELETE PERMANENTLY", restrict the rights on that forum to yourself and anyone else you trust, set "Enable Post Autodelete" to "Yes", and set "Auto-Delete Time Window" to either 0 (deletes immediately) or 1 (deletes after 1 day).  Move the posts that you want permanently deleted into this forum.  If you set it to 0 days, the post will be deleted immediately.  If you set it to 1 day, it will be deleted the next day.