📄 transforms.cs
字号:
//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
// Copyright (c) Telligent Systems Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data.SqlClient;
using System.Web;
using System.IO;
using System.Web.Caching;
using System.Text.RegularExpressions;
using System.Text;
namespace CommunityServer.Components {
/// <summary>
/// This class is used to take the body of a post message and pre-format it to HTML. Two versions
/// of the post body are stored in the database: raw original post and the transformed pre-formatted
/// post. Pre-transforming the post offers better performance.
/// </summary>
public class Transforms {
#region FormatPost
// *********************************************************************
// FormatPost
//
/// <summary>
/// Main public routine called to perform string transforms.
/// </summary>
///
// ********************************************************************/
public static string FormatPostText (string rawPostBody) {
return FormatPostText (rawPostBody, PostType.HTML, true);
}
public static string FormatPostText (string rawPostBody, PostType postType) {
return FormatPostText (rawPostBody, postType, true);
}
public static string FormatPostText (string rawPostBody, PostType 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 != PostType.HTML && postType != PostType.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;
}
#endregion
#region CensorPost
/// <summary>
/// Validates if the supplied text would pass the censor test
/// </summary>
public static bool IsValidCensorText(string text)
{
return CensorPost(text) == text;
}
public static string CensorPost( string formattedPost ) {
if(Globals.IsNullorEmpty(formattedPost))
return formattedPost;
RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Compiled;
ArrayList censors = CommunityServer.Censors.GetCensors();
foreach( Censor censor in censors ) {
string searchPattern;
// if the string contains any regex patterns, then just use the pattern
if( -1 == censor.Word.IndexOfAny( new char[]{'\\', '[', '^', '*', '{', '.', '#', '?', '+', '$', '|' } ) ) {
searchPattern = String.Format(@"\b{0}\b", censor.Word);
}
else {
searchPattern = censor.Word;
}
formattedPost = Regex.Replace(formattedPost, searchPattern, censor.Replacement, options);
}
return formattedPost;
}
#endregion
#region UnHtmlEncode
// *******************************************************************
// UnHtmlEncode
//
/// <summary>
/// If a post has been HtmlEncoded using the Web.HttpUtility, there are
/// a few characters we don't want encoded. This will allow for the
/// bbcode transforms to properly work. So we must "UnHtmlEncode" these
/// items, back to what they originally were.
/// </summary>
/// <param name="formattedPost">string of post</param>
/// <returns>
/// An Un-HtmlEncoded String of select items.
/// </returns>
// *******************************************************************
public static string UnHtmlEncode(string formattedPost) {
RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Compiled;
// "
// TODO: edit the regex match to only replace within a set of brackets [].
//
formattedPost = Regex.Replace(formattedPost, """, "\"", options);
return formattedPost;
}
#endregion
#region BBDecode
// *********************************************************************
// 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 = "</td></tr></table></td></tr></table></BLOCKQUOTE>";
//quoteStartHtml = "<BLOCKQUOTE><table width=\"85%\"><tr><td class=\"txt4\"><img src=\"" + Globals.GetSkinPath() +"/images/icon-quote.gif" + "\"> <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><table width=\"85%\"><tr><td class=\"quoteTable\"><table width=\"100%\"><tr><td width=\"100%\" valign=\"top\" class=\"txt4\">";
string emptyquoteEndHtml = "</td></tr></table></td></tr></table></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><table width=\"85%\"><tr><td class=\"txt4\"><img src=\"themes/default/images/icon-quote.gif" + "\"> <strong>$1 wrote:</strong></td></tr><tr><td class=\"quoteTable\"><table width=\"100%\"><tr><td width=\"100%\" valign=\"top\" class=\"txt4\">";
else
quoteStartHtml = "<BLOCKQUOTE><table width=\"85%\"><tr><td class=\"txt4\"><img src=\"" + Globals.GetSkinPath() +"/images/icon-quote.gif" + "\"> <strong>$1 wrote:</strong></td></tr><tr><td class=\"quoteTable\"><table width=\"100%\"><tr><td width=\"100%\" valign=\"top\" class=\"txt4\">";
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=\"((.|\n)*?)\"\\]", 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=""((.|\n)*?)(?:\s*)""\]((.|\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;
}
#endregion
#region Strip Tags
public static string StripForPreview (string content) {
content = Regex.Replace(content, "<br>", "\n", RegexOptions.IgnoreCase | RegexOptions.Compiled);
content = Regex.Replace(content, "<br/>", "\n", RegexOptions.IgnoreCase | RegexOptions.Compiled);
content = Regex.Replace(content, "<br />", "\n", RegexOptions.IgnoreCase | RegexOptions.Compiled);
content = Regex.Replace(content, "<p>", "\n", RegexOptions.IgnoreCase | RegexOptions.Compiled);
content = content.Replace("'", "'");
return StripHtmlXmlTags(content);
}
public static string StripHtmlXmlTags (string content) {
return Regex.Replace(content, "<[^>]+>", "", RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
// *********************************************************************
// StripScriptTags
//
/// <summary>
/// Helper function used to ensure we don't inject script into the db.
/// </summary>
/// <param name="dirtyText">Text to be cleaned for script tags</param>
/// <returns>Clean text with no script tags.</returns>
///
// ********************************************************************/
public static string StripScriptTags(string content) {
string cleanText;
// Perform RegEx
content = Regex.Replace(content, "<script((.|\n)*?)</script>", "", RegexOptions.IgnoreCase | RegexOptions.Multiline);
cleanText = Regex.Replace(content, "\"javascript:", "", RegexOptions.IgnoreCase | RegexOptions.Multiline);
return cleanText;
}
#endregion
#region Emoticon & User Transforms
public static string EmoticonTransforms (string formattedPost) {
// remove some overhead if this is turned off
if (!CSContext.Current.SiteSettings.EnableEmoticons)
return formattedPost;
try {
// Load the emoticon transform table
//
ArrayList emoticonTxTable = Smilies.GetSmilies();
const string imgFormat = "<img src=\"{0}{1}\" alt=\"{2}\" />";
// EAD 6/27/2004: Changed to loop through twice.
// Once for brackets first, so to capture the Party emoticon
// (special emoticons that REQUIRE brackets), so not to replace
// with other non-bracket icons. Less efficient yes, but captures
// all emoticons properly.
//
string smileyPattern = "";
string replacePattern = "";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -