573 lines
21 KiB
C#
573 lines
21 KiB
C#
|
|
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 {
|
|
|
|
/// <summary>
|
|
/// Contains useful Tools.
|
|
/// </summary>
|
|
public static class Tools {
|
|
|
|
/// <summary>
|
|
/// Gets all the included files for the HTML Head, such as CSS, JavaScript and Icon pluginAssemblies, for a namespace.
|
|
/// </summary>
|
|
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
|
/// <returns>The includes.</returns>
|
|
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(@"<link rel=""stylesheet"" media=""" + firstChunk + @""" href=""" + themePath + Path.GetFileName(css[i]) + @""" type=""text/css"" />" + "\n");
|
|
}
|
|
else {
|
|
result.Append(@"<link rel=""stylesheet"" href=""" + themePath + Path.GetFileName(css[i]) + @""" type=""text/css"" />" + "\n");
|
|
}
|
|
}
|
|
else {
|
|
result.Append(@"<link rel=""stylesheet"" href=""" + themePath + Path.GetFileName(css[i]) + @""" type=""text/css"" />" + "\n");
|
|
}
|
|
}
|
|
|
|
string customEditorCss = Path.Combine(Settings.ThemesDirectory, theme);
|
|
customEditorCss = Path.Combine(customEditorCss, "Editor.css");
|
|
if(File.Exists(customEditorCss)) result.AppendFormat(@"<link rel=""stylesheet"" href=""Themes/{0}/Editor.css"" type=""text/css"" />" + "\n", theme);
|
|
else result.Append(@"<link rel=""stylesheet"" href=""Themes/Editor.css"" type=""text/css"" />" + "\n");
|
|
|
|
// OpenSearch
|
|
result.AppendFormat(@"<link rel=""search"" href=""Search.aspx?OpenSearch=1"" type=""application/opensearchdescription+xml"" title=""{1}"" />",
|
|
Settings.MainUrl, Settings.WikiTitle + " - Search");
|
|
|
|
string[] js = Directory.GetFiles(Settings.ThemesDirectory + theme, "*.js");
|
|
for(int i = 0; i < js.Length; i++) {
|
|
result.Append(@"<script src=""" + themePath + Path.GetFileName(js[i]) + @""" type=""text/javascript""></script>" + "\n");
|
|
}
|
|
|
|
string[] icons = Directory.GetFiles(Settings.ThemesDirectory + theme, "Icon.*");
|
|
if(icons.Length > 0) {
|
|
result.Append(@"<link rel=""shortcut icon"" href=""" + themePath + Path.GetFileName(icons[0]) + @""" type=""");
|
|
switch(Path.GetExtension(icons[0]).ToLowerInvariant()) {
|
|
case ".ico":
|
|
result.Append("image/x-icon");
|
|
break;
|
|
case ".gif":
|
|
result.Append("image/gif");
|
|
break;
|
|
case ".png":
|
|
result.Append("image/png");
|
|
break;
|
|
}
|
|
result.Append(@""" />" + "\n");
|
|
}
|
|
|
|
result.Append(GetJavaScriptIncludes());
|
|
|
|
// Include HTML Head
|
|
result.Append(Settings.Provider.GetMetaDataItem(MetaDataItem.HtmlHead, null));
|
|
|
|
return result.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all the JavaScript files to include.
|
|
/// </summary>
|
|
/// <returns>The JS files.</returns>
|
|
public static string GetJavaScriptIncludes() {
|
|
StringBuilder buffer = new StringBuilder(100);
|
|
|
|
foreach(string js in Directory.GetFiles(Settings.JsDirectory, "*.js")) {
|
|
buffer.Append(@"<script type=""text/javascript"" src=""" + Settings.JsDirectoryName + "/" + Path.GetFileName(js) + @"""></script>" + "\n");
|
|
}
|
|
|
|
return buffer.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a byte number into a string, formatted using KB, MB or GB.
|
|
/// </summary>
|
|
/// <param name="bytes">The # of bytes.</param>
|
|
/// <returns>The formatted string.</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes the Disk Space Usage of a directory.
|
|
/// </summary>
|
|
/// <param name="dir">The directory.</param>
|
|
/// <returns>The used Disk Space, in bytes.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates the standard 5-digit Page Version string.
|
|
/// </summary>
|
|
/// <param name="version">The Page version.</param>
|
|
/// <returns>The 5-digit Version string.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the available Themes.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the available Cultures.
|
|
/// </summary>
|
|
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<string> cultureNames = new List<string>();
|
|
|
|
// 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 "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes the Hash of a Username, mixing it with other data, in order to avoid illegal Account activations.
|
|
/// </summary>
|
|
/// <param name="username">The Username.</param>
|
|
/// <param name="email">The email.</param>
|
|
/// <param name="dateTime">The date/time.</param>
|
|
/// <returns>The secured Hash of the Username.</returns>
|
|
public static string ComputeSecurityHash(string username, string email, DateTime dateTime) {
|
|
return Hash.ComputeSecurityHash(username, email, dateTime, Settings.MasterPassword);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Escapes bad characters in a string (pipes and \n).
|
|
/// </summary>
|
|
/// <param name="input">The input string.</param>
|
|
/// <returns>The escaped string.</returns>
|
|
public static string EscapeString(string input) {
|
|
StringBuilder sb = new StringBuilder(input);
|
|
sb.Replace("\r", "");
|
|
sb.Replace("\n", "%0A");
|
|
sb.Replace("|", "%7C");
|
|
return sb.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unescapes bad characters in a string (pipes and \n).
|
|
/// </summary>
|
|
/// <param name="input">The input string.</param>
|
|
/// <returns>The unescaped string.</returns>
|
|
public static string UnescapeString(string input) {
|
|
StringBuilder sb = new StringBuilder(input);
|
|
sb.Replace("%7C", "|");
|
|
sb.Replace("%0A", "\n");
|
|
return sb.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a random 10-char Password.
|
|
/// </summary>
|
|
/// <returns>The Password.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the approximate System Uptime.
|
|
/// </summary>
|
|
public static TimeSpan SystemUptime {
|
|
get {
|
|
int t = Environment.TickCount;
|
|
if(t < 0) t = t + int.MaxValue;
|
|
t = t / 1000;
|
|
return TimeSpan.FromSeconds(t);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a Time Span to string.
|
|
/// </summary>
|
|
/// <param name="span">The Time Span.</param>
|
|
/// <returns>The string.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes URL-encoding, avoiding to use '+' for spaces.
|
|
/// </summary>
|
|
/// <param name="input">The input string.</param>
|
|
/// <returns>The encoded string.</returns>
|
|
public static string UrlEncode(string input) {
|
|
return HttpContext.Current.Server.UrlEncode(input).Replace("+", "%20");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Executes URL-decoding, replacing spaces as processed by UrlEncode.
|
|
/// </summary>
|
|
/// <param name="input">The input string.</param>
|
|
/// <returns>The decoded string.</returns>
|
|
public static string UrlDecode(string input) {
|
|
return HttpContext.Current.Server.UrlDecode(input.Replace("%20", " "));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all HTML tags from a text.
|
|
/// </summary>
|
|
/// <param name="html">The input HTML.</param>
|
|
/// <returns>The extracted plain text.</returns>
|
|
public static string RemoveHtmlMarkup(string html) {
|
|
StringBuilder sb = new StringBuilder(System.Text.RegularExpressions.Regex.Replace(html, "<[^>]*>", " "));
|
|
sb.Replace(" ", " ");
|
|
sb.Replace(" ", " ");
|
|
return sb.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracts the directory name from a path used in the Files Storage Providers.
|
|
/// </summary>
|
|
/// <param name="path">The path, for example '/folder/blah/'.</param>
|
|
/// <returns>The directory name, for example 'blah'.</returns>
|
|
public static string ExtractDirectoryName(string path) {
|
|
path = path.Trim('/');
|
|
|
|
int idx = path.LastIndexOf("/");
|
|
return idx != -1 ? path.Substring(idx + 1) : path;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects the correct <see cref="T:PageInfo" /> object associated to the current page using the <b>Page</b> and <b>NS</b> parameters in the query string.
|
|
/// </summary>
|
|
/// <param name="loadDefault"><c>true</c> to load the default page of the specified namespace when <b>Page</b> is not specified, <c>false</c> otherwise.</param>
|
|
/// <returns>If <b>Page</b> is specified and exists, the correct <see cref="T:PageInfo" />, otherwise <c>null</c> if <b>loadDefault</b> is <c>false</c>,
|
|
/// or the <see cref="T:PageInfo" /> object representing the default page of the specified namespace if <b>loadDefault</b> is <c>true</c>.</returns>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects the full name of the current page using the <b>Page</b> and <b>NS</b> parameters in the query string.
|
|
/// </summary>
|
|
/// <returns>The full name of the page, regardless of the existence of the page.</returns>
|
|
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('.');
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects the correct <see cref="T:NamespaceInfo" /> object associated to the current namespace using the <b>NS</b> parameter in the query string.
|
|
/// </summary>
|
|
/// <returns>The correct <see cref="T:NamespaceInfo" /> object, or <c>null</c>.</returns>
|
|
public static NamespaceInfo DetectCurrentNamespaceInfo() {
|
|
string nspace = HttpContext.Current.Request["NS"];
|
|
NamespaceInfo nsinfo = nspace != null ? Pages.FindNamespace(nspace) : null;
|
|
return nsinfo;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects the name of the current namespace using the <b>NS</b> parameter in the query string.
|
|
/// </summary>
|
|
/// <returns>The name of the namespace, or an empty string.</returns>
|
|
public static string DetectCurrentNamespace() {
|
|
return HttpContext.Current.Request["NS"] != null ? HttpContext.Current.Request["NS"] : "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the message ID for HTML anchors.
|
|
/// </summary>
|
|
/// <param name="messageDateTime">The message date/time.</param>
|
|
/// <returns>The ID.</returns>
|
|
public static string GetMessageIdForAnchor(DateTime messageDateTime) {
|
|
return "MSG_" + messageDateTime.ToString("yyyyMMddHHmmss");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the name of a file's directory.
|
|
/// </summary>
|
|
/// <param name="filename">The filename.</param>
|
|
/// <returns>The name of the item.</returns>
|
|
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 "/";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the update status of a component.
|
|
/// </summary>
|
|
/// <param name="url">The version file URL.</param>
|
|
/// <param name="currentVersion">The current version.</param>
|
|
/// <param name="newVersion">The new version, if any.</param>
|
|
/// <param name="newAssemblyUrl">The URL of the new assembly, if applicable and available.</param>
|
|
/// <returns>The update status.</returns>
|
|
/// <remarks>This method only works in Full Trust.</remarks>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes the hash value of a string that is value across application instances and versions.
|
|
/// </summary>
|
|
/// <param name="value">The string to compute the hash of.</param>
|
|
/// <returns>The hash value.</returns>
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lists legal update statuses.
|
|
/// </summary>
|
|
public enum UpdateStatus {
|
|
/// <summary>
|
|
/// Error while retrieving version information.
|
|
/// </summary>
|
|
Error,
|
|
/// <summary>
|
|
/// The component is up-to-date.
|
|
/// </summary>
|
|
UpToDate,
|
|
/// <summary>
|
|
/// A new version was found.
|
|
/// </summary>
|
|
NewVersionFound
|
|
}
|
|
|
|
}
|