542 lines
19 KiB
C#
542 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text.RegularExpressions;
|
|
using System.Web;
|
|
using System.Xml;
|
|
using System.Xml.XPath;
|
|
using HtmlAgilityPack;
|
|
using uForum.Businesslogic;
|
|
using umbraco;
|
|
using MarkdownSharp;
|
|
using umbraco.cms.businesslogic.member;
|
|
|
|
namespace uForum.Library {
|
|
public class Xslt {
|
|
|
|
public string ReplaceCode(Match m)
|
|
{
|
|
if (m.Success)
|
|
return "<code>" + HttpContext.Current.Server.HtmlEncode(m.Groups[0].Value).Replace("<br />", "").Replace("<", "<").Replace(">", ">").Trim() + "</code>";
|
|
else
|
|
return "";
|
|
}
|
|
|
|
|
|
public static string CleanBBCode(string html) {
|
|
|
|
string str = html;
|
|
Regex exp;
|
|
|
|
// format the code tags: [code]my site[code]
|
|
// becomes: <code>my site</code>
|
|
exp = new Regex(@"\[code\](.+?)\[/code\]", RegexOptions.Singleline | RegexOptions.IgnoreCase );
|
|
|
|
Xslt x = new Xslt();
|
|
MatchEvaluator exp_eval = new MatchEvaluator(x.ReplaceCode);
|
|
// Replace matched characters using the delegate method.
|
|
str = exp.Replace(str, exp_eval);
|
|
|
|
// format the url tags: [url=www.website.com]my site[/url]
|
|
// becomes: <a href="www.website.com">my site</a>
|
|
exp = new Regex(@"\[quote\=([^\]]+)\]([^\]]+)\[/quote\]", RegexOptions.Singleline);
|
|
str = exp.Replace(str, "<blockquote>$2</blockquote>");
|
|
|
|
return str;
|
|
}
|
|
|
|
public static bool IsMemberInGroup(string GroupName, int memberid) {
|
|
Member m = Utills.GetMember(memberid);
|
|
|
|
foreach (MemberGroup mg in m.Groups.Values) {
|
|
if (mg.Text == GroupName)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static bool IsInGroup(string GroupName) {
|
|
|
|
if (umbraco.library.IsLoggedOn())
|
|
return IsMemberInGroup(GroupName, Member.CurrentMemberId());
|
|
else
|
|
return false;
|
|
}
|
|
|
|
public static void RegisterRssFeed(string url, string title, string alias) {
|
|
umbraco.library.RegisterClientScriptBlock(alias, "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"" + title + "\" href=\"" + url + "\" />" ,false);
|
|
}
|
|
|
|
public static string NiceTopicUrl(int topicId)
|
|
{
|
|
var topic = Businesslogic.Topic.GetTopic(topicId);
|
|
|
|
if (topic == null || topic.Exists == false)
|
|
return "";
|
|
|
|
var url = library.NiceUrl(topic.ParentId);
|
|
|
|
return GlobalSettings.UseDirectoryUrls
|
|
? string.Format("/{0}/{1}-{2}", url.Trim('/'), topic.Id, topic.UrlName)
|
|
: string.Format("/{0}/{1}-{2}.aspx", url.Substring(0, url.LastIndexOf('.')).Trim('/'), topic.Id, topic.UrlName);
|
|
}
|
|
|
|
public static int CommentPageNumber(int commentId, int itemsPerPage) {
|
|
Businesslogic.Comment c = new uForum.Businesslogic.Comment(commentId);
|
|
|
|
int position = c.Position - 1;
|
|
|
|
return (int)(position / itemsPerPage);
|
|
}
|
|
|
|
|
|
public static string NiceCommentUrl(int topicId, int commentId, int itemsPerPage) {
|
|
string url = NiceTopicUrl(topicId);
|
|
if (!string.IsNullOrEmpty(url)) {
|
|
Businesslogic.Comment c = new uForum.Businesslogic.Comment(commentId);
|
|
|
|
int position = c.Position - 1;
|
|
|
|
int page = (int)(position / itemsPerPage);
|
|
|
|
|
|
url += "?p=" + page.ToString() + "#comment" + c.Id.ToString();
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
public static XPathNodeIterator ForumPager(int forumId, int itemsPerPage, int currentPage)
|
|
{
|
|
return ForumPager(forumId, itemsPerPage, currentPage, 10);
|
|
}
|
|
|
|
public static XPathNodeIterator ForumPager(int forumId, int itemsPerPage, int currentPage, int distance)
|
|
{
|
|
var xd = new XmlDocument();
|
|
var totalTopics = 0;
|
|
|
|
if (forumId == 0)
|
|
totalTopics = Businesslogic.Topic.TotalTopics();
|
|
else
|
|
{
|
|
var f = new uForum.Businesslogic.Forum(forumId);
|
|
totalTopics = f.TotalTopics;
|
|
}
|
|
|
|
var pages = xmlHelper.addTextNode(xd, "pages", string.Empty);
|
|
var i = 0;
|
|
var p = 0;
|
|
|
|
while (i < (totalTopics))
|
|
{
|
|
var distanceFromCurrent = p - currentPage;
|
|
if (distanceFromCurrent > -distance && distanceFromCurrent < distance)
|
|
{
|
|
var page = xmlHelper.addTextNode(xd, "page", string.Empty);
|
|
page.Attributes.Append(xmlHelper.addAttribute(xd, "index", p.ToString()));
|
|
|
|
if (p == currentPage)
|
|
{
|
|
page.Attributes.Append(xmlHelper.addAttribute(xd, "current", "true"));
|
|
}
|
|
|
|
pages.AppendChild(page);
|
|
}
|
|
|
|
p++;
|
|
i = (i + itemsPerPage);
|
|
}
|
|
|
|
return pages.CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static XPathNodeIterator TopicPager(int topicId, int itemsPerPage, int currentPage) {
|
|
XmlDocument xd = new XmlDocument();
|
|
Businesslogic.Topic t = uForum.Businesslogic.Topic.GetTopic(topicId);
|
|
|
|
XmlNode pages = umbraco.xmlHelper.addTextNode(xd, "pages", "");
|
|
|
|
int i = 0;
|
|
int p = 0;
|
|
|
|
while (i < (t.Replies)) {
|
|
XmlNode page = umbraco.xmlHelper.addTextNode(xd, "page", "");
|
|
page.Attributes.Append(umbraco.xmlHelper.addAttribute(xd, "index", p.ToString()));
|
|
if(p == currentPage){
|
|
page.Attributes.Append( umbraco.xmlHelper.addAttribute(xd, "current", "true"));
|
|
}
|
|
pages.AppendChild(page);
|
|
|
|
p++;
|
|
i = (i + itemsPerPage);
|
|
}
|
|
|
|
return pages.CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static XPathNodeIterator MemberTopicPager(int memberId, int itemsPerPage, int currentPage)
|
|
{
|
|
XmlDocument xd = new XmlDocument();
|
|
XmlNode pages = umbraco.xmlHelper.addTextNode(xd, "pages", "");
|
|
|
|
int i = 0;
|
|
int p = 0;
|
|
int total = uForum.Businesslogic.Forum.TotalTopicsAndComments(memberId);
|
|
|
|
while (i < (total))
|
|
{
|
|
XmlNode page = umbraco.xmlHelper.addTextNode(xd, "page", "");
|
|
page.Attributes.Append(umbraco.xmlHelper.addAttribute(xd, "index", p.ToString()));
|
|
if (p == currentPage)
|
|
{
|
|
page.Attributes.Append(umbraco.xmlHelper.addAttribute(xd, "current", "true"));
|
|
}
|
|
pages.AppendChild(page);
|
|
|
|
p++;
|
|
i = (i + itemsPerPage);
|
|
}
|
|
|
|
return pages.CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static XPathNodeIterator AllForums(bool includeLatestData) {
|
|
|
|
|
|
XmlDocument xd = new XmlDocument();
|
|
XmlNode x = xd.CreateElement("forums");
|
|
|
|
List<Businesslogic.Forum> forums = Businesslogic.Forum.Forums();
|
|
foreach (Businesslogic.Forum f in forums) {
|
|
x.AppendChild(f.ToXml(xd, includeLatestData));
|
|
}
|
|
|
|
return x.CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static XPathNodeIterator Forum(int id, bool includeLatestData) {
|
|
XmlDocument xd = new XmlDocument();
|
|
|
|
|
|
return new Businesslogic.Forum(id).ToXml(xd, includeLatestData).CreateNavigator().Select(".");
|
|
|
|
}
|
|
|
|
public static XPathNodeIterator Forums(int parentId, bool includeLatestData) {
|
|
XmlDocument xd = new XmlDocument();
|
|
XmlNode x = xd.CreateElement("forums");
|
|
|
|
List<Businesslogic.Forum> forums = Businesslogic.Forum.Forums(parentId);
|
|
foreach (Businesslogic.Forum f in forums) {
|
|
x.AppendChild(f.ToXml(xd, includeLatestData));
|
|
}
|
|
|
|
return x.CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static XPathNodeIterator ForumTopics(int nodeId, int amount){
|
|
XmlDocument xd = new XmlDocument();
|
|
XmlNode x = xd.CreateElement("topics");
|
|
|
|
List<Businesslogic.Topic> topics = Businesslogic.Topic.TopicsInForum(nodeId, amount, 1);
|
|
foreach (Businesslogic.Topic t in topics) {
|
|
x.AppendChild(t.ToXml(xd));
|
|
}
|
|
|
|
return x.CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static XPathNodeIterator Topic(int topicID) {
|
|
XmlDocument xd = new XmlDocument();
|
|
Businesslogic.Topic t = uForum.Businesslogic.Topic.GetTopic(topicID);
|
|
|
|
if (t.Exists)
|
|
xd.AppendChild(t.ToXml(xd));
|
|
|
|
return xd.CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static XPathNodeIterator Comment(int commentID) {
|
|
|
|
XmlDocument xd = new XmlDocument();
|
|
Businesslogic.Comment c = new uForum.Businesslogic.Comment(commentID);
|
|
|
|
return c.ToXml(xd).CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static XPathNodeIterator TopicComments(int topicID) {
|
|
|
|
XmlDocument xd = new XmlDocument();
|
|
Businesslogic.Topic t = uForum.Businesslogic.Topic.GetTopic(topicID);
|
|
|
|
if (t.Exists) {
|
|
XmlNode comments = umbraco.xmlHelper.addTextNode(xd, "comments", "");
|
|
|
|
foreach (Businesslogic.Comment cc in t.Comments) {
|
|
comments.AppendChild( cc.ToXml(xd) );
|
|
}
|
|
|
|
xd.AppendChild(comments);
|
|
}
|
|
|
|
return xd.CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static XPathNodeIterator LatestTopics(int amount) {
|
|
XmlDocument xd = new XmlDocument();
|
|
XmlNode x = xd.CreateElement("topics");
|
|
|
|
List<Businesslogic.Topic> topics = Businesslogic.Topic.Latest(amount);
|
|
foreach (Businesslogic.Topic t in topics) {
|
|
x.AppendChild(t.ToXml(xd));
|
|
}
|
|
|
|
return x.CreateNavigator().Select(".");
|
|
}
|
|
|
|
public static string Sanitize(string html) {
|
|
// Run it through Markdown first
|
|
var md = new Markdown();
|
|
html = md.Transform(html);
|
|
|
|
// Linkify images if they are shown as resized versions (only relevant for new Markdown comments)
|
|
var doc = new HtmlDocument();
|
|
doc.LoadHtml(html);
|
|
|
|
var root = doc.DocumentNode;
|
|
if (root != null)
|
|
{
|
|
var images = root.SelectNodes("//img");
|
|
if (images != null)
|
|
{
|
|
var replace = false;
|
|
foreach (var image in images)
|
|
{
|
|
var src = image.GetAttributeValue("src", "");
|
|
var orgSrc = src.Replace("rs/", "");
|
|
|
|
if (src == orgSrc || image.ParentNode.Name == "a") continue;
|
|
|
|
var a = doc.CreateElement("a");
|
|
a.SetAttributeValue("href", orgSrc);
|
|
a.SetAttributeValue("target", "_blank");
|
|
|
|
a.AppendChild(image.Clone());
|
|
|
|
image.ParentNode.ReplaceChild(a, image);
|
|
|
|
replace = true;
|
|
}
|
|
|
|
if (replace)
|
|
{
|
|
html = root.OuterHtml;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Utills.Sanitize(html);
|
|
}
|
|
|
|
public static string TimeDiff(string secondDate)
|
|
{
|
|
DateTime date;
|
|
if (!DateTime.TryParse(secondDate, out date))
|
|
return secondDate;
|
|
|
|
var TS = DateTime.Now.Subtract(date);
|
|
var span = int.Parse(Math.Round(TS.TotalSeconds, 0).ToString());
|
|
|
|
if (span < 60)
|
|
return "1 minute ago";
|
|
|
|
if (span >= 60 && span < 3600)
|
|
return string.Concat(Math.Round(TS.TotalMinutes), " minutes ago");
|
|
|
|
if (span >= 3600 && span < 7200)
|
|
return "1 hour ago";
|
|
|
|
if (span >= 3600 && span < 86400)
|
|
return string.Concat(Math.Round(TS.TotalHours), " hours ago");
|
|
|
|
if (span >= 86400 && span < 172800)
|
|
return "1 day ago";
|
|
|
|
if (span >= 172800 && span < 604800)
|
|
return string.Concat(Math.Round(TS.TotalDays), " days ago");
|
|
|
|
if (span >= 604800 && span < 1209600)
|
|
return "1 week ago";
|
|
|
|
return date.ToString("MMMM d, yyyy @ hh:mm");
|
|
}
|
|
|
|
|
|
private static readonly Regex linkFinderRegex = new Regex("(((?<!src=\")http://|(?<!src=\".+)www\\.)([A-Z0-9.-:]{1,})\\.[0-9A-Z?;~&#=%\\-_\\./]{2,})", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
|
private static readonly Regex anchorFinderRegex = new Regex("(<a(.*?)href=\"(.*?)\"(.*?)>)(.*?)(</a>)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
|
|
private static readonly Regex preTagFinderRegex = new Regex("(<pre(.*?)>)(.*?)(</pre>)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
|
|
private static readonly string link = "<a rel=\"nofollow\" href=\"{0}{1}\">{2}</a>";
|
|
private static readonly string imgLink = "<img src=\"{0}{1}\" border=\"0\" alt=\"\" />";
|
|
private static readonly Regex Base64ImageRegex = new Regex("(<img src=\"data:image(.*?)/>)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
|
|
private static readonly Regex imageTagFinderRegex = new Regex("(<img(.*?)/>)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
|
|
|
|
public static void TopicNotFound(int topicId)
|
|
{
|
|
HttpContext.Current.Response.RedirectPermanent(string.Concat("/?topicNotFound=", topicId));
|
|
}
|
|
|
|
public static string ResolveLinks(string body)
|
|
{
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(body))
|
|
return body;
|
|
|
|
//REMOVE BASE64 Images
|
|
body = Base64ImageRegex.Replace(body, "");
|
|
|
|
|
|
Dictionary<string, string> replaceValues = new Dictionary<string, string>();
|
|
|
|
int k = 0;
|
|
foreach (Match match in preTagFinderRegex.Matches(body))
|
|
{
|
|
string placeholder = string.Format("[[[[PreTagReplace{0}]]]]", k);
|
|
body = body.Replace(match.Value, placeholder);
|
|
replaceValues.Add(placeholder, "<pre>" + match.Groups[3].Value + "</pre>");
|
|
k++;
|
|
}
|
|
|
|
int j = 0;
|
|
foreach (Match match in anchorFinderRegex.Matches(body))
|
|
{
|
|
string placeholder = string.Format("[[[[AnchorReplacer{0}]]]]", j);
|
|
body = body.Replace(match.Value, placeholder);
|
|
|
|
|
|
if (!match.Groups[5].Value.Contains("://") || imageTagFinderRegex.IsMatch(match.Groups[5].Value))
|
|
{
|
|
if (imageTagFinderRegex.IsMatch(match.Groups[5].Value))
|
|
{
|
|
replaceValues.Add(placeholder, string.Format(imgLink, string.Empty, match.Groups[3].Value, HttpUtility.UrlDecode(match.Groups[5].Value)));
|
|
}
|
|
else
|
|
{
|
|
replaceValues.Add(placeholder, string.Format(link, string.Empty, match.Groups[3].Value, HttpUtility.UrlDecode(match.Groups[5].Value)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
replaceValues.Add(placeholder, string.Format(link, string.Empty, match.Groups[3].Value, HttpUtility.UrlDecode(ShortenUrl(match.Groups[5].Value, 50))));
|
|
}
|
|
j++;
|
|
}
|
|
|
|
|
|
int i = 0;
|
|
foreach (Match match in linkFinderRegex.Matches(body))
|
|
{
|
|
string placeholder = string.Format("[[[[LinkReplacer{0}]]]]", i);
|
|
body = body.Replace(match.Value, placeholder);
|
|
if (!match.Value.Contains("://"))
|
|
{
|
|
replaceValues.Add(placeholder, string.Format(link, "http://", match.Value, HttpUtility.UrlDecode(ShortenUrl(match.Value, 50))));
|
|
}
|
|
else
|
|
{
|
|
replaceValues.Add(placeholder, string.Format(link, string.Empty, match.Value, HttpUtility.UrlDecode(ShortenUrl(match.Value, 50))));
|
|
}
|
|
i++;
|
|
}
|
|
|
|
|
|
foreach (KeyValuePair<string, string> replaceValue in replaceValues)
|
|
{
|
|
body = body.Replace(replaceValue.Key, replaceValue.Value);
|
|
}
|
|
|
|
return body;
|
|
}
|
|
public static bool UseMarkdownEditor()
|
|
{
|
|
return ForumEditor.UseMarkdownEditor();
|
|
}
|
|
|
|
public static bool IsModerator()
|
|
{
|
|
return Utills.IsModerator();
|
|
}
|
|
|
|
public static bool CanSeeTopic(int topicId)
|
|
{
|
|
return Utills.CanSeeTopic(topicId);
|
|
}
|
|
|
|
public static bool CanSeeComment(int commentId)
|
|
{
|
|
return Utills.CanSeeComment(commentId);
|
|
}
|
|
|
|
private static string ShortenUrl(string url, int max) {
|
|
if (url.Length <= max)
|
|
return url;
|
|
|
|
try
|
|
{
|
|
// Remove the protocal
|
|
int startIndex = url.IndexOf("://");
|
|
|
|
if (startIndex > -1)
|
|
url = url.Substring(startIndex + 3);
|
|
|
|
if (url.Length <= max)
|
|
return url;
|
|
|
|
// Remove the folder structure
|
|
int firstIndex = url.IndexOf("/") + 1;
|
|
int lastIndex = url.LastIndexOf("/");
|
|
if (firstIndex < lastIndex)
|
|
url = url.Replace(url.Substring(firstIndex, lastIndex - firstIndex), "...");
|
|
|
|
if (url.Length <= max)
|
|
return url;
|
|
|
|
// Remove URL parameters
|
|
int queryIndex = url.IndexOf("?");
|
|
if (queryIndex > -1)
|
|
url = url.Substring(0, queryIndex);
|
|
|
|
if (url.Length <= max)
|
|
return url;
|
|
|
|
// Remove URL fragment
|
|
int fragmentIndex = url.IndexOf("#");
|
|
if (fragmentIndex > -1)
|
|
url = url.Substring(0, fragmentIndex);
|
|
|
|
if (url.Length <= max)
|
|
return url;
|
|
|
|
// Shorten page
|
|
firstIndex = url.LastIndexOf("/") + 1;
|
|
lastIndex = url.LastIndexOf(".");
|
|
if (lastIndex - firstIndex > 10)
|
|
{
|
|
string page = url.Substring(firstIndex, lastIndex - firstIndex);
|
|
int length = url.Length - max + 3;
|
|
url = url.Replace(page, "..." + page.Substring(length));
|
|
}
|
|
|
|
|
|
return url;
|
|
}
|
|
catch {
|
|
return url;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|