using System;
using System.Configuration;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Security;
using System.Globalization;
using ScrewTurn.Wiki.PluginFramework;
using System.Reflection;
using System.Net;
namespace ScrewTurn.Wiki {
///
/// Contains useful Tools.
///
public static class Tools {
///
/// Gets all the included files for the HTML Head, such as CSS, JavaScript and Icon pluginAssemblies, for a namespace.
///
/// The namespace (null for the root).
/// The includes.
public static string GetIncludes(string nspace) {
string theme = Settings.GetTheme(nspace);
string themePath = Settings.GetThemePath(nspace);
StringBuilder result = new StringBuilder(300);
string[] css = Directory.GetFiles(Settings.ThemesDirectory + theme, "*.css");
string firstChunk;
for(int i = 0; i < css.Length; i++) {
if(Path.GetFileName(css[i]).IndexOf("_") != -1) {
firstChunk = Path.GetFileName(css[i]).Substring(0, Path.GetFileName(css[i]).IndexOf("_")).ToLower(CultureInfo.CurrentCulture);
if(firstChunk.Equals("screen") || firstChunk.Equals("print") || firstChunk.Equals("all") ||
firstChunk.Equals("aural") || firstChunk.Equals("braille") || firstChunk.Equals("embossed") ||
firstChunk.Equals("handheld") || firstChunk.Equals("projection") || firstChunk.Equals("tty") || firstChunk.Equals("tv")) {
result.Append(@"" + "\n");
}
else {
result.Append(@"" + "\n");
}
}
else {
result.Append(@"" + "\n");
}
}
string customEditorCss = Path.Combine(Settings.ThemesDirectory, theme);
customEditorCss = Path.Combine(customEditorCss, "Editor.css");
if(File.Exists(customEditorCss)) result.AppendFormat(@"" + "\n", theme);
else result.Append(@"" + "\n");
// OpenSearch
result.AppendFormat(@"",
Settings.MainUrl, Settings.WikiTitle + " - Search");
string[] js = Directory.GetFiles(Settings.ThemesDirectory + theme, "*.js");
for(int i = 0; i < js.Length; i++) {
result.Append(@"" + "\n");
}
string[] icons = Directory.GetFiles(Settings.ThemesDirectory + theme, "Icon.*");
if(icons.Length > 0) {
result.Append(@"" + "\n");
}
result.Append(GetJavaScriptIncludes());
// Include HTML Head
result.Append(Settings.Provider.GetMetaDataItem(MetaDataItem.HtmlHead, null));
return result.ToString();
}
///
/// Gets all the JavaScript files to include.
///
/// The JS files.
public static string GetJavaScriptIncludes() {
StringBuilder buffer = new StringBuilder(100);
foreach(string js in Directory.GetFiles(Settings.JsDirectory, "*.js")) {
buffer.Append(@"" + "\n");
}
return buffer.ToString();
}
///
/// Converts a byte number into a string, formatted using KB, MB or GB.
///
/// The # of bytes.
/// The formatted string.
public static string BytesToString(long bytes) {
if(bytes < 1024) return bytes.ToString() + " B";
else if(bytes < 1048576) return string.Format("{0:N2} KB", (float)bytes / 1024F);
else if(bytes < 1073741824) return string.Format("{0:N2} MB", (float)bytes / 1048576F);
else return string.Format("{0:N2} GB", (float)bytes / 1073741824F);
}
///
/// Computes the Disk Space Usage of a directory.
///
/// The directory.
/// The used Disk Space, in bytes.
public static long DiskUsage(string dir) {
string[] files = Directory.GetFiles(dir);
string[] directories = Directory.GetDirectories(dir);
long result = 0;
FileInfo file;
for(int i = 0; i < files.Length; i++) {
file = new FileInfo(files[i]);
result += file.Length;
}
for(int i = 0; i < directories.Length; i++) {
result += DiskUsage(directories[i]);
}
return result;
}
///
/// Generates the standard 5-digit Page Version string.
///
/// The Page version.
/// The 5-digit Version string.
public static string GetVersionString(int version) {
string result = version.ToString();
int len = result.Length;
for(int i = 0; i < 5 - len; i++) {
result = "0" + result;
}
return result;
}
///
/// Gets the available Themes.
///
public static string[] AvailableThemes {
get {
string[] dirs = Directory.GetDirectories(Settings.ThemesDirectory);
string[] res = new string[dirs.Length];
for(int i = 0; i < dirs.Length; i++) {
//if(dirs[i].EndsWith("\\")) dirs[i] = dirs[i].Substring(0, dirs[i].Length - 1);
dirs[i] = dirs[i].TrimEnd(Path.DirectorySeparatorChar);
res[i] = dirs[i].Substring(dirs[i].LastIndexOf(Path.DirectorySeparatorChar) + 1);
}
return res;
}
}
///
/// Gets the available Cultures.
///
public static string[] AvailableCultures {
get {
// It seems, at least in VS 2008, that for Precompiled Web Sites, the GlobalResources pluginAssemblies that are not the
// default resource (Culture=neutral), get sorted into subdirectories named by the Culture Info name. Every
// assembly in these directories is called "ScrewTurn.Wiki.resources.dll"
// I'm sure it's possible to just use the subdirectory names in the bin directory to get the culture info names,
// however, I'm not sure what other things might get tossed in there by the compiler now or in the future.
// That's why I'm specifically going for the App_GlobalResources.resources.dlls.
// So, get all of the App_GlobalResources.resources.dll pluginAssemblies from bin and recurse subdirectories
string[] dllFiles = Directory.GetFiles(Path.Combine(Settings.RootDirectory, "bin"), "ScrewTurn.Wiki.resources.dll", SearchOption.AllDirectories);
// List to collect constructed culture names
List cultureNames = new List();
// Manually add en-US culture
CultureInfo enCI = new CultureInfo("en-US");
cultureNames.Add(enCI.Name + "|" + UppercaseInitial(enCI.NativeName) + " - " + enCI.EnglishName);
// For every file we find
// List format: xx-ZZ|Native name (English name)
foreach(string s in dllFiles) {
try {
// Load a reflection only assembly from the filename
Assembly asm = Assembly.ReflectionOnlyLoadFrom(s);
// string for destructive parsing of the assembly's full name
// Which, btw, looks something like this
// App_GlobalResources.resources, Version=0.0.0.0, Culture=zh-cn, PublicKeyToken=null
string fullName = asm.FullName;
// Find the Culture= attribute
int find = fullName.IndexOf("Culture=");
// Remove it and everything prior
fullName = fullName.Substring(find + 8);
// Find the trailing comma
find = fullName.IndexOf(',');
// Remove it and everything after
fullName = fullName.Substring(0, find);
// Fullname should now be the culture info name and we can instantiate the CultureInfo class from it
CultureInfo ci = new CultureInfo(fullName);
// StringBuilders
StringBuilder sb = new StringBuilder();
sb.Append(ci.Name);
sb.Append("|");
sb.Append(UppercaseInitial(ci.NativeName));
sb.Append(" - ");
sb.Append(ci.EnglishName);
// Add the newly constructed Culture string
cultureNames.Add(sb.ToString());
}
catch(Exception ex) {
Log.LogEntry("Error parsing culture info from " + s + Environment.NewLine + ex.Message, EntryType.Error, Log.SystemUsername);
}
}
// If for whatever reason every one fails, this will return a 1 element array with the en-US info.
cultureNames.Sort();
return cultureNames.ToArray();
}
}
private static string UppercaseInitial(string value) {
if(value.Length > 0) {
return value[0].ToString().ToUpper(CultureInfo.CurrentCulture) + value.Substring(1);
}
else return "";
}
///
/// Computes the Hash of a Username, mixing it with other data, in order to avoid illegal Account activations.
///
/// The Username.
/// The email.
/// The date/time.
/// The secured Hash of the Username.
public static string ComputeSecurityHash(string username, string email, DateTime dateTime) {
return Hash.ComputeSecurityHash(username, email, dateTime, Settings.MasterPassword);
}
///
/// Escapes bad characters in a string (pipes and \n).
///
/// The input string.
/// The escaped string.
public static string EscapeString(string input) {
StringBuilder sb = new StringBuilder(input);
sb.Replace("\r", "");
sb.Replace("\n", "%0A");
sb.Replace("|", "%7C");
return sb.ToString();
}
///
/// Unescapes bad characters in a string (pipes and \n).
///
/// The input string.
/// The unescaped string.
public static string UnescapeString(string input) {
StringBuilder sb = new StringBuilder(input);
sb.Replace("%7C", "|");
sb.Replace("%0A", "\n");
return sb.ToString();
}
///
/// Generates a random 10-char Password.
///
/// The Password.
public static string GenerateRandomPassword() {
Random r = new Random();
string password = "";
for(int i = 0; i < 10; i++) {
if(i % 2 == 0)
password += ((char)r.Next(65, 91)).ToString(); // Uppercase letter
else password += ((char)r.Next(97, 123)).ToString(); // Lowercase letter
}
return password;
}
///
/// Gets the approximate System Uptime.
///
public static TimeSpan SystemUptime {
get {
int t = Environment.TickCount;
if(t < 0) t = t + int.MaxValue;
t = t / 1000;
return TimeSpan.FromSeconds(t);
}
}
///
/// Converts a Time Span to string.
///
/// The Time Span.
/// The string.
public static string TimeSpanToString(TimeSpan span) {
string result = span.Days.ToString() + "d ";
result += span.Hours.ToString() + "h ";
result += span.Minutes.ToString() + "m ";
result += span.Seconds.ToString() + "s";
return result;
}
///
/// Executes URL-encoding, avoiding to use '+' for spaces.
///
/// The input string.
/// The encoded string.
public static string UrlEncode(string input) {
return HttpContext.Current.Server.UrlEncode(input).Replace("+", "%20");
}
///
/// Executes URL-decoding, replacing spaces as processed by UrlEncode.
///
/// The input string.
/// The decoded string.
public static string UrlDecode(string input) {
return HttpContext.Current.Server.UrlDecode(input.Replace("%20", " "));
}
///
/// Removes all HTML tags from a text.
///
/// The input HTML.
/// The extracted plain text.
public static string RemoveHtmlMarkup(string html) {
StringBuilder sb = new StringBuilder(System.Text.RegularExpressions.Regex.Replace(html, "<[^>]*>", " "));
sb.Replace(" ", " ");
sb.Replace(" ", " ");
return sb.ToString();
}
///
/// Extracts the directory name from a path used in the Files Storage Providers.
///
/// The path, for example '/folder/blah/'.
/// The directory name, for example 'blah'.
public static string ExtractDirectoryName(string path) {
path = path.Trim('/');
int idx = path.LastIndexOf("/");
return idx != -1 ? path.Substring(idx + 1) : path;
}
///
/// Detects the correct object associated to the current page using the Page and NS parameters in the query string.
///
/// true to load the default page of the specified namespace when Page is not specified, false otherwise.
/// If Page is specified and exists, the correct , otherwise null if loadDefault is false,
/// or the object representing the default page of the specified namespace if loadDefault is true.
public static PageInfo DetectCurrentPageInfo(bool loadDefault) {
string nspace = HttpContext.Current.Request["NS"];
NamespaceInfo nsinfo = nspace != null ? Pages.FindNamespace(nspace) : null;
string page = HttpContext.Current.Request["Page"];
if(string.IsNullOrEmpty(page)) {
if(loadDefault) {
if(nsinfo == null) page = Settings.DefaultPage;
else page = nsinfo.DefaultPage != null ? nsinfo.DefaultPage.FullName : "";
}
else return null;
}
string fullName = null;
if(!page.StartsWith(nspace + ".")) fullName = nspace + "." + page;
else fullName = page;
fullName = fullName.Trim('.');
return Pages.FindPage(fullName);
}
///
/// Detects the full name of the current page using the Page and NS parameters in the query string.
///
/// The full name of the page, regardless of the existence of the page.
public static string DetectCurrentFullName() {
string nspace = HttpContext.Current.Request["NS"] != null ? HttpContext.Current.Request["NS"] : "";
string page = HttpContext.Current.Request["Page"] != null ? HttpContext.Current.Request["Page"] : "";
string fullName = null;
if(!page.StartsWith(nspace + ".")) fullName = nspace + "." + page;
else fullName = page;
return fullName.Trim('.');
}
///
/// Detects the correct object associated to the current namespace using the NS parameter in the query string.
///
/// The correct object, or null.
public static NamespaceInfo DetectCurrentNamespaceInfo() {
string nspace = HttpContext.Current.Request["NS"];
NamespaceInfo nsinfo = nspace != null ? Pages.FindNamespace(nspace) : null;
return nsinfo;
}
///
/// Detects the name of the current namespace using the NS parameter in the query string.
///
/// The name of the namespace, or an empty string.
public static string DetectCurrentNamespace() {
return HttpContext.Current.Request["NS"] != null ? HttpContext.Current.Request["NS"] : "";
}
///
/// Gets the message ID for HTML anchors.
///
/// The message date/time.
/// The ID.
public static string GetMessageIdForAnchor(DateTime messageDateTime) {
return "MSG_" + messageDateTime.ToString("yyyyMMddHHmmss");
}
///
/// Gets the name of a file's directory.
///
/// The filename.
/// The name of the item.
public static string GetDirectoryName(string filename) {
if(filename != null) {
int index = filename.LastIndexOf("/");
if(index > 0) {
string directoryName = filename.Substring(0, index + 1);
if(!directoryName.StartsWith("/")) directoryName = "/" + directoryName;
return directoryName;
}
}
// Assume to navigate in the root directory
return "/";
}
///
/// Gets the update status of a component.
///
/// The version file URL.
/// The current version.
/// The new version, if any.
/// The URL of the new assembly, if applicable and available.
/// The update status.
/// This method only works in Full Trust.
public static UpdateStatus GetUpdateStatus(string url, string currentVersion, out string newVersion, out string newAssemblyUrl) {
// TODO: Verify usage of WebPermission class
// http://msdn.microsoft.com/en-us/library/system.net.webpermission.aspx
string urlHash = "UpdUrlCache-" + url.GetHashCode().ToString();
try {
string ver = null;
if(HttpContext.Current != null) {
ver = HttpContext.Current.Cache[urlHash] as string;
}
if(ver == null) {
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.AllowAutoRedirect = true;
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
if(res.StatusCode != HttpStatusCode.OK) {
newVersion = null;
newAssemblyUrl = null;
return UpdateStatus.Error;
}
StreamReader sr = new StreamReader(res.GetResponseStream());
ver = sr.ReadToEnd();
sr.Close();
if(HttpContext.Current != null) {
HttpContext.Current.Cache.Add(urlHash, ver, null, DateTime.Now.AddMinutes(5),
System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
}
}
string[] lines = ver.Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
if(lines.Length == 0) {
newVersion = null;
newAssemblyUrl = null;
return UpdateStatus.Error;
}
string[] versions = lines[0].Split('|');
bool upToDate = false;
for(int i = 0; i < versions.Length; i++) {
ver = versions[i];
if(versions[i].Equals(currentVersion)) {
if(i == versions.Length - 1) upToDate = true;
else upToDate = false;
ver = versions[versions.Length - 1];
break;
}
}
if(upToDate) {
newVersion = null;
newAssemblyUrl = null;
return UpdateStatus.UpToDate;
}
else {
newVersion = ver;
if(lines.Length == 2) newAssemblyUrl = lines[1];
else newAssemblyUrl = null;
return UpdateStatus.NewVersionFound;
}
}
catch(Exception) {
if(HttpContext.Current != null) {
HttpContext.Current.Cache.Add(urlHash, "", null, DateTime.Now.AddMinutes(5),
System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
}
newVersion = null;
newAssemblyUrl = null;
return UpdateStatus.Error;
}
}
///
/// Computes the hash value of a string that is value across application instances and versions.
///
/// The string to compute the hash of.
/// The hash value.
public static uint HashDocumentNameForTemporaryIndex(string value) {
if(value == null) throw new ArgumentNullException("value");
// sdbm algorithm, borrowed from http://www.cse.yorku.ca/~oz/hash.html
uint hash = 0;
foreach(char c in value) {
// hash(i) = hash(i - 1) * 65599 + str[i]
hash = c + (hash << 6) + (hash << 16) - hash;
}
return hash;
}
}
///
/// Lists legal update statuses.
///
public enum UpdateStatus {
///
/// Error while retrieving version information.
///
Error,
///
/// The component is up-to-date.
///
UpToDate,
///
/// A new version was found.
///
NewVersionFound
}
}